diff -Nru draco-1.5.3+dfsg/BUILDING.md draco-1.5.5+dfsg/BUILDING.md --- draco-1.5.3+dfsg/BUILDING.md 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/BUILDING.md 2022-10-29 00:55:03.000000000 +0000 @@ -77,6 +77,15 @@ Transcoder ---------- +Before attempting to build Draco with transcoding support you must run an +additional Git command to obtain the submodules: + +~~~~~ bash +# Run this command from within your Draco clone. +$ git submodule update --init +# See below if you prefer to use existing versions of Draco dependencies. +~~~~~ + In order to build the `draco_transcoder` target, the transcoding support needs to be explicitly enabled when you run `cmake`, for example: @@ -90,7 +99,7 @@ produced libraries and executables compared to the default CMake settings. The following CMake variables can be used to configure Draco to use local -copies of third party dependencies. +copies of third party dependencies instead of git submodules. - `DRACO_EIGEN_PATH`: this path must contain an Eigen directory that includes the Eigen sources. @@ -99,7 +108,7 @@ - `DRACO_TINYGLTF_PATH`: this path must contain tiny_gltf.h and its dependencies. -If not specified the Draco build requires the presence of the submodules that +When not specified the Draco build requires the presence of the submodules that are stored within `draco/third_party`. Debugging and Optimization diff -Nru draco-1.5.3+dfsg/cmake/draco_build_definitions.cmake draco-1.5.5+dfsg/cmake/draco_build_definitions.cmake --- draco-1.5.3+dfsg/cmake/draco_build_definitions.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/draco_build_definitions.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_DRACO_BUILD_DEFINITIONS_CMAKE_) return() @@ -60,7 +60,7 @@ # passed to libtool. # # We set DRACO_SOVERSION = [c-a].a.r - set(LT_CURRENT 5) + set(LT_CURRENT 7) set(LT_REVISION 0) set(LT_AGE 0) math(EXPR DRACO_SOVERSION_MAJOR "${LT_CURRENT} - ${LT_AGE}") @@ -70,7 +70,7 @@ unset(LT_AGE) list(APPEND draco_include_paths "${draco_root}" "${draco_root}/src" - "${draco_build}") + "${draco_build}") if(DRACO_ABSL) list(APPEND draco_include_paths "${draco_root}/third_party/abseil-cpp") @@ -84,8 +84,7 @@ list(APPEND draco_defines "DRACO_CMAKE=1" - "DRACO_FLAGS_SRCDIR=\"${draco_root}\"" - "DRACO_FLAGS_TMPDIR=\"/tmp\"") + "DRACO_FLAGS_SRCDIR=\"${draco_root}\"" "DRACO_FLAGS_TMPDIR=\"/tmp\"") if(MSVC OR WIN32) list(APPEND draco_defines "_CRT_SECURE_NO_DEPRECATE=1" "NOMINMAX=1") @@ -158,13 +157,9 @@ set(draco_neon_source_file_suffix "neon.cc") set(draco_sse4_source_file_suffix "sse4.cc") - if((${CMAKE_CXX_COMPILER_ID} - STREQUAL - "GNU" - AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 5) - OR (${CMAKE_CXX_COMPILER_ID} - STREQUAL - "Clang" + if((${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" AND ${CMAKE_CXX_COMPILER_VERSION} + VERSION_LESS 5) + OR (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang" AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 4)) message( WARNING "GNU/GCC < v5 or Clang/LLVM < v4, ENABLING COMPATIBILITY MODE.") @@ -173,10 +168,9 @@ if(EMSCRIPTEN) draco_check_emscripten_environment() - draco_get_required_emscripten_flags(FLAG_LIST_VAR_COMPILER - draco_base_cxx_flags - FLAG_LIST_VAR_LINKER - draco_base_exe_linker_flags) + draco_get_required_emscripten_flags( + FLAG_LIST_VAR_COMPILER draco_base_cxx_flags + FLAG_LIST_VAR_LINKER draco_base_exe_linker_flags) endif() draco_configure_sanitizer() diff -Nru draco-1.5.3+dfsg/cmake/draco_cpu_detection.cmake draco-1.5.5+dfsg/cmake/draco_cpu_detection.cmake --- draco-1.5.3+dfsg/cmake/draco_cpu_detection.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/draco_cpu_detection.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_DRACO_CPU_DETECTION_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/draco_dependencies.cmake draco-1.5.5+dfsg/cmake/draco_dependencies.cmake --- draco-1.5.3+dfsg/cmake/draco_dependencies.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/draco_dependencies.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2022 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_DRACO_DEPENDENCIES_CMAKE) return() @@ -106,14 +106,16 @@ set(gtest_path "${draco_root}/third_party/googletest") endif() - list(APPEND draco_test_include_paths - ${draco_include_paths} - "${gtest_path}/include" - "${gtest_path}/googlemock" - "${gtest_path}/googletest/include" - "${gtest_path}/googletest") + list( + APPEND + draco_test_include_paths + ${draco_include_paths} + "${gtest_path}/include" + "${gtest_path}/googlemock" + "${gtest_path}/googletest/include" + "${gtest_path}/googletest") - list(APPEND draco_gtest_all "${gtest_path}/googletest/src/gtest-all.cc") + list(APPEND draco_gtest_all "${gtest_path}/googletest/src/gtest-all.cc") list(APPEND draco_gtest_main "${gtest_path}/googletest/src/gtest_main.cc") endmacro() diff -Nru draco-1.5.3+dfsg/cmake/draco_emscripten.cmake draco-1.5.5+dfsg/cmake/draco_emscripten.cmake --- draco-1.5.3+dfsg/cmake/draco_emscripten.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/draco_emscripten.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_DRACO_EMSCRIPTEN_CMAKE_) return() @@ -58,7 +58,7 @@ # what are supposedly link-only flags sent with compile commands, but then # proceeds to produce broken code if the warnings are heeded. list(APPEND ${em_FLAG_LIST_VAR_COMPILER} - "-Wno-unused-command-line-argument") + "-Wno-unused-command-line-argument") list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-Wno-almost-asm") list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "--memory-init-file" "0") @@ -71,7 +71,7 @@ list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sMODULARIZE=1") list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sFILESYSTEM=0") list(APPEND ${em_FLAG_LIST_VAR_COMPILER} - "-sEXPORTED_FUNCTIONS=[\"_free\",\"_malloc\"]") + "-sEXPORTED_FUNCTIONS=[\"_free\",\"_malloc\"]") list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sPRECISE_F32=1") list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sNODEJS_CATCH_EXIT=0") list(APPEND ${em_FLAG_LIST_VAR_COMPILER} "-sNODEJS_CATCH_REJECTION=0") @@ -105,10 +105,11 @@ "${glue_multi_arg_opts}" ${ARGN}) if(DRACO_VERBOSE GREATER 1) - message("--------- draco_generate_emscripten_glue -----------\n" - "glue_INPUT_IDL=${glue_INPUT_IDL}\n" - "glue_OUTPUT_PATH=${glue_OUTPUT_PATH}\n" ] - "----------------------------------------------------\n") + message( + "--------- draco_generate_emscripten_glue -----------\n" + "glue_INPUT_IDL=${glue_INPUT_IDL}\n" + "glue_OUTPUT_PATH=${glue_OUTPUT_PATH}\n" + "----------------------------------------------------\n") endif() if(NOT glue_INPUT_IDL OR NOT glue_OUTPUT_PATH) @@ -118,22 +119,22 @@ endif() # Generate the glue source. - execute_process(COMMAND ${PYTHON_EXECUTABLE} - $ENV{EMSCRIPTEN}/tools/webidl_binder.py - ${glue_INPUT_IDL} ${glue_OUTPUT_PATH}) + execute_process( + COMMAND ${PYTHON_EXECUTABLE} $ENV{EMSCRIPTEN}/tools/webidl_binder.py + ${glue_INPUT_IDL} ${glue_OUTPUT_PATH}) if(NOT EXISTS "${glue_OUTPUT_PATH}.cpp") message(FATAL_ERROR "JS glue generation failed for ${glue_INPUT_IDL}.") endif() # Create a dependency so that it regenerated on edits. - add_custom_command(OUTPUT "${glue_OUTPUT_PATH}.cpp" - COMMAND ${PYTHON_EXECUTABLE} - $ENV{EMSCRIPTEN}/tools/webidl_binder.py - ${glue_INPUT_IDL} ${glue_OUTPUT_PATH} - DEPENDS ${draco_js_dec_idl} - COMMENT "Generating ${glue_OUTPUT_PATH}.cpp." - WORKING_DIRECTORY ${draco_build} - VERBATIM) + add_custom_command( + OUTPUT "${glue_OUTPUT_PATH}.cpp" + COMMAND ${PYTHON_EXECUTABLE} $ENV{EMSCRIPTEN}/tools/webidl_binder.py + ${glue_INPUT_IDL} ${glue_OUTPUT_PATH} + DEPENDS ${draco_js_dec_idl} + COMMENT "Generating ${glue_OUTPUT_PATH}.cpp." + WORKING_DIRECTORY ${draco_build} + VERBATIM) endmacro() # Wrapper for draco_add_executable() that handles the extra work necessary for @@ -159,8 +160,14 @@ unset(emexe_LINK_FLAGS) set(optional_args) set(single_value_args NAME GLUE_PATH) - set(multi_value_args SOURCES DEFINES FEATURES INCLUDES LINK_FLAGS - PRE_LINK_JS_SOURCES POST_LINK_JS_SOURCES) + set(multi_value_args + SOURCES + DEFINES + FEATURES + INCLUDES + LINK_FLAGS + PRE_LINK_JS_SOURCES + POST_LINK_JS_SOURCES) cmake_parse_arguments(emexe "${optional_args}" "${single_value_args}" "${multi_value_args}" ${ARGN}) @@ -175,17 +182,18 @@ endif() if(DRACO_VERBOSE GREATER 1) - message("--------- draco_add_emscripten_executable ---------\n" - "emexe_NAME=${emexe_NAME}\n" - "emexe_SOURCES=${emexe_SOURCES}\n" - "emexe_DEFINES=${emexe_DEFINES}\n" - "emexe_INCLUDES=${emexe_INCLUDES}\n" - "emexe_LINK_FLAGS=${emexe_LINK_FLAGS}\n" - "emexe_GLUE_PATH=${emexe_GLUE_PATH}\n" - "emexe_FEATURES=${emexe_FEATURES}\n" - "emexe_PRE_LINK_JS_SOURCES=${emexe_PRE_LINK_JS_SOURCES}\n" - "emexe_POST_LINK_JS_SOURCES=${emexe_POST_LINK_JS_SOURCES}\n" - "----------------------------------------------------\n") + message( + "--------- draco_add_emscripten_executable ---------\n" + "emexe_NAME=${emexe_NAME}\n" + "emexe_SOURCES=${emexe_SOURCES}\n" + "emexe_DEFINES=${emexe_DEFINES}\n" + "emexe_INCLUDES=${emexe_INCLUDES}\n" + "emexe_LINK_FLAGS=${emexe_LINK_FLAGS}\n" + "emexe_GLUE_PATH=${emexe_GLUE_PATH}\n" + "emexe_FEATURES=${emexe_FEATURES}\n" + "emexe_PRE_LINK_JS_SOURCES=${emexe_PRE_LINK_JS_SOURCES}\n" + "emexe_POST_LINK_JS_SOURCES=${emexe_POST_LINK_JS_SOURCES}\n" + "----------------------------------------------------\n") endif() # The Emscripten linker needs the C++ flags in addition to whatever has been @@ -194,31 +202,30 @@ if(DRACO_GLTF_BITSTREAM) # Add "_gltf" suffix to target output name. - draco_add_executable(NAME - ${emexe_NAME} - OUTPUT_NAME - ${emexe_NAME}_gltf - SOURCES - ${emexe_SOURCES} - DEFINES - ${emexe_DEFINES} - INCLUDES - ${emexe_INCLUDES} - LINK_FLAGS - ${emexe_LINK_FLAGS}) + draco_add_executable( + NAME ${emexe_NAME} + OUTPUT_NAME ${emexe_NAME}_gltf + SOURCES ${emexe_SOURCES} + DEFINES ${emexe_DEFINES} + INCLUDES ${emexe_INCLUDES} + LINK_FLAGS ${emexe_LINK_FLAGS}) else() - draco_add_executable(NAME ${emexe_NAME} SOURCES ${emexe_SOURCES} DEFINES - ${emexe_DEFINES} INCLUDES ${emexe_INCLUDES} LINK_FLAGS - ${emexe_LINK_FLAGS}) + draco_add_executable( + NAME ${emexe_NAME} + SOURCES ${emexe_SOURCES} + DEFINES ${emexe_DEFINES} + INCLUDES ${emexe_INCLUDES} + LINK_FLAGS ${emexe_LINK_FLAGS}) endif() foreach(feature ${emexe_FEATURES}) draco_enable_feature(FEATURE ${feature} TARGETS ${emexe_NAME}) endforeach() - set_property(SOURCE ${emexe_SOURCES} - APPEND - PROPERTY OBJECT_DEPENDS "${emexe_GLUE_PATH}.cpp") + set_property( + SOURCE ${emexe_SOURCES} + APPEND + PROPERTY OBJECT_DEPENDS "${emexe_GLUE_PATH}.cpp") em_link_pre_js(${emexe_NAME} ${emexe_PRE_LINK_JS_SOURCES}) em_link_post_js(${emexe_NAME} "${emexe_GLUE_PATH}.js" ${emexe_POST_LINK_JS_SOURCES}) diff -Nru draco-1.5.3+dfsg/cmake/draco_flags.cmake draco-1.5.5+dfsg/cmake/draco_flags.cmake --- draco-1.5.3+dfsg/cmake/draco_flags.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/draco_flags.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_DRACO_FLAGS_CMAKE_) return() @@ -38,7 +38,7 @@ endif() set_source_files_properties(${compiler_SOURCES} PROPERTIES COMPILE_FLAGS - ${compiler_FLAGS}) + ${compiler_FLAGS}) if(DRACO_VERBOSE GREATER 1) foreach(source ${compiler_SOURCES}) diff -Nru draco-1.5.3+dfsg/cmake/draco_helpers.cmake draco-1.5.5+dfsg/cmake/draco_helpers.cmake --- draco-1.5.3+dfsg/cmake/draco_helpers.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/draco_helpers.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_DRACO_HELPERS_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/draco_install.cmake draco-1.5.5+dfsg/cmake/draco_install.cmake --- draco-1.5.3+dfsg/cmake/draco_install.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/draco_install.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_DRACO_INSTALL_CMAKE_) return() @@ -47,9 +47,8 @@ DESTINATION "${target_directory}") endforeach() - install( - FILES "${draco_build}/draco/draco_features.h" - DESTINATION "${includes_path}/draco/") + install(FILES "${draco_build}/draco/draco_features.h" + DESTINATION "${includes_path}/draco/") install(TARGETS draco_decoder DESTINATION "${bin_path}") install(TARGETS draco_encoder DESTINATION "${bin_path}") @@ -67,9 +66,9 @@ LIBRARY DESTINATION "${libs_path}") else() install( - TARGETS draco_static - EXPORT dracoExport - DESTINATION "${libs_path}") + TARGETS draco_static + EXPORT dracoExport + DESTINATION "${libs_path}") if(BUILD_SHARED_LIBS) install( @@ -116,9 +115,7 @@ FILE draco-targets.cmake DESTINATION "${data_path}/cmake/draco") - install( - FILES - "${draco_build}/draco-config.cmake" - "${draco_build}/draco-config-version.cmake" - DESTINATION "${data_path}/cmake/draco") + install(FILES "${draco_build}/draco-config.cmake" + "${draco_build}/draco-config-version.cmake" + DESTINATION "${data_path}/cmake/draco") endmacro() diff -Nru draco-1.5.3+dfsg/cmake/draco_intrinsics.cmake draco-1.5.5+dfsg/cmake/draco_intrinsics.cmake --- draco-1.5.3+dfsg/cmake/draco_intrinsics.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/draco_intrinsics.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_DRACO_INTRINSICS_CMAKE_) return() @@ -75,17 +75,15 @@ unset(sse4_sources) list(APPEND sse4_sources ${arg_SOURCES}) - list(FILTER sse4_sources INCLUDE REGEX - "${draco_sse4_source_file_suffix}$") + list(FILTER sse4_sources INCLUDE REGEX "${draco_sse4_source_file_suffix}$") if(sse4_sources) unset(sse4_flags) - draco_get_intrinsics_flag_for_suffix(SUFFIX - ${draco_sse4_source_file_suffix} - VARIABLE sse4_flags) + draco_get_intrinsics_flag_for_suffix( + SUFFIX ${draco_sse4_source_file_suffix} VARIABLE sse4_flags) if(sse4_flags) draco_set_compiler_flags_for_sources(SOURCES ${sse4_sources} FLAGS - ${sse4_flags}) + ${sse4_flags}) endif() endif() endif() @@ -93,17 +91,15 @@ if(DRACO_ENABLE_NEON AND draco_have_neon) unset(neon_sources) list(APPEND neon_sources ${arg_SOURCES}) - list(FILTER neon_sources INCLUDE REGEX - "${draco_neon_source_file_suffix}$") + list(FILTER neon_sources INCLUDE REGEX "${draco_neon_source_file_suffix}$") if(neon_sources AND DRACO_NEON_INTRINSICS_FLAG) unset(neon_flags) - draco_get_intrinsics_flag_for_suffix(SUFFIX - ${draco_neon_source_file_suffix} - VARIABLE neon_flags) + draco_get_intrinsics_flag_for_suffix( + SUFFIX ${draco_neon_source_file_suffix} VARIABLE neon_flags) if(neon_flags) draco_set_compiler_flags_for_sources(SOURCES ${neon_sources} FLAGS - ${neon_flags}) + ${neon_flags}) endif() endif() endif() diff -Nru draco-1.5.3+dfsg/cmake/draco_options.cmake draco-1.5.5+dfsg/cmake/draco_options.cmake --- draco-1.5.3+dfsg/cmake/draco_options.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/draco_options.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_DRACO_OPTIONS_CMAKE_) return() @@ -32,17 +32,22 @@ cmake_parse_arguments(option "${optional_args}" "${single_value_args}" "${multi_value_args}" ${ARGN}) - if(NOT (option_NAME AND option_HELPSTRING AND DEFINED option_VALUE)) + if(NOT + (option_NAME + AND option_HELPSTRING + AND DEFINED option_VALUE)) message(FATAL_ERROR "draco_option: NAME HELPSTRING and VALUE required.") endif() option(${option_NAME} ${option_HELPSTRING} ${option_VALUE}) if(DRACO_VERBOSE GREATER 2) - message("--------- draco_option ---------\n" "option_NAME=${option_NAME}\n" - "option_HELPSTRING=${option_HELPSTRING}\n" - "option_VALUE=${option_VALUE}\n" - "------------------------------------------\n") + message( + "--------- draco_option ---------\n" + "option_NAME=${option_NAME}\n" + "option_HELPSTRING=${option_HELPSTRING}\n" + "option_VALUE=${option_VALUE}\n" + "------------------------------------------\n") endif() list(APPEND draco_options ${option_NAME}) @@ -58,37 +63,70 @@ # Set default options. macro(draco_set_default_options) - draco_option(NAME DRACO_FAST HELPSTRING "Try to build faster libs." VALUE OFF) - draco_option(NAME DRACO_JS_GLUE HELPSTRING - "Enable JS Glue and JS targets when using Emscripten." VALUE ON) - draco_option(NAME DRACO_IE_COMPATIBLE HELPSTRING - "Enable support for older IE builds when using Emscripten." VALUE - OFF) - draco_option(NAME DRACO_MESH_COMPRESSION HELPSTRING "Enable mesh compression." - VALUE ON) - draco_option(NAME DRACO_POINT_CLOUD_COMPRESSION HELPSTRING - "Enable point cloud compression." VALUE ON) - draco_option(NAME DRACO_PREDICTIVE_EDGEBREAKER HELPSTRING - "Enable predictive edgebreaker." VALUE ON) - draco_option(NAME DRACO_STANDARD_EDGEBREAKER HELPSTRING - "Enable stand edgebreaker." VALUE ON) - draco_option(NAME DRACO_BACKWARDS_COMPATIBILITY HELPSTRING - "Enable backwards compatibility." VALUE ON) - draco_option(NAME DRACO_DECODER_ATTRIBUTE_DEDUPLICATION HELPSTRING - "Enable attribute deduping." VALUE OFF) - draco_option(NAME DRACO_TESTS HELPSTRING "Enables tests." VALUE OFF) - draco_option(NAME DRACO_WASM HELPSTRING "Enables WASM support." VALUE OFF) - draco_option(NAME DRACO_UNITY_PLUGIN HELPSTRING - "Build plugin library for Unity." VALUE OFF) - draco_option(NAME DRACO_ANIMATION_ENCODING HELPSTRING "Enable animation." - VALUE OFF) - draco_option(NAME DRACO_GLTF_BITSTREAM HELPSTRING - "Draco GLTF extension bitstream specified features only." - VALUE OFF) - draco_option(NAME DRACO_MAYA_PLUGIN HELPSTRING - "Build plugin library for Maya." VALUE OFF) - draco_option(NAME DRACO_TRANSCODER_SUPPORTED HELPSTRING - "Enable the Draco transcoder." VALUE OFF) + draco_option( + NAME DRACO_FAST + HELPSTRING "Try to build faster libs." + VALUE OFF) + draco_option( + NAME DRACO_JS_GLUE + HELPSTRING "Enable JS Glue and JS targets when using Emscripten." + VALUE ON) + draco_option( + NAME DRACO_IE_COMPATIBLE + HELPSTRING "Enable support for older IE builds when using Emscripten." + VALUE OFF) + draco_option( + NAME DRACO_MESH_COMPRESSION + HELPSTRING "Enable mesh compression." + VALUE ON) + draco_option( + NAME DRACO_POINT_CLOUD_COMPRESSION + HELPSTRING "Enable point cloud compression." + VALUE ON) + draco_option( + NAME DRACO_PREDICTIVE_EDGEBREAKER + HELPSTRING "Enable predictive edgebreaker." + VALUE ON) + draco_option( + NAME DRACO_STANDARD_EDGEBREAKER + HELPSTRING "Enable stand edgebreaker." + VALUE ON) + draco_option( + NAME DRACO_BACKWARDS_COMPATIBILITY + HELPSTRING "Enable backwards compatibility." + VALUE ON) + draco_option( + NAME DRACO_DECODER_ATTRIBUTE_DEDUPLICATION + HELPSTRING "Enable attribute deduping." + VALUE OFF) + draco_option( + NAME DRACO_TESTS + HELPSTRING "Enables tests." + VALUE OFF) + draco_option( + NAME DRACO_WASM + HELPSTRING "Enables WASM support." + VALUE OFF) + draco_option( + NAME DRACO_UNITY_PLUGIN + HELPSTRING "Build plugin library for Unity." + VALUE OFF) + draco_option( + NAME DRACO_ANIMATION_ENCODING + HELPSTRING "Enable animation." + VALUE OFF) + draco_option( + NAME DRACO_GLTF_BITSTREAM + HELPSTRING "Draco GLTF extension bitstream specified features only." + VALUE OFF) + draco_option( + NAME DRACO_MAYA_PLUGIN + HELPSTRING "Build plugin library for Maya." + VALUE OFF) + draco_option( + NAME DRACO_TRANSCODER_SUPPORTED + HELPSTRING "Enable the Draco transcoder." + VALUE OFF) draco_check_deprecated_options() endmacro() diff -Nru draco-1.5.3+dfsg/cmake/draco_sanitizer.cmake draco-1.5.5+dfsg/cmake/draco_sanitizer.cmake --- draco-1.5.3+dfsg/cmake/draco_sanitizer.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/draco_sanitizer.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_DRACO_SANITIZER_CMAKE_) return() @@ -19,16 +19,18 @@ # Handles the details of enabling sanitizers. macro(draco_configure_sanitizer) - if(DRACO_SANITIZE AND NOT EMSCRIPTEN AND NOT MSVC) + if(DRACO_SANITIZE + AND NOT EMSCRIPTEN + AND NOT MSVC) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(DRACO_SANITIZE MATCHES "cfi") list(APPEND SAN_CXX_FLAGS "-flto" "-fno-sanitize-trap=cfi") list(APPEND SAN_LINKER_FLAGS "-flto" "-fno-sanitize-trap=cfi" - "-fuse-ld=gold") + "-fuse-ld=gold") endif() - if(${CMAKE_SIZEOF_VOID_P} EQUAL 4 - AND DRACO_SANITIZE MATCHES "integer|undefined") + if(${CMAKE_SIZEOF_VOID_P} EQUAL 4 AND DRACO_SANITIZE MATCHES + "integer|undefined") list(APPEND SAN_LINKER_FLAGS "--rtlib=compiler-rt" "-lgcc_s") endif() endif() diff -Nru draco-1.5.3+dfsg/cmake/draco_targets.cmake draco-1.5.5+dfsg/cmake/draco_targets.cmake --- draco-1.5.3+dfsg/cmake/draco_targets.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/draco_targets.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_DRACO_TARGETS_CMAKE_) return() @@ -65,26 +65,33 @@ unset(exe_LIB_DEPS) set(optional_args TEST) set(single_value_args NAME OUTPUT_NAME) - set(multi_value_args SOURCES DEFINES INCLUDES COMPILE_FLAGS LINK_FLAGS - OBJLIB_DEPS LIB_DEPS) + set(multi_value_args + SOURCES + DEFINES + INCLUDES + COMPILE_FLAGS + LINK_FLAGS + OBJLIB_DEPS + LIB_DEPS) cmake_parse_arguments(exe "${optional_args}" "${single_value_args}" "${multi_value_args}" ${ARGN}) if(DRACO_VERBOSE GREATER 1) - message("--------- draco_add_executable ---------\n" - "exe_TEST=${exe_TEST}\n" - "exe_TEST_DEFINES_MAIN=${exe_TEST_DEFINES_MAIN}\n" - "exe_NAME=${exe_NAME}\n" - "exe_OUTPUT_NAME=${exe_OUTPUT_NAME}\n" - "exe_SOURCES=${exe_SOURCES}\n" - "exe_DEFINES=${exe_DEFINES}\n" - "exe_INCLUDES=${exe_INCLUDES}\n" - "exe_COMPILE_FLAGS=${exe_COMPILE_FLAGS}\n" - "exe_LINK_FLAGS=${exe_LINK_FLAGS}\n" - "exe_OBJLIB_DEPS=${exe_OBJLIB_DEPS}\n" - "exe_LIB_DEPS=${exe_LIB_DEPS}\n" - "------------------------------------------\n") + message( + "--------- draco_add_executable ---------\n" + "exe_TEST=${exe_TEST}\n" + "exe_TEST_DEFINES_MAIN=${exe_TEST_DEFINES_MAIN}\n" + "exe_NAME=${exe_NAME}\n" + "exe_OUTPUT_NAME=${exe_OUTPUT_NAME}\n" + "exe_SOURCES=${exe_SOURCES}\n" + "exe_DEFINES=${exe_DEFINES}\n" + "exe_INCLUDES=${exe_INCLUDES}\n" + "exe_COMPILE_FLAGS=${exe_COMPILE_FLAGS}\n" + "exe_LINK_FLAGS=${exe_LINK_FLAGS}\n" + "exe_OBJLIB_DEPS=${exe_OBJLIB_DEPS}\n" + "exe_LIB_DEPS=${exe_LIB_DEPS}\n" + "------------------------------------------\n") endif() if(NOT (exe_NAME AND exe_SOURCES)) @@ -122,8 +129,8 @@ endif() if(exe_COMPILE_FLAGS OR DRACO_CXX_FLAGS) - target_compile_options(${exe_NAME} - PRIVATE ${exe_COMPILE_FLAGS} ${DRACO_CXX_FLAGS}) + target_compile_options(${exe_NAME} PRIVATE ${exe_COMPILE_FLAGS} + ${DRACO_CXX_FLAGS}) endif() if(exe_LINK_FLAGS OR DRACO_EXE_LINKER_FLAGS) @@ -131,8 +138,8 @@ list(APPEND exe_LINK_FLAGS "${DRACO_EXE_LINKER_FLAGS}") # LINK_FLAGS is managed as a string. draco_set_and_stringify(SOURCE "${exe_LINK_FLAGS}" DEST exe_LINK_FLAGS) - set_target_properties(${exe_NAME} - PROPERTIES LINK_FLAGS "${exe_LINK_FLAGS}") + set_target_properties(${exe_NAME} PROPERTIES LINK_FLAGS + "${exe_LINK_FLAGS}") else() target_link_options(${exe_NAME} PRIVATE ${exe_LINK_FLAGS} ${DRACO_EXE_LINKER_FLAGS}) @@ -227,27 +234,36 @@ unset(lib_TARGET_PROPERTIES) set(optional_args TEST) set(single_value_args NAME OUTPUT_NAME TYPE) - set(multi_value_args SOURCES DEFINES INCLUDES COMPILE_FLAGS LINK_FLAGS - OBJLIB_DEPS LIB_DEPS PUBLIC_INCLUDES TARGET_PROPERTIES) + set(multi_value_args + SOURCES + DEFINES + INCLUDES + COMPILE_FLAGS + LINK_FLAGS + OBJLIB_DEPS + LIB_DEPS + PUBLIC_INCLUDES + TARGET_PROPERTIES) cmake_parse_arguments(lib "${optional_args}" "${single_value_args}" "${multi_value_args}" ${ARGN}) if(DRACO_VERBOSE GREATER 1) - message("--------- draco_add_library ---------\n" - "lib_TEST=${lib_TEST}\n" - "lib_NAME=${lib_NAME}\n" - "lib_OUTPUT_NAME=${lib_OUTPUT_NAME}\n" - "lib_TYPE=${lib_TYPE}\n" - "lib_SOURCES=${lib_SOURCES}\n" - "lib_DEFINES=${lib_DEFINES}\n" - "lib_INCLUDES=${lib_INCLUDES}\n" - "lib_COMPILE_FLAGS=${lib_COMPILE_FLAGS}\n" - "lib_LINK_FLAGS=${lib_LINK_FLAGS}\n" - "lib_OBJLIB_DEPS=${lib_OBJLIB_DEPS}\n" - "lib_LIB_DEPS=${lib_LIB_DEPS}\n" - "lib_PUBLIC_INCLUDES=${lib_PUBLIC_INCLUDES}\n" - "---------------------------------------\n") + message( + "--------- draco_add_library ---------\n" + "lib_TEST=${lib_TEST}\n" + "lib_NAME=${lib_NAME}\n" + "lib_OUTPUT_NAME=${lib_OUTPUT_NAME}\n" + "lib_TYPE=${lib_TYPE}\n" + "lib_SOURCES=${lib_SOURCES}\n" + "lib_DEFINES=${lib_DEFINES}\n" + "lib_INCLUDES=${lib_INCLUDES}\n" + "lib_COMPILE_FLAGS=${lib_COMPILE_FLAGS}\n" + "lib_LINK_FLAGS=${lib_LINK_FLAGS}\n" + "lib_OBJLIB_DEPS=${lib_OBJLIB_DEPS}\n" + "lib_LIB_DEPS=${lib_LIB_DEPS}\n" + "lib_PUBLIC_INCLUDES=${lib_PUBLIC_INCLUDES}\n" + "---------------------------------------\n") endif() if(NOT (lib_NAME AND lib_TYPE)) @@ -282,8 +298,8 @@ if(lib_OUTPUT_NAME) if(NOT (BUILD_SHARED_LIBS AND MSVC)) - set_target_properties(${lib_NAME} - PROPERTIES OUTPUT_NAME ${lib_OUTPUT_NAME}) + set_target_properties(${lib_NAME} PROPERTIES OUTPUT_NAME + ${lib_OUTPUT_NAME}) endif() endif() @@ -300,8 +316,8 @@ endif() if(lib_COMPILE_FLAGS OR DRACO_CXX_FLAGS) - target_compile_options(${lib_NAME} - PRIVATE ${lib_COMPILE_FLAGS} ${DRACO_CXX_FLAGS}) + target_compile_options(${lib_NAME} PRIVATE ${lib_COMPILE_FLAGS} + ${DRACO_CXX_FLAGS}) endif() if(lib_LINK_FLAGS) @@ -343,9 +359,9 @@ if(NOT EMSCRIPTEN) # VERSION and SOVERSION as necessary if((lib_TYPE STREQUAL BUNDLE OR lib_TYPE STREQUAL SHARED) AND NOT MSVC) - set_target_properties(${lib_NAME} - PROPERTIES VERSION ${DRACO_SOVERSION} SOVERSION - ${DRACO_SOVERSION_MAJOR}) + set_target_properties( + ${lib_NAME} PROPERTIES VERSION ${DRACO_SOVERSION} + SOVERSION ${DRACO_SOVERSION_MAJOR}) endif() endif() diff -Nru draco-1.5.3+dfsg/cmake/draco_tests.cmake draco-1.5.5+dfsg/cmake/draco_tests.cmake --- draco-1.5.3+dfsg/cmake/draco_tests.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/draco_tests.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_DRACO_TESTS_CMAKE) return() @@ -26,73 +26,78 @@ list( APPEND - draco_test_sources - "${draco_src_root}/animation/keyframe_animation_encoding_test.cc" - "${draco_src_root}/animation/keyframe_animation_test.cc" - "${draco_src_root}/attributes/point_attribute_test.cc" - "${draco_src_root}/compression/attributes/point_d_vector_test.cc" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_test.cc" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_test.cc" - "${draco_src_root}/compression/attributes/sequential_integer_attribute_encoding_test.cc" - "${draco_src_root}/compression/bit_coders/rans_coding_test.cc" - "${draco_src_root}/compression/decode_test.cc" - "${draco_src_root}/compression/encode_test.cc" - "${draco_src_root}/compression/entropy/shannon_entropy_test.cc" - "${draco_src_root}/compression/entropy/symbol_coding_test.cc" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_encoding_test.cc" - "${draco_src_root}/compression/mesh/mesh_encoder_test.cc" - "${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_encoding_test.cc" - "${draco_src_root}/compression/point_cloud/point_cloud_sequential_encoding_test.cc" - "${draco_src_root}/core/buffer_bit_coding_test.cc" - "${draco_src_root}/core/draco_test_base.h" - "${draco_src_root}/core/draco_test_utils.cc" - "${draco_src_root}/core/draco_test_utils.h" - "${draco_src_root}/core/math_utils_test.cc" - "${draco_src_root}/core/quantization_utils_test.cc" - "${draco_src_root}/core/status.cc" - "${draco_src_root}/core/status_test.cc" - "${draco_src_root}/core/vector_d_test.cc" - "${draco_src_root}/io/file_reader_test_common.h" - "${draco_src_root}/io/file_utils_test.cc" - "${draco_src_root}/io/file_writer_utils_test.cc" - "${draco_src_root}/io/stdio_file_reader_test.cc" - "${draco_src_root}/io/stdio_file_writer_test.cc" - "${draco_src_root}/io/obj_decoder_test.cc" - "${draco_src_root}/io/obj_encoder_test.cc" - "${draco_src_root}/io/ply_decoder_test.cc" - "${draco_src_root}/io/ply_reader_test.cc" - "${draco_src_root}/io/stl_decoder_test.cc" - "${draco_src_root}/io/stl_encoder_test.cc" - "${draco_src_root}/io/point_cloud_io_test.cc" - "${draco_src_root}/mesh/mesh_are_equivalent_test.cc" - "${draco_src_root}/mesh/mesh_cleanup_test.cc" - "${draco_src_root}/mesh/triangle_soup_mesh_builder_test.cc" - "${draco_src_root}/metadata/metadata_encoder_test.cc" - "${draco_src_root}/metadata/metadata_test.cc" - "${draco_src_root}/point_cloud/point_cloud_builder_test.cc" - "${draco_src_root}/point_cloud/point_cloud_test.cc") + draco_test_sources + "${draco_src_root}/animation/keyframe_animation_encoding_test.cc" + "${draco_src_root}/animation/keyframe_animation_test.cc" + "${draco_src_root}/attributes/point_attribute_test.cc" + "${draco_src_root}/compression/attributes/point_d_vector_test.cc" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_test.cc" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_test.cc" + "${draco_src_root}/compression/attributes/sequential_integer_attribute_encoding_test.cc" + "${draco_src_root}/compression/bit_coders/rans_coding_test.cc" + "${draco_src_root}/compression/decode_test.cc" + "${draco_src_root}/compression/encode_test.cc" + "${draco_src_root}/compression/entropy/shannon_entropy_test.cc" + "${draco_src_root}/compression/entropy/symbol_coding_test.cc" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_encoding_test.cc" + "${draco_src_root}/compression/mesh/mesh_encoder_test.cc" + "${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_encoding_test.cc" + "${draco_src_root}/compression/point_cloud/point_cloud_sequential_encoding_test.cc" + "${draco_src_root}/core/buffer_bit_coding_test.cc" + "${draco_src_root}/core/draco_test_base.h" + "${draco_src_root}/core/draco_test_utils.cc" + "${draco_src_root}/core/draco_test_utils.h" + "${draco_src_root}/core/math_utils_test.cc" + "${draco_src_root}/core/quantization_utils_test.cc" + "${draco_src_root}/core/status.cc" + "${draco_src_root}/core/status_test.cc" + "${draco_src_root}/core/vector_d_test.cc" + "${draco_src_root}/io/file_reader_test_common.h" + "${draco_src_root}/io/file_utils_test.cc" + "${draco_src_root}/io/file_writer_utils_test.cc" + "${draco_src_root}/io/stdio_file_reader_test.cc" + "${draco_src_root}/io/stdio_file_writer_test.cc" + "${draco_src_root}/io/obj_decoder_test.cc" + "${draco_src_root}/io/obj_encoder_test.cc" + "${draco_src_root}/io/ply_decoder_test.cc" + "${draco_src_root}/io/ply_reader_test.cc" + "${draco_src_root}/io/stl_decoder_test.cc" + "${draco_src_root}/io/stl_encoder_test.cc" + "${draco_src_root}/io/point_cloud_io_test.cc" + "${draco_src_root}/mesh/mesh_are_equivalent_test.cc" + "${draco_src_root}/mesh/mesh_cleanup_test.cc" + "${draco_src_root}/mesh/triangle_soup_mesh_builder_test.cc" + "${draco_src_root}/metadata/metadata_encoder_test.cc" + "${draco_src_root}/metadata/metadata_test.cc" + "${draco_src_root}/point_cloud/point_cloud_builder_test.cc" + "${draco_src_root}/point_cloud/point_cloud_test.cc") if(DRACO_TRANSCODER_SUPPORTED) list( APPEND - draco_test_sources - "${draco_src_root}/animation/animation_test.cc" - "${draco_src_root}/io/gltf_decoder_test.cc" - "${draco_src_root}/io/gltf_encoder_test.cc" - "${draco_src_root}/io/gltf_utils_test.cc" - "${draco_src_root}/io/scene_io_test.cc" - "${draco_src_root}/io/texture_io_test.cc" - "${draco_src_root}/material/material_library_test.cc" - "${draco_src_root}/material/material_test.cc" - "${draco_src_root}/scene/instance_array_test.cc" - "${draco_src_root}/scene/light_test.cc" - "${draco_src_root}/scene/mesh_group_test.cc" - "${draco_src_root}/scene/scene_test.cc" - "${draco_src_root}/scene/scene_utils_test.cc" - "${draco_src_root}/scene/trs_matrix_test.cc" - "${draco_src_root}/texture/texture_library_test.cc" - "${draco_src_root}/texture/texture_map_test.cc" - "${draco_src_root}/texture/texture_transform_test.cc") + draco_test_sources + "${draco_src_root}/animation/animation_test.cc" + "${draco_src_root}/io/gltf_decoder_test.cc" + "${draco_src_root}/io/gltf_encoder_test.cc" + "${draco_src_root}/io/gltf_utils_test.cc" + "${draco_src_root}/io/gltf_test_helper.cc" + "${draco_src_root}/io/gltf_test_helper.h" + "${draco_src_root}/io/scene_io_test.cc" + "${draco_src_root}/io/texture_io_test.cc" + "${draco_src_root}/material/material_library_test.cc" + "${draco_src_root}/material/material_test.cc" + "${draco_src_root}/metadata/property_table_test.cc" + "${draco_src_root}/metadata/structural_metadata_test.cc" + "${draco_src_root}/scene/instance_array_test.cc" + "${draco_src_root}/scene/light_test.cc" + "${draco_src_root}/scene/mesh_group_test.cc" + "${draco_src_root}/scene/scene_test.cc" + "${draco_src_root}/scene/scene_are_equivalent_test.cc" + "${draco_src_root}/scene/scene_utils_test.cc" + "${draco_src_root}/scene/trs_matrix_test.cc" + "${draco_src_root}/texture/texture_library_test.cc" + "${draco_src_root}/texture/texture_map_test.cc" + "${draco_src_root}/texture/texture_transform_test.cc") endif() macro(draco_setup_test_targets) @@ -100,37 +105,26 @@ draco_setup_googletest() if(NOT (EXISTS ${draco_gtest_all} AND EXISTS ${draco_gtest_main})) - message(FATAL_ERROR - "googletest missing, run git submodule update --init") + message(FATAL_ERROR "googletest missing, run git submodule update --init") endif() list(APPEND draco_test_defines GTEST_HAS_PTHREAD=0) - draco_add_library(TEST - NAME - draco_gtest - TYPE - STATIC - SOURCES - ${draco_gtest_all} - DEFINES - ${draco_defines} - ${draco_test_defines} - INCLUDES - ${draco_test_include_paths}) - - draco_add_library(TEST - NAME - draco_gtest_main - TYPE - STATIC - SOURCES - ${draco_gtest_main} - DEFINES - ${draco_defines} - ${draco_test_defines} - INCLUDES - ${draco_test_include_paths}) + draco_add_library( + TEST + NAME draco_gtest + TYPE STATIC + SOURCES ${draco_gtest_all} + DEFINES ${draco_defines} ${draco_test_defines} + INCLUDES ${draco_test_include_paths}) + + draco_add_library( + TEST + NAME draco_gtest_main + TYPE STATIC + SOURCES ${draco_gtest_main} + DEFINES ${draco_defines} ${draco_test_defines} + INCLUDES ${draco_test_include_paths}) set(DRACO_TEST_DATA_DIR "${draco_root}/testdata") set(DRACO_TEST_TEMP_DIR "${draco_build}/draco_test_temp") @@ -141,32 +135,18 @@ "${draco_build}/testing/draco_test_config.h") # Create the test targets. - draco_add_executable(NAME - draco_tests - SOURCES - ${draco_test_sources} - DEFINES - ${draco_defines} - ${draco_test_defines} - INCLUDES - ${draco_test_include_paths} - LIB_DEPS - ${draco_dependency} - draco_gtest - draco_gtest_main) - - draco_add_executable(NAME - draco_factory_tests - SOURCES - ${draco_factory_test_sources} - DEFINES - ${draco_defines} - ${draco_test_defines} - INCLUDES - ${draco_test_include_paths} - LIB_DEPS - ${draco_dependency} - draco_gtest - draco_gtest_main) + draco_add_executable( + NAME draco_tests + SOURCES ${draco_test_sources} + DEFINES ${draco_defines} ${draco_test_defines} + INCLUDES ${draco_test_include_paths} + LIB_DEPS ${draco_dependency} draco_gtest draco_gtest_main) + + draco_add_executable( + NAME draco_factory_tests + SOURCES ${draco_factory_test_sources} + DEFINES ${draco_defines} ${draco_test_defines} + INCLUDES ${draco_test_include_paths} + LIB_DEPS ${draco_dependency} draco_gtest draco_gtest_main) endif() endmacro() diff -Nru draco-1.5.3+dfsg/cmake/draco_variables.cmake draco-1.5.5+dfsg/cmake/draco_variables.cmake --- draco-1.5.3+dfsg/cmake/draco_variables.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/draco_variables.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_DRACO_VARIABLES_CMAKE_) return() @@ -28,8 +28,7 @@ if("${${variable_name}}" STREQUAL "") message( - FATAL_ERROR - "Empty variable ${variable_name} is required to build draco.") + FATAL_ERROR "Empty variable ${variable_name} is required to build draco.") endif() if(NOT IS_DIRECTORY "${${variable_name}}") @@ -56,13 +55,16 @@ macro(draco_dump_cmake_flag_variables) unset(flag_variables) list(APPEND flag_variables "CMAKE_CXX_FLAGS_INIT" "CMAKE_CXX_FLAGS" - "CMAKE_EXE_LINKER_FLAGS_INIT" "CMAKE_EXE_LINKER_FLAGS") + "CMAKE_EXE_LINKER_FLAGS_INIT" "CMAKE_EXE_LINKER_FLAGS") if(CMAKE_BUILD_TYPE) - list(APPEND flag_variables "CMAKE_BUILD_TYPE" - "CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}_INIT" - "CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}" - "CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE}_INIT" - "CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE}") + list( + APPEND + flag_variables + "CMAKE_BUILD_TYPE" + "CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}_INIT" + "CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}" + "CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE}_INIT" + "CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE}") endif() foreach(flag_variable ${flag_variables}) message("${flag_variable}:${${flag_variable}}") diff -Nru draco-1.5.3+dfsg/cmake/toolchains/aarch64-linux-gnu.cmake draco-1.5.5+dfsg/cmake/toolchains/aarch64-linux-gnu.cmake --- draco-1.5.3+dfsg/cmake/toolchains/aarch64-linux-gnu.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/aarch64-linux-gnu.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_AARCH64_LINUX_GNU_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/android.cmake draco-1.5.5+dfsg/cmake/toolchains/android.cmake --- draco-1.5.3+dfsg/cmake/toolchains/android.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/android.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_ANDROID_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/android-ndk-common.cmake draco-1.5.5+dfsg/cmake/toolchains/android-ndk-common.cmake --- draco-1.5.3+dfsg/cmake/toolchains/android-ndk-common.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/android-ndk-common.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_ANDROID_NDK_COMMON_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/arm64-android-ndk-libcpp.cmake draco-1.5.5+dfsg/cmake/toolchains/arm64-android-ndk-libcpp.cmake --- draco-1.5.3+dfsg/cmake/toolchains/arm64-android-ndk-libcpp.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/arm64-android-ndk-libcpp.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_ARM64_ANDROID_NDK_LIBCPP_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/arm64-ios.cmake draco-1.5.5+dfsg/cmake/toolchains/arm64-ios.cmake --- draco-1.5.3+dfsg/cmake/toolchains/arm64-ios.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/arm64-ios.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_ARM64_IOS_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/arm64-linux-gcc.cmake draco-1.5.5+dfsg/cmake/toolchains/arm64-linux-gcc.cmake --- draco-1.5.3+dfsg/cmake/toolchains/arm64-linux-gcc.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/arm64-linux-gcc.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_ARM64_LINUX_GCC_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/arm-ios-common.cmake draco-1.5.5+dfsg/cmake/toolchains/arm-ios-common.cmake --- draco-1.5.3+dfsg/cmake/toolchains/arm-ios-common.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/arm-ios-common.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_ARM_IOS_COMMON_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/arm-linux-gnueabihf.cmake draco-1.5.5+dfsg/cmake/toolchains/arm-linux-gnueabihf.cmake --- draco-1.5.3+dfsg/cmake/toolchains/arm-linux-gnueabihf.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/arm-linux-gnueabihf.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_ARM_LINUX_GNUEABIHF_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/armv7-android-ndk-libcpp.cmake draco-1.5.5+dfsg/cmake/toolchains/armv7-android-ndk-libcpp.cmake --- draco-1.5.3+dfsg/cmake/toolchains/armv7-android-ndk-libcpp.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/armv7-android-ndk-libcpp.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_ARMV7_ANDROID_NDK_LIBCPP_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/armv7-ios.cmake draco-1.5.5+dfsg/cmake/toolchains/armv7-ios.cmake --- draco-1.5.3+dfsg/cmake/toolchains/armv7-ios.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/armv7-ios.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_ARMV7_IOS_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/armv7-linux-gcc.cmake draco-1.5.5+dfsg/cmake/toolchains/armv7-linux-gcc.cmake --- draco-1.5.3+dfsg/cmake/toolchains/armv7-linux-gcc.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/armv7-linux-gcc.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_ARMV7_LINUX_GCC_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/armv7s-ios.cmake draco-1.5.5+dfsg/cmake/toolchains/armv7s-ios.cmake --- draco-1.5.3+dfsg/cmake/toolchains/armv7s-ios.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/armv7s-ios.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_ARMV7S_IOS_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/i386-ios.cmake draco-1.5.5+dfsg/cmake/toolchains/i386-ios.cmake --- draco-1.5.3+dfsg/cmake/toolchains/i386-ios.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/i386-ios.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_i386_IOS_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/x86_64-android-ndk-libcpp.cmake draco-1.5.5+dfsg/cmake/toolchains/x86_64-android-ndk-libcpp.cmake --- draco-1.5.3+dfsg/cmake/toolchains/x86_64-android-ndk-libcpp.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/x86_64-android-ndk-libcpp.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_X86_64_ANDROID_NDK_LIBCPP_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/x86_64-ios.cmake draco-1.5.5+dfsg/cmake/toolchains/x86_64-ios.cmake --- draco-1.5.3+dfsg/cmake/toolchains/x86_64-ios.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/x86_64-ios.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_X86_64_IOS_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/cmake/toolchains/x86-android-ndk-libcpp.cmake draco-1.5.5+dfsg/cmake/toolchains/x86-android-ndk-libcpp.cmake --- draco-1.5.3+dfsg/cmake/toolchains/x86-android-ndk-libcpp.cmake 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/cmake/toolchains/x86-android-ndk-libcpp.cmake 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. if(DRACO_CMAKE_TOOLCHAINS_X86_ANDROID_NDK_LIBCPP_CMAKE_) return() diff -Nru draco-1.5.3+dfsg/.cmake-format.py draco-1.5.5+dfsg/.cmake-format.py --- draco-1.5.3+dfsg/.cmake-format.py 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/.cmake-format.py 2022-10-29 00:55:03.000000000 +0000 @@ -1,102 +1,107 @@ -# Generated with cmake-format 0.5.1 -# How wide to allow formatted cmake files -line_width = 80 - -# How many spaces to tab for indent -tab_size = 2 - -# If arglists are longer than this, break them always -max_subargs_per_line = 10 - -# If true, separate flow control names from their parentheses with a space -separate_ctrl_name_with_space = False - -# If true, separate function names from parentheses with a space -separate_fn_name_with_space = False - -# If a statement is wrapped to more than one line, than dangle the closing -# parenthesis on its own line -dangle_parens = False - -# What character to use for bulleted lists -bullet_char = '*' - -# What character to use as punctuation after numerals in an enumerated list -enum_char = '.' - -# What style line endings to use in the output. -line_ending = u'unix' - -# Format command names consistently as 'lower' or 'upper' case -command_case = u'lower' - -# Format keywords consistently as 'lower' or 'upper' case -keyword_case = u'unchanged' - -# Specify structure for custom cmake functions -additional_commands = { - "foo": { - "flags": [ - "BAR", - "BAZ" - ], - "kwargs": { - "HEADERS": "*", - "DEPENDS": "*", - "SOURCES": "*" - } +with section('parse'): + # Specify structure for custom cmake functions + additional_commands = { + 'draco_add_emscripten_executable': { + 'kwargs': { + 'NAME': '*', + 'SOURCES': '*', + 'OUTPUT_NAME': '*', + 'DEFINES': '*', + 'INCLUDES': '*', + 'COMPILE_FLAGS': '*', + 'LINK_FLAGS': '*', + 'OBJLIB_DEPS': '*', + 'LIB_DEPS': '*', + 'GLUE_PATH': '*', + 'PRE_LINK_JS_SOURCES': '*', + 'POST_LINK_JS_SOURCES': '*', + 'FEATURES': '*', + }, + 'pargs': 0 + }, + 'draco_add_executable': { + 'kwargs': { + 'NAME': '*', + 'SOURCES': '*', + 'OUTPUT_NAME': '*', + 'TEST': 0, + 'DEFINES': '*', + 'INCLUDES': '*', + 'COMPILE_FLAGS': '*', + 'LINK_FLAGS': '*', + 'OBJLIB_DEPS': '*', + 'LIB_DEPS': '*', + }, + 'pargs': 0 + }, + 'draco_add_library': { + 'kwargs': { + 'NAME': '*', + 'TYPE': '*', + 'SOURCES': '*', + 'TEST': 0, + 'OUTPUT_NAME': '*', + 'DEFINES': '*', + 'INCLUDES': '*', + 'COMPILE_FLAGS': '*', + 'LINK_FLAGS': '*', + 'OBJLIB_DEPS': '*', + 'LIB_DEPS': '*', + 'PUBLIC_INCLUDES': '*', + }, + 'pargs': 0 + }, + 'draco_generate_emscripten_glue': { + 'kwargs': { + 'INPUT_IDL': '*', + 'OUTPUT_PATH': '*', + }, + 'pargs': 0 + }, + 'draco_get_required_emscripten_flags': { + 'kwargs': { + 'FLAG_LIST_VAR_COMPILER': '*', + 'FLAG_LIST_VAR_LINKER': '*', + }, + 'pargs': 0 + }, + 'draco_option': { + 'kwargs': { + 'NAME': '*', + 'HELPSTRING': '*', + 'VALUE': '*', + }, + 'pargs': 0 + }, } -} -# A list of command names which should always be wrapped -always_wrap = [] +with section('format'): + # Formatting options. -# Specify the order of wrapping algorithms during successive reflow attempts -algorithm_order = [0, 1, 2, 3, 4] + # How wide to allow formatted cmake files + line_width = 80 -# If true, the argument lists which are known to be sortable will be sorted -# lexicographicall -autosort = False + # How many spaces to tab for indent + tab_size = 2 -# enable comment markup parsing and reflow -enable_markup = True + # If true, separate flow control names from their parentheses with a space + separate_ctrl_name_with_space = False -# If comment markup is enabled, don't reflow the first comment block in -# eachlistfile. Use this to preserve formatting of your -# copyright/licensestatements. -first_comment_is_literal = True + # If true, separate function names from parentheses with a space + separate_fn_name_with_space = False -# If comment markup is enabled, don't reflow any comment block which matchesthis -# (regex) pattern. Default is `None` (disabled). -literal_comment_pattern = None + # If a statement is wrapped to more than one line, than dangle the closing + # parenthesis on its own line. + dangle_parens = False -# Regular expression to match preformat fences in comments -# default=r'^\s*([`~]{3}[`~]*)(.*)$' -fence_pattern = u'^\\s*([`~]{3}[`~]*)(.*)$' + # Do not sort argument lists. + enable_sort = False -# Regular expression to match rulers in comments -# default=r'^\s*[^\w\s]{3}.*[^\w\s]{3}$' -ruler_pattern = u'^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$' + # What style line endings to use in the output. + line_ending = 'unix' -# If true, emit the unicode byte-order mark (BOM) at the start of the file -emit_byteorder_mark = False + # Format command names consistently as 'lower' or 'upper' case + command_case = 'canonical' -# If a comment line starts with at least this many consecutive hash characters, -# then don't lstrip() them off. This allows for lazy hash rulers where the first -# hash char is not separated by space -hashruler_min_length = 10 - -# If true, then insert a space between the first hash char and remaining hash -# chars in a hash ruler, and normalize its length to fill the column -canonicalize_hashrulers = True - -# Specify the encoding of the input file. Defaults to utf-8. -input_encoding = u'utf-8' - -# Specify the encoding of the output file. Defaults to utf-8. Note that cmake -# only claims to support utf-8 so be careful when using anything else -output_encoding = u'utf-8' - -# A dictionary containing any per-command configuration overrides. Currently -# only `command_case` is supported. -per_command = {} + # Format keywords consistently as 'lower' or 'upper' case + keyword_case = 'upper' diff -Nru draco-1.5.3+dfsg/CMakeLists.txt draco-1.5.5+dfsg/CMakeLists.txt --- draco-1.5.3+dfsg/CMakeLists.txt 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/CMakeLists.txt 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2021 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. cmake_minimum_required(VERSION 3.12 FATAL_ERROR) project(draco C CXX) @@ -25,11 +25,13 @@ if("${draco_root}" STREQUAL "${draco_build}") message( - FATAL_ERROR "Building from within the Draco source tree is not supported.\n" - "Hint: Run these commands\n" - "$ rm -rf CMakeCache.txt CMakeFiles\n" - "$ mkdir -p ../draco_build\n" "$ cd ../draco_build\n" - "And re-run CMake from the draco_build directory.") + FATAL_ERROR + "Building from within the Draco source tree is not supported.\n" + "Hint: Run these commands\n" + "$ rm -rf CMakeCache.txt CMakeFiles\n" + "$ mkdir -p ../draco_build\n" + "$ cd ../draco_build\n" + "And re-run CMake from the draco_build directory.") endif() include(FindPythonInterp) @@ -83,446 +85,475 @@ draco_generate_features_h() # Draco source file listing variables. -list(APPEND draco_attributes_sources - "${draco_src_root}/attributes/attribute_octahedron_transform.cc" - "${draco_src_root}/attributes/attribute_octahedron_transform.h" - "${draco_src_root}/attributes/attribute_quantization_transform.cc" - "${draco_src_root}/attributes/attribute_quantization_transform.h" - "${draco_src_root}/attributes/attribute_transform.cc" - "${draco_src_root}/attributes/attribute_transform.h" - "${draco_src_root}/attributes/attribute_transform_data.h" - "${draco_src_root}/attributes/attribute_transform_type.h" - "${draco_src_root}/attributes/geometry_attribute.cc" - "${draco_src_root}/attributes/geometry_attribute.h" - "${draco_src_root}/attributes/geometry_indices.h" - "${draco_src_root}/attributes/point_attribute.cc" - "${draco_src_root}/attributes/point_attribute.h") - -list( - APPEND - draco_compression_attributes_dec_sources - "${draco_src_root}/compression/attributes/attributes_decoder.cc" - "${draco_src_root}/compression/attributes/attributes_decoder.h" - "${draco_src_root}/compression/attributes/kd_tree_attributes_decoder.cc" - "${draco_src_root}/compression/attributes/kd_tree_attributes_decoder.h" - "${draco_src_root}/compression/attributes/kd_tree_attributes_shared.h" - "${draco_src_root}/compression/attributes/mesh_attribute_indices_encoding_data.h" - "${draco_src_root}/compression/attributes/normal_compression_utils.h" - "${draco_src_root}/compression/attributes/point_d_vector.h" - "${draco_src_root}/compression/attributes/sequential_attribute_decoder.cc" - "${draco_src_root}/compression/attributes/sequential_attribute_decoder.h" - "${draco_src_root}/compression/attributes/sequential_attribute_decoders_controller.cc" - "${draco_src_root}/compression/attributes/sequential_attribute_decoders_controller.h" - "${draco_src_root}/compression/attributes/sequential_integer_attribute_decoder.cc" - "${draco_src_root}/compression/attributes/sequential_integer_attribute_decoder.h" - "${draco_src_root}/compression/attributes/sequential_normal_attribute_decoder.cc" - "${draco_src_root}/compression/attributes/sequential_normal_attribute_decoder.h" - "${draco_src_root}/compression/attributes/sequential_quantization_attribute_decoder.cc" - "${draco_src_root}/compression/attributes/sequential_quantization_attribute_decoder.h" - ) - -list( - APPEND - draco_compression_attributes_enc_sources - "${draco_src_root}/compression/attributes/attributes_encoder.cc" - "${draco_src_root}/compression/attributes/attributes_encoder.h" - "${draco_src_root}/compression/attributes/kd_tree_attributes_encoder.cc" - "${draco_src_root}/compression/attributes/kd_tree_attributes_encoder.h" - "${draco_src_root}/compression/attributes/linear_sequencer.h" - "${draco_src_root}/compression/attributes/points_sequencer.h" - "${draco_src_root}/compression/attributes/sequential_attribute_encoder.cc" - "${draco_src_root}/compression/attributes/sequential_attribute_encoder.h" - "${draco_src_root}/compression/attributes/sequential_attribute_encoders_controller.cc" - "${draco_src_root}/compression/attributes/sequential_attribute_encoders_controller.h" - "${draco_src_root}/compression/attributes/sequential_integer_attribute_encoder.cc" - "${draco_src_root}/compression/attributes/sequential_integer_attribute_encoder.h" - "${draco_src_root}/compression/attributes/sequential_normal_attribute_encoder.cc" - "${draco_src_root}/compression/attributes/sequential_normal_attribute_encoder.h" - "${draco_src_root}/compression/attributes/sequential_quantization_attribute_encoder.cc" - "${draco_src_root}/compression/attributes/sequential_quantization_attribute_encoder.h" - ) - - -list( - APPEND - draco_compression_attributes_pred_schemes_dec_sources - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_decoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_shared.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_data.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_decoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_area.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_base.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_decoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_encoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_decoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_decoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_decoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_decoder_factory.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_decoder_interface.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_decoding_transform.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_delta_decoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_factory.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_interface.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_decoding_transform.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_decoding_transform.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_base.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_wrap_decoding_transform.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_wrap_transform_base.h" - ) - -list( - APPEND - draco_compression_attributes_pred_schemes_enc_sources - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_shared.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_data.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_encoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_encoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_area.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_base.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_encoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_encoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_encoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_encoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_delta_encoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoder.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoder_interface.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoding_transform.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_factory.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_interface.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_encoding_transform.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_encoding_transform.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_base.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_wrap_encoding_transform.h" - "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_wrap_transform_base.h" - ) - -list( - APPEND - draco_compression_bit_coders_sources - "${draco_src_root}/compression/bit_coders/adaptive_rans_bit_coding_shared.h" - "${draco_src_root}/compression/bit_coders/adaptive_rans_bit_decoder.cc" - "${draco_src_root}/compression/bit_coders/adaptive_rans_bit_decoder.h" - "${draco_src_root}/compression/bit_coders/adaptive_rans_bit_encoder.cc" - "${draco_src_root}/compression/bit_coders/adaptive_rans_bit_encoder.h" - "${draco_src_root}/compression/bit_coders/direct_bit_decoder.cc" - "${draco_src_root}/compression/bit_coders/direct_bit_decoder.h" - "${draco_src_root}/compression/bit_coders/direct_bit_encoder.cc" - "${draco_src_root}/compression/bit_coders/direct_bit_encoder.h" - "${draco_src_root}/compression/bit_coders/folded_integer_bit_decoder.h" - "${draco_src_root}/compression/bit_coders/folded_integer_bit_encoder.h" - "${draco_src_root}/compression/bit_coders/rans_bit_decoder.cc" - "${draco_src_root}/compression/bit_coders/rans_bit_decoder.h" - "${draco_src_root}/compression/bit_coders/rans_bit_encoder.cc" - "${draco_src_root}/compression/bit_coders/rans_bit_encoder.h" - "${draco_src_root}/compression/bit_coders/symbol_bit_decoder.cc" - "${draco_src_root}/compression/bit_coders/symbol_bit_decoder.h" - "${draco_src_root}/compression/bit_coders/symbol_bit_encoder.cc" - "${draco_src_root}/compression/bit_coders/symbol_bit_encoder.h") - -list(APPEND draco_enc_config_sources - "${draco_src_root}/compression/config/compression_shared.h" - "${draco_src_root}/compression/config/draco_options.h" - "${draco_src_root}/compression/config/encoder_options.h" - "${draco_src_root}/compression/config/encoding_features.h") - -list(APPEND draco_dec_config_sources - "${draco_src_root}/compression/config/compression_shared.h" - "${draco_src_root}/compression/config/decoder_options.h" - "${draco_src_root}/compression/config/draco_options.h") +list( + APPEND + draco_attributes_sources + "${draco_src_root}/attributes/attribute_octahedron_transform.cc" + "${draco_src_root}/attributes/attribute_octahedron_transform.h" + "${draco_src_root}/attributes/attribute_quantization_transform.cc" + "${draco_src_root}/attributes/attribute_quantization_transform.h" + "${draco_src_root}/attributes/attribute_transform.cc" + "${draco_src_root}/attributes/attribute_transform.h" + "${draco_src_root}/attributes/attribute_transform_data.h" + "${draco_src_root}/attributes/attribute_transform_type.h" + "${draco_src_root}/attributes/geometry_attribute.cc" + "${draco_src_root}/attributes/geometry_attribute.h" + "${draco_src_root}/attributes/geometry_indices.h" + "${draco_src_root}/attributes/point_attribute.cc" + "${draco_src_root}/attributes/point_attribute.h") + +list( + APPEND + draco_compression_attributes_dec_sources + "${draco_src_root}/compression/attributes/attributes_decoder.cc" + "${draco_src_root}/compression/attributes/attributes_decoder.h" + "${draco_src_root}/compression/attributes/attributes_decoder_interface.h" + "${draco_src_root}/compression/attributes/kd_tree_attributes_decoder.cc" + "${draco_src_root}/compression/attributes/kd_tree_attributes_decoder.h" + "${draco_src_root}/compression/attributes/kd_tree_attributes_shared.h" + "${draco_src_root}/compression/attributes/mesh_attribute_indices_encoding_data.h" + "${draco_src_root}/compression/attributes/normal_compression_utils.h" + "${draco_src_root}/compression/attributes/point_d_vector.h" + "${draco_src_root}/compression/attributes/sequential_attribute_decoder.cc" + "${draco_src_root}/compression/attributes/sequential_attribute_decoder.h" + "${draco_src_root}/compression/attributes/sequential_attribute_decoders_controller.cc" + "${draco_src_root}/compression/attributes/sequential_attribute_decoders_controller.h" + "${draco_src_root}/compression/attributes/sequential_integer_attribute_decoder.cc" + "${draco_src_root}/compression/attributes/sequential_integer_attribute_decoder.h" + "${draco_src_root}/compression/attributes/sequential_normal_attribute_decoder.cc" + "${draco_src_root}/compression/attributes/sequential_normal_attribute_decoder.h" + "${draco_src_root}/compression/attributes/sequential_quantization_attribute_decoder.cc" + "${draco_src_root}/compression/attributes/sequential_quantization_attribute_decoder.h" +) + +list( + APPEND + draco_compression_attributes_enc_sources + "${draco_src_root}/compression/attributes/attributes_encoder.cc" + "${draco_src_root}/compression/attributes/attributes_encoder.h" + "${draco_src_root}/compression/attributes/kd_tree_attributes_encoder.cc" + "${draco_src_root}/compression/attributes/kd_tree_attributes_encoder.h" + "${draco_src_root}/compression/attributes/linear_sequencer.h" + "${draco_src_root}/compression/attributes/points_sequencer.h" + "${draco_src_root}/compression/attributes/sequential_attribute_encoder.cc" + "${draco_src_root}/compression/attributes/sequential_attribute_encoder.h" + "${draco_src_root}/compression/attributes/sequential_attribute_encoders_controller.cc" + "${draco_src_root}/compression/attributes/sequential_attribute_encoders_controller.h" + "${draco_src_root}/compression/attributes/sequential_integer_attribute_encoder.cc" + "${draco_src_root}/compression/attributes/sequential_integer_attribute_encoder.h" + "${draco_src_root}/compression/attributes/sequential_normal_attribute_encoder.cc" + "${draco_src_root}/compression/attributes/sequential_normal_attribute_encoder.h" + "${draco_src_root}/compression/attributes/sequential_quantization_attribute_encoder.cc" + "${draco_src_root}/compression/attributes/sequential_quantization_attribute_encoder.h" +) + + +list( + APPEND + draco_compression_attributes_pred_schemes_dec_sources + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_decoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_shared.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_data.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_decoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_area.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_base.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_decoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_encoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_decoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_decoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_decoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_decoder_factory.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_decoder_interface.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_decoding_transform.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_delta_decoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_factory.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_interface.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_decoding_transform.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_decoding_transform.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_base.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_wrap_decoding_transform.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_wrap_transform_base.h" +) + +list( + APPEND + draco_compression_attributes_pred_schemes_enc_sources + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_shared.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_data.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_encoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_encoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_area.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_base.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_encoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_encoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_encoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_encoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_delta_encoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoder.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoder_interface.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_encoding_transform.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_factory.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_interface.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_encoding_transform.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_encoding_transform.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_base.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_wrap_encoding_transform.h" + "${draco_src_root}/compression/attributes/prediction_schemes/prediction_scheme_wrap_transform_base.h" +) + +list( + APPEND + draco_compression_bit_coders_sources + "${draco_src_root}/compression/bit_coders/adaptive_rans_bit_coding_shared.h" + "${draco_src_root}/compression/bit_coders/adaptive_rans_bit_decoder.cc" + "${draco_src_root}/compression/bit_coders/adaptive_rans_bit_decoder.h" + "${draco_src_root}/compression/bit_coders/adaptive_rans_bit_encoder.cc" + "${draco_src_root}/compression/bit_coders/adaptive_rans_bit_encoder.h" + "${draco_src_root}/compression/bit_coders/direct_bit_decoder.cc" + "${draco_src_root}/compression/bit_coders/direct_bit_decoder.h" + "${draco_src_root}/compression/bit_coders/direct_bit_encoder.cc" + "${draco_src_root}/compression/bit_coders/direct_bit_encoder.h" + "${draco_src_root}/compression/bit_coders/folded_integer_bit_decoder.h" + "${draco_src_root}/compression/bit_coders/folded_integer_bit_encoder.h" + "${draco_src_root}/compression/bit_coders/rans_bit_decoder.cc" + "${draco_src_root}/compression/bit_coders/rans_bit_decoder.h" + "${draco_src_root}/compression/bit_coders/rans_bit_encoder.cc" + "${draco_src_root}/compression/bit_coders/rans_bit_encoder.h" + "${draco_src_root}/compression/bit_coders/symbol_bit_decoder.cc" + "${draco_src_root}/compression/bit_coders/symbol_bit_decoder.h" + "${draco_src_root}/compression/bit_coders/symbol_bit_encoder.cc" + "${draco_src_root}/compression/bit_coders/symbol_bit_encoder.h") + +list( + APPEND + draco_enc_config_sources + "${draco_src_root}/compression/config/compression_shared.h" + "${draco_src_root}/compression/config/draco_options.h" + "${draco_src_root}/compression/config/encoder_options.h" + "${draco_src_root}/compression/config/encoding_features.h") + +list( + APPEND + draco_dec_config_sources + "${draco_src_root}/compression/config/compression_shared.h" + "${draco_src_root}/compression/config/decoder_options.h" + "${draco_src_root}/compression/config/draco_options.h") + +list(APPEND draco_compression_options_sources + "${draco_src_root}/compression/draco_compression_options.cc" + "${draco_src_root}/compression/draco_compression_options.h") list(APPEND draco_compression_decode_sources - "${draco_src_root}/compression/decode.cc" - "${draco_src_root}/compression/decode.h" - "${draco_src_root}/compression/draco_compression_options.h") - -list(APPEND draco_compression_encode_sources - "${draco_src_root}/compression/draco_compression_options.h" - "${draco_src_root}/compression/encode.cc" - "${draco_src_root}/compression/encode.h" - "${draco_src_root}/compression/encode_base.h" - "${draco_src_root}/compression/expert_encode.cc" - "${draco_src_root}/compression/expert_encode.h") - -list( - APPEND - draco_compression_mesh_traverser_sources - "${draco_src_root}/compression/mesh/traverser/depth_first_traverser.h" - "${draco_src_root}/compression/mesh/traverser/max_prediction_degree_traverser.h" - "${draco_src_root}/compression/mesh/traverser/mesh_attribute_indices_encoding_observer.h" - "${draco_src_root}/compression/mesh/traverser/mesh_traversal_sequencer.h" - "${draco_src_root}/compression/mesh/traverser/traverser_base.h") - -list( - APPEND - draco_compression_mesh_dec_sources - "${draco_src_root}/compression/mesh/mesh_decoder.cc" - "${draco_src_root}/compression/mesh/mesh_decoder.h" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder.cc" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder.h" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder_impl.cc" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder_impl.h" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder_impl_interface.h" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_shared.h" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_decoder.h" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_predictive_decoder.h" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_valence_decoder.h" - "${draco_src_root}/compression/mesh/mesh_sequential_decoder.cc" - "${draco_src_root}/compression/mesh/mesh_sequential_decoder.h") - -list( - APPEND - draco_compression_mesh_enc_sources - "${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder.cc" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder.h" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder_impl.cc" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder_impl.h" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder_impl_interface.h" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_shared.h" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_encoder.h" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h" - "${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_valence_encoder.h" - "${draco_src_root}/compression/mesh/mesh_encoder.cc" - "${draco_src_root}/compression/mesh/mesh_encoder.h" - "${draco_src_root}/compression/mesh/mesh_sequential_encoder.cc" - "${draco_src_root}/compression/mesh/mesh_sequential_encoder.h") - -list( - APPEND - draco_compression_point_cloud_dec_sources - "${draco_src_root}/compression/point_cloud/point_cloud_decoder.cc" - "${draco_src_root}/compression/point_cloud/point_cloud_decoder.h" - "${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_decoder.cc" - "${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_decoder.h" - "${draco_src_root}/compression/point_cloud/point_cloud_sequential_decoder.cc" - "${draco_src_root}/compression/point_cloud/point_cloud_sequential_decoder.h" - ) - -list( - APPEND - draco_compression_point_cloud_enc_sources - "${draco_src_root}/compression/point_cloud/point_cloud_encoder.cc" - "${draco_src_root}/compression/point_cloud/point_cloud_encoder.h" - "${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_encoder.cc" - "${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_encoder.h" - "${draco_src_root}/compression/point_cloud/point_cloud_sequential_encoder.cc" - "${draco_src_root}/compression/point_cloud/point_cloud_sequential_encoder.h" - ) - -list(APPEND draco_compression_entropy_sources - "${draco_src_root}/compression/entropy/ans.h" - "${draco_src_root}/compression/entropy/rans_symbol_coding.h" - "${draco_src_root}/compression/entropy/rans_symbol_decoder.h" - "${draco_src_root}/compression/entropy/rans_symbol_encoder.h" - "${draco_src_root}/compression/entropy/shannon_entropy.cc" - "${draco_src_root}/compression/entropy/shannon_entropy.h" - "${draco_src_root}/compression/entropy/symbol_decoding.cc" - "${draco_src_root}/compression/entropy/symbol_decoding.h" - "${draco_src_root}/compression/entropy/symbol_encoding.cc" - "${draco_src_root}/compression/entropy/symbol_encoding.h") - -list(APPEND draco_core_sources - "${draco_src_root}/core/bit_utils.cc" - "${draco_src_root}/core/bit_utils.h" - "${draco_src_root}/core/bounding_box.cc" - "${draco_src_root}/core/bounding_box.h" - "${draco_src_root}/core/constants.h" - "${draco_src_root}/core/cycle_timer.cc" - "${draco_src_root}/core/cycle_timer.h" - "${draco_src_root}/core/data_buffer.cc" - "${draco_src_root}/core/data_buffer.h" - "${draco_src_root}/core/decoder_buffer.cc" - "${draco_src_root}/core/decoder_buffer.h" - "${draco_src_root}/core/divide.cc" - "${draco_src_root}/core/divide.h" - "${draco_src_root}/core/draco_index_type.h" - "${draco_src_root}/core/draco_index_type_vector.h" - "${draco_src_root}/core/draco_types.cc" - "${draco_src_root}/core/draco_types.h" - "${draco_src_root}/core/draco_version.h" - "${draco_src_root}/core/encoder_buffer.cc" - "${draco_src_root}/core/encoder_buffer.h" - "${draco_src_root}/core/hash_utils.cc" - "${draco_src_root}/core/hash_utils.h" - "${draco_src_root}/core/macros.h" - "${draco_src_root}/core/math_utils.h" - "${draco_src_root}/core/options.cc" - "${draco_src_root}/core/options.h" - "${draco_src_root}/core/quantization_utils.cc" - "${draco_src_root}/core/quantization_utils.h" - "${draco_src_root}/core/status.h" - "${draco_src_root}/core/status_or.h" - "${draco_src_root}/core/varint_decoding.h" - "${draco_src_root}/core/varint_encoding.h" - "${draco_src_root}/core/vector_d.h") - -list(APPEND draco_io_sources - "${draco_src_root}/io/file_reader_factory.cc" - "${draco_src_root}/io/file_reader_factory.h" - "${draco_src_root}/io/file_reader_interface.h" - "${draco_src_root}/io/file_utils.cc" - "${draco_src_root}/io/file_utils.h" - "${draco_src_root}/io/file_writer_factory.cc" - "${draco_src_root}/io/file_writer_factory.h" - "${draco_src_root}/io/file_writer_interface.h" - "${draco_src_root}/io/file_writer_utils.h" - "${draco_src_root}/io/file_writer_utils.cc" - "${draco_src_root}/io/mesh_io.cc" - "${draco_src_root}/io/mesh_io.h" - "${draco_src_root}/io/obj_decoder.cc" - "${draco_src_root}/io/obj_decoder.h" - "${draco_src_root}/io/obj_encoder.cc" - "${draco_src_root}/io/obj_encoder.h" - "${draco_src_root}/io/parser_utils.cc" - "${draco_src_root}/io/parser_utils.h" - "${draco_src_root}/io/ply_decoder.cc" - "${draco_src_root}/io/ply_decoder.h" - "${draco_src_root}/io/ply_encoder.cc" - "${draco_src_root}/io/ply_encoder.h" - "${draco_src_root}/io/ply_property_reader.h" - "${draco_src_root}/io/ply_property_writer.h" - "${draco_src_root}/io/ply_reader.cc" - "${draco_src_root}/io/ply_reader.h" - "${draco_src_root}/io/stl_decoder.cc" - "${draco_src_root}/io/stl_decoder.h" - "${draco_src_root}/io/stl_encoder.cc" - "${draco_src_root}/io/stl_encoder.h" - "${draco_src_root}/io/point_cloud_io.cc" - "${draco_src_root}/io/point_cloud_io.h" - "${draco_src_root}/io/stdio_file_reader.cc" - "${draco_src_root}/io/stdio_file_reader.h" - "${draco_src_root}/io/stdio_file_writer.cc" - "${draco_src_root}/io/stdio_file_writer.h") - -list(APPEND draco_mesh_sources - "${draco_src_root}/mesh/corner_table.cc" - "${draco_src_root}/mesh/corner_table.h" - "${draco_src_root}/mesh/corner_table_iterators.h" - "${draco_src_root}/mesh/mesh.cc" - "${draco_src_root}/mesh/mesh.h" - "${draco_src_root}/mesh/mesh_are_equivalent.cc" - "${draco_src_root}/mesh/mesh_are_equivalent.h" - "${draco_src_root}/mesh/mesh_attribute_corner_table.cc" - "${draco_src_root}/mesh/mesh_attribute_corner_table.h" - "${draco_src_root}/mesh/mesh_cleanup.cc" - "${draco_src_root}/mesh/mesh_cleanup.h" - "${draco_src_root}/mesh/mesh_misc_functions.cc" - "${draco_src_root}/mesh/mesh_misc_functions.h" - "${draco_src_root}/mesh/mesh_stripifier.cc" - "${draco_src_root}/mesh/mesh_stripifier.h" - "${draco_src_root}/mesh/triangle_soup_mesh_builder.cc" - "${draco_src_root}/mesh/triangle_soup_mesh_builder.h" - "${draco_src_root}/mesh/valence_cache.h") - -list(APPEND draco_point_cloud_sources - "${draco_src_root}/point_cloud/point_cloud.cc" - "${draco_src_root}/point_cloud/point_cloud.h" - "${draco_src_root}/point_cloud/point_cloud_builder.cc" - "${draco_src_root}/point_cloud/point_cloud_builder.h") - -list( - APPEND - draco_points_common_sources - "${draco_src_root}/compression/point_cloud/algorithms/point_cloud_compression_method.h" - "${draco_src_root}/compression/point_cloud/algorithms/point_cloud_types.h" - "${draco_src_root}/compression/point_cloud/algorithms/quantize_points_3.h" - "${draco_src_root}/compression/point_cloud/algorithms/queuing_policy.h") - -list( - APPEND - draco_points_dec_sources - "${draco_src_root}/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.cc" - "${draco_src_root}/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.h" - "${draco_src_root}/compression/point_cloud/algorithms/float_points_tree_decoder.cc" - "${draco_src_root}/compression/point_cloud/algorithms/float_points_tree_decoder.h" - ) - -list( - APPEND - draco_points_enc_sources - "${draco_src_root}/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.cc" - "${draco_src_root}/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.h" - "${draco_src_root}/compression/point_cloud/algorithms/float_points_tree_encoder.cc" - "${draco_src_root}/compression/point_cloud/algorithms/float_points_tree_encoder.h" - ) - -list(APPEND draco_metadata_sources - "${draco_src_root}/metadata/geometry_metadata.cc" - "${draco_src_root}/metadata/geometry_metadata.h" - "${draco_src_root}/metadata/metadata.cc" - "${draco_src_root}/metadata/metadata.h") + "${draco_src_root}/compression/decode.cc" + "${draco_src_root}/compression/decode.h") + +list( + APPEND + draco_compression_encode_sources + "${draco_src_root}/compression/encode.cc" + "${draco_src_root}/compression/encode.h" + "${draco_src_root}/compression/encode_base.h" + "${draco_src_root}/compression/expert_encode.cc" + "${draco_src_root}/compression/expert_encode.h") + +list( + APPEND + draco_compression_mesh_traverser_sources + "${draco_src_root}/compression/mesh/traverser/depth_first_traverser.h" + "${draco_src_root}/compression/mesh/traverser/max_prediction_degree_traverser.h" + "${draco_src_root}/compression/mesh/traverser/mesh_attribute_indices_encoding_observer.h" + "${draco_src_root}/compression/mesh/traverser/mesh_traversal_sequencer.h" + "${draco_src_root}/compression/mesh/traverser/traverser_base.h") + +list( + APPEND + draco_compression_mesh_dec_sources + "${draco_src_root}/compression/mesh/mesh_decoder.cc" + "${draco_src_root}/compression/mesh/mesh_decoder.h" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder.cc" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder.h" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder_impl.cc" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder_impl.h" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_decoder_impl_interface.h" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_shared.h" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_decoder.h" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_predictive_decoder.h" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_valence_decoder.h" + "${draco_src_root}/compression/mesh/mesh_sequential_decoder.cc" + "${draco_src_root}/compression/mesh/mesh_sequential_decoder.h") + +list( + APPEND + draco_compression_mesh_enc_sources + "${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder.cc" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder.h" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder_impl.cc" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder_impl.h" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_encoder_impl_interface.h" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_shared.h" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_encoder.h" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h" + "${draco_src_root}/compression/mesh/mesh_edgebreaker_traversal_valence_encoder.h" + "${draco_src_root}/compression/mesh/mesh_encoder.cc" + "${draco_src_root}/compression/mesh/mesh_encoder.h" + "${draco_src_root}/compression/mesh/mesh_sequential_encoder.cc" + "${draco_src_root}/compression/mesh/mesh_sequential_encoder.h") + +list( + APPEND + draco_compression_point_cloud_dec_sources + "${draco_src_root}/compression/point_cloud/point_cloud_decoder.cc" + "${draco_src_root}/compression/point_cloud/point_cloud_decoder.h" + "${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_decoder.cc" + "${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_decoder.h" + "${draco_src_root}/compression/point_cloud/point_cloud_sequential_decoder.cc" + "${draco_src_root}/compression/point_cloud/point_cloud_sequential_decoder.h") + +list( + APPEND + draco_compression_point_cloud_enc_sources + "${draco_src_root}/compression/point_cloud/point_cloud_encoder.cc" + "${draco_src_root}/compression/point_cloud/point_cloud_encoder.h" + "${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_encoder.cc" + "${draco_src_root}/compression/point_cloud/point_cloud_kd_tree_encoder.h" + "${draco_src_root}/compression/point_cloud/point_cloud_sequential_encoder.cc" + "${draco_src_root}/compression/point_cloud/point_cloud_sequential_encoder.h") + +list( + APPEND + draco_compression_entropy_sources + "${draco_src_root}/compression/entropy/ans.h" + "${draco_src_root}/compression/entropy/rans_symbol_coding.h" + "${draco_src_root}/compression/entropy/rans_symbol_decoder.h" + "${draco_src_root}/compression/entropy/rans_symbol_encoder.h" + "${draco_src_root}/compression/entropy/shannon_entropy.cc" + "${draco_src_root}/compression/entropy/shannon_entropy.h" + "${draco_src_root}/compression/entropy/symbol_decoding.cc" + "${draco_src_root}/compression/entropy/symbol_decoding.h" + "${draco_src_root}/compression/entropy/symbol_encoding.cc" + "${draco_src_root}/compression/entropy/symbol_encoding.h") + +list( + APPEND + draco_core_sources + "${draco_src_root}/core/bit_utils.cc" + "${draco_src_root}/core/bit_utils.h" + "${draco_src_root}/core/bounding_box.cc" + "${draco_src_root}/core/bounding_box.h" + "${draco_src_root}/core/constants.h" + "${draco_src_root}/core/cycle_timer.cc" + "${draco_src_root}/core/cycle_timer.h" + "${draco_src_root}/core/data_buffer.cc" + "${draco_src_root}/core/data_buffer.h" + "${draco_src_root}/core/decoder_buffer.cc" + "${draco_src_root}/core/decoder_buffer.h" + "${draco_src_root}/core/divide.cc" + "${draco_src_root}/core/divide.h" + "${draco_src_root}/core/draco_index_type.h" + "${draco_src_root}/core/draco_index_type_vector.h" + "${draco_src_root}/core/draco_types.cc" + "${draco_src_root}/core/draco_types.h" + "${draco_src_root}/core/draco_version.h" + "${draco_src_root}/core/encoder_buffer.cc" + "${draco_src_root}/core/encoder_buffer.h" + "${draco_src_root}/core/hash_utils.cc" + "${draco_src_root}/core/hash_utils.h" + "${draco_src_root}/core/macros.h" + "${draco_src_root}/core/math_utils.h" + "${draco_src_root}/core/options.cc" + "${draco_src_root}/core/options.h" + "${draco_src_root}/core/quantization_utils.cc" + "${draco_src_root}/core/quantization_utils.h" + "${draco_src_root}/core/status.h" + "${draco_src_root}/core/status_or.h" + "${draco_src_root}/core/varint_decoding.h" + "${draco_src_root}/core/varint_encoding.h" + "${draco_src_root}/core/vector_d.h") + +list( + APPEND + draco_io_sources + "${draco_src_root}/io/file_reader_factory.cc" + "${draco_src_root}/io/file_reader_factory.h" + "${draco_src_root}/io/file_reader_interface.h" + "${draco_src_root}/io/file_utils.cc" + "${draco_src_root}/io/file_utils.h" + "${draco_src_root}/io/file_writer_factory.cc" + "${draco_src_root}/io/file_writer_factory.h" + "${draco_src_root}/io/file_writer_interface.h" + "${draco_src_root}/io/file_writer_utils.h" + "${draco_src_root}/io/file_writer_utils.cc" + "${draco_src_root}/io/mesh_io.cc" + "${draco_src_root}/io/mesh_io.h" + "${draco_src_root}/io/obj_decoder.cc" + "${draco_src_root}/io/obj_decoder.h" + "${draco_src_root}/io/obj_encoder.cc" + "${draco_src_root}/io/obj_encoder.h" + "${draco_src_root}/io/parser_utils.cc" + "${draco_src_root}/io/parser_utils.h" + "${draco_src_root}/io/ply_decoder.cc" + "${draco_src_root}/io/ply_decoder.h" + "${draco_src_root}/io/ply_encoder.cc" + "${draco_src_root}/io/ply_encoder.h" + "${draco_src_root}/io/ply_property_reader.h" + "${draco_src_root}/io/ply_property_writer.h" + "${draco_src_root}/io/ply_reader.cc" + "${draco_src_root}/io/ply_reader.h" + "${draco_src_root}/io/stl_decoder.cc" + "${draco_src_root}/io/stl_decoder.h" + "${draco_src_root}/io/stl_encoder.cc" + "${draco_src_root}/io/stl_encoder.h" + "${draco_src_root}/io/point_cloud_io.cc" + "${draco_src_root}/io/point_cloud_io.h" + "${draco_src_root}/io/stdio_file_reader.cc" + "${draco_src_root}/io/stdio_file_reader.h" + "${draco_src_root}/io/stdio_file_writer.cc" + "${draco_src_root}/io/stdio_file_writer.h") + +list( + APPEND + draco_mesh_sources + "${draco_src_root}/mesh/corner_table.cc" + "${draco_src_root}/mesh/corner_table.h" + "${draco_src_root}/mesh/corner_table_iterators.h" + "${draco_src_root}/mesh/mesh.cc" + "${draco_src_root}/mesh/mesh.h" + "${draco_src_root}/mesh/mesh_are_equivalent.cc" + "${draco_src_root}/mesh/mesh_are_equivalent.h" + "${draco_src_root}/mesh/mesh_attribute_corner_table.cc" + "${draco_src_root}/mesh/mesh_attribute_corner_table.h" + "${draco_src_root}/mesh/mesh_cleanup.cc" + "${draco_src_root}/mesh/mesh_cleanup.h" + "${draco_src_root}/mesh/mesh_features.cc" + "${draco_src_root}/mesh/mesh_features.h" + "${draco_src_root}/mesh/mesh_indices.h" + "${draco_src_root}/mesh/mesh_misc_functions.cc" + "${draco_src_root}/mesh/mesh_misc_functions.h" + "${draco_src_root}/mesh/mesh_stripifier.cc" + "${draco_src_root}/mesh/mesh_stripifier.h" + "${draco_src_root}/mesh/triangle_soup_mesh_builder.cc" + "${draco_src_root}/mesh/triangle_soup_mesh_builder.h" + "${draco_src_root}/mesh/valence_cache.h") + +list( + APPEND + draco_point_cloud_sources + "${draco_src_root}/point_cloud/point_cloud.cc" + "${draco_src_root}/point_cloud/point_cloud.h" + "${draco_src_root}/point_cloud/point_cloud_builder.cc" + "${draco_src_root}/point_cloud/point_cloud_builder.h") + +list( + APPEND + draco_points_common_sources + "${draco_src_root}/compression/point_cloud/algorithms/point_cloud_compression_method.h" + "${draco_src_root}/compression/point_cloud/algorithms/point_cloud_types.h" + "${draco_src_root}/compression/point_cloud/algorithms/quantize_points_3.h" + "${draco_src_root}/compression/point_cloud/algorithms/queuing_policy.h") + +list( + APPEND + draco_points_dec_sources + "${draco_src_root}/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.cc" + "${draco_src_root}/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.h" + "${draco_src_root}/compression/point_cloud/algorithms/float_points_tree_decoder.cc" + "${draco_src_root}/compression/point_cloud/algorithms/float_points_tree_decoder.h" +) + +list( + APPEND + draco_points_enc_sources + "${draco_src_root}/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.cc" + "${draco_src_root}/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.h" + "${draco_src_root}/compression/point_cloud/algorithms/float_points_tree_encoder.cc" + "${draco_src_root}/compression/point_cloud/algorithms/float_points_tree_encoder.h" +) + +list( + APPEND + draco_metadata_sources + "${draco_src_root}/metadata/geometry_metadata.cc" + "${draco_src_root}/metadata/geometry_metadata.h" + "${draco_src_root}/metadata/metadata.cc" + "${draco_src_root}/metadata/metadata.h" + "${draco_src_root}/metadata/property_table.cc" + "${draco_src_root}/metadata/property_table.h" + "${draco_src_root}/metadata/structural_metadata.cc" + "${draco_src_root}/metadata/structural_metadata.h") list(APPEND draco_metadata_enc_sources - "${draco_src_root}/metadata/metadata_encoder.cc" - "${draco_src_root}/metadata/metadata_encoder.h") + "${draco_src_root}/metadata/metadata_encoder.cc" + "${draco_src_root}/metadata/metadata_encoder.h") list(APPEND draco_metadata_dec_sources - "${draco_src_root}/metadata/metadata_decoder.cc" - "${draco_src_root}/metadata/metadata_decoder.h") + "${draco_src_root}/metadata/metadata_decoder.cc" + "${draco_src_root}/metadata/metadata_decoder.h") list(APPEND draco_animation_sources - "${draco_src_root}/animation/keyframe_animation.cc" - "${draco_src_root}/animation/keyframe_animation.h") + "${draco_src_root}/animation/keyframe_animation.cc" + "${draco_src_root}/animation/keyframe_animation.h") list(APPEND draco_animation_enc_sources - "${draco_src_root}/animation/keyframe_animation_encoder.cc" - "${draco_src_root}/animation/keyframe_animation_encoder.h") + "${draco_src_root}/animation/keyframe_animation_encoder.cc" + "${draco_src_root}/animation/keyframe_animation_encoder.h") list(APPEND draco_animation_dec_sources - "${draco_src_root}/animation/keyframe_animation_decoder.cc" - "${draco_src_root}/animation/keyframe_animation_decoder.h") + "${draco_src_root}/animation/keyframe_animation_decoder.cc" + "${draco_src_root}/animation/keyframe_animation_decoder.h") -list( - APPEND draco_js_dec_sources - "${draco_src_root}/javascript/emscripten/decoder_webidl_wrapper.cc" - "${draco_src_root}/javascript/emscripten/draco_decoder_glue_wrapper.cc" - ) - -list( - APPEND draco_js_enc_sources - "${draco_src_root}/javascript/emscripten/draco_encoder_glue_wrapper.cc" - "${draco_src_root}/javascript/emscripten/encoder_webidl_wrapper.cc") +list(APPEND draco_js_dec_sources + "${draco_src_root}/javascript/emscripten/decoder_webidl_wrapper.cc" + "${draco_src_root}/javascript/emscripten/draco_decoder_glue_wrapper.cc") + +list(APPEND draco_js_enc_sources + "${draco_src_root}/javascript/emscripten/draco_encoder_glue_wrapper.cc" + "${draco_src_root}/javascript/emscripten/encoder_webidl_wrapper.cc") list( APPEND - draco_animation_js_dec_sources - "${draco_src_root}/javascript/emscripten/animation_decoder_webidl_wrapper.cc" - "${draco_src_root}/javascript/emscripten/draco_animation_decoder_glue_wrapper.cc" - ) + draco_animation_js_dec_sources + "${draco_src_root}/javascript/emscripten/animation_decoder_webidl_wrapper.cc" + "${draco_src_root}/javascript/emscripten/draco_animation_decoder_glue_wrapper.cc" +) list( APPEND - draco_animation_js_enc_sources - "${draco_src_root}/javascript/emscripten/animation_encoder_webidl_wrapper.cc" - "${draco_src_root}/javascript/emscripten/draco_animation_encoder_glue_wrapper.cc" - ) + draco_animation_js_enc_sources + "${draco_src_root}/javascript/emscripten/animation_encoder_webidl_wrapper.cc" + "${draco_src_root}/javascript/emscripten/draco_animation_encoder_glue_wrapper.cc" +) list(APPEND draco_unity_plug_sources - "${draco_src_root}/unity/draco_unity_plugin.cc" - "${draco_src_root}/unity/draco_unity_plugin.h") + "${draco_src_root}/unity/draco_unity_plugin.cc" + "${draco_src_root}/unity/draco_unity_plugin.h") list(APPEND draco_maya_plug_sources - "${draco_src_root}/maya/draco_maya_plugin.cc" - "${draco_src_root}/maya/draco_maya_plugin.h") + "${draco_src_root}/maya/draco_maya_plugin.cc" + "${draco_src_root}/maya/draco_maya_plugin.h") if(DRACO_TRANSCODER_SUPPORTED) - list(APPEND draco_animation_sources + list( + APPEND + draco_animation_sources "${draco_src_root}/animation/animation.cc" "${draco_src_root}/animation/animation.h" "${draco_src_root}/animation/node_animation_data.h" "${draco_src_root}/animation/skin.cc" "${draco_src_root}/animation/skin.h") - list(APPEND draco_io_sources + list( + APPEND + draco_io_sources "${draco_src_root}/io/gltf_decoder.cc" "${draco_src_root}/io/gltf_decoder.h" "${draco_src_root}/io/gltf_encoder.cc" @@ -537,13 +568,17 @@ "${draco_src_root}/io/tiny_gltf_utils.cc" "${draco_src_root}/io/tiny_gltf_utils.h") - list(APPEND draco_material_sources + list( + APPEND + draco_material_sources "${draco_src_root}/material/material.cc" "${draco_src_root}/material/material.h" "${draco_src_root}/material/material_library.cc" "${draco_src_root}/material/material_library.h") - list(APPEND draco_mesh_sources + list( + APPEND + draco_mesh_sources "${draco_src_root}/mesh/mesh_connected_components.cc" "${draco_src_root}/mesh/mesh_connected_components.h" "${draco_src_root}/mesh/mesh_splitter.cc" @@ -551,7 +586,9 @@ "${draco_src_root}/mesh/mesh_utils.cc" "${draco_src_root}/mesh/mesh_utils.h") - list(APPEND draco_scene_sources + list( + APPEND + draco_scene_sources "${draco_src_root}/scene/instance_array.cc" "${draco_src_root}/scene/instance_array.h" "${draco_src_root}/scene/light.cc" @@ -559,6 +596,8 @@ "${draco_src_root}/scene/mesh_group.h" "${draco_src_root}/scene/scene.cc" "${draco_src_root}/scene/scene.h" + "${draco_src_root}/scene/scene_are_equivalent.cc" + "${draco_src_root}/scene/scene_are_equivalent.h" "${draco_src_root}/scene/scene_indices.h" "${draco_src_root}/scene/scene_node.h" "${draco_src_root}/scene/scene_utils.cc" @@ -566,7 +605,9 @@ "${draco_src_root}/scene/trs_matrix.cc" "${draco_src_root}/scene/trs_matrix.h") - list(APPEND draco_texture_sources + list( + APPEND + draco_texture_sources "${draco_src_root}/texture/source_image.cc" "${draco_src_root}/texture/source_image.h" "${draco_src_root}/texture/texture.h" @@ -591,117 +632,104 @@ message(FATAL_ERROR "The transcoder is not supported in Emscripten.") endif() - list(APPEND draco_decoder_src - ${draco_attributes_sources} - ${draco_compression_attributes_dec_sources} - ${draco_compression_attributes_pred_schemes_dec_sources} - ${draco_compression_bit_coders_sources} - ${draco_compression_decode_sources} - ${draco_compression_entropy_sources} - ${draco_compression_mesh_traverser_sources} - ${draco_compression_mesh_dec_sources} - ${draco_compression_point_cloud_dec_sources} - ${draco_core_sources} - ${draco_dec_config_sources} - ${draco_js_dec_sources} - ${draco_mesh_sources} - ${draco_metadata_dec_sources} - ${draco_metadata_sources} - ${draco_point_cloud_sources} - ${draco_points_dec_sources}) - - list(APPEND draco_encoder_src - ${draco_attributes_sources} - ${draco_compression_attributes_enc_sources} - ${draco_compression_attributes_pred_schemes_enc_sources} - ${draco_compression_bit_coders_sources} - ${draco_compression_encode_sources} - ${draco_compression_entropy_sources} - ${draco_compression_mesh_traverser_sources} - ${draco_compression_mesh_enc_sources} - ${draco_compression_point_cloud_enc_sources} - ${draco_core_sources} - ${draco_enc_config_sources} - ${draco_js_enc_sources} - ${draco_mesh_sources} - ${draco_metadata_enc_sources} - ${draco_metadata_sources} - ${draco_point_cloud_sources} - ${draco_points_enc_sources}) + list( + APPEND + draco_decoder_src + ${draco_attributes_sources} + ${draco_compression_attributes_dec_sources} + ${draco_compression_attributes_pred_schemes_dec_sources} + ${draco_compression_bit_coders_sources} + ${draco_compression_decode_sources} + ${draco_compression_entropy_sources} + ${draco_compression_mesh_traverser_sources} + ${draco_compression_mesh_dec_sources} + ${draco_compression_options_sources} + ${draco_compression_point_cloud_dec_sources} + ${draco_core_sources} + ${draco_dec_config_sources} + ${draco_js_dec_sources} + ${draco_mesh_sources} + ${draco_metadata_dec_sources} + ${draco_metadata_sources} + ${draco_point_cloud_sources} + ${draco_points_dec_sources}) + + list( + APPEND + draco_encoder_src + ${draco_attributes_sources} + ${draco_compression_attributes_enc_sources} + ${draco_compression_attributes_pred_schemes_enc_sources} + ${draco_compression_bit_coders_sources} + ${draco_compression_encode_sources} + ${draco_compression_entropy_sources} + ${draco_compression_mesh_traverser_sources} + ${draco_compression_mesh_enc_sources} + ${draco_compression_options_sources} + ${draco_compression_point_cloud_enc_sources} + ${draco_core_sources} + ${draco_enc_config_sources} + ${draco_js_enc_sources} + ${draco_mesh_sources} + ${draco_metadata_enc_sources} + ${draco_metadata_sources} + ${draco_point_cloud_sources} + ${draco_points_enc_sources}) list(APPEND draco_js_dec_idl - "${draco_src_root}/javascript/emscripten/draco_web_decoder.idl") + "${draco_src_root}/javascript/emscripten/draco_web_decoder.idl") list(APPEND draco_js_enc_idl - "${draco_src_root}/javascript/emscripten/draco_web_encoder.idl") + "${draco_src_root}/javascript/emscripten/draco_web_encoder.idl") list( - APPEND - draco_animation_js_dec_idl - "${draco_src_root}/javascript/emscripten/draco_animation_web_decoder.idl") + APPEND draco_animation_js_dec_idl + "${draco_src_root}/javascript/emscripten/draco_animation_web_decoder.idl") list( - APPEND - draco_animation_js_enc_idl - "${draco_src_root}/javascript/emscripten/draco_animation_web_encoder.idl") + APPEND draco_animation_js_enc_idl + "${draco_src_root}/javascript/emscripten/draco_animation_web_encoder.idl") list(APPEND draco_pre_link_js_sources - "${draco_src_root}/javascript/emscripten/prepareCallbacks.js" - "${draco_src_root}/javascript/emscripten/version.js") + "${draco_src_root}/javascript/emscripten/prepareCallbacks.js" + "${draco_src_root}/javascript/emscripten/version.js") list(APPEND draco_post_link_js_sources - "${draco_src_root}/javascript/emscripten/finalize.js") + "${draco_src_root}/javascript/emscripten/finalize.js") list(APPEND draco_post_link_js_decoder_sources ${draco_post_link_js_sources} - "${draco_src_root}/javascript/emscripten/decoder_functions.js") + "${draco_src_root}/javascript/emscripten/decoder_functions.js") set(draco_decoder_glue_path "${draco_build}/glue_decoder") set(draco_encoder_glue_path "${draco_build}/glue_encoder") - draco_generate_emscripten_glue(INPUT_IDL ${draco_js_dec_idl} OUTPUT_PATH - ${draco_decoder_glue_path}) - draco_generate_emscripten_glue(INPUT_IDL ${draco_js_enc_idl} OUTPUT_PATH - ${draco_encoder_glue_path}) + draco_generate_emscripten_glue(INPUT_IDL ${draco_js_dec_idl} + OUTPUT_PATH ${draco_decoder_glue_path}) + draco_generate_emscripten_glue(INPUT_IDL ${draco_js_enc_idl} + OUTPUT_PATH ${draco_encoder_glue_path}) if(DRACO_DECODER_ATTRIBUTE_DEDUPLICATION) list(APPEND draco_decoder_features - "DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED" - "DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED") + "DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED" + "DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED") endif() - draco_add_emscripten_executable(NAME - draco_decoder - SOURCES - ${draco_decoder_src} - DEFINES - ${draco_defines} - FEATURES - ${draco_decoder_features} - INCLUDES - ${draco_include_paths} - LINK_FLAGS - "-sEXPORT_NAME=\"DracoDecoderModule\"" - GLUE_PATH - ${draco_decoder_glue_path} - PRE_LINK_JS_SOURCES - ${draco_pre_link_js_sources} - POST_LINK_JS_SOURCES - ${draco_post_link_js_decoder_sources}) + draco_add_emscripten_executable( + NAME draco_decoder + SOURCES ${draco_decoder_src} + DEFINES ${draco_defines} + FEATURES ${draco_decoder_features} + INCLUDES ${draco_include_paths} + LINK_FLAGS "-sEXPORT_NAME=\"DracoDecoderModule\"" + GLUE_PATH ${draco_decoder_glue_path} + PRE_LINK_JS_SOURCES ${draco_pre_link_js_sources} + POST_LINK_JS_SOURCES ${draco_post_link_js_decoder_sources}) draco_add_emscripten_executable( - NAME - draco_encoder - SOURCES - ${draco_encoder_src} - DEFINES - ${draco_defines} - FEATURES - DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED - DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED - INCLUDES - ${draco_include_paths} - LINK_FLAGS - "-sEXPORT_NAME=\"DracoEncoderModule\"" - GLUE_PATH - ${draco_encoder_glue_path} - PRE_LINK_JS_SOURCES - ${draco_pre_link_js_sources} - POST_LINK_JS_SOURCES - ${draco_post_link_js_sources}) + NAME draco_encoder + SOURCES ${draco_encoder_src} + DEFINES ${draco_defines} + FEATURES DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED + DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED + INCLUDES ${draco_include_paths} + LINK_FLAGS "-sEXPORT_NAME=\"DracoEncoderModule\"" + GLUE_PATH ${draco_encoder_glue_path} + PRE_LINK_JS_SOURCES ${draco_pre_link_js_sources} + POST_LINK_JS_SOURCES ${draco_post_link_js_sources}) if(DRACO_ANIMATION_ENCODING) set(draco_anim_decoder_glue_path "${draco_build}/glue_animation_decoder") @@ -713,235 +741,268 @@ OUTPUT_PATH ${draco_anim_encoder_glue_path}) draco_add_emscripten_executable( - NAME - draco_animation_decoder - SOURCES - ${draco_animation_dec_sources} - ${draco_animation_js_dec_sources} - ${draco_animation_sources} - ${draco_decoder_src} - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths} - LINK_FLAGS - "-sEXPORT_NAME=\"DracoAnimationDecoderModule\"" - GLUE_PATH - ${draco_anim_decoder_glue_path} - PRE_LINK_JS_SOURCES - ${draco_pre_link_js_sources} - POST_LINK_JS_SOURCES - ${draco_post_link_js_decoder_sources}) + NAME draco_animation_decoder + SOURCES ${draco_animation_dec_sources} ${draco_animation_js_dec_sources} + ${draco_animation_sources} ${draco_decoder_src} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths} + LINK_FLAGS "-sEXPORT_NAME=\"DracoAnimationDecoderModule\"" + GLUE_PATH ${draco_anim_decoder_glue_path} + PRE_LINK_JS_SOURCES ${draco_pre_link_js_sources} + POST_LINK_JS_SOURCES ${draco_post_link_js_decoder_sources}) draco_add_emscripten_executable( - NAME - draco_animation_encoder - SOURCES - ${draco_animation_enc_sources} - ${draco_animation_js_enc_sources} - ${draco_animation_sources} - ${draco_encoder_src} - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths} - LINK_FLAGS - "-sEXPORT_NAME=\"DracoAnimationEncoderModule\"" - GLUE_PATH - ${draco_anim_encoder_glue_path} - PRE_LINK_JS_SOURCES - ${draco_pre_link_js_sources} - POST_LINK_JS_SOURCES - ${draco_post_link_js_sources}) + NAME draco_animation_encoder + SOURCES ${draco_animation_enc_sources} ${draco_animation_js_enc_sources} + ${draco_animation_sources} ${draco_encoder_src} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths} + LINK_FLAGS "-sEXPORT_NAME=\"DracoAnimationEncoderModule\"" + GLUE_PATH ${draco_anim_encoder_glue_path} + PRE_LINK_JS_SOURCES ${draco_pre_link_js_sources} + POST_LINK_JS_SOURCES ${draco_post_link_js_sources}) endif() else() # Standard Draco libs, encoder and decoder. Object collections that mirror the # Draco directory structure. - draco_add_library(NAME draco_attributes TYPE OBJECT SOURCES - ${draco_attributes_sources} DEFINES ${draco_defines} - INCLUDES ${draco_include_paths}) - draco_add_library(NAME - draco_compression_attributes_dec - OBJECT - ${draco_compression_attributes_dec_sources} - TYPE - OBJECT - SOURCES - ${draco_compression_attributes_dec_sources} - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths}) - draco_add_library(NAME draco_compression_attributes_enc TYPE OBJECT SOURCES - ${draco_compression_attributes_enc_sources} DEFINES - ${draco_defines} INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_compression_attributes_pred_schemes_dec TYPE - OBJECT SOURCES - ${draco_compression_attributes_pred_schemes_dec_sources}) - draco_add_library(NAME draco_compression_attributes_pred_schemes_enc TYPE - OBJECT SOURCES - ${draco_compression_attributes_pred_schemes_enc_sources} - DEFINES ${draco_defines} INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_compression_bit_coders TYPE OBJECT SOURCES - ${draco_compression_bit_coders_sources} DEFINES - ${draco_defines} INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_enc_config TYPE OBJECT SOURCES - ${draco_enc_config_sources} DEFINES ${draco_defines} - INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_dec_config TYPE OBJECT SOURCES - ${draco_dec_config_sources} DEFINES ${draco_defines} - INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_compression_decode TYPE OBJECT SOURCES - ${draco_compression_decode_sources} DEFINES ${draco_defines} - INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_compression_encode TYPE OBJECT SOURCES - ${draco_compression_encode_sources} DEFINES ${draco_defines} - INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_compression_entropy TYPE OBJECT SOURCES - ${draco_compression_entropy_sources} DEFINES - ${draco_defines} INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_compression_mesh_traverser TYPE OBJECT SOURCES - ${draco_compression_mesh_traverser_sources} DEFINES - ${draco_defines} INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_compression_mesh_dec TYPE OBJECT SOURCES - ${draco_compression_mesh_dec_sources} DEFINES - ${draco_defines} INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_compression_mesh_enc TYPE OBJECT SOURCES - ${draco_compression_mesh_enc_sources} DEFINES - ${draco_defines} INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_compression_point_cloud_dec TYPE OBJECT SOURCES - ${draco_compression_point_cloud_dec_sources} DEFINES - ${draco_defines} INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_compression_point_cloud_enc TYPE OBJECT SOURCES - ${draco_compression_point_cloud_enc_sources} DEFINES - ${draco_defines} INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_core TYPE OBJECT SOURCES ${draco_core_sources} - DEFINES ${draco_defines} INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_io TYPE OBJECT SOURCES ${draco_io_sources} - DEFINES ${draco_defines} INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_mesh TYPE OBJECT SOURCES ${draco_mesh_sources} - DEFINES ${draco_defines} INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_metadata_dec TYPE OBJECT SOURCES - ${draco_metadata_dec_sources} DEFINES ${draco_defines} - INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_metadata_enc TYPE OBJECT SOURCES - ${draco_metadata_enc_sources} DEFINES ${draco_defines} - INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_metadata TYPE OBJECT SOURCES - ${draco_metadata_sources} DEFINES ${draco_defines} INCLUDES - ${draco_include_paths}) - draco_add_library(NAME draco_animation_dec TYPE OBJECT SOURCES - ${draco_animation_dec_sources} DEFINES ${draco_defines} - INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_animation_enc TYPE OBJECT SOURCES - ${draco_animation_enc_sources} DEFINES ${draco_defines} - INCLUDES ${draco_include_paths}) - draco_add_library(NAME draco_animation TYPE OBJECT SOURCES - ${draco_animation_sources} DEFINES ${draco_defines} INCLUDES - ${draco_include_paths}) - draco_add_library(NAME draco_point_cloud TYPE OBJECT SOURCES - ${draco_point_cloud_sources} DEFINES ${draco_defines} - INCLUDES ${draco_include_paths}) - draco_add_library(NAME - draco_points_dec - TYPE - OBJECT - SOURCES - ${draco_points_common_sources} - ${draco_points_dec_sources} - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths}) - draco_add_library(NAME - draco_points_enc - TYPE - OBJECT - SOURCES - ${draco_points_common_sources} - ${draco_points_enc_sources} - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths}) + draco_add_library( + NAME draco_attributes + TYPE OBJECT + SOURCES ${draco_attributes_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_compression_attributes_dec OBJECT + ${draco_compression_attributes_dec_sources} + TYPE OBJECT + SOURCES ${draco_compression_attributes_dec_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_compression_attributes_enc + TYPE OBJECT + SOURCES ${draco_compression_attributes_enc_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_compression_attributes_pred_schemes_dec + TYPE OBJECT + SOURCES ${draco_compression_attributes_pred_schemes_dec_sources}) + draco_add_library( + NAME draco_compression_attributes_pred_schemes_enc + TYPE OBJECT + SOURCES ${draco_compression_attributes_pred_schemes_enc_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_compression_bit_coders + TYPE OBJECT + SOURCES ${draco_compression_bit_coders_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_enc_config + TYPE OBJECT + SOURCES ${draco_enc_config_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_dec_config + TYPE OBJECT + SOURCES ${draco_dec_config_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_compression_decode + TYPE OBJECT + SOURCES ${draco_compression_decode_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_compression_encode + TYPE OBJECT + SOURCES ${draco_compression_encode_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_compression_entropy + TYPE OBJECT + SOURCES ${draco_compression_entropy_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_compression_mesh_traverser + TYPE OBJECT + SOURCES ${draco_compression_mesh_traverser_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_compression_mesh_dec + TYPE OBJECT + SOURCES ${draco_compression_mesh_dec_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_compression_mesh_enc + TYPE OBJECT + SOURCES ${draco_compression_mesh_enc_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_compression_options + TYPE OBJECT + SOURCES ${draco_compression_options_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_compression_point_cloud_dec + TYPE OBJECT + SOURCES ${draco_compression_point_cloud_dec_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_compression_point_cloud_enc + TYPE OBJECT + SOURCES ${draco_compression_point_cloud_enc_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_core + TYPE OBJECT + SOURCES ${draco_core_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_io + TYPE OBJECT + SOURCES ${draco_io_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_mesh + TYPE OBJECT + SOURCES ${draco_mesh_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_metadata_dec + TYPE OBJECT + SOURCES ${draco_metadata_dec_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_metadata_enc + TYPE OBJECT + SOURCES ${draco_metadata_enc_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_metadata + TYPE OBJECT + SOURCES ${draco_metadata_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_animation_dec + TYPE OBJECT + SOURCES ${draco_animation_dec_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_animation_enc + TYPE OBJECT + SOURCES ${draco_animation_enc_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_animation + TYPE OBJECT + SOURCES ${draco_animation_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_point_cloud + TYPE OBJECT + SOURCES ${draco_point_cloud_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_points_dec + TYPE OBJECT + SOURCES ${draco_points_common_sources} ${draco_points_dec_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + draco_add_library( + NAME draco_points_enc + TYPE OBJECT + SOURCES ${draco_points_common_sources} ${draco_points_enc_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) if(DRACO_TRANSCODER_SUPPORTED) if(MSVC) # TODO(https://github.com/google/draco/issues/826) - set_source_files_properties( - "${draco_src_root}/io/gltf_decoder.cc" - PROPERTIES COMPILE_OPTIONS "/Od") + set_source_files_properties("${draco_src_root}/io/gltf_decoder.cc" + PROPERTIES COMPILE_OPTIONS "/Od") endif() draco_add_library( - NAME - draco_material - TYPE - OBJECT - SOURCES - ${draco_material_sources} - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths}) + NAME draco_material + TYPE OBJECT + SOURCES ${draco_material_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) draco_add_library( - NAME - draco_scene - TYPE - OBJECT - SOURCES - ${draco_scene_sources} - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths}) + NAME draco_scene + TYPE OBJECT + SOURCES ${draco_scene_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) draco_add_library( - NAME - draco_texture - TYPE - OBJECT - SOURCES - ${draco_texture_sources} - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths}) + NAME draco_texture + TYPE OBJECT + SOURCES ${draco_texture_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) endif() - list(APPEND draco_object_library_deps - draco_attributes - draco_compression_attributes_dec - draco_compression_attributes_enc - draco_compression_attributes_pred_schemes_dec - draco_compression_attributes_pred_schemes_enc - draco_compression_bit_coders - draco_compression_decode - draco_compression_encode - draco_compression_entropy - draco_compression_mesh_dec - draco_compression_mesh_enc - draco_compression_point_cloud_dec - draco_compression_point_cloud_enc - draco_core - draco_dec_config - draco_enc_config - draco_io - draco_mesh - draco_metadata - draco_metadata_dec - draco_metadata_enc - draco_animation - draco_animation_dec - draco_animation_enc - draco_point_cloud - draco_points_dec - draco_points_enc) + list( + APPEND + draco_object_library_deps + draco_attributes + draco_compression_attributes_dec + draco_compression_attributes_enc + draco_compression_attributes_pred_schemes_dec + draco_compression_attributes_pred_schemes_enc + draco_compression_bit_coders + draco_compression_decode + draco_compression_encode + draco_compression_entropy + draco_compression_mesh_dec + draco_compression_mesh_enc + draco_compression_options + draco_compression_point_cloud_dec + draco_compression_point_cloud_enc + draco_core + draco_dec_config + draco_enc_config + draco_io + draco_mesh + draco_metadata + draco_metadata_dec + draco_metadata_enc + draco_animation + draco_animation_dec + draco_animation_enc + draco_point_cloud + draco_points_dec + draco_points_enc) if(DRACO_TRANSCODER_SUPPORTED) - list(APPEND draco_object_library_deps - draco_material draco_scene draco_texture) + list(APPEND draco_object_library_deps draco_material draco_scene + draco_texture) endif() # Library targets that consume the object collections. @@ -958,49 +1019,33 @@ else() set(draco_lib_type STATIC) endif() - draco_add_library(NAME - draco - OUTPUT_NAME - draco - TYPE - ${draco_lib_type} - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths} - OBJLIB_DEPS - ${draco_object_library_deps}) + draco_add_library( + NAME draco + OUTPUT_NAME draco + TYPE ${draco_lib_type} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths} + OBJLIB_DEPS ${draco_object_library_deps}) add_library(draco::draco ALIAS draco) else() - draco_add_library(NAME - draco_static - OUTPUT_NAME - draco - TYPE - STATIC - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths} - OBJLIB_DEPS - ${draco_object_library_deps}) + draco_add_library( + NAME draco_static + OUTPUT_NAME draco + TYPE STATIC + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths} + OBJLIB_DEPS ${draco_object_library_deps}) if(BUILD_SHARED_LIBS) - draco_add_library(NAME - draco_shared - SOURCES - "${draco_src_root}/core/draco_version.h" - OUTPUT_NAME - draco - TYPE - SHARED - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths} - LIB_DEPS - draco_static) + draco_add_library( + NAME draco_shared + SOURCES "${draco_src_root}/core/draco_version.h" + OUTPUT_NAME draco + TYPE SHARED + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths} + LIB_DEPS draco_static) add_library(draco::draco ALIAS draco_shared) set_target_properties(draco_shared PROPERTIES EXPORT_NAME draco) else() @@ -1016,22 +1061,20 @@ set(unity_decoder_lib_type MODULE) endif() - draco_add_library(NAME draco_unity_plugin TYPE OBJECT SOURCES - ${draco_unity_plug_sources} DEFINES ${draco_defines} - INCLUDES ${draco_include_paths}) - - draco_add_library(NAME - dracodec_unity - TYPE - ${unity_decoder_lib_type} - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths} - OBJLIB_DEPS - draco_unity_plugin - LIB_DEPS - ${draco_plugin_dependency}) + draco_add_library( + NAME draco_unity_plugin + TYPE OBJECT + SOURCES ${draco_unity_plug_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + + draco_add_library( + NAME dracodec_unity + TYPE ${unity_decoder_lib_type} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths} + OBJLIB_DEPS draco_unity_plugin + LIB_DEPS ${draco_plugin_dependency}) # For Mac, we need to build a .bundle for the unity plugin. if(APPLE) @@ -1040,22 +1083,20 @@ endif() if(DRACO_MAYA_PLUGIN) - draco_add_library(NAME draco_maya_plugin TYPE OBJECT SOURCES - ${draco_maya_plug_sources} DEFINES ${draco_defines} - INCLUDES ${draco_include_paths}) - - draco_add_library(NAME - draco_maya_wrapper - TYPE - MODULE - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths} - OBJLIB_DEPS - draco_maya_plugin - LIB_DEPS - ${draco_plugin_dependency}) + draco_add_library( + NAME draco_maya_plugin + TYPE OBJECT + SOURCES ${draco_maya_plug_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths}) + + draco_add_library( + NAME draco_maya_wrapper + TYPE MODULE + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths} + OBJLIB_DEPS draco_maya_plugin + LIB_DEPS ${draco_plugin_dependency}) # For Mac, we need to build a .bundle for the plugin. if(APPLE) @@ -1064,44 +1105,30 @@ endif() # Draco app targets. - draco_add_executable(NAME - draco_decoder - SOURCES - "${draco_src_root}/tools/draco_decoder.cc" - ${draco_io_sources} - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths} - LIB_DEPS - ${draco_dependency}) - - draco_add_executable(NAME - draco_encoder - SOURCES - "${draco_src_root}/tools/draco_encoder.cc" - ${draco_io_sources} - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths} - LIB_DEPS - ${draco_dependency}) + draco_add_executable( + NAME draco_decoder + SOURCES "${draco_src_root}/tools/draco_decoder.cc" ${draco_io_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths} + LIB_DEPS ${draco_dependency}) + + draco_add_executable( + NAME draco_encoder + SOURCES "${draco_src_root}/tools/draco_encoder.cc" ${draco_io_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths} + LIB_DEPS ${draco_dependency}) if(DRACO_TRANSCODER_SUPPORTED) - draco_add_executable(NAME - draco_transcoder - SOURCES - "${draco_src_root}/tools/draco_transcoder.cc" - "${draco_src_root}/tools/draco_transcoder_lib.cc" - "${draco_src_root}/tools/draco_transcoder_lib.h" - ${draco_io_sources} - DEFINES - ${draco_defines} - INCLUDES - ${draco_include_paths} - LIB_DEPS - ${draco_dependency}) + draco_add_executable( + NAME draco_transcoder + SOURCES "${draco_src_root}/tools/draco_transcoder.cc" + "${draco_src_root}/tools/draco_transcoder_lib.cc" + "${draco_src_root}/tools/draco_transcoder_lib.h" + ${draco_io_sources} + DEFINES ${draco_defines} + INCLUDES ${draco_include_paths} + LIB_DEPS ${draco_dependency}) endif() draco_setup_install_target() diff -Nru draco-1.5.3+dfsg/debian/changelog draco-1.5.5+dfsg/debian/changelog --- draco-1.5.3+dfsg/debian/changelog 2022-09-11 00:27:39.000000000 +0000 +++ draco-1.5.5+dfsg/debian/changelog 2022-11-28 19:45:00.000000000 +0000 @@ -1,4 +1,4 @@ -draco (1.5.3+dfsg-2~18.04.sav0) bionic; urgency=medium +draco (1.5.5+dfsg-2~18.04.sav0) bionic; urgency=medium * Backport to Bionic * d/patches/: Modify 0003-Use-C-17-filesystem-library.patch for older GCC @@ -8,7 +8,21 @@ - Add -Wl,--as-needed to DEB_LDFLAGS_MAINT_APPEND (older binutils) * d/control: Set debhelper-compat (= 11) BD - -- Rob Savoury Sat, 10 Sep 2022 17:27:39 -0700 + -- Rob Savoury Mon, 28 Nov 2022 11:45:00 -0800 + +draco (1.5.5+dfsg-2) unstable; urgency=medium + + * Upload to unstable. + + -- Timo Röhling Thu, 03 Nov 2022 23:31:51 +0100 + +draco (1.5.5+dfsg-1) experimental; urgency=medium + + * Update d/watch + * New upstream version 1.5.5+dfsg + * SOVERSION bump to 7 + + -- Timo Röhling Mon, 31 Oct 2022 21:25:09 +0100 draco (1.5.3+dfsg-2) unstable; urgency=medium diff -Nru draco-1.5.3+dfsg/debian/control draco-1.5.5+dfsg/debian/control --- draco-1.5.3+dfsg/debian/control 2022-09-11 00:20:31.000000000 +0000 +++ draco-1.5.5+dfsg/debian/control 2022-11-28 19:44:51.000000000 +0000 @@ -3,24 +3,19 @@ Priority: optional Maintainer: Timo Röhling Build-Depends: - cmake, - debhelper-compat (= 11), - googletest, - nlohmann-json3-dev, - libeigen3-dev (>= 3.4), - libstb-dev, - libtinygltf-dev, + cmake, + debhelper-compat (= 11), + googletest, + libeigen3-dev (>= 3.4), + libstb-dev, + libtinygltf-dev, + nlohmann-json3-dev, Homepage: https://github.com/google/draco Standards-Version: 4.6.1 Rules-Requires-Root: no Vcs-Git: https://salsa.debian.org/roehling/draco.git Vcs-Browser: https://salsa.debian.org/roehling/draco - -Package: draco -Section: graphics -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} -Description: Encoder and decoder for 3D geometric meshes and point clouds +Description: Library for compressing 3D geometric meshes and point clouds Draco is a library for compressing and decompressing 3D geometric meshes and point clouds. It is intended to improve the storage and transmission of 3D graphics. @@ -33,45 +28,36 @@ now be downloaded faster, 3D graphics in the browser can load quicker, and VR and AR scenes can now be transmitted with a fraction of the bandwidth and rendered quickly. + +Package: draco +Section: graphics +Architecture: any +Depends: + ${misc:Depends}, + ${shlibs:Depends}, +Description: Encoder and decoder for 3D geometric meshes and point clouds + ${source:Extended-Description} . This package provides command line tools to compress and decompress files using Draco. -Package: libdraco5 +Package: libdraco7 Architecture: any Multi-Arch: same -Depends: ${shlibs:Depends}, ${misc:Depends} -Description: Library for compressing 3D geometric meshes and point clouds - Draco is a library for compressing and decompressing 3D geometric meshes - and point clouds. It is intended to improve the storage and transmission - of 3D graphics. - . - Draco is designed and built for compression efficiency and speed. The code - supports compressing points, connectivity information, texture coordinates, - color information, normals, and any other generic attributes associated with - geometry. With Draco, applications using 3D graphics can be significantly - smaller without compromising visual fidelity. For users, this means apps can - now be downloaded faster, 3D graphics in the browser can load quicker, and VR - and AR scenes can now be transmitted with a fraction of the bandwidth and - rendered quickly. +Depends: + ${misc:Depends}, + ${shlibs:Depends}, +Description: ${source:Synopsis} + ${source:Extended-Description} Package: libdraco-dev Section: libdevel Architecture: any Multi-Arch: same -Depends: libdraco5 (= ${binary:Version}), ${misc:Depends} -Description: Library for compressing 3D geometric meshes and point clouds (headers) - Draco is a library for compressing and decompressing 3D geometric meshes - and point clouds. It is intended to improve the storage and transmission - of 3D graphics. - . - Draco is designed and built for compression efficiency and speed. The code - supports compressing points, connectivity information, texture coordinates, - color information, normals, and any other generic attributes associated with - geometry. With Draco, applications using 3D graphics can be significantly - smaller without compromising visual fidelity. For users, this means apps can - now be downloaded faster, 3D graphics in the browser can load quicker, and VR - and AR scenes can now be transmitted with a fraction of the bandwidth and - rendered quickly. +Depends: + ${misc:Depends}, + libdraco7 (= ${binary:Version}), +Description: ${source:Synopsis} (headers) + ${source:Extended-Description} . This package provides the development headers for the library. diff -Nru draco-1.5.3+dfsg/debian/libdraco5.install draco-1.5.5+dfsg/debian/libdraco5.install --- draco-1.5.3+dfsg/debian/libdraco5.install 2022-07-09 20:01:45.000000000 +0000 +++ draco-1.5.5+dfsg/debian/libdraco5.install 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -usr/lib/*/lib*.so.* diff -Nru draco-1.5.3+dfsg/debian/libdraco7.install draco-1.5.5+dfsg/debian/libdraco7.install --- draco-1.5.3+dfsg/debian/libdraco7.install 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/debian/libdraco7.install 2022-10-31 20:09:45.000000000 +0000 @@ -0,0 +1 @@ +usr/lib/*/lib*.so.* diff -Nru draco-1.5.3+dfsg/debian/patches/0001-Fix-removal-of-build-dir-prefix-from-include-path.patch draco-1.5.5+dfsg/debian/patches/0001-Fix-removal-of-build-dir-prefix-from-include-path.patch --- draco-1.5.3+dfsg/debian/patches/0001-Fix-removal-of-build-dir-prefix-from-include-path.patch 2022-07-09 20:01:46.000000000 +0000 +++ draco-1.5.5+dfsg/debian/patches/0001-Fix-removal-of-build-dir-prefix-from-include-path.patch 2022-10-31 20:13:28.000000000 +0000 @@ -7,7 +7,7 @@ 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cmake/draco_install.cmake b/cmake/draco_install.cmake -index e15a054..df2bf26 100644 +index f7e1153..7789e6a 100644 --- a/cmake/draco_install.cmake +++ b/cmake/draco_install.cmake @@ -36,11 +36,10 @@ macro(draco_setup_install_target) diff -Nru draco-1.5.3+dfsg/debian/patches/0002-Install-proper-CMake-targets.patch draco-1.5.5+dfsg/debian/patches/0002-Install-proper-CMake-targets.patch --- draco-1.5.3+dfsg/debian/patches/0002-Install-proper-CMake-targets.patch 2022-07-09 20:01:46.000000000 +0000 +++ draco-1.5.5+dfsg/debian/patches/0002-Install-proper-CMake-targets.patch 2022-10-31 20:13:28.000000000 +0000 @@ -7,10 +7,10 @@ 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/draco_install.cmake b/cmake/draco_install.cmake -index df2bf26..48a7e2c 100644 +index 7789e6a..296387e 100644 --- a/cmake/draco_install.cmake +++ b/cmake/draco_install.cmake -@@ -97,7 +97,7 @@ macro(draco_setup_install_target) +@@ -96,7 +96,7 @@ macro(draco_setup_install_target) configure_package_config_file( "${draco_root}/cmake/draco-config.cmake.template" "${draco_build}/draco-config.cmake" @@ -19,17 +19,15 @@ write_basic_package_version_file( "${draco_build}/draco-config-version.cmake" -@@ -113,11 +113,11 @@ macro(draco_setup_install_target) +@@ -112,9 +112,9 @@ macro(draco_setup_install_target) EXPORT dracoExport NAMESPACE draco:: FILE draco-targets.cmake - DESTINATION "${data_path}/cmake/draco") + DESTINATION "${libs_path}/cmake/draco") - install( - FILES - "${draco_build}/draco-config.cmake" - "${draco_build}/draco-config-version.cmake" -- DESTINATION "${data_path}/cmake/draco") -+ DESTINATION "${libs_path}/cmake/draco") + install(FILES "${draco_build}/draco-config.cmake" + "${draco_build}/draco-config-version.cmake" +- DESTINATION "${data_path}/cmake/draco") ++ DESTINATION "${libs_path}/cmake/draco") endmacro() diff -Nru draco-1.5.3+dfsg/debian/patches/0004-Install-missing-files.patch draco-1.5.5+dfsg/debian/patches/0004-Install-missing-files.patch --- draco-1.5.3+dfsg/debian/patches/0004-Install-missing-files.patch 2022-09-11 00:26:11.000000000 +0000 +++ draco-1.5.5+dfsg/debian/patches/0004-Install-missing-files.patch 2022-10-31 20:13:28.000000000 +0000 @@ -8,22 +8,22 @@ 2 files changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt -index 11b07e1..3bd811a 100644 +index 0f07ff0..2b587c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -232,6 +232,7 @@ list( - "${draco_src_root}/compression/bit_coders/symbol_bit_encoder.h") - - list(APPEND draco_enc_config_sources -+ "${draco_src_root}/compression/draco_compression_options.h" - "${draco_src_root}/compression/config/compression_shared.h" - "${draco_src_root}/compression/config/draco_options.h" - "${draco_src_root}/compression/config/encoder_options.h" +@@ -237,6 +237,7 @@ list( + list( + APPEND + draco_enc_config_sources ++ "${draco_src_root}/compression/draco_compression_options.h" + "${draco_src_root}/compression/config/compression_shared.h" + "${draco_src_root}/compression/config/draco_options.h" + "${draco_src_root}/compression/config/encoder_options.h" diff --git a/cmake/draco_install.cmake b/cmake/draco_install.cmake -index 48a7e2c..ff55bd7 100644 +index 296387e..2623091 100644 --- a/cmake/draco_install.cmake +++ b/cmake/draco_install.cmake -@@ -52,6 +52,9 @@ macro(draco_setup_install_target) +@@ -51,6 +51,9 @@ macro(draco_setup_install_target) install(TARGETS draco_decoder DESTINATION "${bin_path}") install(TARGETS draco_encoder DESTINATION "${bin_path}") diff -Nru draco-1.5.3+dfsg/debian/watch draco-1.5.5+dfsg/debian/watch --- draco-1.5.3+dfsg/debian/watch 2022-02-22 08:30:00.000000000 +0000 +++ draco-1.5.5+dfsg/debian/watch 2022-10-31 20:09:45.000000000 +0000 @@ -1,5 +1,6 @@ version=4 -opts=filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/@PACKAGE@-$1.tar.gz/,\ - dversionmangle=s/\+dfsg\d*$//,oversionmangle=s/$/+dfsg/,\ - repack \ -https://github.com/google/draco/releases/latest .*/v?(\d\S+)\.tar\.gz +opts=searchmode=plain,filenamemangle=s%(?:.*?/)?v?@ANY_VERSION@%@PACKAGE@-$1.tar.gz%,\ + dversionmangle=auto,oversionmangle=s/$/+dfsg/,repack \ + https://api.github.com/repos/google/draco/tags \ + tarball/(?:.*?/)?v?@ANY_VERSION@ + diff -Nru draco-1.5.3+dfsg/.gitattributes draco-1.5.5+dfsg/.gitattributes --- draco-1.5.3+dfsg/.gitattributes 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/.gitattributes 2022-10-29 00:55:03.000000000 +0000 @@ -1 +1 @@ -*.obj eol=lf +*.obj eol=lf \ No newline at end of file diff -Nru draco-1.5.3+dfsg/README.md draco-1.5.5+dfsg/README.md --- draco-1.5.3+dfsg/README.md 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/README.md 2022-10-29 00:55:03.000000000 +0000 @@ -14,6 +14,21 @@ new Draco releases are launched. To avoid the issue pin your sites to a versioned release. +### Version 1.5.5 release: +* Using the versioned www.gstatic.com WASM and Javascript decoders continues + to be recommended. To use v1.5.5, use this URL: + * https://www.gstatic.com/draco/versioned/decoders/1.5.5/* +* Bug fix: https://github.com/google/draco/issues/935 + +### Version 1.5.4 release: +* Using the versioned www.gstatic.com WASM and Javascript decoders continues + to be recommended. To use v1.5.4, use this URL: + * https://www.gstatic.com/draco/versioned/decoders/1.5.4/* +* Added partial support for glTF extensions EXT_mesh_features and + EXT_structural_metadata. +* Bug fixes. +* Security fixes. + ### Version 1.5.3 release: * Using the versioned www.gstatic.com WASM and Javascript decoders continues to be recommended. To use v1.5.3, use this URL: diff -Nru draco-1.5.3+dfsg/src/draco/attributes/geometry_attribute.cc draco-1.5.5+dfsg/src/draco/attributes/geometry_attribute.cc --- draco-1.5.3+dfsg/src/draco/attributes/geometry_attribute.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/attributes/geometry_attribute.cc 2022-10-29 00:55:03.000000000 +0000 @@ -26,7 +26,7 @@ unique_id_(0) {} void GeometryAttribute::Init(GeometryAttribute::Type attribute_type, - DataBuffer *buffer, int8_t num_components, + DataBuffer *buffer, uint8_t num_components, DataType data_type, bool normalized, int64_t byte_stride, int64_t byte_offset) { buffer_ = buffer; diff -Nru draco-1.5.3+dfsg/src/draco/attributes/geometry_attribute.h draco-1.5.5+dfsg/src/draco/attributes/geometry_attribute.h --- draco-1.5.3+dfsg/src/draco/attributes/geometry_attribute.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/attributes/geometry_attribute.h 2022-10-29 00:55:03.000000000 +0000 @@ -15,6 +15,7 @@ #ifndef DRACO_ATTRIBUTES_GEOMETRY_ATTRIBUTE_H_ #define DRACO_ATTRIBUTES_GEOMETRY_ATTRIBUTE_H_ +#include #include #include #include @@ -23,6 +24,9 @@ #include "draco/core/data_buffer.h" #include "draco/core/hash_utils.h" #include "draco/draco_features.h" +#ifdef DRACO_TRANSCODER_SUPPORTED +#include "draco/core/status.h" +#endif namespace draco { @@ -70,7 +74,7 @@ GeometryAttribute(); // Initializes and enables the attribute. - void Init(Type attribute_type, DataBuffer *buffer, int8_t num_components, + void Init(Type attribute_type, DataBuffer *buffer, uint8_t num_components, DataType data_type, bool normalized, int64_t byte_stride, int64_t byte_offset); bool IsValid() const { return buffer_ != nullptr; } @@ -141,6 +145,17 @@ buffer_->Write(byte_pos, value, byte_stride()); } +#ifdef DRACO_TRANSCODER_SUPPORTED + // Sets a value of an attribute entry. The input |value| must have + // |input_num_components| entries and it will be automatically converted to + // the internal format used by the geometry attribute. If the conversion is + // not possible, an error status will be returned. + template + Status ConvertAndSetAttributeValue(AttributeValueIndex avi, + int input_num_components, + const InputT *value); +#endif + // DEPRECATED: Use // ConvertValue(AttributeValueIndex att_id, // int out_num_components, @@ -245,10 +260,11 @@ // Returns the number of components that are stored for each entry. // For position attribute this is usually three (x,y,z), // while texture coordinates have two components (u,v). - int8_t num_components() const { return num_components_; } + uint8_t num_components() const { return num_components_; } // Indicates whether the data type should be normalized before interpretation, // that is, it should be divided by the max value of the data type. bool normalized() const { return normalized_; } + void set_normalized(bool normalized) { normalized_ = normalized; } // The buffer storing the entire data of the attribute. const DataBuffer *buffer() const { return buffer_; } // Returns the number of bytes between two attribute entries, this is, at @@ -272,7 +288,7 @@ // T is the stored attribute data type. // OutT is the desired data type of the attribute. template - bool ConvertTypedValue(AttributeValueIndex att_id, int8_t out_num_components, + bool ConvertTypedValue(AttributeValueIndex att_id, uint8_t out_num_components, OutT *out_value) const { const uint8_t *src_address = GetAddress(att_id); @@ -282,68 +298,132 @@ return false; } const T in_value = *reinterpret_cast(src_address); + if (!ConvertComponentValue(in_value, normalized_, + out_value + i)) { + return false; + } + src_address += sizeof(T); + } + // Fill empty data for unused output components if needed. + for (int i = num_components_; i < out_num_components; ++i) { + out_value[i] = static_cast(0); + } + return true; + } - // Make sure the |in_value| can be represented as an integral type OutT. - if (std::is_integral::value) { - // Make sure the |in_value| fits within the range of values that OutT - // is able to represent. Perform the check only for integral types. - if (!std::is_same::value && std::is_integral::value) { - static constexpr OutT kOutMin = - std::is_signed::value ? std::numeric_limits::min() : 0; - if (in_value < kOutMin || - in_value > std::numeric_limits::max()) { - return false; - } +#ifdef DRACO_TRANSCODER_SUPPORTED + // Function that converts input |value| from type T to the internal attribute + // representation defined by OutT and |num_components_|. + template + Status ConvertAndSetAttributeTypedValue(AttributeValueIndex avi, + int8_t input_num_components, + const T *value) { + uint8_t *address = GetAddress(avi); + + // Convert all components available in both the original and output formats. + for (int i = 0; i < num_components_; ++i) { + if (!IsAddressValid(address)) { + return ErrorStatus("GeometryAttribute: Invalid address."); + } + OutT *const out_value = reinterpret_cast(address); + if (i < input_num_components) { + if (!ConvertComponentValue(*(value + i), normalized_, + out_value)) { + return ErrorStatus( + "GeometryAttribute: Failed to convert component value."); + } + } else { + *out_value = static_cast(0); + } + address += sizeof(OutT); + } + return OkStatus(); + } +#endif // DRACO_TRANSCODER_SUPPORTED + + // Converts |in_value| of type T into |out_value| of type OutT. If + // |normalized| is true, any conversion between floating point and integer + // values will be treating integers as normalized types (the entire integer + // range will be used to represent 0-1 floating point range). + template + static bool ConvertComponentValue(const T &in_value, bool normalized, + OutT *out_value) { + // Make sure the |in_value| can be represented as an integral type OutT. + if (std::is_integral::value) { + // Make sure the |in_value| fits within the range of values that OutT + // is able to represent. Perform the check only for integral types. + if (!std::is_same::value && std::is_integral::value) { + static constexpr OutT kOutMin = + std::is_signed::value ? std::numeric_limits::min() : 0; + if (in_value < kOutMin || in_value > std::numeric_limits::max()) { + return false; } + } - // Check conversion of floating point |in_value| to integral value OutT. - if (std::is_floating_point::value) { - // Make sure the floating point |in_value| is not NaN and not Inf as - // integral type OutT is unable to represent these values. - if (sizeof(in_value) > sizeof(double)) { - if (std::isnan(static_cast(in_value)) || - std::isinf(static_cast(in_value))) { - return false; - } - } else if (sizeof(in_value) > sizeof(float)) { - if (std::isnan(static_cast(in_value)) || - std::isinf(static_cast(in_value))) { - return false; - } - } else { - if (std::isnan(static_cast(in_value)) || - std::isinf(static_cast(in_value))) { - return false; - } + // Check conversion of floating point |in_value| to integral value OutT. + if (std::is_floating_point::value) { + // Make sure the floating point |in_value| is not NaN and not Inf as + // integral type OutT is unable to represent these values. + if (sizeof(in_value) > sizeof(double)) { + if (std::isnan(static_cast(in_value)) || + std::isinf(static_cast(in_value))) { + return false; } - - // Make sure the floating point |in_value| fits within the range of - // values that integral type OutT is able to represent. - if (in_value < std::numeric_limits::min() || - in_value >= std::numeric_limits::max()) { + } else if (sizeof(in_value) > sizeof(float)) { + if (std::isnan(static_cast(in_value)) || + std::isinf(static_cast(in_value))) { + return false; + } + } else { + if (std::isnan(static_cast(in_value)) || + std::isinf(static_cast(in_value))) { return false; } } + + // Make sure the floating point |in_value| fits within the range of + // values that integral type OutT is able to represent. + if (in_value < std::numeric_limits::min() || + in_value >= std::numeric_limits::max()) { + return false; + } } + } - out_value[i] = static_cast(in_value); + if (std::is_integral::value && std::is_floating_point::value && + normalized) { // When converting integer to floating point, normalize the value if // necessary. - if (std::is_integral::value && std::is_floating_point::value && - normalized_) { - out_value[i] /= static_cast(std::numeric_limits::max()); + *out_value = static_cast(in_value); + *out_value /= static_cast(std::numeric_limits::max()); + } else if (std::is_floating_point::value && + std::is_integral::value && normalized) { + // Converting from floating point to a normalized integer. + if (in_value > 1 || in_value < 0) { + // Normalized float values need to be between 0 and 1. + return false; } - // TODO(ostava): Add handling of normalized attributes when converting - // between different integer representations. If the attribute is - // normalized, integer values should be converted as if they represent 0-1 - // range. E.g. when we convert uint16 to uint8, the range <0, 2^16 - 1> - // should be converted to range <0, 2^8 - 1>. - src_address += sizeof(T); - } - // Fill empty data for unused output components if needed. - for (int i = num_components_; i < out_num_components; ++i) { - out_value[i] = static_cast(0); + // TODO(ostava): Consider allowing float to normalized integer conversion + // for 64-bit integer types. Currently it doesn't work because we don't + // have a floating point type that could store all 64 bit integers. + if (sizeof(OutT) > 4) { + return false; + } + // Expand the float to the range of the output integer and round it to the + // nearest representable value. Use doubles for the math to ensure the + // integer values are represented properly during the conversion process. + *out_value = static_cast(std::floor( + in_value * static_cast(std::numeric_limits::max()) + + 0.5)); + } else { + *out_value = static_cast(in_value); } + + // TODO(ostava): Add handling of normalized attributes when converting + // between different integer representations. If the attribute is + // normalized, integer values should be converted as if they represent 0-1 + // range. E.g. when we convert uint16 to uint8, the range <0, 2^16 - 1> + // should be converted to range <0, 2^8 - 1>. return true; } @@ -352,7 +432,7 @@ // attribute. The purpose is to detect if any changes happened to the buffer // since the time it was attached. DataBufferDescriptor buffer_descriptor_; - int8_t num_components_; + uint8_t num_components_; DataType data_type_; bool normalized_; int64_t byte_stride_; @@ -368,6 +448,54 @@ friend struct GeometryAttributeHasher; }; +#ifdef DRACO_TRANSCODER_SUPPORTED +template +Status GeometryAttribute::ConvertAndSetAttributeValue(AttributeValueIndex avi, + int input_num_components, + const InputT *value) { + switch (this->data_type()) { + case DT_INT8: + return ConvertAndSetAttributeTypedValue( + avi, input_num_components, value); + case DT_UINT8: + return ConvertAndSetAttributeTypedValue( + avi, input_num_components, value); + case DT_INT16: + return ConvertAndSetAttributeTypedValue( + avi, input_num_components, value); + case DT_UINT16: + return ConvertAndSetAttributeTypedValue( + avi, input_num_components, value); + case DT_INT32: + return ConvertAndSetAttributeTypedValue( + avi, input_num_components, value); + case DT_UINT32: + return ConvertAndSetAttributeTypedValue( + avi, input_num_components, value); + case DT_INT64: + return ConvertAndSetAttributeTypedValue( + avi, input_num_components, value); + case DT_UINT64: + return ConvertAndSetAttributeTypedValue( + avi, input_num_components, value); + case DT_FLOAT32: + return ConvertAndSetAttributeTypedValue( + avi, input_num_components, value); + case DT_FLOAT64: + return ConvertAndSetAttributeTypedValue( + avi, input_num_components, value); + case DT_BOOL: + return ConvertAndSetAttributeTypedValue( + avi, input_num_components, value); + default: + break; + } + return ErrorStatus( + "GeometryAttribute::SetAndConvertAttributeValue: Unsupported " + "attribute type."); +} +#endif + // Hashing support // Function object for using Attribute as a hash key. diff -Nru draco-1.5.3+dfsg/src/draco/compression/attributes/kd_tree_attributes_decoder.cc draco-1.5.5+dfsg/src/draco/compression/attributes/kd_tree_attributes_decoder.cc --- draco-1.5.3+dfsg/src/draco/compression/attributes/kd_tree_attributes_decoder.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/attributes/kd_tree_attributes_decoder.cc 2022-10-29 00:55:03.000000000 +0000 @@ -78,10 +78,13 @@ DRACO_DCHECK_EQ(attributes_.size(), 1); // Expect only ONE attribute. AttributeTuple &att = attributes_[0]; PointAttribute *attribute = std::get<0>(att); + const AttributeValueIndex avi = attribute->mapped_index(point_id_); + if (avi >= static_cast(attribute->size())) { + return *this; + } const uint32_t &offset = std::get<1>(att); DRACO_DCHECK_EQ(offset, 0); // expected to be zero - attribute->SetAttributeValue(attribute->mapped_index(point_id_), - &val[0] + offset); + attribute->SetAttributeValue(avi, &val[0] + offset); return *this; } // Additional operator taking std::vector as argument. @@ -89,6 +92,10 @@ for (auto index = 0; index < attributes_.size(); index++) { AttributeTuple &att = attributes_[index]; PointAttribute *attribute = std::get<0>(att); + const AttributeValueIndex avi = attribute->mapped_index(point_id_); + if (avi >= static_cast(attribute->size())) { + return *this; + } const uint32_t &offset = std::get<1>(att); const uint32_t &data_size = std::get<3>(att); const uint32_t &num_components = std::get<4>(att); @@ -103,10 +110,6 @@ // redirect to copied data data_source = reinterpret_cast(data_); } - const AttributeValueIndex avi = attribute->mapped_index(point_id_); - if (avi >= static_cast(attribute->size())) { - return *this; - } attribute->SetAttributeValue(avi, data_source); } return *this; @@ -195,54 +198,55 @@ data_size, num_components); total_dimensionality += num_components; } - PointAttributeVectorOutputIterator out_it(atts); + typedef PointAttributeVectorOutputIterator OutIt; + OutIt out_it(atts); switch (compression_level) { case 0: { - DynamicIntegerPointsKdTreeDecoder<0> decoder(total_dimensionality); - if (!decoder.DecodePoints(in_buffer, out_it)) { + if (!DecodePoints<0, OutIt>(total_dimensionality, num_points, in_buffer, + &out_it)) { return false; } break; } case 1: { - DynamicIntegerPointsKdTreeDecoder<1> decoder(total_dimensionality); - if (!decoder.DecodePoints(in_buffer, out_it)) { + if (!DecodePoints<1, OutIt>(total_dimensionality, num_points, in_buffer, + &out_it)) { return false; } break; } case 2: { - DynamicIntegerPointsKdTreeDecoder<2> decoder(total_dimensionality); - if (!decoder.DecodePoints(in_buffer, out_it)) { + if (!DecodePoints<2, OutIt>(total_dimensionality, num_points, in_buffer, + &out_it)) { return false; } break; } case 3: { - DynamicIntegerPointsKdTreeDecoder<3> decoder(total_dimensionality); - if (!decoder.DecodePoints(in_buffer, out_it)) { + if (!DecodePoints<3, OutIt>(total_dimensionality, num_points, in_buffer, + &out_it)) { return false; } break; } case 4: { - DynamicIntegerPointsKdTreeDecoder<4> decoder(total_dimensionality); - if (!decoder.DecodePoints(in_buffer, out_it)) { + if (!DecodePoints<4, OutIt>(total_dimensionality, num_points, in_buffer, + &out_it)) { return false; } break; } case 5: { - DynamicIntegerPointsKdTreeDecoder<5> decoder(total_dimensionality); - if (!decoder.DecodePoints(in_buffer, out_it)) { + if (!DecodePoints<5, OutIt>(total_dimensionality, num_points, in_buffer, + &out_it)) { return false; } break; } case 6: { - DynamicIntegerPointsKdTreeDecoder<6> decoder(total_dimensionality); - if (!decoder.DecodePoints(in_buffer, out_it)) { + if (!DecodePoints<6, OutIt>(total_dimensionality, num_points, in_buffer, + &out_it)) { return false; } break; @@ -252,6 +256,19 @@ } return true; } + +template +bool KdTreeAttributesDecoder::DecodePoints(int total_dimensionality, + int num_expected_points, + DecoderBuffer *in_buffer, + OutIteratorT *out_iterator) { + DynamicIntegerPointsKdTreeDecoder decoder(total_dimensionality); + if (!decoder.DecodePoints(in_buffer, *out_iterator) || + decoder.num_decoded_points() != num_expected_points) { + return false; + } + return true; +} bool KdTreeAttributesDecoder::DecodeDataNeededByPortableTransforms( DecoderBuffer *in_buffer) { diff -Nru draco-1.5.3+dfsg/src/draco/compression/attributes/kd_tree_attributes_decoder.h draco-1.5.5+dfsg/src/draco/compression/attributes/kd_tree_attributes_decoder.h --- draco-1.5.3+dfsg/src/draco/compression/attributes/kd_tree_attributes_decoder.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/attributes/kd_tree_attributes_decoder.h 2022-10-29 00:55:03.000000000 +0000 @@ -31,6 +31,10 @@ bool TransformAttributesToOriginalFormat() override; private: + template + bool DecodePoints(int total_dimensionality, int num_expected_points, + DecoderBuffer *in_buffer, OutIteratorT *out_iterator); + template bool TransformAttributeBackToSignedType(PointAttribute *att, int num_processed_signed_components); diff -Nru draco-1.5.3+dfsg/src/draco/compression/attributes/normal_compression_utils.h draco-1.5.5+dfsg/src/draco/compression/attributes/normal_compression_utils.h --- draco-1.5.3+dfsg/src/draco/compression/attributes/normal_compression_utils.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/attributes/normal_compression_utils.h 2022-10-29 00:55:03.000000000 +0000 @@ -208,7 +208,9 @@ DRACO_DCHECK_LE(t, center_value_); DRACO_DCHECK_GE(s, -center_value_); DRACO_DCHECK_GE(t, -center_value_); - return std::abs(s) + std::abs(t) <= center_value_; + const uint32_t st = + static_cast(std::abs(s)) + static_cast(std::abs(t)); + return st <= center_value_; } void InvertDiamond(int32_t *s, int32_t *t) const { @@ -230,19 +232,29 @@ sign_t = (*t > 0) ? 1 : -1; } - const int32_t corner_point_s = sign_s * center_value_; - const int32_t corner_point_t = sign_t * center_value_; - *s = 2 * *s - corner_point_s; - *t = 2 * *t - corner_point_t; + // Perform the addition and subtraction using unsigned integers to avoid + // signed integer overflows for bad data. Note that the result will be + // unchanged for non-overflowing cases. + const uint32_t corner_point_s = sign_s * center_value_; + const uint32_t corner_point_t = sign_t * center_value_; + uint32_t us = *s; + uint32_t ut = *t; + us = us + us - corner_point_s; + ut = ut + ut - corner_point_t; if (sign_s * sign_t >= 0) { - int32_t temp = *s; - *s = -*t; - *t = -temp; + uint32_t temp = us; + us = -ut; + ut = -temp; } else { - std::swap(*s, *t); + std::swap(us, ut); } - *s = (*s + corner_point_s) / 2; - *t = (*t + corner_point_t) / 2; + us = us + corner_point_s; + ut = ut + corner_point_t; + + *s = us; + *t = ut; + *s /= 2; + *t /= 2; } void InvertDirection(int32_t *s, int32_t *t) const { diff -Nru draco-1.5.3+dfsg/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_decoder.h draco-1.5.5+dfsg/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_decoder.h --- draco-1.5.3+dfsg/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_decoder.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_decoder.h 2022-10-29 00:55:03.000000000 +0000 @@ -22,6 +22,7 @@ #include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h" #include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h" #include "draco/compression/bit_coders/rans_bit_decoder.h" +#include "draco/core/math_utils.h" #include "draco/core/varint_decoding.h" #include "draco/draco_features.h" @@ -161,7 +162,8 @@ if (!is_crease) { ++num_used_parallelograms; for (int j = 0; j < num_components; ++j) { - multi_pred_vals[j] += pred_vals[i][j]; + multi_pred_vals[j] = + AddAsUnsigned(multi_pred_vals[j], pred_vals[i][j]); } } } diff -Nru draco-1.5.3+dfsg/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_decoder.h draco-1.5.5+dfsg/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_decoder.h --- draco-1.5.3+dfsg/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_decoder.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_decoder.h 2022-10-29 00:55:03.000000000 +0000 @@ -18,6 +18,7 @@ #include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h" #include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h" +#include "draco/core/math_utils.h" #include "draco/draco_features.h" namespace draco { @@ -89,7 +90,8 @@ p, corner_id, table, *vertex_to_data_map, out_data, num_components, parallelogram_pred_vals.get())) { for (int c = 0; c < num_components; ++c) { - pred_vals[c] += parallelogram_pred_vals[c]; + pred_vals[c] = + AddAsUnsigned(pred_vals[c], parallelogram_pred_vals[c]); } ++num_parallelograms; } diff -Nru draco-1.5.3+dfsg/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h draco-1.5.5+dfsg/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h --- draco-1.5.3+dfsg/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h 2022-10-29 00:55:03.000000000 +0000 @@ -17,6 +17,9 @@ #include +#include +#include + #include "draco/attributes/point_attribute.h" #include "draco/core/math_utils.h" #include "draco/core/vector_d.h" @@ -105,10 +108,14 @@ next_data_id = mesh_data_.vertex_to_data_map()->at(next_vert_id); prev_data_id = mesh_data_.vertex_to_data_map()->at(prev_vert_id); + typedef VectorD Vec2; + typedef VectorD Vec3; + typedef VectorD Vec2u; + if (prev_data_id < data_id && next_data_id < data_id) { // Both other corners have available UV coordinates for prediction. - const VectorD n_uv = GetTexCoordForEntryId(next_data_id, data); - const VectorD p_uv = GetTexCoordForEntryId(prev_data_id, data); + const Vec2 n_uv = GetTexCoordForEntryId(next_data_id, data); + const Vec2 p_uv = GetTexCoordForEntryId(prev_data_id, data); if (p_uv == n_uv) { // We cannot do a reliable prediction on degenerated UV triangles. predicted_value_[0] = p_uv[0]; @@ -117,9 +124,9 @@ } // Get positions at all corners. - const VectorD tip_pos = GetPositionForEntryId(data_id); - const VectorD next_pos = GetPositionForEntryId(next_data_id); - const VectorD prev_pos = GetPositionForEntryId(prev_data_id); + const Vec3 tip_pos = GetPositionForEntryId(data_id); + const Vec3 next_pos = GetPositionForEntryId(next_data_id); + const Vec3 prev_pos = GetPositionForEntryId(prev_data_id); // We use the positions of the above triangle to predict the texture // coordinate on the tip corner C. // To convert the triangle into the UV coordinate system we first compute @@ -135,17 +142,17 @@ // Where next_pos is point (N), prev_pos is point (P) and tip_pos is the // position of predicted coordinate (C). // - const VectorD pn = prev_pos - next_pos; + const Vec3 pn = prev_pos - next_pos; const uint64_t pn_norm2_squared = pn.SquaredNorm(); if (pn_norm2_squared != 0) { // Compute the projection of C onto PN by computing dot product of CN with // PN and normalizing it by length of PN. This gives us a factor |s| where // |s = PN.Dot(CN) / PN.SquaredNorm2()|. This factor can be used to // compute X in UV space |X_UV| as |X_UV = N_UV + s * PN_UV|. - const VectorD cn = tip_pos - next_pos; + const Vec3 cn = tip_pos - next_pos; const int64_t cn_dot_pn = pn.Dot(cn); - const VectorD pn_uv = p_uv - n_uv; + const Vec2 pn_uv = p_uv - n_uv; // Because we perform all computations with integers, we don't explicitly // compute the normalized factor |s|, but rather we perform all operations // over UV vectors in a non-normalized coordinate system scaled with a @@ -153,19 +160,23 @@ // // x_uv = X_UV * PN.Norm2Squared() // - const VectorD x_uv = - n_uv * pn_norm2_squared + (cn_dot_pn * pn_uv); - + const int64_t n_uv_absmax_element = + std::max(std::abs(n_uv[0]), std::abs(n_uv[1])); + if (n_uv_absmax_element > + std::numeric_limits::max() / pn_norm2_squared) { + // Return false if the below multiplication would overflow. + return false; + } + const Vec2 x_uv = n_uv * pn_norm2_squared + (cn_dot_pn * pn_uv); const int64_t pn_absmax_element = std::max(std::max(std::abs(pn[0]), std::abs(pn[1])), std::abs(pn[2])); if (cn_dot_pn > std::numeric_limits::max() / pn_absmax_element) { - // return false if squared length calculation would overflow. + // Return false if squared length calculation would overflow. return false; } // Compute squared length of vector CX in position coordinate system: - const VectorD x_pos = - next_pos + (cn_dot_pn * pn) / pn_norm2_squared; + const Vec3 x_pos = next_pos + (cn_dot_pn * pn) / pn_norm2_squared; const uint64_t cx_norm2_squared = (tip_pos - x_pos).SquaredNorm(); // Compute vector CX_UV in the uv space by rotating vector PN_UV by 90 @@ -182,7 +193,7 @@ // // cx_uv = CX.Norm2() * PN.Norm2() * Rot(PN_UV) // - VectorD cx_uv(pn_uv[1], -pn_uv[0]); // Rotated PN_UV. + Vec2 cx_uv(pn_uv[1], -pn_uv[0]); // Rotated PN_UV. // Compute CX.Norm2() * PN.Norm2() const uint64_t norm_squared = IntSqrt(cx_norm2_squared * pn_norm2_squared); @@ -191,17 +202,15 @@ // Predicted uv coordinate is then computed by either adding or // subtracting CX_UV to/from X_UV. - VectorD predicted_uv; + Vec2 predicted_uv; if (is_encoder_t) { // When encoding, compute both possible vectors and determine which one // results in a better prediction. // Both vectors need to be transformed back from the scaled space to // the real UV coordinate space. - const VectorD predicted_uv_0((x_uv + cx_uv) / - pn_norm2_squared); - const VectorD predicted_uv_1((x_uv - cx_uv) / - pn_norm2_squared); - const VectorD c_uv = GetTexCoordForEntryId(data_id, data); + const Vec2 predicted_uv_0((x_uv + cx_uv) / pn_norm2_squared); + const Vec2 predicted_uv_1((x_uv - cx_uv) / pn_norm2_squared); + const Vec2 c_uv = GetTexCoordForEntryId(data_id, data); if ((c_uv - predicted_uv_0).SquaredNorm() < (c_uv - predicted_uv_1).SquaredNorm()) { predicted_uv = predicted_uv_0; @@ -217,10 +226,12 @@ } const bool orientation = orientations_.back(); orientations_.pop_back(); + // Perform operations in unsigned type to avoid signed integer overflow. + // Note that the result will be the same (for non-overflowing values). if (orientation) { - predicted_uv = (x_uv + cx_uv) / pn_norm2_squared; + predicted_uv = Vec2(Vec2u(x_uv) + Vec2u(cx_uv)) / pn_norm2_squared; } else { - predicted_uv = (x_uv - cx_uv) / pn_norm2_squared; + predicted_uv = Vec2(Vec2u(x_uv) - Vec2u(cx_uv)) / pn_norm2_squared; } } predicted_value_[0] = static_cast(predicted_uv[0]); diff -Nru draco-1.5.3+dfsg/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc draco-1.5.5+dfsg/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc --- draco-1.5.3+dfsg/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc 2022-10-29 00:55:03.000000000 +0000 @@ -30,12 +30,16 @@ } if (encoder->GetGeometryType() == TRIANGULAR_MESH) { // Use speed setting to select the best encoding method. + const int att_quant = + options.GetAttributeInt(att_id, "quantization_bits", -1); const PointAttribute *const att = encoder->point_cloud()->attribute(att_id); - if (att->attribute_type() == GeometryAttribute::TEX_COORD && + if (att_quant != -1 && + att->attribute_type() == GeometryAttribute::TEX_COORD && att->num_components() == 2) { // Texture coordinate predictor needs a position attribute that is either // integer or quantized. For numerical reasons, we require the position - // quantization to be at most 21 bits (TODO(b/231259902)). + // quantization to be at most 21 bits and the 2*position_quantization + + // uv_quantization < 64 (TODO(b/231259902)). const PointAttribute *const pos_att = encoder->point_cloud()->GetNamedAttribute( GeometryAttribute::POSITION); @@ -49,8 +53,10 @@ GeometryAttribute::POSITION); const int pos_quant = options.GetAttributeInt(pos_att_id, "quantization_bits", -1); - // Must be quantized but the quantization is restricted to 21 bits. - if (pos_quant > 0 && pos_quant <= 21) { + // Must be quantized but the quantization is restricted to 21 bits and + // 2*|pos_quant|+|att_quant| must be smaller than 64 bits. + if (pos_quant > 0 && pos_quant <= 21 && + 2 * pos_quant + att_quant < 64) { is_pos_att_valid = true; } } diff -Nru draco-1.5.3+dfsg/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_decoding_transform.h draco-1.5.5+dfsg/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_decoding_transform.h --- draco-1.5.3+dfsg/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_decoding_transform.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_decoding_transform.h 2022-10-29 00:55:03.000000000 +0000 @@ -21,6 +21,7 @@ #include "draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h" #include "draco/core/decoder_buffer.h" #include "draco/core/macros.h" +#include "draco/core/math_utils.h" #include "draco/core/vector_d.h" namespace draco { @@ -98,9 +99,8 @@ if (!pred_is_in_bottom_left) { pred = this->RotatePoint(pred, rotation_count); } - Point2 orig = pred + corr; - orig[0] = this->ModMax(orig[0]); - orig[1] = this->ModMax(orig[1]); + Point2 orig(this->ModMax(AddAsUnsigned(pred[0], corr[0])), + this->ModMax(AddAsUnsigned(pred[1], corr[1]))); if (!pred_is_in_bottom_left) { const int32_t reverse_rotation_count = (4 - rotation_count) % 4; orig = this->RotatePoint(orig, reverse_rotation_count); diff -Nru draco-1.5.3+dfsg/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_decoding_transform.h draco-1.5.5+dfsg/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_decoding_transform.h --- draco-1.5.3+dfsg/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_decoding_transform.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_decoding_transform.h 2022-10-29 00:55:03.000000000 +0000 @@ -80,19 +80,31 @@ private: Point2 ComputeOriginalValue(Point2 pred, const Point2 &corr) const { const Point2 t(this->center_value(), this->center_value()); - pred = pred - t; + typedef typename std::make_unsigned::type UnsignedDataTypeT; + typedef VectorD Point2u; + + // Perform the addition in unsigned type to avoid signed integer overflow. + // Note that the result will be the same (for non-overflowing values). + pred = Point2(Point2u(pred) - Point2u(t)); const bool pred_is_in_diamond = this->IsInDiamond(pred[0], pred[1]); if (!pred_is_in_diamond) { this->InvertDiamond(&pred[0], &pred[1]); } - Point2 orig = pred + corr; + + // Perform the addition in unsigned type to avoid signed integer overflow. + // Note that the result will be the same (for non-overflowing values). + Point2 orig(Point2u(pred) + Point2u(corr)); + orig[0] = this->ModMax(orig[0]); orig[1] = this->ModMax(orig[1]); if (!pred_is_in_diamond) { this->InvertDiamond(&orig[0], &orig[1]); } - orig = orig + t; + + // Perform the addition in unsigned type to avoid signed integer overflow. + // Note that the result will be the same (for non-overflowing values). + orig = Point2(Point2u(orig) + Point2u(t)); return orig; } }; diff -Nru draco-1.5.3+dfsg/src/draco/compression/bit_coders/direct_bit_decoder.h draco-1.5.5+dfsg/src/draco/compression/bit_coders/direct_bit_decoder.h --- draco-1.5.3+dfsg/src/draco/compression/bit_coders/direct_bit_decoder.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/bit_coders/direct_bit_decoder.h 2022-10-29 00:55:03.000000000 +0000 @@ -47,14 +47,13 @@ // Decode the next |nbits| and return the sequence in |value|. |nbits| must be // > 0 and <= 32. - void DecodeLeastSignificantBits32(int nbits, uint32_t *value) { + bool DecodeLeastSignificantBits32(int nbits, uint32_t *value) { DRACO_DCHECK_EQ(true, nbits <= 32); DRACO_DCHECK_EQ(true, nbits > 0); const int remaining = 32 - num_used_bits_; if (nbits <= remaining) { if (pos_ == bits_.end()) { - *value = 0; - return; + return false; } *value = (*pos_ << num_used_bits_) >> (32 - nbits); num_used_bits_ += nbits; @@ -64,8 +63,7 @@ } } else { if (pos_ + 1 == bits_.end()) { - *value = 0; - return; + return false; } const uint32_t value_l = ((*pos_) << num_used_bits_); num_used_bits_ = nbits - remaining; @@ -73,6 +71,7 @@ const uint32_t value_r = (*pos_) >> (32 - num_used_bits_); *value = (value_l >> (32 - num_used_bits_ - remaining)) | value_r; } + return true; } void EndDecoding() {} diff -Nru draco-1.5.3+dfsg/src/draco/compression/config/encoder_options.h draco-1.5.5+dfsg/src/draco/compression/config/encoder_options.h --- draco-1.5.3+dfsg/src/draco/compression/config/encoder_options.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/config/encoder_options.h 2022-10-29 00:55:03.000000000 +0000 @@ -65,6 +65,10 @@ this->SetGlobalInt("encoding_speed", encoding_speed); this->SetGlobalInt("decoding_speed", decoding_speed); } + bool IsSpeedSet() const { + return this->IsGlobalOptionSet("encoding_speed") || + this->IsGlobalOptionSet("decoding_speed"); + } // Sets a given feature as supported or unsupported by the target decoder. // Encoder will always use only supported features when encoding the input diff -Nru draco-1.5.3+dfsg/src/draco/compression/draco_compression_options.cc draco-1.5.5+dfsg/src/draco/compression/draco_compression_options.cc --- draco-1.5.3+dfsg/src/draco/compression/draco_compression_options.cc 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/draco_compression_options.cc 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,59 @@ +// Copyright 2022 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "draco/compression/draco_compression_options.h" + +#ifdef DRACO_TRANSCODER_SUPPORTED + +namespace draco { + +SpatialQuantizationOptions::SpatialQuantizationOptions(int quantization_bits) { + SetQuantizationBits(quantization_bits); +} + +void SpatialQuantizationOptions::SetQuantizationBits(int quantization_bits) { + mode_ = LOCAL_QUANTIZATION_BITS; + quantization_bits_ = quantization_bits; +} + +bool SpatialQuantizationOptions::AreQuantizationBitsDefined() const { + return mode_ == LOCAL_QUANTIZATION_BITS; +} + +SpatialQuantizationOptions &SpatialQuantizationOptions::SetGrid(float spacing) { + mode_ = GLOBAL_GRID; + spacing_ = spacing; + return *this; +} + +bool SpatialQuantizationOptions::operator==( + const SpatialQuantizationOptions &other) const { + if (mode_ != other.mode_) { + return false; + } + if (mode_ == LOCAL_QUANTIZATION_BITS) { + if (quantization_bits_ != other.quantization_bits_) { + return false; + } + } else if (mode_ == GLOBAL_GRID) { + if (spacing_ != other.spacing_) { + return false; + } + } + return true; +} + +} // namespace draco + +#endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/compression/draco_compression_options.h draco-1.5.5+dfsg/src/draco/compression/draco_compression_options.h --- draco-1.5.3+dfsg/src/draco/compression/draco_compression_options.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/draco_compression_options.h 2022-10-29 00:55:03.000000000 +0000 @@ -21,11 +21,58 @@ #include "draco/core/status.h" namespace draco { + +// Quantization options for positions. Currently there are two modes for +// quantizing positions: +// +// 1. Quantization bits: +// - User defined number of quantization bits that is evenly distributed +// to cover the compressed geometry. +// 2. Grid: +// - Positions are snapped to a global grid defined by grid spacing. +// - This method is primarily intended to be used when the location of +// quantized vertices needs to be consistent between multiple +// geometries. +class SpatialQuantizationOptions { + public: + explicit SpatialQuantizationOptions(int quantization_bits); + + // Sets quantization bits that are going to be used for the compressed + // geometry. If the geometry is a scene, the same number of quantization bits + // is going to be applied to each mesh of the scene. Quantized values are + // going to be distributed within the bounds of individual meshes. + void SetQuantizationBits(int quantization_bits); + + // If this returns true, quantization_bits() should be used to get the + // desired number of quantization bits for compression. Otherwise the grid + // mode is selected and spacing() should be used to get the desired grid + // spacing. + bool AreQuantizationBitsDefined() const; + const int quantization_bits() const { return quantization_bits_; } + + // Defines quantization grid used for the compressed geometry. All vertices + // are going to be snapped to the nearest grid vertex that corresponds to an + // integer quantized position. |spacing| defines the distance between two grid + // vertices. E.g. a grid with |spacing| = 10 would have grid vertices at + // locations {10 * i, 10 * j, 10 * k} where i, j, k are integer numbers. + SpatialQuantizationOptions &SetGrid(float spacing); + + const float spacing() const { return spacing_; } + + bool operator==(const SpatialQuantizationOptions &other) const; + + private: + enum Mode { LOCAL_QUANTIZATION_BITS, GLOBAL_GRID }; + Mode mode_ = LOCAL_QUANTIZATION_BITS; + int quantization_bits_; // Default quantization bits for positions. + float spacing_ = 0.f; +}; + // TODO(fgalligan): Add support for unified_position_quantization. // Struct to hold Draco compression options. struct DracoCompressionOptions { int compression_level = 7; // compression level [0-10], most=10, least=0. - int quantization_bits_position = 11; + SpatialQuantizationOptions quantization_position{11}; int quantization_bits_normal = 8; int quantization_bits_tex_coord = 10; int quantization_bits_color = 8; @@ -36,7 +83,7 @@ bool operator==(const DracoCompressionOptions &other) const { return compression_level == other.compression_level && - quantization_bits_position == other.quantization_bits_position && + quantization_position == other.quantization_position && quantization_bits_normal == other.quantization_bits_normal && quantization_bits_tex_coord == other.quantization_bits_tex_coord && quantization_bits_color == other.quantization_bits_color && @@ -54,8 +101,15 @@ Status Check() const { DRACO_RETURN_IF_ERROR( Validate("Compression level", compression_level, 0, 10)); - DRACO_RETURN_IF_ERROR( - Validate("Position quantization", quantization_bits_position, 0, 30)); + if (quantization_position.AreQuantizationBitsDefined()) { + DRACO_RETURN_IF_ERROR(Validate("Position quantization", + quantization_position.quantization_bits(), + 0, 30)); + } else { + if (quantization_position.spacing() <= 0.f) { + return ErrorStatus("Position quantization spacing is invalid."); + } + } DRACO_RETURN_IF_ERROR( Validate("Normals quantization", quantization_bits_normal, 0, 30)); DRACO_RETURN_IF_ERROR( diff -Nru draco-1.5.3+dfsg/src/draco/compression/draco_compression_options_test.cc draco-1.5.5+dfsg/src/draco/compression/draco_compression_options_test.cc --- draco-1.5.3+dfsg/src/draco/compression/draco_compression_options_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/draco_compression_options_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,45 @@ +#include "draco/compression/draco_compression_options.h" + +#include "draco/core/draco_test_utils.h" + +#ifdef DRACO_TRANSCODER_SUPPORTED + +namespace { + +TEST(DracoCompressionOptionsTest, TestPositionQuantizationBits) { + // Test verifies that we can define draco compression options using + // quantization bits. + draco::SpatialQuantizationOptions options(10); + + // Quantization bits should be used by default. + ASSERT_TRUE(options.AreQuantizationBitsDefined()); + ASSERT_EQ(options.quantization_bits(), 10); + + // Change the quantization bits. + options.SetQuantizationBits(9); + ASSERT_TRUE(options.AreQuantizationBitsDefined()); + ASSERT_EQ(options.quantization_bits(), 9); + + // If we select the grid, quantization bits should not be used. + options.SetGrid(0.5f); + ASSERT_FALSE(options.AreQuantizationBitsDefined()); +} + +TEST(DracoCompressionOptionsTest, TestPositionQuantizationGrid) { + // Test verifies that we can define draco compression options using + // quantization grid. + draco::SpatialQuantizationOptions options(10); + + // Quantization bits should be used by default. + ASSERT_TRUE(options.AreQuantizationBitsDefined()); + + // Set the grid parameters. + options.SetGrid(0.25f); + ASSERT_FALSE(options.AreQuantizationBitsDefined()); + + ASSERT_EQ(options.spacing(), 0.25f); +} + +} // namespace + +#endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/compression/encode_test.cc draco-1.5.5+dfsg/src/draco/compression/encode_test.cc --- draco-1.5.3+dfsg/src/draco/compression/encode_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/encode_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -402,4 +402,170 @@ ASSERT_NE(decoded_mesh, nullptr); } +#ifdef DRACO_TRANSCODER_SUPPORTED +TEST_F(EncodeTest, TestDracoCompressionOptions) { + // This test verifies that we can set the encoder's compression options via + // draco::Mesh's compression options. + const auto mesh = draco::ReadMeshFromTestFile("test_nm.obj"); + ASSERT_NE(mesh, nullptr); + + // First set compression level and quantization manually. + draco::Encoder encoder_manual; + draco::EncoderBuffer buffer_manual; + encoder_manual.SetAttributeQuantization(draco::GeometryAttribute::POSITION, + 8); + encoder_manual.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, 7); + encoder_manual.SetSpeedOptions(4, 4); + + DRACO_ASSERT_OK(encoder_manual.EncodeMeshToBuffer(*mesh, &buffer_manual)); + + // Now do the same with options provided via DracoCompressionOptions. + draco::DracoCompressionOptions compression_options; + compression_options.compression_level = 6; + compression_options.quantization_position.SetQuantizationBits(8); + compression_options.quantization_bits_normal = 7; + mesh->SetCompressionOptions(compression_options); + mesh->SetCompressionEnabled(true); + + draco::Encoder encoder_auto; + draco::EncoderBuffer buffer_auto; + DRACO_ASSERT_OK(encoder_auto.EncodeMeshToBuffer(*mesh, &buffer_auto)); + + // Ensure that both encoders produce the same result. + ASSERT_EQ(buffer_manual.size(), buffer_auto.size()); + + // Now change some of the mesh's compression settings and ensure the + // compression changes as well. + compression_options.compression_level = 7; + mesh->SetCompressionOptions(compression_options); + buffer_auto.Clear(); + DRACO_ASSERT_OK(encoder_auto.EncodeMeshToBuffer(*mesh, &buffer_auto)); + ASSERT_NE(buffer_manual.size(), buffer_auto.size()); + + // Check that |mesh| compression options do not override the encoder options. + mesh->GetCompressionOptions().compression_level = 10; + mesh->GetCompressionOptions().quantization_position.SetQuantizationBits(10); + mesh->GetCompressionOptions().quantization_bits_normal = 10; + draco::EncoderBuffer buffer; + DRACO_ASSERT_OK(encoder_manual.EncodeMeshToBuffer(*mesh, &buffer)); + ASSERT_EQ(buffer.size(), buffer_manual.size()); +} + +TEST_F(EncodeTest, TestDracoCompressionOptionsManualOverride) { + // This test verifies that we can use encoder's option to override compression + // options provided in draco::Mesh's compression options. + const auto mesh = draco::ReadMeshFromTestFile("test_nm.obj"); + ASSERT_NE(mesh, nullptr); + + // Set some compression options. + draco::DracoCompressionOptions compression_options; + compression_options.compression_level = 6; + compression_options.quantization_position.SetQuantizationBits(8); + compression_options.quantization_bits_normal = 7; + mesh->SetCompressionOptions(compression_options); + mesh->SetCompressionEnabled(true); + + draco::Encoder encoder; + draco::EncoderBuffer buffer_no_override; + DRACO_ASSERT_OK(encoder.EncodeMeshToBuffer(*mesh, &buffer_no_override)); + + // Now override some options and ensure the compression is different. + encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 5); + draco::EncoderBuffer buffer_with_override; + DRACO_ASSERT_OK(encoder.EncodeMeshToBuffer(*mesh, &buffer_with_override)); + ASSERT_LT(buffer_with_override.size(), buffer_no_override.size()); +} + +TEST_F(EncodeTest, TestDracoCompressionOptionsGridQuantization) { + // Test verifies that we can set position quantization via grid spacing. + + // 1x1x1 cube. + const auto mesh = draco::ReadMeshFromTestFile("cube_att.obj"); + ASSERT_NE(mesh, nullptr); + mesh->SetCompressionEnabled(true); + + // Set grid quantization for positions. + draco::DracoCompressionOptions compression_options; + // This should result in 10x10x10 quantization. + compression_options.quantization_position.SetGrid(0.1); + mesh->SetCompressionOptions(compression_options); + + draco::ExpertEncoder encoder(*mesh); + draco::EncoderBuffer buffer; + DRACO_ASSERT_OK(encoder.EncodeToBuffer(&buffer)); + + // The grid options should be reflected in the |encoder|. Check that the + // computed values are correct. + const int pos_att_id = + mesh->GetNamedAttributeId(draco::GeometryAttribute::POSITION); + draco::Vector3f origin; + encoder.options().GetAttributeVector(pos_att_id, "quantization_origin", 3, + &origin[0]); + ASSERT_EQ(origin, draco::Vector3f(0.f, 0.f, 0.f)); + + // We need 4 quantization bits (for 10 values). + ASSERT_EQ( + encoder.options().GetAttributeInt(pos_att_id, "quantization_bits", -1), + 4); + + // The quantization range should be (1 << quantization_bits) * spacing. + ASSERT_NEAR(encoder.options().GetAttributeFloat(pos_att_id, + "quantization_range", 0.f), + 16.f * 0.1f, 1e-6f); +} + +TEST_F(EncodeTest, TestDracoCompressionOptionsGridQuantizationWithOffset) { + // Test verifies that we can set position quantization via grid spacing when + // the geometry is not perfectly aligned with the quantization grid. + + // 1x1x1 cube. + const auto mesh = draco::ReadMeshFromTestFile("cube_att.obj"); + ASSERT_NE(mesh, nullptr); + + // Move all positions a bit. + auto *pos_att = mesh->attribute( + mesh->GetNamedAttributeId(draco::GeometryAttribute::POSITION)); + for (draco::AttributeValueIndex avi(0); avi < pos_att->size(); ++avi) { + draco::Vector3f pos; + pos_att->GetValue(avi, &pos[0]); + pos = pos + draco::Vector3f(-0.55f, 0.65f, 10.75f); + pos_att->SetAttributeValue(avi, &pos[0]); + } + + mesh->SetCompressionEnabled(true); + + // Set grid quantization for positions. + draco::DracoCompressionOptions compression_options; + // This should result in 16x16x16 quantization if the grid was perfectly + // aligned but since it is not we should expect 17 or 18 values per component. + compression_options.quantization_position.SetGrid(0.0625f); + mesh->SetCompressionOptions(compression_options); + + draco::ExpertEncoder encoder(*mesh); + draco::EncoderBuffer buffer; + DRACO_ASSERT_OK(encoder.EncodeToBuffer(&buffer)); + + // The grid options should be reflected in the |encoder|. Check that the + // computed values are correct. + const int pos_att_id = + mesh->GetNamedAttributeId(draco::GeometryAttribute::POSITION); + draco::Vector3f origin; + encoder.options().GetAttributeVector(pos_att_id, "quantization_origin", 3, + &origin[0]); + // The origin is the first lower value on the quantization grid for each + // component of the mesh. + ASSERT_EQ(origin, draco::Vector3f(-0.5625f, 0.625f, 10.75f)); + + // We need 5 quantization bits (for 17-18 values). + ASSERT_EQ( + encoder.options().GetAttributeInt(pos_att_id, "quantization_bits", -1), + 5); + + // The quantization range should be (1 << quantization_bits) * spacing. + ASSERT_NEAR(encoder.options().GetAttributeFloat(pos_att_id, + "quantization_range", 0.f), + 32.f * 0.0625f, 1e-6f); +} +#endif // DRACO_TRANSCODER_SUPPORTED + } // namespace diff -Nru draco-1.5.3+dfsg/src/draco/compression/expert_encode.cc draco-1.5.5+dfsg/src/draco/compression/expert_encode.cc --- draco-1.5.3+dfsg/src/draco/compression/expert_encode.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/expert_encode.cc 2022-10-29 00:55:03.000000000 +0000 @@ -20,6 +20,9 @@ #include "draco/compression/point_cloud/point_cloud_kd_tree_encoder.h" #include "draco/compression/point_cloud/point_cloud_sequential_encoder.h" #endif +#ifdef DRACO_TRANSCODER_SUPPORTED +#include "draco/core/bit_utils.h" +#endif namespace draco { @@ -101,6 +104,11 @@ Status ExpertEncoder::EncodeMeshToBuffer(const Mesh &m, EncoderBuffer *out_buffer) { +#ifdef DRACO_TRANSCODER_SUPPORTED + // Apply DracoCompressionOptions associated with the mesh. + DRACO_RETURN_IF_ERROR(ApplyCompressionOptions(m)); +#endif + std::unique_ptr encoder; // Select the encoding method only based on the provided options. int encoding_method = options().GetGlobalInt("encoding_method", -1); @@ -179,4 +187,106 @@ return status; } +#ifdef DRACO_TRANSCODER_SUPPORTED +Status ExpertEncoder::ApplyCompressionOptions(const Mesh &mesh) { + if (!mesh.IsCompressionEnabled()) { + return OkStatus(); + } + const auto &compression_options = mesh.GetCompressionOptions(); + + // Set any encoder options that haven't been explicitly set by users (don't + // override existing options). + if (!options().IsSpeedSet()) { + options().SetSpeed(10 - compression_options.compression_level, + 10 - compression_options.compression_level); + } + + for (int ai = 0; ai < mesh.num_attributes(); ++ai) { + if (options().IsAttributeOptionSet(ai, "quantization_bits")) { + continue; // Don't override options that have been set. + } + int quantization_bits = 0; + const auto type = mesh.attribute(ai)->attribute_type(); + switch (type) { + case GeometryAttribute::POSITION: + if (compression_options.quantization_position + .AreQuantizationBitsDefined()) { + quantization_bits = + compression_options.quantization_position.quantization_bits(); + } else { + DRACO_RETURN_IF_ERROR(ApplyGridQuantization(mesh, ai)); + } + break; + case GeometryAttribute::TEX_COORD: + quantization_bits = compression_options.quantization_bits_tex_coord; + break; + case GeometryAttribute::NORMAL: + quantization_bits = compression_options.quantization_bits_normal; + break; + case GeometryAttribute::COLOR: + quantization_bits = compression_options.quantization_bits_color; + break; + case GeometryAttribute::TANGENT: + quantization_bits = compression_options.quantization_bits_tangent; + break; + case GeometryAttribute::WEIGHTS: + quantization_bits = compression_options.quantization_bits_weight; + break; + case GeometryAttribute::GENERIC: + quantization_bits = compression_options.quantization_bits_generic; + break; + default: + break; + } + if (quantization_bits > 0) { + options().SetAttributeInt(ai, "quantization_bits", quantization_bits); + } + } + return OkStatus(); +} + +Status ExpertEncoder::ApplyGridQuantization(const Mesh &mesh, + int attribute_index) { + const auto compression_options = mesh.GetCompressionOptions(); + if (mesh.attribute(attribute_index)->num_components() != 3) { + return ErrorStatus( + "Invalid number of components: Grid quantization is currently " + "supported only for 3D positions."); + } + const float spacing = compression_options.quantization_position.spacing(); + // Compute quantization properties based on the grid spacing. + const auto &bbox = mesh.ComputeBoundingBox(); + // Snap min and max points of the |bbox| to the quantization grid vertices. + Vector3f min_pos; + int num_values = 0; // Number of values that we need to encode. + for (int c = 0; c < 3; ++c) { + // Min / max position on grid vertices in grid coordinates. + const float min_grid_pos = floor(bbox.GetMinPoint()[c] / spacing); + const float max_grid_pos = ceil(bbox.GetMaxPoint()[c] / spacing); + + // Min pos on grid vertex in mesh coordinates. + min_pos[c] = min_grid_pos * spacing; + + const float component_num_values = + static_cast(max_grid_pos) - static_cast(min_grid_pos) + 1; + if (component_num_values > num_values) { + num_values = component_num_values; + } + } + // Now compute the number of bits needed to encode |num_values|. + int bits = MostSignificantBit(num_values); + if ((1 << bits) < num_values) { + // If the |num_values| is larger than number of values representable by + // |bits|, we need to use one more bit. This will be almost always true + // unless |num_values| was equal to 1 << |bits|. + bits++; + } + // Compute the range in mesh coordinates that matches the quantization bits. + const float range = (1 << bits) * spacing; + SetAttributeExplicitQuantization(attribute_index, bits, 3, min_pos.data(), + range); + return OkStatus(); +} +#endif // DRACO_TRANSCODER_SUPPORTED + } // namespace draco diff -Nru draco-1.5.3+dfsg/src/draco/compression/expert_encode.h draco-1.5.5+dfsg/src/draco/compression/expert_encode.h --- draco-1.5.3+dfsg/src/draco/compression/expert_encode.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/expert_encode.h 2022-10-29 00:55:03.000000000 +0000 @@ -138,6 +138,12 @@ Status EncodeMeshToBuffer(const Mesh &m, EncoderBuffer *out_buffer); +#ifdef DRACO_TRANSCODER_SUPPORTED + // Applies compression options stored in |mesh|. + Status ApplyCompressionOptions(const Mesh &mesh); + Status ApplyGridQuantization(const Mesh &mesh, int attribute_index); +#endif // DRACO_TRANSCODER_SUPPORTED + const PointCloud *point_cloud_; const Mesh *mesh_; }; diff -Nru draco-1.5.3+dfsg/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl.cc draco-1.5.5+dfsg/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl.cc --- draco-1.5.3+dfsg/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl.cc 2022-10-29 00:55:03.000000000 +0000 @@ -484,7 +484,10 @@ attribute_data_[i].connectivity_data.AddSeamEdge(CornerIndex(c)); } // Recompute vertices from the newly added seam edges. - attribute_data_[i].connectivity_data.RecomputeVertices(nullptr, nullptr); + if (!attribute_data_[i].connectivity_data.RecomputeVertices(nullptr, + nullptr)) { + return false; + } } pos_encoding_data_.Init(corner_table_->num_vertices()); @@ -574,6 +577,17 @@ const CornerIndex corner_b = corner_table_->Next(corner_table_->LeftMostCorner(vertex_x)); + if (corner_a == corner_b) { + // All matched corners must be different. + return -1; + } + if (corner_table_->Opposite(corner_a) != kInvalidCornerIndex || + corner_table_->Opposite(corner_b) != kInvalidCornerIndex) { + // One of the corners is already opposite to an existing face, which + // should not happen unless the input was tampered with. + return -1; + } + // New tip corner. const CornerIndex corner(3 * face.value()); // Update opposite corner mappings. @@ -616,6 +630,11 @@ return -1; } const CornerIndex corner_a = active_corner_stack.back(); + if (corner_table_->Opposite(corner_a) != kInvalidCornerIndex) { + // Active corner is already opposite to an existing face, which should + // not happen unless the input was tampered with. + return -1; + } // First corner on the new face is either corner "l" or "r". const CornerIndex corner(3 * face.value()); @@ -681,10 +700,14 @@ } const CornerIndex corner_a = active_corner_stack.back(); + if (corner_a == corner_b) { + // All matched corners must be different. + return -1; + } if (corner_table_->Opposite(corner_a) != kInvalidCornerIndex || corner_table_->Opposite(corner_b) != kInvalidCornerIndex) { // One of the corners is already opposite to an existing face, which - // should not happen unless the input was tempered with. + // should not happen unless the input was tampered with. return -1; } @@ -713,9 +736,15 @@ // Also update the vertex id at corner "n" and all corners that are // connected to it in the CCW direction. + const CornerIndex first_corner = corner_n; while (corner_n != kInvalidCornerIndex) { corner_table_->MapCornerToVertex(corner_n, vertex_p); corner_n = corner_table_->SwingLeft(corner_n); + if (corner_n == first_corner) { + // We reached the start again which should not happen for split + // symbols. + return -1; + } } // Make sure the old vertex n is now mapped to an invalid corner (make it // isolated). @@ -842,6 +871,18 @@ const CornerIndex corner_c = corner_table_->Next(corner_table_->LeftMostCorner(vert_x)); + if (corner == corner_b || corner == corner_c || corner_b == corner_c) { + // All matched corners must be different. + return -1; + } + if (corner_table_->Opposite(corner) != kInvalidCornerIndex || + corner_table_->Opposite(corner_b) != kInvalidCornerIndex || + corner_table_->Opposite(corner_c) != kInvalidCornerIndex) { + // One of the corners is already opposite to an existing face, which + // should not happen unless the input was tampered with. + return -1; + } + const VertexIndex vert_p = corner_table_->Vertex(corner_table_->Next(corner_c)); diff -Nru draco-1.5.3+dfsg/src/draco/compression/mesh/mesh_sequential_decoder.cc draco-1.5.5+dfsg/src/draco/compression/mesh/mesh_sequential_decoder.cc --- draco-1.5.3+dfsg/src/draco/compression/mesh/mesh_sequential_decoder.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/mesh/mesh_sequential_decoder.cc 2022-10-29 00:55:03.000000000 +0000 @@ -158,6 +158,10 @@ index_diff = -index_diff; } const int32_t index_value = index_diff + last_index_value; + if (index_value < 0) { + // Negative indices are not allowed. + return false; + } face[j] = index_value; last_index_value = index_value; } diff -Nru draco-1.5.3+dfsg/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.h draco-1.5.5+dfsg/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.h --- draco-1.5.3+dfsg/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.h 2022-10-29 00:55:03.000000000 +0000 @@ -20,6 +20,7 @@ #include #include #include +#include #include "draco/compression/bit_coders/adaptive_rans_bit_decoder.h" #include "draco/compression/bit_coders/direct_bit_decoder.h" @@ -103,6 +104,9 @@ const uint32_t dimension() const { return dimension_; } + // Returns the number of decoded points. Must be called after DecodePoints(). + uint32_t num_decoded_points() const { return num_decoded_points_; } + private: uint32_t GetAxis(uint32_t num_remaining_points, const VectorUint32 &levels, uint32_t last_axis); @@ -274,8 +278,10 @@ p_[axes_[j]] = 0; const uint32_t num_remaining_bits = bit_length_ - levels[axes_[j]]; if (num_remaining_bits) { - remaining_bits_decoder_.DecodeLeastSignificantBits32( - num_remaining_bits, &p_[axes_[j]]); + if (!remaining_bits_decoder_.DecodeLeastSignificantBits32( + num_remaining_bits, &p_[axes_[j]])) { + return false; + } } p_[axes_[j]] = old_base[axes_[j]] | p_[axes_[j]]; } diff -Nru draco-1.5.3+dfsg/src/draco/core/bounding_box.cc draco-1.5.5+dfsg/src/draco/core/bounding_box.cc --- draco-1.5.3+dfsg/src/draco/core/bounding_box.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/core/bounding_box.cc 2022-10-29 00:55:03.000000000 +0000 @@ -27,4 +27,13 @@ BoundingBox::BoundingBox(const Vector3f &min_point, const Vector3f &max_point) : min_point_(min_point), max_point_(max_point) {} +const bool BoundingBox::IsValid() const { + return GetMinPoint()[0] != std::numeric_limits::max() && + GetMinPoint()[1] != std::numeric_limits::max() && + GetMinPoint()[2] != std::numeric_limits::max() && + GetMaxPoint()[0] != std::numeric_limits::lowest() && + GetMaxPoint()[1] != std::numeric_limits::lowest() && + GetMaxPoint()[2] != std::numeric_limits::lowest(); +} + } // namespace draco diff -Nru draco-1.5.3+dfsg/src/draco/core/bounding_box.h draco-1.5.5+dfsg/src/draco/core/bounding_box.h --- draco-1.5.3+dfsg/src/draco/core/bounding_box.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/core/bounding_box.h 2022-10-29 00:55:03.000000000 +0000 @@ -38,6 +38,11 @@ // Returns the maximum point of the bounding box. inline const Vector3f &GetMaxPoint() const { return max_point_; } + // Checks if the bounding box object was created with the default constructor + // then never updated. Internally, checks if the bounding box minimum and + // maximum points hold the largest positive and smallest negative values. + const bool IsValid() const; + // Conditionally updates the bounding box with a given |new_point|. void Update(const Vector3f &new_point) { for (int i = 0; i < 3; i++) { diff -Nru draco-1.5.3+dfsg/src/draco/core/draco_index_type_vector.h draco-1.5.5+dfsg/src/draco/core/draco_index_type_vector.h --- draco-1.5.3+dfsg/src/draco/core/draco_index_type_vector.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/core/draco_index_type_vector.h 2022-10-29 00:55:03.000000000 +0000 @@ -25,24 +25,32 @@ // A wrapper around the standard std::vector that supports indexing of the // vector entries using the strongly typed indices as defined in -// draco_index_type.h . -// TODO(ostava): Make the interface more complete. It's currently missing -// features such as iterators. +// draco_index_type.h. +// TODO(ostava): Make the interface more complete. It's currently missing some +// features. template class IndexTypeVector { public: typedef typename std::vector::const_reference const_reference; typedef typename std::vector::reference reference; + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; IndexTypeVector() {} explicit IndexTypeVector(size_t size) : vector_(size) {} IndexTypeVector(size_t size, const ValueTypeT &val) : vector_(size, val) {} + iterator begin() { return vector_.begin(); } + const_iterator begin() const { return vector_.begin(); } + iterator end() { return vector_.end(); } + const_iterator end() const { return vector_.end(); } + void clear() { vector_.clear(); } void reserve(size_t size) { vector_.reserve(size); } void resize(size_t size) { vector_.resize(size); } void resize(size_t size, const ValueTypeT &val) { vector_.resize(size, val); } void assign(size_t size, const ValueTypeT &val) { vector_.assign(size, val); } + iterator erase(iterator position) { return vector_.erase(position); } void swap(IndexTypeVector &arg) { vector_.swap(arg.vector_); diff -Nru draco-1.5.3+dfsg/src/draco/core/draco_version.h draco-1.5.5+dfsg/src/draco/core/draco_version.h --- draco-1.5.3+dfsg/src/draco/core/draco_version.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/core/draco_version.h 2022-10-29 00:55:03.000000000 +0000 @@ -18,9 +18,7 @@ namespace draco { // Draco version is comprised of ... -static const char kDracoVersion[] = "1.5.3"; - -const char *Version() { return kDracoVersion; } +static const char kDracoVersion[] = "1.5.5"; } // namespace draco diff -Nru draco-1.5.3+dfsg/src/draco/core/math_utils.h draco-1.5.5+dfsg/src/draco/core/math_utils.h --- draco-1.5.3+dfsg/src/draco/core/math_utils.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/core/math_utils.h 2022-10-29 00:55:03.000000000 +0000 @@ -19,6 +19,8 @@ #include "draco/core/vector_d.h" +namespace draco { + #define DRACO_INCREMENT_MOD(I, M) (((I) == ((M)-1)) ? 0 : ((I) + 1)) // Returns floor(sqrt(x)) where x is an integer number. The main intend of this @@ -52,4 +54,26 @@ return square_root; } +// Performs the addition in unsigned type to avoid signed integer overflow. Note +// that the result will be the same (for non-overflowing values). +template < + typename DataTypeT, + typename std::enable_if::value && + std::is_signed::value>::type* = nullptr> +inline DataTypeT AddAsUnsigned(DataTypeT a, DataTypeT b) { + typedef typename std::make_unsigned::type DataTypeUT; + return static_cast(static_cast(a) + + static_cast(b)); +} + +template < + typename DataTypeT, + typename std::enable_if::value || + !std::is_signed::value>::type* = nullptr> +inline DataTypeT AddAsUnsigned(DataTypeT a, DataTypeT b) { + return a + b; +} + +} // namespace draco + #endif // DRACO_CORE_MATH_UTILS_H_ diff -Nru draco-1.5.3+dfsg/src/draco/core/math_utils_test.cc draco-1.5.5+dfsg/src/draco/core/math_utils_test.cc --- draco-1.5.3+dfsg/src/draco/core/math_utils_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/core/math_utils_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -5,7 +5,7 @@ #include "draco/core/draco_test_base.h" -using draco::Vector3f; +namespace draco { TEST(MathUtils, Mod) { EXPECT_EQ(DRACO_INCREMENT_MOD(1, 1 << 1), 0); } @@ -20,3 +20,5 @@ ASSERT_EQ(IntSqrt(number), static_cast(floor(std::sqrt(number)))); } } + +} // namespace draco diff -Nru draco-1.5.3+dfsg/src/draco/core/options.cc draco-1.5.5+dfsg/src/draco/core/options.cc --- draco-1.5.3+dfsg/src/draco/core/options.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/core/options.cc 2022-10-29 00:55:03.000000000 +0000 @@ -20,8 +20,6 @@ namespace draco { -Options::Options() {} - void Options::MergeAndReplace(const Options &other_options) { for (const auto &item : other_options.options_) { options_[item.first] = item.second; diff -Nru draco-1.5.3+dfsg/src/draco/core/options.h draco-1.5.5+dfsg/src/draco/core/options.h --- draco-1.5.3+dfsg/src/draco/core/options.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/core/options.h 2022-10-29 00:55:03.000000000 +0000 @@ -27,7 +27,8 @@ // data type. class Options { public: - Options(); + Options() = default; + ~Options() = default; // Merges |other_options| on top of the existing options of this instance // replacing all entries that are present in both options instances. diff -Nru draco-1.5.3+dfsg/src/draco/core/vector_d_test.cc draco-1.5.5+dfsg/src/draco/core/vector_d_test.cc --- draco-1.5.3+dfsg/src/draco/core/vector_d_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/core/vector_d_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -32,16 +32,6 @@ typedef draco::VectorD Vector3i; typedef draco::VectorD Vector4i; -template -void TestSquaredDistance(const draco::VectorD v1, - const draco::VectorD v2, - const CoeffT result) { - CoeffT squared_distance = SquaredDistance(v1, v2); - ASSERT_EQ(squared_distance, result); - squared_distance = SquaredDistance(v2, v1); - ASSERT_EQ(squared_distance, result); -} - TEST(VectorDTest, TestOperators) { { const Vector3f v; @@ -170,56 +160,6 @@ ASSERT_EQ(normalized[2], 0); } -TEST(VectorDTest, TestSquaredDistance) { - // Test Vector2f: float, 2D. - Vector2f v1_2f(5.5, 10.5); - Vector2f v2_2f(3.5, 15.5); - float result_f = 29; - TestSquaredDistance(v1_2f, v2_2f, result_f); - - // Test Vector3f: float, 3D. - Vector3f v1_3f(5.5, 10.5, 2.3); - Vector3f v2_3f(3.5, 15.5, 0); - result_f = 34.29; - TestSquaredDistance(v1_3f, v2_3f, result_f); - - // Test Vector4f: float, 4D. - Vector4f v1_4f(5.5, 10.5, 2.3, 7.2); - Vector4f v2_4f(3.5, 15.5, 0, 9.9); - result_f = 41.58; - TestSquaredDistance(v1_4f, v2_4f, result_f); - - // Test Vector5f: float, 5D. - Vector5f v1_5f(5.5, 10.5, 2.3, 7.2, 1.0); - Vector5f v2_5f(3.5, 15.5, 0, 9.9, 0.2); - result_f = 42.22; - TestSquaredDistance(v1_5f, v2_5f, result_f); - - // Test Vector 2ui: uint32_t, 2D. - Vector2ui v1_2ui(5, 10); - Vector2ui v2_2ui(3, 15); - uint32_t result_ui = 29; - TestSquaredDistance(v1_2ui, v2_2ui, result_ui); - - // Test Vector 3ui: uint32_t, 3D. - Vector3ui v1_3ui(5, 10, 2); - Vector3ui v2_3ui(3, 15, 0); - result_ui = 33; - TestSquaredDistance(v1_3ui, v2_3ui, result_ui); - - // Test Vector 4ui: uint32_t, 4D. - Vector4ui v1_4ui(5, 10, 2, 7); - Vector4ui v2_4ui(3, 15, 0, 9); - result_ui = 37; - TestSquaredDistance(v1_4ui, v2_4ui, result_ui); - - // Test Vector 5ui: uint32_t, 5D. - Vector5ui v1_5ui(5, 10, 2, 7, 1); - Vector5ui v2_5ui(3, 15, 0, 9, 12); - result_ui = 158; - TestSquaredDistance(v1_5ui, v2_5ui, result_ui); -} - TEST(VectorDTest, TestCrossProduct3D) { const Vector3i e1(1, 0, 0); const Vector3i e2(0, 1, 0); diff -Nru draco-1.5.3+dfsg/src/draco/io/gltf_decoder.cc draco-1.5.5+dfsg/src/draco/io/gltf_decoder.cc --- draco-1.5.3+dfsg/src/draco/io/gltf_decoder.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/gltf_decoder.cc 2022-10-29 00:55:03.000000000 +0000 @@ -17,17 +17,24 @@ #ifdef DRACO_TRANSCODER_SUPPORTED #include +#include #include +#include #include #include #include +#include "draco/core/draco_types.h" #include "draco/core/hash_utils.h" #include "draco/core/status.h" #include "draco/core/status_or.h" #include "draco/io/tiny_gltf_utils.h" +#include "draco/material/material_library.h" #include "draco/mesh/mesh.h" +#include "draco/mesh/mesh_features.h" #include "draco/mesh/triangle_soup_mesh_builder.h" +#include "draco/metadata/property_table.h" +#include "draco/point_cloud/point_cloud_builder.h" #include "draco/scene/scene_indices.h" #include "draco/texture/source_image.h" #include "draco/texture/texture_utils.h" @@ -72,6 +79,9 @@ return GeometryAttribute::JOINTS; } else if (attribute_name == "WEIGHTS_0") { return GeometryAttribute::WEIGHTS; + } else if (attribute_name.rfind("_FEATURE_ID_") == 0) { + // Feature ID attribute like _FEATURE_ID_5 from EXT_mesh_features extension. + return GeometryAttribute::GENERIC; } return GeometryAttribute::INVALID; } @@ -168,12 +178,77 @@ return output; } -template -StatusOr> CopyDataAs(const tinygltf::Model &model, - const tinygltf::Accessor &accessor) { +// Specialization for arithmetic types. +template < + typename TypeT, + typename std::enable_if::value>::type * = nullptr> +StatusOr> CopyDataAs(const tinygltf::Model &model, + const tinygltf::Accessor &accessor) { + if (std::is_same::value) { + if (TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE != accessor.componentType) { + return ErrorStatus("Accessor data cannot be converted to Uint8."); + } + } else if (std::is_same::value) { + if (TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE != accessor.componentType && + TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT != accessor.componentType) { + return ErrorStatus("Accessor data cannot be converted to Uint16."); + } + } else if (std::is_same::value) { + if (TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE != accessor.componentType && + TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT != accessor.componentType && + TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT != accessor.componentType) { + return ErrorStatus("Accessor data cannot be converted to Uint32."); + } + } else if (std::is_same::value) { + if (TINYGLTF_COMPONENT_TYPE_FLOAT != accessor.componentType) { + return ErrorStatus("Accessor data cannot be converted to Float."); + } + } + if (accessor.bufferView < 0) { + return Status(Status::DRACO_ERROR, "Error CopyDataAs() bufferView < 0."); + } + + const tinygltf::BufferView &buffer_view = + model.bufferViews[accessor.bufferView]; + if (buffer_view.buffer < 0) { + return Status(Status::DRACO_ERROR, "Error CopyDataAs() buffer < 0."); + } + + const tinygltf::Buffer &buffer = model.buffers[buffer_view.buffer]; + + const uint8_t *const data_start = + buffer.data.data() + buffer_view.byteOffset + accessor.byteOffset; + const int byte_stride = accessor.ByteStride(buffer_view); + const int component_size = + tinygltf::GetComponentSizeInBytes(accessor.componentType); + + std::vector output; + output.resize(accessor.count); + + const int num_components = + TinyGltfUtils::GetNumComponentsForType(accessor.type); + int out_index = 0; + const uint8_t *data = data_start; + for (int i = 0; i < accessor.count; ++i) { + for (int c = 0; c < num_components; ++c) { + TypeT value = 0; + memcpy(&value, data + (c * component_size), component_size); + output[out_index++] = value; + } + data += byte_stride; + } + return output; +} + +// Specialization for remaining types is used for draco::VectorD. +template ::value>::type * = + nullptr> +StatusOr> CopyDataAs(const tinygltf::Model &model, + const tinygltf::Accessor &accessor) { const int num_components = TinyGltfUtils::GetNumComponentsForType(accessor.type); - if (num_components != VectorT::dimension) { + if (num_components != TypeT::dimension) { return Status(Status::DRACO_ERROR, "Dimension does not equal num components."); } @@ -195,21 +270,18 @@ const int component_size = tinygltf::GetComponentSizeInBytes(accessor.componentType); - std::vector output; + std::vector output; output.resize(accessor.count); const uint8_t *data = data_start; for (int i = 0; i < accessor.count; ++i) { - VectorT values; - + TypeT values; for (int c = 0; c < num_components; ++c) { memcpy(&values[c], data + (c * component_size), component_size); } - output[i] = values; data += byte_stride; } - return output; } @@ -218,11 +290,11 @@ Status CopyDataFromBufferView(const tinygltf::Model &model, int buffer_view_id, std::vector *data) { if (buffer_view_id < 0) { - return Status(Status::DRACO_ERROR, "Error CopyDataAs() bufferView < 0."); + return ErrorStatus("Error CopyDataFromBufferView() bufferView < 0."); } const tinygltf::BufferView &buffer_view = model.bufferViews[buffer_view_id]; if (buffer_view.buffer < 0) { - return Status(Status::DRACO_ERROR, "Error CopyDataAs() buffer < 0."); + return ErrorStatus("Error CopyDataFromBufferView() buffer < 0."); } if (buffer_view.byteStride != 0) { return Status(Status::DRACO_ERROR, "Error buffer view byteStride != 0."); @@ -360,7 +432,11 @@ } // namespace GltfDecoder::GltfDecoder() - : next_face_id_(0), total_indices_count_(0), material_att_id_(-1) {} + : next_face_id_(0), + next_point_id_(0), + total_face_indices_count_(0), + total_point_indices_count_(0), + material_att_id_(-1) {} StatusOr> GltfDecoder::DecodeFromFile( const std::string &file_name) { @@ -452,8 +528,18 @@ StatusOr> GltfDecoder::BuildMesh() { DRACO_RETURN_IF_ERROR(GatherAttributeAndMaterialStats()); - mb_.Start(total_indices_count_ / 3); - DRACO_RETURN_IF_ERROR(AddAttributesToDracoMesh()); + if (total_face_indices_count_ > 0 && total_point_indices_count_ > 0) { + return ErrorStatus( + "Decoding to mesh can't handle triangle and point primitives at the " + "same time."); + } + if (total_face_indices_count_ > 0) { + mb_.Start(total_face_indices_count_ / 3); + DRACO_RETURN_IF_ERROR(AddAttributesToDracoMesh(&mb_)); + } else { + pb_.Start(total_point_indices_count_); + DRACO_RETURN_IF_ERROR(AddAttributesToDracoMesh(&pb_)); + } for (const tinygltf::Scene &scene : gltf_model_.scenes) { for (int i = 0; i < scene.nodes.size(); ++i) { @@ -461,13 +547,45 @@ DRACO_RETURN_IF_ERROR(DecodeNode(scene.nodes[i], parent_matrix)); } } - std::unique_ptr mesh = mb_.Finalize(); + DRACO_ASSIGN_OR_RETURN( + std::unique_ptr mesh, + BuildMeshFromBuilder(total_face_indices_count_ > 0, &mb_, &pb_)); DRACO_RETURN_IF_ERROR(CopyTextures(mesh.get())); + SetAttributePropertiesOnDracoMesh(mesh.get()); DRACO_RETURN_IF_ERROR(AddMaterialsToDracoMesh(mesh.get())); + DRACO_RETURN_IF_ERROR(AddMeshFeaturesToDracoMesh(mesh.get())); + DRACO_RETURN_IF_ERROR(AddStructuralMetadataToGeometry(mesh.get())); + MoveNonMaterialTextures(mesh.get()); return mesh; } +Status GltfDecoder::AddMeshFeaturesToDracoMesh(Mesh *mesh) { + for (const tinygltf::Scene &scene : gltf_model_.scenes) { + for (int i = 0; i < scene.nodes.size(); ++i) { + DRACO_RETURN_IF_ERROR(AddMeshFeaturesToDracoMesh(scene.nodes[i], mesh)); + } + } + return OkStatus(); +} + +Status GltfDecoder::AddMeshFeaturesToDracoMesh(int node_index, Mesh *mesh) { + const tinygltf::Node &node = gltf_model_.nodes[node_index]; + if (node.mesh >= 0) { + const tinygltf::Mesh &gltf_mesh = gltf_model_.meshes[node.mesh]; + for (const auto &primitive : gltf_mesh.primitives) { + // Decode mesh feature ID sets if present in this primitive. + DRACO_RETURN_IF_ERROR(DecodeMeshFeatures( + primitive, &mesh->GetMaterialLibrary().MutableTextureLibrary(), + mesh)); + } + } + for (int i = 0; i < node.children.size(); ++i) { + DRACO_RETURN_IF_ERROR(AddMeshFeaturesToDracoMesh(node.children[i], mesh)); + } + return OkStatus(); +} + Status GltfDecoder::CheckUnsupportedFeatures() { // Check for morph targets. for (const auto &mesh : gltf_model_.meshes) { @@ -568,8 +686,10 @@ Status GltfDecoder::DecodePrimitive(const tinygltf::Primitive &primitive, const Eigen::Matrix4d &transform_matrix) { - if (primitive.mode != TINYGLTF_MODE_TRIANGLES) { - return Status(Status::DRACO_ERROR, "Primitive does not contain triangles."); + if (primitive.mode != TINYGLTF_MODE_TRIANGLES && + primitive.mode != TINYGLTF_MODE_POINTS) { + return Status(Status::DRACO_ERROR, + "Primitive does not contain triangles or points."); } // Store the transformation scale of this primitive loading as draco::Mesh. @@ -583,6 +703,7 @@ DRACO_ASSIGN_OR_RETURN(const std::vector indices_data, DecodePrimitiveIndices(primitive)); const int number_of_faces = indices_data.size() / 3; + const int number_of_points = indices_data.size(); for (const auto &attribute : primitive.attributes) { const tinygltf::Accessor &accessor = @@ -594,29 +715,14 @@ continue; } - const bool reverse_winding = Determinant(transform_matrix) < 0; - if (attribute.first == "TEXCOORD_0" || attribute.first == "TEXCOORD_1") { - DRACO_RETURN_IF_ERROR(AddTexCoordToMeshBuilder(accessor, indices_data, - att_id, number_of_faces, - reverse_winding, &mb_)); - } else if (attribute.first == "TANGENT") { - const Eigen::Matrix4d matrix = UpdateMatrixForNormals(transform_matrix); - DRACO_RETURN_IF_ERROR(AddTangentToMeshBuilder( - accessor, indices_data, att_id, number_of_faces, matrix, - reverse_winding, &mb_)); - } else if (attribute.first == "POSITION" || attribute.first == "NORMAL") { - const Eigen::Matrix4d matrix = - (attribute.first == "NORMAL") - ? UpdateMatrixForNormals(transform_matrix) - : transform_matrix; - const bool normalize = (attribute.first == "NORMAL"); - DRACO_RETURN_IF_ERROR(AddTransformedDataToMeshBuilder( - accessor, indices_data, att_id, number_of_faces, matrix, normalize, - reverse_winding, &mb_)); + if (primitive.mode == TINYGLTF_MODE_TRIANGLES) { + DRACO_RETURN_IF_ERROR(AddAttributeValuesToBuilder( + attribute.first, accessor, indices_data, att_id, number_of_faces, + transform_matrix, &mb_)); } else { - DRACO_RETURN_IF_ERROR(AddAttributeDataByTypes(accessor, indices_data, - att_id, number_of_faces, - reverse_winding, &mb_)); + DRACO_RETURN_IF_ERROR(AddAttributeValuesToBuilder( + attribute.first, accessor, indices_data, att_id, number_of_points, + transform_matrix, &pb_)); } } @@ -626,24 +732,18 @@ const auto it = gltf_primitive_material_to_draco_material_.find(material_index); if (it != gltf_primitive_material_to_draco_material_.end()) { - if (gltf_primitive_material_to_draco_material_.size() < 256) { - const uint8_t material_value = it->second; - DRACO_RETURN_IF_ERROR(AddMaterialDataToMeshBuilder( - material_value, number_of_faces)); - } else if (gltf_primitive_material_to_draco_material_.size() < - (1 << 16)) { - const uint16_t material_value = it->second; - DRACO_RETURN_IF_ERROR(AddMaterialDataToMeshBuilder( - material_value, number_of_faces)); + if (primitive.mode == TINYGLTF_MODE_TRIANGLES) { + DRACO_RETURN_IF_ERROR( + AddMaterialDataToBuilder(it->second, number_of_faces, &mb_)); } else { - const uint32_t material_value = it->second; - DRACO_RETURN_IF_ERROR(AddMaterialDataToMeshBuilder( - material_value, number_of_faces)); + DRACO_RETURN_IF_ERROR( + AddMaterialDataToBuilder(it->second, number_of_points, &pb_)); } } } next_face_id_ += number_of_faces; + next_point_id_ += number_of_points; return OkStatus(); } @@ -682,32 +782,38 @@ void GltfDecoder::SumAttributeStats(const std::string &attribute_name, int count) { - const auto it = total_attribute_counts_.find(attribute_name); - if (it == total_attribute_counts_.end()) { - total_attribute_counts_[attribute_name] = count; - } else { - total_attribute_counts_[attribute_name] += count; - } + // We know that there must be a valid entry for |attribute_name| at this time. + mesh_attribute_data_[attribute_name].total_attribute_counts += count; } Status GltfDecoder::CheckTypes(const std::string &attribute_name, - int component_type, int type) { - const auto it_ct = attribute_component_type_.find(attribute_name); - if (it_ct == attribute_component_type_.end()) { - attribute_component_type_[attribute_name] = component_type; - } else if (attribute_component_type_[attribute_name] != component_type) { + int component_type, int type, bool normalized) { + auto it_mad = mesh_attribute_data_.find(attribute_name); + + if (it_mad == mesh_attribute_data_.end()) { + MeshAttributeData mad; + mad.component_type = component_type; + mad.attribute_type = type; + mad.normalized = normalized; + mesh_attribute_data_[attribute_name] = mad; + return OkStatus(); + } + if (it_mad->second.component_type != component_type) { return Status( Status::DRACO_ERROR, attribute_name + " attribute component type does not match previous."); } - - const auto it_t = attribute_type_.find(attribute_name); - if (it_t == attribute_type_.end()) { - attribute_type_[attribute_name] = type; - } else if (attribute_type_[attribute_name] != type) { + if (it_mad->second.attribute_type != type) { return Status(Status::DRACO_ERROR, attribute_name + " attribute type does not match previous."); } + if (it_mad->second.normalized != normalized) { + return Status( + Status::DRACO_ERROR, + attribute_name + + " attribute normalized property does not match previous."); + } + return OkStatus(); } @@ -715,21 +821,28 @@ const tinygltf::Primitive &primitive) { DRACO_ASSIGN_OR_RETURN(const int indices_count, DecodePrimitiveIndicesCount(primitive)); - total_indices_count_ += indices_count; + if (primitive.mode == TINYGLTF_MODE_TRIANGLES) { + total_face_indices_count_ += indices_count; + } else if (primitive.mode == TINYGLTF_MODE_POINTS) { + total_point_indices_count_ += indices_count; + } else { + return ErrorStatus("Unsupported primitive indices mode."); + } for (const auto &attribute : primitive.attributes) { const tinygltf::Accessor &accessor = gltf_model_.accessors[attribute.second]; - DRACO_RETURN_IF_ERROR( - CheckTypes(attribute.first, accessor.componentType, accessor.type)); + DRACO_RETURN_IF_ERROR(CheckTypes(attribute.first, accessor.componentType, + accessor.type, accessor.normalized)); SumAttributeStats(attribute.first, accessor.count); } return OkStatus(); } -Status GltfDecoder::AddAttributesToDracoMesh() { - for (const auto &attribute : total_attribute_counts_) { +template +Status GltfDecoder::AddAttributesToDracoMesh(BuilderT *builder) { + for (const auto &attribute : mesh_attribute_data_) { const GeometryAttribute::Type draco_att_type = GltfAttributeToDracoAttribute(attribute.first); if (draco_att_type == GeometryAttribute::INVALID) { @@ -740,8 +853,8 @@ } DRACO_ASSIGN_OR_RETURN( const int att_id, - AddAttribute(draco_att_type, attribute_component_type_[attribute.first], - attribute_type_[attribute.first], &mb_)); + AddAttribute(draco_att_type, attribute.second.component_type, + attribute.second.attribute_type, builder)); attribute_name_to_draco_mesh_attribute_id_[attribute.first] = att_id; } @@ -754,17 +867,54 @@ component_type = DT_UINT16; } material_att_id_ = - mb_.AddAttribute(GeometryAttribute::MATERIAL, 1, component_type); + builder->AddAttribute(GeometryAttribute::MATERIAL, 1, component_type); } return OkStatus(); } -Status GltfDecoder::AddTangentToMeshBuilder( +template +Status GltfDecoder::AddAttributeValuesToBuilder( + const std::string &attribute_name, const tinygltf::Accessor &accessor, + const std::vector &indices_data, int att_id, + int number_of_elements, const Eigen::Matrix4d &transform_matrix, + BuilderT *builder) { + const bool reverse_winding = Determinant(transform_matrix) < 0; + if (attribute_name == "TEXCOORD_0" || attribute_name == "TEXCOORD_1") { + DRACO_RETURN_IF_ERROR(AddTexCoordToBuilder(accessor, indices_data, att_id, + number_of_elements, + reverse_winding, builder)); + } else if (attribute_name == "TANGENT") { + const Eigen::Matrix4d matrix = UpdateMatrixForNormals(transform_matrix); + DRACO_RETURN_IF_ERROR(AddTangentToBuilder(accessor, indices_data, att_id, + number_of_elements, matrix, + reverse_winding, builder)); + } else if (attribute_name == "POSITION" || attribute_name == "NORMAL") { + const Eigen::Matrix4d matrix = + (attribute_name == "NORMAL") ? UpdateMatrixForNormals(transform_matrix) + : transform_matrix; + const bool normalize = (attribute_name == "NORMAL"); + DRACO_RETURN_IF_ERROR(AddTransformedDataToBuilder( + accessor, indices_data, att_id, number_of_elements, matrix, normalize, + reverse_winding, builder)); + } else if (attribute_name.rfind("_FEATURE_ID_") == 0) { + DRACO_RETURN_IF_ERROR(AddFeatureIdToBuilder( + accessor, indices_data, att_id, number_of_elements, reverse_winding, + attribute_name, builder)); + } else { + DRACO_RETURN_IF_ERROR(AddAttributeDataByTypes(accessor, indices_data, + att_id, number_of_elements, + reverse_winding, builder)); + } + return OkStatus(); +} + +template +Status GltfDecoder::AddTangentToBuilder( const tinygltf::Accessor &accessor, - const std::vector &indices_data, int att_id, int number_of_faces, - const Eigen::Matrix4d &transform_matrix, bool reverse_winding, - TriangleSoupMeshBuilder *mb) { + const std::vector &indices_data, int att_id, + int number_of_elements, const Eigen::Matrix4d &transform_matrix, + bool reverse_winding, BuilderT *builder) { DRACO_ASSIGN_OR_RETURN( std::vector data, TinyGltfUtils::CopyDataAsFloat(gltf_model_, accessor)); @@ -787,15 +937,16 @@ } } - SetValuesPerFace(indices_data, att_id, number_of_faces, data, - reverse_winding, mb); + SetValuesForBuilder(indices_data, att_id, number_of_elements, data, + reverse_winding, builder); return OkStatus(); } -Status GltfDecoder::AddTexCoordToMeshBuilder( +template +Status GltfDecoder::AddTexCoordToBuilder( const tinygltf::Accessor &accessor, - const std::vector &indices_data, int att_id, int number_of_faces, - bool reverse_winding, TriangleSoupMeshBuilder *mb) { + const std::vector &indices_data, int att_id, + int number_of_elements, bool reverse_winding, BuilderT *builder) { DRACO_ASSIGN_OR_RETURN( std::vector data, TinyGltfUtils::CopyDataAsFloat(gltf_model_, accessor)); @@ -806,16 +957,49 @@ uv[1] = 1.0 - uv[1]; } - SetValuesPerFace(indices_data, att_id, number_of_faces, data, - reverse_winding, mb); + SetValuesForBuilder(indices_data, att_id, number_of_elements, data, + reverse_winding, builder); return OkStatus(); } -Status GltfDecoder::AddTransformedDataToMeshBuilder( +template +Status GltfDecoder::AddFeatureIdToBuilder( const tinygltf::Accessor &accessor, - const std::vector &indices_data, int att_id, int number_of_faces, - const Eigen::Matrix4d &transform_matrix, bool normalize, - bool reverse_winding, TriangleSoupMeshBuilder *mb) { + const std::vector &indices_data, int att_id, + int number_of_elements, bool reverse_winding, + const std::string &attribute_name, BuilderT *builder) { + // Check that the feature ID attribute has correct type. + const int num_components = + TinyGltfUtils::GetNumComponentsForType(accessor.type); + if (num_components != 1) { + return ErrorStatus("Invalid feature ID attribute type."); + } + const draco::DataType draco_component_type = + GltfComponentTypeToDracoType(accessor.componentType); + if (draco_component_type != DT_UINT8 && draco_component_type != DT_UINT16 && + draco_component_type != DT_FLOAT32) { + return ErrorStatus("Invalid feature ID attribute component type."); + } + + // Set feature ID attribute values to mesh faces. + DRACO_RETURN_IF_ERROR(AddAttributeDataByTypes(accessor, indices_data, att_id, + number_of_elements, + reverse_winding, builder)); + + // Store feature ID attribute name with index like _FEATURE_ID_5 in Draco + // attribute metadata. + std::unique_ptr metadata(new draco::AttributeMetadata()); + metadata->AddEntryString("attribute_name", attribute_name); + builder->AddAttributeMetadata(att_id, std::move(metadata)); + return OkStatus(); +} + +template +Status GltfDecoder::AddTransformedDataToBuilder( + const tinygltf::Accessor &accessor, + const std::vector &indices_data, int att_id, + int number_of_elements, const Eigen::Matrix4d &transform_matrix, + bool normalize, bool reverse_winding, BuilderT *builder) { DRACO_ASSIGN_OR_RETURN( std::vector data, TinyGltfUtils::CopyDataAsFloat(gltf_model_, accessor)); @@ -832,12 +1016,36 @@ } } - SetValuesPerFace(indices_data, att_id, number_of_faces, data, - reverse_winding, mb); + SetValuesForBuilder(indices_data, att_id, number_of_elements, data, + reverse_winding, builder); return OkStatus(); } template +void GltfDecoder::SetValuesForBuilder(const std::vector &indices_data, + int att_id, int number_of_elements, + const std::vector &data, + bool reverse_winding, + TriangleSoupMeshBuilder *builder) { + SetValuesPerFace(indices_data, att_id, number_of_elements, data, + reverse_winding, builder); +} + +template +void GltfDecoder::SetValuesForBuilder(const std::vector &indices_data, + int att_id, int number_of_elements, + const std::vector &data, + bool reverse_winding, + PointCloudBuilder *builder) { + for (int i = 0; i < number_of_elements; ++i) { + const uint32_t v_id = indices_data[i]; + const PointIndex pi(v_id + next_point_id_); + builder->SetAttributeValueForPoint(att_id, pi, + GetDataContentAddress(data[v_id])); + } +} + +template void GltfDecoder::SetValuesPerFace(const std::vector &indices_data, int att_id, int number_of_faces, const std::vector &data, @@ -852,16 +1060,37 @@ const uint32_t v_prev_id = indices_data[base_corner + prev_offset]; const FaceIndex face_index(f + next_face_id_); - mb->SetAttributeValuesForFace(att_id, face_index, data[v_id].data(), - data[v_next_id].data(), - data[v_prev_id].data()); + mb->SetAttributeValuesForFace(att_id, face_index, + GetDataContentAddress(data[v_id]), + GetDataContentAddress(data[v_next_id]), + GetDataContentAddress(data[v_prev_id])); } } +// Get the address of data content for arithmetic types |T|. +template +const void *GetDataContentAddressImpl(const T &data, + std::true_type /* is_arithmetic */) { + return &data; +} + +// Get the address of data content for vector types |T|. +template +const void *GetDataContentAddressImpl(const T &data, + std::false_type /* is_arithmetic */) { + return data.data(); +} + +template +const void *GltfDecoder::GetDataContentAddress(const T &data) const { + return GetDataContentAddressImpl(data, std::is_arithmetic()); +} + +template Status GltfDecoder::AddAttributeDataByTypes( const tinygltf::Accessor &accessor, - const std::vector &indices_data, int att_id, int number_of_faces, - bool reverse_winding, TriangleSoupMeshBuilder *mb) { + const std::vector &indices_data, int att_id, + int number_of_elements, bool reverse_winding, BuilderT *builder) { typedef VectorD Vector2u8i; typedef VectorD Vector3u8i; typedef VectorD Vector4u8i; @@ -869,27 +1098,62 @@ typedef VectorD Vector3u16i; typedef VectorD Vector4u16i; switch (accessor.type) { + case TINYGLTF_TYPE_SCALAR: + switch (accessor.componentType) { + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: { + DRACO_ASSIGN_OR_RETURN(std::vector data, + CopyDataAs(gltf_model_, accessor)); + SetValuesForBuilder(indices_data, att_id, number_of_elements, + data, reverse_winding, builder); + } break; + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: { + DRACO_ASSIGN_OR_RETURN(std::vector data, + CopyDataAs(gltf_model_, accessor)); + SetValuesForBuilder(indices_data, att_id, + number_of_elements, data, + reverse_winding, builder); + } break; + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: { + DRACO_ASSIGN_OR_RETURN(std::vector data, + CopyDataAs(gltf_model_, accessor)); + SetValuesForBuilder(indices_data, att_id, + number_of_elements, data, + reverse_winding, builder); + } break; + case TINYGLTF_COMPONENT_TYPE_FLOAT: { + DRACO_ASSIGN_OR_RETURN(std::vector data, + CopyDataAs(gltf_model_, accessor)); + SetValuesForBuilder(indices_data, att_id, number_of_elements, + data, reverse_winding, builder); + } break; + default: + return ErrorStatus("Add attribute data, unknown component type."); + } + break; case TINYGLTF_TYPE_VEC2: switch (accessor.componentType) { case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: { DRACO_ASSIGN_OR_RETURN(std::vector data, CopyDataAs(gltf_model_, accessor)); - SetValuesPerFace(indices_data, att_id, number_of_faces, - data, reverse_winding, mb); + SetValuesForBuilder(indices_data, att_id, + number_of_elements, data, + reverse_winding, builder); } break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: { DRACO_ASSIGN_OR_RETURN( std::vector data, CopyDataAs(gltf_model_, accessor)); - SetValuesPerFace(indices_data, att_id, number_of_faces, - data, reverse_winding, mb); + SetValuesForBuilder(indices_data, att_id, + number_of_elements, data, + reverse_winding, builder); } break; case TINYGLTF_COMPONENT_TYPE_FLOAT: { DRACO_ASSIGN_OR_RETURN( std::vector data, TinyGltfUtils::CopyDataAsFloat(gltf_model_, accessor)); - SetValuesPerFace(indices_data, att_id, number_of_faces, - data, reverse_winding, mb); + SetValuesForBuilder(indices_data, att_id, + number_of_elements, data, + reverse_winding, builder); } break; default: return Status(Status::DRACO_ERROR, @@ -901,22 +1165,25 @@ case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: { DRACO_ASSIGN_OR_RETURN(std::vector data, CopyDataAs(gltf_model_, accessor)); - SetValuesPerFace(indices_data, att_id, number_of_faces, - data, reverse_winding, mb); + SetValuesForBuilder(indices_data, att_id, + number_of_elements, data, + reverse_winding, builder); } break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: { DRACO_ASSIGN_OR_RETURN( std::vector data, CopyDataAs(gltf_model_, accessor)); - SetValuesPerFace(indices_data, att_id, number_of_faces, - data, reverse_winding, mb); + SetValuesForBuilder(indices_data, att_id, + number_of_elements, data, + reverse_winding, builder); } break; case TINYGLTF_COMPONENT_TYPE_FLOAT: { DRACO_ASSIGN_OR_RETURN( std::vector data, TinyGltfUtils::CopyDataAsFloat(gltf_model_, accessor)); - SetValuesPerFace(indices_data, att_id, number_of_faces, - data, reverse_winding, mb); + SetValuesForBuilder(indices_data, att_id, + number_of_elements, data, + reverse_winding, builder); } break; default: return Status(Status::DRACO_ERROR, @@ -928,22 +1195,25 @@ case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: { DRACO_ASSIGN_OR_RETURN(std::vector data, CopyDataAs(gltf_model_, accessor)); - SetValuesPerFace(indices_data, att_id, number_of_faces, - data, reverse_winding, mb); + SetValuesForBuilder(indices_data, att_id, + number_of_elements, data, + reverse_winding, builder); } break; case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: { DRACO_ASSIGN_OR_RETURN( std::vector data, CopyDataAs(gltf_model_, accessor)); - SetValuesPerFace(indices_data, att_id, number_of_faces, - data, reverse_winding, mb); + SetValuesForBuilder(indices_data, att_id, + number_of_elements, data, + reverse_winding, builder); } break; case TINYGLTF_COMPONENT_TYPE_FLOAT: { DRACO_ASSIGN_OR_RETURN( std::vector data, TinyGltfUtils::CopyDataAsFloat(gltf_model_, accessor)); - SetValuesPerFace(indices_data, att_id, number_of_faces, - data, reverse_winding, mb); + SetValuesForBuilder(indices_data, att_id, + number_of_elements, data, + reverse_winding, builder); } break; default: return Status(Status::DRACO_ERROR, @@ -988,6 +1258,18 @@ return OkStatus(); } +void GltfDecoder::SetAttributePropertiesOnDracoMesh(Mesh *mesh) { + for (const auto &mad : mesh_attribute_data_) { + const int att_id = attribute_name_to_draco_mesh_attribute_id_[mad.first]; + if (att_id == -1) { + continue; + } + if (mad.second.normalized) { + mesh->attribute(att_id)->set_normalized(true); + } + } +} + Status GltfDecoder::AddMaterialsToDracoMesh(Mesh *mesh) { bool is_normal_map_used = false; @@ -1020,13 +1302,44 @@ return OkStatus(); } +template +Status GltfDecoder::AddMaterialDataToBuilder(int material_value, + int number_of_elements, + BuilderT *builder) { + if (gltf_primitive_material_to_draco_material_.size() < 256) { + const uint8_t typed_material_value = material_value; + DRACO_RETURN_IF_ERROR(AddMaterialDataToBuilderInternal( + typed_material_value, number_of_elements, builder)); + } else if (gltf_primitive_material_to_draco_material_.size() < (1 << 16)) { + const uint16_t typed_material_value = material_value; + DRACO_RETURN_IF_ERROR(AddMaterialDataToBuilderInternal( + typed_material_value, number_of_elements, builder)); + } else { + const uint32_t typed_material_value = material_value; + DRACO_RETURN_IF_ERROR(AddMaterialDataToBuilderInternal( + typed_material_value, number_of_elements, builder)); + } + return OkStatus(); +} + template -Status GltfDecoder::AddMaterialDataToMeshBuilder(T material_value, - int number_of_faces) { +Status GltfDecoder::AddMaterialDataToBuilderInternal( + T material_value, int number_of_faces, TriangleSoupMeshBuilder *builder) { for (int f = 0; f < number_of_faces; ++f) { const FaceIndex face_index(f + next_face_id_); - mb_.SetPerFaceAttributeValueForFace(material_att_id_, face_index, - &material_value); + builder->SetPerFaceAttributeValueForFace(material_att_id_, face_index, + &material_value); + } + return OkStatus(); +} + +template +Status GltfDecoder::AddMaterialDataToBuilderInternal( + T material_value, int number_of_points, PointCloudBuilder *builder) { + for (int pi = 0; pi < number_of_points; ++pi) { + const PointIndex point_index(pi + next_point_id_); + builder->SetAttributeValueForPoint(material_att_id_, point_index, + &material_value); } return OkStatus(); } @@ -1040,8 +1353,8 @@ } const tinygltf::Texture &input_texture = gltf_model_.textures[texture_index]; - const auto texture_it = - gltf_image_to_draco_texture_.find(input_texture.source); + int source_index = input_texture.source; + const auto texture_it = gltf_image_to_draco_texture_.find(source_index); if (texture_it != gltf_image_to_draco_texture_.end()) { Texture *const texture = texture_it->second; // Default GLTF 2.0 sampler uses REPEAT mode along both S and T directions. @@ -1084,6 +1397,8 @@ DRACO_RETURN_IF_ERROR(GatherAttributeAndMaterialStats()); DRACO_RETURN_IF_ERROR(AddLightsToScene()); DRACO_RETURN_IF_ERROR(AddMaterialsVariantsNamesToScene()); + DRACO_RETURN_IF_ERROR(AddStructuralMetadataToGeometry(scene_.get())); + DRACO_RETURN_IF_ERROR(CopyTextures(scene_.get())); for (const tinygltf::Scene &scene : gltf_model_.scenes) { for (int i = 0; i < scene.nodes.size(); ++i) { DRACO_RETURN_IF_ERROR( @@ -1093,9 +1408,9 @@ } DRACO_RETURN_IF_ERROR(AddAnimationsToScene()); - DRACO_RETURN_IF_ERROR(CopyTextures(scene_.get())); DRACO_RETURN_IF_ERROR(AddMaterialsToScene()); DRACO_RETURN_IF_ERROR(AddSkinsToScene()); + MoveNonMaterialTextures(scene_.get()); return OkStatus(); } @@ -1182,6 +1497,186 @@ return OkStatus(); } +template +Status GltfDecoder::AddStructuralMetadataToGeometry(GeometryT *geometry) { + // Check whether the glTF model has structural metadata. + const auto &e = gltf_model_.extensions.find("EXT_structural_metadata"); + if (e == gltf_model_.extensions.end()) { + // The glTF model has no structural metadata. + return OkStatus(); + } + const tinygltf::Value::Object &o = e->second.Get(); + + // Decode property table schema. + { + const auto &value = o.find("schema"); + if (value == o.end()) { + return ErrorStatus("Structural metadata extension has no schema."); + } + const tinygltf::Value &object = value->second; + if (!object.IsObject()) { + return ErrorStatus("Structural metadata extension schema is malformed."); + } + + // Decodes tinygltf::Value into PropertyTable::Schema::Object. + struct SchemaParser { + static Status Parse(const tinygltf::Value &value, + PropertyTable::Schema::Object *object) { + switch (value.Type()) { + case tinygltf::OBJECT_TYPE: { + for (auto &it : value.Get()) { + object->SetObjects().emplace_back(it.first); + DRACO_RETURN_IF_ERROR( + Parse(it.second, &object->SetObjects().back())); + } + } break; + case tinygltf::ARRAY_TYPE: { + for (int i = 0; i < value.ArrayLen(); ++i) { + object->SetArray().emplace_back(); + DRACO_RETURN_IF_ERROR( + Parse(value.Get(i), &object->SetArray().back())); + } + } break; + case tinygltf::STRING_TYPE: + object->SetString(value.Get()); + break; + case tinygltf::INT_TYPE: + object->SetInteger(value.Get()); + break; + case tinygltf::BOOL_TYPE: + object->SetBoolean(value.Get()); + break; + case tinygltf::REAL_TYPE: + case tinygltf::BINARY_TYPE: + case tinygltf::NULL_TYPE: + default: + // Not used in the schema JSON. + return ErrorStatus("Unsupported JSON type in schema."); + } + return OkStatus(); + } + }; + + // Parse property table schema and set it to |geometry|. + PropertyTable::Schema schema; + DRACO_RETURN_IF_ERROR(SchemaParser::Parse(object, &schema.json)); + geometry->GetStructuralMetadata().SetPropertyTableSchema(schema); + } + + // Decode property tables. + { + const auto &tables = o.find("propertyTables"); + if (tables == o.end()) { + return ErrorStatus( + "Structural metadata extension has no property tables."); + } + const tinygltf::Value &tables_array = tables->second; + if (!tables_array.IsArray()) { + return ErrorStatus("Property tables array is malformed."); + } + + // Loop over all property tables. + for (int i = 0; i < tables_array.Size(); i++) { + // Create a property table and populate it below. + std::unique_ptr property_table(new PropertyTable()); + + const auto &object = tables_array.Get(i); + if (!object.IsObject()) { + return ErrorStatus("Property table is malformed."); + } + const auto o = object.Get(); + + // The "class" property is required. + bool success; + std::string str_value; + DRACO_ASSIGN_OR_RETURN(success, DecodeString("class", o, &str_value)); + if (success) { + property_table->SetClass(str_value); + } else { + return ErrorStatus("Property class is malformed."); + } + + // The "count" property is required. + int int_value; + DRACO_ASSIGN_OR_RETURN(success, DecodeInt("count", o, &int_value)); + if (success) { + property_table->SetCount(int_value); + } else { + return ErrorStatus("Property count is malformed."); + } + + // The "name" property is optional. + DRACO_ASSIGN_OR_RETURN(success, DecodeString("name", o, &str_value)); + if (success) { + property_table->SetName(str_value); + } + + // Decode property table properties (columns). + { + constexpr char kName[] = "properties"; + if (!object.Has(kName)) { + return ErrorStatus("Property table is malformed."); + } + const tinygltf::Value &value = object.Get(kName); + if (!value.IsObject()) { + return ErrorStatus( + "Property table properties property is malformed."); + } + + // Loop over property table properties. + for (const auto &key : value.Keys()) { + // Create a property table property and populate it below. + std::unique_ptr property( + new PropertyTable::Property()); + + const auto &property_object = value.Get(key); + if (!property_object.IsObject()) { + return ErrorStatus("Property entry is malformed."); + } + property->SetName(key); + const auto o = property_object.Get(); + + // The "values" property is required. + DRACO_ASSIGN_OR_RETURN( + success, + DecodePropertyTableData("values", o, &property->GetData())); + if (!success) { + return ErrorStatus("Property values property is malformed."); + } + + // All other properties are not required. + DRACO_ASSIGN_OR_RETURN( + success, DecodeString("stringOffsetType", o, &str_value)); + if (success) { + property->GetStringOffsets().type = str_value; + } + DRACO_ASSIGN_OR_RETURN( + success, DecodeString("arrayOffsetType", o, &str_value)); + if (success) { + property->GetArrayOffsets().type = str_value; + } + DRACO_ASSIGN_OR_RETURN( + success, + DecodePropertyTableData("arrayOffsets", o, + &property->GetArrayOffsets().data)); + DRACO_ASSIGN_OR_RETURN( + success, + DecodePropertyTableData("stringOffsets", o, + &property->GetStringOffsets().data)); + + // Add property to the property table. + property_table->AddProperty(std::move(property)); + } + } + + // Add property table to structural metadata. + geometry->GetStructuralMetadata().AddPropertyTable( + std::move(property_table)); + } + } + return OkStatus(); +} + Status GltfDecoder::AddAnimationsToScene() { for (const auto &animation : gltf_model_.animations) { const AnimationIndex animation_index = scene_->AddAnimation(); @@ -1292,8 +1787,10 @@ Status GltfDecoder::DecodePrimitiveForScene( const tinygltf::Primitive &primitive, MeshGroup *mesh_group) { - if (primitive.mode != TINYGLTF_MODE_TRIANGLES) { - return Status(Status::DRACO_ERROR, "Primitive does not contain triangles."); + if (primitive.mode != TINYGLTF_MODE_TRIANGLES && + primitive.mode != TINYGLTF_MODE_POINTS) { + return Status(Status::DRACO_ERROR, + "Primitive does not contain triangles or points."); } // Decode materials variants mappings if present in this primitive. @@ -1317,52 +1814,66 @@ DRACO_ASSIGN_OR_RETURN(const std::vector indices_data, DecodePrimitiveIndices(primitive)); const int number_of_faces = indices_data.size() / 3; + const int number_of_points = indices_data.size(); // Note that glTF mesh |primitive| has no name; no name is set to Draco mesh. TriangleSoupMeshBuilder mb; - mb.Start(number_of_faces); + PointCloudBuilder pb; + if (primitive.mode == TINYGLTF_MODE_TRIANGLES) { + mb.Start(number_of_faces); + } else { + pb.Start(number_of_points); + } + std::set normalized_attributes; for (const auto &attribute : primitive.attributes) { const tinygltf::Accessor &accessor = gltf_model_.accessors[attribute.second]; const int component_type = accessor.componentType; const int type = accessor.type; - DRACO_ASSIGN_OR_RETURN( - const int att_id, - AddAttribute(attribute.first, component_type, type, &mb)); + const bool normalized = accessor.normalized; + int att_id = -1; + if (primitive.mode == TINYGLTF_MODE_TRIANGLES) { + DRACO_ASSIGN_OR_RETURN( + att_id, AddAttribute(attribute.first, component_type, type, &mb)); + } else { + DRACO_ASSIGN_OR_RETURN( + att_id, AddAttribute(attribute.first, component_type, type, &pb)); + } if (att_id == -1) { continue; } + if (normalized) { + normalized_attributes.insert(att_id); + } - const bool reverse_winding = false; - if (attribute.first == "TEXCOORD_0" || attribute.first == "TEXCOORD_1") { - DRACO_RETURN_IF_ERROR(AddTexCoordToMeshBuilder(accessor, indices_data, - att_id, number_of_faces, - reverse_winding, &mb)); - } else if (attribute.first == "TANGENT") { - const Eigen::Matrix4d matrix = Eigen::Matrix4d::Identity(); - DRACO_RETURN_IF_ERROR(AddTangentToMeshBuilder( - accessor, indices_data, att_id, number_of_faces, matrix, - reverse_winding, &mb)); - } else if (attribute.first == "POSITION" || attribute.first == "NORMAL") { - const Eigen::Matrix4d matrix = Eigen::Matrix4d::Identity(); - const bool normalize = (attribute.first == "NORMAL"); - DRACO_RETURN_IF_ERROR(AddTransformedDataToMeshBuilder( - accessor, indices_data, att_id, number_of_faces, matrix, normalize, - reverse_winding, &mb)); + if (primitive.mode == TINYGLTF_MODE_TRIANGLES) { + DRACO_RETURN_IF_ERROR(AddAttributeValuesToBuilder( + attribute.first, accessor, indices_data, att_id, number_of_faces, + Eigen::Matrix4d::Identity(), &mb)); } else { - DRACO_RETURN_IF_ERROR(AddAttributeDataByTypes(accessor, indices_data, - att_id, number_of_faces, - reverse_winding, &mb)); + DRACO_RETURN_IF_ERROR(AddAttributeValuesToBuilder( + attribute.first, accessor, indices_data, att_id, number_of_points, + Eigen::Matrix4d::Identity(), &pb)); } } int material_index = primitive.material; - std::unique_ptr mesh = mb.Finalize(); - if (mesh == nullptr) { - return Status(Status::DRACO_ERROR, "Could not build Draco mesh."); - } + DRACO_ASSIGN_OR_RETURN( + std::unique_ptr mesh, + BuildMeshFromBuilder(primitive.mode == TINYGLTF_MODE_TRIANGLES, &mb, + &pb)); + + // Set all normalized flags for appropriate attributes. + for (const int32_t att_id : normalized_attributes) { + mesh->attribute(att_id)->set_normalized(true); + } + // Decode mesh feature ID sets if present in this primitive. + DRACO_RETURN_IF_ERROR(DecodeMeshFeatures( + primitive, &scene_->GetMaterialLibrary().MutableTextureLibrary(), + mesh.get())); + const MeshIndex mesh_index = scene_->AddMesh(std::move(mesh)); if (mesh_index == kInvalidMeshIndex) { return Status(Status::DRACO_ERROR, "Could not add Draco mesh to scene."); @@ -1425,23 +1936,196 @@ return OkStatus(); } +Status GltfDecoder::DecodeMeshFeatures(const tinygltf::Primitive &primitive, + TextureLibrary *texture_library, + Mesh *mesh) { + const auto &e = primitive.extensions.find("EXT_mesh_features"); + if (e == primitive.extensions.end()) { + return OkStatus(); + } + std::vector> mesh_features; + DRACO_RETURN_IF_ERROR( + DecodeMeshFeatures(e->second.Get(), + texture_library, &mesh_features)); + for (int i = 0; i < mesh_features.size(); i++) { + const MeshFeaturesIndex mfi = + mesh->AddMeshFeatures(std::move(mesh_features[i])); + if (scene_ == nullptr) { + // If we are decoding to a mesh, we need to restrict the mesh features to + // the primitive's material. + // TODO(ostava): This will not work properly when two primitives share the + // same material but have different mesh features. We will need to + // duplicate the materials in this case. + const auto mat_it = + gltf_primitive_material_to_draco_material_.find(primitive.material); + if (mat_it != gltf_primitive_material_to_draco_material_.end()) { + mesh->AddMeshFeaturesMaterialMask(mfi, mat_it->second); + } + } + } + return OkStatus(); +} + +Status GltfDecoder::DecodeMeshFeatures( + const tinygltf::Value::Object &extension, TextureLibrary *texture_library, + std::vector> *mesh_features) { + // Decode all mesh feature ID sets from JSON like this: + // "EXT_mesh_features": { + // "featureIds": [ + // { + // "label": "water", + // "featureCount": 2, + // "propertyTable": 0, + // "attribute": 0 + // }, + // { + // "featureCount": 12, + // "nullFeatureId": 100, + // "texture" : { + // "index": 0, + // "texCoord": 0, + // "channels": [0, 1, 2, 3] + // } + // } + // ] + // } + const auto &object = extension.find("featureIds"); + if (object == extension.end()) { + return ErrorStatus("Mesh features extension is malformed."); + } + const tinygltf::Value &array = object->second; + if (!array.IsArray()) { + return ErrorStatus("Mesh features array is malformed."); + } + for (int i = 0; i < array.Size(); i++) { + // Create a new feature ID set object and populate it below. + mesh_features->push_back(std::unique_ptr(new MeshFeatures())); + MeshFeatures &features = *mesh_features->back(); + + const auto &object = array.Get(i); + if (!object.IsObject()) { + return ErrorStatus("Mesh features array entry is malformed."); + } + + // The "featureCount" property is required. + { + constexpr char kName[] = "featureCount"; + if (!object.Has(kName)) { + return ErrorStatus("Mesh features is malformed."); + } + const tinygltf::Value &value = object.Get(kName); + if (!value.IsInt()) { + return ErrorStatus("Feature count property is malformed."); + } + features.SetFeatureCount(value.Get()); + } + + // All other properties are optional. + { + constexpr char kName[] = "nullFeatureId"; + if (object.Has(kName)) { + const tinygltf::Value &value = object.Get(kName); + if (!value.IsInt()) { + return ErrorStatus("Null feature ID property is malformed."); + } + features.SetNullFeatureId(value.Get()); + } + } + { + constexpr char kName[] = "label"; + if (object.Has(kName)) { + const tinygltf::Value &value = object.Get(kName); + if (!value.IsString()) { + return ErrorStatus("Label property is malformed."); + } + features.SetLabel(value.Get()); + } + } + { + constexpr char kName[] = "attribute"; + if (object.Has(kName)) { + const tinygltf::Value &value = object.Get(kName); + if (!value.IsInt()) { + return ErrorStatus("Attribute property is malformed."); + } + features.SetAttributeIndex(value.Get()); + } + } + { + constexpr char kName[] = "texture"; + if (object.Has(kName)) { + const tinygltf::Value &value = object.Get(kName); + if (!value.IsObject()) { + return ErrorStatus("Texture property is malformed."); + } + + // Decode texture contining mesh feature IDs into the |features| object + // via a temporary |material| object. + Material material(texture_library); + const auto &container_object = object.Get(); + DRACO_RETURN_IF_ERROR(DecodeTexture(kName, TextureMap::GENERIC, + container_object, &material)); + features.SetTextureMap( + *material.GetTextureMapByType(TextureMap::GENERIC)); + + // Decode array of texture channel indices. + std::vector channels; + { + constexpr char kName[] = "channels"; + if (value.Has(kName)) { + const tinygltf::Value &array = value.Get(kName); + if (!array.IsArray()) { + return ErrorStatus("Channels property is malformed."); + } + for (int i = 0; i < array.Size(); i++) { + const tinygltf::Value &value = array.Get(i); + if (!value.IsNumber()) { + return Status(Status::DRACO_ERROR, + "Channels value is malformed."); + } + channels.push_back(value.Get()); + } + } else { + channels = {0}; + } + } + features.SetTextureChannels(channels); + } + } + { + constexpr char kName[] = "propertyTable"; + if (object.Has(kName)) { + const tinygltf::Value &value = object.Get(kName); + if (!value.IsInt()) { + return ErrorStatus("Property table property is malformed."); + } + features.SetPropertyTableIndex(value.Get()); + } + } + } + return OkStatus(); +} + +template StatusOr GltfDecoder::AddAttribute(const std::string &attribute_name, int component_type, int type, - TriangleSoupMeshBuilder *mb) { + BuilderT *builder) { const GeometryAttribute::Type draco_att_type = GltfAttributeToDracoAttribute(attribute_name); if (draco_att_type == GeometryAttribute::INVALID) { - return Status(Status::DRACO_ERROR, - "Attribute " + attribute_name + " is not supported."); + // Return attribute id -1 that will be ignored and not included in the mesh. + return -1; } DRACO_ASSIGN_OR_RETURN( - const int att_id, AddAttribute(draco_att_type, component_type, type, mb)); + const int att_id, + AddAttribute(draco_att_type, component_type, type, builder)); return att_id; } +template StatusOr GltfDecoder::AddAttribute(GeometryAttribute::Type attribute_type, int component_type, int type, - TriangleSoupMeshBuilder *mb) { + BuilderT *builder) { const int num_components = TinyGltfUtils::GetNumComponentsForType(type); if (num_components == 0) { return Status(Status::DRACO_ERROR, @@ -1454,8 +2138,8 @@ return Status(Status::DRACO_ERROR, "Could not add attribute with invalid type."); } - const int att_id = - mb->AddAttribute(attribute_type, num_components, draco_component_type); + const int att_id = builder->AddAttribute(attribute_type, num_components, + draco_component_type); if (att_id < 0) { return Status(Status::DRACO_ERROR, "Could not add attribute."); } @@ -1883,6 +2567,51 @@ return true; } +StatusOr GltfDecoder::DecodeInt(const std::string &name, + const tinygltf::Value::Object &object, + int *value) { + const auto &it = object.find(name); + if (it == object.end()) { + return false; + } + const tinygltf::Value &number = it->second; + if (!number.IsNumber()) { + return ErrorStatus("Invalid " + name + "."); + } + *value = number.Get(); + return true; +} + +StatusOr GltfDecoder::DecodeString(const std::string &name, + const tinygltf::Value::Object &object, + std::string *value) { + const auto &it = object.find(name); + if (it == object.end()) { + return false; + } + const tinygltf::Value &string = it->second; + if (!string.IsString()) { + return ErrorStatus("Invalid " + name + "."); + } + *value = string.Get(); + return true; +} + +StatusOr GltfDecoder::DecodePropertyTableData( + const std::string &name, const tinygltf::Value::Object &object, + PropertyTable::Property::Data *data) { + int buffer_view_index; + DRACO_ASSIGN_OR_RETURN(const bool success, + DecodeInt(name, object, &buffer_view_index)); + if (!success) { + return false; + } + DRACO_RETURN_IF_ERROR( + CopyDataFromBufferView(gltf_model_, buffer_view_index, &data->data)); + data->target = gltf_model_.bufferViews[buffer_view_index].target; + return true; +} + StatusOr GltfDecoder::DecodeVector3f( const std::string &name, const tinygltf::Value::Object &object, Vector3f *value) { @@ -2068,6 +2797,47 @@ return OkStatus(); } +void GltfDecoder::MoveNonMaterialTextures(Mesh *mesh) { + std::unordered_set non_material_textures; + for (MeshFeaturesIndex i(0); i < mesh->NumMeshFeatures(); i++) { + Texture *const texture = mesh->GetMeshFeatures(i).GetTextureMap().texture(); + if (texture != nullptr) { + non_material_textures.insert(texture); + } + } + MoveNonMaterialTextures(non_material_textures, + &mesh->GetMaterialLibrary().MutableTextureLibrary(), + &mesh->GetNonMaterialTextureLibrary()); +} + +void GltfDecoder::MoveNonMaterialTextures(Scene *scene) { + std::unordered_set non_material_textures; + for (MeshIndex i(0); i < scene->NumMeshes(); i++) { + for (MeshFeaturesIndex j(0); j < scene->GetMesh(i).NumMeshFeatures(); j++) { + Texture *const texture = + scene->GetMesh(i).GetMeshFeatures(j).GetTextureMap().texture(); + if (texture != nullptr) { + non_material_textures.insert(texture); + } + } + } + MoveNonMaterialTextures(non_material_textures, + &scene->GetMaterialLibrary().MutableTextureLibrary(), + &scene->GetNonMaterialTextureLibrary()); +} + +void GltfDecoder::MoveNonMaterialTextures( + const std::unordered_set &non_material_textures, + TextureLibrary *material_tl, TextureLibrary *non_material_tl) { + // TODO(vytyaz): Consider textures that are both material and non-material. + for (int i = 0; i < material_tl->NumTextures(); i++) { + // Move non-material texture from material to non-material texture library. + if (non_material_textures.count(material_tl->GetTexture(i)) == 1) { + non_material_tl->PushTexture(material_tl->RemoveTexture(i--)); + } + } +} + bool GltfDecoder::PrimitiveSignature::operator==( const PrimitiveSignature &signature) const { return primitive.indices == signature.primitive.indices && @@ -2092,6 +2862,25 @@ return hash; } +StatusOr> GltfDecoder::BuildMeshFromBuilder( + bool use_mesh_builder, TriangleSoupMeshBuilder *mb, PointCloudBuilder *pb) { + std::unique_ptr mesh; + if (use_mesh_builder) { + mesh = mb->Finalize(); + } else { + std::unique_ptr pc = pb->Finalize(true); + if (pc) { + mesh.reset(new Mesh()); + PointCloud *mesh_pc = mesh.get(); + mesh_pc->Copy(*pc); + } + } + if (!mesh) { + return ErrorStatus("Failed to build Draco mesh from glTF data."); + } + return mesh; +} + } // namespace draco #endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/io/gltf_decoder.h draco-1.5.5+dfsg/src/draco/io/gltf_decoder.h --- draco-1.5.3+dfsg/src/draco/io/gltf_decoder.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/gltf_decoder.h 2022-10-29 00:55:03.000000000 +0000 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "draco/core/decoder_buffer.h" @@ -31,6 +32,7 @@ #include "draco/io/texture_io.h" #include "draco/mesh/mesh.h" #include "draco/mesh/triangle_soup_mesh_builder.h" +#include "draco/point_cloud/point_cloud_builder.h" #include "draco/scene/scene.h" #include "tiny_gltf.h" @@ -130,7 +132,7 @@ // Checks that all the same glTF attribute types in different meshes and // primitives contain the same characteristics. Status CheckTypes(const std::string &attribute_name, int component_type, - int type); + int type, bool normalized); // Accumulates the number of elements per attribute for |primitive|. Status AccumulatePrimitiveStats(const tinygltf::Primitive &primitive); @@ -139,48 +141,86 @@ // GatherAttributeAndMaterialStats() must be called before this function. The // GeometryAttribute::MATERIAL attribute will be created only if the glTF file // contains more than one material. - Status AddAttributesToDracoMesh(); + template + Status AddAttributesToDracoMesh(BuilderT *builder); + + // Copies attribute data from |accessor| and adds it to a Draco mesh using the + // geometry builder |builder|. + template + Status AddAttributeValuesToBuilder(const std::string &attribute_name, + const tinygltf::Accessor &accessor, + const std::vector &indices_data, + int att_id, int number_of_elements, + const Eigen::Matrix4d &transform_matrix, + BuilderT *builder); // Copies the tangent attribute data from |accessor| and adds it to a Draco // mesh. This function will transform all of the data by |transform_matrix| // and then normalize before adding the data to the Draco mesh. // |indices_data| is the indices data from the glTF file. |att_id| is the - // attribute id of the tangent attribute in the Draco mesh. |number_of_faces| - // This is the number of faces this function will process. |reverse_winding| - // if set will change the orientation of the data. - Status AddTangentToMeshBuilder(const tinygltf::Accessor &accessor, - const std::vector &indices_data, - int att_id, int number_of_faces, - const Eigen::Matrix4d &transform_matrix, - bool reverse_winding, - TriangleSoupMeshBuilder *mb); + // attribute id of the tangent attribute in the Draco mesh. + // |number_of_elements| is the number of faces or points this function will + // process. |reverse_winding| if set will change the orientation of the data. + template + Status AddTangentToBuilder(const tinygltf::Accessor &accessor, + const std::vector &indices_data, + int att_id, int number_of_elements, + const Eigen::Matrix4d &transform_matrix, + bool reverse_winding, BuilderT *builder); // Copies the texture coordinate attribute data from |accessor| and adds it to // a Draco mesh. This function will flip the data on the horizontal axis as // Draco meshes store the texture coordinates differently than glTF. // |indices_data| is the indices data from the glTF file. |att_id| is the // attribute id of the texture coordinate attribute in the Draco mesh. - // |number_of_faces| This is the number of faces this function will process. - // |reverse_winding| if set will change the orientation of the data. - Status AddTexCoordToMeshBuilder(const tinygltf::Accessor &accessor, - const std::vector &indices_data, - int att_id, int number_of_faces, - bool reverse_winding, - TriangleSoupMeshBuilder *mb); + // |number_of_elements| is the number of faces or points this function will + // process. |reverse_winding| if set will change the orientation of the data. + template + Status AddTexCoordToBuilder(const tinygltf::Accessor &accessor, + const std::vector &indices_data, + int att_id, int number_of_elements, + bool reverse_winding, BuilderT *builder); + + // Copies the mesh feature ID attribute data from |accessor| and adds it to a + // Draco mesh. |indices_data| is the indices data from the glTF file. |att_id| + // is the attribute ID of the mesh feature ID attribute in the Draco mesh. + // |number_of_elements| is the number of faces or points this function will + // process. |reverse_winding| if set will change the orientation of the data. + template + Status AddFeatureIdToBuilder(const tinygltf::Accessor &accessor, + const std::vector &indices_data, + int att_id, int number_of_elements, + bool reverse_winding, + const std::string &attribute_name, + BuilderT *builder); // Copies the attribute data from |accessor| and adds it to a Draco mesh. // This function will transform all of the data by |transform_matrix| before // adding the data to the Draco mesh. |indices_data| is the indices data // from the glTF file. |att_id| is the attribute id of the attribute in the - // Draco mesh. |number_of_faces| This is the number of faces this function - // will process. |normalize| if set will normalize all of the vector data - // after transformation. |reverse_winding| if set will change the orientation - // of the data. - Status AddTransformedDataToMeshBuilder( - const tinygltf::Accessor &accessor, - const std::vector &indices_data, int att_id, - int number_of_faces, const Eigen::Matrix4d &transform_matrix, - bool normalize, bool reverse_winding, TriangleSoupMeshBuilder *mb); + // Draco mesh. |number_of_elements| is the number of faces or points this + // function will process. |normalize| if set will normalize all of the vector + // data after transformation. |reverse_winding| if set will change the + // orientation of the data. + template + Status AddTransformedDataToBuilder(const tinygltf::Accessor &accessor, + const std::vector &indices_data, + int att_id, int number_of_elements, + const Eigen::Matrix4d &transform_matrix, + bool normalize, bool reverse_winding, + BuilderT *builder); + + // Sets values in |data| into the builder |builder| for |att_id|. + template + void SetValuesForBuilder(const std::vector &indices_data, + int att_id, int number_of_elements, + const std::vector &data, bool reverse_winding, + TriangleSoupMeshBuilder *builder); + template + void SetValuesForBuilder(const std::vector &indices_data, + int att_id, int number_of_elements, + const std::vector &data, bool reverse_winding, + PointCloudBuilder *builder); // Sets values in |data| into the mesh builder |mb| for |att_id|. // |reverse_winding| if set will change the orientation of the data. @@ -189,26 +229,43 @@ int number_of_faces, const std::vector &data, bool reverse_winding, TriangleSoupMeshBuilder *mb); + // Returns an address pointing to the content stored in |data|. This is used + // when passing values to mesh / point cloud builder when the input type can + // be either a VectorD or an arithmetic type. + template + const void *GetDataContentAddress(const T &data) const; + // Adds the attribute data in |accessor| to |mb| for unique attribute // |att_id|. |indices_data| is the mesh's indices data. |reverse_winding| if // set will change the orientation of the data. + template Status AddAttributeDataByTypes(const tinygltf::Accessor &accessor, const std::vector &indices_data, - int att_id, int number_of_faces, - bool reverse_winding, - TriangleSoupMeshBuilder *mb); + int att_id, int number_of_elements, + bool reverse_winding, BuilderT *builder); // Adds the textures to |owner|. template Status CopyTextures(T *owner); + // Sets extra attribute properties on a constructed draco mesh. + void SetAttributePropertiesOnDracoMesh(Mesh *mesh); + // Adds the materials to |mesh|. Status AddMaterialsToDracoMesh(Mesh *mesh); // Adds the material data for the GeometryAttribute::MATERIAL attribute to the // Draco mesh. + template + Status AddMaterialDataToBuilder(int material_value, int number_of_elements, + BuilderT *builder); + template + Status AddMaterialDataToBuilderInternal(T material_value, int number_of_faces, + TriangleSoupMeshBuilder *builder); template - Status AddMaterialDataToMeshBuilder(T material_value, int number_of_faces); + Status AddMaterialDataToBuilderInternal(T material_value, + int number_of_points, + PointCloudBuilder *builder); // Checks if the glTF file contains a texture. If there is a texture, this // function will read the texture data and add it to the Draco |material|. If @@ -251,17 +308,39 @@ const tinygltf::Value::Object &extension, std::vector *mappings); - // Adds an attribute of type |attribute_name| to |mb|. Returns the + // Decodes glTF mesh feature ID sets from all glTF primitives and adds them to + // |mesh|. + Status AddMeshFeaturesToDracoMesh(Mesh *mesh); + + // Decodes glTF mesh feature ID sets from glTF primitive in glTF node at + // |node_index| and adds them to |mesh|. + Status AddMeshFeaturesToDracoMesh(int node_index, Mesh *mesh); + + // Decodes glTF structural metadata from glTF model and adds it to |geometry|. + template + Status AddStructuralMetadataToGeometry(GeometryT *geometry); + + // Decodes glTF mesh feature ID sets from |primitive| and adds them to |mesh|. + Status DecodeMeshFeatures(const tinygltf::Primitive &primitive, + TextureLibrary *texture_library, Mesh *mesh); + + // Decodes glTF mesh feature ID sets from |extension| and adds them to the + // |mesh_features| vector. + Status DecodeMeshFeatures( + const tinygltf::Value::Object &extension, TextureLibrary *texture_library, + std::vector> *mesh_features); + + // Adds an attribute of type |attribute_name| to |builder|. Returns the // attribute id. + template StatusOr AddAttribute(const std::string &attribute_name, - int component_type, int type, - TriangleSoupMeshBuilder *mb); + int component_type, int type, BuilderT *builder); - // Adds an attribute of |attribute_type| to |mb|. Returns the - // attribute id. + // Adds an attribute of |attribute_type| to |builder|. Returns the attribute + // id. + template StatusOr AddAttribute(GeometryAttribute::Type attribute_type, - int component_type, int type, - TriangleSoupMeshBuilder *mb); + int component_type, int type, BuilderT *builder); // Returns true if the KHR_texture_transform extension is set in |extension|. // If the KHR_texture_transform extension is set then the values are returned @@ -307,6 +386,24 @@ const tinygltf::Value::Object &object, float *value); + // Decodes an integer value with |name| from |object| to |value| and returns + // true if a well-formed value with such |name| is present. + static StatusOr DecodeInt(const std::string &name, + const tinygltf::Value::Object &object, + int *value); + + // Decodes a string value with |name| from |object| to |value| and returns + // true if a well-formed value with such |name| is present. + static StatusOr DecodeString(const std::string &name, + const tinygltf::Value::Object &object, + std::string *value); + + // Decodes data and data target from buffer view index with |name| in |object| + // to |data| and returns true if a well-formed data is present. + StatusOr DecodePropertyTableData(const std::string &name, + const tinygltf::Value::Object &object, + PropertyTable::Property::Data *data); + // Decodes a 3D vector with |name| from |object| to |value| and returns true // if a well-formed vector with such |name| is present. static StatusOr DecodeVector3f(const std::string &name, @@ -332,6 +429,23 @@ // Adds the skins to the scene. Status AddSkinsToScene(); + // All material and non-material textures (e.g., from EXT_mesh_features) are + // initially loaded into a texture library inside the the material library. + // These methods move |non_material_textures| from material texture library + // |material_tl| to non-material texture library |non_material_tl|. + static void MoveNonMaterialTextures(Mesh *mesh); + static void MoveNonMaterialTextures(Scene *scene); + static void MoveNonMaterialTextures( + const std::unordered_set &non_material_textures, + TextureLibrary *material_tl, TextureLibrary *non_material_tl); + + // Builds and returns a mesh constructed from either mesh builder |mb| or + // point cloud builder |pb|. Mesh builder is used if |use_mesh_builder| is set + // to true. + static StatusOr> BuildMeshFromBuilder( + bool use_mesh_builder, TriangleSoupMeshBuilder *mb, + PointCloudBuilder *pb); + // Map of glTF Mesh to Draco scene mesh group. std::map gltf_mesh_to_scene_mesh_group_; @@ -343,25 +457,34 @@ // Class used to build the Draco mesh. TriangleSoupMeshBuilder mb_; + PointCloudBuilder pb_; // Next face index used when adding attribute data to the Draco mesh. int next_face_id_; + // Next point index used when adding attribute data to the point cloud. + int next_point_id_; + // Total number of indices from all the meshes and primitives. - int total_indices_count_; + int total_face_indices_count_; + int total_point_indices_count_; // This is the id of the GeometryAttribute::MATERIAL attribute added to the // Draco mesh. int material_att_id_; - // Map of glTF attribute name to attribute element counts. - std::map total_attribute_counts_; + // Data used when decoding the entire glTF asset into a single draco::Mesh. + // The struct tracks the total number of elements across all matching + // attributes and it ensures all matching attributes are compatible. + struct MeshAttributeData { + int component_type = 0; + int attribute_type = 0; + bool normalized = false; + int total_attribute_counts = 0; + }; // Map of glTF attribute name to attribute component type. - std::map attribute_component_type_; - - // Map of glTF attribute name to attribute type. - std::map attribute_type_; + std::map mesh_attribute_data_; // Map of glTF attribute name to Draco mesh attribute id. std::map attribute_name_to_draco_mesh_attribute_id_; diff -Nru draco-1.5.3+dfsg/src/draco/io/gltf_decoder_test.cc draco-1.5.5+dfsg/src/draco/io/gltf_decoder_test.cc --- draco-1.5.3+dfsg/src/draco/io/gltf_decoder_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/gltf_decoder_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -29,6 +29,7 @@ #include "draco/core/draco_test_base.h" #include "draco/core/draco_test_utils.h" #include "draco/core/draco_types.h" +#include "draco/io/gltf_test_helper.h" #include "draco/mesh/mesh_are_equivalent.h" #include "draco/mesh/mesh_utils.h" #include "draco/scene/scene_indices.h" @@ -302,6 +303,24 @@ EXPECT_EQ(mesh->num_faces(), 224) << "Unexpected number of faces."; ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 1); ASSERT_EQ(mesh->GetMaterialLibrary().GetMaterial(0)->NumTextureMaps(), 0); + ASSERT_NE(mesh->GetNamedAttribute(GeometryAttribute::COLOR), nullptr); + ASSERT_EQ(mesh->GetNamedAttribute(GeometryAttribute::COLOR)->data_type(), + draco::DT_UINT8); + // Ensure the normalized property for the color attribute is set properly. + ASSERT_TRUE(mesh->GetNamedAttribute(GeometryAttribute::COLOR)->normalized()); +} + +// Tests COLOR_0 input attribute when the asset is loaded into a scene. +TEST(GltfDecoderTest, ColorAttributeGltfScene) { + const std::string file_name = "test_pos_color.gltf"; + const std::unique_ptr scene(DecodeGltfFileToScene(file_name)); + ASSERT_EQ(scene->NumMeshes(), 1); + const Mesh &mesh = scene->GetMesh(MeshIndex(0)); + ASSERT_NE(mesh.GetNamedAttribute(GeometryAttribute::COLOR), nullptr); + ASSERT_EQ(mesh.GetNamedAttribute(GeometryAttribute::COLOR)->data_type(), + draco::DT_UINT8); + // Ensure the normalized property for the color attribute is set properly. + ASSERT_TRUE(mesh.GetNamedAttribute(GeometryAttribute::COLOR)->normalized()); } // Tests a mesh with two sets of texture coordinates. @@ -1233,5 +1252,150 @@ ASSERT_EQ(dragon_mappings[1].variants[0], 1); } +TEST(GltfDecoderTest, DecodeMeshWithMeshFeaturesWithStructuralMetadata) { + // Checks decoding of a simple glTF with mesh features and structural metadata + // property table as draco::Mesh. + constexpr bool kDracoCompressionEnabled = false; + const auto path = GetTestFileFullPath("BoxMeta/glTF/BoxMeta.gltf"); + draco::GltfDecoder decoder; + DRACO_ASSIGN_OR_ASSERT(auto mesh, decoder.DecodeFromFile(path)); + ASSERT_NE(mesh, nullptr); + GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh, kDracoCompressionEnabled); + GltfTestHelper::CheckBoxMetaStructuralMetadata(*mesh); +} + +TEST(GltfDecoderTest, DecodeMeshWithMeshFeaturesWithDracoCompression) { + // Checks decoding of a simple glTF with mesh features compressed with Draco + // as draco::Mesh. + constexpr bool kDracoCompressionEnabled = true; + const auto path = GetTestFileFullPath("BoxMetaDraco/glTF/BoxMetaDraco.gltf"); + draco::GltfDecoder decoder; + DRACO_ASSIGN_OR_ASSERT(auto mesh, decoder.DecodeFromFile(path)); + ASSERT_NE(mesh, nullptr); + GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh, kDracoCompressionEnabled); +} + +TEST(GltfDecoderTest, DecodeSceneWithMeshFeaturesWithStructuralMetadata) { + // Checks decoding of a simple glTF with mesh features and structural metadata + // property table as draco::Scene. + constexpr bool kHasDracoCompression = false; + const auto path = GetTestFileFullPath("BoxMeta/glTF/BoxMeta.gltf"); + draco::GltfDecoder decoder; + DRACO_ASSIGN_OR_ASSERT(auto scene, decoder.DecodeFromFileToScene(path)); + ASSERT_NE(scene, nullptr); + GltfTestHelper::CheckBoxMetaMeshFeatures(*scene, kHasDracoCompression); + GltfTestHelper::CheckBoxMetaStructuralMetadata(*scene); +} + +TEST(GltfDecoderTest, DecodeSceneWithMeshFeaturesWithDracoCompression) { + // Checks decoding of a simple glTF with mesh features compressed with Draco + // as draco::Scene. + constexpr bool kHasDracoCompression = true; + const auto path = GetTestFileFullPath("BoxMetaDraco/glTF/BoxMetaDraco.gltf"); + draco::GltfDecoder decoder; + DRACO_ASSIGN_OR_ASSERT(auto scene, decoder.DecodeFromFileToScene(path)); + ASSERT_NE(scene, nullptr); + GltfTestHelper::CheckBoxMetaMeshFeatures(*scene, kHasDracoCompression); +} + +TEST(GltfDecoderTest, DecodePointCloudToMesh) { + // Checks decoding of a simple glTF with point primitives (no meshes). + const auto path = GetTestFileFullPath( + "SphereTwoMaterials/sphere_two_materials_point_cloud.gltf"); + draco::GltfDecoder decoder; + DRACO_ASSIGN_OR_ASSERT(auto mesh, decoder.DecodeFromFile(path)); + ASSERT_NE(mesh, nullptr); + + // Check the point cloud has expected number of points and attributes. + ASSERT_EQ(mesh->num_faces(), 0); + ASSERT_EQ(mesh->num_points(), 462); + + ASSERT_EQ(mesh->NumNamedAttributes(draco::GeometryAttribute::NORMAL), 1); + ASSERT_EQ(mesh->NumNamedAttributes(draco::GeometryAttribute::TEX_COORD), 1); + ASSERT_EQ(mesh->NumNamedAttributes(draco::GeometryAttribute::TANGENT), 1); + ASSERT_EQ(mesh->NumNamedAttributes(draco::GeometryAttribute::MATERIAL), 1); + + // Check the point cloud has two materials. + ASSERT_EQ(mesh->GetNamedAttribute(draco::GeometryAttribute::MATERIAL)->size(), + 2); +} + +TEST(GltfDecoderTest, DecodeMeshAndPointCloudToMesh) { + // Checks decoding of a simple glTF with a mesh and point primitives into + // draco::Mesh. This should fail (draco::Mesh can't support mixed primitives). + const auto path = GetTestFileFullPath( + "SphereTwoMaterials/sphere_two_materials_mesh_and_point_cloud.gltf"); + draco::GltfDecoder decoder; + ASSERT_FALSE(decoder.DecodeFromFile(path).ok()); +} + +TEST(GltfDecoderTest, DecodePointCloudToScene) { + // Checks decoding of a simple glTF with point primitives (no meshes) into + // draco::Scene. + const auto path = GetTestFileFullPath( + "SphereTwoMaterials/sphere_two_materials_point_cloud.gltf"); + draco::GltfDecoder decoder; + DRACO_ASSIGN_OR_ASSERT(auto scene, decoder.DecodeFromFileToScene(path)); + ASSERT_NE(scene, nullptr); + + ASSERT_EQ(scene->NumMeshes(), 2); + + // Check that each point cloud has expected number of points and attributes. + for (draco::MeshIndex mi(0); mi < scene->NumMeshes(); ++mi) { + const auto &mesh = scene->GetMesh(mi); + ASSERT_EQ(mesh.num_faces(), 0); + ASSERT_EQ(mesh.num_points(), 231); + + ASSERT_EQ(mesh.NumNamedAttributes(draco::GeometryAttribute::NORMAL), 1); + ASSERT_EQ(mesh.NumNamedAttributes(draco::GeometryAttribute::TEX_COORD), 1); + ASSERT_EQ(mesh.NumNamedAttributes(draco::GeometryAttribute::TANGENT), 1); + ASSERT_EQ(mesh.NumNamedAttributes(draco::GeometryAttribute::MATERIAL), 0); + } + + // Check the materials are properly assigned to each point cloud. + const auto instances = draco::SceneUtils::ComputeAllInstances(*scene); + ASSERT_EQ(instances.size(), 2); + ASSERT_EQ(draco::SceneUtils::GetMeshInstanceMaterialIndex( + *scene, instances[draco::MeshInstanceIndex(0)]), + 0); + ASSERT_EQ(draco::SceneUtils::GetMeshInstanceMaterialIndex( + *scene, instances[draco::MeshInstanceIndex(1)]), + 1); +} + +TEST(GltfDecoderTest, DecodeMeshAndPointCloudToScene) { + // Checks decoding of a simple glTF with a mesh and point primitives into + // draco::Scene. + const auto path = GetTestFileFullPath( + "SphereTwoMaterials/sphere_two_materials_mesh_and_point_cloud.gltf"); + draco::GltfDecoder decoder; + DRACO_ASSIGN_OR_ASSERT(auto scene, decoder.DecodeFromFileToScene(path)); + ASSERT_NE(scene, nullptr); + + ASSERT_EQ(scene->NumMeshes(), 2); + + // First mesh should be a real mesh while the other one should be a point + // cloud (no faces). Otherwise, they should have the same properties. + for (draco::MeshIndex mi(0); mi < scene->NumMeshes(); ++mi) { + const auto &mesh = scene->GetMesh(mi); + ASSERT_EQ(mesh.num_faces(), mi.value() == 0 ? 224 : 0); + ASSERT_EQ(mesh.num_points(), 231); + + ASSERT_EQ(mesh.NumNamedAttributes(draco::GeometryAttribute::NORMAL), 1); + ASSERT_EQ(mesh.NumNamedAttributes(draco::GeometryAttribute::TEX_COORD), 1); + ASSERT_EQ(mesh.NumNamedAttributes(draco::GeometryAttribute::TANGENT), 1); + } +} + +TEST(GltfDecoderTest, TestLoadUnsupportedTexCoordAttributes) { + // Checks that unsupported attributes (TEXCOORD_2 ... TEXCOORD_7) are ignored + // without causing the decoder to fail. + auto scene = draco::ReadSceneFromTestFile("UnusedTexCoords/TexCoord2.gltf"); + ASSERT_NE(scene, nullptr); + ASSERT_EQ(scene->GetMesh(draco::MeshIndex(0)) + .NumNamedAttributes(draco::GeometryAttribute::TEX_COORD), + 2); +} + } // namespace draco #endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/io/gltf_encoder.cc draco-1.5.5+dfsg/src/draco/io/gltf_encoder.cc --- draco-1.5.3+dfsg/src/draco/io/gltf_encoder.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/gltf_encoder.cc 2022-10-29 00:55:03.000000000 +0000 @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include "draco/attributes/geometry_attribute.h" @@ -33,6 +35,7 @@ #include "draco/io/file_utils.h" #include "draco/io/gltf_utils.h" #include "draco/io/texture_io.h" +#include "draco/mesh/mesh_features.h" #include "draco/mesh/mesh_splitter.h" #include "draco/mesh/mesh_utils.h" #include "draco/scene/instance_array.h" @@ -79,6 +82,24 @@ } } +// Checks |att| metadata entry in |mesh| with key "attribute_name" and returns +// entry value if it begins with "_FEATURE_ID_", or an empty string otherwise. +std::string GetFeatureIdAttributeName(const PointAttribute &att, + const Mesh &mesh) { + const auto *const metadata = + mesh.GetAttributeMetadataByAttributeId(att.unique_id()); + if (metadata) { + std::string attribute_name; + if (metadata->GetEntryString("attribute_name", &attribute_name)) { + constexpr char kPrefix[] = "_FEATURE_ID_"; + if (attribute_name.rfind(kPrefix) == 0) { + return attribute_name; + } + } + } + return std::string(); +} + // Struct to hold glTF Scene data. struct GltfScene { std::vector node_indices; @@ -173,6 +194,7 @@ struct GltfBufferView { int64_t buffer_byte_offset = -1; int64_t byte_length = 0; + int target = 0; }; // Struct to hold information about a Draco compressed mesh. @@ -189,6 +211,7 @@ int mode; int material; std::vector material_variants_mappings; + std::vector mesh_features; std::map attributes; GltfDracoCompressedMesh compressed_mesh_info; }; @@ -242,8 +265,10 @@ const std::string &image_name(int i) const { return images_[i].image_name; } void set_add_images_to_buffer(bool flag) { add_images_to_buffer_ = flag; } + bool add_images_to_buffer() const { return add_images_to_buffer_; } void set_output_type(GltfEncoder::OutputType type) { output_type_ = type; } GltfEncoder::OutputType output_type() const { return output_type_; } + void set_json_output_mode(JsonWriter::Mode mode) { gltf_json_.SetMode(mode); } private: // Pad |buffer_| to 4 byte boundary. @@ -341,13 +366,14 @@ // contained in the encoded MaterialLibrary (e.g. for images that are locally // modified before they are encoded to disk). The image file name is generated // by combining |image_stem| and image mime type contained in the |texture|. - StatusOr AddImage( - const std::string &image_stem, const Texture *texture, int num_components, - std::unordered_map *texture_to_image_index_map); - StatusOr AddImage( - const std::string &image_stem, const Texture *texture, - std::unique_ptr owned_texture, int num_components, - std::unordered_map *texture_to_image_index_map); + StatusOr AddImage(const std::string &image_stem, const Texture *texture, + int num_components); + StatusOr AddImage(const std::string &image_stem, const Texture *texture, + std::unique_ptr owned_texture, + int num_components); + + // Saves an image with a given |image_index| into a buffer. + Status SaveImageToBuffer(int image_index); // Adds |sampler| to vector of samplers and returns the index. If |sampler| is // equal to default values then |sampler| is not added to the vector and @@ -392,17 +418,24 @@ // contain any mesh group instance arrays. Status AddInstanceArrays(const Scene &scene); + // Adds structural metadata from |geometry| to the asset, if any. + template + void AddStructuralMetadata(const GeometryT &geometry); + // Adds float |data| representing |num_components|-length vectors to the // encoder as accessor and return the new accessor index. StatusOr AddData(const std::vector &data, int num_components); + // Adds property table |data| as buffer view and returns buffer view index. + StatusOr AddBufferView(const PropertyTable::Property::Data &data); + bool EncodeAssetProperty(EncoderBuffer *buf_out); bool EncodeScenesProperty(EncoderBuffer *buf_out); bool EncodeInitialSceneProperty(EncoderBuffer *buf_out); bool EncodeNodesProperty(EncoderBuffer *buf_out); - bool EncodeMeshesProperty(EncoderBuffer *buf_out); - void EncodePrimitiveExtensionsProperty(const GltfPrimitive &primitive, - EncoderBuffer *buf_out); + Status EncodeMeshesProperty(EncoderBuffer *buf_out); + Status EncodePrimitiveExtensionsProperty(const GltfPrimitive &primitive, + EncoderBuffer *buf_out); Status EncodeMaterials(EncoderBuffer *buf_out); // Encodes a color material. |red|, |green|, |blue|, |alpha|, and @@ -418,42 +451,44 @@ Status EncodeTextureMap(const std::string &object_name, int image_index, int tex_coord_index, const Material &material, const TextureMap &texture_map); + + // Encodes a texture map similar to the method above. When the |object_name| + // is "texture" and |channels| is not empty, then the |channels| is encoded + // into the "channels" property as required by the "texture" object of the + // EXT_mesh_features extension. + Status EncodeTextureMap(const std::string &object_name, int image_index, + int tex_coord_index, const Material &material, + const TextureMap &texture_map, + const std::vector &channels); Status EncodeMaterialsProperty(EncoderBuffer *buf_out); - // TODO(vytyaz): Use this type in other places. - typedef std::unordered_map TextureToImageIndexMapType; void EncodeMaterialUnlitExtension(const Material &material); Status EncodeMaterialSheenExtension(const Material &material, const Material &defaults, - int material_index, - TextureToImageIndexMapType *map); + int material_index); Status EncodeMaterialTransmissionExtension(const Material &material, const Material &defaults, - int material_index, - TextureToImageIndexMapType *map); + int material_index); Status EncodeMaterialClearcoatExtension(const Material &material, const Material &defaults, - int material_index, - TextureToImageIndexMapType *map); + int material_index); Status EncodeMaterialVolumeExtension(const Material &material, const Material &defaults, - int material_index, - TextureToImageIndexMapType *map); + int material_index); Status EncodeMaterialIorExtension(const Material &material, const Material &defaults); Status EncodeMaterialSpecularExtension(const Material &material, const Material &defaults, - int material_index, - TextureToImageIndexMapType *map); + int material_index); Status EncodeTexture(const std::string &name, const std::string &stem_suffix, TextureMap::Type type, int num_components, - const Material &material, int material_index, - TextureToImageIndexMapType *map); + const Material &material, int material_index); Status EncodeAnimationsProperty(EncoderBuffer *buf_out); Status EncodeSkinsProperty(EncoderBuffer *buf_out); Status EncodeTopLevelExtensionsProperty(EncoderBuffer *buf_out); Status EncodeLightsProperty(EncoderBuffer *buf_out); Status EncodeMaterialsVariantsNamesProperty(EncoderBuffer *buf_out); + Status EncodeStructuralMetadataProperty(EncoderBuffer *buf_out); bool EncodeAccessorsProperty(EncoderBuffer *buf_out); bool EncodeBufferViewsProperty(EncoderBuffer *buf_out); bool EncodeBuffersProperty(EncoderBuffer *buf_out); @@ -526,6 +561,8 @@ std::vector images_; std::vector textures_; + std::unordered_map texture_to_image_index_map_; + std::string buffer_name_; EncoderBuffer buffer_; JsonWriter gltf_json_; @@ -560,10 +597,18 @@ std::vector> lights_; std::vector materials_variants_names_; std::vector instance_arrays_; + PropertyTable::Schema property_table_schema_; + std::vector property_tables_; // Indicates whether Draco compression is used for any of the asset meshes. bool draco_compression_used_; + // Indicates whether mesh features are used. + bool mesh_features_used_; + + // Counter for naming mesh feature textures. + int mesh_features_texture_index_; + // If set GltfAsset will add the images to |buffer_| instead of writing the // images to separate files. bool add_images_to_buffer_; @@ -575,6 +620,11 @@ std::vector texture_samplers_; GltfEncoder::OutputType output_type_; + + // Temporary storage for meshes created during the runtime of the GltfEncoder. + // We need to store them here to ensure their content doesn't get deleted + // before it is used by the encoder. + std::vector> local_meshes_; }; int GltfAsset::UnsignedIntComponentSize(unsigned int max_value) { @@ -606,6 +656,8 @@ scene_index_(-1), buffer_name_("buffer0.bin"), draco_compression_used_(false), + mesh_features_used_(false), + mesh_features_texture_index_(0), add_images_to_buffer_(false), output_type_(GltfEncoder::COMPACT) {} @@ -621,6 +673,8 @@ GltfMesh gltf_mesh; meshes_.push_back(gltf_mesh); + AddStructuralMetadata(mesh); + const int32_t material_att_id = mesh.GetNamedAttributeId(GeometryAttribute::MATERIAL); if (material_att_id == -1) { @@ -636,7 +690,7 @@ if (!split_maybe.ok()) { return false; } - const auto split_meshes = std::move(split_maybe).value(); + auto split_meshes = std::move(split_maybe).value(); for (int i = 0; i < split_meshes.size(); ++i) { if (split_meshes[i] == nullptr) { continue; // Empty mesh. Ignore. @@ -644,9 +698,18 @@ uint32_t mat_index = 0; mat_att->GetValue(AttributeValueIndex(i), &mat_index); + // Copy over mesh features for a given material index. + Mesh::CopyMeshFeaturesForMaterial(mesh, split_meshes[i].get(), mat_index); + + // Move the split mesh to a temporary storage of the GltfAsset. This will + // ensure the mesh will stay alive as long the asset needs it. We have to + // do this because the split mesh may contain mesh features data that are + // used later in the encoding process. + local_meshes_.push_back(std::move(split_meshes[i])); + // The material index in the glTF file corresponds to the index of the // split mesh. - if (!AddDracoMesh(*(split_meshes[i].get()), mat_index, {})) { + if (!AddDracoMesh(*(local_meshes_.back().get()), mat_index, {})) { return false; } } @@ -685,9 +748,7 @@ if (!EncodeNodesProperty(buf_out)) { return Status(Status::DRACO_ERROR, "Failed encoding nodes."); } - if (!EncodeMeshesProperty(buf_out)) { - return Status(Status::DRACO_ERROR, "Failed encoding meshes."); - } + DRACO_RETURN_IF_ERROR(EncodeMeshesProperty(buf_out)); DRACO_RETURN_IF_ERROR(EncodeMaterials(buf_out)); if (!EncodeAccessorsProperty(buf_out)) { return Status(Status::DRACO_ERROR, "Failed encoding accessors."); @@ -783,31 +844,47 @@ // Configure attribute quantization. for (int i = 0; i < mesh_copy->num_attributes(); ++i) { const PointAttribute *const att = mesh_copy->attribute(i); - int num_quantization_bits = -1; - switch (att->attribute_type()) { - case GeometryAttribute::POSITION: - num_quantization_bits = compression_options.quantization_bits_position; - break; - case GeometryAttribute::NORMAL: - num_quantization_bits = compression_options.quantization_bits_normal; - break; - case GeometryAttribute::TEX_COORD: - num_quantization_bits = compression_options.quantization_bits_tex_coord; - break; - case GeometryAttribute::TANGENT: - num_quantization_bits = compression_options.quantization_bits_tangent; - break; - case GeometryAttribute::WEIGHTS: - num_quantization_bits = compression_options.quantization_bits_weight; - break; - case GeometryAttribute::GENERIC: - num_quantization_bits = compression_options.quantization_bits_generic; - break; - default: - break; - } - if (num_quantization_bits > 0) { - encoder.SetAttributeQuantization(i, num_quantization_bits); + if (att->attribute_type() == GeometryAttribute::POSITION && + !compression_options.quantization_position + .AreQuantizationBitsDefined()) { + // TODO(ostava): Handle grid option. This will be implemented in a + // separate CL. + return ErrorStatus("Grid quantization not implemented yet."); + } else { + int num_quantization_bits = -1; + switch (att->attribute_type()) { + case GeometryAttribute::POSITION: + num_quantization_bits = + compression_options.quantization_position.quantization_bits(); + break; + case GeometryAttribute::NORMAL: + num_quantization_bits = compression_options.quantization_bits_normal; + break; + case GeometryAttribute::TEX_COORD: + num_quantization_bits = + compression_options.quantization_bits_tex_coord; + break; + case GeometryAttribute::TANGENT: + num_quantization_bits = compression_options.quantization_bits_tangent; + break; + case GeometryAttribute::WEIGHTS: + num_quantization_bits = compression_options.quantization_bits_weight; + break; + case GeometryAttribute::GENERIC: + if (GetFeatureIdAttributeName(*att, *mesh_copy).empty()) { + num_quantization_bits = + compression_options.quantization_bits_generic; + } else { + // Quantization is explicitly disabled for feature ID attributes. + encoder.SetAttributeQuantization(i, -1); + } + break; + default: + break; + } + if (num_quantization_bits > 0) { + encoder.SetAttributeQuantization(i, num_quantization_bits); + } } } @@ -949,6 +1026,10 @@ } primitive.material = material_id; primitive.material_variants_mappings = material_variants_mappings; + primitive.mesh_features.reserve(mesh.NumMeshFeatures()); + for (MeshFeaturesIndex i(0); i < mesh.NumMeshFeatures(); ++i) { + primitive.mesh_features.push_back(&mesh.GetMeshFeatures(i)); + } primitive.indices = indices_index; primitive.attributes.insert( std::pair("POSITION", position_index)); @@ -996,12 +1077,14 @@ AddAttributeToDracoExtension(mesh, GeometryAttribute::WEIGHTS, 0, "WEIGHTS_0", &primitive.compressed_mesh_info); } - for (const std::pair &generics_accessor : - generics_accessors) { - primitive.attributes.insert(generics_accessor); - AddAttributeToDracoExtension(mesh, GeometryAttribute::GENERIC, 0, - generics_accessor.first, - &primitive.compressed_mesh_info); + for (int att_index = 0; att_index < generics_accessors.size(); ++att_index) { + const std::string &attribute_name = generics_accessors[att_index].first; + if (!attribute_name.empty()) { + primitive.attributes.insert(generics_accessors[att_index]); + AddAttributeToDracoExtension(mesh, GeometryAttribute::GENERIC, att_index, + attribute_name, + &primitive.compressed_mesh_info); + } } meshes_.back().primitives.push_back(primitive); @@ -1193,14 +1276,17 @@ } // Adds generic attributes that have metadata describing the attribute name. -// This allows for export of application-specific attributes. -// Returns a vector of attribute-name, accessor pairs for each valid attribute. +// This allows for export of application-specific attributes and feature ID +// attributes defined in glTF extension EXT_mesh_features. Returns a vector of +// attribute-name, accessor pairs for each valid attribute. The length of the +// vector is equal to the number of generic attributes. Vector entries +// corresponding to unsupported attributes (e.g., with no metadata) contain +// empty attribute names. std::vector> GltfAsset::AddDracoGenerics( const Mesh &mesh, int num_encoded_points) { const int num_attributes = mesh.NumNamedAttributes(GeometryAttribute::GENERIC); - std::vector> attrs; - attrs.reserve(num_attributes); + std::vector> attrs(num_attributes); for (int i = 0; i < num_attributes; ++i) { const PointAttribute *const att = mesh.GetNamedAttribute(GeometryAttribute::GENERIC, i); @@ -1214,7 +1300,35 @@ int accessor = AddAttribute(*att, mesh.num_points(), num_encoded_points, mesh.IsCompressionEnabled()); - attrs.emplace_back(attr_name, accessor); + attrs[i] = {attr_name, accessor}; + } + } else { + // Try to find feature ID attribute name like "_FEATURE_ID_5" then check + // that the attribute stores scalar values of complient data types as + // defined by the EXT_mesh_features glTF extension. + attr_name = GetFeatureIdAttributeName(*att, mesh); + if (!attr_name.empty() && att->num_components() == 1) { + int accessor = -1; + switch (att->data_type()) { + case DT_UINT8: + accessor = AddAttribute(*att, mesh.num_points(), + num_encoded_points, + mesh.IsCompressionEnabled()); + break; + case DT_UINT16: + accessor = AddAttribute(*att, mesh.num_points(), + num_encoded_points, + mesh.IsCompressionEnabled()); + break; + case DT_FLOAT32: + accessor = AddAttribute(*att, mesh.num_points(), + num_encoded_points, + mesh.IsCompressionEnabled()); + break; + default: + continue; + } + attrs[i] = {attr_name, accessor}; } } } @@ -1252,19 +1366,23 @@ return true; } -StatusOr GltfAsset::AddImage( - const std::string &image_stem, const Texture *texture, int num_components, - std::unordered_map *texture_to_image_index_map) { - return AddImage(image_stem, texture, nullptr, num_components, - texture_to_image_index_map); -} - -StatusOr GltfAsset::AddImage( - const std::string &image_stem, const Texture *texture, - std::unique_ptr owned_texture, int num_components, - std::unordered_map *texture_to_image_index_map) { - const auto it = texture_to_image_index_map->find(texture); - if (it != texture_to_image_index_map->end()) { +StatusOr GltfAsset::AddImage(const std::string &image_stem, + const Texture *texture, int num_components) { + return AddImage(image_stem, texture, nullptr, num_components); +} + +StatusOr GltfAsset::AddImage(const std::string &image_stem, + const Texture *texture, + std::unique_ptr owned_texture, + int num_components) { + const auto it = texture_to_image_index_map_.find(texture); + if (it != texture_to_image_index_map_.end()) { + // We already have an image for the given |texture|. Update its number of + // components if needed. + GltfImage &image = images_[it->second]; + if (image.num_components < num_components) { + image.num_components = num_components; + } return it->second; } const std::string extension = TextureUtils::GetTargetExtension(*texture); @@ -1274,26 +1392,6 @@ image.owned_texture = std::move(owned_texture); image.num_components = num_components; - if (add_images_to_buffer_) { - std::vector buffer; - DRACO_RETURN_IF_ERROR(WriteTextureToBuffer(*texture, &buffer)); - - // Add the image data to the buffer. - const size_t buffer_start_offset = buffer_.size(); - buffer_.Encode(buffer.data(), buffer.size()); - if (!PadBuffer()) { - return Status(Status::DRACO_ERROR, "Could not pad buffer in AddImage."); - } - - // Add a buffer view pointing to the image data in the buffer. - GltfBufferView buffer_view; - buffer_view.buffer_byte_offset = buffer_start_offset; - buffer_view.byte_length = buffer_.size() - buffer_start_offset; - buffer_views_.push_back(buffer_view); - - image.buffer_view = buffer_views_.size() - 1; - } - // Always maintain the mime_type. Used elsewhere to determine image type. if (extension == "jpg") { image.mime_type = "image/jpeg"; @@ -1314,10 +1412,36 @@ } images_.push_back(std::move(image)); - (*texture_to_image_index_map)[texture] = images_.size() - 1; + texture_to_image_index_map_[texture] = images_.size() - 1; return images_.size() - 1; } +Status GltfAsset::SaveImageToBuffer(int image_index) { + GltfImage &image = images_[image_index]; + const Texture *const texture = image.texture; + const int num_components = image.num_components; + std::vector buffer; + DRACO_RETURN_IF_ERROR(WriteTextureToBuffer(*texture, &buffer)); + + // Add the image data to the buffer. + const size_t buffer_start_offset = buffer_.size(); + buffer_.Encode(buffer.data(), buffer.size()); + if (!PadBuffer()) { + return Status(Status::DRACO_ERROR, + "Could not pad buffer in SaveImageToBuffer."); + } + + // Add a buffer view pointing to the image data in the buffer. + GltfBufferView buffer_view; + buffer_view.buffer_byte_offset = buffer_start_offset; + buffer_view.byte_length = buffer_.size() - buffer_start_offset; + buffer_views_.push_back(buffer_view); + + image.buffer_view = buffer_views_.size() - 1; + return OkStatus(); +} + +// TODO(vytyaz): The return type could be int. StatusOr GltfAsset::AddTextureSampler(const TextureSampler &sampler) { // If sampler is equal to defaults do not add to vector and return -1. if (sampler.min_filter == TextureMap::UNSPECIFIED && @@ -1358,6 +1482,7 @@ DRACO_RETURN_IF_ERROR(AddLights(scene)); DRACO_RETURN_IF_ERROR(AddMaterialsVariantsNames(scene)); DRACO_RETURN_IF_ERROR(AddInstanceArrays(scene)); + AddStructuralMetadata(scene); return OkStatus(); } @@ -1413,6 +1538,12 @@ primitive.material = instance.material_index; primitive.material_variants_mappings = instance.materials_variants_mappings; + const Mesh &mesh = scene.GetMesh(instance.mesh_index); + primitive.mesh_features.clear(); + primitive.mesh_features.reserve(mesh.NumMeshFeatures()); + for (MeshFeaturesIndex j(0); j < mesh.NumMeshFeatures(); ++j) { + primitive.mesh_features.push_back(&mesh.GetMeshFeatures(j)); + } meshes_.back().primitives.push_back(primitive); } } @@ -1706,6 +1837,18 @@ return OkStatus(); } +template +void GltfAsset::AddStructuralMetadata(const GeometryT &geometry) { + const StructuralMetadata &structural_metadata = + geometry.GetStructuralMetadata(); + if (!structural_metadata.GetPropertyTableSchema().Empty()) { + property_table_schema_ = structural_metadata.GetPropertyTableSchema(); + for (int i = 0; i < structural_metadata.NumPropertyTables(); ++i) { + property_tables_.push_back(&structural_metadata.GetPropertyTable(i)); + } + } +} + StatusOr GltfAsset::AddData(const std::vector &data, int num_components) { std::string type; @@ -1765,6 +1908,21 @@ return static_cast(accessors_.size() - 1); } +StatusOr GltfAsset::AddBufferView( + const PropertyTable::Property::Data &data) { + const size_t buffer_start_offset = buffer_.size(); + buffer_.Encode(data.data.data(), data.data.size()); + if (!PadBuffer()) { + return ErrorStatus("AddBufferView: PadBuffer returned DRACO_ERROR."); + } + GltfBufferView buffer_view; + buffer_view.buffer_byte_offset = buffer_start_offset; + buffer_view.byte_length = buffer_.size() - buffer_start_offset; + buffer_view.target = data.target; + buffer_views_.push_back(buffer_view); + return static_cast(buffer_views_.size() - 1); +} + bool GltfAsset::EncodeAssetProperty(EncoderBuffer *buf_out) { gltf_json_.BeginObject("asset"); gltf_json_.OutputValue("version", version_); @@ -1909,7 +2067,8 @@ return buf_out->Encode(asset_str.data(), asset_str.length()); } -bool GltfAsset::EncodeMeshesProperty(EncoderBuffer *buf_out) { +Status GltfAsset::EncodeMeshesProperty(EncoderBuffer *buf_out) { + mesh_features_texture_index_ = 0; gltf_json_.BeginArray("meshes"); for (int i = 0; i < meshes_.size(); ++i) { @@ -1939,7 +2098,8 @@ if (primitive.material >= 0) { gltf_json_.OutputValue("material", primitive.material); } - EncodePrimitiveExtensionsProperty(primitive, buf_out); + DRACO_RETURN_IF_ERROR( + EncodePrimitiveExtensionsProperty(primitive, buf_out)); gltf_json_.EndObject(); } @@ -1952,18 +2112,23 @@ gltf_json_.EndArray(); const std::string asset_str = gltf_json_.MoveData(); - return buf_out->Encode(asset_str.data(), asset_str.length()); + if (!buf_out->Encode(asset_str.data(), asset_str.length())) { + return ErrorStatus("Failed encoding meshes."); + } + return OkStatus(); } -void GltfAsset::EncodePrimitiveExtensionsProperty( +Status GltfAsset::EncodePrimitiveExtensionsProperty( const GltfPrimitive &primitive, EncoderBuffer *buf_out) { // Return if the primitive has no extensions to encode. const bool has_draco_mesh_compression = primitive.compressed_mesh_info.buffer_view_index >= 0; const bool has_materials_variants = !primitive.material_variants_mappings.empty(); - if (!has_draco_mesh_compression && !has_materials_variants) { - return; + const bool has_mesh_features = !primitive.mesh_features.empty(); + if (!has_draco_mesh_compression && !has_materials_variants && + !has_mesh_features) { + return OkStatus(); } // Encode primitive extensions. @@ -1995,7 +2160,53 @@ gltf_json_.EndArray(); // mappings array. gltf_json_.EndObject(); // KHR_materials_variants entry. } + if (has_mesh_features) { + gltf_json_.BeginObject("EXT_mesh_features"); + gltf_json_.BeginArray("featureIds"); + for (int i = 0; i < primitive.mesh_features.size(); i++) { + const auto &features = primitive.mesh_features[i]; + gltf_json_.BeginObject(); + if (!features->GetLabel().empty()) { + gltf_json_.OutputValue("label", features->GetLabel()); + } + gltf_json_.OutputValue("featureCount", features->GetFeatureCount()); + if (features->GetAttributeIndex() != -1) { + gltf_json_.OutputValue("attribute", features->GetAttributeIndex()); + } + if (features->GetPropertyTableIndex() != -1) { + gltf_json_.OutputValue("propertyTable", + features->GetPropertyTableIndex()); + } + if (features->GetTextureMap().tex_coord_index() != -1) { + const TextureMap &texture_map = features->GetTextureMap(); + const std::string texture_stem = TextureUtils::GetOrGenerateTargetStem( + *texture_map.texture(), mesh_features_texture_index_++, + "_MeshFeatures"); + + // Save image as RGBA if the A channel is used to store feature ID. + const auto &channels = features->GetTextureChannels(); + const int num_channels = + std::count(channels.begin(), channels.end(), 3) == 1 ? 4 : 3; + DRACO_ASSIGN_OR_RETURN( + const int image_index, + AddImage(texture_stem, texture_map.texture(), num_channels)); + const int tex_coord_index = texture_map.tex_coord_index(); + Material dummy_material; + DRACO_RETURN_IF_ERROR(EncodeTextureMap("texture", image_index, + tex_coord_index, dummy_material, + texture_map, channels)); + } + if (features->GetNullFeatureId() != -1) { + gltf_json_.OutputValue("nullFeatureId", features->GetNullFeatureId()); + } + gltf_json_.EndObject(); + mesh_features_used_ = true; + } + gltf_json_.EndArray(); // featureIds array. + gltf_json_.EndObject(); // EXT_mesh_features entry. + } gltf_json_.EndObject(); // extensions entry. + return OkStatus(); } Status GltfAsset::EncodeMaterials(EncoderBuffer *buf_out) { @@ -2039,6 +2250,15 @@ int image_index, int tex_coord_index, const Material &material, const TextureMap &texture_map) { + return EncodeTextureMap(object_name, image_index, tex_coord_index, material, + texture_map, {}); +} + +Status GltfAsset::EncodeTextureMap(const std::string &object_name, + int image_index, int tex_coord_index, + const Material &material, + const TextureMap &texture_map, + const std::vector &channels) { // Create a new texture sampler (or reuse an existing one if possible). const TextureSampler sampler(texture_map.min_filter(), texture_map.mag_filter(), @@ -2069,6 +2289,16 @@ } } + // The "texture" object of the EXT_mesh_features extension has a custom + // property "channels" that is encoded here. + if (object_name == "texture" && !channels.empty()) { + gltf_json_.BeginArray("channels"); + for (const int channel : channels) { + gltf_json_.OutputValue(channel); + } + gltf_json_.EndArray(); // channels array. + } + // Check if |texture_map| is using the KHR_texture_transform extension. if (!TextureTransform::IsDefault(texture_map.texture_transform())) { gltf_json_.BeginObject("extensions"); @@ -2113,9 +2343,6 @@ } Status GltfAsset::EncodeMaterialsProperty(EncoderBuffer *buf_out) { - // Stores mapping between Draco textures and glTF image indices. - std::unordered_map texture_to_image_index_map; - gltf_json_.BeginArray("materials"); for (int i = 0; i < material_library_.NumMaterials(); ++i) { const Material *const material = material_library_.GetMaterial(i); @@ -2157,8 +2384,7 @@ *color->texture(), i, "_BaseColor"); DRACO_ASSIGN_OR_RETURN( const int color_image_index, - AddImage(texture_stem, color->texture(), rgba ? 4 : 3, - &texture_to_image_index_map)); + AddImage(texture_stem, color->texture(), rgba ? 4 : 3)); DRACO_RETURN_IF_ERROR( EncodeTextureMap("baseColorTexture", color_image_index, color->tex_coord_index(), *material, *color)); @@ -2173,8 +2399,7 @@ *metallic->texture(), i, "_OcclusionMetallicRoughness"); // Metallic and occlusion textures are already combined. DRACO_ASSIGN_OR_RETURN(occlusion_metallic_roughness_image_index, - AddImage(texture_stem, metallic->texture(), 3, - &texture_to_image_index_map)); + AddImage(texture_stem, metallic->texture(), 3)); } if (occlusion_metallic_roughness_image_index != -1) DRACO_RETURN_IF_ERROR(EncodeTextureMap( @@ -2187,8 +2412,7 @@ const std::string texture_stem = TextureUtils::GetOrGenerateTargetStem( *metallic->texture(), i, "_MetallicRoughness"); DRACO_ASSIGN_OR_RETURN(const int metallic_roughness_image_index, - AddImage(texture_stem, metallic->texture(), 3, - &texture_to_image_index_map)); + AddImage(texture_stem, metallic->texture(), 3)); DRACO_RETURN_IF_ERROR(EncodeTextureMap( "metallicRoughnessTexture", metallic_roughness_image_index, metallic->tex_coord_index(), *material, *metallic)); @@ -2203,8 +2427,7 @@ const std::string texture_stem = TextureUtils::GetOrGenerateTargetStem( *normal->texture(), i, "_Normal"); DRACO_ASSIGN_OR_RETURN(const int normal_image_index, - AddImage(texture_stem, normal->texture(), 3, - &texture_to_image_index_map)); + AddImage(texture_stem, normal->texture(), 3)); DRACO_RETURN_IF_ERROR( EncodeTextureMap("normalTexture", normal_image_index, normal->tex_coord_index(), *material, *normal)); @@ -2227,8 +2450,7 @@ *occlusion->texture(), i, suffix); DRACO_ASSIGN_OR_RETURN( const int occlusion_image_index, - AddImage(texture_stem, occlusion->texture(), num_components, - &texture_to_image_index_map)); + AddImage(texture_stem, occlusion->texture(), num_components)); DRACO_RETURN_IF_ERROR(EncodeTextureMap( "occlusionTexture", occlusion_image_index, occlusion->tex_coord_index(), *material, *occlusion)); @@ -2238,8 +2460,7 @@ const std::string texture_stem = TextureUtils::GetOrGenerateTargetStem( *emissive->texture(), i, "_Emissive"); DRACO_ASSIGN_OR_RETURN(const int emissive_image_index, - AddImage(texture_stem, emissive->texture(), 3, - &texture_to_image_index_map)); + AddImage(texture_stem, emissive->texture(), 3)); DRACO_RETURN_IF_ERROR( EncodeTextureMap("emissiveTexture", emissive_image_index, emissive->tex_coord_index(), *material, *emissive)); @@ -2283,28 +2504,28 @@ // PBR extensions can only be added to non-unlit materials. Material defaults; if (material->HasSheen()) { - DRACO_RETURN_IF_ERROR(EncodeMaterialSheenExtension( - *material, defaults, i, &texture_to_image_index_map)); + DRACO_RETURN_IF_ERROR( + EncodeMaterialSheenExtension(*material, defaults, i)); } if (material->HasTransmission()) { - DRACO_RETURN_IF_ERROR(EncodeMaterialTransmissionExtension( - *material, defaults, i, &texture_to_image_index_map)); + DRACO_RETURN_IF_ERROR( + EncodeMaterialTransmissionExtension(*material, defaults, i)); } if (material->HasClearcoat()) { - DRACO_RETURN_IF_ERROR(EncodeMaterialClearcoatExtension( - *material, defaults, i, &texture_to_image_index_map)); + DRACO_RETURN_IF_ERROR( + EncodeMaterialClearcoatExtension(*material, defaults, i)); } if (material->HasVolume()) { - DRACO_RETURN_IF_ERROR(EncodeMaterialVolumeExtension( - *material, defaults, i, &texture_to_image_index_map)); + DRACO_RETURN_IF_ERROR( + EncodeMaterialVolumeExtension(*material, defaults, i)); } if (material->HasIor()) { DRACO_RETURN_IF_ERROR( EncodeMaterialIorExtension(*material, defaults)); } if (material->HasSpecular()) { - DRACO_RETURN_IF_ERROR(EncodeMaterialSpecularExtension( - *material, defaults, i, &texture_to_image_index_map)); + DRACO_RETURN_IF_ERROR( + EncodeMaterialSpecularExtension(*material, defaults, i)); } } @@ -2375,6 +2596,9 @@ if (!images_.empty()) { gltf_json_.BeginArray("images"); for (int i = 0; i < images_.size(); ++i) { + if (add_images_to_buffer_) { + DRACO_RETURN_IF_ERROR(SaveImageToBuffer(i)); + } gltf_json_.BeginObject(); if (images_[i].buffer_view >= 0) { gltf_json_.OutputValue("bufferView", images_[i].buffer_view); @@ -2400,9 +2624,9 @@ gltf_json_.EndObject(); } -Status GltfAsset::EncodeMaterialSheenExtension( - const Material &material, const Material &defaults, int material_index, - TextureToImageIndexMapType *map) { +Status GltfAsset::EncodeMaterialSheenExtension(const Material &material, + const Material &defaults, + int material_index) { extensions_used_.insert("KHR_materials_sheen"); gltf_json_.BeginObject("KHR_materials_sheen"); @@ -2423,21 +2647,21 @@ // TODO(vytyaz): Combine sheen color and roughness images if possible. DRACO_RETURN_IF_ERROR(EncodeTexture("sheenColorTexture", "_SheenColor", TextureMap::SHEEN_COLOR, -1, material, - material_index, map)); + material_index)); // Add sheen roughness texture (A channel) if present. - DRACO_RETURN_IF_ERROR(EncodeTexture( - "sheenRoughnessTexture", "_SheenRoughness", TextureMap::SHEEN_ROUGHNESS, - 4, material, material_index, map)); + DRACO_RETURN_IF_ERROR( + EncodeTexture("sheenRoughnessTexture", "_SheenRoughness", + TextureMap::SHEEN_ROUGHNESS, 4, material, material_index)); gltf_json_.EndObject(); // KHR_materials_sheen object. return OkStatus(); } -Status GltfAsset::EncodeMaterialTransmissionExtension( - const Material &material, const Material &defaults, int material_index, - TextureToImageIndexMapType *map) { +Status GltfAsset::EncodeMaterialTransmissionExtension(const Material &material, + const Material &defaults, + int material_index) { extensions_used_.insert("KHR_materials_transmission"); gltf_json_.BeginObject("KHR_materials_transmission"); @@ -2451,16 +2675,16 @@ // TODO(vytyaz): Store texture in a grayscale format if possible. DRACO_RETURN_IF_ERROR(EncodeTexture("transmissionTexture", "_Transmission", TextureMap::TRANSMISSION, 3, material, - material_index, map)); + material_index)); gltf_json_.EndObject(); // KHR_materials_transmission object. return OkStatus(); } -Status GltfAsset::EncodeMaterialClearcoatExtension( - const Material &material, const Material &defaults, int material_index, - TextureToImageIndexMapType *map) { +Status GltfAsset::EncodeMaterialClearcoatExtension(const Material &material, + const Material &defaults, + int material_index) { extensions_used_.insert("KHR_materials_clearcoat"); gltf_json_.BeginObject("KHR_materials_clearcoat"); @@ -2481,26 +2705,26 @@ // TODO(vytyaz): Store texture in a grayscale format if possible. DRACO_RETURN_IF_ERROR(EncodeTexture("clearcoatTexture", "_Clearcoat", TextureMap::CLEARCOAT, 3, material, - material_index, map)); + material_index)); // Add clearcoat roughness texture (G channel) if present. DRACO_RETURN_IF_ERROR(EncodeTexture( "clearcoatRoughnessTexture", "_ClearcoatRoughness", - TextureMap::CLEARCOAT_ROUGHNESS, 3, material, material_index, map)); + TextureMap::CLEARCOAT_ROUGHNESS, 3, material, material_index)); // Add clearcoat normal texture (RGB channels) if present. - DRACO_RETURN_IF_ERROR(EncodeTexture( - "clearcoatNormalTexture", "_ClearcoatNormal", - TextureMap::CLEARCOAT_NORMAL, 3, material, material_index, map)); + DRACO_RETURN_IF_ERROR( + EncodeTexture("clearcoatNormalTexture", "_ClearcoatNormal", + TextureMap::CLEARCOAT_NORMAL, 3, material, material_index)); gltf_json_.EndObject(); // KHR_materials_clearcoat object. return OkStatus(); } -Status GltfAsset::EncodeMaterialVolumeExtension( - const Material &material, const Material &defaults, int material_index, - TextureToImageIndexMapType *map) { +Status GltfAsset::EncodeMaterialVolumeExtension(const Material &material, + const Material &defaults, + int material_index) { extensions_used_.insert("KHR_materials_volume"); gltf_json_.BeginObject("KHR_materials_volume"); @@ -2524,7 +2748,7 @@ // Add thickness texture (G channel) if present. DRACO_RETURN_IF_ERROR(EncodeTexture("thicknessTexture", "_Thickness", TextureMap::THICKNESS, 3, material, - material_index, map)); + material_index)); gltf_json_.EndObject(); // KHR_materials_volume object. @@ -2546,9 +2770,9 @@ return OkStatus(); } -Status GltfAsset::EncodeMaterialSpecularExtension( - const Material &material, const Material &defaults, int material_index, - TextureToImageIndexMapType *map) { +Status GltfAsset::EncodeMaterialSpecularExtension(const Material &material, + const Material &defaults, + int material_index) { extensions_used_.insert("KHR_materials_specular"); gltf_json_.BeginObject("KHR_materials_specular"); @@ -2567,12 +2791,12 @@ // TODO(vytyaz): Combine specular and specular color images if possible. DRACO_RETURN_IF_ERROR(EncodeTexture("specularTexture", "_Specular", TextureMap::SPECULAR, 4, material, - material_index, map)); + material_index)); // Add specular color texture (RGB channels) if present. DRACO_RETURN_IF_ERROR(EncodeTexture("specularColorTexture", "_SpecularColor", TextureMap::SPECULAR_COLOR, -1, material, - material_index, map)); + material_index)); gltf_json_.EndObject(); // KHR_materials_specular object. @@ -2582,8 +2806,7 @@ Status GltfAsset::EncodeTexture(const std::string &name, const std::string &stem_suffix, TextureMap::Type type, int num_components, - const Material &material, int material_index, - TextureToImageIndexMapType *map) { + const Material &material, int material_index) { const TextureMap *const texture_map = material.GetTextureMapByType(type); if (texture_map) { if (num_components == -1) { @@ -2594,7 +2817,7 @@ *texture_map->texture(), material_index, stem_suffix); DRACO_ASSIGN_OR_RETURN( const int image_index, - AddImage(texture_stem, texture_map->texture(), num_components, map)); + AddImage(texture_stem, texture_map->texture(), num_components)); DRACO_RETURN_IF_ERROR(EncodeTextureMap(name, image_index, texture_map->tex_coord_index(), material, *texture_map)); @@ -2694,7 +2917,8 @@ Status GltfAsset::EncodeTopLevelExtensionsProperty(EncoderBuffer *buf_out) { // Return if there are no top-level asset extensions to encode. - if (lights_.empty() && materials_variants_names_.empty()) { + if (lights_.empty() && materials_variants_names_.empty() && + property_tables_.empty()) { return OkStatus(); } @@ -2702,6 +2926,7 @@ gltf_json_.BeginObject("extensions"); DRACO_RETURN_IF_ERROR(EncodeLightsProperty(buf_out)); DRACO_RETURN_IF_ERROR(EncodeMaterialsVariantsNamesProperty(buf_out)); + DRACO_RETURN_IF_ERROR(EncodeStructuralMetadataProperty(buf_out)); gltf_json_.EndObject(); // extensions entry. return OkStatus(); } @@ -2777,6 +3002,102 @@ return OkStatus(); } +Status GltfAsset::EncodeStructuralMetadataProperty(EncoderBuffer *buf_out) { + if (property_table_schema_.Empty()) { + return OkStatus(); + } + + gltf_json_.BeginObject("EXT_structural_metadata"); + + // Encodes property table schema. + struct SchemaWriter { + static void Write(const PropertyTable::Schema::Object &object, + JsonWriter *json_writer) { + switch (object.GetType()) { + case PropertyTable::Schema::Object::OBJECT: + json_writer->BeginObject(object.GetName()); + for (const PropertyTable::Schema::Object &obj : object.GetObjects()) { + Write(obj, json_writer); + } + json_writer->EndObject(); + break; + case PropertyTable::Schema::Object::ARRAY: + json_writer->BeginArray(object.GetName()); + for (const PropertyTable::Schema::Object &obj : object.GetArray()) { + Write(obj, json_writer); + } + json_writer->EndArray(); + break; + case PropertyTable::Schema::Object::STRING: + json_writer->OutputValue(object.GetName(), object.GetString()); + break; + case PropertyTable::Schema::Object::INTEGER: + json_writer->OutputValue(object.GetName(), object.GetInteger()); + break; + case PropertyTable::Schema::Object::BOOLEAN: + json_writer->OutputValue(object.GetName(), object.GetBoolean()); + break; + } + } + }; + + // Encode property table schema. + SchemaWriter::Write(property_table_schema_.json, &gltf_json_); + + // Encode all property tables. + gltf_json_.BeginArray("propertyTables"); + for (const PropertyTable *const table : property_tables_) { + gltf_json_.BeginObject(); + if (!table->GetName().empty()) { + gltf_json_.OutputValue("name", table->GetName()); + } + if (!table->GetClass().empty()) { + gltf_json_.OutputValue("class", table->GetClass()); + } + gltf_json_.OutputValue("count", table->GetCount()); + + // Encoder all property table properties. + gltf_json_.BeginObject("properties"); + for (int i = 0; i < table->NumProperties(); ++i) { + const PropertyTable::Property &property = table->GetProperty(i); + gltf_json_.BeginObject(property.GetName()); + + // Encode property values. + DRACO_ASSIGN_OR_RETURN(const int buffer_view_index, + AddBufferView(property.GetData())); + gltf_json_.OutputValue("values", buffer_view_index); + + // Encode offsets for variable-length arrays. + if (!property.GetArrayOffsets().data.data.empty()) { + if (!property.GetArrayOffsets().type.empty()) { + gltf_json_.OutputValue("arrayOffsetType", + property.GetArrayOffsets().type); + } + DRACO_ASSIGN_OR_RETURN(const int buffer_view_index, + AddBufferView(property.GetArrayOffsets().data)); + gltf_json_.OutputValue("arrayOffsets", buffer_view_index); + } + + // Encode offsets for strings. + if (!property.GetStringOffsets().data.data.empty()) { + if (!property.GetStringOffsets().type.empty()) { + gltf_json_.OutputValue("stringOffsetType", + property.GetStringOffsets().type); + } + DRACO_ASSIGN_OR_RETURN(const int buffer_view_index, + AddBufferView(property.GetStringOffsets().data)); + gltf_json_.OutputValue("stringOffsets", buffer_view_index); + } + gltf_json_.EndObject(); // Named property entry. + } + gltf_json_.EndObject(); // properties entry. + gltf_json_.EndObject(); + } + gltf_json_.EndArray(); // propertyTables entry. + gltf_json_.EndObject(); // EXT_structural_metadata entry. + return OkStatus(); +} + bool GltfAsset::EncodeAccessorsProperty(EncoderBuffer *buf_out) { gltf_json_.BeginArray("accessors"); @@ -2831,6 +3152,9 @@ gltf_json_.OutputValue("buffer", 0); gltf_json_.OutputValue("byteOffset", buffer_views_[i].buffer_byte_offset); gltf_json_.OutputValue("byteLength", buffer_views_[i].byte_length); + if (buffer_views_[i].target != 0) { + gltf_json_.OutputValue("target", buffer_views_[i].target); + } gltf_json_.EndObject(); } @@ -2874,6 +3198,12 @@ extensions_used_.insert("EXT_mesh_gpu_instancing"); extensions_required_.insert("EXT_mesh_gpu_instancing"); } + if (mesh_features_used_) { + extensions_used_.insert("EXT_mesh_features"); + } + if (!property_table_schema_.Empty()) { + extensions_used_.insert("EXT_structural_metadata"); + } if (!extensions_required_.empty()) { gltf_json_.BeginArray("extensionsRequired"); @@ -3142,6 +3472,7 @@ Status GltfEncoder::EncodeToBuffer(const Mesh &mesh, GltfAsset *gltf_asset, EncoderBuffer *out_buffer) { out_buffer_ = out_buffer; + SetJsonWriterMode(gltf_asset); if (!gltf_asset->AddDracoMesh(mesh)) { return Status(Status::DRACO_ERROR, "Error adding Draco mesh."); } @@ -3151,10 +3482,20 @@ Status GltfEncoder::EncodeToBuffer(const Scene &scene, GltfAsset *gltf_asset, EncoderBuffer *out_buffer) { out_buffer_ = out_buffer; + SetJsonWriterMode(gltf_asset); DRACO_RETURN_IF_ERROR(gltf_asset->AddScene(scene)); return gltf_asset->Output(out_buffer); } +void GltfEncoder::SetJsonWriterMode(class GltfAsset *gltf_asset) { + if (gltf_asset->output_type() == COMPACT && + gltf_asset->add_images_to_buffer()) { + gltf_asset->set_json_output_mode(JsonWriter::COMPACT); + } else { + gltf_asset->set_json_output_mode(JsonWriter::READABLE); + } +} + Status GltfEncoder::WriteGltfFiles(const GltfAsset &gltf_asset, const EncoderBuffer &buffer, const std::string &filename, diff -Nru draco-1.5.3+dfsg/src/draco/io/gltf_encoder.h draco-1.5.5+dfsg/src/draco/io/gltf_encoder.h --- draco-1.5.3+dfsg/src/draco/io/gltf_encoder.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/gltf_encoder.h 2022-10-29 00:55:03.000000000 +0000 @@ -38,7 +38,8 @@ public: // Types of output modes for the glTF data encoder. |COMPACT| will output // required and non-default glTF data. |VERBOSE| will output required and - // default glTF data. + // default glTF data as well as readable JSON even when the output is saved in + // a glTF-Binary file. enum OutputType { COMPACT, VERBOSE }; GltfEncoder(); @@ -95,6 +96,10 @@ Status EncodeToBuffer(const Scene &scene, class GltfAsset *gltf_asset, EncoderBuffer *out_buffer); + // Sets appropriate Json writer mode based on the provided |gltf_asset| + // options. + static void SetJsonWriterMode(class GltfAsset *gltf_asset); + // Writes the ".gltf" and associted files. |gltf_asset| holds the glTF data. // |buffer| is the encoded glTF json data. |filename| is the name of the // ".gltf" file. |bin_filename| is the name of the glTF bin file. The other diff -Nru draco-1.5.3+dfsg/src/draco/io/gltf_encoder_test.cc draco-1.5.5+dfsg/src/draco/io/gltf_encoder_test.cc --- draco-1.5.3+dfsg/src/draco/io/gltf_encoder_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/gltf_encoder_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -26,6 +26,7 @@ #include "draco/io/file_reader_interface.h" #include "draco/io/file_utils.h" #include "draco/io/gltf_decoder.h" +#include "draco/io/gltf_test_helper.h" #include "draco/io/parser_utils.h" #include "draco/io/texture_io.h" #include "draco/material/material_utils.h" @@ -308,6 +309,17 @@ ASSERT_NE(*mesh_gltf, nullptr); } + // Encode |mesh| to a temporary glTF file. Then decode the glTF file as a + // scene and return it in |scene_gltf|. + void MeshToDecodedGltfScene(const Mesh &mesh, + std::unique_ptr *scene_gltf) { + const std::string gltf_file_full_path = + draco::GetTestTempFileFullPath("test.gltf"); + EncodeMeshToFile(mesh, gltf_file_full_path); + *scene_gltf = std::move(ReadSceneFromFile(gltf_file_full_path)).value(); + ASSERT_NE(*scene_gltf, nullptr); + } + // Encode |scene| to a temporary glTF file. Then decode the glTF file and // return the scene in |scene_gltf|. void SceneToDecodedGltfScene(const Scene &scene, @@ -697,7 +709,7 @@ const size_t default_bin_size = draco::GetFileSize(gltf_bin_filename); // Test applying more quantization will make the compressed size smaller. - options.quantization_bits_position = 6; + options.quantization_position.SetQuantizationBits(6); options.quantization_bits_normal = 6; options.quantization_bits_tex_coord = 6; SceneUtils::SetDracoCompressionOptions(&options, scene.get()); @@ -723,7 +735,7 @@ draco::GetFileSize(gltf_bin_filename); ASSERT_LT(more_weight_quantization_bin_size, more_quantization_bin_size); - options.quantization_bits_position = 20; + options.quantization_position.SetQuantizationBits(20); options.quantization_bits_normal = 20; options.quantization_bits_tex_coord = 20; options.quantization_bits_weight = 20; @@ -794,7 +806,7 @@ // Test setting more position quantization then the default makes the // compressed size smaller. - options.quantization_bits_position = 6; + options.quantization_position.SetQuantizationBits(6); SceneUtils::SetDracoCompressionOptions(&options, scene.get()); ASSERT_TRUE(gltf_encoder.EncodeToFile(*scene, gltf_file_full_path, folder_path)) @@ -1489,6 +1501,157 @@ EncodeSceneToGltfAndCompare(scene.get()); } +// Tests encoding of draco::Scene to glTF with various mesh feature ID sets and +// structural metadata property table. +TEST_F(GltfEncoderTest, EncodeSceneWithMeshFeaturesWithStructuralMetadata) { + const std::string file_name = "BoxMeta/glTF/BoxMeta.gltf"; + constexpr bool kHasMeshFeatures = true; + constexpr bool kHasStructuralMetadata = true; + constexpr bool kHasDracoCompression = false; + + // Read test file from file. + const std::unique_ptr scene(DecodeTestGltfFileToScene(file_name)); + ASSERT_NE(scene, nullptr); + + // Encode the scene to glTF and decode it back to draco::Scene and check. + std::unique_ptr scene_from_gltf; + SceneToDecodedGltfScene(*scene, &scene_from_gltf); + ASSERT_NE(scene_from_gltf, nullptr); + GltfTestHelper::CheckBoxMetaMeshFeatures(*scene_from_gltf, + kHasDracoCompression); + GltfTestHelper::CheckBoxMetaStructuralMetadata(*scene_from_gltf); +} + +// Tests encoding of draco::Scene with Draco compression to glTF with various +// mesh feature ID sets. +TEST_F(GltfEncoderTest, EncodeSceneWithMeshFeaturesWithDracoCompression) { + const std::string file_name = "BoxMetaDraco/glTF/BoxMetaDraco.gltf"; + constexpr bool kHasMeshFeatures = true; + constexpr bool kHasStructuralMetadata = false; + constexpr bool kHasDracoCompression = true; + + // Read test file from file. + const std::unique_ptr scene(DecodeTestGltfFileToScene(file_name)); + ASSERT_NE(scene, nullptr); + + // Encode the scene to glTF and decode it back to draco::Scene and check. + std::unique_ptr scene_from_gltf; + SceneToDecodedGltfScene(*scene, &scene_from_gltf); + ASSERT_NE(scene_from_gltf, nullptr); + GltfTestHelper::CheckBoxMetaMeshFeatures(*scene_from_gltf, + kHasDracoCompression); +} + +// Tests encoding of draco::Mesh to glTF with various mesh feature ID sets and +// structural metadata property table. +TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithStructuralMetadata) { + const std::string file_name = "BoxMeta/glTF/BoxMeta.gltf"; + constexpr bool kHasDracoCompression = false; + + // Read test file from file. + const std::unique_ptr mesh(ReadMeshFromTestFile(file_name)); + ASSERT_NE(mesh, nullptr); + + // Encode the scene to glTF and decode it back to draco::Mesh and check. + std::unique_ptr mesh_from_gltf; + MeshToDecodedGltfMesh(*mesh, &mesh_from_gltf); + ASSERT_NE(mesh_from_gltf, nullptr); + GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh_from_gltf, + kHasDracoCompression); + GltfTestHelper::CheckBoxMetaStructuralMetadata(*mesh_from_gltf); +} + +// Tests encoding of draco::Mesh with Draco compression to glTF with various +// mesh feature ID sets. +TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithDracoCompression) { + constexpr bool kHasDracoCompression = true; + const std::string file_name = "BoxMetaDraco/glTF/BoxMetaDraco.gltf"; + + // Read test file from file. + const std::unique_ptr mesh(ReadMeshFromTestFile(file_name)); + ASSERT_NE(mesh, nullptr); + + // Encode the scene to glTF and decode it back to draco::Mesh and check. + std::unique_ptr mesh_from_gltf; + MeshToDecodedGltfMesh(*mesh, &mesh_from_gltf); + ASSERT_NE(mesh_from_gltf, nullptr); + GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh_from_gltf, + kHasDracoCompression); +} + +// Tests encoding of draco::Mesh with mesh features associated with different +// mesh primitives. +TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithMultiplePrimitives) { + const std::string file_name = "BoxesMeta/glTF/BoxesMeta.gltf"; + + // Read test file from file. + const std::unique_ptr mesh(ReadMeshFromTestFile(file_name)); + ASSERT_NE(mesh, nullptr); + // All mesh features should share two textures. + ASSERT_EQ(mesh->GetNonMaterialTextureLibrary().NumTextures(), 2); + + // Encode the scene to glTF and decode it back to draco::Mesh and check. + std::unique_ptr mesh_from_gltf; + MeshToDecodedGltfMesh(*mesh, &mesh_from_gltf); + ASSERT_NE(mesh_from_gltf, nullptr); + + ASSERT_EQ(mesh_from_gltf->GetMaterialLibrary().NumMaterials(), 2); + ASSERT_EQ(mesh_from_gltf->NumMeshFeatures(), 5); + + // First two mesh features should be used by material 0 and the reamining by + // material 1. + for (draco::MeshFeaturesIndex mfi(0); mfi < 5; ++mfi) { + // Each mesh feature should be used by a single material. + ASSERT_EQ(mesh_from_gltf->NumMeshFeaturesMaterialMasks(mfi), 1); + if (mfi.value() < 2) { + ASSERT_EQ(mesh_from_gltf->GetMeshFeaturesMaterialMask(mfi, 0), 0); + } else { + ASSERT_EQ(mesh_from_gltf->GetMeshFeaturesMaterialMask(mfi, 0), 1); + } + } + // All mesh features should share two textures. + ASSERT_EQ(mesh_from_gltf->GetNonMaterialTextureLibrary().NumTextures(), 2); + + // Ensure it still works correctly when we re-encode the source |mesh| as a + // scene. + std::unique_ptr scene_from_gltf; + MeshToDecodedGltfScene(*mesh, &scene_from_gltf); + ASSERT_NE(scene_from_gltf, nullptr); + + ASSERT_EQ(scene_from_gltf->NumMeshes(), 2); + + // First mesh should have 2 mesh features and the other one 3 mesh features. + ASSERT_EQ(scene_from_gltf->GetMesh(draco::MeshIndex(0)).NumMeshFeatures(), 2); + ASSERT_EQ(scene_from_gltf->GetMesh(draco::MeshIndex(1)).NumMeshFeatures(), 3); + + // All mesh features should share two textures. + ASSERT_EQ(scene_from_gltf->GetNonMaterialTextureLibrary().NumTextures(), 2); +} + +// Tests encoding of draco::Mesh containing a point cloud and two materials. +TEST_F(GltfEncoderTest, EncodePointCloudWithMaterials) { + const std::string file_name = + "SphereTwoMaterials/sphere_two_materials_point_cloud.gltf"; + + // Read test file from file. + const std::unique_ptr mesh(ReadMeshFromTestFile(file_name)); + ASSERT_NE(mesh, nullptr); + + // Input should have no faces. + ASSERT_EQ(mesh->num_faces(), 0); + + // There should be two materials + ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 2); + + // Encode the mesh to glTF and decode it back to draco::Mesh and check. + std::unique_ptr mesh_from_gltf; + MeshToDecodedGltfMesh(*mesh, &mesh_from_gltf); + ASSERT_NE(mesh_from_gltf, nullptr); + + ASSERT_EQ(mesh_from_gltf->num_faces(), 0); + ASSERT_EQ(mesh_from_gltf->GetMaterialLibrary().NumMaterials(), 2); +} + } // namespace draco #endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/io/gltf_test_helper.cc draco-1.5.5+dfsg/src/draco/io/gltf_test_helper.cc --- draco-1.5.3+dfsg/src/draco/io/gltf_test_helper.cc 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/gltf_test_helper.cc 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,823 @@ +// Copyright 2022 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "draco/io/gltf_test_helper.h" + +#include +#include +#include +#include +#include + +#include "draco/core/draco_test_base.h" +#include "draco/core/draco_test_utils.h" +#include "draco/metadata/property_table.h" +#include "draco/texture/texture_library.h" + +namespace draco { + +#ifdef DRACO_TRANSCODER_SUPPORTED + +void GltfTestHelper::AddBoxMetaMeshFeatures(Scene *scene) { + // Check the scene. + ASSERT_NE(scene, nullptr); + ASSERT_EQ(scene->NumMeshes(), 1); + TextureLibrary &texture_library = scene->GetNonMaterialTextureLibrary(); + ASSERT_EQ(texture_library.NumTextures(), 0); + + // Check the mesh. + Mesh &mesh = scene->GetMesh(MeshIndex(0)); + ASSERT_EQ(mesh.num_faces(), 12); + ASSERT_EQ(mesh.num_attributes(), 2); + ASSERT_EQ(mesh.num_points(), 24); + + // Get mesh element counts. + const int num_faces = mesh.num_faces(); + const int num_corners = 3 * mesh.num_faces(); + const int num_vertices = + mesh.GetNamedAttribute(GeometryAttribute::POSITION)->size(); + + // Add feature ID set with per-face Uint8 attribute named _FEATURE_ID_0. + { + // Create feature ID attribute. + constexpr DataType kType = DataType::DT_UINT8; + std::unique_ptr pa(new PointAttribute()); + pa->Init(GeometryAttribute::GENERIC, 1, kType, false, mesh.num_faces()); + for (AttributeValueIndex avi(0); avi < num_faces; ++avi) { + const int8_t val = avi.value(); + pa->SetAttributeValue(avi, &val); + } + const int att_id = mesh.AddPerFaceAttribute(std::move(pa)); + std::unique_ptr metadata(new AttributeMetadata()); + metadata->AddEntryString("attribute_name", "_FEATURE_ID_0"); + mesh.AddAttributeMetadata(att_id, std::move(metadata)); + + // Add feature ID set to the mesh. + std::unique_ptr features(new MeshFeatures()); + features->SetLabel("faces"); + features->SetFeatureCount(num_faces); + features->SetNullFeatureId(100); + features->SetPropertyTableIndex(0); + features->SetAttributeIndex(0); + mesh.AddMeshFeatures(std::move(features)); + } + + // Add feature ID set with per-vertex Uint16 attribute named _FEATURE_ID_1. + { + // Create feature ID attribute. + constexpr DataType kType = DataType::DT_UINT16; + std::unique_ptr pa(new PointAttribute()); + pa->Init(GeometryAttribute::GENERIC, 1, kType, false, num_vertices); + for (AttributeValueIndex avi(0); avi < num_vertices; ++avi) { + const uint16_t val = avi.value(); + pa->SetAttributeValue(avi, &val); + } + const int att_id = mesh.AddPerVertexAttribute(std::move(pa)); + std::unique_ptr metadata(new AttributeMetadata()); + metadata->AddEntryString("attribute_name", "_FEATURE_ID_1"); + mesh.AddAttributeMetadata(att_id, std::move(metadata)); + + // Add feature ID set to the mesh. + std::unique_ptr features(new MeshFeatures()); + features->SetLabel("vertices"); + features->SetFeatureCount(num_vertices); + features->SetNullFeatureId(101); + features->SetPropertyTableIndex(1); + features->SetAttributeIndex(1); + mesh.AddMeshFeatures(std::move(features)); + } + + // Add feature ID set with per-corner Float attribute named _FEATURE_ID_2. + { + // Create feature ID attribute. + constexpr DataType kType = DataType::DT_FLOAT32; + std::unique_ptr pa(new PointAttribute()); + pa->Init(GeometryAttribute::GENERIC, 1, kType, false, num_corners); + IndexTypeVector corner_to_value( + num_corners); + for (AttributeValueIndex avi(0); avi < num_corners; ++avi) { + const float val = avi.value(); + pa->SetAttributeValue(avi, &val); + corner_to_value[CornerIndex(avi.value())] = avi; + } + const int att_id = + mesh.AddAttributeWithConnectivity(std::move(pa), corner_to_value); + std::unique_ptr metadata(new AttributeMetadata()); + metadata->AddEntryString("attribute_name", "_FEATURE_ID_2"); + mesh.AddAttributeMetadata(att_id, std::move(metadata)); + + // Add feature ID set to the mesh. + std::unique_ptr features(new MeshFeatures()); + features->SetFeatureCount(num_corners); + features->SetAttributeIndex(2); + mesh.AddMeshFeatures(std::move(features)); + } + + // Add feature ID set with the IDs stored in the R texture channel and + // accessible via the first texture coordinate attribute. + { + // Add the first texture coordinate attribute. + constexpr DataType kType = DataType::DT_FLOAT32; + std::unique_ptr pa(new PointAttribute()); + pa->Init(GeometryAttribute::TEX_COORD, 2, kType, false, num_vertices); + std::vector> uv = { + {0.0000f, 0.0000f}, {0.0000f, 0.5000f}, {0.0000f, 1.0000f}, + {0.5000f, 0.0000f}, {0.5000f, 0.5000f}, {0.5000f, 1.0000f}, + {1.0000f, 0.0000f}, {1.0000f, 0.5000f}}; + for (AttributeValueIndex avi(0); avi < num_vertices; ++avi) { + const int index = avi.value(); + pa->SetAttributeValue(avi, uv[index].data()); + } + mesh.AddPerVertexAttribute(std::move(pa)); + } + + // Add feature ID set with the IDs stored in the GBA texture channels and + // accessible via the second texture coordinate attribute. + { + // Add the second texture coordinate attribute. + constexpr DataType kType = DataType::DT_FLOAT32; + std::unique_ptr pa(new PointAttribute()); + pa->Init(GeometryAttribute::TEX_COORD, 2, kType, false, num_vertices); + std::vector> uv = { + {0.0000f, 0.0000f}, {0.0000f, 0.5000f}, {0.0000f, 1.0000f}, + {0.5000f, 0.0000f}, {0.5000f, 0.5000f}, {0.5000f, 1.0000f}, + {1.0000f, 0.0000f}, {1.0000f, 0.5000f}}; + for (AttributeValueIndex avi(0); avi < num_vertices; ++avi) { + const int index = avi.value(); + pa->SetAttributeValue(avi, uv[index].data()); + } + mesh.AddPerVertexAttribute(std::move(pa)); + ASSERT_EQ(mesh.NumNamedAttributes(GeometryAttribute::TEX_COORD), 2); + } +} + +void GltfTestHelper::AddBoxMetaStructuralMetadata(Scene *scene) { + // Add structural metadata property table schema in the following JSON: + // "schema": { + // "id": "galaxy", + // "classes": { + // "planet": { + // "properties": { + // "color": { + // "componentType": "UINT8", + // "description": "The RGB color.", + // "required": true, + // "type": "VEC3" + // }, + // "name": { + // "description": "The name.", + // "required": true, + // "type": "STRING" + // } + // "sequence": { + // "description": "The number sequence.", + // "required": false, + // "type": "SCALAR" + // } + // } + // } + // }, + // "enums": { + // "classifications": { + // "description": "Classifications of planets.", + // "name": "classifications", + // "values": [ + // { "name": "Unspecified", "value": 0 }, + // { "name": "Gas Giant", "value": 1 }, + // { "name": "Waterworld", "value": 2 }, + // { "name": "Agriworld", "value": 3 }, + // { "name": "Ordnance", "value": 4 } + // ] + // } + // } + // } + typedef PropertyTable::Schema::Object Object; + PropertyTable::Schema schema; + Object &json = schema.json; + json.SetObjects().emplace_back("id", "galaxy"); + json.SetObjects().emplace_back("classes"); + json.SetObjects().back().SetObjects().emplace_back("planet"); + Object &planet = json.SetObjects().back().SetObjects().back(); + planet.SetObjects().emplace_back("properties"); + Object &properties = planet.SetObjects().back(); + + properties.SetObjects().emplace_back("color"); + Object &color = properties.SetObjects().back(); + color.SetObjects().emplace_back("componentType", "UINT8"); + color.SetObjects().emplace_back("description", "The RGB color."); + color.SetObjects().emplace_back("required", true); + color.SetObjects().emplace_back("type", "VEC3"); + + properties.SetObjects().emplace_back("name"); + Object &name = properties.SetObjects().back(); + name.SetObjects().emplace_back("description", "The name."); + name.SetObjects().emplace_back("required", true); + name.SetObjects().emplace_back("type", "STRING"); + + properties.SetObjects().emplace_back("sequence"); + Object &sequence = properties.SetObjects().back(); + sequence.SetObjects().emplace_back("description", "The number sequence."); + sequence.SetObjects().emplace_back("required", false); + sequence.SetObjects().emplace_back("type", "SCALAR"); + + json.SetObjects().emplace_back("enums"); + json.SetObjects().back().SetObjects().emplace_back("classifications"); + Object &classifications = json.SetObjects().back().SetObjects().back(); + classifications.SetObjects().emplace_back("description", + "Classifications of planets."); + classifications.SetObjects().emplace_back("name", "classifications"); + classifications.SetObjects().emplace_back("values"); + Object &values = classifications.SetObjects().back(); + + values.SetArray().emplace_back(); + values.SetArray().back().SetObjects().emplace_back("name", "Unspecified"); + values.SetArray().back().SetObjects().emplace_back("value", 0); + + values.SetArray().emplace_back(); + values.SetArray().back().SetObjects().emplace_back("name", "Gas Giant"); + values.SetArray().back().SetObjects().emplace_back("value", 1); + + values.SetArray().emplace_back(); + values.SetArray().back().SetObjects().emplace_back("name", "Waterworld"); + values.SetArray().back().SetObjects().emplace_back("value", 2); + + values.SetArray().emplace_back(); + values.SetArray().back().SetObjects().emplace_back("name", "Agriworld"); + values.SetArray().back().SetObjects().emplace_back("value", 3); + + values.SetArray().emplace_back(); + values.SetArray().back().SetObjects().emplace_back("name", "Ordnance"); + values.SetArray().back().SetObjects().emplace_back("value", 4); + + // Add property table schema to the scene. + scene->GetStructuralMetadata().SetPropertyTableSchema(schema); + + // Add structural metadata property table. + std::unique_ptr table(new PropertyTable()); + table->SetName("Galaxy far far away."); + table->SetClass("planet"); + table->SetCount(16); + + // Add property describing RGB color components of the planet class. + { + std::unique_ptr property( + new PropertyTable::Property()); + property->SetName("color"); + property->GetData().target = 34962; // ARRAY_BUFFER. + property->GetData().data = {94, 94, 194, // Tatooine + 94, 145, 161, // Corusant + 118, 171, 91, // Naboo + 103, 139, 178, // Alderaan + 83, 98, 154, // Dagobah + 91, 177, 175, // Mandalore + 190, 92, 108, // Corellia + 72, 69, 169, // Kamino + 154, 90, 101, // Kashyyyk + 174, 85, 175, // Dantooine + 184, 129, 96, // Hoth + 185, 91, 180, // Mustafar + 194, 150, 83, // Bespin + 204, 111, 134, // Yavin + 182, 90, 89, // Geonosis + 0, 0, 0}; // UNLABELED + table->AddProperty(std::move(property)); + } + + // Add property that describes names of the planet class. + { + std::unique_ptr property( + new PropertyTable::Property()); + property->SetName("name"); + property->GetData().target = 34963; // ELEMENT_ARRAY_BUFFER. + const std::string data = + "named_class:Tatooine" + "named_class:Corusant" + "named_class:Naboo" + "named_class:Alderaan" + "named_class:Dagobah" + "named_class:Mandalore" + "named_class:Corellia" + "named_class:Kamino" + "named_class:Kashyyyk" + "named_class:Dantooine" + "named_class:Hoth" + "named_class:Mustafar" + "named_class:Bespin" + "named_class:Yavin" + "named_class:Geonosis" + "UNLABELED"; + property->GetData().data.assign(data.begin(), data.end()); + property->GetStringOffsets().type = "UINT32"; + property->GetStringOffsets().data.target = 34963; // ELEMENT_ARRAY_BUFFER. + property->GetStringOffsets().data.data = {0, 0, 0, 0, // Tatooine + 20, 0, 0, 0, // Corusant + 40, 0, 0, 0, // Naboo + 57, 0, 0, 0, // Alderaan + 77, 0, 0, 0, // Dagobah + 96, 0, 0, 0, // Mandalore + 117, 0, 0, 0, // Corellia + 137, 0, 0, 0, // Kamino + 155, 0, 0, 0, // Kashyyyk + 175, 0, 0, 0, // Dantooine + 196, 0, 0, 0, // Hoth + 212, 0, 0, 0, // Mustafar + 232, 0, 0, 0, // Bespin + 250, 0, 0, 0, // Yavin + 12, 1, 0, 0, // Geonosis + 32, 1, 0, 0, // UNLABELED + 41, 1, 0, 0}; + table->AddProperty(std::move(property)); + } + + // Add property that contains variable-length number sequence of the planet + // class. + { + std::unique_ptr property( + new PropertyTable::Property()); + property->SetName("sequence"); + property->GetData().target = 34963; // ELEMENT_ARRAY_BUFFER. + const std::vector data = { + 0.5f, 1.5f, 2.5f, 3.5f, 4.5f, 5.5f, // Tatooine + 6.5f, 7.5f, // Corusant + 8.5f, // Naboo + 9.5f, // Alderaan + 10.5f, 11.5f, // Dagobah + 12.5f, 13.5f, 14.5f, 15.5f, // Mandalore + 16.5f, 17.5f, // Corellia + 18.5f, 19.5f, // Kamino + 20.5f, 21.5f, 22.5f, // Kashyyyk + 23.5f, 24.5f, 25.5f, // Dantooine + 26.5f, 27.5f, // Hoth + 28.5f, 29.5f, // Mustafar + 30.5f, 31.5f, 32.5f, // Bespin + 33.5f, 34.5f, 35.5f, // Yavin + 36.5f, 37.5f, 38.5f, 39.5f, 40.5f // Geonosis + }; // UNLABELED (empty array). + property->GetData().data.resize(4 * data.size()); + memcpy(property->GetData().data.data(), data.data(), 4 * data.size()); + property->GetArrayOffsets().type = "UINT8"; + property->GetArrayOffsets().data.target = 34963; // ELEMENT_ARRAY_BUFFER. + property->GetArrayOffsets().data.data = { + 0 * 4, // Tatooine + 6 * 4, // Corusant + 8 * 4, // Naboo + 9 * 4, // Alderaan + 10 * 4, // Dagobah + 12 * 4, // Mandalore + 16 * 4, // Corellia + 18 * 4, // Kamino + 20 * 4, // Kashyyyk + 23 * 4, // Dantooine + 26 * 4, // Hoth + 28 * 4, // Mustafar + 30 * 4, // Bespin + 33 * 4, // Yavin + 36 * 4, // Geonosis + 41 * 4, // UNLABELED (empty array). + 41 * 4}; + table->AddProperty(std::move(property)); + } + + // Add property table to the scene. + scene->GetStructuralMetadata().AddPropertyTable(std::move(table)); +} + +template <> +void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &geometry, + bool has_draco_compression) { + CheckBoxMetaMeshFeatures(geometry, geometry.GetNonMaterialTextureLibrary(), + has_draco_compression); +} + +template <> +void GltfTestHelper::CheckBoxMetaMeshFeatures(const Scene &geometry, + bool has_draco_compression) { + ASSERT_EQ(geometry.NumMeshes(), 1); + CheckBoxMetaMeshFeatures(geometry.GetMesh(MeshIndex(0)), + geometry.GetNonMaterialTextureLibrary(), + has_draco_compression); +} + +void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh, + const TextureLibrary &texture_lib, + bool has_draco_compression) { + // Check texture library. + ASSERT_EQ(texture_lib.NumTextures(), 2); + + // Check basic mesh properties. + ASSERT_EQ(mesh.NumMeshFeatures(), 5); + ASSERT_EQ(mesh.num_faces(), 12); + ASSERT_EQ(mesh.num_attributes(), 7); + ASSERT_EQ(mesh.num_points(), 36); + ASSERT_EQ(mesh.NumNamedAttributes(GeometryAttribute::GENERIC), 3); + ASSERT_EQ(mesh.NumNamedAttributes(GeometryAttribute::TEX_COORD), 2); + + // Get mesh element counts. + const int num_faces = mesh.num_faces(); + const int num_corners = 3 * mesh.num_faces(); + const int num_vertices = + mesh.GetNamedAttribute(GeometryAttribute::POSITION)->size(); + + // Check mesh feature ID set at index 0. + { + // Check mesh features. + const MeshFeatures &features = mesh.GetMeshFeatures(MeshFeaturesIndex(0)); + ASSERT_EQ(features.GetLabel(), "faces"); + ASSERT_EQ(features.GetFeatureCount(), num_faces); + ASSERT_EQ(features.GetNullFeatureId(), 100); + ASSERT_EQ(features.GetPropertyTableIndex(), 0); + ASSERT_EQ(features.GetAttributeIndex(), 0); + ASSERT_TRUE(features.GetTextureChannels().empty()); + ASSERT_EQ(features.GetTextureMap().texture(), nullptr); + ASSERT_EQ(features.GetTextureMap().tex_coord_index(), -1); + + // Check per-face Uint8 attribute named _FEATURE_ID_0. + const int att_id = + mesh.GetAttributeIdByMetadataEntry("attribute_name", "_FEATURE_ID_0"); + auto att = mesh.GetAttributeByUniqueId(att_id); + ASSERT_NE(att, nullptr); + ASSERT_EQ(att->attribute_type(), GeometryAttribute::GENERIC); + ASSERT_EQ(att->data_type(), DataType::DT_UINT8); + ASSERT_EQ(att->num_components(), 1); + ASSERT_EQ(att->size(), num_faces); + ASSERT_EQ(att->indices_map_size(), num_corners); + + // Check that the values are all the numbers from 0 to 12. + const std::vector expected_values = + has_draco_compression + ? std::vector{7, 11, 10, 3, 2, 5, 4, 1, 6, 9, 8, 0} + : std::vector{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + for (int i = 0; i < num_faces; i++) { + uint8_t val; + att->GetValue(AttributeValueIndex(i), &val); + ASSERT_EQ(val, expected_values[i]); + } + + // Check that the corners of each face have a common value. + for (int i = 0; i < num_faces; i++) { + const auto face = mesh.face(FaceIndex(i)); + ASSERT_EQ(*att->GetAddressOfMappedIndex(face[0]), + *att->GetAddressOfMappedIndex(face[1])); + ASSERT_EQ(*att->GetAddressOfMappedIndex(face[0]), + *att->GetAddressOfMappedIndex(face[2])); + } + } + + // Check the 2nd mesh feature ID set at index 1. + { + // Check mesh features. + const MeshFeatures &features = mesh.GetMeshFeatures(MeshFeaturesIndex(1)); + ASSERT_EQ(features.GetLabel(), "vertices"); + ASSERT_EQ(features.GetFeatureCount(), num_vertices); + ASSERT_EQ(features.GetNullFeatureId(), 101); + ASSERT_EQ(features.GetPropertyTableIndex(), 1); + ASSERT_EQ(features.GetAttributeIndex(), 1); + ASSERT_TRUE(features.GetTextureChannels().empty()); + ASSERT_EQ(features.GetTextureMap().texture(), nullptr); + ASSERT_EQ(features.GetTextureMap().tex_coord_index(), -1); + + // Check per-vertex Uint16 attribute named _FEATURE_ID_1. + const int att_id = + mesh.GetAttributeIdByMetadataEntry("attribute_name", "_FEATURE_ID_1"); + auto att = mesh.GetAttributeByUniqueId(att_id); + ASSERT_NE(att, nullptr); + ASSERT_EQ(att->attribute_type(), GeometryAttribute::GENERIC); + ASSERT_EQ(att->data_type(), DataType::DT_UINT16); + ASSERT_EQ(att->num_components(), 1); + ASSERT_EQ(att->size(), num_vertices); + ASSERT_EQ(att->indices_map_size(), num_corners); + + // Check that the values are all the numbers from 0 to 7. + const std::vector expected_values = + has_draco_compression ? std::vector{3, 6, 7, 4, 5, 0, 1, 2} + : std::vector{0, 1, 2, 3, 4, 5, 6, 7}; + for (int i = 0; i < num_vertices; i++) { + uint16_t val; + att->GetValue(AttributeValueIndex(i), &val); + ASSERT_EQ(val, expected_values[i]); + } + + // Check that the corners of a face have unique values. + for (int i = 0; i < num_faces; i++) { + const auto face = mesh.face(FaceIndex(i)); + ASSERT_NE(*att->GetAddressOfMappedIndex(face[0]), + *att->GetAddressOfMappedIndex(face[1])); + ASSERT_NE(*att->GetAddressOfMappedIndex(face[1]), + *att->GetAddressOfMappedIndex(face[2])); + ASSERT_NE(*att->GetAddressOfMappedIndex(face[2]), + *att->GetAddressOfMappedIndex(face[0])); + } + } + + // Check the 3rd mesh feature ID set at index 2. + { + // Check mesh features. + const MeshFeatures &features = mesh.GetMeshFeatures(MeshFeaturesIndex(2)); + ASSERT_TRUE(features.GetLabel().empty()); + ASSERT_EQ(features.GetFeatureCount(), num_corners); + ASSERT_EQ(features.GetNullFeatureId(), -1); + ASSERT_EQ(features.GetPropertyTableIndex(), -1); + ASSERT_EQ(features.GetAttributeIndex(), 2); + ASSERT_TRUE(features.GetTextureChannels().empty()); + ASSERT_EQ(features.GetTextureMap().texture(), nullptr); + ASSERT_EQ(features.GetTextureMap().tex_coord_index(), -1); + + // Check per-corner Float attribute named _FEATURE_ID_2. + const int att_id = + mesh.GetAttributeIdByMetadataEntry("attribute_name", "_FEATURE_ID_2"); + auto att = mesh.GetAttributeByUniqueId(att_id); + ASSERT_NE(att, nullptr); + ASSERT_EQ(att->attribute_type(), GeometryAttribute::GENERIC); + ASSERT_EQ(att->data_type(), DataType::DT_FLOAT32); + ASSERT_EQ(att->num_components(), 1); + ASSERT_EQ(att->size(), num_corners); + ASSERT_EQ(att->indices_map_size(), 0); + ASSERT_TRUE(att->is_mapping_identity()); + + // Check that the values are from 0 to 35. + const std::vector expected_values = + has_draco_compression + ? std::vector{23, 21, 22, 33, 34, 35, 31, 32, 30, 9, 10, 11, + 7, 8, 6, 15, 16, 17, 14, 12, 13, 5, 3, 4, + 19, 20, 18, 27, 28, 29, 26, 24, 25, 1, 2, 0} + : std::vector{0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35}; + for (int i = 0; i < num_corners; i++) { + float val; + att->GetValue(AttributeValueIndex(i), &val); + ASSERT_EQ(val, expected_values[i]); + } + + // Check that the corners have unique values. + for (int i = 0; i < num_faces; i++) { + const auto face = mesh.face(FaceIndex(i)); + float v0, v1, v2; + att->GetMappedValue(face[0], &v0); + att->GetMappedValue(face[1], &v1); + att->GetMappedValue(face[2], &v2); + ASSERT_EQ(v0, expected_values[3 * i + 0]); + ASSERT_EQ(v1, expected_values[3 * i + 1]); + ASSERT_EQ(v2, expected_values[3 * i + 2]); + } + } + + // Check mesh feature ID set at index 3. + { + // Check mesh features. + const MeshFeatures &features = mesh.GetMeshFeatures(MeshFeaturesIndex(3)); + ASSERT_TRUE(features.GetLabel().empty()); + ASSERT_EQ(features.GetFeatureCount(), 6); + ASSERT_EQ(features.GetNullFeatureId(), -1); + ASSERT_EQ(features.GetPropertyTableIndex(), -1); + ASSERT_EQ(features.GetAttributeIndex(), -1); + } + + // Check mesh feature ID set at index 4. + { + // Check mesh features. + const MeshFeatures &features = mesh.GetMeshFeatures(MeshFeaturesIndex(4)); + ASSERT_EQ(features.GetLabel(), "water"); + ASSERT_EQ(features.GetFeatureCount(), 2); + ASSERT_EQ(features.GetNullFeatureId(), -1); + ASSERT_EQ(features.GetPropertyTableIndex(), -1); + ASSERT_EQ(features.GetAttributeIndex(), -1); + } +} + +void GltfTestHelper::CheckBoxMetaStructuralMetadata( + const StructuralMetadata &structural_metadata) { + // Check property table schema. + { + const PropertyTable::Schema &schema = + structural_metadata.GetPropertyTableSchema(); + ASSERT_FALSE(schema.Empty()); + const PropertyTable::Schema::Object &json = schema.json; + ASSERT_EQ(json.GetObjects().size(), 3); + ASSERT_EQ(json.GetObjects()[0].GetName(), "classes"); + ASSERT_EQ(json.GetObjects()[0].GetObjects().size(), 1); + ASSERT_EQ(json.GetObjects()[0].GetObjects()[0].GetName(), "planet"); + ASSERT_EQ(json.GetObjects()[0].GetObjects()[0].GetObjects().size(), 1); + + const auto &properties = + json.GetObjects()[0].GetObjects()[0].GetObjects()[0]; + ASSERT_EQ(properties.GetName(), "properties"); + ASSERT_EQ(properties.GetObjects().size(), 3); + + const auto &color = properties.GetObjects()[0]; + ASSERT_EQ(color.GetName(), "color"); + ASSERT_EQ(color.GetObjects().size(), 4); + ASSERT_EQ(color.GetObjects()[0].GetName(), "componentType"); + ASSERT_EQ(color.GetObjects()[1].GetName(), "description"); + ASSERT_EQ(color.GetObjects()[2].GetName(), "required"); + ASSERT_EQ(color.GetObjects()[3].GetName(), "type"); + ASSERT_EQ(color.GetObjects()[0].GetString(), "UINT8"); + ASSERT_EQ(color.GetObjects()[1].GetString(), "The RGB color."); + ASSERT_TRUE(color.GetObjects()[2].GetBoolean()); + ASSERT_EQ(color.GetObjects()[3].GetString(), "VEC3"); + + const auto &name = properties.GetObjects()[1]; + ASSERT_EQ(name.GetName(), "name"); + ASSERT_EQ(name.GetObjects().size(), 3); + ASSERT_EQ(name.GetObjects()[0].GetName(), "description"); + ASSERT_EQ(name.GetObjects()[1].GetName(), "required"); + ASSERT_EQ(name.GetObjects()[2].GetName(), "type"); + ASSERT_EQ(name.GetObjects()[0].GetString(), "The name."); + ASSERT_TRUE(name.GetObjects()[1].GetBoolean()); + ASSERT_EQ(name.GetObjects()[2].GetString(), "STRING"); + + const auto &sequence = properties.GetObjects()[2]; + ASSERT_EQ(sequence.GetName(), "sequence"); + ASSERT_EQ(sequence.GetObjects().size(), 3); + ASSERT_EQ(sequence.GetObjects()[0].GetName(), "description"); + ASSERT_EQ(sequence.GetObjects()[1].GetName(), "required"); + ASSERT_EQ(sequence.GetObjects()[2].GetName(), "type"); + ASSERT_EQ(sequence.GetObjects()[0].GetString(), "The number sequence."); + ASSERT_FALSE(sequence.GetObjects()[1].GetBoolean()); + ASSERT_EQ(sequence.GetObjects()[2].GetString(), "SCALAR"); + + ASSERT_EQ(json.GetObjects()[1].GetName(), "enums"); + const auto &classifications = json.GetObjects()[1].GetObjects()[0]; + ASSERT_EQ(classifications.GetName(), "classifications"); + ASSERT_EQ(classifications.GetObjects()[0].GetName(), "description"); + ASSERT_EQ(classifications.GetObjects()[0].GetString(), + "Classifications of planets."); + ASSERT_EQ(classifications.GetObjects()[1].GetName(), "name"); + ASSERT_EQ(classifications.GetObjects()[1].GetString(), "classifications"); + ASSERT_EQ(classifications.GetObjects()[2].GetName(), "values"); + const auto &values = classifications.GetObjects()[2]; + ASSERT_EQ(values.GetArray()[0].GetObjects()[0].GetName(), "name"); + ASSERT_EQ(values.GetArray()[1].GetObjects()[0].GetName(), "name"); + ASSERT_EQ(values.GetArray()[2].GetObjects()[0].GetName(), "name"); + ASSERT_EQ(values.GetArray()[3].GetObjects()[0].GetName(), "name"); + ASSERT_EQ(values.GetArray()[4].GetObjects()[0].GetName(), "name"); + ASSERT_EQ(values.GetArray()[0].GetObjects()[0].GetString(), "Unspecified"); + ASSERT_EQ(values.GetArray()[1].GetObjects()[0].GetString(), "Gas Giant"); + ASSERT_EQ(values.GetArray()[2].GetObjects()[0].GetString(), "Waterworld"); + ASSERT_EQ(values.GetArray()[3].GetObjects()[0].GetString(), "Agriworld"); + ASSERT_EQ(values.GetArray()[4].GetObjects()[0].GetString(), "Ordnance"); + ASSERT_EQ(values.GetArray()[0].GetObjects()[1].GetName(), "value"); + ASSERT_EQ(values.GetArray()[1].GetObjects()[1].GetName(), "value"); + ASSERT_EQ(values.GetArray()[2].GetObjects()[1].GetName(), "value"); + ASSERT_EQ(values.GetArray()[3].GetObjects()[1].GetName(), "value"); + ASSERT_EQ(values.GetArray()[4].GetObjects()[1].GetName(), "value"); + ASSERT_EQ(values.GetArray()[0].GetObjects()[1].GetInteger(), 0); + ASSERT_EQ(values.GetArray()[1].GetObjects()[1].GetInteger(), 1); + ASSERT_EQ(values.GetArray()[2].GetObjects()[1].GetInteger(), 2); + ASSERT_EQ(values.GetArray()[3].GetObjects()[1].GetInteger(), 3); + ASSERT_EQ(values.GetArray()[4].GetObjects()[1].GetInteger(), 4); + + ASSERT_EQ(json.GetObjects()[2].GetName(), "id"); + ASSERT_EQ(json.GetObjects()[2].GetString(), "galaxy"); + } + + // Check property table. + constexpr int kRows = 16; + ASSERT_EQ(structural_metadata.NumPropertyTables(), 1); + const PropertyTable &table = structural_metadata.GetPropertyTable(0); + ASSERT_EQ(table.GetName(), "Galaxy far far away."); + ASSERT_EQ(table.GetClass(), "planet"); + ASSERT_EQ(table.GetCount(), kRows); + ASSERT_EQ(table.NumProperties(), 3); + + // Check property that describes RGB color components of the planet class. + { + const PropertyTable::Property &property = table.GetProperty(0); + ASSERT_EQ(property.GetName(), "color"); + + ASSERT_EQ(property.GetData().data.size(), kRows * 3); // RGB components. + ASSERT_EQ(property.GetData().target, 34962); // ARRAY_BUFFER. + + ASSERT_EQ(property.GetData().data[0], 94); // Tatooine [94, 94, 194]. + ASSERT_EQ(property.GetData().data[1], 94); + ASSERT_EQ(property.GetData().data[2], 194); + ASSERT_EQ(property.GetData().data[18], 190); // Corellia [190, 92, 108]. + ASSERT_EQ(property.GetData().data[19], 92); + ASSERT_EQ(property.GetData().data[20], 108); + ASSERT_EQ(property.GetData().data[45], 0); // UNLABELED [0, 0, 0]. + ASSERT_EQ(property.GetData().data[46], 0); + ASSERT_EQ(property.GetData().data[47], 0); + + ASSERT_TRUE(property.GetArrayOffsets().type.empty()); + ASSERT_TRUE(property.GetArrayOffsets().data.data.empty()); + ASSERT_EQ(property.GetArrayOffsets().data.target, 0); + ASSERT_TRUE(property.GetStringOffsets().type.empty()); + ASSERT_TRUE(property.GetStringOffsets().data.data.empty()); + ASSERT_EQ(property.GetStringOffsets().data.target, 0); + } + + // Check property that describes names of the planet class. + { + const PropertyTable::Property &property = table.GetProperty(1); + ASSERT_EQ(property.GetName(), "name"); + const std::vector &data = property.GetData().data; + const std::vector &offsets = property.GetStringOffsets().data.data; + + ASSERT_EQ(data.size(), 296); // Concatenated label strings. + ASSERT_EQ(property.GetData().target, 34963); // ELEMENT_ARRAY_BUFFER. + + ASSERT_EQ(property.GetStringOffsets().type, "UINT32"); + ASSERT_EQ(offsets.size(), 4 * (kRows + 1)); + ASSERT_EQ(property.GetStringOffsets().data.target, 34963); + + ASSERT_EQ(offsets[0], 0); // Tatooine 0. + ASSERT_EQ(offsets[1], 0); + ASSERT_EQ(offsets[2], 0); + ASSERT_EQ(offsets[3], 0); + ASSERT_EQ(offsets[60], 32); // UNLABELED 287. + ASSERT_EQ(offsets[61], 1); + ASSERT_EQ(offsets[62], 0); + ASSERT_EQ(offsets[63], 0); + ASSERT_EQ(offsets[64], 41); // Beyond UNLABELED 296. + ASSERT_EQ(offsets[65], 1); + ASSERT_EQ(offsets[66], 0); + ASSERT_EQ(offsets[67], 0); + + struct Name { + static std::string Extract(const std::vector &data, + const std::vector &offsets, int row) { + const int b = offsets[4 * (row + 0)] + 255 * offsets[4 * (row + 0) + 1]; + const int e = offsets[4 * (row + 1)] + 255 * offsets[4 * (row + 1) + 1]; + return std::string(data.begin() + b, data.begin() + e); + } + }; + + // Check that the names can be extracted from the data. + ASSERT_EQ(Name::Extract(data, offsets, 0), "named_class:Tatooine"); + ASSERT_EQ(Name::Extract(data, offsets, 6), "named_class:Corellia"); + ASSERT_EQ(Name::Extract(data, offsets, 15), "UNLABELED"); + + ASSERT_TRUE(property.GetArrayOffsets().type.empty()); + ASSERT_TRUE(property.GetArrayOffsets().data.data.empty()); + ASSERT_EQ(property.GetArrayOffsets().data.target, 0); + } + + // Check property that describes number sequence of the planet class. + { + const PropertyTable::Property &property = table.GetProperty(2); + ASSERT_EQ(property.GetName(), "sequence"); + const std::vector &data = property.GetData().data; + const std::vector &offsets = property.GetArrayOffsets().data.data; + + ASSERT_EQ(data.size(), 41 * 4); // Concatenated float arrays. + ASSERT_EQ(property.GetData().target, 34963); // ELEMENT_ARRAY_BUFFER. + + ASSERT_EQ(property.GetArrayOffsets().type, "UINT8"); + ASSERT_EQ(offsets.size(), 20); // kRows + 1 + padding. + ASSERT_EQ(property.GetArrayOffsets().data.target, 34963); + + ASSERT_EQ(offsets[0], 0 * 4); // Tatooine + ASSERT_EQ(offsets[1], 6 * 4); // Corusant + ASSERT_EQ(offsets[6], 16 * 4); // Corellia + ASSERT_EQ(offsets[14], 36 * 4); // Geonosis + ASSERT_EQ(offsets[15], 41 * 4); // UNLABELED (empty array). + ASSERT_EQ(offsets[16], 41 * 4); // Beyond UNLABELED (empty array). + + struct Sequence { + static std::vector Extract(const std::vector &data, + const std::vector &offsets, + int row) { + const int n = (offsets[row + 1] - offsets[row]) / 4; + std::vector result; + result.reserve(n); + for (int i = 0; i < n; ++i) { + const void *const pointer = &data[offsets[row] + 4 * i]; + result.push_back(*static_cast(pointer)); + } + return result; + } + }; + + // Check that the number sequence arrays can be extracted from the data. + ASSERT_EQ( + Sequence::Extract(data, offsets, 0), + (std::vector{0.5f, 1.5f, 2.5f, 3.5f, 4.5f, 5.5f})); // Tatooine + ASSERT_EQ(Sequence::Extract(data, offsets, 1), + (std::vector{6.5f, 7.5f})); // Corusant + ASSERT_EQ( + Sequence::Extract(data, offsets, 14), + (std::vector{36.5f, 37.5f, 38.5f, 39.5f, 40.5f})); // Geonosis + ASSERT_TRUE(Sequence::Extract(data, offsets, 15) + .empty()); // UNLABELED (empty array). + + ASSERT_TRUE(property.GetStringOffsets().type.empty()); + ASSERT_TRUE(property.GetStringOffsets().data.data.empty()); + ASSERT_EQ(property.GetStringOffsets().data.target, 0); + } +} + +#endif // DRACO_TRANSCODER_SUPPORTED + +} // namespace draco diff -Nru draco-1.5.3+dfsg/src/draco/io/gltf_test_helper.h draco-1.5.5+dfsg/src/draco/io/gltf_test_helper.h --- draco-1.5.3+dfsg/src/draco/io/gltf_test_helper.h 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/gltf_test_helper.h 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,61 @@ +// Copyright 2022 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef DRACO_IO_GLTF_DECODER_TEST_HELPER_H_ +#define DRACO_IO_GLTF_DECODER_TEST_HELPER_H_ + +#include "draco/draco_features.h" + +#ifdef DRACO_TRANSCODER_SUPPORTED +#include "draco/scene/scene.h" + +namespace draco { + +// Helper class for testing Draco glTF encoder and decoder. +class GltfTestHelper { + public: + // Adds various mesh feature ID sets (via attributes and via textures) and + // structural metadata property table and property table schema to the box + // |scene| loaded from the test file testdata/Box/glTF/Box.gltf. + static void AddBoxMetaMeshFeatures(Scene *scene); + static void AddBoxMetaStructuralMetadata(Scene *scene); + + // Checks the box |geometry| (draco::Mesh or draco::Scene) with mesh features + // loaded from one of these test files, with or without Draco compression: + // 1. testdata/BoxMeta/glTF/BoxMeta.gltf + // 2. testdata/BoxMetaDraco/glTF/BoxMetaDraco.gltf + template + static void CheckBoxMetaMeshFeatures(const GeometryT &geometry, + bool has_draco_compression); + + // Checks the box |geometry| (draco::Mesh or draco::Scene) with structural + // metadata that includes property table and property table schema loaded from + // test file testdata/BoxMeta/glTF/BoxMeta.gltf. + template + static void CheckBoxMetaStructuralMetadata(const GeometryT &geometry) { + CheckBoxMetaStructuralMetadata(geometry.GetStructuralMetadata()); + } + + private: + static void CheckBoxMetaMeshFeatures(const Mesh &mesh, + const TextureLibrary &texture_lib, + bool has_draco_compression); + static void CheckBoxMetaStructuralMetadata( + const StructuralMetadata &structural_metadata); +}; + +} // namespace draco + +#endif // DRACO_TRANSCODER_SUPPORTED +#endif // DRACO_IO_GLTF_DECODER_TEST_HELPER_H_ diff -Nru draco-1.5.3+dfsg/src/draco/io/gltf_utils.cc draco-1.5.5+dfsg/src/draco/io/gltf_utils.cc --- draco-1.5.3+dfsg/src/draco/io/gltf_utils.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/gltf_utils.cc 2022-10-29 00:55:03.000000000 +0000 @@ -14,6 +14,9 @@ // #include "draco/io/gltf_utils.h" +#include +#include + #ifdef DRACO_TRANSCODER_SUPPORTED namespace draco { @@ -36,6 +39,22 @@ return os << indent.indent_; } +std::ostream &operator<<(std::ostream &os, + const JsonWriter::IndentWrapper &indent) { + if (indent.writer.mode_ == JsonWriter::READABLE) { + os << indent.writer.indent_writer_; + } + return os; +} + +std::ostream &operator<<(std::ostream &os, + const JsonWriter::Separator &separator) { + if (separator.writer.mode_ == JsonWriter::READABLE) { + os << " "; + } + return os; +} + void JsonWriter::Reset() { last_type_ = START; o_.clear(); @@ -48,27 +67,27 @@ FinishPreviousLine(BEGIN); o_ << indent_; if (!name.empty()) { - o_ << "\"" << name << "\": "; + o_ << "\"" << name << "\":" << separator_; } o_ << "{"; - indent_.Increase(); + indent_writer_.Increase(); } void JsonWriter::EndObject() { FinishPreviousLine(END); - indent_.Decrease(); + indent_writer_.Decrease(); o_ << indent_ << "}"; } void JsonWriter::BeginArray(const std::string &name) { FinishPreviousLine(BEGIN); - o_ << indent_ << "\"" << name << "\": ["; - indent_.Increase(); + o_ << indent_ << "\"" << name << "\":" << separator_ << "["; + indent_writer_.Increase(); } void JsonWriter::EndArray() { FinishPreviousLine(END); - indent_.Decrease(); + indent_writer_.Decrease(); o_ << indent_ << "]"; } @@ -80,7 +99,9 @@ (last_type_ == END && curr_type == VALUE)) { o_ << ","; } - o_ << std::endl; + if (mode_ == READABLE) { + o_ << std::endl; + } } last_type_ = curr_type; } diff -Nru draco-1.5.3+dfsg/src/draco/io/gltf_utils.h draco-1.5.5+dfsg/src/draco/io/gltf_utils.h --- draco-1.5.3+dfsg/src/draco/io/gltf_utils.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/gltf_utils.h 2022-10-29 00:55:03.000000000 +0000 @@ -75,8 +75,11 @@ class JsonWriter { public: enum OutputType { START, BEGIN, END, VALUE }; + enum Mode { READABLE, COMPACT }; - JsonWriter() : last_type_(START) {} + JsonWriter() + : last_type_(START), mode_(READABLE), indent_(*this), separator_(*this) {} + void SetMode(Mode mode) { mode_ = mode; } // Clear the stringstream and set last type to START. void Reset(); @@ -108,23 +111,25 @@ void OutputValue(const std::string &name, const std::string &value) { FinishPreviousLine(VALUE); - o_ << indent_ << "\"" << name << "\": \"" << value << "\""; + o_ << indent_ << "\"" << name << "\":" << separator_ << "\"" << value + << "\""; } void OutputValue(const std::string &name, const char *value) { FinishPreviousLine(VALUE); - o_ << indent_ << "\"" << name << "\": \"" << value << "\""; + o_ << indent_ << "\"" << name << "\":" << separator_ << "\"" << value + << "\""; } template void OutputValue(const std::string &name, const T &value) { FinishPreviousLine(VALUE); - o_ << indent_ << "\"" << name << "\": " << value; + o_ << indent_ << "\"" << name << "\":" << separator_ << value; } void OutputValue(const std::string &name, const bool &value) { FinishPreviousLine(VALUE); - o_ << indent_ << "\"" << name << "\": " << ToString(value); + o_ << indent_ << "\"" << name << "\":" << separator_ << ToString(value); } // Return the current output and then clear the stringstream. @@ -137,9 +142,27 @@ // Returns string representation of a Boolean |value|. static std::string ToString(bool value) { return value ? "true" : "false"; } + // Helper struct used for conditional indent writing to the output stream. + struct IndentWrapper { + explicit IndentWrapper(const JsonWriter &writer) : writer(writer) {} + const JsonWriter &writer; + }; + friend std::ostream &operator<<(std::ostream &os, + const IndentWrapper &indent); + + // Helper struct used for conditional separator writing to the output stream. + struct Separator { + explicit Separator(const JsonWriter &writer) : writer(writer) {} + const JsonWriter &writer; + }; + friend std::ostream &operator<<(std::ostream &os, const Separator &separator); + std::stringstream o_; - Indent indent_; + Indent indent_writer_; OutputType last_type_; + Mode mode_; + IndentWrapper indent_; + Separator separator_; }; } // namespace draco diff -Nru draco-1.5.3+dfsg/src/draco/io/gltf_utils_test.cc draco-1.5.5+dfsg/src/draco/io/gltf_utils_test.cc --- draco-1.5.3+dfsg/src/draco/io/gltf_utils_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/gltf_utils_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -241,6 +241,85 @@ CompareGolden(&json_writer, "0.10000000149011612,\n1"); } +TEST_F(GltfUtilsTest, TestObjectsCompact) { + JsonWriter json_writer; + json_writer.SetMode(JsonWriter::COMPACT); + json_writer.BeginObject(); + json_writer.EndObject(); + CompareGolden(&json_writer, "{}"); + + json_writer.Reset(); + json_writer.BeginObject("object"); + json_writer.EndObject(); + CompareGolden(&json_writer, "\"object\":{}"); + + json_writer.Reset(); + json_writer.BeginObject("object"); + json_writer.OutputValue(0); + json_writer.EndObject(); + CompareGolden(&json_writer, "\"object\":{0}"); + + json_writer.Reset(); + json_writer.BeginObject("object"); + json_writer.OutputValue(0); + json_writer.OutputValue(1); + json_writer.OutputValue(2); + json_writer.OutputValue(3); + json_writer.EndObject(); + CompareGolden(&json_writer, "\"object\":{0,1,2,3}"); + + json_writer.Reset(); + json_writer.BeginObject("object1"); + json_writer.EndObject(); + json_writer.BeginObject("object2"); + json_writer.EndObject(); + CompareGolden(&json_writer, "\"object1\":{},\"object2\":{}"); + + json_writer.Reset(); + json_writer.BeginObject("object1"); + json_writer.BeginObject("object2"); + json_writer.EndObject(); + json_writer.EndObject(); + CompareGolden(&json_writer, "\"object1\":{\"object2\":{}}"); +} + +TEST_F(GltfUtilsTest, TestArraysCompact) { + JsonWriter json_writer; + json_writer.SetMode(JsonWriter::COMPACT); + json_writer.BeginArray("array"); + json_writer.EndArray(); + CompareGolden(&json_writer, "\"array\":[]"); + + json_writer.Reset(); + json_writer.BeginArray("array"); + json_writer.OutputValue(0); + json_writer.EndArray(); + CompareGolden(&json_writer, "\"array\":[0]"); + + json_writer.Reset(); + json_writer.BeginArray("array"); + json_writer.OutputValue(0); + json_writer.OutputValue(1); + json_writer.OutputValue(2); + json_writer.OutputValue(3); + json_writer.EndArray(); + CompareGolden(&json_writer, "\"array\":[0,1,2,3]"); + + json_writer.Reset(); + json_writer.BeginArray("array1"); + json_writer.EndArray(); + json_writer.BeginArray("array2"); + json_writer.EndArray(); + CompareGolden(&json_writer, "\"array1\":[],\"array2\":[]"); + + json_writer.Reset(); + json_writer.BeginArray("array1"); + json_writer.BeginArray("array2"); + json_writer.EndArray(); + json_writer.EndArray(); + CompareGolden(&json_writer, "\"array1\":[\"array2\":[]]"); +} + } // namespace draco #endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/io/mesh_io.cc draco-1.5.5+dfsg/src/draco/io/mesh_io.cc --- draco-1.5.3+dfsg/src/draco/io/mesh_io.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/mesh_io.cc 2022-10-29 00:55:03.000000000 +0000 @@ -65,6 +65,7 @@ // Wavefront OBJ file format. ObjDecoder obj_decoder; obj_decoder.set_use_metadata(options.GetBool("use_metadata", false)); + obj_decoder.set_preserve_polygons(options.GetBool("preserve_polygons")); const Status obj_status = obj_decoder.DecodeFromFile(file_name, mesh.get(), mesh_files); if (!obj_status.ok()) { diff -Nru draco-1.5.3+dfsg/src/draco/io/obj_encoder.cc draco-1.5.5+dfsg/src/draco/io/obj_encoder.cc --- draco-1.5.3+dfsg/src/draco/io/obj_encoder.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/obj_encoder.cc 2022-10-29 00:55:03.000000000 +0000 @@ -165,7 +165,8 @@ } sub_obj_att_ = in_point_cloud_->GetAttributeByUniqueId( sub_obj_metadata->att_unique_id()); - if (sub_obj_att_ == nullptr || sub_obj_att_->size() == 0) { + if (sub_obj_att_ == nullptr || sub_obj_att_->size() == 0 || + sub_obj_att_->num_components() != 1) { return false; } return true; diff -Nru draco-1.5.3+dfsg/src/draco/io/ply_encoder.cc draco-1.5.5+dfsg/src/draco/io/ply_encoder.cc --- draco-1.5.3+dfsg/src/draco/io/ply_encoder.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/ply_encoder.cc 2022-10-29 00:55:03.000000000 +0000 @@ -143,7 +143,8 @@ buffer()->Encode(header_str.data(), header_str.length()); // Store point attributes. - for (PointIndex v(0); v < in_point_cloud_->num_points(); ++v) { + const int num_points = in_point_cloud_->num_points(); + for (PointIndex v(0); v < num_points; ++v) { const auto *const pos_att = in_point_cloud_->attribute(pos_att_id); buffer()->Encode(pos_att->GetAddress(pos_att->mapped_index(v)), pos_att->byte_stride()); @@ -166,9 +167,13 @@ buffer()->Encode(static_cast(3)); const auto &f = in_mesh_->face(i); - buffer()->Encode(f[0]); - buffer()->Encode(f[1]); - buffer()->Encode(f[2]); + for (int c = 0; c < 3; ++c) { + if (f[c] >= num_points) { + // Invalid point stored on the |in_mesh_| face. + return false; + } + buffer()->Encode(f[c]); + } if (tex_coord_att_id >= 0) { // Two coordinates for every corner -> 6. diff -Nru draco-1.5.3+dfsg/src/draco/io/texture_io_test.cc draco-1.5.5+dfsg/src/draco/io/texture_io_test.cc --- draco-1.5.3+dfsg/src/draco/io/texture_io_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/io/texture_io_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -15,6 +15,12 @@ #include "draco/io/texture_io.h" #ifdef DRACO_TRANSCODER_SUPPORTED +#include +#include +#include +#include +#include + #include "draco/core/draco_test_utils.h" #include "draco/io/file_utils.h" diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_are_equivalent.cc draco-1.5.5+dfsg/src/draco/mesh/mesh_are_equivalent.cc --- draco-1.5.3+dfsg/src/draco/mesh/mesh_are_equivalent.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_are_equivalent.cc 2022-10-29 00:55:03.000000000 +0000 @@ -15,6 +15,9 @@ #include "draco/mesh/mesh_are_equivalent.h" #include +#include + +#include "draco/texture/texture_utils.h" namespace draco { @@ -114,6 +117,55 @@ // face with respect to lex order. Init(mesh0, mesh1); +#ifdef DRACO_TRANSCODER_SUPPORTED + // Compare geometry compression settings. + if (mesh0.IsCompressionEnabled() != mesh1.IsCompressionEnabled()) { + return false; + } + if (mesh0.GetCompressionOptions() != mesh1.GetCompressionOptions()) { + return false; + } + + // Compare non-material texture library sizes. + if (mesh0.GetNonMaterialTextureLibrary().NumTextures() != + mesh1.GetNonMaterialTextureLibrary().NumTextures()) { + return false; + } + + // Compare mesh feature ID sets. + if (mesh0.NumMeshFeatures() != mesh1.NumMeshFeatures()) { + return false; + } + for (MeshFeaturesIndex i(0); i < mesh0.NumMeshFeatures(); ++i) { + const MeshFeatures &features0 = mesh0.GetMeshFeatures(i); + const MeshFeatures &features1 = mesh1.GetMeshFeatures(i); + if (features0.GetAttributeIndex() != features1.GetAttributeIndex()) { + return false; + } + if (features0.GetFeatureCount() != features1.GetFeatureCount()) { + return false; + } + if (features0.GetLabel() != features1.GetLabel()) { + return false; + } + if (features0.GetNullFeatureId() != features1.GetNullFeatureId()) { + return false; + } + if (features0.GetTextureChannels() != features1.GetTextureChannels()) { + return false; + } + if (features0.GetPropertyTableIndex() != + features1.GetPropertyTableIndex()) { + return false; + } + const TextureMap &map0 = features0.GetTextureMap(); + const TextureMap &map1 = features1.GetTextureMap(); + if (map0.tex_coord_index() != map1.tex_coord_index()) { + return false; + } + } +#endif // DRACO_TRANSCODER_SUPPORTED + // Check for every attribute that is valid that every corner is identical. typedef GeometryAttribute::Type AttributeType; const int att_max = AttributeType::NAMED_ATTRIBUTES_COUNT; diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_are_equivalent_test.cc draco-1.5.5+dfsg/src/draco/mesh/mesh_are_equivalent_test.cc --- draco-1.5.3+dfsg/src/draco/mesh/mesh_are_equivalent_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_are_equivalent_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -15,6 +15,7 @@ #include "draco/mesh/mesh_are_equivalent.h" #include +#include #include "draco/core/draco_test_base.h" #include "draco/core/draco_test_utils.h" @@ -30,6 +31,14 @@ const std::string file_name = "test_nm.obj"; const std::unique_ptr mesh(ReadMeshFromTestFile(file_name)); ASSERT_NE(mesh, nullptr) << "Failed to load test model." << file_name; + +#ifdef DRACO_TRANSCODER_SUPPORTED + // Add mesh feature ID set to the mesh. + std::unique_ptr mesh_features(new MeshFeatures()); + mesh->AddMeshFeatures(std::move(mesh_features)); +#endif + + // Check that mesh is equivalent to itself. MeshAreEquivalent equiv; ASSERT_TRUE(equiv(*mesh, *mesh)); } @@ -95,4 +104,32 @@ ASSERT_TRUE(equiv(*mesh0, *mesh1)); } +#ifdef DRACO_TRANSCODER_SUPPORTED + +TEST_F(MeshAreEquivalentTest, TestMeshFeatures) { + const std::string file_name = "test_nm.obj"; + const std::unique_ptr mesh0(ReadMeshFromTestFile(file_name)); + const std::unique_ptr mesh1(ReadMeshFromTestFile(file_name)); + ASSERT_NE(mesh0, nullptr); + ASSERT_NE(mesh1, nullptr); + + // Add identical mesh feature ID sets to meshes. + mesh0->AddMeshFeatures(std::unique_ptr(new MeshFeatures())); + mesh1->AddMeshFeatures(std::unique_ptr(new MeshFeatures())); + + // Empty feature sets should match. + MeshAreEquivalent equiv; + ASSERT_TRUE(equiv(*mesh0, *mesh1)); + + // Make mesh features different and check that the meshes are not equivalent. + mesh0->GetMeshFeatures(MeshFeaturesIndex(0)).SetFeatureCount(5); + mesh1->GetMeshFeatures(MeshFeaturesIndex(0)).SetFeatureCount(6); + ASSERT_FALSE(equiv(*mesh0, *mesh1)); + + // Make mesh features identical and check that the meshes are equivalent. + mesh0->GetMeshFeatures(MeshFeaturesIndex(0)).SetFeatureCount(1); + mesh1->GetMeshFeatures(MeshFeaturesIndex(0)).SetFeatureCount(1); + ASSERT_TRUE(equiv(*mesh0, *mesh1)); +} +#endif // DRACO_TRANSCODER_SUPPORTED } // namespace draco diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_attribute_corner_table.cc draco-1.5.5+dfsg/src/draco/mesh/mesh_attribute_corner_table.cc --- draco-1.5.3+dfsg/src/draco/mesh/mesh_attribute_corner_table.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_attribute_corner_table.cc 2022-10-29 00:55:03.000000000 +0000 @@ -126,18 +126,18 @@ } } -void MeshAttributeCornerTable::RecomputeVertices(const Mesh *mesh, +bool MeshAttributeCornerTable::RecomputeVertices(const Mesh *mesh, const PointAttribute *att) { DRACO_DCHECK(GetValenceCache().IsCacheEmpty()); if (mesh != nullptr && att != nullptr) { - RecomputeVerticesInternal(mesh, att); + return RecomputeVerticesInternal(mesh, att); } else { - RecomputeVerticesInternal(nullptr, nullptr); + return RecomputeVerticesInternal(nullptr, nullptr); } } template -void MeshAttributeCornerTable::RecomputeVerticesInternal( +bool MeshAttributeCornerTable::RecomputeVerticesInternal( const Mesh *mesh, const PointAttribute *att) { DRACO_DCHECK(GetValenceCache().IsCacheEmpty()); vertex_to_attribute_entry_id_map_.clear(); @@ -167,6 +167,11 @@ while (act_c != kInvalidCornerIndex) { first_c = act_c; act_c = SwingLeft(act_c); + if (act_c == c) { + // We reached the initial corner which shouldn't happen when we swing + // left from |c|. + return false; + } } } corner_to_vertex_map_[first_c.value()] = VertexIndex(first_vert_id.value()); @@ -189,6 +194,7 @@ act_c = corner_table_->SwingRight(act_c); } } + return true; } int MeshAttributeCornerTable::Valence(VertexIndex v) const { diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_attribute_corner_table.h draco-1.5.5+dfsg/src/draco/mesh/mesh_attribute_corner_table.h --- draco-1.5.3+dfsg/src/draco/mesh/mesh_attribute_corner_table.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_attribute_corner_table.h 2022-10-29 00:55:03.000000000 +0000 @@ -40,7 +40,7 @@ // whenever the seam edges are updated). // |mesh| and |att| can be null, in which case mapping between vertices and // attribute value ids is set to identity. - void RecomputeVertices(const Mesh *mesh, const PointAttribute *att); + bool RecomputeVertices(const Mesh *mesh, const PointAttribute *att); inline bool IsCornerOppositeToSeamEdge(CornerIndex corner) const { return is_edge_on_seam_[corner.value()]; @@ -172,7 +172,7 @@ private: template - void RecomputeVerticesInternal(const Mesh *mesh, const PointAttribute *att); + bool RecomputeVerticesInternal(const Mesh *mesh, const PointAttribute *att); std::vector is_edge_on_seam_; std::vector is_vertex_on_seam_; diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh.cc draco-1.5.5+dfsg/src/draco/mesh/mesh.cc --- draco-1.5.3+dfsg/src/draco/mesh/mesh.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh.cc 2022-10-29 00:55:03.000000000 +0000 @@ -15,6 +15,10 @@ #include "draco/mesh/mesh.h" #include +#include +#include +#include +#include namespace draco { @@ -37,6 +41,32 @@ material_library_.Copy(src.material_library_); compression_enabled_ = src.compression_enabled_; compression_options_ = src.compression_options_; + + // Copy mesh feature ID sets. + mesh_features_.clear(); + for (MeshFeaturesIndex i(0); i < src.NumMeshFeatures(); i++) { + std::unique_ptr mesh_features(new MeshFeatures()); + mesh_features->Copy(src.GetMeshFeatures(i)); + AddMeshFeatures(std::move(mesh_features)); + } + mesh_features_material_mask_ = src.mesh_features_material_mask_; + + // Copy non-material textures. + non_material_texture_library_.Copy(src.non_material_texture_library_); + + // Update pointers to non-material textures in mesh feature ID sets. + if (non_material_texture_library_.NumTextures() != 0) { + const auto texture_to_index_map = + src.non_material_texture_library_.ComputeTextureToIndexMap(); + for (MeshFeaturesIndex j(0); j < NumMeshFeatures(); ++j) { + Mesh::UpdateMeshFeaturesTexturePointer(texture_to_index_map, + &non_material_texture_library_, + &GetMeshFeatures(j)); + } + } + + // Copy structural metadata. + structural_metadata_.Copy(src.structural_metadata_); } namespace { @@ -247,7 +277,9 @@ set_num_points(num_used_points); } -void Mesh::RemoveUnusedMaterials() { +void Mesh::RemoveUnusedMaterials() { RemoveUnusedMaterials(true); } + +void Mesh::RemoveUnusedMaterials(bool remove_unused_material_indices) { const int mat_att_index = GetNamedAttributeId(GeometryAttribute::MATERIAL); if (mat_att_index == -1) { // Remove all materials except for the first one. @@ -261,42 +293,84 @@ // Deduplicate attribute values in the material attribute to ensure that one // attribute value index corresponds to one unique material index. + // Note that this does not remove unused material indices. mat_att->DeduplicateValues(*mat_att); // Gather all material indices that are referenced by faces of the mesh. const int num_materials = GetMaterialLibrary().NumMaterials(); std::vector is_material_used(num_materials, false); int num_used_materials = 0; - for (FaceIndex fi(0); fi < num_faces(); ++fi) { + + // Helper function that updates |is_material_used| for the processed mesh. + auto update_used_materials = [&is_material_used, &num_used_materials, mat_att, + num_materials](PointIndex pi) { uint32_t mat_index = 0; - mat_att->GetMappedValue(faces_[fi][0], &mat_index); + mat_att->GetMappedValue(pi, &mat_index); if (mat_index < num_materials) { if (!is_material_used[mat_index]) { is_material_used[mat_index] = true; num_used_materials++; } } + }; + + if (num_faces() > 0) { + for (FaceIndex fi(0); fi < num_faces(); ++fi) { + update_used_materials(faces_[fi][0]); + } + } else { + // Handle the mesh as a point cloud and check materials used by points. + for (PointIndex pi(0); pi < num_points(); ++pi) { + update_used_materials(pi); + } + } + + // Check if any of the (unused) materials is used by mesh features. If so, + // user should remove unused mesh features first. + for (MeshFeaturesIndex mfi(0); mfi < NumMeshFeatures(); ++mfi) { + for (int mask_index = 0; mask_index < NumMeshFeaturesMaterialMasks(mfi); + ++mask_index) { + const int mat_index = GetMeshFeaturesMaterialMask(mfi, mask_index); + if (mat_index < num_materials && !is_material_used[mat_index]) { + is_material_used[mat_index] = true; + num_used_materials++; + } + } } + if (num_used_materials == num_materials) { return; // All materials are used, don't do anything. } - // Remove unused materials from the material library. + // Remove unused materials from the material library or replace them with + // default materials if we do not remove unused material indices. for (int mi = num_materials - 1; mi >= 0; --mi) { - if (!is_material_used[mi]) { - GetMaterialLibrary().RemoveMaterial(mi); + if (!is_material_used[mi] && mi < GetMaterialLibrary().NumMaterials()) { + if (remove_unused_material_indices) { + GetMaterialLibrary().RemoveMaterial(mi); + } else { + GetMaterialLibrary().MutableMaterial(mi)->Clear(); + } } } GetMaterialLibrary().RemoveUnusedTextures(); + if (!remove_unused_material_indices) { + // All the code below handles updating of material indices. Since we do not + // want to update them, we can return early. + return; + } + // Compute map between old and new material indices. int new_material_index = 0; IndexTypeVector old_to_new_material_attribute_value_index_map(mat_att->size(), -1); + std::vector old_to_new_material_value_map(num_materials, -1); for (AttributeValueIndex avi(0); avi < mat_att->size(); ++avi) { uint32_t mat_index = 0; mat_att->GetValue(avi, &mat_index); if (mat_index < num_materials && is_material_used[mat_index]) { + old_to_new_material_value_map[mat_index] = new_material_index; old_to_new_material_attribute_value_index_map[avi] = new_material_index++; } } @@ -317,6 +391,64 @@ pi, AttributeValueIndex( old_to_new_material_attribute_value_index_map[old_avi])); } + + // Update material indices on mesh features. + for (MeshFeaturesIndex mfi(0); mfi < NumMeshFeatures(); ++mfi) { + for (int mask_index = 0; mask_index < NumMeshFeaturesMaterialMasks(mfi); + ++mask_index) { + const int old_mat_index = GetMeshFeaturesMaterialMask(mfi, mask_index); + if (old_mat_index < num_materials && is_material_used[old_mat_index]) { + mesh_features_material_mask_[mfi][mask_index] = + old_to_new_material_value_map[old_mat_index]; + } + } + } +} + +void Mesh::UpdateMeshFeaturesTexturePointer( + const std::unordered_map &texture_to_index_map, + TextureLibrary *texture_library, MeshFeatures *mesh_features) { + TextureMap &texture_map = mesh_features->GetTextureMap(); + if (texture_map.texture() == nullptr) { + return; + } + const auto it = texture_to_index_map.find(texture_map.texture()); + DRACO_DCHECK(it != texture_to_index_map.end()); + const int texture_index = it->second; + DRACO_DCHECK(texture_index < texture_library->NumTextures()); + texture_map.SetTexture(texture_library->GetTexture(texture_index)); +} + +void Mesh::CopyMeshFeaturesForMaterial(const Mesh &source_mesh, + Mesh *target_mesh, int material_index) { + for (MeshFeaturesIndex mfi(0); mfi < source_mesh.NumMeshFeatures(); ++mfi) { + // Mesh features is used if it doesn't have any material mask or if one + // of the material masks matches |material_index|. + bool is_used = source_mesh.NumMeshFeaturesMaterialMasks(mfi) == 0; + for (int mask_index = 0; + !is_used && mask_index < source_mesh.NumMeshFeaturesMaterialMasks(mfi); + ++mask_index) { + if (source_mesh.GetMeshFeaturesMaterialMask(mfi, mask_index) == + material_index) { + is_used = true; + } + } + if (is_used) { + // Copy over the mesh features to the target mesh. Note that texture + // pointers are not updated at this step. + std::unique_ptr new_mf(new MeshFeatures()); + new_mf->Copy(source_mesh.GetMeshFeatures(mfi)); + target_mesh->AddMeshFeatures(std::move(new_mf)); + } + } +} + +int32_t Mesh::AddPerFaceAttribute(std::unique_ptr att) { + IndexTypeVector corner_map(num_faces() * 3); + for (CornerIndex ci(0); ci < num_faces() * 3; ++ci) { + corner_map[ci] = AttributeValueIndex(ci.value() / 3); + } + return AddAttributeWithConnectivity(std::move(att), corner_map); } #endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_features.cc draco-1.5.5+dfsg/src/draco/mesh/mesh_features.cc --- draco-1.5.3+dfsg/src/draco/mesh/mesh_features.cc 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_features.cc 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,98 @@ +// Copyright 2022 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "draco/mesh/mesh_features.h" + +#include +#include + +#ifdef DRACO_TRANSCODER_SUPPORTED + +namespace draco { + +MeshFeatures::MeshFeatures() + : feature_count_(0), + null_feature_id_(-1), + attribute_index_(-1), + property_table_index_(-1) {} + +void MeshFeatures::Copy(const MeshFeatures &src) { + label_ = src.label_; + feature_count_ = src.feature_count_; + null_feature_id_ = src.null_feature_id_; + attribute_index_ = src.attribute_index_; + texture_map_.Copy(src.texture_map_); + texture_channels_ = src.texture_channels_; + property_table_index_ = src.property_table_index_; +} + +void MeshFeatures::SetLabel(const std::string &label) { label_ = label; } + +const std::string &MeshFeatures::GetLabel() const { return label_; } + +void MeshFeatures::SetFeatureCount(int feature_count) { + feature_count_ = feature_count; +} + +int MeshFeatures::GetFeatureCount() const { return feature_count_; } + +void MeshFeatures::SetNullFeatureId(int null_feature_id) { + null_feature_id_ = null_feature_id; +} + +int MeshFeatures::GetNullFeatureId() const { return null_feature_id_; } + +void MeshFeatures::SetAttributeIndex(int attribute_index) { + attribute_index_ = attribute_index; +} + +int MeshFeatures::GetAttributeIndex() const { return attribute_index_; } + +void MeshFeatures::SetTextureMap(const TextureMap &texture_map) { + texture_map_.Copy(texture_map); +} + +void MeshFeatures::SetTextureMap(Texture *texture, int tex_coord_index) { + texture_map_.SetProperties(TextureMap::GENERIC, tex_coord_index); + texture_map_.SetTexture(texture); +} + +const TextureMap &MeshFeatures::GetTextureMap() const { return texture_map_; } + +TextureMap &MeshFeatures::GetTextureMap() { return texture_map_; } + +void MeshFeatures::SetTextureChannels( + const std::vector &texture_channels) { + texture_channels_ = texture_channels; +} + +const std::vector &MeshFeatures::GetTextureChannels() const { + return texture_channels_; +} + +std::vector &MeshFeatures::GetTextureChannels() { + return texture_channels_; +} + +void MeshFeatures::SetPropertyTableIndex(int property_table_index) { + property_table_index_ = property_table_index; +} + +int MeshFeatures::GetPropertyTableIndex() const { + return property_table_index_; +} + +} // namespace draco + +#endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_features.h draco-1.5.5+dfsg/src/draco/mesh/mesh_features.h --- draco-1.5.3+dfsg/src/draco/mesh/mesh_features.h 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_features.h 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,93 @@ +// Copyright 2022 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef DRACO_MESH_MESH_FEATURES_H_ +#define DRACO_MESH_MESH_FEATURES_H_ + +#include "draco/draco_features.h" + +#ifdef DRACO_TRANSCODER_SUPPORTED +#include +#include + +#include "draco/texture/texture_library.h" +#include "draco/texture/texture_map.h" + +namespace draco { + +// Describes a mesh feature ID set according to the EXT_mesh_features glTF +// extension. Feature IDs are either associated with geometry vertices or with +// texture pixels and stored in a geometry attribute or in texture channels, +// respectively. Optionally, the feature ID set may be associated with a +// property table defined in the EXT_structural_metadata glTF extension. +class MeshFeatures { + public: + // Creates an empty feature ID set that is associated neither with vertices, + // nor with texture pixels, nor with property tables. + MeshFeatures(); + + // Copies all data from |src| mesh feature ID set. + void Copy(const MeshFeatures &src); + + // Label assigned to this feature ID set. + void SetLabel(const std::string &label); + const std::string &GetLabel() const; + + // The number of unique features in this feature ID set. + void SetFeatureCount(int feature_count); + int GetFeatureCount() const; + + // Non-negative null feature ID value indicating the absence of an associated + // feature. The value of -1 indicates that the null feature ID is not set. + void SetNullFeatureId(int null_feature_id); + int GetNullFeatureId() const; + + // Index of the feature ID vertex attribute, e.g., 5 for an attribute named + // _FEATURE_ID_5, or -1 if the feature ID is not associated with vertices. + void SetAttributeIndex(int attribute_index); + int GetAttributeIndex() const; + + // Feature ID texture map and texture channels containing feature IDs + // associated with texture pixels. Only used when |attribute_index_| is -1. + // The RGBA channels are numbered from 0 to 3. See the glTF extension + // documentation for reconstruction of feature ID from the channel values. + void SetTextureMap(const TextureMap &texture_map); + void SetTextureMap(Texture *texture, int tex_coord_index); + const TextureMap &GetTextureMap() const; + TextureMap &GetTextureMap(); + void SetTextureChannels(const std::vector &texture_channels); + const std::vector &GetTextureChannels() const; + std::vector &GetTextureChannels(); + + // Non-negative index of the property table this feature ID set is associated + // with. Property tables are defined in the EXT_structural_metadata glTF + // extension. The value of -1 indicates that this feature ID set is not + // associated with any property tables. + void SetPropertyTableIndex(int property_table_index); + int GetPropertyTableIndex() const; + + private: + std::string label_; + int feature_count_; + int null_feature_id_; + int attribute_index_; + TextureMap texture_map_; + std::vector texture_channels_; + int property_table_index_; +}; + +} // namespace draco + +#endif // DRACO_TRANSCODER_SUPPORTED +#endif // DRACO_MESH_MESH_FEATURES_H_ diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_features_test.cc draco-1.5.5+dfsg/src/draco/mesh/mesh_features_test.cc --- draco-1.5.3+dfsg/src/draco/mesh/mesh_features_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_features_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,98 @@ +// Copyright 2022 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "draco/mesh/mesh_features.h" + +#include +#include + +#include "draco/core/draco_test_base.h" +#include "draco/core/draco_test_utils.h" +#include "draco/texture/texture_map.h" + +namespace { + +#ifdef DRACO_TRANSCODER_SUPPORTED + +TEST(MeshFeaturesTest, TestDefaults) { + // Test construction of an empty feature ID set. + draco::MeshFeatures mesh_features; + ASSERT_TRUE(mesh_features.GetLabel().empty()); + ASSERT_EQ(mesh_features.GetFeatureCount(), 0); + ASSERT_EQ(mesh_features.GetNullFeatureId(), -1); + ASSERT_EQ(mesh_features.GetAttributeIndex(), -1); + ASSERT_EQ(mesh_features.GetPropertyTableIndex(), -1); + ASSERT_TRUE(mesh_features.GetTextureChannels().empty()); + ASSERT_EQ(mesh_features.GetTextureMap().texture(), nullptr); + ASSERT_EQ(mesh_features.GetTextureMap().type(), draco::TextureMap::GENERIC); +} + +TEST(MeshFeaturesTest, TestSettersAndGetters) { + // Test setter and getter methods of the feature ID set. + draco::MeshFeatures mesh_features; + mesh_features.SetLabel("continent"); + mesh_features.SetFeatureCount(8); + mesh_features.SetNullFeatureId(0); + mesh_features.SetAttributeIndex(2); + mesh_features.SetPropertyTableIndex(10); + std::vector channels = {2, 3}; + mesh_features.SetTextureChannels({2, 3}); + draco::TextureMap texture_map; + texture_map.SetProperties(draco::TextureMap::GENERIC, 1); + std::unique_ptr texture(new draco::Texture()); + texture_map.SetTexture(texture.get()); + mesh_features.SetTextureMap(texture_map); + + // Check that mesh feature set properties can be accessed via getters. + ASSERT_EQ(mesh_features.GetLabel(), "continent"); + ASSERT_EQ(mesh_features.GetFeatureCount(), 8); + ASSERT_EQ(mesh_features.GetNullFeatureId(), 0); + ASSERT_EQ(mesh_features.GetAttributeIndex(), 2); + ASSERT_EQ(mesh_features.GetPropertyTableIndex(), 10); + ASSERT_EQ(mesh_features.GetTextureChannels(), channels); + ASSERT_EQ(mesh_features.GetTextureMap().texture(), texture.get()); + ASSERT_EQ(mesh_features.GetTextureMap().type(), draco::TextureMap::GENERIC); +} + +TEST(MeshFeaturesTest, TestCopy) { + // Test that feature ID set can be copied. + draco::MeshFeatures mesh_features; + mesh_features.SetLabel("continent"); + mesh_features.SetFeatureCount(8); + mesh_features.SetNullFeatureId(0); + mesh_features.SetAttributeIndex(2); + mesh_features.SetPropertyTableIndex(10); + std::vector channels = {2, 3}; + mesh_features.SetTextureChannels({2, 3}); + std::unique_ptr texture(new draco::Texture()); + mesh_features.SetTextureMap(texture.get(), 1); + + // Make a copy. + draco::MeshFeatures copy; + copy.Copy(mesh_features); + + // Check the copy. + ASSERT_EQ(copy.GetLabel(), "continent"); + ASSERT_EQ(copy.GetFeatureCount(), 8); + ASSERT_EQ(copy.GetNullFeatureId(), 0); + ASSERT_EQ(copy.GetAttributeIndex(), 2); + ASSERT_EQ(copy.GetPropertyTableIndex(), 10); + ASSERT_EQ(copy.GetTextureChannels(), channels); + ASSERT_EQ(copy.GetTextureMap().texture(), texture.get()); + ASSERT_EQ(copy.GetTextureMap().type(), draco::TextureMap::GENERIC); +} + +#endif // DRACO_TRANSCODER_SUPPORTED + +} // namespace diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh.h draco-1.5.5+dfsg/src/draco/mesh/mesh.h --- draco-1.5.3+dfsg/src/draco/mesh/mesh.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh.h 2022-10-29 00:55:03.000000000 +0000 @@ -16,6 +16,7 @@ #define DRACO_MESH_MESH_H_ #include +#include #include "draco/attributes/geometry_indices.h" #include "draco/core/hash_utils.h" @@ -25,6 +26,9 @@ #ifdef DRACO_TRANSCODER_SUPPORTED #include "draco/compression/draco_compression_options.h" #include "draco/material/material_library.h" +#include "draco/mesh/mesh_features.h" +#include "draco/mesh/mesh_indices.h" +#include "draco/metadata/structural_metadata.h" #endif #include "draco/point_cloud/point_cloud.h" @@ -117,7 +121,12 @@ // Removes points that are not mapped to any face of the mesh. All attribute // values are going to be removed as well. void RemoveIsolatedPoints(); -#endif + + // Adds a point attribute |att| to the mesh and returns the index of the + // newly inserted attribute. Attribute values are mapped 1:1 to face indices. + // Returns -1 in case of error. + int32_t AddPerFaceAttribute(std::unique_ptr att); +#endif // DRACO_TRANSCODER_SUPPORTED MeshAttributeElementType GetAttributeElementType(int att_id) const { return attribute_data_[att_id].element_type; @@ -154,7 +163,12 @@ MaterialLibrary &GetMaterialLibrary() { return material_library_; } // Removes all materials that are not referenced by any face of the mesh. + // Optional argument |remove_unused_material_indices| can be used to control + // whether unusued material indices are removed as well (default = true). + // If material indices are not removed, the unused material indices will + // point to empty (default) materials. void RemoveUnusedMaterials(); + void RemoveUnusedMaterials(bool remove_unused_material_indices); // Enables or disables Draco geometry compression for this mesh. void SetCompressionEnabled(bool enabled) { compression_enabled_ = enabled; } @@ -171,7 +185,71 @@ DracoCompressionOptions &GetCompressionOptions() { return compression_options_; } -#endif + + // Library that contains non-material textures. + const TextureLibrary &GetNonMaterialTextureLibrary() const { + return non_material_texture_library_; + } + TextureLibrary &GetNonMaterialTextureLibrary() { + return non_material_texture_library_; + } + + // Mesh feature ID sets as defined by EXT_mesh_features glTF extension. + MeshFeaturesIndex AddMeshFeatures( + std::unique_ptr mesh_features) { + mesh_features_.push_back(std::move(mesh_features)); + mesh_features_material_mask_.push_back({}); + return MeshFeaturesIndex(mesh_features_.size() - 1); + } + int NumMeshFeatures() const { return mesh_features_.size(); } + const MeshFeatures &GetMeshFeatures(MeshFeaturesIndex index) const { + return *mesh_features_[index]; + } + MeshFeatures &GetMeshFeatures(MeshFeaturesIndex index) { + return *mesh_features_[index]; + } + void RemoveMeshFeatures(MeshFeaturesIndex index) { + mesh_features_.erase(mesh_features_.begin() + index.value()); + mesh_features_material_mask_.erase(mesh_features_material_mask_.begin() + + index.value()); + } + + // Restricts given mesh features to faces mapped to a material with + // |material_index|. Note that single mesh features can be restricted to + // multiple materials. + void AddMeshFeaturesMaterialMask(MeshFeaturesIndex index, + int material_index) { + mesh_features_material_mask_[index].push_back(material_index); + } + + size_t NumMeshFeaturesMaterialMasks(MeshFeaturesIndex index) const { + return mesh_features_material_mask_[index].size(); + } + int GetMeshFeaturesMaterialMask(MeshFeaturesIndex index, + int mask_index) const { + return mesh_features_material_mask_[index][mask_index]; + } + + // Updates mesh features texture pointer to point to a new |texture_library|. + // The current texture pointer is used to determine the texture index in the + // new texture library via a given |texture_to_index_map|. + static void UpdateMeshFeaturesTexturePointer( + const std::unordered_map &texture_to_index_map, + TextureLibrary *texture_library, MeshFeatures *mesh_features); + + // Copies over mesh features from |source_mesh| and stores them in + // |target_mesh| as long as the mesh features material mask is valid for + // given |material_index|. + static void CopyMeshFeaturesForMaterial(const Mesh &source_mesh, + Mesh *target_mesh, + int material_index); + + // Structural metadata. + const StructuralMetadata &GetStructuralMetadata() const { + return structural_metadata_; + } + StructuralMetadata &GetStructuralMetadata() { return structural_metadata_; } +#endif // DRACO_TRANSCODER_SUPPORTED protected: #ifdef DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED @@ -206,7 +284,29 @@ // TODO(vytyaz): Store encoded bitstream that this mesh compresses into. bool compression_enabled_; DracoCompressionOptions compression_options_; -#endif + + // Sets of feature IDs as defined by EXT_mesh_features glTF extension. + IndexTypeVector> + mesh_features_; + + // When the Mesh contains multiple materials, |mesh_features_material_mask_| + // can be used to limit specific MeshFeaturesIndex to a vector of material + // indices. If for a given mesh feature index, the material indices are empty, + // the corresponding mesh features are applied to the entire mesh. + IndexTypeVector> + mesh_features_material_mask_; + + // Texture library for storing non-material textures used by this mesh, e.g., + // textures containing mesh feature IDs of EXT_mesh_features glTF extension. + // If the mesh is part of the scene then the textures are stored in the scene. + // Note that mesh features contain pointers to non-material textures. It is + // responsibility of class user to update these pointers when updating the + // textures. See Mesh::Copy() for example. + TextureLibrary non_material_texture_library_; + + // Structural metadata defined by the EXT_structural_metadata glTF extension. + StructuralMetadata structural_metadata_; +#endif // DRACO_TRANSCODER_SUPPORTED friend struct MeshHasher; }; diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_indices.h draco-1.5.5+dfsg/src/draco/mesh/mesh_indices.h --- draco-1.5.3+dfsg/src/draco/mesh/mesh_indices.h 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_indices.h 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,37 @@ +// Copyright 2022 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifdef DRACO_TRANSCODER_SUPPORTED +#ifndef DRACO_MESH_MESH_INDICES_H_ +#define DRACO_MESH_MESH_INDICES_H_ + +#include + +#include + +#include "draco/core/draco_index_type.h" + +namespace draco { + +// Index of a mesh feature ID set. +DEFINE_NEW_DRACO_INDEX_TYPE(uint32_t, MeshFeaturesIndex) + +// Constants denoting invalid indices. +static constexpr MeshFeaturesIndex kInvalidMeshFeaturesIndex( + std::numeric_limits::max()); + +} // namespace draco + +#endif // DRACO_MESH_MESH_INDICES_H_ +#endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_splitter.cc draco-1.5.5+dfsg/src/draco/mesh/mesh_splitter.cc --- draco-1.5.3+dfsg/src/draco/mesh/mesh_splitter.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_splitter.cc 2022-10-29 00:55:03.000000000 +0000 @@ -15,18 +15,73 @@ #include "draco/mesh/mesh_splitter.h" #ifdef DRACO_TRANSCODER_SUPPORTED +#include +#include #include +#include "draco/mesh/mesh_utils.h" +#include "draco/mesh/triangle_soup_mesh_builder.h" +#include "draco/point_cloud/point_cloud_builder.h" + namespace draco { -MeshSplitter::MeshSplitter() : preserve_materials_(false) {} +// Helper class that handles splitting of meshes with faces / without faces, +// i.e. point clouds. +template +class MeshSplitterInternal { + public: + struct WorkData : public MeshSplitter::WorkData { + // TriangleSoupMeshBuilder or PointCloudBuilder. + std::vector builders; + }; + + // Computes number of elements (faces or points) for each sub-mesh. + Status InitializeWorkDataNumElements(const Mesh &mesh, int split_attribute_id, + WorkData *work_data) const; + // Initializes a builder for a given sub-mesh. + void InitializeBuilder(int b_index, int num_elements, const Mesh &mesh, + int ignored_attribute_id, WorkData *work_data) const; + // Add all faces or points to the builders. + void AddElementsToBuilder(const Mesh &mesh, + const PointAttribute *split_attribute, + WorkData *work_data) const; + // Builds the meshes from the data accumulated in the builders. + StatusOr BuildMeshes(const Mesh &mesh, + WorkData *work_data) const; +}; + +namespace { + +// Helper functions for copying single element from source |mesh| to a target +// builder |b_index| stored in |work_data|. +void AddElementToBuilder( + int b_index, FaceIndex source_i, FaceIndex target_i, const Mesh &mesh, + MeshSplitterInternal::WorkData *work_data); +void AddElementToBuilder( + int b_index, PointIndex source_i, PointIndex target_i, const Mesh &mesh, + MeshSplitterInternal::WorkData *work_data); +} // namespace + +MeshSplitter::MeshSplitter() + : preserve_materials_(false), + remove_unused_material_indices_(true), + preserve_mesh_features_(false) {} StatusOr MeshSplitter::SplitMesh( const Mesh &mesh, uint32_t split_attribute_id) { if (mesh.num_attributes() <= split_attribute_id) { return Status(Status::DRACO_ERROR, "Invalid attribute id."); } + if (mesh.num_faces() == 0) { + return SplitMeshInternal(mesh, split_attribute_id); + } else { + return SplitMeshInternal(mesh, split_attribute_id); + } +} +template +StatusOr MeshSplitter::SplitMeshInternal( + const Mesh &mesh, int split_attribute_id) { const PointAttribute *const split_attribute = mesh.attribute(split_attribute_id); @@ -40,9 +95,48 @@ split_attribute->attribute_type() == GeometryAttribute::MATERIAL; const int num_out_meshes = split_attribute->size(); - WorkData work_data; - work_data.num_sub_mesh_faces.resize(num_out_meshes, 0); + MeshSplitterInternal splitter_internal; + typename MeshSplitterInternal::WorkData work_data; + work_data.num_sub_mesh_elements.resize(num_out_meshes, 0); + work_data.split_by_materials = + (split_attribute->attribute_type() == GeometryAttribute::MATERIAL); + + DRACO_RETURN_IF_ERROR(splitter_internal.InitializeWorkDataNumElements( + mesh, split_attribute_id, &work_data)); + + // Create the sub-meshes. + work_data.builders.resize(num_out_meshes); + // Map between attribute ids of the input and output meshes. + work_data.att_id_map.resize(mesh.num_attributes(), -1); + const int ignored_att_id = + (!preserve_split_attribute ? split_attribute_id : -1); + for (int mi = 0; mi < num_out_meshes; ++mi) { + if (work_data.num_sub_mesh_elements[mi] == 0) { + continue; // Empty mesh, don't initialize it. + } + + const int num_elements = work_data.num_sub_mesh_elements[mi]; + splitter_internal.InitializeBuilder(mi, num_elements, mesh, ignored_att_id, + &work_data); + + // Reset the element counter for the sub-mesh. It will be used to keep track + // of number of elements added to the sub-mesh. + work_data.num_sub_mesh_elements[mi] = 0; + } + + splitter_internal.AddElementsToBuilder(mesh, split_attribute, &work_data); + DRACO_ASSIGN_OR_RETURN(MeshVector out_meshes, + splitter_internal.BuildMeshes(mesh, &work_data)); + return FinalizeMeshes(mesh, work_data, std::move(out_meshes)); +} + +template <> +Status +MeshSplitterInternal::InitializeWorkDataNumElements( + const Mesh &mesh, int split_attribute_id, WorkData *work_data) const { + const PointAttribute *const split_attribute = + mesh.attribute(split_attribute_id); // Verify that the attribute values are defined "per-face", i.e., all points // on a face are always mapped to the same attribute value. for (FaceIndex fi(0); fi < mesh.num_faces(); ++fi) { @@ -54,132 +148,182 @@ "Attribute values not consistent on a face."); } } - work_data.num_sub_mesh_faces[avi.value()] += 1; + work_data->num_sub_mesh_elements[avi.value()] += 1; } + return OkStatus(); +} - // Create the sub-meshes. - work_data.mesh_builders.resize(num_out_meshes); - // Map between attribute ids of the input and output meshes. - work_data.att_id_map.resize(mesh.num_attributes(), -1); - const int ignored_att_id = - (!preserve_split_attribute ? split_attribute_id : -1); - for (int mi = 0; mi < num_out_meshes; ++mi) { - if (work_data.num_sub_mesh_faces[mi] == 0) { - continue; // Empty mesh, don't initialize it. - } +template <> +Status MeshSplitterInternal::InitializeWorkDataNumElements( + const Mesh &mesh, int split_attribute_id, WorkData *work_data) const { + const PointAttribute *const split_attribute = + mesh.attribute(split_attribute_id); + // Each point can have a different value. Just accumulate the number of points + // with the same attribute value index. + for (PointIndex pi(0); pi < mesh.num_points(); ++pi) { + const AttributeValueIndex avi = split_attribute->mapped_index(pi); + work_data->num_sub_mesh_elements[avi.value()] += 1; + } + return OkStatus(); +} - const int num_faces = work_data.num_sub_mesh_faces[mi]; - InitializeMeshBuilder(mi, num_faces, mesh, ignored_att_id, &work_data); +template +void MeshSplitterInternal::InitializeBuilder( + int b_index, int num_elements, const Mesh &mesh, int ignored_attribute_id, + WorkData *work_data) const { + work_data->builders[b_index].Start(num_elements); - // Reset the face counter for the sub-mesh. It will be used to keep track of - // number of faces added to the sub-mesh. - work_data.num_sub_mesh_faces[mi] = 0; + // Add all attributes. + for (int ai = 0; ai < mesh.num_attributes(); ++ai) { + if (ai == ignored_attribute_id) { + continue; + } + const GeometryAttribute *const src_att = mesh.attribute(ai); + work_data->att_id_map[ai] = work_data->builders[b_index].AddAttribute( + src_att->attribute_type(), src_att->num_components(), + src_att->data_type()); } +} +template <> +void MeshSplitterInternal::AddElementsToBuilder( + const Mesh &mesh, const PointAttribute *split_attribute, + WorkData *work_data) const { // Go over all faces of the input mesh and add them to the appropriate // sub-mesh. for (FaceIndex fi(0); fi < mesh.num_faces(); ++fi) { const auto face = mesh.face(fi); const int sub_mesh_id = split_attribute->mapped_index(face[0]).value(); - const FaceIndex target_fi(work_data.num_sub_mesh_faces[sub_mesh_id]++); - AddFaceToMeshBuilder(sub_mesh_id, fi, target_fi, mesh, &work_data); + const FaceIndex target_fi(work_data->num_sub_mesh_elements[sub_mesh_id]++); + AddElementToBuilder(sub_mesh_id, fi, target_fi, mesh, work_data); } - - return FinalizeMeshes(mesh, &work_data); } -StatusOr MeshSplitter::SplitMeshToComponents( - const Mesh &mesh, const MeshConnectedComponents &connected_components) { - // Create the sub-meshes. - const int num_out_meshes = connected_components.NumConnectedComponents(); - WorkData work_data; - work_data.mesh_builders.resize(num_out_meshes); - work_data.num_sub_mesh_faces.resize(num_out_meshes, 0); - work_data.att_id_map.resize(mesh.num_attributes(), -1); - for (int mi = 0; mi < num_out_meshes; ++mi) { - const int num_faces = connected_components.NumConnectedComponentFaces(mi); - work_data.num_sub_mesh_faces[mi] = num_faces; - InitializeMeshBuilder(mi, num_faces, mesh, -1, &work_data); - } - - // Go over all faces of the input mesh and add them to the appropriate +template <> +void MeshSplitterInternal::AddElementsToBuilder( + const Mesh &mesh, const PointAttribute *split_attribute, + WorkData *work_data) const { + // Go over all points of the input mesh and add them to the appropriate // sub-mesh. - for (int mi = 0; mi < num_out_meshes; ++mi) { - for (int cfi = 0; cfi < connected_components.NumConnectedComponentFaces(mi); - ++cfi) { - const FaceIndex fi( - connected_components.GetConnectedComponent(mi).faces[cfi]); - const FaceIndex target_fi(cfi); - AddFaceToMeshBuilder(mi, fi, target_fi, mesh, &work_data); - } + for (PointIndex pi(0); pi < mesh.num_points(); ++pi) { + const int sub_mesh_id = split_attribute->mapped_index(pi).value(); + const PointIndex target_pi(work_data->num_sub_mesh_elements[sub_mesh_id]++); + AddElementToBuilder(sub_mesh_id, pi, target_pi, mesh, work_data); } - return FinalizeMeshes(mesh, &work_data); } -void MeshSplitter::InitializeMeshBuilder(int mb_index, int num_faces, - const Mesh &mesh, - int ignored_attribute_id, - WorkData *work_data) const { - work_data->mesh_builders[mb_index].Start(num_faces); - work_data->mesh_builders[mb_index].SetName(mesh.GetName()); +namespace { - // Add all attributes. +void AddElementToBuilder( + int b_index, FaceIndex source_i, FaceIndex target_i, const Mesh &mesh, + MeshSplitterInternal::WorkData *work_data) { + const auto &face = mesh.face(source_i); for (int ai = 0; ai < mesh.num_attributes(); ++ai) { - if (ai == ignored_attribute_id) { + const PointAttribute *const src_att = mesh.attribute(ai); + const int target_att_id = work_data->att_id_map[ai]; + if (target_att_id == -1) { continue; } - const GeometryAttribute *const src_att = mesh.attribute(ai); - work_data->att_id_map[ai] = work_data->mesh_builders[mb_index].AddAttribute( - src_att->attribute_type(), src_att->num_components(), - src_att->data_type()); + // Add value for each corner of the face. + work_data->builders[b_index].SetAttributeValuesForFace( + target_att_id, target_i, src_att->GetAddressOfMappedIndex(face[0]), + src_att->GetAddressOfMappedIndex(face[1]), + src_att->GetAddressOfMappedIndex(face[2])); } } -void MeshSplitter::AddFaceToMeshBuilder(int mb_index, FaceIndex source_fi, - FaceIndex target_fi, const Mesh &mesh, - WorkData *work_data) const { - const auto face = mesh.face(source_fi); +void AddElementToBuilder( + int b_index, PointIndex source_i, PointIndex target_i, const Mesh &mesh, + MeshSplitterInternal::WorkData *work_data) { for (int ai = 0; ai < mesh.num_attributes(); ++ai) { const PointAttribute *const src_att = mesh.attribute(ai); const int target_att_id = work_data->att_id_map[ai]; if (target_att_id == -1) { continue; } - work_data->mesh_builders[mb_index].SetAttributeValuesForFace( - target_att_id, target_fi, src_att->GetAddressOfMappedIndex(face[0]), - src_att->GetAddressOfMappedIndex(face[1]), - src_att->GetAddressOfMappedIndex(face[2])); + // Add value for the point |target_i|. + work_data->builders[b_index].SetAttributeValueForPoint( + target_att_id, target_i, src_att->GetAddressOfMappedIndex(source_i)); } } -StatusOr MeshSplitter::FinalizeMeshes( +} // namespace + +template <> +StatusOr +MeshSplitterInternal::BuildMeshes( const Mesh &mesh, WorkData *work_data) const { - // Finalize meshes. - const int num_out_meshes = work_data->mesh_builders.size(); - MeshVector out_meshes(num_out_meshes); + const int num_out_meshes = work_data->builders.size(); + MeshSplitter::MeshVector out_meshes(num_out_meshes); + for (int mi = 0; mi < num_out_meshes; ++mi) { + if (work_data->num_sub_mesh_elements[mi] == 0) { + continue; + } + out_meshes[mi] = work_data->builders[mi].Finalize(); + if (out_meshes[mi] == nullptr) { + continue; + } + } + return out_meshes; +} + +template <> +StatusOr +MeshSplitterInternal::BuildMeshes( + const Mesh &mesh, WorkData *work_data) const { + const int num_out_meshes = work_data->builders.size(); + MeshSplitter::MeshVector out_meshes(num_out_meshes); for (int mi = 0; mi < num_out_meshes; ++mi) { - if (work_data->num_sub_mesh_faces[mi] == 0) { + if (work_data->num_sub_mesh_elements[mi] == 0) { + continue; + } + // For point clouds, we first build a point cloud and copy it over into + // a draco::Mesh. + std::unique_ptr pc = work_data->builders[mi].Finalize(true); + if (pc == nullptr) { continue; } - out_meshes[mi] = work_data->mesh_builders[mi].Finalize(); + std::unique_ptr mesh(new Mesh()); + PointCloud *mesh_pc = mesh.get(); + mesh_pc->Copy(*pc); + out_meshes[mi] = std::move(mesh); + } + return out_meshes; +} + +StatusOr MeshSplitter::FinalizeMeshes( + const Mesh &mesh, const WorkData &work_data, MeshVector out_meshes) const { + // Finalize meshes. + const int num_out_meshes = out_meshes.size(); + + // If we are going to preserve mesh features, we will need to update texture + // pointers for all mesh feature textures. Here we store the mapping between + // the old texture pointers and their indices. + std::unordered_map features_texture_to_index_map; + if (preserve_mesh_features_) { + features_texture_to_index_map = + mesh.GetNonMaterialTextureLibrary().ComputeTextureToIndexMap(); + } + + for (int mi = 0; mi < num_out_meshes; ++mi) { if (out_meshes[mi] == nullptr) { continue; } + out_meshes[mi]->SetName(mesh.GetName()); if (preserve_materials_) { out_meshes[mi]->GetMaterialLibrary().Copy(mesh.GetMaterialLibrary()); - out_meshes[mi]->RemoveUnusedMaterials(); } // Copy metadata of the original mesh to the output meshes. if (mesh.GetMetadata() != nullptr) { - const Metadata &metadata = *mesh.GetMetadata(); + const GeometryMetadata &metadata = *mesh.GetMetadata(); out_meshes[mi]->AddMetadata( std::unique_ptr(new GeometryMetadata(metadata))); } // Copy over attribute unique ids. for (int att_id = 0; att_id < mesh.num_attributes(); ++att_id) { - const int mapped_att_id = work_data->att_id_map[att_id]; + const int mapped_att_id = work_data.att_id_map[att_id]; if (mapped_att_id == -1) { continue; } @@ -191,9 +335,117 @@ // Copy compression settings of the original mesh to the output meshes. out_meshes[mi]->SetCompressionEnabled(mesh.IsCompressionEnabled()); out_meshes[mi]->SetCompressionOptions(mesh.GetCompressionOptions()); + + if (preserve_mesh_features_) { + // Copy mesh features from the source |mesh| to the |out_meshes[mi]|. + for (MeshFeaturesIndex mfi(0); mfi < mesh.NumMeshFeatures(); ++mfi) { + if (work_data.split_by_materials) { + // Copy over only those mesh features that were masked to the material + // corresponding to |mi|. + bool is_used = false; + if (mesh.NumMeshFeaturesMaterialMasks(mfi) == 0) { + is_used = true; + } else { + for (int mask_index = 0; + mask_index < mesh.NumMeshFeaturesMaterialMasks(mfi); + ++mask_index) { + if (mesh.GetMeshFeaturesMaterialMask(mfi, mask_index) == mi) { + is_used = true; + break; + } + } + } + if (!is_used) { + // Ignore this mesh features. + continue; + } + } + // Create a copy of source mesh features. + std::unique_ptr mf(new MeshFeatures()); + mf->Copy(mesh.GetMeshFeatures(mfi)); + const MeshFeaturesIndex new_mfi = + out_meshes[mi]->AddMeshFeatures(std::move(mf)); + if (work_data.split_by_materials && !preserve_materials_) { + // If the input |mesh| was split by materials and we didn't preserve + // the materials, all mesh features must be masked to material 0. + out_meshes[mi]->AddMeshFeaturesMaterialMask(new_mfi, 0); + } else { + // Otherwise mesh features use same masking as the source mesh because + // the material attribute is still present in the split meshes. + // Note that this masking can be later changed in + // RemoveUnusedMaterials() call below. + for (int mask_index = 0; + mask_index < mesh.NumMeshFeaturesMaterialMasks(mfi); + ++mask_index) { + out_meshes[mi]->AddMeshFeaturesMaterialMask( + new_mfi, mesh.GetMeshFeaturesMaterialMask(mfi, mask_index)); + } + } + } + + // Copy over all features textures to the split mesh. + out_meshes[mi]->GetNonMaterialTextureLibrary().Copy( + mesh.GetNonMaterialTextureLibrary()); + + // Update mesh features texture pointers to the new library. + for (MeshFeaturesIndex mfi(0); mfi < out_meshes[mi]->NumMeshFeatures(); + ++mfi) { + Mesh::UpdateMeshFeaturesTexturePointer( + features_texture_to_index_map, + &out_meshes[mi]->GetNonMaterialTextureLibrary(), + &out_meshes[mi]->GetMeshFeatures(mfi)); + } + + // This will remove any mesh features that may not be be actually used + // by this |out_meshes[mi]| (e.g. because corresponding material indices + // were not present in this split mesh). This also removes any unused + // features textures from the non-material texture library. + DRACO_RETURN_IF_ERROR( + MeshUtils::RemoveUnusedMeshFeatures(out_meshes[mi].get())); + } + + // Remove unused materials after we remove mesh features because some of + // the mesh features may have referenced old material indices. + if (preserve_materials_) { + out_meshes[mi]->RemoveUnusedMaterials(remove_unused_material_indices_); + } + + // Copy structural metadata from input mesh to each of the output meshes. + out_meshes[mi]->GetStructuralMetadata().Copy(mesh.GetStructuralMetadata()); } return std::move(out_meshes); } +StatusOr MeshSplitter::SplitMeshToComponents( + const Mesh &mesh, const MeshConnectedComponents &connected_components) { + // Create the sub-meshes. + const int num_out_meshes = connected_components.NumConnectedComponents(); + MeshSplitterInternal splitter_internal; + typename MeshSplitterInternal::WorkData work_data; + work_data.builders.resize(num_out_meshes); + work_data.num_sub_mesh_elements.resize(num_out_meshes, 0); + work_data.att_id_map.resize(mesh.num_attributes(), -1); + for (int mi = 0; mi < num_out_meshes; ++mi) { + const int num_faces = connected_components.NumConnectedComponentFaces(mi); + work_data.num_sub_mesh_elements[mi] = num_faces; + splitter_internal.InitializeBuilder(mi, num_faces, mesh, -1, &work_data); + } + + // Go over all faces of the input mesh and add them to the appropriate + // sub-mesh. + for (int mi = 0; mi < num_out_meshes; ++mi) { + for (int cfi = 0; cfi < connected_components.NumConnectedComponentFaces(mi); + ++cfi) { + const FaceIndex fi( + connected_components.GetConnectedComponent(mi).faces[cfi]); + const FaceIndex target_fi(cfi); + AddElementToBuilder(mi, fi, target_fi, mesh, &work_data); + } + } + DRACO_ASSIGN_OR_RETURN(auto out_meshes, + splitter_internal.BuildMeshes(mesh, &work_data)); + return FinalizeMeshes(mesh, work_data, std::move(out_meshes)); +} + } // namespace draco #endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_splitter.h draco-1.5.5+dfsg/src/draco/mesh/mesh_splitter.h --- draco-1.5.3+dfsg/src/draco/mesh/mesh_splitter.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_splitter.h 2022-10-29 00:55:03.000000000 +0000 @@ -15,6 +15,9 @@ #ifndef DRACO_MESH_MESH_SPLITTER_H_ #define DRACO_MESH_MESH_SPLITTER_H_ +#include +#include + #include "draco/draco_features.h" #ifdef DRACO_TRANSCODER_SUPPORTED @@ -35,16 +38,38 @@ // Sets a flag that tells the splitter to preserve all materials on the input // mesh during mesh splitting. When set, the materials used on sub-meshes are // going to be copied over. Any redundant materials on sub-meshes are going to - // be deleted. + // be deleted but material indices may still be preserved depending on the + // SetRemoveUnusedMaterialIndices() flag. // Default = false. void SetPreserveMaterials(bool flag) { preserve_materials_ = flag; } + // Sets a flag that tells the splitter to delete any unused material indices + // on the generated sub-meshes. This option is currently used only when + // SetPreserveMaterials() was set to true. If this option is set to false, the + // material indices of the MATERIAL attribute will be the same as in the + // source mesh. If the flag is true, then the unused material indices will be + // removed and they may no longer correspond to the source mesh. Note that + // when this flag is false, any unused materials would be replaced with empty + // (default) materials. + // Default = true. + void SetRemoveUnusedMaterialIndices(bool flag) { + remove_unused_material_indices_ = flag; + } + + // Sets a flag that tells the splitter to preserve all mesh features on the + // input mesh during mesh splitting. When set, the mesh features used on + // sub-meshes are going to be copied over. Any redundant mesh features on + // sub-meshes are going to be deleted. + // Default = false. + void SetPreserveMeshFeatures(bool flag) { preserve_mesh_features_ = flag; } + // Splits the input |mesh| according to attribute values stored in the - // specified attribute. The attribute values need to be defined per-face, that - // is, all points attached to a single face must share the same attribute - // value. Each attribute value (AttributeValueIndex) is mapped to a single - // output mesh. If an AttributeValueIndex is unused, no mesh is created for - // the given value. + // specified attribute. If the |mesh| contains faces, the attribute values + // need to be defined per-face, that is, all points attached to a single face + // must share the same attribute value. Meshes without faces are treated as + // point clouds and the attribute values can be defined per-point. Each + // attribute value (AttributeValueIndex) is mapped to a single output mesh. If + // an AttributeValueIndex is unused, no mesh is created for the given value. StatusOr SplitMesh(const Mesh &mesh, uint32_t split_attribute_id); // Splits the input |mesh| into separate components defined in @@ -58,20 +83,24 @@ struct WorkData { // Map between attribute ids of the input and output meshes. std::vector att_id_map; - std::vector num_sub_mesh_faces; - std::vector mesh_builders; + std::vector num_sub_mesh_elements; + bool split_by_materials = false; }; - void InitializeMeshBuilder(int mb_index, int num_faces, const Mesh &mesh, - int ignored_attribute_id, - WorkData *work_data) const; - void AddFaceToMeshBuilder(int mb_index, FaceIndex source_fi, - FaceIndex target_fi, const Mesh &mesh, - WorkData *work_data) const; + template + StatusOr SplitMeshInternal(const Mesh &mesh, + int split_attribute_id); + StatusOr FinalizeMeshes(const Mesh &mesh, - WorkData *work_data) const; + const WorkData &work_data, + MeshVector out_meshes) const; bool preserve_materials_; + bool remove_unused_material_indices_; + bool preserve_mesh_features_; + + template + friend class MeshSplitterInternal; }; } // namespace draco diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_splitter_test.cc draco-1.5.5+dfsg/src/draco/mesh/mesh_splitter_test.cc --- draco-1.5.3+dfsg/src/draco/mesh/mesh_splitter_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_splitter_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -15,6 +15,9 @@ #include "draco/mesh/mesh_splitter.h" #ifdef DRACO_TRANSCODER_SUPPORTED +#include +#include + #include "draco/core/draco_test_base.h" #include "draco/core/draco_test_utils.h" #include "draco/core/vector_d.h" diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_test.cc draco-1.5.5+dfsg/src/draco/mesh/mesh_test.cc --- draco-1.5.3+dfsg/src/draco/mesh/mesh_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -15,13 +15,17 @@ #include "draco/mesh/mesh.h" #include +#include #include "draco/core/draco_test_base.h" #include "draco/core/draco_test_utils.h" #ifdef DRACO_TRANSCODER_SUPPORTED #include "draco/compression/draco_compression_options.h" +#include "draco/material/material_utils.h" #include "draco/mesh/mesh_are_equivalent.h" +#include "draco/mesh/mesh_features.h" +#include "draco/mesh/mesh_utils.h" #include "draco/mesh/triangle_soup_mesh_builder.h" #endif // DRACO_TRANSCODER_SUPPORTED @@ -108,6 +112,92 @@ } } +TEST(MeshTest, RemoveUnusedMaterialsOnPointClud) { + // Input mesh has 29 materials defined in the source file but only 7 are + // actually used. Same as above test but we remove all faces and treat the + // model as a point cloud. + const std::unique_ptr mesh = + draco::ReadMeshFromTestFile("mat_test.obj"); + ASSERT_NE(mesh, nullptr); + + // Make it a point cloud. + mesh->SetNumFaces(0); + + const draco::PointAttribute *const mat_att = + mesh->GetNamedAttribute(draco::GeometryAttribute::MATERIAL); + ASSERT_NE(mat_att, nullptr); + ASSERT_EQ(mat_att->size(), 29); + + ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), mat_att->size()); + + // Get materials on all points. + std::vector point_materials(mesh->num_points(), + nullptr); + for (draco::PointIndex pi(0); pi < mesh->num_points(); ++pi) { + uint32_t mat_index = 0; + mat_att->GetMappedValue(pi, &mat_index); + point_materials[pi.value()] = + mesh->GetMaterialLibrary().GetMaterial(mat_index); + } + + mesh->RemoveUnusedMaterials(); + + ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 7); + + // Ensure the material attribute contains material indices in the valid range. + for (draco::AttributeValueIndex avi(0); avi < mat_att->size(); ++avi) { + uint32_t mat_index = 0; + mat_att->GetValue(avi, &mat_index); + ASSERT_LT(mat_index, mesh->GetMaterialLibrary().NumMaterials()); + } + + // Ensure all materials are still the same for all points. + for (draco::PointIndex pi(0); pi < mesh->num_points(); ++pi) { + uint32_t mat_index = 0; + mat_att->GetMappedValue(pi, &mat_index); + ASSERT_EQ(mesh->GetMaterialLibrary().GetMaterial(mat_index), + point_materials[pi.value()]); + } +} + +TEST(MeshTest, RemoveUnusedMaterialsNoIndices) { + // The same as above but we actually want to remove only materials and not + // material indices. Therefore we should end up with the same number of + // materials as source but all unused materials should be "default". + const std::unique_ptr mesh = + draco::ReadMeshFromTestFile("mat_test.obj"); + ASSERT_NE(mesh, nullptr); + + const draco::PointAttribute *const mat_att = + mesh->GetNamedAttribute(draco::GeometryAttribute::MATERIAL); + ASSERT_NE(mat_att, nullptr); + ASSERT_EQ(mat_att->size(), 29); + + ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), mat_att->size()); + + // Do not remove unused material indices. + mesh->RemoveUnusedMaterials(false); + + ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 29); + + // Gether which materials were actually used and check that all remaining + // materials are "default". + std::vector is_mat_used(mesh->GetMaterialLibrary().NumMaterials(), + false); + for (draco::AttributeValueIndex avi(0); avi < mat_att->size(); ++avi) { + uint32_t mat_index = 0; + mat_att->GetValue(avi, &mat_index); + is_mat_used[mat_index] = true; + } + + for (int mi = 0; mi < mesh->GetMaterialLibrary().NumMaterials(); ++mi) { + if (!is_mat_used[mi]) { + ASSERT_TRUE(draco::MaterialUtils::AreMaterialsEquivalent( + *mesh->GetMaterialLibrary().GetMaterial(mi), draco::Material())); + } + } +} + TEST(MeshTest, TestAddNewAttributeWithConnectivity) { // Tests that we can add new attributes with arbitrary connectivity to an // existing mesh. @@ -346,6 +436,189 @@ ASSERT_TRUE(mesh_copy.IsCompressionEnabled()); ASSERT_EQ(mesh_copy.GetCompressionOptions(), compression_options); } + +// Tests adding and removing of mesh features to a mesh. +TEST(MeshTest, TestMeshFeatures) { + // Create a mesh with two feature ID sets. + draco::Mesh mesh; + ASSERT_EQ(mesh.NumMeshFeatures(), 0); + std::unique_ptr oceans(new draco::MeshFeatures()); + std::unique_ptr continents(new draco::MeshFeatures()); + oceans->SetLabel("oceans"); + continents->SetLabel("continents"); + const draco::MeshFeaturesIndex index_0 = + mesh.AddMeshFeatures(std::move(oceans)); + const draco::MeshFeaturesIndex index_1 = + mesh.AddMeshFeatures(std::move(continents)); + ASSERT_EQ(index_0, draco::MeshFeaturesIndex(0)); + ASSERT_EQ(index_1, draco::MeshFeaturesIndex(1)); + + // Check that the mesh has two feature ID sets. + ASSERT_EQ(mesh.NumMeshFeatures(), 2); + ASSERT_EQ(mesh.GetMeshFeatures(index_0).GetLabel(), "oceans"); + ASSERT_EQ(mesh.GetMeshFeatures(index_1).GetLabel(), "continents"); + + // Remove one feature ID set and check the remaining feature ID set. + mesh.RemoveMeshFeatures(draco::MeshFeaturesIndex(1)); + ASSERT_EQ(mesh.NumMeshFeatures(), 1); + ASSERT_EQ(mesh.GetMeshFeatures(draco::MeshFeaturesIndex(0)).GetLabel(), + "oceans"); + + // Remove the remaining feature ID set and check that no sets remain. + mesh.RemoveMeshFeatures(draco::MeshFeaturesIndex(0)); + ASSERT_EQ(mesh.NumMeshFeatures(), 0); +} + +// Tests copying of a mesh with feature ID sets. +TEST(MeshTest, MeshCopyWithMeshFeatures) { + const std::unique_ptr mesh = + draco::ReadMeshFromTestFile("cube_att.obj"); + ASSERT_NE(mesh, nullptr); + + // Add two textures to the non-material texture library of the mesh. + std::unique_ptr texture0(new draco::Texture()); + std::unique_ptr texture1(new draco::Texture()); + texture0->Resize(128, 128); + texture1->Resize(256, 256); + texture0->FillImage(draco::RGBA(100, 0, 0, 0)); + texture1->FillImage(draco::RGBA(200, 0, 0, 0)); + draco::TextureLibrary &library = mesh->GetNonMaterialTextureLibrary(); + library.PushTexture(std::move(texture0)); + library.PushTexture(std::move(texture1)); + + // Add feature ID set referring to an attribute. + const draco::MeshFeaturesIndex index_0 = mesh->AddMeshFeatures( + std::unique_ptr(new draco::MeshFeatures())); + mesh->GetMeshFeatures(index_0).SetLabel("planet"); + mesh->GetMeshFeatures(index_0).SetFeatureCount(2); + mesh->GetMeshFeatures(index_0).SetAttributeIndex(1); + + // Add feature ID set referring to texture at index 0. + const draco::MeshFeaturesIndex index_1 = mesh->AddMeshFeatures( + std::unique_ptr(new draco::MeshFeatures())); + mesh->GetMeshFeatures(index_1).SetLabel("continents"); + mesh->GetMeshFeatures(index_1).SetFeatureCount(7); + mesh->GetMeshFeatures(index_1).GetTextureMap().SetTexture( + library.GetTexture(0)); + + // Add feature ID set referring to a texture at index 1. + const draco::MeshFeaturesIndex index_2 = mesh->AddMeshFeatures( + std::unique_ptr(new draco::MeshFeatures())); + mesh->GetMeshFeatures(index_2).SetLabel("oceans"); + mesh->GetMeshFeatures(index_2).SetFeatureCount(5); + mesh->GetMeshFeatures(index_2).GetTextureMap().SetTexture( + library.GetTexture(1)); + + // Check mesh feature ID set texture pointers. + ASSERT_EQ(library.NumTextures(), 2); + ASSERT_EQ(mesh->NumMeshFeatures(), 3); + ASSERT_EQ(mesh->GetMeshFeatures(index_0).GetTextureMap().texture(), nullptr); + ASSERT_EQ(mesh->GetMeshFeatures(index_1).GetTextureMap().texture(), + library.GetTexture(0)); + ASSERT_EQ(mesh->GetMeshFeatures(index_2).GetTextureMap().texture(), + library.GetTexture(1)); + + // Copy the mesh. + draco::Mesh mesh_copy; + mesh_copy.Copy(*mesh); + + // Check that the meshes are equivalent. + draco::MeshAreEquivalent eq; + ASSERT_TRUE(eq(*mesh, mesh_copy)); + + // Also check that the texture pointers have been updated correctly. + const draco::TextureLibrary &library_copy = + mesh_copy.GetNonMaterialTextureLibrary(); + ASSERT_EQ(library_copy.NumTextures(), 2); + ASSERT_EQ(mesh_copy.NumMeshFeatures(), 3); + ASSERT_EQ(mesh_copy.GetMeshFeatures(index_0).GetTextureMap().texture(), + nullptr); + ASSERT_EQ(mesh_copy.GetMeshFeatures(index_1).GetTextureMap().texture(), + library_copy.GetTexture(0)); + ASSERT_EQ(mesh_copy.GetMeshFeatures(index_2).GetTextureMap().texture(), + library_copy.GetTexture(1)); +} + +// Tests copying of a mesh with structural metadata. +TEST(MeshTest, TestCopyWithStructuralMetadata) { + const std::unique_ptr mesh = + draco::ReadMeshFromTestFile("cube_att.obj"); + ASSERT_NE(mesh, nullptr); + + // Add structural metadata to the mesh. + draco::PropertyTable::Schema schema; + schema.json.SetString("Data"); + mesh->GetStructuralMetadata().SetPropertyTableSchema(schema); + + // Copy the mesh. + draco::Mesh copy; + copy.Copy(*mesh); + + // Check that the structural metadata has been copied. + ASSERT_EQ( + copy.GetStructuralMetadata().GetPropertyTableSchema().json.GetString(), + "Data"); +} + +// Tests removing of unused materials for a mesh with mesh features. +TEST(MeshTest, RemoveUnusedMaterialsWithMeshFeatures) { + const std::unique_ptr mesh = + draco::ReadMeshFromTestFile("BoxesMeta/glTF/BoxesMeta.gltf"); + ASSERT_NE(mesh, nullptr); + + // Input has five mesh features, two associated with material 0 and three with + // material 1. + ASSERT_EQ(mesh->NumMeshFeatures(), 5); + ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(0), 0), + 0); + ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(1), 0), + 0); + ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(2), 0), + 1); + ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(3), 0), + 1); + ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(4), 0), + 1); + + // Remove material 0. + draco::PointAttribute *mat_att = mesh->attribute( + mesh->GetNamedAttributeId(draco::GeometryAttribute::MATERIAL)); + // Map mat value 0 to 1. + uint32_t new_mat_index = 1; + mat_att->SetAttributeValue(draco::AttributeValueIndex(0), &new_mat_index); + + // This should not do anything because we still have the material 0 referenced + // by mesh features 0 and 1. + mesh->RemoveUnusedMaterials(); + + ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 2); + ASSERT_EQ(mesh->NumMeshFeatures(), 5); + + // Now remove unused mesh features (should be 0 and 1). + DRACO_ASSERT_OK(draco::MeshUtils::RemoveUnusedMeshFeatures(mesh.get())); + + ASSERT_EQ(mesh->NumMeshFeatures(), 3); + // All remaining mesh features should be still mapped to material 1. + ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(0), 0), + 1); + ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(1), 0), + 1); + ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(2), 0), + 1); + + // Now remove the unused materials (0). + mesh->RemoveUnusedMaterials(); + + // Only one material should be remaining and all the mesh features should now + // be mapped to material 0. + ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 1); + ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(0), 0), + 0); + ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(1), 0), + 0); + ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(2), 0), + 0); +} #endif // DRACO_TRANSCODER_SUPPORTED // Test bounding box. diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_utils.cc draco-1.5.5+dfsg/src/draco/mesh/mesh_utils.cc --- draco-1.5.3+dfsg/src/draco/mesh/mesh_utils.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_utils.cc 2022-10-29 00:55:03.000000000 +0000 @@ -14,6 +14,10 @@ // #include "draco/mesh/mesh_utils.h" +#include +#include +#include + #ifdef DRACO_TRANSCODER_SUPPORTED #include "draco/attributes/attribute_quantization_transform.h" #include "draco/core/quantization_utils.h" @@ -163,6 +167,86 @@ } } +Status MeshUtils::RemoveUnusedMeshFeatures(Mesh *mesh) { + // Unused mesh features are features that are not used by any face / vertex + // of the |mesh|. Currently, each mesh feature can be "masked" for specific + // materials, in which case we need to check whether the mask materials + // are present in the |mesh|. If not, we can remove the mesh features from the + // mesh. + const PointAttribute *const mat_att = + mesh->GetNamedAttribute(GeometryAttribute::MATERIAL); + // Find which materials are used. + std::unordered_set used_materials; + if (mat_att == nullptr) { + // Only material with index 0 is assumed to be used. + used_materials.insert(0); + } else { + for (AttributeValueIndex avi(0); avi < mat_att->size(); ++avi) { + uint32_t mat_index = 0; + mat_att->GetValue(avi, &mat_index); + used_materials.insert(mat_index); + } + } + + std::vector unused_mesh_features; + for (MeshFeaturesIndex mfi(0); mfi < mesh->NumMeshFeatures(); ++mfi) { + bool is_used = false; + if (mesh->NumMeshFeaturesMaterialMasks(mfi) == 0) { + is_used = true; + } else { + for (int mask_i = 0; mask_i < mesh->NumMeshFeaturesMaterialMasks(mfi); + ++mask_i) { + const int material_index = + mesh->GetMeshFeaturesMaterialMask(mfi, mask_i); + if (used_materials.count(material_index)) { + is_used = true; + break; + } + } + } + if (!is_used) { + unused_mesh_features.push_back(mfi); + } + } + + // Remove the unused mesh features (from back). + for (auto it = unused_mesh_features.rbegin(); + it != unused_mesh_features.rend(); ++it) { + const MeshFeaturesIndex mfi = *it; + mesh->RemoveMeshFeatures(mfi); + } + + // Remove all features textures that are not used anymore. + + // First find which textures are referenced by the mesh features. + std::unordered_set used_textures; + for (MeshFeaturesIndex mfi(0); mfi < mesh->NumMeshFeatures(); ++mfi) { + const Texture *const texture = + mesh->GetMeshFeatures(mfi).GetTextureMap().texture(); + if (texture) { + used_textures.insert(texture); + } + } + + if (!used_textures.empty() && + mesh->GetNonMaterialTextureLibrary().NumTextures() == 0) { + return ErrorStatus( + "Trying to remove mesh features textures that are not owned by the " + "mesh."); + } + + // Remove all unreferenced textures from the non-material texture library. + for (int ti = mesh->GetNonMaterialTextureLibrary().NumTextures() - 1; ti >= 0; + --ti) { + const Texture *const texture = + mesh->GetNonMaterialTextureLibrary().GetTexture(ti); + if (used_textures.count(texture) == 0) { + mesh->GetNonMaterialTextureLibrary().RemoveTexture(ti); + } + } + return OkStatus(); +} + bool MeshUtils::FlipTextureUvValues(bool flip_u, bool flip_v, PointAttribute *att) { if (att->attribute_type() != GeometryAttribute::TEX_COORD) { diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_utils.h draco-1.5.5+dfsg/src/draco/mesh/mesh_utils.h --- draco-1.5.3+dfsg/src/draco/mesh/mesh_utils.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_utils.h 2022-10-29 00:55:03.000000000 +0000 @@ -38,6 +38,11 @@ // names are left unchanged. static void MergeMetadata(const Mesh &src_mesh, Mesh *dst_mesh); + // Removes unused MeshFeatures from |mesh|. If the |mesh| contains any mesh + // feature textures, the textures must be owned by the |mesh| otherwise an + // error is returned. + static Status RemoveUnusedMeshFeatures(Mesh *mesh); + // Flips the UV values of |att|. static bool FlipTextureUvValues(bool flip_u, bool flip_v, PointAttribute *att); diff -Nru draco-1.5.3+dfsg/src/draco/mesh/mesh_utils_test.cc draco-1.5.5+dfsg/src/draco/mesh/mesh_utils_test.cc --- draco-1.5.3+dfsg/src/draco/mesh/mesh_utils_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/mesh_utils_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -347,6 +347,45 @@ ASSERT_EQ(metadata_value, 3); } +TEST(MeshUtilsTest, RemoveUnusedMeshFeatures) { + // Test verifies that MeshUtils::RemoveUnusedMeshFeatures works as intended. + std::unique_ptr mesh = + draco::ReadMeshFromTestFile("BoxesMeta/glTF/BoxesMeta.gltf"); + ASSERT_NE(mesh, nullptr); + + // The input mesh should have five mesh features and two features textures. + ASSERT_EQ(mesh->NumMeshFeatures(), 5); + ASSERT_EQ(mesh->GetNonMaterialTextureLibrary().NumTextures(), 2); + + // All of those features and textures should be used so calling the method + // below shouldn't do anything. + draco::MeshUtils::RemoveUnusedMeshFeatures(mesh.get()); + ASSERT_EQ(mesh->NumMeshFeatures(), 5); + ASSERT_EQ(mesh->GetNonMaterialTextureLibrary().NumTextures(), 2); + + // Now remove material 1 that is mapped to first two mesh features. + draco::PointAttribute *mat_att = mesh->attribute( + mesh->GetNamedAttributeId(draco::GeometryAttribute::MATERIAL)); + + // This basically remaps all faces from material 1 to material 0. + uint32_t mat_index = 0; + mat_att->SetAttributeValue(draco::AttributeValueIndex(1), &mat_index); + + // Try to remove the mesh features again. + draco::MeshUtils::RemoveUnusedMeshFeatures(mesh.get()); + + // Three of the mesh features should have been removed as well as one mesh + // features texture. + ASSERT_EQ(mesh->NumMeshFeatures(), 2); + ASSERT_EQ(mesh->GetNonMaterialTextureLibrary().NumTextures(), 1); + + // Ensure the remaining mesh features are mapped to the correct material. + for (draco::MeshFeaturesIndex mfi(0); mfi < mesh->NumMeshFeatures(); ++mfi) { + ASSERT_EQ(mesh->NumMeshFeaturesMaterialMasks(mfi), 1); + ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(mfi, 0), 0); + } +} + } // namespace #endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/mesh/triangle_soup_mesh_builder.cc draco-1.5.5+dfsg/src/draco/mesh/triangle_soup_mesh_builder.cc --- draco-1.5.3+dfsg/src/draco/mesh/triangle_soup_mesh_builder.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/triangle_soup_mesh_builder.cc 2022-10-29 00:55:03.000000000 +0000 @@ -32,8 +32,14 @@ int TriangleSoupMeshBuilder::AddAttribute( GeometryAttribute::Type attribute_type, int8_t num_components, DataType data_type) { + return AddAttribute(attribute_type, num_components, data_type, false); +} + +int TriangleSoupMeshBuilder::AddAttribute( + GeometryAttribute::Type attribute_type, int8_t num_components, + DataType data_type, bool normalized) { GeometryAttribute va; - va.Init(attribute_type, nullptr, num_components, data_type, false, + va.Init(attribute_type, nullptr, num_components, data_type, normalized, DataTypeLength(data_type) * num_components, 0); attribute_element_types_.push_back(-1); return mesh_->AddAttribute(va, true, mesh_->num_points()); diff -Nru draco-1.5.3+dfsg/src/draco/mesh/triangle_soup_mesh_builder.h draco-1.5.5+dfsg/src/draco/mesh/triangle_soup_mesh_builder.h --- draco-1.5.3+dfsg/src/draco/mesh/triangle_soup_mesh_builder.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/triangle_soup_mesh_builder.h 2022-10-29 00:55:03.000000000 +0000 @@ -16,8 +16,13 @@ #define DRACO_MESH_TRIANGLE_SOUP_MESH_BUILDER_H_ #include +#include #include "draco/draco_features.h" + +#ifdef DRACO_TRANSCODER_SUPPORTED +#include "draco/core/status.h" +#endif #include "draco/mesh/mesh.h" namespace draco { @@ -27,6 +32,9 @@ // deduplicated. class TriangleSoupMeshBuilder { public: + // Index type of the inserted element. + typedef FaceIndex ElementIndex; + // Starts mesh building for a given number of faces. // TODO(ostava): Currently it's necessary to select the correct number of // faces upfront. This should be generalized, but it will require us to @@ -41,6 +49,8 @@ // Adds an empty attribute to the mesh. Returns the new attribute's id. int AddAttribute(GeometryAttribute::Type attribute_type, int8_t num_components, DataType data_type); + int AddAttribute(GeometryAttribute::Type attribute_type, + int8_t num_components, DataType data_type, bool normalized); // Sets values for a given attribute on all corners of a given face. void SetAttributeValuesForFace(int att_id, FaceIndex face_id, @@ -48,6 +58,17 @@ const void *corner_value_1, const void *corner_value_2); +#ifdef DRACO_TRANSCODER_SUPPORTED + // Converts input values of type T into internal representation used by + // |att_id|. Each input value needs to have |input_num_components| entries. + template + Status ConvertAndSetAttributeValuesForFace(int att_id, FaceIndex face_id, + int input_num_components, + const T *corner_value_0, + const T *corner_value_1, + const T *corner_value_2); +#endif + // Sets value for a per-face attribute. If all faces of a given attribute are // set with this method, the attribute will be marked as per-face, otherwise // it will be marked as per-corner attribute. @@ -76,6 +97,30 @@ std::unique_ptr mesh_; }; +#ifdef DRACO_TRANSCODER_SUPPORTED +template +Status TriangleSoupMeshBuilder::ConvertAndSetAttributeValuesForFace( + int att_id, FaceIndex face_id, int input_num_components, + const T *corner_value_0, const T *corner_value_1, const T *corner_value_2) { + const int start_index = 3 * face_id.value(); + PointAttribute *const att = mesh_->attribute(att_id); + DRACO_RETURN_IF_ERROR( + att->ConvertAndSetAttributeValue(AttributeValueIndex(start_index + 0), + input_num_components, corner_value_0)); + DRACO_RETURN_IF_ERROR( + att->ConvertAndSetAttributeValue(AttributeValueIndex(start_index + 1), + input_num_components, corner_value_1)); + DRACO_RETURN_IF_ERROR( + att->ConvertAndSetAttributeValue(AttributeValueIndex(start_index + 2), + input_num_components, corner_value_2)); + mesh_->SetFace(face_id, + {{PointIndex(start_index), PointIndex(start_index + 1), + PointIndex(start_index + 2)}}); + attribute_element_types_[att_id] = MESH_CORNER_ATTRIBUTE; + return OkStatus(); +} +#endif // DRACO_TRANSCODER_SUPPORTED + } // namespace draco #endif // DRACO_MESH_TRIANGLE_SOUP_MESH_BUILDER_H_ diff -Nru draco-1.5.3+dfsg/src/draco/mesh/triangle_soup_mesh_builder_test.cc draco-1.5.5+dfsg/src/draco/mesh/triangle_soup_mesh_builder_test.cc --- draco-1.5.3+dfsg/src/draco/mesh/triangle_soup_mesh_builder_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/mesh/triangle_soup_mesh_builder_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -14,7 +14,11 @@ // #include "draco/mesh/triangle_soup_mesh_builder.h" +#include +#include + #include "draco/core/draco_test_base.h" +#include "draco/core/draco_test_utils.h" #include "draco/core/vector_d.h" namespace draco { @@ -145,7 +149,7 @@ Vector3f(0.f, 1.f, 0.f).data(), Vector3f(1.f, 1.f, 0.f).data(), Vector3f(0.f, 1.f, 1.f).data()); - mb.SetPerFaceAttributeValueForFace(gen_att_id, FaceIndex(4), &bool_false);; + mb.SetPerFaceAttributeValueForFace(gen_att_id, FaceIndex(4), &bool_false); mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(5), Vector3f(0.f, 1.f, 1.f).data(), @@ -203,4 +207,61 @@ << "Unexpected attribute element type."; } +#ifdef DRACO_TRANSCODER_SUPPORTED +TEST_F(TriangleSoupMeshBuilderTest, NormalizedColor) { + // This tests, verifies that the mesh builder constructs a valid model with + // normalized integer colors using floating points as input. + TriangleSoupMeshBuilder mb; + mb.Start(2); + const int pos_att_id = + mb.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32); + const int color_att_id = + mb.AddAttribute(GeometryAttribute::COLOR, 3, DT_UINT8, true); + + mb.SetAttributeValuesForFace( + pos_att_id, FaceIndex(0), Vector3f(0.f, 0.f, 0.f).data(), + Vector3f(1.f, 0.f, 0.f).data(), Vector3f(0.f, 1.f, 0.f).data()); + DRACO_ASSERT_OK(mb.ConvertAndSetAttributeValuesForFace( + color_att_id, FaceIndex(0), 4, Vector4f(0.f, 0.f, 0.f, 1.f).data(), + Vector4f(1.f, 1.f, 1.f, 1.f).data(), + Vector4f(0.5f, 0.5f, 0.5f, 1.f).data())); + mb.SetAttributeValuesForFace( + pos_att_id, FaceIndex(1), Vector3f(0.f, 1.f, 0.f).data(), + Vector3f(1.f, 0.f, 0.f).data(), Vector3f(1.f, 1.f, 0.f).data()); + + DRACO_ASSERT_OK(mb.ConvertAndSetAttributeValuesForFace( + color_att_id, FaceIndex(1), 4, Vector4f(0.5f, 0.5f, 0.5f, 1.f).data(), + Vector4f(1.f, 1.f, 1.f, 1.f).data(), + Vector4f(0.25f, 0.0f, 1.f, 1.f).data())); + + std::unique_ptr mesh = mb.Finalize(); + ASSERT_NE(mesh, nullptr) << "Failed to build the test mesh."; + + EXPECT_EQ(mesh->num_points(), 4) << "Unexpected number of vertices."; + EXPECT_EQ(mesh->num_faces(), 2) << "Unexpected number of faces."; + + const auto *col_att = + mesh->GetNamedAttribute(draco::GeometryAttribute::COLOR); + ASSERT_NE(col_att, nullptr) << "Missing color attribute."; + ASSERT_EQ(col_att->size(), 4); + + // All colors should be in range 0-255. + uint8_t max_val = 0, min_val = 255; + for (draco::AttributeValueIndex avi(0); avi < col_att->size(); ++avi) { + VectorD cval; + col_att->GetValue(avi, &cval); + const uint8_t max = cval.MaxCoeff(); + const uint8_t min = cval.MinCoeff(); + if (max > max_val) { + max_val = max; + } + if (min < min_val) { + min_val = min; + } + } + ASSERT_EQ(max_val, 255); + ASSERT_EQ(min_val, 0); +} +#endif + } // namespace draco diff -Nru draco-1.5.3+dfsg/src/draco/metadata/metadata_decoder.cc draco-1.5.5+dfsg/src/draco/metadata/metadata_decoder.cc --- draco-1.5.3+dfsg/src/draco/metadata/metadata_decoder.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/metadata/metadata_decoder.cc 2022-10-29 00:55:03.000000000 +0000 @@ -131,6 +131,9 @@ if (data_size == 0) { return false; } + if (data_size > buffer_->remaining_size()) { + return false; + } std::vector entry_value(data_size); if (!buffer_->Decode(&entry_value[0], data_size)) { return false; diff -Nru draco-1.5.3+dfsg/src/draco/metadata/property_table.cc draco-1.5.5+dfsg/src/draco/metadata/property_table.cc --- draco-1.5.3+dfsg/src/draco/metadata/property_table.cc 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/metadata/property_table.cc 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,183 @@ +// Copyright 2022 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "draco/metadata/property_table.h" + +#include +#include +#include +#include + +#ifdef DRACO_TRANSCODER_SUPPORTED + +namespace draco { + +bool PropertyTable::Schema::Object::operator==(const Object& other) const { + if (type_ != other.type_ || name_ != other.name_) { + return false; + } + switch (type_) { + case OBJECT: + if (objects_.size() != other.objects_.size()) { + return false; + } + for (int i = 0; i < objects_.size(); ++i) { + if (objects_[i] != other.objects_[i]) { + return false; + } + } + break; + case ARRAY: + if (array_.size() != other.array_.size()) { + return false; + } + for (int i = 0; i < array_.size(); ++i) { + if (array_[i] != other.array_[i]) { + return false; + } + } + break; + case STRING: + return string_ == other.string_; + case INTEGER: + return integer_ == other.integer_; + case BOOLEAN: + return boolean_ == other.boolean_; + } + return true; +} + +void PropertyTable::Schema::Object::Copy(const Object& src) { + name_ = src.name_; + type_ = src.type_; + objects_.reserve(src.objects_.size()); + for (const Object& obj : src.objects_) { + objects_.emplace_back(); + objects_.back().Copy(obj); + } + array_.reserve(src.array_.size()); + for (const Object& obj : src.array_) { + array_.emplace_back(); + array_.back().Copy(obj); + } + string_ = src.string_; + integer_ = src.integer_; + boolean_ = src.boolean_; +} + +PropertyTable::Property::Property() {} + +bool PropertyTable::Property::Data::operator==(const Data& other) const { + return data == other.data && target == other.target; +} + +bool PropertyTable::Property::Offsets::operator==(const Offsets& other) const { + return data == other.data && type == other.type; +} + +bool PropertyTable::Property::operator==(const Property& other) const { + return name_ == other.name_ && data_ == other.data_ && + array_offsets_ == other.array_offsets_ && + string_offsets_ == other.string_offsets_; +} + +void PropertyTable::Property::Copy(const Property& src) { + name_ = src.name_; + data_ = src.data_; + array_offsets_ = src.array_offsets_; + string_offsets_ = src.string_offsets_; +} + +void PropertyTable::Property::SetName(const std::string& name) { name_ = name; } +const std::string& PropertyTable::Property::GetName() const { return name_; } + +PropertyTable::Property::Data& PropertyTable::Property::GetData() { + return data_; +} +const PropertyTable::Property::Data& PropertyTable::Property::GetData() const { + return data_; +} + +const PropertyTable::Property::Offsets& +PropertyTable::Property::GetArrayOffsets() const { + return array_offsets_; +} +PropertyTable::Property::Offsets& PropertyTable::Property::GetArrayOffsets() { + return array_offsets_; +} + +const PropertyTable::Property::Offsets& +PropertyTable::Property::GetStringOffsets() const { + return string_offsets_; +} +PropertyTable::Property::Offsets& PropertyTable::Property::GetStringOffsets() { + return string_offsets_; +} + +PropertyTable::PropertyTable() : count_(0) {} + +bool PropertyTable::operator==(const PropertyTable& other) const { + if (name_ != other.name_ || class_ != other.class_ || + count_ != other.count_ || + properties_.size() != other.properties_.size()) { + return false; + } + for (int i = 0; i < properties_.size(); ++i) { + if (*properties_[i] != *other.properties_[i]) { + return false; + } + } + return true; +} + +void PropertyTable::Copy(const PropertyTable& src) { + name_ = src.name_; + class_ = src.class_; + count_ = src.count_; + properties_.clear(); + properties_.reserve(src.properties_.size()); + for (int i = 0; i < src.properties_.size(); ++i) { + std::unique_ptr property(new Property()); + property->Copy(src.GetProperty(i)); + properties_.push_back(std::move(property)); + } +} + +void PropertyTable::SetName(const std::string& value) { name_ = value; } +const std::string& PropertyTable::GetName() const { return name_; } + +void PropertyTable::SetClass(const std::string& value) { class_ = value; } +const std::string& PropertyTable::GetClass() const { return class_; } + +void PropertyTable::SetCount(int count) { count_ = count; } +int PropertyTable::GetCount() const { return count_; } + +int PropertyTable::AddProperty(std::unique_ptr property) { + properties_.push_back(std::move(property)); + return properties_.size() - 1; +} +int PropertyTable::NumProperties() const { return properties_.size(); } +const PropertyTable::Property& PropertyTable::GetProperty(int index) const { + return *properties_[index]; +} +PropertyTable::Property& PropertyTable::GetProperty(int index) { + return *properties_[index]; +} +void PropertyTable::RemoveProperty(int index) { + properties_.erase(properties_.begin() + index); +} + +} // namespace draco + +#endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/metadata/property_table.h draco-1.5.5+dfsg/src/draco/metadata/property_table.h --- draco-1.5.3+dfsg/src/draco/metadata/property_table.h 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/metadata/property_table.h 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,243 @@ +// Copyright 2022 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef DRACO_METADATA_PROPERTY_TABLE_H_ +#define DRACO_METADATA_PROPERTY_TABLE_H_ + +#include "draco/draco_features.h" + +#ifdef DRACO_TRANSCODER_SUPPORTED + +#include +#include +#include + +namespace draco { + +// Describes a property table as defined in the EXT_structural_metadata glTF +// extension, including property table schema and table properties (columns). +class PropertyTable { + public: + // Describes property table schema in the form of a JSON object. + struct Schema { + // JSON object of the schema. + // TODO(vytyaz): Consider using a third_party/json library. Currently there + // is a conflict between Filament's assert_invariant() macro and JSON + // library's assert_invariant() method that causes compile errors in Draco + // visualization library. + class Object { + public: + enum Type { OBJECT, ARRAY, STRING, INTEGER, BOOLEAN }; + + // Constructors. + Object() : Object("") {} + explicit Object(const std::string& name) + : name_(name), type_(OBJECT), integer_(0), boolean_(false) {} + Object(const std::string& name, const std::string& value) : Object(name) { + SetString(value); + } + Object(const std::string& name, const char* value) : Object(name) { + SetString(value); + } + Object(const std::string& name, int value) : Object(name) { + SetInteger(value); + } + Object(const std::string& name, bool value) : Object(name) { + SetBoolean(value); + } + + // Methods for comparing two objects. + bool operator==(const Object& other) const; + bool operator!=(const Object& other) const { return !(*this == other); } + + // Method for copying the object. + void Copy(const Object& src); + + // Methods for getting object name and type. + const std::string& GetName() const { return name_; } + Type GetType() const { return type_; } + + // Methods for getting object value. + const std::vector& GetObjects() const { return objects_; } + const std::vector& GetArray() const { return array_; } + const std::string& GetString() const { return string_; } + int GetInteger() const { return integer_; } + bool GetBoolean() const { return boolean_; } + + // Methods for setting object value. + std::vector& SetObjects() { + type_ = OBJECT; + return objects_; + } + std::vector& SetArray() { + type_ = ARRAY; + return array_; + } + void SetString(const std::string& value) { + type_ = STRING; + string_ = value; + } + void SetInteger(int value) { + type_ = INTEGER; + integer_ = value; + } + void SetBoolean(bool value) { + type_ = BOOLEAN; + boolean_ = value; + } + + private: + std::string name_; + Type type_; + std::vector objects_; + std::vector array_; + std::string string_; + int integer_; + bool boolean_; + }; + + // Valid schema top-level JSON object name is "schema". + Schema() : json("schema") {} + + // Methods for comparing two schemas. + bool operator==(const Schema& other) const { return json == other.json; } + bool operator!=(const Schema& other) const { return !(*this == other); } + + // Valid schema top-level JSON object is required to have child objects. + bool Empty() const { return json.GetObjects().empty(); } + + // Top-level JSON object of the schema. + Object json; + }; + + // Describes a property (column) of a property table. + class Property { + public: + // Describes glTF buffer view data. + struct Data { + // Methods for comparing two data objects. + bool operator==(const Data& other) const; + bool operator!=(const Data& other) const { return !(*this == other); } + + // Buffer view data. + std::vector data; + + // Data target corresponds to the target property of the glTF bufferView + // object and classifies the type or nature of the data. + int target = 0; + }; + + // Describes offsets of the entries in property data when the data + // represents an array of strings or an array of variable-length number + // arrays. + struct Offsets { + // Methods for comparing two offsets. + bool operator==(const Offsets& other) const; + bool operator!=(const Offsets& other) const { return !(*this == other); } + + // Data containing the offset entries. + Data data; + + // Data type of the offset entries. + std::string type; + }; + + // Creates an empty property. + Property(); + + // Methods for comparing two properties. + bool operator==(const Property& other) const; + bool operator!=(const Property& other) const { return !(*this == other); } + + // Copies all data from |src| property. + void Copy(const Property& src); + + // Name of this property. + void SetName(const std::string& name); + const std::string& GetName() const; + + // Property data stores one table column worth of data. For example, when + // the data of type UINT8 is [11, 22] then the property values are 11 and 22 + // for the first and second table rows. See EXT_structural_metadata glTF + // extension documentation for more details. + Data& GetData(); + const Data& GetData() const; + + // Array offsets are used when property data contains a variable-length + // number arrays. For example, when the data is [0, 1, 2, 3, 4] and the + // array offsets are [0, 2, 5] for a two-row table, then the property value + // arrays are [0, 1] and [2, 3, 4] for the first and second table rows, + // respectively. See EXT_structural_metadata glTF extension documentation + // for more details. + const Offsets& GetArrayOffsets() const; + Offsets& GetArrayOffsets(); + + // String offsets are used when property data contains strings. For example, + // when the data is "SeaLand" and the array offsets are [0, 3, 7] for a + // two-row table, then the property strings are "Sea" and "Land" for the + // first and second table rows, respectively. See EXT_structural_metadata + // glTF extension documentation for more details. + const Offsets& GetStringOffsets() const; + Offsets& GetStringOffsets(); + + private: + std::string name_; + Data data_; + Offsets array_offsets_; + Offsets string_offsets_; + // TODO(vytyaz): Support property value modifiers min, max, offset, scale. + }; + + // Creates an empty property table. + PropertyTable(); + + // Methods for comparing two property tables. + bool operator==(const PropertyTable& other) const; + bool operator!=(const PropertyTable& other) const { + return !(*this == other); + } + + // Copies all data from |src| property table. + void Copy(const PropertyTable& src); + + // Name of this property table. + void SetName(const std::string& value); + const std::string& GetName() const; + + // Class of this property table. + void SetClass(const std::string& value); + const std::string& GetClass() const; + + // Number of rows in this property table. + void SetCount(int count); + int GetCount() const; + + // Table properties (columns). + int AddProperty(std::unique_ptr property); + int NumProperties() const; + const Property& GetProperty(int index) const; + Property& GetProperty(int index); + void RemoveProperty(int index); + + private: + std::string name_; + std::string class_; + int count_; + std::vector> properties_; +}; + +} // namespace draco + +#endif // DRACO_TRANSCODER_SUPPORTED +#endif // DRACO_METADATA_PROPERTY_TABLE_H_ diff -Nru draco-1.5.3+dfsg/src/draco/metadata/property_table_test.cc draco-1.5.5+dfsg/src/draco/metadata/property_table_test.cc --- draco-1.5.3+dfsg/src/draco/metadata/property_table_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/metadata/property_table_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,624 @@ +// Copyright 2022 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "draco/metadata/property_table.h" + +#include +#include +#include + +#include "draco/core/draco_test_base.h" +#include "draco/core/draco_test_utils.h" + +namespace { + +#ifdef DRACO_TRANSCODER_SUPPORTED + +TEST(PropertyTableTest, TestPropertyDataDefaults) { + // Test construction of an empty property data. + draco::PropertyTable::Property::Data data; + ASSERT_TRUE(data.data.empty()); + ASSERT_EQ(data.target, 0); +} + +TEST(PropertyTableTest, TestPropertyDefaults) { + // Test construction of an empty property table property. + draco::PropertyTable::Property property; + ASSERT_TRUE(property.GetName().empty()); + ASSERT_TRUE(property.GetData().data.empty()); + { + const auto &offsets = property.GetArrayOffsets(); + ASSERT_TRUE(offsets.type.empty()); + ASSERT_TRUE(offsets.data.data.empty()); + ASSERT_EQ(offsets.data.target, 0); + } + { + const auto &offsets = property.GetStringOffsets(); + ASSERT_TRUE(offsets.type.empty()); + ASSERT_TRUE(offsets.data.data.empty()); + ASSERT_EQ(offsets.data.target, 0); + } +} + +TEST(PropertyTableTest, TestPropertyTableDefaults) { + // Test construction of an empty property table. + draco::PropertyTable table; + ASSERT_TRUE(table.GetName().empty()); + ASSERT_TRUE(table.GetClass().empty()); + ASSERT_EQ(table.GetCount(), 0); + ASSERT_EQ(table.NumProperties(), 0); +} + +TEST(PropertyTableTest, TestSchemaDefaults) { + // Test construction of an empty property table schema. + draco::PropertyTable::Schema schema; + ASSERT_TRUE(schema.Empty()); + ASSERT_EQ(schema.json.GetName(), "schema"); + ASSERT_EQ(schema.json.GetType(), + draco::PropertyTable::Schema::Object::OBJECT); + ASSERT_TRUE(schema.json.GetObjects().empty()); + ASSERT_TRUE(schema.json.GetArray().empty()); + ASSERT_TRUE(schema.json.GetString().empty()); + ASSERT_EQ(schema.json.GetInteger(), 0); + ASSERT_FALSE(schema.json.GetBoolean()); +} + +TEST(PropertyTableTest, TestSchemaObjectDefaultConstructor) { + // Test construction of an empty property table schema object. + draco::PropertyTable::Schema::Object object; + ASSERT_TRUE(object.GetName().empty()); + ASSERT_EQ(object.GetType(), draco::PropertyTable::Schema::Object::OBJECT); + ASSERT_TRUE(object.GetObjects().empty()); + ASSERT_TRUE(object.GetArray().empty()); + ASSERT_TRUE(object.GetString().empty()); + ASSERT_EQ(object.GetInteger(), 0); + ASSERT_FALSE(object.GetBoolean()); +} + +TEST(PropertyTableTest, TestSchemaObjectNamedConstructor) { + // Test construction of a named property table schema object. + draco::PropertyTable::Schema::Object object("Flexible Demeanour"); + ASSERT_EQ(object.GetName(), "Flexible Demeanour"); + ASSERT_EQ(object.GetType(), draco::PropertyTable::Schema::Object::OBJECT); + ASSERT_TRUE(object.GetObjects().empty()); +} + +TEST(PropertyTableTest, TestSchemaObjectStringConstructor) { + // Test construction of property table schema object storing a string. + draco::PropertyTable::Schema::Object object("Flexible Demeanour", "GCU"); + ASSERT_EQ(object.GetName(), "Flexible Demeanour"); + ASSERT_EQ(object.GetType(), draco::PropertyTable::Schema::Object::STRING); + ASSERT_EQ(object.GetString(), "GCU"); +} + +TEST(PropertyTableTest, TestSchemaObjectIntegerConstructor) { + // Test construction of property table schema object storing an integer. + draco::PropertyTable::Schema::Object object("Flexible Demeanour", 12); + ASSERT_EQ(object.GetName(), "Flexible Demeanour"); + ASSERT_EQ(object.GetType(), draco::PropertyTable::Schema::Object::INTEGER); + ASSERT_EQ(object.GetInteger(), 12); +} + +TEST(PropertyTableTest, TestSchemaObjectBooleanConstructor) { + // Test construction of property table schema object storing a boolean. + draco::PropertyTable::Schema::Object object("Flexible Demeanour", true); + ASSERT_EQ(object.GetName(), "Flexible Demeanour"); + ASSERT_EQ(object.GetType(), draco::PropertyTable::Schema::Object::BOOLEAN); + ASSERT_TRUE(object.GetBoolean()); +} + +TEST(PropertyTableTest, TestSchemaObjectSettersAndGetters) { + // Test value setters and getters of property table schema object. + typedef draco::PropertyTable::Schema::Object Object; + Object object; + ASSERT_EQ(object.GetType(), Object::OBJECT); + + object.SetArray().push_back(Object("entry", 12)); + ASSERT_EQ(object.GetType(), Object::ARRAY); + ASSERT_EQ(object.GetArray().size(), 1); + ASSERT_EQ(object.GetArray()[0].GetName(), "entry"); + ASSERT_EQ(object.GetArray()[0].GetInteger(), 12); + + object.SetObjects().push_back(Object("object", 9)); + ASSERT_EQ(object.GetType(), Object::OBJECT); + ASSERT_EQ(object.GetObjects().size(), 1); + ASSERT_EQ(object.GetObjects()[0].GetName(), "object"); + ASSERT_EQ(object.GetObjects()[0].GetInteger(), 9); + + object.SetString("matter"); + ASSERT_EQ(object.GetType(), Object::STRING); + ASSERT_EQ(object.GetString(), "matter"); + + object.SetInteger(5); + ASSERT_EQ(object.GetType(), Object::INTEGER); + ASSERT_EQ(object.GetInteger(), 5); + + object.SetBoolean(true); + ASSERT_EQ(object.GetType(), Object::BOOLEAN); + ASSERT_EQ(object.GetBoolean(), true); +} + +TEST(PropertyTableTest, TestSchemaCompare) { + typedef draco::PropertyTable::Schema Schema; + // Test comparison of two schema objects. + { + // Compare the same empty schema object. + Schema a; + ASSERT_TRUE(a == a); + ASSERT_FALSE(a != a); + } + { + // Compare two empty schema objects. + Schema a; + Schema b; + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + } + { + // Compare two schema objects with different JSON objects. + Schema a; + Schema b; + a.json.SetBoolean(true); + b.json.SetBoolean(false); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } +} + +TEST(PropertyTableTest, TestSchemaObjectCompare) { + // Test comparison of two schema JSON objects. + typedef draco::PropertyTable::Schema::Object Object; + { + // Compare the same object. + Object a; + ASSERT_TRUE(a == a); + ASSERT_FALSE(a != a); + } + { + // Compare two default objects. + Object a; + Object b; + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + } + { + // Compare two objects with different names. + Object a("one"); + Object b("two"); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two objects with different types. + Object a; + Object b; + a.SetInteger(1); + b.SetString("one"); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two identical string-type objects. + Object a; + Object b; + a.SetString("one"); + b.SetString("one"); + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + } + { + // Compare two different string-type objects. + Object a; + Object b; + a.SetString("one"); + b.SetString("two"); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two identical integer-type objects. + Object a; + Object b; + a.SetInteger(1); + b.SetInteger(1); + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + } + { + // Compare two different integer-type objects. + Object a; + Object b; + a.SetInteger(1); + b.SetInteger(2); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two identical boolean-type objects. + Object a; + Object b; + a.SetBoolean(true); + b.SetBoolean(true); + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + } + { + // Compare two different boolean-type objects. + Object a; + Object b; + a.SetBoolean(true); + b.SetBoolean(false); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two identical object-type objects. + Object a; + Object b; + a.SetObjects().emplace_back("one"); + b.SetObjects().emplace_back("one"); + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + } + { + // Compare two different object-type objects. + Object a; + Object b; + a.SetObjects().emplace_back("one"); + b.SetObjects().emplace_back("two"); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two object-type objects with different counts. + Object a; + Object b; + a.SetObjects().emplace_back("one"); + b.SetObjects().emplace_back("one"); + b.SetObjects().emplace_back("two"); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two identical array-type objects. + Object a; + Object b; + a.SetArray().emplace_back("", 1); + b.SetArray().emplace_back("", 1); + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + } + { + // Compare two different array-type objects. + Object a; + Object b; + a.SetArray().emplace_back("", 1); + b.SetArray().emplace_back("", 2); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two array-type objects with different counts. + Object a; + Object b; + a.SetArray().emplace_back("", 1); + b.SetArray().emplace_back("", 1); + b.SetArray().emplace_back("", 2); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } +} + +TEST(PropertyTableTest, TestPropertySettersAndGetters) { + // Test setter and getter methods of the property table property. + draco::PropertyTable::Property property; + property.SetName("Unfortunate Conflict Of Evidence"); + property.GetData().data.push_back(2); + + // Check that property members can be accessed via getters. + ASSERT_EQ(property.GetName(), "Unfortunate Conflict Of Evidence"); + ASSERT_EQ(property.GetData().data.size(), 1); + ASSERT_EQ(property.GetData().data[0], 2); +} + +TEST(PropertyTableTest, TestPropertyTableSettersAndGetters) { + // Test setter and getter methods of the property table. + draco::PropertyTable table; + table.SetName("Just Read The Instructions"); + table.SetClass("General Contact Unit"); + table.SetCount(456); + { + std::unique_ptr property( + new draco::PropertyTable::Property()); + property->SetName("Determinist"); + ASSERT_EQ(table.AddProperty(std::move(property)), 0); + } + { + std::unique_ptr property( + new draco::PropertyTable::Property()); + property->SetName("Revisionist"); + ASSERT_EQ(table.AddProperty(std::move(property)), 1); + } + + // Check that property table members can be accessed via getters. + ASSERT_EQ(table.GetName(), "Just Read The Instructions"); + ASSERT_EQ(table.GetClass(), "General Contact Unit"); + ASSERT_EQ(table.GetCount(), 456); + ASSERT_EQ(table.NumProperties(), 2); + ASSERT_EQ(table.GetProperty(0).GetName(), "Determinist"); + ASSERT_EQ(table.GetProperty(1).GetName(), "Revisionist"); + + // Check that proeprties can be removed. + table.RemoveProperty(0); + ASSERT_EQ(table.NumProperties(), 1); + ASSERT_EQ(table.GetProperty(0).GetName(), "Revisionist"); + table.RemoveProperty(0); + ASSERT_EQ(table.NumProperties(), 0); +} + +TEST(PropertyTableTest, TestPropertyCopy) { + // Test that property table property can be copied. + draco::PropertyTable::Property property; + property.SetName("Unfortunate Conflict Of Evidence"); + property.GetData().data.push_back(2); + + // Make a copy. + draco::PropertyTable::Property copy; + copy.Copy(property); + + // Check the copy. + ASSERT_EQ(copy.GetName(), "Unfortunate Conflict Of Evidence"); + ASSERT_EQ(copy.GetData().data.size(), 1); + ASSERT_EQ(copy.GetData().data[0], 2); +} + +TEST(PropertyTableTest, TestPropertyTableCopy) { + // Test that property table can be copied. + draco::PropertyTable table; + table.SetName("Just Read The Instructions"); + table.SetClass("General Contact Unit"); + table.SetCount(456); + { + std::unique_ptr property( + new draco::PropertyTable::Property()); + property->SetName("Determinist"); + table.AddProperty(std::move(property)); + } + { + std::unique_ptr property( + new draco::PropertyTable::Property()); + property->SetName("Revisionist"); + table.AddProperty(std::move(property)); + } + + // Make a copy. + draco::PropertyTable copy; + copy.Copy(table); + + // Check the copy. + ASSERT_EQ(copy.GetName(), "Just Read The Instructions"); + ASSERT_EQ(copy.GetClass(), "General Contact Unit"); + ASSERT_EQ(copy.GetCount(), 456); + ASSERT_EQ(copy.NumProperties(), 2); + ASSERT_EQ(copy.GetProperty(0).GetName(), "Determinist"); + ASSERT_EQ(copy.GetProperty(1).GetName(), "Revisionist"); +} + +TEST(PropertyTableTest, TestPropertyDataCompare) { + // Test comparison of two property data objects. + typedef draco::PropertyTable::Property::Data Data; + { + // Compare the same data object. + Data a; + ASSERT_TRUE(a == a); + ASSERT_FALSE(a != a); + } + { + // Compare two default data objects. + Data a; + Data b; + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + } + { + // Compare two data objects with different targets. + Data a; + Data b; + a.target = 1; + b.target = 2; + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two data objects with different data vectors. + Data a; + Data b; + a.data = {1}; + a.data = {2}; + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } +} + +TEST(PropertyTableTest, TestPropertyOffsets) { + // Test comparison of two property offsets. + typedef draco::PropertyTable::Property::Offsets Offsets; + { + // Compare the same offsets object. + Offsets a; + ASSERT_TRUE(a == a); + ASSERT_FALSE(a != a); + } + { + // Compare two default offsets objects. + Offsets a; + Offsets b; + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + } + { + // Compare two offsets objects with different types. + Offsets a; + Offsets b; + a.type = 1; + b.type = 2; + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two offsets objects with different data objects. + Offsets a; + Offsets b; + a.data.target = 1; + b.data.target = 2; + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } +} + +TEST(PropertyTableTest, TestPropertyCompare) { + // Test comparison of two properties. + typedef draco::PropertyTable::Property Property; + { + // Compare the same property object. + Property a; + ASSERT_TRUE(a == a); + ASSERT_FALSE(a != a); + } + { + // Compare two default property objects. + Property a; + Property b; + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + } + { + // Compare two property objects with different names. + Property a; + Property b; + a.SetName("one"); + b.SetName("two"); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two property objects with different data. + Property a; + Property b; + a.GetData().target = 1; + b.GetData().target = 2; + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two property objects with different array offsets. + Property a; + Property b; + a.GetArrayOffsets().data.target = 1; + b.GetArrayOffsets().data.target = 2; + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two property objects with different string offsets. + Property a; + Property b; + a.GetStringOffsets().data.target = 1; + b.GetStringOffsets().data.target = 2; + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } +} + +TEST(PropertyTableTest, TestPropertyTableCompare) { + // Test comparison of two property tables. + typedef draco::PropertyTable PropertyTable; + typedef draco::PropertyTable::Property Property; + { + // Compare the same property table object. + PropertyTable a; + ASSERT_TRUE(a == a); + ASSERT_FALSE(a != a); + } + { + // Compare two default property tables. + PropertyTable a; + PropertyTable b; + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + } + { + // Compare two property tables with different names. + PropertyTable a; + PropertyTable b; + a.SetName("one"); + b.SetName("two"); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two property tables with different classes. + PropertyTable a; + PropertyTable b; + a.SetClass("one"); + b.SetClass("two"); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two property tables with different counts. + PropertyTable a; + PropertyTable b; + a.SetCount(1); + b.SetCount(2); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two property tables with identical properties. + PropertyTable a; + PropertyTable b; + a.AddProperty(std::unique_ptr(new Property)); + b.AddProperty(std::unique_ptr(new Property)); + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + } + { + // Compare two property tables with different number of properties. + PropertyTable a; + PropertyTable b; + a.AddProperty(std::unique_ptr(new Property)); + b.AddProperty(std::unique_ptr(new Property)); + b.AddProperty(std::unique_ptr(new Property)); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two property tables with different properties. + PropertyTable a; + PropertyTable b; + std::unique_ptr p1(new Property); + std::unique_ptr p2(new Property); + p1->SetName("one"); + p2->SetName("two"); + a.AddProperty(std::move(p1)); + b.AddProperty(std::move(p2)); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } +} + +#endif // DRACO_TRANSCODER_SUPPORTED + +} // namespace diff -Nru draco-1.5.3+dfsg/src/draco/metadata/structural_metadata.cc draco-1.5.5+dfsg/src/draco/metadata/structural_metadata.cc --- draco-1.5.3+dfsg/src/draco/metadata/structural_metadata.cc 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/metadata/structural_metadata.cc 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,74 @@ +// Copyright 2022 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "draco/metadata/structural_metadata.h" + +#include +#include + +#ifdef DRACO_TRANSCODER_SUPPORTED + +namespace draco { + +StructuralMetadata::StructuralMetadata() {} + +bool StructuralMetadata::operator==(const StructuralMetadata &other) const { + return property_table_schema_ == other.property_table_schema_ && + property_tables_ == other.property_tables_; +} + +void StructuralMetadata::Copy(const StructuralMetadata &src) { + property_table_schema_.json.Copy(src.property_table_schema_.json); + property_tables_.resize(src.property_tables_.size()); + for (int i = 0; i < property_tables_.size(); ++i) { + property_tables_[i] = std::unique_ptr(new PropertyTable()); + property_tables_[i]->Copy(*src.property_tables_[i]); + } +} + +void StructuralMetadata::SetPropertyTableSchema( + const PropertyTable::Schema &schema) { + property_table_schema_ = schema; +} + +const PropertyTable::Schema &StructuralMetadata::GetPropertyTableSchema() + const { + return property_table_schema_; +} + +int StructuralMetadata::AddPropertyTable( + std::unique_ptr property_table) { + property_tables_.push_back(std::move(property_table)); + return property_tables_.size() - 1; +} + +int StructuralMetadata::NumPropertyTables() const { + return property_tables_.size(); +} + +const PropertyTable &StructuralMetadata::GetPropertyTable(int index) const { + return *property_tables_[index]; +} + +PropertyTable &StructuralMetadata::GetPropertyTable(int index) { + return *property_tables_[index]; +} + +void StructuralMetadata::RemovePropertyTable(int index) { + property_tables_.erase(property_tables_.begin() + index); +} + +} // namespace draco + +#endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/metadata/structural_metadata.h draco-1.5.5+dfsg/src/draco/metadata/structural_metadata.h --- draco-1.5.3+dfsg/src/draco/metadata/structural_metadata.h 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/metadata/structural_metadata.h 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,64 @@ +// Copyright 2022 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef DRACO_METADATA_STRUCTURAL_METADATA_H_ +#define DRACO_METADATA_STRUCTURAL_METADATA_H_ + +#include "draco/draco_features.h" + +#ifdef DRACO_TRANSCODER_SUPPORTED + +#include +#include +#include + +#include "draco/metadata/property_table.h" + +namespace draco { + +// Holds data associated with EXT_structural_metadata glTF extension. +class StructuralMetadata { + public: + StructuralMetadata(); + + // Methods for comparing two structural metadata objects. + bool operator==(const StructuralMetadata &other) const; + bool operator!=(const StructuralMetadata &other) const { + return !(*this == other); + } + + // Copies |src| structural metadata into this object. + void Copy(const StructuralMetadata &src); + + // Property table schema. + void SetPropertyTableSchema(const PropertyTable::Schema &schema); + const PropertyTable::Schema &GetPropertyTableSchema() const; + + // Property tables. + int AddPropertyTable(std::unique_ptr property_table); + int NumPropertyTables() const; + const PropertyTable &GetPropertyTable(int index) const; + PropertyTable &GetPropertyTable(int index); + void RemovePropertyTable(int index); + + private: + // Property table schema and property tables. + PropertyTable::Schema property_table_schema_; + std::vector> property_tables_; +}; + +} // namespace draco + +#endif // DRACO_TRANSCODER_SUPPORTED +#endif // DRACO_METADATA_STRUCTURAL_METADATA_H_ diff -Nru draco-1.5.3+dfsg/src/draco/metadata/structural_metadata_test.cc draco-1.5.5+dfsg/src/draco/metadata/structural_metadata_test.cc --- draco-1.5.3+dfsg/src/draco/metadata/structural_metadata_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/metadata/structural_metadata_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,170 @@ +// Copyright 2022 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "draco/metadata/structural_metadata.h" + +#include +#include +#include + +#include "draco/core/draco_test_base.h" +#include "draco/core/draco_test_utils.h" + +namespace { + +#ifdef DRACO_TRANSCODER_SUPPORTED + +TEST(StructuralMetadataTest, TestCopy) { + // Tests copying of structural metadata. + draco::StructuralMetadata structural_metadata; + + // Add property table schema to structural metadata. + draco::PropertyTable::Schema schema; + schema.json.SetString("Culture"); + structural_metadata.SetPropertyTableSchema(schema); + + // Add property table to structural metadata. + std::unique_ptr table(new draco::PropertyTable()); + table->SetName("Just Read The Instructions"); + table->SetClass("General Contact Unit"); + table->SetCount(456); + { + std::unique_ptr property( + new draco::PropertyTable::Property()); + property->SetName("Determinist"); + table->AddProperty(std::move(property)); + } + { + std::unique_ptr property( + new draco::PropertyTable::Property()); + property->SetName("Revisionist"); + table->AddProperty(std::move(property)); + } + ASSERT_EQ(structural_metadata.AddPropertyTable(std::move(table)), 0); + + // Copy the structural metadata. + draco::StructuralMetadata copy; + copy.Copy(structural_metadata); + + // Check that the structural metadata property table schema has been copied. + ASSERT_EQ(copy.GetPropertyTableSchema().json.GetString(), "Culture"); + + // Check that the structural metadata property table has been copied. + ASSERT_EQ(copy.NumPropertyTables(), 1); + ASSERT_EQ(copy.GetPropertyTable(0).GetName(), "Just Read The Instructions"); + ASSERT_EQ(copy.GetPropertyTable(0).GetClass(), "General Contact Unit"); + ASSERT_EQ(copy.GetPropertyTable(0).GetCount(), 456); + ASSERT_EQ(copy.GetPropertyTable(0).NumProperties(), 2); + ASSERT_EQ(copy.GetPropertyTable(0).GetProperty(0).GetName(), "Determinist"); + ASSERT_EQ(copy.GetPropertyTable(0).GetProperty(1).GetName(), "Revisionist"); +} + +TEST(StructuralMetadataTest, TestPropertyTables) { + // Tests adding and removing of property tables to structural metadata. + draco::StructuralMetadata structural_metadata; + + // Check that property tables can be added. + { + std::unique_ptr table(new draco::PropertyTable()); + table->SetName("Just Read The Instructions"); + ASSERT_EQ(structural_metadata.AddPropertyTable(std::move(table)), 0); + } + { + std::unique_ptr table(new draco::PropertyTable()); + table->SetName("So Much For Subtlety"); + ASSERT_EQ(structural_metadata.AddPropertyTable(std::move(table)), 1); + } + { + std::unique_ptr table(new draco::PropertyTable()); + table->SetName("Of Course I Still Love You"); + ASSERT_EQ(structural_metadata.AddPropertyTable(std::move(table)), 2); + } + draco::StructuralMetadata &sm = structural_metadata; + + // Check that the property tables can be removed. + ASSERT_EQ(sm.NumPropertyTables(), 3); + ASSERT_EQ(sm.GetPropertyTable(0).GetName(), "Just Read The Instructions"); + ASSERT_EQ(sm.GetPropertyTable(1).GetName(), "So Much For Subtlety"); + ASSERT_EQ(sm.GetPropertyTable(2).GetName(), "Of Course I Still Love You"); + + sm.RemovePropertyTable(1); + ASSERT_EQ(sm.NumPropertyTables(), 2); + ASSERT_EQ(sm.GetPropertyTable(0).GetName(), "Just Read The Instructions"); + ASSERT_EQ(sm.GetPropertyTable(1).GetName(), "Of Course I Still Love You"); + + sm.RemovePropertyTable(1); + ASSERT_EQ(sm.NumPropertyTables(), 1); + ASSERT_EQ(sm.GetPropertyTable(0).GetName(), "Just Read The Instructions"); + + sm.RemovePropertyTable(0); + ASSERT_EQ(sm.NumPropertyTables(), 0); +} + +TEST(StructuralMetadataTest, TestCompare) { + // Test comparison of two structural metadata objects. + typedef draco::PropertyTable PropertyTable; + { + // Compare the same structural metadata object. + draco::StructuralMetadata a; + ASSERT_TRUE(a == a); + ASSERT_FALSE(a != a); + } + { + // Compare two identical structural metadata objects. + draco::StructuralMetadata a; + draco::StructuralMetadata b; + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + } + { + // Compare two structural metadata objects with different schemas. + draco::StructuralMetadata a; + draco::StructuralMetadata b; + PropertyTable::Schema s1; + PropertyTable::Schema s2; + s1.json.SetString("one"); + s2.json.SetString("two"); + a.SetPropertyTableSchema(s1); + b.SetPropertyTableSchema(s2); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two objects with different number of proeprty tables. + draco::StructuralMetadata a; + draco::StructuralMetadata b; + a.AddPropertyTable(std::unique_ptr(new PropertyTable())); + b.AddPropertyTable(std::unique_ptr(new PropertyTable())); + b.AddPropertyTable(std::unique_ptr(new PropertyTable())); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } + { + // Compare two objects with different proeprty tables. + draco::StructuralMetadata a; + draco::StructuralMetadata b; + auto p1 = std::unique_ptr(new PropertyTable()); + auto p2 = std::unique_ptr(new PropertyTable()); + p1->SetName("one"); + p2->SetName("two"); + a.AddPropertyTable(std::move(p1)); + b.AddPropertyTable(std::move(p2)); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + } +} + +#endif // DRACO_TRANSCODER_SUPPORTED + +} // namespace diff -Nru draco-1.5.3+dfsg/src/draco/point_cloud/point_cloud_builder.h draco-1.5.5+dfsg/src/draco/point_cloud/point_cloud_builder.h --- draco-1.5.3+dfsg/src/draco/point_cloud/point_cloud_builder.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/point_cloud/point_cloud_builder.h 2022-10-29 00:55:03.000000000 +0000 @@ -15,6 +15,8 @@ #ifndef DRACO_POINT_CLOUD_POINT_CLOUD_BUILDER_H_ #define DRACO_POINT_CLOUD_POINT_CLOUD_BUILDER_H_ +#include + #include "draco/point_cloud/point_cloud.h" namespace draco { @@ -37,6 +39,9 @@ class PointCloudBuilder { public: + // Index type of the inserted element. + typedef PointIndex ElementIndex; + PointCloudBuilder(); // Starts collecting point cloud data. @@ -71,6 +76,12 @@ // used until the method Start() is called again. std::unique_ptr Finalize(bool deduplicate_points); + // Add metadata for an attribute. + void AddAttributeMetadata(int32_t att_id, + std::unique_ptr metadata) { + point_cloud_->AddAttributeMetadata(att_id, std::move(metadata)); + } + private: std::unique_ptr point_cloud_; }; diff -Nru draco-1.5.3+dfsg/src/draco/point_cloud/point_cloud.cc draco-1.5.5+dfsg/src/draco/point_cloud/point_cloud.cc --- draco-1.5.3+dfsg/src/draco/point_cloud/point_cloud.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/point_cloud/point_cloud.cc 2022-10-29 00:55:03.000000000 +0000 @@ -287,6 +287,11 @@ BoundingBox PointCloud::ComputeBoundingBox() const { BoundingBox bounding_box; auto pc_att = GetNamedAttribute(GeometryAttribute::POSITION); + if (pc_att == nullptr) { + // Return default invalid bounding box. + return bounding_box; + } + // TODO(b/199760503): Make the BoundingBox a template type, it may not be easy // because PointCloud is not a template. // Or simply add some preconditioning here to make sure the position attribute diff -Nru draco-1.5.3+dfsg/src/draco/scene/scene_are_equivalent.cc draco-1.5.5+dfsg/src/draco/scene/scene_are_equivalent.cc --- draco-1.5.3+dfsg/src/draco/scene/scene_are_equivalent.cc 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/scene/scene_are_equivalent.cc 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,109 @@ +// Copyright 2019 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "draco/scene/scene_are_equivalent.h" + +#ifdef DRACO_TRANSCODER_SUPPORTED +#include "draco/mesh/mesh_are_equivalent.h" + +namespace draco { + +bool SceneAreEquivalent::operator()(const Scene &scene0, const Scene &scene1) { + // Check scene component sizes. + if (scene0.NumAnimations() != scene1.NumAnimations()) { + return false; + } + if (scene0.NumMeshGroups() != scene1.NumMeshGroups()) { + return false; + } + if (scene0.NumSkins() != scene1.NumSkins()) { + return false; + } + + // Check equivalence of each mesh. + if (scene0.NumMeshes() != scene1.NumMeshes()) { + return false; + } + for (MeshIndex i(0); i < scene0.NumMeshes(); i++) { + if (!AreEquivalent(scene0.GetMesh(i), scene1.GetMesh(i))) { + return false; + } + } + + // Check eqiuvalence of each node. + if (scene0.NumNodes() != scene1.NumNodes()) { + return false; + } + for (SceneNodeIndex i(0); i < scene0.NumNodes(); i++) { + if (!AreEquivalent(*scene0.GetNode(i), *scene1.GetNode(i))) { + return false; + } + } + + // Check non-material texture library sizes. + if (scene0.GetNonMaterialTextureLibrary().NumTextures() != + scene1.GetNonMaterialTextureLibrary().NumTextures()) { + return false; + } + + // TODO(vytyaz): Check remaining scene properties like animations and skins. + return true; +} + +bool SceneAreEquivalent::AreEquivalent(const Mesh &mesh0, const Mesh &mesh1) { + MeshAreEquivalent eq; + return eq(mesh0, mesh1); +} + +bool SceneAreEquivalent::AreEquivalent(const SceneNode &node0, + const SceneNode &node1) { + // Check equivalence of node indices. + if (node0.GetMeshGroupIndex() != node1.GetMeshGroupIndex()) { + return false; + } + if (node0.GetSkinIndex() != node1.GetSkinIndex()) { + return false; + } + + // Check equivalence of node transformations. + if (node0.GetTrsMatrix().ComputeTransformationMatrix() != + node1.GetTrsMatrix().ComputeTransformationMatrix()) { + return false; + } + + // Check equivalence of node children. + if (node0.NumChildren() != node1.NumChildren()) { + return false; + } + for (int i = 0; i < node0.NumChildren(); i++) { + if (node0.Child(i) != node1.Child(i)) { + return false; + } + } + + // Check equivalence of node parents. + if (node0.NumParents() != node1.NumParents()) { + return false; + } + for (int i = 0; i < node0.NumParents(); i++) { + if (node0.Parent(i) != node1.Parent(i)) { + return false; + } + } + return true; +} + +} // namespace draco + +#endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/scene/scene_are_equivalent.h draco-1.5.5+dfsg/src/draco/scene/scene_are_equivalent.h --- draco-1.5.3+dfsg/src/draco/scene/scene_are_equivalent.h 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/scene/scene_are_equivalent.h 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,42 @@ +// Copyright 2019 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef DRACO_SCENE_SCENE_ARE_EQUIVALENT_H_ +#define DRACO_SCENE_SCENE_ARE_EQUIVALENT_H_ + +#include "draco/draco_features.h" + +#ifdef DRACO_TRANSCODER_SUPPORTED +#include "draco/scene/scene.h" + +namespace draco { + +// A functor to compare two scenes for equivalency up to permutation of mesh +// vertices. +class SceneAreEquivalent { + public: + // Returns true if both scenes are equivalent up to permutation of + // the internal order of mesh vertices. This includes all attributes. + bool operator()(const Scene &scene0, const Scene &scene1); + + private: + static bool AreEquivalent(const Mesh &mesh0, const Mesh &mesh1); + static bool AreEquivalent(const SceneNode &node0, const SceneNode &node1); +}; + +} // namespace draco + +#endif // DRACO_TRANSCODER_SUPPORTED +#endif // DRACO_SCENE_SCENE_ARE_EQUIVALENT_H_ diff -Nru draco-1.5.3+dfsg/src/draco/scene/scene_are_equivalent_test.cc draco-1.5.5+dfsg/src/draco/scene/scene_are_equivalent_test.cc --- draco-1.5.3+dfsg/src/draco/scene/scene_are_equivalent_test.cc 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/scene/scene_are_equivalent_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,86 @@ +// Copyright 2019 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "draco/scene/scene_are_equivalent.h" + +#include +#include +#include +#include + +#include "draco/core/draco_test_base.h" +#include "draco/core/draco_test_utils.h" +#include "draco/io/scene_io.h" +#include "draco/scene/scene.h" + +namespace draco { + +#ifdef DRACO_TRANSCODER_SUPPORTED +class SceneAreEquivalentTest : public ::testing::Test {}; + +TEST_F(SceneAreEquivalentTest, TestOnIndenticalScenes) { + const std::string file_name = "CesiumMilkTruck/glTF/CesiumMilkTruck.gltf"; + const std::unique_ptr scene(ReadSceneFromTestFile(file_name)); + ASSERT_NE(scene, nullptr) << "Failed to load test scene: " << file_name; + + // Add mesh feature ID set to a scene mesh. + std::unique_ptr mesh_features(new MeshFeatures()); + scene->GetMesh(MeshIndex(2)).AddMeshFeatures(std::move(mesh_features)); + + SceneAreEquivalent equiv; + ASSERT_TRUE(equiv(*scene, *scene)); +} + +TEST_F(SceneAreEquivalentTest, TestOnDifferentScenes) { + const std::string file_name0 = "CesiumMilkTruck/glTF/CesiumMilkTruck.gltf"; + const std::string file_name1 = "Lantern/glTF/Lantern.gltf"; + const std::unique_ptr scene0(ReadSceneFromTestFile(file_name0)); + const std::unique_ptr scene1(ReadSceneFromTestFile(file_name1)); + ASSERT_NE(scene0, nullptr) << "Failed to load test scene: " << file_name0; + ASSERT_NE(scene1, nullptr) << "Failed to load test scene: " << file_name1; + SceneAreEquivalent equiv; + ASSERT_FALSE(equiv(*scene0, *scene1)); +} + +TEST_F(SceneAreEquivalentTest, TestMeshFeatures) { + const std::string file_name = "CesiumMilkTruck/glTF/CesiumMilkTruck.gltf"; + const std::unique_ptr scene0(ReadSceneFromTestFile(file_name)); + const std::unique_ptr scene1(ReadSceneFromTestFile(file_name)); + ASSERT_NE(scene0, nullptr); + ASSERT_NE(scene1, nullptr); + + // Add identical mesh feature ID sets to mesh at index 0. + Mesh &mesh0 = scene0->GetMesh(MeshIndex(0)); + Mesh &mesh1 = scene1->GetMesh(MeshIndex(0)); + mesh0.AddMeshFeatures(std::unique_ptr(new MeshFeatures())); + mesh1.AddMeshFeatures(std::unique_ptr(new MeshFeatures())); + + // Empty feature sets should match. + SceneAreEquivalent equiv; + ASSERT_TRUE(equiv(*scene0, *scene1)); + + // Make mesh features different and check that the meshes are not equivalent. + mesh0.GetMeshFeatures(MeshFeaturesIndex(0)).SetFeatureCount(5); + mesh1.GetMeshFeatures(MeshFeaturesIndex(0)).SetFeatureCount(6); + ASSERT_FALSE(equiv(*scene0, *scene1)); + + // Make mesh features identical and check that the meshes are equivalent. + mesh0.GetMeshFeatures(MeshFeaturesIndex(0)).SetFeatureCount(1); + mesh1.GetMeshFeatures(MeshFeaturesIndex(0)).SetFeatureCount(1); + ASSERT_TRUE(equiv(*scene0, *scene1)); +} + +#endif // DRACO_TRANSCODER_SUPPORTED + +} // namespace draco diff -Nru draco-1.5.3+dfsg/src/draco/scene/scene.cc draco-1.5.5+dfsg/src/draco/scene/scene.cc --- draco-1.5.3+dfsg/src/draco/scene/scene.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/scene/scene.cc 2022-10-29 00:55:03.000000000 +0000 @@ -68,6 +68,28 @@ } material_library_.Copy(s.material_library_); + +#ifdef DRACO_TRANSCODER_SUPPORTED + // Copy non-material textures. + non_material_texture_library_.Copy(s.non_material_texture_library_); + + // Update pointers to non-material textures in mesh feature ID sets of all + // scene meshes. + if (non_material_texture_library_.NumTextures() != 0) { + const auto texture_to_index_map = + s.non_material_texture_library_.ComputeTextureToIndexMap(); + for (MeshIndex i(0); i < NumMeshes(); ++i) { + for (MeshFeaturesIndex j(0); j < GetMesh(i).NumMeshFeatures(); ++j) { + Mesh::UpdateMeshFeaturesTexturePointer(texture_to_index_map, + &non_material_texture_library_, + &GetMesh(i).GetMeshFeatures(j)); + } + } + } +#endif // DRACO_TRANSCODER_SUPPORTED + + // Copy structural metadata. + structural_metadata_.Copy(s.structural_metadata_); } Status Scene::RemoveMesh(MeshIndex index) { diff -Nru draco-1.5.3+dfsg/src/draco/scene/scene.h draco-1.5.5+dfsg/src/draco/scene/scene.h --- draco-1.5.3+dfsg/src/draco/scene/scene.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/scene/scene.h 2022-10-29 00:55:03.000000000 +0000 @@ -23,6 +23,7 @@ #include "draco/animation/animation.h" #include "draco/animation/skin.h" #include "draco/mesh/mesh.h" +#include "draco/metadata/structural_metadata.h" #include "draco/scene/instance_array.h" #include "draco/scene/light.h" #include "draco/scene/mesh_group.h" @@ -140,6 +141,20 @@ } MaterialLibrary &GetMaterialLibrary() { return material_library_; } + // Library that contains non-material textures. + const TextureLibrary &GetNonMaterialTextureLibrary() const { + return non_material_texture_library_; + } + TextureLibrary &GetNonMaterialTextureLibrary() { + return non_material_texture_library_; + } + + // Structural metadata. + const StructuralMetadata &GetStructuralMetadata() const { + return structural_metadata_; + } + StructuralMetadata &GetStructuralMetadata() { return structural_metadata_; } + // Creates an animation and returns the index to the animation. AnimationIndex AddAnimation() { std::unique_ptr animation(new Animation()); @@ -225,6 +240,16 @@ // Materials used by this scene. MaterialLibrary material_library_; + + // Texture library for storing non-material textures used by this scene, e.g., + // textures containing mesh feature IDs of EXT_mesh_features glTF extension. + // Note that scene meshes contain pointers to non-material textures. It is + // responsibility of class user to update these pointers when updating the + // textures. See Scene::Copy() for example. + TextureLibrary non_material_texture_library_; + + // Structural metadata defined by the EXT_structural_metadata glTF extension. + StructuralMetadata structural_metadata_; }; } // namespace draco diff -Nru draco-1.5.3+dfsg/src/draco/scene/scene_test.cc draco-1.5.5+dfsg/src/draco/scene/scene_test.cc --- draco-1.5.3+dfsg/src/draco/scene/scene_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/scene/scene_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -14,10 +14,15 @@ // #include "draco/scene/scene.h" +#include +#include +#include + #include "draco/core/draco_test_base.h" #include "draco/core/draco_test_utils.h" #include "draco/core/status.h" #include "draco/mesh/mesh_are_equivalent.h" +#include "draco/scene/scene_are_equivalent.h" #include "draco/scene/scene_indices.h" namespace { @@ -263,6 +268,28 @@ ASSERT_FALSE(dst_scene.RemoveMaterial(3).ok()); } +TEST(SceneTest, TestCopyWithStructuralMetadata) { + // Tests copying of a scene with structural metadata. + auto scene_ptr = + draco::ReadSceneFromTestFile("CesiumMilkTruck/glTF/CesiumMilkTruck.gltf"); + ASSERT_NE(scene_ptr, nullptr); + draco::Scene &scene = *scene_ptr; + + // Add structural metadata to the scene. + draco::PropertyTable::Schema schema; + schema.json.SetString("Data"); + scene.GetStructuralMetadata().SetPropertyTableSchema(schema); + + // Copy the scene. + draco::Scene copy; + copy.Copy(scene); + + // Check that the structural metadata has been copied. + ASSERT_EQ( + copy.GetStructuralMetadata().GetPropertyTableSchema().json.GetString(), + "Data"); +} + #endif // DRACO_TRANSCODER_SUPPORTED } // namespace diff -Nru draco-1.5.3+dfsg/src/draco/scene/scene_utils.cc draco-1.5.5+dfsg/src/draco/scene/scene_utils.cc --- draco-1.5.3+dfsg/src/draco/scene/scene_utils.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/scene/scene_utils.cc 2022-10-29 00:55:03.000000000 +0000 @@ -16,11 +16,14 @@ #include "draco/scene/scene_utils.h" #ifdef DRACO_TRANSCODER_SUPPORTED +#include #include #include +#include #include #include +#include "draco/core/draco_index_type_vector.h" #include "draco/core/hash_utils.h" #include "draco/core/vector_d.h" #include "draco/mesh/mesh_splitter.h" @@ -200,6 +203,22 @@ return mesh_bbox; } +namespace { + +// Updates texture pointers in mesh features of |mesh| to texture pointers +// stored in |new_texture_library|. |texture_to_index_map| stores texture +// indices of the old texture pointers within |mesh|. +void UpdateMeshFeaturesTexturesOnMesh( + const std::unordered_map &texture_to_index_map, + TextureLibrary *new_texture_library, Mesh *mesh) { + for (MeshFeaturesIndex mfi(0); mfi < mesh->NumMeshFeatures(); ++mfi) { + mesh->UpdateMeshFeaturesTexturePointer( + texture_to_index_map, new_texture_library, &mesh->GetMeshFeatures(mfi)); + } +} + +} // namespace + StatusOr> SceneUtils::MeshToScene( std::unique_ptr mesh) { const size_t num_mesh_materials = mesh->GetMaterialLibrary().NumMaterials(); @@ -212,6 +231,13 @@ scene->GetMaterialLibrary().MutableMaterial(0); } + // Copy mesh feature textures. + scene->GetNonMaterialTextureLibrary().Copy( + mesh->GetNonMaterialTextureLibrary()); + + const auto old_texture_to_index_map = + mesh->GetNonMaterialTextureLibrary().ComputeTextureToIndexMap(); + const SceneNodeIndex scene_node_index = scene->AddNode(); SceneNode *const scene_node = scene->GetNode(scene_node_index); const MeshGroupIndex mesh_group_index = scene->AddMeshGroup(); @@ -224,6 +250,11 @@ return Status(Status::DRACO_ERROR, "Could not add Draco mesh to scene."); } mesh_group->AddMeshInstance({mesh_index, 0, {}}); + + UpdateMeshFeaturesTexturesOnMesh(old_texture_to_index_map, + &scene->GetNonMaterialTextureLibrary(), + &scene->GetMesh(mesh_index)); + } else { const int32_t mat_att_id = mesh->GetNamedAttributeId(GeometryAttribute::MATERIAL); @@ -261,6 +292,13 @@ int material_index = 0; mat_att->GetValue(AttributeValueIndex(i), &material_index); mesh_group->AddMeshInstance({mesh_index, material_index, {}}); + + // Copy over mesh features that were associated with the |material_index|. + Mesh &scene_mesh = scene->GetMesh(mesh_index); + Mesh::CopyMeshFeaturesForMaterial(*mesh, &scene_mesh, material_index); + UpdateMeshFeaturesTexturesOnMesh(old_texture_to_index_map, + &scene->GetNonMaterialTextureLibrary(), + &scene_mesh); } } @@ -640,7 +678,7 @@ } // Find materials that reference a texture. - const MaterialLibrary &material_library = scene->GetMaterialLibrary(); + MaterialLibrary &material_library = scene->GetMaterialLibrary(); std::vector materials_with_textures(material_library.NumMaterials(), false); for (int i = 0; i < material_library.NumMaterials(); ++i) { @@ -649,31 +687,84 @@ } } - // Find which materials have a refernece to them. - std::vector is_material_referenced(material_library.NumMaterials(), - false); - // Remove TEX_COORD attributes for meshes that reference a material that does - // not contain any texture maps. - // TODO(fgalligan): Remove TEX_COORD attributes for meshes that references a - // material that does not reference the specific texture. E.g. A mesh has two - // TEX_COORD attributes but the material only uses one of the TEX_COORD - // attributes. + // Maps material index to a set of meshes that use that material. + std::vector> material_meshes( + material_library.NumMaterials()); + + // Maps mesh index to a set of materials used by that mesh. + IndexTypeVector> mesh_materials( + scene->NumMeshes()); + + // Maps mesh index to a set of tex coord indices referenced by materials. + IndexTypeVector> tex_coord_referenced( + scene->NumMeshes()); + + // Populate the maps that will be used to remove unused texture coordinates. for (int mgi = 0; mgi < scene->NumMeshGroups(); ++mgi) { const MeshGroup *const mesh_group = scene->GetMeshGroup(MeshGroupIndex(mgi)); for (int mi = 0; mi < mesh_group->NumMeshInstances(); ++mi) { const MeshIndex mesh_index = mesh_group->GetMeshInstance(mi).mesh_index; const int material_index = mesh_group->GetMeshInstance(mi).material_index; - if (material_index > -1) { - is_material_referenced[material_index] = true; + if (material_index == -1) { + continue; + } + + // Populate mesh-material mapping. + material_meshes[material_index].insert(mesh_index); + mesh_materials[mesh_index].insert(material_index); + + // Populate texture coordinate indices referenced by material textures. + const auto material = material_library.GetMaterial(material_index); + for (int i = 0; i < material->NumTextureMaps(); i++) { + const TextureMap *const texture_map = material->GetTextureMapByIndex(i); + const int tex_coord_index = texture_map->tex_coord_index(); + tex_coord_referenced[mesh_index].insert(tex_coord_index); + } + } + } - if (!materials_with_textures[material_index]) { - if (options.remove_unused_tex_coords) { - Mesh &mesh = scene->GetMesh(mesh_index); - // |mesh| references a material that does not have a texture. - while (mesh.NumNamedAttributes(GeometryAttribute::TEX_COORD) > 0) { - mesh.DeleteAttribute( - mesh.GetNamedAttributeId(GeometryAttribute::TEX_COORD, 0)); + // From each mesh, remove texture coordinate attributes that are not + // referenced by any materials and decrement texture coordinate indices in + // texture maps of the mesh materials accordingly. + if (options.remove_unused_tex_coords) { + for (MeshIndex mi(0); mi < scene->NumMeshes(); ++mi) { + // Do not remove unreferenced texture coordinates when the mesh materials + // are used by any other meshes to avoid corrupting those other meshes. + // TODO(vytyaz): Consider removing this limitation. + bool remove_tex_coord = true; + for (const int material_index : mesh_materials[mi]) { + if (material_meshes[material_index].size() != 1) { + // Materials of this mesh are used by other meshes. + remove_tex_coord = false; + break; + } + } + if (!remove_tex_coord) { + continue; + } + + // Remove unreferenced texture coordinate sets from this mesh. + Mesh &mesh = scene->GetMesh(mi); + const int tex_coord_count = + mesh.NumNamedAttributes(GeometryAttribute::TEX_COORD); + for (int tci = tex_coord_count - 1; tci >= 0; tci--) { + if (tex_coord_referenced[mi].count(tci) != 0) { + // Texture coordinate set is referenced. + continue; + } + mesh.DeleteAttribute( + mesh.GetNamedAttributeId(GeometryAttribute::TEX_COORD, tci)); + + // Decrement texture coordinate indices in all materials of this mesh. + for (const int material_index : mesh_materials[mi]) { + auto material = material_library.MutableMaterial(material_index); + for (int i = 0; i < material->NumTextureMaps(); i++) { + auto texture_map = material->GetTextureMapByIndex(i); + // Decrement the indices that are greater than the removed index. + if (texture_map->tex_coord_index() > tci) { + texture_map->SetProperties(texture_map->type(), + texture_map->tex_coord_index() - 1); } } } @@ -682,10 +773,10 @@ } if (options.remove_unused_materials) { - // Remove materials with no reference to them. + // Remove materials that are not used by any mesh. for (int i = material_library.NumMaterials() - 1; i >= 0; --i) { - if (!is_material_referenced[i]) { - // Material |i| is not referenced. + if (material_meshes[i].empty()) { + // Material |i| is not used. scene->RemoveMaterial(i); } } @@ -834,6 +925,38 @@ return false; } +IndexTypeVector +SceneUtils::FindLargestBaseMeshTransforms(const Scene &scene) { + IndexTypeVector transforms( + scene.NumMeshes(), Eigen::Matrix4d::Identity()); + + // In case a mesh has multiple instances we want to use the instance with + // the largest scale. + IndexTypeVector transform_scale(scene.NumMeshes(), 0.f); + + const auto instances = SceneUtils::ComputeAllInstances(scene); + for (MeshInstanceIndex i(0); i < instances.size(); ++i) { + const auto &instance = instances[i]; + + // Compute the scale of the transform. + const Vector3f scale_vec(instance.transform.col(0).norm(), + instance.transform.col(1).norm(), + instance.transform.col(2).norm()); + + // In our framework we support uniform scale only. For now, just take the + // maximum scale across all axes. + // TODO(ostava): Investigate how to properly support non-uniform scaling. + const float max_scale = scale_vec.MaxCoeff(); + + if (transform_scale[instance.mesh_index] < max_scale) { + transform_scale[instance.mesh_index] = max_scale; + transforms[instance.mesh_index] = instance.transform; + } + } + + return transforms; +} + } // namespace draco #endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/scene/scene_utils.h draco-1.5.5+dfsg/src/draco/scene/scene_utils.h --- draco-1.5.3+dfsg/src/draco/scene/scene_utils.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/scene/scene_utils.h 2022-10-29 00:55:03.000000000 +0000 @@ -115,7 +115,7 @@ bool remove_unused_mesh_groups = true; bool remove_unused_meshes = true; bool remove_unused_nodes = false; - bool remove_unused_tex_coords = true; + bool remove_unused_tex_coords = false; bool remove_unused_materials = true; }; static void Cleanup(Scene *scene); @@ -137,6 +137,11 @@ // Returns true if geometry compression is eabled for any of |scene| meshes. static bool IsDracoCompressionEnabled(const Scene &scene); + + // Returns a single tranformation matrix for each base mesh of the |scene| + // corresponding to the instance with the maximum scale. + static IndexTypeVector + FindLargestBaseMeshTransforms(const Scene &scene); }; } // namespace draco diff -Nru draco-1.5.3+dfsg/src/draco/scene/scene_utils_test.cc draco-1.5.5+dfsg/src/draco/scene/scene_utils_test.cc --- draco-1.5.3+dfsg/src/draco/scene/scene_utils_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/scene/scene_utils_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -305,6 +305,52 @@ // because scene has two mesh groups and scene_from_mesh has only one. } +TEST(SceneUtilsTest, TestMeshToSceneMultipleMeshFeatures) { + const std::string filename = "BoxesMeta/glTF/BoxesMeta.gltf"; + std::unique_ptr scene = draco::ReadSceneFromTestFile(filename); + ASSERT_NE(scene, nullptr); + std::unique_ptr mesh = draco::ReadMeshFromTestFile(filename); + ASSERT_NE(mesh, nullptr); + ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 2); + ASSERT_EQ(mesh->NumMeshFeatures(), 5); + + DRACO_ASSIGN_OR_ASSERT(const std::unique_ptr scene_from_mesh, + draco::SceneUtils::MeshToScene(std::move(mesh))); + ASSERT_NE(scene_from_mesh, nullptr); + ASSERT_EQ(scene_from_mesh->NumMeshes(), 2); + ASSERT_EQ(scene_from_mesh->GetMaterialLibrary().NumMaterials(), 2); + ASSERT_EQ(scene_from_mesh->NumMeshGroups(), 1); + const draco::MeshGroup *const mesh_group = + scene_from_mesh->GetMeshGroup(draco::MeshGroupIndex(0)); + ASSERT_EQ(mesh_group->NumMeshInstances(), 2); + + // Meshes of the new scene should have the same properties as meshes loaded + // directly into |scene|. + for (draco::MeshIndex mi(0); mi < scene->NumMeshes(); ++mi) { + ASSERT_EQ(scene->GetMesh(mi).NumMeshFeatures(), + scene_from_mesh->GetMesh(mi).NumMeshFeatures()); + for (draco::MeshFeaturesIndex mfi(0); + mfi < scene->GetMesh(mi).NumMeshFeatures(); ++mfi) { + const auto &scene_mf = scene->GetMesh(mi).GetMeshFeatures(mfi); + const auto &scene_from_mesh_mf = + scene_from_mesh->GetMesh(mi).GetMeshFeatures(mfi); + ASSERT_EQ(scene_mf.GetAttributeIndex(), + scene_from_mesh_mf.GetAttributeIndex()); + ASSERT_EQ(scene_mf.GetPropertyTableIndex(), + scene_from_mesh_mf.GetPropertyTableIndex()); + ASSERT_EQ(scene_mf.GetLabel(), scene_from_mesh_mf.GetLabel()); + ASSERT_EQ(scene_mf.GetNullFeatureId(), + scene_from_mesh_mf.GetNullFeatureId()); + ASSERT_EQ(scene_mf.GetFeatureCount(), + scene_from_mesh_mf.GetFeatureCount()); + ASSERT_EQ(scene_mf.GetTextureChannels(), + scene_from_mesh_mf.GetTextureChannels()); + ASSERT_EQ(scene_mf.GetTextureMap().texture() != nullptr, + scene_from_mesh_mf.GetTextureMap().texture() != nullptr); + } + } +} + TEST(SceneUtilsTest, TestInstantiateMeshWithIdentityTransformation) { auto scene = draco::ReadSceneFromTestFile("CesiumMilkTruck/glTF/CesiumMilkTruck.gltf"); @@ -522,7 +568,7 @@ ASSERT_EQ(draco::SceneUtils::ComputeAllInstances(*scene).size(), 7); } -TEST(SceneUtilsTest, TestCleanupUnusedTexCoords) { +TEST(SceneUtilsTest, TestCleanupUnusedTexCoordsNoTextures) { // The glTF file has two tex coords that are unused because the materials do // not reference any textures. auto scene = draco::ReadSceneFromTestFile("UnusedTexCoords/NoTextures.gltf"); @@ -531,13 +577,77 @@ .NumNamedAttributes(draco::GeometryAttribute::TEX_COORD), 2); - // Cleanup scene. + // Cleanup scene and check that unused UV are not removed by default. draco::SceneUtils::Cleanup(scene.get()); ASSERT_EQ(scene->GetMesh(draco::MeshIndex(0)) .NumNamedAttributes(draco::GeometryAttribute::TEX_COORD), + 2); + + // Cleanup scene and check that unused UV are removed when requested. + draco::SceneUtils::CleanupOptions options; + options.remove_unused_tex_coords = true; + draco::SceneUtils::Cleanup(scene.get(), options); + ASSERT_EQ(scene->GetMesh(draco::MeshIndex(0)) + .NumNamedAttributes(draco::GeometryAttribute::TEX_COORD), 0); } +TEST(SceneUtilsTest, TestCleanupUnusedTexCoords0NoReferences) { + auto scene = draco::ReadSceneFromTestFile( + "UnusedTexCoords/TexCoord0InvalidTexCoord1Valid.gltf"); + ASSERT_NE(scene, nullptr); + typedef draco::GeometryAttribute Att; + + draco::Mesh &mesh = scene->GetMesh(draco::MeshIndex(0)); + ASSERT_EQ(mesh.NumNamedAttributes(Att::TEX_COORD), 2); + ASSERT_EQ(mesh.GetNamedAttribute(Att::TEX_COORD, 0)->size(), 14); + ASSERT_EQ(mesh.GetNamedAttribute(Att::TEX_COORD, 1)->size(), 4); + auto &ml = scene->GetMaterialLibrary(); + ASSERT_EQ(ml.NumMaterials(), 1); + ASSERT_EQ(ml.GetMaterial(0)->NumTextureMaps(), 1); + ASSERT_EQ(ml.GetMaterial(0)->GetTextureMapByIndex(0)->tex_coord_index(), 1); + + // Cleanup unused texture coordinate attributes. + draco::SceneUtils::CleanupOptions options; + options.remove_unused_tex_coords = true; + draco::SceneUtils::Cleanup(scene.get(), options); + + // Check that the unreferenced attribute was removed. + ASSERT_EQ(mesh.NumNamedAttributes(Att::TEX_COORD), 1); + ASSERT_EQ(mesh.GetNamedAttribute(Att::TEX_COORD, 0)->size(), 4); + ASSERT_EQ(ml.NumMaterials(), 1); + ASSERT_EQ(ml.GetMaterial(0)->NumTextureMaps(), 1); + ASSERT_EQ(ml.GetMaterial(0)->GetTextureMapByIndex(0)->tex_coord_index(), 0); +} + +TEST(SceneUtilsTest, TestCleanupUnusedTexCoords1NoReferences) { + auto scene = draco::ReadSceneFromTestFile( + "UnusedTexCoords/TexCoord0ValidTexCoord1Invalid.gltf"); + ASSERT_NE(scene, nullptr); + typedef draco::GeometryAttribute Att; + + draco::Mesh &mesh = scene->GetMesh(draco::MeshIndex(0)); + ASSERT_EQ(mesh.NumNamedAttributes(Att::TEX_COORD), 2); + ASSERT_EQ(mesh.GetNamedAttribute(Att::TEX_COORD, 0)->size(), 14); + ASSERT_EQ(mesh.GetNamedAttribute(Att::TEX_COORD, 1)->size(), 4); + auto &ml = scene->GetMaterialLibrary(); + ASSERT_EQ(ml.NumMaterials(), 1); + ASSERT_EQ(ml.GetMaterial(0)->NumTextureMaps(), 1); + ASSERT_EQ(ml.GetMaterial(0)->GetTextureMapByIndex(0)->tex_coord_index(), 0); + + // Cleanup unused texture coordinate attributes. + draco::SceneUtils::CleanupOptions options; + options.remove_unused_tex_coords = true; + draco::SceneUtils::Cleanup(scene.get(), options); + + // Check that the unreferenced attribute was removed. + ASSERT_EQ(mesh.NumNamedAttributes(Att::TEX_COORD), 1); + ASSERT_EQ(mesh.GetNamedAttribute(Att::TEX_COORD, 0)->size(), 14); + ASSERT_EQ(ml.NumMaterials(), 1); + ASSERT_EQ(ml.GetMaterial(0)->NumTextureMaps(), 1); + ASSERT_EQ(ml.GetMaterial(0)->GetTextureMapByIndex(0)->tex_coord_index(), 0); +} + TEST(SceneUtilsTest, TestComputeGlobalNodeTransform) { // Tests that we can compute global transformation of scene nodes. @@ -628,6 +738,26 @@ ASSERT_FALSE(scene->GetMesh(MeshIndex(3)).IsCompressionEnabled()); } +TEST(SceneUtilsTest, TestFindLargestBaseMeshTransforms) { + // Tests that FindLargestBaseMeshTransforms() works as expected. + auto scene = + draco::ReadSceneFromTestFile("CubeScaledInstances/glTF/cube_att.gltf"); + ASSERT_NE(scene, nullptr); + + // There should be one base mesh with four instances. + ASSERT_EQ(scene->NumMeshes(), 1); + ASSERT_EQ(draco::SceneUtils::ComputeAllInstances(*scene).size(), 4); + + const auto transforms = + draco::SceneUtils::FindLargestBaseMeshTransforms(*scene); + + ASSERT_EQ(transforms.size(), 1); // One transform for the single base mesh. + + // The largest instance should have a uniform scale 4. + const draco::MeshIndex mi(0); + ASSERT_EQ(transforms[mi].diagonal(), Eigen::Vector4d(4, 4, 4, 1)); +} + } // namespace #endif // DRACO_TRANSCODER_SUPPORTED diff -Nru draco-1.5.3+dfsg/src/draco/texture/texture.h draco-1.5.5+dfsg/src/draco/texture/texture.h --- draco-1.5.3+dfsg/src/draco/texture/texture.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/texture/texture.h 2022-10-29 00:55:03.000000000 +0000 @@ -18,6 +18,9 @@ #include "draco/draco_features.h" #ifdef DRACO_TRANSCODER_SUPPORTED +#include +#include + #include "draco/io/image_compression_options.h" #include "draco/texture/source_image.h" diff -Nru draco-1.5.3+dfsg/src/draco/texture/texture_library.cc draco-1.5.5+dfsg/src/draco/texture/texture_library.cc --- draco-1.5.3+dfsg/src/draco/texture/texture_library.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/texture/texture_library.cc 2022-10-29 00:55:03.000000000 +0000 @@ -14,6 +14,8 @@ // #include "draco/texture/texture_library.h" +#include + #ifdef DRACO_TRANSCODER_SUPPORTED namespace draco { @@ -39,6 +41,15 @@ return textures_.size() - 1; } +std::unordered_map +TextureLibrary::ComputeTextureToIndexMap() const { + std::unordered_map ret; + for (int i = 0; i < textures_.size(); ++i) { + ret[textures_[i].get()] = i; + } + return ret; +} + std::unique_ptr TextureLibrary::RemoveTexture(int index) { std::unique_ptr ret = std::move(textures_[index]); textures_.erase(textures_.begin() + index); diff -Nru draco-1.5.3+dfsg/src/draco/texture/texture_library.h draco-1.5.5+dfsg/src/draco/texture/texture_library.h --- draco-1.5.3+dfsg/src/draco/texture/texture_library.h 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/texture/texture_library.h 2022-10-29 00:55:03.000000000 +0000 @@ -19,6 +19,7 @@ #ifdef DRACO_TRANSCODER_SUPPORTED #include +#include #include #include "draco/texture/texture.h" @@ -48,6 +49,9 @@ Texture *GetTexture(int index) { return textures_[index].get(); } const Texture *GetTexture(int index) const { return textures_[index].get(); } + // Returns a map from texture pointer to texture index for all textures. + std::unordered_map ComputeTextureToIndexMap() const; + // Removes and returns a texture from the library. The returned texture can be // either used by the caller or ignored in which case it would be // automatically deleted. diff -Nru draco-1.5.3+dfsg/src/draco/texture/texture_library_test.cc draco-1.5.5+dfsg/src/draco/texture/texture_library_test.cc --- draco-1.5.3+dfsg/src/draco/texture/texture_library_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/texture/texture_library_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -14,6 +14,8 @@ // #include "draco/texture/texture_library.h" +#include + #include "draco/core/draco_test_utils.h" #include "draco/io/texture_io.h" diff -Nru draco-1.5.3+dfsg/src/draco/texture/texture_utils_test.cc draco-1.5.5+dfsg/src/draco/texture/texture_utils_test.cc --- draco-1.5.3+dfsg/src/draco/texture/texture_utils_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/texture/texture_utils_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -19,7 +19,7 @@ #ifdef DRACO_TRANSCODER_SUPPORTED #include "draco/core/draco_test_utils.h" #include "draco/io/texture_io.h" -#include "draco/texture/color.h" +#include "draco/texture/color_utils.h" namespace { diff -Nru draco-1.5.3+dfsg/src/draco/tools/draco_transcoder.cc draco-1.5.5+dfsg/src/draco/tools/draco_transcoder.cc --- draco-1.5.3+dfsg/src/draco/tools/draco_transcoder.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/tools/draco_transcoder.cc 2022-10-29 00:55:03.000000000 +0000 @@ -92,8 +92,8 @@ } else if (!strcmp("-o", argv[i]) && i < argc_check) { file_options.output_filename = argv[++i]; } else if (!strcmp("-qp", argv[i]) && i < argc_check) { - transcode_options.geometry.quantization_bits_position = - StringToInt(argv[++i]); + transcode_options.geometry.quantization_position.SetQuantizationBits( + StringToInt(argv[++i])); } else if (!strcmp("-qt", argv[i]) && i < argc_check) { transcode_options.geometry.quantization_bits_tex_coord = StringToInt(argv[++i]); diff -Nru draco-1.5.3+dfsg/src/draco/tools/draco_transcoder_lib_test.cc draco-1.5.5+dfsg/src/draco/tools/draco_transcoder_lib_test.cc --- draco-1.5.3+dfsg/src/draco/tools/draco_transcoder_lib_test.cc 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/tools/draco_transcoder_lib_test.cc 2022-10-29 00:55:03.000000000 +0000 @@ -159,7 +159,7 @@ const size_t first_glb_size = draco::GetFileSize(draco::GetTestTempFileFullPath("first.glb")); - options.geometry.quantization_bits_position = 10; + options.geometry.quantization_position.SetQuantizationBits(10); DRACO_ASSIGN_OR_ASSERT(std::unique_ptr dt2, draco::DracoTranscoder::Create(options)); file_options.output_filename = draco::GetTestTempFileFullPath("second.glb"); diff -Nru draco-1.5.3+dfsg/src/draco/tools/install_test/CMakeLists.txt draco-1.5.5+dfsg/src/draco/tools/install_test/CMakeLists.txt --- draco-1.5.3+dfsg/src/draco/tools/install_test/CMakeLists.txt 2022-07-07 02:40:17.000000000 +0000 +++ draco-1.5.5+dfsg/src/draco/tools/install_test/CMakeLists.txt 2022-10-29 00:55:03.000000000 +0000 @@ -1,16 +1,16 @@ # Copyright 2022 The Draco Authors # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. cmake_minimum_required(VERSION 3.12) project(install_test C CXX) diff -Nru draco-1.5.3+dfsg/testdata/BoxesMeta/glTF/BoxesMeta.gltf draco-1.5.5+dfsg/testdata/BoxesMeta/glTF/BoxesMeta.gltf --- draco-1.5.3+dfsg/testdata/BoxesMeta/glTF/BoxesMeta.gltf 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/testdata/BoxesMeta/glTF/BoxesMeta.gltf 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,295 @@ +{ + "asset": { + "version": "2.0", + "generator": "draco_decoder" + }, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "scene": 0, + "nodes": [ + { + "children": [ + 1 + ], + "translation": [ + 0, + 0, + 2 + ] + }, + { + "mesh": 0 + } + ], + "meshes": [ + { + "name": "Mesh", + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 1, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "_FEATURE_ID_0": 5 + }, + "indices": 0, + "mode": 4, + "material": 0, + "extensions": { + "EXT_mesh_features": { + "featureIds": [ + { + "label": "faces", + "featureCount": 12, + "attribute": 0, + "propertyTable": 0, + "nullFeatureId": 100 + }, + { + "label": "water", + "featureCount": 2, + "texture": { + "index": 1, + "texCoord": 1, + "channels": [ + 1, + 2, + 3 + ] + } + } + ] + } + } + }, + { + "attributes": { + "NORMAL": 2, + "POSITION": 1, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "_FEATURE_ID_0": 5, + "_FEATURE_ID_1": 7 + }, + "indices": 0, + "mode": 4, + "material": 1, + "extensions": { + "EXT_mesh_features": { + "featureIds": [ + { + "featureCount": 36, + "attribute": 0 + }, + { + "featureCount": 6, + "texture": { + "index": 0, + "texCoord": 0, + "channels": [ + 0 + ] + } + }, + { + "label": "water", + "featureCount": 2, + "texture": { + "index": 1, + "texCoord": 1, + "channels": [ + 1, + 2, + 3 + ] + } + } + ] + } + } + } + ] + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.80000001192092896, + 0, + 0, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE", + "name": "Red" + }, + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.80000001192092896, + 0, + 0, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE", + "name": "Red" + } + ], + "textures": [ + { + "source": 0, + "sampler": 0 + }, + { + "source": 1, + "sampler": 0 + } + ], + "samplers": [ + { + "wrapS": 33071, + "wrapT": 33071 + } + ], + "images": [ + { + "uri": "Texture0_MeshFeatures.png" + }, + { + "uri": "Texture1_MeshFeatures.png" + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5121, + "count": 36, + "type": "SCALAR" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 36, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 36, + "type": "VEC3" + }, + { + "bufferView": 3, + "componentType": 5126, + "count": 36, + "type": "VEC2" + }, + { + "bufferView": 4, + "componentType": 5126, + "count": 36, + "type": "VEC2" + }, + { + "bufferView": 5, + "componentType": 5121, + "count": 36, + "type": "SCALAR" + }, + { + "bufferView": 6, + "componentType": 5123, + "count": 36, + "type": "SCALAR" + }, + { + "bufferView": 7, + "componentType": 5126, + "count": 36, + "type": "SCALAR" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 36 + }, + { + "buffer": 0, + "byteOffset": 36, + "byteLength": 432 + }, + { + "buffer": 0, + "byteOffset": 468, + "byteLength": 432 + }, + { + "buffer": 0, + "byteOffset": 900, + "byteLength": 288 + }, + { + "buffer": 0, + "byteOffset": 1188, + "byteLength": 288 + }, + { + "buffer": 0, + "byteOffset": 1476, + "byteLength": 36 + }, + { + "buffer": 0, + "byteOffset": 1512, + "byteLength": 72 + }, + { + "buffer": 0, + "byteOffset": 1584, + "byteLength": 144 + } + ], + "buffers": [ + { + "byteLength": 1728, + "uri": "buffer0.bin" + } + ], + "extensionsUsed": [ + "EXT_mesh_features" + ] +} Binary files /tmp/tmpry1wl9ua/dzSl8hVTiE/draco-1.5.3+dfsg/testdata/BoxesMeta/glTF/buffer0.bin and /tmp/tmpry1wl9ua/XO2yWrPokU/draco-1.5.5+dfsg/testdata/BoxesMeta/glTF/buffer0.bin differ Binary files /tmp/tmpry1wl9ua/dzSl8hVTiE/draco-1.5.3+dfsg/testdata/BoxesMeta/glTF/Texture0_MeshFeatures.png and /tmp/tmpry1wl9ua/XO2yWrPokU/draco-1.5.5+dfsg/testdata/BoxesMeta/glTF/Texture0_MeshFeatures.png differ Binary files /tmp/tmpry1wl9ua/dzSl8hVTiE/draco-1.5.3+dfsg/testdata/BoxesMeta/glTF/Texture1_MeshFeatures.png and /tmp/tmpry1wl9ua/XO2yWrPokU/draco-1.5.5+dfsg/testdata/BoxesMeta/glTF/Texture1_MeshFeatures.png differ diff -Nru draco-1.5.3+dfsg/testdata/BoxMeta/glTF/BoxMeta.gltf draco-1.5.5+dfsg/testdata/BoxMeta/glTF/BoxMeta.gltf --- draco-1.5.3+dfsg/testdata/BoxMeta/glTF/BoxMeta.gltf 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/testdata/BoxMeta/glTF/BoxMeta.gltf 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,363 @@ +{ + "asset": { + "version": "2.0", + "generator": "draco_decoder" + }, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "scene": 0, + "nodes": [ + { + "children": [ + 1 + ], + "translation": [ + 0, + 0, + 2 + ] + }, + { + "mesh": 0 + } + ], + "meshes": [ + { + "name": "Mesh", + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 1, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "_FEATURE_ID_0": 5, + "_FEATURE_ID_1": 6, + "_FEATURE_ID_2": 7 + }, + "indices": 0, + "mode": 4, + "material": 0, + "extensions": { + "EXT_mesh_features": { + "featureIds": [ + { + "label": "faces", + "featureCount": 12, + "attribute": 0, + "propertyTable": 0, + "nullFeatureId": 100 + }, + { + "label": "vertices", + "featureCount": 8, + "attribute": 1, + "propertyTable": 1, + "nullFeatureId": 101 + }, + { + "featureCount": 36, + "attribute": 2 + }, + { + "featureCount": 6, + "texture": { + "index": 0, + "texCoord": 0, + "channels": [ + 0 + ] + } + }, + { + "label": "water", + "featureCount": 2, + "texture": { + "index": 1, + "texCoord": 1, + "channels": [ + 1, + 2, + 3 + ] + } + } + ] + } + } + } + ] + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.80000001192092896, + 0, + 0, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE", + "name": "Red" + } + ], + "textures": [ + { + "source": 0, + "sampler": 0 + }, + { + "source": 1, + "sampler": 0 + } + ], + "samplers": [ + { + "wrapS": 33071, + "wrapT": 33071 + } + ], + "images": [ + { + "uri": "Texture0_MeshFeatures.png" + }, + { + "uri": "Texture1_MeshFeatures.png" + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5121, + "count": 36, + "type": "SCALAR" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 36, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 36, + "type": "VEC3" + }, + { + "bufferView": 3, + "componentType": 5126, + "count": 36, + "type": "VEC2" + }, + { + "bufferView": 4, + "componentType": 5126, + "count": 36, + "type": "VEC2" + }, + { + "bufferView": 5, + "componentType": 5121, + "count": 36, + "type": "SCALAR" + }, + { + "bufferView": 6, + "componentType": 5123, + "count": 36, + "type": "SCALAR" + }, + { + "bufferView": 7, + "componentType": 5126, + "count": 36, + "type": "SCALAR" + } + ], + "extensions": { + "EXT_structural_metadata": { + "schema": { + "id": "galaxy", + "classes": { + "planet": { + "properties": { + "color": { + "componentType": "UINT8", + "description": "The RGB color.", + "required": true, + "type": "VEC3" + }, + "name": { + "description": "The name.", + "required": true, + "type": "STRING" + }, + "sequence": { + "description": "The number sequence.", + "required": false, + "type": "SCALAR" + } + } + } + }, + "enums": { + "classifications": { + "description": "Classifications of planets.", + "name": "classifications", + "values": [ + { + "name": "Unspecified", + "value": 0 + }, + { + "name": "Gas Giant", + "value": 1 + }, + { + "name": "Waterworld", + "value": 2 + }, + { + "name": "Agriworld", + "value": 3 + }, + { + "name": "Ordnance", + "value": 4 + } + ] + } + } + }, + "propertyTables": [ + { + "name": "Galaxy far far away.", + "class": "planet", + "count": 16, + "properties": { + "color": { + "values": 8 + }, + "name": { + "values": 9, + "stringOffsetType": "UINT32", + "stringOffsets": 10 + }, + "sequence": { + "values": 11, + "arrayOffsetType": "UINT8", + "arrayOffsets": 12 + } + } + } + ] + } + }, + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 36 + }, + { + "buffer": 0, + "byteOffset": 36, + "byteLength": 432 + }, + { + "buffer": 0, + "byteOffset": 468, + "byteLength": 432 + }, + { + "buffer": 0, + "byteOffset": 900, + "byteLength": 288 + }, + { + "buffer": 0, + "byteOffset": 1188, + "byteLength": 288 + }, + { + "buffer": 0, + "byteOffset": 1476, + "byteLength": 36 + }, + { + "buffer": 0, + "byteOffset": 1512, + "byteLength": 72 + }, + { + "buffer": 0, + "byteOffset": 1584, + "byteLength": 144 + }, + { + "buffer": 0, + "byteOffset": 1728, + "byteLength": 48, + "target": 34962 + }, + { + "buffer": 0, + "byteOffset": 1776, + "byteLength": 296, + "target": 34963 + }, + { + "buffer": 0, + "byteOffset": 2072, + "byteLength": 68, + "target": 34963 + }, + { + "buffer": 0, + "byteOffset": 2140, + "byteLength": 164, + "target": 34963 + }, + { + "buffer": 0, + "byteOffset": 2304, + "byteLength": 20, + "target": 34963 + } + ], + "buffers": [ + { + "byteLength": 2324, + "uri": "buffer0.bin" + } + ], + "extensionsUsed": [ + "EXT_mesh_features", + "EXT_structural_metadata" + ] +} Binary files /tmp/tmpry1wl9ua/dzSl8hVTiE/draco-1.5.3+dfsg/testdata/BoxMeta/glTF/buffer0.bin and /tmp/tmpry1wl9ua/XO2yWrPokU/draco-1.5.5+dfsg/testdata/BoxMeta/glTF/buffer0.bin differ Binary files /tmp/tmpry1wl9ua/dzSl8hVTiE/draco-1.5.3+dfsg/testdata/BoxMeta/glTF/Texture0_MeshFeatures.png and /tmp/tmpry1wl9ua/XO2yWrPokU/draco-1.5.5+dfsg/testdata/BoxMeta/glTF/Texture0_MeshFeatures.png differ Binary files /tmp/tmpry1wl9ua/dzSl8hVTiE/draco-1.5.3+dfsg/testdata/BoxMeta/glTF/Texture1_MeshFeatures.png and /tmp/tmpry1wl9ua/XO2yWrPokU/draco-1.5.5+dfsg/testdata/BoxMeta/glTF/Texture1_MeshFeatures.png differ diff -Nru draco-1.5.3+dfsg/testdata/BoxMetaDraco/glTF/BoxMetaDraco.gltf draco-1.5.5+dfsg/testdata/BoxMetaDraco/glTF/BoxMetaDraco.gltf --- draco-1.5.3+dfsg/testdata/BoxMetaDraco/glTF/BoxMetaDraco.gltf 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/testdata/BoxMetaDraco/glTF/BoxMetaDraco.gltf 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,226 @@ +{ + "asset": { + "version": "2.0", + "generator": "draco_decoder" + }, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "scene": 0, + "nodes": [ + { + "children": [ + 1 + ], + "translation": [ + 0, + 0, + 2 + ] + }, + { + "mesh": 0 + } + ], + "meshes": [ + { + "name": "Mesh", + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 1, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "_FEATURE_ID_0": 5, + "_FEATURE_ID_1": 6, + "_FEATURE_ID_2": 7 + }, + "indices": 0, + "mode": 4, + "material": 0, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 0, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TEXCOORD_0": 5, + "TEXCOORD_1": 6, + "_FEATURE_ID_0": 2, + "_FEATURE_ID_1": 3, + "_FEATURE_ID_2": 4 + } + }, + "EXT_mesh_features": { + "featureIds": [ + { + "label": "faces", + "featureCount": 12, + "attribute": 0, + "propertyTable": 0, + "nullFeatureId": 100 + }, + { + "label": "vertices", + "featureCount": 8, + "attribute": 1, + "propertyTable": 1, + "nullFeatureId": 101 + }, + { + "featureCount": 36, + "attribute": 2 + }, + { + "featureCount": 6, + "texture": { + "index": 0, + "texCoord": 0, + "channels": [ + 0 + ] + } + }, + { + "label": "water", + "featureCount": 2, + "texture": { + "index": 1, + "texCoord": 1, + "channels": [ + 1, + 2, + 3 + ] + } + } + ] + } + } + } + ] + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.80000001192092896, + 0, + 0, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE", + "name": "Red" + } + ], + "textures": [ + { + "source": 0, + "sampler": 0 + }, + { + "source": 1, + "sampler": 0 + } + ], + "samplers": [ + { + "wrapS": 33071, + "wrapT": 33071 + } + ], + "images": [ + { + "uri": "Texture0_MeshFeatures.png" + }, + { + "uri": "Texture1_MeshFeatures.png" + } + ], + "accessors": [ + { + "componentType": 5121, + "count": 36, + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 36, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 36, + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 36, + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 36, + "type": "VEC2" + }, + { + "componentType": 5121, + "count": 36, + "type": "SCALAR" + }, + { + "componentType": 5123, + "count": 36, + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 36, + "type": "SCALAR" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 620 + } + ], + "buffers": [ + { + "byteLength": 620, + "uri": "buffer0.bin" + } + ], + "extensionsRequired": [ + "KHR_draco_mesh_compression" + ], + "extensionsUsed": [ + "EXT_mesh_features", + "KHR_draco_mesh_compression" + ] +} Binary files /tmp/tmpry1wl9ua/dzSl8hVTiE/draco-1.5.3+dfsg/testdata/BoxMetaDraco/glTF/buffer0.bin and /tmp/tmpry1wl9ua/XO2yWrPokU/draco-1.5.5+dfsg/testdata/BoxMetaDraco/glTF/buffer0.bin differ Binary files /tmp/tmpry1wl9ua/dzSl8hVTiE/draco-1.5.3+dfsg/testdata/BoxMetaDraco/glTF/Texture0_MeshFeatures.png and /tmp/tmpry1wl9ua/XO2yWrPokU/draco-1.5.5+dfsg/testdata/BoxMetaDraco/glTF/Texture0_MeshFeatures.png differ Binary files /tmp/tmpry1wl9ua/dzSl8hVTiE/draco-1.5.3+dfsg/testdata/BoxMetaDraco/glTF/Texture1_MeshFeatures.png and /tmp/tmpry1wl9ua/XO2yWrPokU/draco-1.5.5+dfsg/testdata/BoxMetaDraco/glTF/Texture1_MeshFeatures.png differ Binary files /tmp/tmpry1wl9ua/dzSl8hVTiE/draco-1.5.3+dfsg/testdata/SphereTwoMaterials/buffer0.bin and /tmp/tmpry1wl9ua/XO2yWrPokU/draco-1.5.5+dfsg/testdata/SphereTwoMaterials/buffer0.bin differ diff -Nru draco-1.5.3+dfsg/testdata/SphereTwoMaterials/sphere_two_materials_mesh_and_point_cloud.gltf draco-1.5.5+dfsg/testdata/SphereTwoMaterials/sphere_two_materials_mesh_and_point_cloud.gltf --- draco-1.5.3+dfsg/testdata/SphereTwoMaterials/sphere_two_materials_mesh_and_point_cloud.gltf 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/testdata/SphereTwoMaterials/sphere_two_materials_mesh_and_point_cloud.gltf 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,352 @@ +{ + "asset": { + "version": "2.0", + "generator": "draco_decoder" + }, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "scene": 0, + "nodes": [ + { + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 1, + "TANGENT": 4, + "TEXCOORD_0": 3 + }, + "indices": 0, + "mode": 4, + "material": 0 + }, + { + "attributes": { + "NORMAL": 7, + "POSITION": 6, + "TANGENT": 9, + "TEXCOORD_0": 8 + }, + "mode": 0, + "material": 1 + } + ] + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "metallicFactor": 1, + "roughnessFactor": 1 + }, + "normalTexture": { + "index": 1 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaCutoff": 0.5, + "alphaMode": "OPAQUE" + }, + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 2 + }, + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + }, + "normalTexture": { + "index": 3 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaCutoff": 0.5, + "alphaMode": "BLEND" + } + ], + "textures": [ + { + "source": 0 + }, + { + "source": 1 + }, + { + "source": 0 + }, + { + "source": 1 + } + ], + "images": [ + { + "uri": "Texture0_BaseColor.png" + }, + { + "uri": "Texture0_Normal.png" + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5121, + "count": 672, + "normalized": false, + "max": [ + 230 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 0.949482, + 0.991337 + ], + "min": [ + 0.00866322, + 0.0169369 + ], + "type": "VEC2" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 0.997891, + 0.961565, + 0.962921, + 1 + ], + "min": [ + -0.926271, + -0.9153, + -0.994848, + 1 + ], + "type": "VEC4" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5121, + "count": 672, + "normalized": false, + "max": [ + 230 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 4, + 1, + 1 + ], + "min": [ + 2, + -1, + -1 + ], + "type": "VEC3" + }, + { + "bufferView": 7, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "bufferView": 8, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 0.988142, + 0.991337 + ], + "min": [ + 0.00866322, + 0.0576978 + ], + "type": "VEC2" + }, + { + "bufferView": 9, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 0.997891, + 0.961565, + 0.962921, + 1 + ], + "min": [ + -0.926271, + -0.9153, + -0.994848, + 1 + ], + "type": "VEC4" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 672 + }, + { + "buffer": 0, + "byteOffset": 672, + "byteLength": 2772 + }, + { + "buffer": 0, + "byteOffset": 3444, + "byteLength": 2772 + }, + { + "buffer": 0, + "byteOffset": 6216, + "byteLength": 1848 + }, + { + "buffer": 0, + "byteOffset": 8064, + "byteLength": 3696 + }, + { + "buffer": 0, + "byteOffset": 11760, + "byteLength": 672 + }, + { + "buffer": 0, + "byteOffset": 12432, + "byteLength": 2772 + }, + { + "buffer": 0, + "byteOffset": 15204, + "byteLength": 2772 + }, + { + "buffer": 0, + "byteOffset": 17976, + "byteLength": 1848 + }, + { + "buffer": 0, + "byteOffset": 19824, + "byteLength": 3696 + } + ], + "buffers": [ + { + "byteLength": 23520, + "uri": "buffer0.bin" + } + ] +} diff -Nru draco-1.5.3+dfsg/testdata/SphereTwoMaterials/sphere_two_materials_point_cloud.gltf draco-1.5.5+dfsg/testdata/SphereTwoMaterials/sphere_two_materials_point_cloud.gltf --- draco-1.5.3+dfsg/testdata/SphereTwoMaterials/sphere_two_materials_point_cloud.gltf 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/testdata/SphereTwoMaterials/sphere_two_materials_point_cloud.gltf 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,351 @@ +{ + "asset": { + "version": "2.0", + "generator": "draco_decoder" + }, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "scene": 0, + "nodes": [ + { + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 2, + "POSITION": 1, + "TANGENT": 4, + "TEXCOORD_0": 3 + }, + "mode": 0, + "material": 0 + }, + { + "attributes": { + "NORMAL": 7, + "POSITION": 6, + "TANGENT": 9, + "TEXCOORD_0": 8 + }, + "mode": 0, + "material": 1 + } + ] + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "metallicFactor": 1, + "roughnessFactor": 1 + }, + "normalTexture": { + "index": 1 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaCutoff": 0.5, + "alphaMode": "OPAQUE" + }, + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 2 + }, + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + }, + "normalTexture": { + "index": 3 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaCutoff": 0.5, + "alphaMode": "BLEND" + } + ], + "textures": [ + { + "source": 0 + }, + { + "source": 1 + }, + { + "source": 0 + }, + { + "source": 1 + } + ], + "images": [ + { + "uri": "Texture0_BaseColor.png" + }, + { + "uri": "Texture0_Normal.png" + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5121, + "count": 672, + "normalized": false, + "max": [ + 230 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "bufferView": 3, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 0.949482, + 0.991337 + ], + "min": [ + 0.00866322, + 0.0169369 + ], + "type": "VEC2" + }, + { + "bufferView": 4, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 0.997891, + 0.961565, + 0.962921, + 1 + ], + "min": [ + -0.926271, + -0.9153, + -0.994848, + 1 + ], + "type": "VEC4" + }, + { + "bufferView": 5, + "byteOffset": 0, + "componentType": 5121, + "count": 672, + "normalized": false, + "max": [ + 230 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 6, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 4, + 1, + 1 + ], + "min": [ + 2, + -1, + -1 + ], + "type": "VEC3" + }, + { + "bufferView": 7, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "bufferView": 8, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 0.988142, + 0.991337 + ], + "min": [ + 0.00866322, + 0.0576978 + ], + "type": "VEC2" + }, + { + "bufferView": 9, + "byteOffset": 0, + "componentType": 5126, + "count": 231, + "normalized": false, + "max": [ + 0.997891, + 0.961565, + 0.962921, + 1 + ], + "min": [ + -0.926271, + -0.9153, + -0.994848, + 1 + ], + "type": "VEC4" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 672 + }, + { + "buffer": 0, + "byteOffset": 672, + "byteLength": 2772 + }, + { + "buffer": 0, + "byteOffset": 3444, + "byteLength": 2772 + }, + { + "buffer": 0, + "byteOffset": 6216, + "byteLength": 1848 + }, + { + "buffer": 0, + "byteOffset": 8064, + "byteLength": 3696 + }, + { + "buffer": 0, + "byteOffset": 11760, + "byteLength": 672 + }, + { + "buffer": 0, + "byteOffset": 12432, + "byteLength": 2772 + }, + { + "buffer": 0, + "byteOffset": 15204, + "byteLength": 2772 + }, + { + "buffer": 0, + "byteOffset": 17976, + "byteLength": 1848 + }, + { + "buffer": 0, + "byteOffset": 19824, + "byteLength": 3696 + } + ], + "buffers": [ + { + "byteLength": 23520, + "uri": "buffer0.bin" + } + ] +} Binary files /tmp/tmpry1wl9ua/dzSl8hVTiE/draco-1.5.3+dfsg/testdata/SphereTwoMaterials/Texture0_BaseColor.png and /tmp/tmpry1wl9ua/XO2yWrPokU/draco-1.5.5+dfsg/testdata/SphereTwoMaterials/Texture0_BaseColor.png differ Binary files /tmp/tmpry1wl9ua/dzSl8hVTiE/draco-1.5.3+dfsg/testdata/SphereTwoMaterials/Texture0_Normal.png and /tmp/tmpry1wl9ua/XO2yWrPokU/draco-1.5.5+dfsg/testdata/SphereTwoMaterials/Texture0_Normal.png differ diff -Nru draco-1.5.3+dfsg/testdata/UnusedTexCoords/TexCoord2.gltf draco-1.5.5+dfsg/testdata/UnusedTexCoords/TexCoord2.gltf --- draco-1.5.3+dfsg/testdata/UnusedTexCoords/TexCoord2.gltf 1970-01-01 00:00:00.000000000 +0000 +++ draco-1.5.5+dfsg/testdata/UnusedTexCoords/TexCoord2.gltf 2022-10-29 00:55:03.000000000 +0000 @@ -0,0 +1,262 @@ +{ + "accessors" : [ + { + "bufferView" : 0, + "componentType" : 5121, + "count" : 36, + "max" : [ + 23 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 1, + "componentType" : 5126, + "count" : 24, + "max" : [ + 1.0000004768371582, + 1.0, + 1.0000005960464478 + ], + "min" : [ + -1.0000003576278687, + -1.0, + -1.0000003576278687 + ], + "type" : "VEC3" + }, + { + "bufferView" : 2, + "componentType" : 5126, + "count" : 24, + "max" : [ + 1.0, + 1.0, + 1.0 + ], + "min" : [ + -1.0, + -1.0, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 3, + "componentType" : 5126, + "count" : 24, + "max" : [ + 1.0, + 6.661325971652322e-16, + 1.0, + 1.0 + ], + "min" : [ + -1.0, + -2.9802322387695312e-08, + -1.0, + 1.0 + ], + "type" : "VEC4" + }, + { + "bufferView" : 4, + "componentType" : 5126, + "count" : 24, + "max" : [ + 0.7499566674232483, + 0.9999134124518605 + ], + "min" : [ + 8.658754813950509e-05, + 8.660554885864258e-05 + ], + "type" : "VEC2" + }, + { + "bufferView" : 5, + "componentType" : 5126, + "count" : 24, + "max" : [ + 0.25, + 0.25 + ], + "min" : [ + 0.0, + 0.0 + ], + "type" : "VEC2" + } + ], + "asset" : { + "copyright" : "hiloteam", + "generator" : "Khronos Blender glTF 2.0 exporter", + "version" : "2.0" + }, + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 36, + "byteOffset" : 0, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 288, + "byteOffset" : 36, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 288, + "byteOffset" : 324, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 384, + "byteOffset" : 612, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 192, + "byteOffset" : 996, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 192, + "byteOffset" : 1188, + "target" : 34962 + } + ], + "buffers" : [ + { + "byteLength" : 1380, + "uri" : "MultiUVTest.bin" + } + ], + "cameras" : [ + { + "name" : "Camera", + "perspective" : { + "aspectRatio" : 1.7777777777777777, + "yfov" : 0.5033799372418416, + "zfar" : 100.0, + "znear" : 0.10000000149011612 + }, + "type" : "perspective" + } + ], + "images" : [ + { + "uri" : "uv0.png" + }, + { + "uri" : "uv1.png" + } + ], + "materials" : [ + { + "emissiveFactor" : [ + 1.0, + 1.0, + 1.0 + ], + "emissiveTexture" : { + "index" : 1, + "texCoord" : 1 + }, + "name" : "Material", + "pbrMetallicRoughness" : { + "baseColorTexture" : { + "index" : 0 + } + } + } + ], + "meshes" : [ + { + "name" : "Cube", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 2, + "POSITION" : 1, + "TANGENT" : 3, + "TEXCOORD_0" : 4, + "TEXCOORD_1" : 5, + "TEXCOORD_2" : 6, + "TEXCOORD_3" : 7, + "TEXCOORD_4" : 8, + "TEXCOORD_5" : 9, + "TEXCOORD_6" : 10, + "TEXCOORD_7" : 11 + }, + "indices" : 0, + "material" : 0 + } + ] + } + ], + "nodes" : [ + { + "camera" : 0, + "name" : "Correction_Camera", + "rotation" : [ + -0.7071067690849304, + -0.0, + 0.0, + 0.7071067690849304 + ] + }, + { + "children" : [ + 0 + ], + "name" : "Camera", + "rotation" : [ + 0.483536034822464, + 0.33687159419059753, + -0.20870360732078552, + 0.7804827094078064 + ], + "translation" : [ + 7.481131553649902, + 5.34366512298584, + 6.5076398849487305 + ] + }, + { + "mesh" : 0, + "name" : "Cube" + } + ], + "samplers" : [ + {} + ], + "scene" : 0, + "scenes" : [ + { + "name" : "Scene", + "nodes" : [ + 2, + 1 + ] + } + ], + "textures" : [ + { + "sampler" : 0, + "source" : 0 + }, + { + "sampler" : 0, + "source" : 1 + } + ] +}