From 79185b7058646feb727178428f8ba8b636d88b36 Mon Sep 17 00:00:00 2001 From: Frank Galligan Date: Thu, 12 Jan 2017 16:50:49 -0800 Subject: [PATCH] Update snapshot to 0.9.1 --- BUILD | 20 - CMakeLists.txt | 302 +- README.md | 4 +- cmake/draco_test_config.h.cmake | 12 + compression/attributes/attributes_encoder.h | 17 +- .../attributes/kd_tree_attributes_encoder.cc | 3 + ...mesh_attribute_indices_encoding_observer.h | 2 +- .../attributes/mesh_traversal_sequencer.h | 2 +- .../attributes/normal_compression_utils.h | 42 +- compression/attributes/points_sequencer.h | 2 +- ...sh_prediction_scheme_multi_parallelogram.h | 9 +- .../mesh_prediction_scheme_parallelogram.h | 9 +- .../mesh_prediction_scheme_tex_coords.h | 20 +- .../prediction_schemes/prediction_scheme.h | 6 +- ...ction_scheme_normal_octahedron_transform.h | 42 +- ...scheme_normal_octahedron_transform_test.cc | 63 + .../prediction_scheme_transform.h | 6 +- ...equential_attribute_decoders_controller.cc | 6 +- .../sequential_attribute_encoder.cc | 2 +- ...equential_attribute_encoders_controller.cc | 8 +- .../sequential_integer_attribute_decoder.cc | 4 +- .../sequential_integer_attribute_encoder.cc | 4 +- ...uential_integer_attribute_encoding_test.cc | 61 + ...=> sequential_normal_attribute_decoder.cc} | 14 +- ... => sequential_normal_attribute_decoder.h} | 14 +- ...=> sequential_normal_attribute_encoder.cc} | 10 +- ... => sequential_normal_attribute_encoder.h} | 12 +- ...quential_quantization_attribute_decoder.cc | 2 +- ...quential_quantization_attribute_encoder.cc | 2 +- compression/config/encoder_options.cc | 16 +- compression/encode.cc | 14 +- compression/encode.h | 17 + compression/mesh/mesh_decoder.h | 4 +- .../mesh/mesh_edgebreaker_decoder_impl.cc | 31 +- .../mesh/mesh_edgebreaker_encoder_impl.cc | 14 +- .../mesh/mesh_edgebreaker_traversal_decoder.h | 12 +- .../mesh/mesh_edgebreaker_traversal_encoder.h | 8 +- ...edgebreaker_traversal_predictive_encoder.h | 2 +- compression/mesh/mesh_encoder.h | 4 +- compression/mesh/mesh_encoder_test.cc | 93 + .../algorithms/float_points_kd_tree_encoder.h | 2 +- .../integer_points_kd_tree_decoder.cc | 14 +- .../integer_points_kd_tree_decoder.h | 27 +- .../integer_points_kd_tree_encoder.cc | 14 +- .../integer_points_kd_tree_encoder.h | 25 +- compression/point_cloud/point_cloud_decoder.h | 2 +- .../point_cloud/point_cloud_encoder.cc | 11 +- compression/point_cloud/point_cloud_encoder.h | 2 +- .../point_cloud_kd_tree_encoding_test.cc | 119 + .../point_cloud_sequential_encoding_test.cc | 62 + core/adaptive_rans_coding.cc | 4 +- core/adaptive_rans_coding.h | 4 +- core/ans.h | 6 +- core/bit_coder.cc | 3 +- core/bit_coder.h | 10 +- core/bit_coder_test.cc | 113 + core/data_buffer.cc | 2 +- core/decoder_buffer.h | 8 +- core/direct_bit_coding.h | 6 +- core/draco_test_base.h | 11 + core/draco_test_utils.cc | 81 + core/draco_test_utils.h | 39 + core/draco_tests.cc | 6 + core/draco_version.h | 2 +- core/encoder_buffer.cc | 4 +- core/encoder_buffer.h | 4 +- core/folded_bit32_coding.h | 4 +- core/hash_utils.cc | 16 +- core/hash_utils.h | 2 +- core/macros.h | 4 + core/math_utils_test.cc | 4 + core/quantization_utils_test.cc | 51 + core/rans_coding.cc | 5 +- core/rans_coding.h | 4 +- core/rans_coding_test.cc | 7 + core/rans_symbol_decoder.h | 2 +- core/rans_symbol_encoder.h | 10 +- core/symbol_coding_test.cc | 119 + core/symbol_decoding.cc | 2 +- core/symbol_encoding.cc | 17 +- core/vector_d_test.cc | 72 + io/obj_decoder.cc | 4 +- io/obj_decoder_test.cc | 52 + io/parser_utils.cc | 1 + io/ply_decoder_test.cc | 51 + io/ply_property_reader.h | 2 + io/ply_reader.cc | 2 +- io/ply_reader_test.cc | 142 + io/point_cloud_io_test.cc | 64 + mesh/corner_table.cc | 8 +- mesh/corner_table.h | 2 +- mesh/mesh.h | 4 +- mesh/mesh_cleanup.cc | 6 +- mesh/mesh_cleanup_test.cc | 131 + mesh/mesh_test.cc | 47 + mesh/triangle_soup_mesh_builder.cc | 2 +- point_cloud/point_cloud.cc | 5 +- point_cloud/point_cloud.h | 9 +- point_cloud/point_cloud_builder.cc | 71 + point_cloud/point_cloud_builder.h | 80 + point_cloud/point_cloud_builder_test.cc | 171 + testdata/cube_subd.obj | 4626 +++++++++++++++++ testdata/extra_vertex.obj | 42 + testdata/one_face_123.obj | 11 + testdata/one_face_321.obj | 11 + testdata/sphere.obj | 459 ++ testdata/test_extra_whitespace.ply | 18 + testdata/test_more_datatypes.ply | 18 + testdata/test_nm.obj | 414 ++ testdata/test_nm.obj.edgebreaker.out | Bin 0 -> 2546 bytes testdata/test_nm.obj.sequential.out | Bin 0 -> 2546 bytes testdata/test_pos_color.ply | Bin 0 -> 5015 bytes testdata/test_pos_color_ascii.ply | 352 ++ tools/draco_decoder.cc | 2 +- tools/draco_encoder.cc | 10 +- 115 files changed, 8246 insertions(+), 386 deletions(-) delete mode 100644 BUILD create mode 100644 cmake/draco_test_config.h.cmake create mode 100644 compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_test.cc create mode 100644 compression/attributes/sequential_integer_attribute_encoding_test.cc rename compression/attributes/{mesh_normal_attribute_decoder.cc => sequential_normal_attribute_decoder.cc} (81%) rename compression/attributes/{mesh_normal_attribute_decoder.h => sequential_normal_attribute_decoder.h} (82%) rename compression/attributes/{mesh_normal_attribute_encoder.cc => sequential_normal_attribute_encoder.cc} (85%) rename compression/attributes/{mesh_normal_attribute_encoder.h => sequential_normal_attribute_encoder.h} (83%) create mode 100644 compression/mesh/mesh_encoder_test.cc create mode 100644 compression/point_cloud/point_cloud_kd_tree_encoding_test.cc create mode 100644 compression/point_cloud/point_cloud_sequential_encoding_test.cc create mode 100644 core/bit_coder_test.cc create mode 100644 core/draco_test_base.h create mode 100644 core/draco_test_utils.cc create mode 100644 core/draco_test_utils.h create mode 100644 core/draco_tests.cc create mode 100644 core/math_utils_test.cc create mode 100644 core/quantization_utils_test.cc create mode 100644 core/rans_coding_test.cc create mode 100644 core/symbol_coding_test.cc create mode 100644 core/vector_d_test.cc create mode 100644 io/obj_decoder_test.cc create mode 100644 io/ply_decoder_test.cc create mode 100644 io/ply_reader_test.cc create mode 100644 io/point_cloud_io_test.cc create mode 100644 mesh/mesh_cleanup_test.cc create mode 100644 mesh/mesh_test.cc create mode 100644 point_cloud/point_cloud_builder.cc create mode 100644 point_cloud/point_cloud_builder.h create mode 100644 point_cloud/point_cloud_builder_test.cc create mode 100644 testdata/cube_subd.obj create mode 100644 testdata/extra_vertex.obj create mode 100644 testdata/one_face_123.obj create mode 100644 testdata/one_face_321.obj create mode 100644 testdata/sphere.obj create mode 100644 testdata/test_extra_whitespace.ply create mode 100644 testdata/test_more_datatypes.ply create mode 100644 testdata/test_nm.obj create mode 100644 testdata/test_nm.obj.edgebreaker.out create mode 100644 testdata/test_nm.obj.sequential.out create mode 100644 testdata/test_pos_color.ply create mode 100644 testdata/test_pos_color_ascii.ply diff --git a/BUILD b/BUILD deleted file mode 100644 index a758ce4..0000000 --- a/BUILD +++ /dev/null @@ -1,20 +0,0 @@ -# Description: -# Geometry and API compression and decompression utils. - -package( - default_hdrs_check = "strict", - default_visibility = ["//visibility:public"], - features = [ - "-layering_check", - ], -) - -licenses(["notice"]) # Apache 2.0 - -exports_files(["LICENSE"]) - -cc_library( - name = "draco_inc", - includes = ["."], - visibility = ["//visibility:public"], -) diff --git a/CMakeLists.txt b/CMakeLists.txt index 479c731..e690ea9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,8 +12,10 @@ require_cxx_flag_nomsvc("-std=c++11") option(ENABLE_STANDARD_EDGEBREAKER "" ON) option(ENABLE_PREDICTIVE_EDGEBREAKER "" ON) option(ENABLE_EXTRA_WARNINGS "" OFF) +option(ENABLE_TESTS "Enables tests." OFF) option(ENABLE_WERROR "" OFF) option(ENABLE_WEXTRA "" OFF) +option(IGNORE_EMPTY_BUILD_TYPE "" OFF) if (ENABLE_STANDARD_EDGEBREAKER) add_cxx_preproc_definition("DRACO_STANDARD_EDGEBREAKER_SUPPORTED") @@ -62,15 +64,17 @@ if (GIT_FOUND) if (NOT EXISTS "${draco_git_dir}") set(draco_git_dir "${draco_root}/../../../.git") endif () - execute_process(COMMAND ${GIT_EXECUTABLE} - --git-dir=${draco_git_dir} rev-parse HEAD - OUTPUT_VARIABLE draco_git_hash) - execute_process( - COMMAND ${GIT_EXECUTABLE} --git-dir=${draco_git_dir}/.git describe - OUTPUT_VARIABLE draco_git_desc ERROR_QUIET) - # Consume newlines from Git output. - string(STRIP "${draco_git_hash}" draco_git_hash) - string(STRIP "${draco_git_desc}" draco_git_desc) + if (EXISTS "${draco_git_dir}") + execute_process(COMMAND ${GIT_EXECUTABLE} + --git-dir=${draco_git_dir} rev-parse HEAD + OUTPUT_VARIABLE draco_git_hash) + execute_process( + COMMAND ${GIT_EXECUTABLE} --git-dir=${draco_git_dir}/.git describe + OUTPUT_VARIABLE draco_git_desc ERROR_QUIET) + # Consume newlines from Git output. + string(STRIP "${draco_git_hash}" draco_git_hash) + string(STRIP "${draco_git_desc}" draco_git_desc) + endif () endif () if (draco_git_hash STREQUAL "") set(draco_git_desc "unknown") @@ -83,6 +87,49 @@ configure_file("${draco_root}/cmake/draco_version.cc.cmake" configure_file("${draco_root}/cmake/draco_version.h.cmake" "${draco_build_dir}/draco_version.h" COPYONLY) +if (EMSCRIPTEN) + include(FindPythonInterp) + if (NOT PYTHONINTERP_FOUND) + message(FATAL_ERROR + "Python required for Emscripten builds, but cmake cannot find it.") + endif () +else () + # When not building the JS library, and when a build type is not specified, + # default to producing a release mode Draco library to avoid benchmarking + # shenanigans, but only when Draco is not being pulled in via another cmake + # file. + if (CMAKE_BUILD_TYPE STREQUAL "" AND NOT IGNORE_EMPTY_BUILD_TYPE) + if (CMAKE_CURRENT_LIST_FILE STREQUAL CMAKE_PARENT_LIST_FILE) + message(INFO "|Draco: ignoring empty build type, forcing release mode.") + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Draco overridden build type" + FORCE) + endif () + endif () + + if (ENABLE_TESTS) + # Googletest defaults. + set(GTEST_SOURCE_DIR + "${draco_root}/../googletest" CACHE STRING + "Path to googletest source directory") + set(GTEST_BUILD_DIR + "${draco_build_dir}/googletest" CACHE STRING + "Path to directory where googletest will be configured and built.") + + # Confirm Googletest is where expected. + if (NOT EXISTS "${GTEST_SOURCE_DIR}/CMakeLists.txt") + set(ENABLE_TESTS OFF) + message("Tests disabled: Google test CMakeLists.txt does not exist.") + else () + set(DRACO_TEST_DATA_DIR "${draco_root}/testdata") + configure_file("${draco_root}/cmake/draco_test_config.h.cmake" + "${draco_build_dir}/testing/draco_test_config.h") + add_subdirectory("${GTEST_SOURCE_DIR}" "${GTEST_BUILD_DIR}") + endif () + + include_directories("${GTEST_SOURCE_DIR}") + endif () +endif () + # Draco source file listing variables. set(draco_compression_attributes_decoder_sources "${draco_root}/compression/attributes/attributes_decoder.cc" @@ -91,8 +138,6 @@ set(draco_compression_attributes_decoder_sources "${draco_root}/compression/attributes/kd_tree_attributes_decoder.h" "${draco_root}/compression/attributes/kd_tree_attributes_shared.h" "${draco_root}/compression/attributes/mesh_attribute_indices_encoding_data.h" - "${draco_root}/compression/attributes/mesh_normal_attribute_decoder.cc" - "${draco_root}/compression/attributes/mesh_normal_attribute_decoder.h" "${draco_root}/compression/attributes/mesh_traversal_sequencer.h" "${draco_root}/compression/attributes/normal_compression_utils.h" "${draco_root}/compression/attributes/sequential_attribute_decoder.cc" @@ -101,6 +146,8 @@ set(draco_compression_attributes_decoder_sources "${draco_root}/compression/attributes/sequential_attribute_decoders_controller.h" "${draco_root}/compression/attributes/sequential_integer_attribute_decoder.cc" "${draco_root}/compression/attributes/sequential_integer_attribute_decoder.h" + "${draco_root}/compression/attributes/sequential_normal_attribute_decoder.cc" + "${draco_root}/compression/attributes/sequential_normal_attribute_decoder.h" "${draco_root}/compression/attributes/sequential_quantization_attribute_decoder.cc" "${draco_root}/compression/attributes/sequential_quantization_attribute_decoder.h") @@ -111,8 +158,6 @@ set(draco_compression_attributes_encoder_sources "${draco_root}/compression/attributes/kd_tree_attributes_encoder.h" "${draco_root}/compression/attributes/linear_sequencer.h" "${draco_root}/compression/attributes/mesh_attribute_indices_encoding_observer.h" - "${draco_root}/compression/attributes/mesh_normal_attribute_encoder.cc" - "${draco_root}/compression/attributes/mesh_normal_attribute_encoder.h" "${draco_root}/compression/attributes/points_sequencer.h" "${draco_root}/compression/attributes/sequential_attribute_encoder.cc" "${draco_root}/compression/attributes/sequential_attribute_encoder.h" @@ -120,6 +165,8 @@ set(draco_compression_attributes_encoder_sources "${draco_root}/compression/attributes/sequential_attribute_encoders_controller.h" "${draco_root}/compression/attributes/sequential_integer_attribute_encoder.cc" "${draco_root}/compression/attributes/sequential_integer_attribute_encoder.h" + "${draco_root}/compression/attributes/sequential_normal_attribute_encoder.cc" + "${draco_root}/compression/attributes/sequential_normal_attribute_encoder.h" "${draco_root}/compression/attributes/sequential_quantization_attribute_encoder.cc" "${draco_root}/compression/attributes/sequential_quantization_attribute_encoder.h") @@ -291,7 +338,9 @@ set(draco_point_cloud_sources "${draco_root}/point_cloud/point_attribute.cc" "${draco_root}/point_cloud/point_attribute.h" "${draco_root}/point_cloud/point_cloud.cc" - "${draco_root}/point_cloud/point_cloud.h") + "${draco_root}/point_cloud/point_cloud.h" + "${draco_root}/point_cloud/point_cloud_builder.cc" + "${draco_root}/point_cloud/point_cloud_builder.h") set(draco_points_common_sources "${draco_root}/compression/point_cloud/algorithms/point_cloud_types.h" @@ -310,77 +359,170 @@ set(draco_points_encoder_sources "${draco_root}/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.cc" "${draco_root}/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.h") +set(draco_js_sources + "${draco_root}/javascript/emscripten/draco_glue_wrapper.cc" + "${draco_root}/javascript/emscripten/webidl_wrapper.cc") + +set(draco_test_sources + "${draco_root}/core/draco_tests.cc" + "${draco_root}/core/draco_test_base.h" + "${draco_root}/core/draco_test_utils.cc" + "${draco_root}/core/draco_test_utils.h" + "${draco_root}/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_test.cc" + "${draco_root}/compression/attributes/sequential_integer_attribute_encoding_test.cc" + "${draco_root}/compression/mesh/mesh_encoder_test.cc" + "${draco_root}/compression/point_cloud/point_cloud_kd_tree_encoding_test.cc" + "${draco_root}/compression/point_cloud/point_cloud_sequential_encoding_test.cc" + "${draco_root}/core/bit_coder_test.cc" + "${draco_root}/core/math_utils_test.cc" + "${draco_root}/core/quantization_utils_test.cc" + "${draco_root}/core/rans_coding_test.cc" + "${draco_root}/core/symbol_coding_test.cc" + "${draco_root}/core/vector_d_test.cc" + "${draco_root}/io/obj_decoder_test.cc" + "${draco_root}/io/ply_decoder_test.cc" + "${draco_root}/io/ply_reader_test.cc" + "${draco_root}/io/point_cloud_io_test.cc" + "${draco_root}/mesh/mesh_cleanup_test.cc" + "${draco_root}/mesh/mesh_test.cc" + "${draco_root}/point_cloud/point_cloud_builder_test.cc") + +set(draco_version_sources + "${draco_build_dir}/draco_version.cc" + "${draco_build_dir}/draco_version.h") + include_directories("${draco_root}") # # Draco targets. # +if (EMSCRIPTEN) + # Draco js decoder. + add_compiler_flag_if_supported("-s ALLOW_MEMORY_GROWTH=1") + add_compiler_flag_if_supported("--memory-init-file 0") + add_compiler_flag_if_supported("-fno-omit-frame-pointer") -# Object collections that mirror the Draco directory structure. -add_library(draco_compression_attributes_decoder OBJECT - ${draco_compression_attributes_decoder_sources}) -add_library(draco_compression_attributes_encoder OBJECT - ${draco_compression_attributes_encoder_sources}) - add_library(draco_compression_attributes_pred_schemes OBJECT - ${draco_compression_attributes_pred_schemes_sources}) -add_library(draco_compression_config OBJECT ${draco_compression_config_sources}) -add_library(draco_compression_decode OBJECT ${draco_compression_decode_sources}) -add_library(draco_compression_encode OBJECT ${draco_compression_encode_sources}) -add_library(draco_compression_mesh_decoder OBJECT - ${draco_compression_mesh_decoder_sources}) -add_library(draco_compression_mesh_encoder OBJECT - ${draco_compression_mesh_encoder_sources}) -add_library(draco_compression_point_cloud_decoder OBJECT - ${draco_compression_point_cloud_decoder_sources}) -add_library(draco_compression_point_cloud_encoder OBJECT - ${draco_compression_point_cloud_encoder_sources}) -add_library(draco_core OBJECT ${draco_core_sources}) -add_library(draco_io OBJECT ${draco_io_sources}) -add_library(draco_mesh OBJECT ${draco_mesh_sources}) -add_library(draco_point_cloud OBJECT ${draco_point_cloud_sources}) -add_library(draco_points_decoder OBJECT - ${draco_points_common_sources} - ${draco_points_decoder_sources}) -add_library(draco_points_encoder OBJECT - ${draco_points_common_sources} - ${draco_points_encoder_sources}) + if (CMAKE_BUILD_TYPE STREQUAL "") + # Force -O3 when no build type is specified. + add_compiler_flag_if_supported("-O3") + endif () -# Library targets that consume the object collections. -add_library(dracodec - "${draco_build_dir}/draco_version.cc" - "${draco_build_dir}/draco_version.h" - $ - $ - $ - $ - $ - $ - $ - $ - $) -add_library(draco - "${draco_build_dir}/draco_version.cc" - "${draco_build_dir}/draco_version.h" - $ - $ - $ - $ - $ - $ - $ - $ - $ - $ - $ - $ - $ - $ - $ - $) + set(draco_js_idl "${draco_root}/javascript/emscripten/draco_web.idl") -# Draco app targets. -add_executable(draco_decoder "${draco_root}/tools/draco_decoder.cc") -target_link_libraries(draco_decoder PUBLIC dracodec) -add_executable(draco_encoder - "${draco_root}/tools/draco_encoder.cc") -target_link_libraries(draco_encoder PUBLIC draco) + # Generate ${draco_build_dir}/glue.cpp at cmake generation time so it can be + # added to targets without cmake reporting errors. + execute_process(COMMAND ${PYTHON_EXECUTABLE} + $ENV{EMSCRIPTEN}/tools/webidl_binder.py ${draco_js_idl} + ${draco_build_dir}/glue + OUTPUT_FILE ${draco_build_dir}/glue.cpp) + + # Add a custom rule depending on the IDL to regenerate + # ${draco_build_dir}/glue.cpp as needed. + add_custom_command(OUTPUT ${draco_build_dir}/glue.cpp + COMMAND ${PYTHON_EXECUTABLE} + $ENV{EMSCRIPTEN}/tools/webidl_binder.py ${draco_js_idl} + ${draco_build_dir}/glue + DEPENDS ${draco_js_idl} + COMMENT "Generating ${draco_build_dir}/glue.cpp." + WORKING_DIRECTORY ${draco_build_dir} + VERBATIM) + + # Add path to glue.cpp to draco include paths. + include_directories("${draco_build_dir}") + + add_executable(draco_decoder + ${draco_compression_attributes_decoder_sources} + ${draco_compression_decode_sources} + ${draco_compression_mesh_decoder_sources} + ${draco_compression_point_cloud_decoder_sources} + ${draco_core_sources} + ${draco_io_sources} + ${draco_mesh_sources} + ${draco_point_cloud_sources} + ${draco_points_decoder_sources} + ${draco_js_sources} + ${draco_version_sources}) + + # Make $draco_js_sources source files depend on glue.cpp. + set_property(SOURCE ${draco_js_sources} APPEND PROPERTY OBJECT_DEPENDS + ${draco_build_dir}/glue.cpp) + em_link_post_js(draco_decoder "${draco_build_dir}/glue.js") +else () + # Standard Draco libs, encoder and decoder. + # Object collections that mirror the Draco directory structure. + add_library(draco_compression_attributes_decoder OBJECT + ${draco_compression_attributes_decoder_sources}) + add_library(draco_compression_attributes_encoder OBJECT + ${draco_compression_attributes_encoder_sources}) + add_library(draco_compression_attributes_pred_schemes OBJECT + ${draco_compression_attributes_pred_schemes_sources}) + add_library(draco_compression_config OBJECT + ${draco_compression_config_sources}) + add_library(draco_compression_decode OBJECT + ${draco_compression_decode_sources}) + add_library(draco_compression_encode OBJECT + ${draco_compression_encode_sources}) + add_library(draco_compression_mesh_decoder OBJECT + ${draco_compression_mesh_decoder_sources}) + add_library(draco_compression_mesh_encoder OBJECT + ${draco_compression_mesh_encoder_sources}) + add_library(draco_compression_point_cloud_decoder OBJECT + ${draco_compression_point_cloud_decoder_sources}) + add_library(draco_compression_point_cloud_encoder OBJECT + ${draco_compression_point_cloud_encoder_sources}) + add_library(draco_core OBJECT ${draco_core_sources}) + add_library(draco_io OBJECT ${draco_io_sources}) + add_library(draco_mesh OBJECT ${draco_mesh_sources}) + add_library(draco_point_cloud OBJECT ${draco_point_cloud_sources}) + add_library(draco_points_decoder OBJECT + ${draco_points_common_sources} + ${draco_points_decoder_sources}) + add_library(draco_points_encoder OBJECT + ${draco_points_common_sources} + ${draco_points_encoder_sources}) + + # Library targets that consume the object collections. + add_library(dracodec + ${draco_version_sources} + $ + $ + $ + $ + $ + $ + $ + $ + $) + add_library(draco + ${draco_version_sources} + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $) + + # Draco app targets. + add_executable(draco_decoder "${draco_root}/tools/draco_decoder.cc") + target_link_libraries(draco_decoder PUBLIC dracodec) + add_executable(draco_encoder + "${draco_root}/tools/draco_encoder.cc") + target_link_libraries(draco_encoder PUBLIC draco) + + if (ENABLE_TESTS) + add_executable(draco_tests ${draco_test_sources}) + include_directories("${draco_build_dir}" + "${GTEST_SOURCE_DIR}/googletest/include") + target_link_libraries(draco_tests draco gtest) + endif () +endif () diff --git a/README.md b/README.md index 2d53c2d..47e891d 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,8 @@ Unlike Visual Studio and Xcode projects, the build configuration for make builds is controlled when you run `cmake`. The following examples demonstrate various build configurations. -Omitting the build type produces makefiles that use build flags containing -neither optimization nor debug flags: +Omitting the build type produces makefiles that use release build flags +by default: ~~~~~ bash cmake . diff --git a/cmake/draco_test_config.h.cmake b/cmake/draco_test_config.h.cmake new file mode 100644 index 0000000..97b1a62 --- /dev/null +++ b/cmake/draco_test_config.h.cmake @@ -0,0 +1,12 @@ +#ifndef DRACO_TESTING_DRACO_TEST_CONFIG_H_ +#define DRACO_TESTING_DRACO_TEST_CONFIG_H_ + +// If this file is named draco_test_config.h.cmake: +// This file is used as input at cmake generation time. + +// If this file is named draco_test_config.h: +// GENERATED FILE, DO NOT EDIT. SEE ABOVE. + +#define DRACO_TEST_DATA_DIR "${DRACO_TEST_DATA_DIR}" + +#endif // DRACO_TESTING_DRACO_TEST_CONFIG_H_ diff --git a/compression/attributes/attributes_encoder.h b/compression/attributes/attributes_encoder.h index 3c40d44..31ec3e6 100644 --- a/compression/attributes/attributes_encoder.h +++ b/compression/attributes/attributes_encoder.h @@ -54,17 +54,19 @@ class AttributesEncoder { // Returns the number of attributes that need to be encoded before the // specified attribute is encoded. // Note that the attribute is specified by its point attribute id. - virtual int NumParentAttributes(int32_t point_attribute_id) const { + virtual int NumParentAttributes(int32_t /* point_attribute_id */) const { return 0; } - virtual int GetParentAttributeId(int32_t point_attribute_id, - int32_t parent_i) const { + virtual int GetParentAttributeId(int32_t /* point_attribute_id */, + int32_t /* parent_i */) const { return -1; } // Marks a given attribute as a parent of another attribute. - virtual bool MarkParentAttribute(int32_t point_attribute_id) { return false; } + virtual bool MarkParentAttribute(int32_t /* point_attribute_id */) { + return false; + } // Returns an attribute containing the encoded version of the attribute data. // I.e., the data that is going to be used by the decoder after the attribute @@ -74,13 +76,13 @@ class AttributesEncoder { // dependent attributes that require to use the same data that will be // availalbe during decoding. virtual const PointAttribute *GetLossyAttributeData( - int32_t point_attribute_id) { + int32_t /* point_attribute_id */) { return nullptr; } void AddAttributeId(int32_t id) { point_attribute_ids_.push_back(id); - if (id >= point_attribute_to_local_id_map_.size()) + if (id >= static_cast(point_attribute_to_local_id_map_.size())) point_attribute_to_local_id_map_.resize(id + 1, -1); point_attribute_to_local_id_map_[id] = point_attribute_ids_.size() - 1; } @@ -100,7 +102,8 @@ class AttributesEncoder { protected: int32_t GetLocalIdForPointAttribute(int32_t point_attribute_id) const { - if (point_attribute_id >= point_attribute_to_local_id_map_.size()) + const int id_map_size = point_attribute_to_local_id_map_.size(); + if (point_attribute_id >= id_map_size) return -1; return point_attribute_to_local_id_map_[point_attribute_id]; } diff --git a/compression/attributes/kd_tree_attributes_encoder.cc b/compression/attributes/kd_tree_attributes_encoder.cc index 5a5d4f5..c2a1981 100644 --- a/compression/attributes/kd_tree_attributes_encoder.cc +++ b/compression/attributes/kd_tree_attributes_encoder.cc @@ -56,6 +56,9 @@ class PointAttributeVectorIterator { ret.point_id_ += dif; return ret; } + bool operator<(const Self &other) const { + return point_id_ < other.point_id_; + } private: const PointAttribute *attribute_; diff --git a/compression/attributes/mesh_attribute_indices_encoding_observer.h b/compression/attributes/mesh_attribute_indices_encoding_observer.h index 7c16809..a8140d9 100644 --- a/compression/attributes/mesh_attribute_indices_encoding_observer.h +++ b/compression/attributes/mesh_attribute_indices_encoding_observer.h @@ -44,7 +44,7 @@ class MeshAttributeIndicesEncodingObserver { // Interface for TraversalObserverT - void OnNewFaceVisited(FaceIndex face) {} + void OnNewFaceVisited(FaceIndex /* face */) {} void OnNewVertexVisited(VertexIndex vertex, CornerIndex corner) { const PointIndex point_id = diff --git a/compression/attributes/mesh_traversal_sequencer.h b/compression/attributes/mesh_traversal_sequencer.h index 07b7281..c002d91 100644 --- a/compression/attributes/mesh_traversal_sequencer.h +++ b/compression/attributes/mesh_traversal_sequencer.h @@ -66,7 +66,7 @@ class MeshTraversalSequencer : public PointsSequencer { protected: bool GenerateSequenceInternal() override { if (corner_order_) { - for (int i = 0; i < corner_order_->size(); ++i) { + for (uint32_t i = 0; i < corner_order_->size(); ++i) { ProcessCorner(corner_order_->at(i)); } } else { diff --git a/compression/attributes/normal_compression_utils.h b/compression/attributes/normal_compression_utils.h index efb46cf..4774cc8 100644 --- a/compression/attributes/normal_compression_utils.h +++ b/compression/attributes/normal_compression_utils.h @@ -74,7 +74,7 @@ void UnitVectorToQuantizedOctahedralCoords(const T *vector, int32_t s = static_cast(floor(ss * max_value + 0.5)); int32_t t = static_cast(floor(tt * max_value + 0.5)); - const int32_t center_value = max_value / 2; + const int32_t center_value = static_cast(max_value / 2); // Convert all edge points in the top left and bottom right quadrants to // their corresponding position in the bottom left and top right quadrants. @@ -82,8 +82,8 @@ void UnitVectorToQuantizedOctahedralCoords(const T *vector, // for the inversion to occur correctly. if ((s == 0 && t == 0) || (s == 0 && t == max_value) || (s == max_value && t == 0)) { - s = max_value; - t = max_value; + s = static_cast(max_value); + t = static_cast(max_value); } else if (s == 0 && t > center_value) { t = center_value - (t - center_value); } else if (s == max_value && t < center_value) { @@ -157,6 +157,42 @@ void QuantizedOctaherdalCoordsToUnitVector(int32_t in_s, int32_t in_t, in_t / max_quantized_value, out_vector); } +template +bool IsInDiamond(const T &max_value_, const T &s, const T &t) { + return std::abs(static_cast(s)) + std::abs(static_cast(t)) <= + static_cast(max_value_); +} + +template +void InvertRepresentation(const T &max_value_, T *s, T *t) { + T sign_s = 0; + T sign_t = 0; + if (*s >= 0 && *t >= 0) { + sign_s = 1; + sign_t = 1; + } else if (*s <= 0 && *t <= 0) { + sign_s = -1; + sign_t = -1; + } else { + sign_s = (*s > 0) ? 1 : -1; + sign_t = (*t > 0) ? 1 : -1; + } + + const T corner_point_s = sign_s * max_value_; + const T corner_point_t = sign_t * max_value_; + *s = 2 * *s - corner_point_s; + *t = 2 * *t - corner_point_t; + if (sign_s * sign_t >= 0) { + T temp = *s; + *s = -*t; + *t = -temp; + } else { + std::swap(*s, *t); + } + *s = (*s + corner_point_s) / 2; + *t = (*t + corner_point_t) / 2; +} + } // namespace draco #endif // DRACO_COMPRESSION_ATTRIBUTES_NORMAL_COMPRESSION_UTILS_H_ diff --git a/compression/attributes/points_sequencer.h b/compression/attributes/points_sequencer.h index 70878d3..14c6a35 100644 --- a/compression/attributes/points_sequencer.h +++ b/compression/attributes/points_sequencer.h @@ -43,7 +43,7 @@ class PointsSequencer { // sufficient information to compute the inverse map, because not all point // ids are necessarily contained within the map. // Must be implemented for sequencers that are used by attribute decoders. - virtual bool UpdatePointToAttributeIndexMapping(PointAttribute *attribute) { + virtual bool UpdatePointToAttributeIndexMapping(PointAttribute * /* attr */) { return false; } diff --git a/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram.h b/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram.h index 95bb38d..f25de07 100644 --- a/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram.h +++ b/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram.h @@ -63,7 +63,7 @@ class MeshPredictionSchemeMultiParallelogram template bool MeshPredictionSchemeMultiParallelogram:: Encode(const DataTypeT *in_data, CorrType *out_corr, int size, - int num_components, const PointIndex *entry_to_point_id_map) { + int num_components, const PointIndex * /* entry_to_point_id_map */) { this->transform().InitializeEncoding(in_data, size, num_components); const CornerTable *const table = this->mesh_data().corner_table(); const std::vector *const vertex_to_data_map = @@ -140,8 +140,8 @@ bool MeshPredictionSchemeMultiParallelogram:: template bool MeshPredictionSchemeMultiParallelogram:: - Decode(const CorrType *in_corr, DataTypeT *out_data, int size, - int num_components, const PointIndex *entry_to_point_id_map) { + Decode(const CorrType *in_corr, DataTypeT *out_data, int /* size */, + int num_components, const PointIndex * /* entry_to_point_id_map */) { this->transform().InitializeDecoding(num_components); std::unique_ptr pred_vals(new DataTypeT[num_components]()); @@ -152,7 +152,8 @@ bool MeshPredictionSchemeMultiParallelogram:: const std::vector *const vertex_to_data_map = this->mesh_data().vertex_to_data_map(); - for (int p = 1; p < this->mesh_data().data_to_corner_map()->size(); ++p) { + const int corner_map_size = this->mesh_data().data_to_corner_map()->size(); + for (int p = 1; p < corner_map_size; ++p) { const CornerIndex start_corner_id = this->mesh_data().data_to_corner_map()->at(p); diff --git a/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram.h b/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram.h index 2e53fc6..4e5de17 100644 --- a/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram.h +++ b/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram.h @@ -66,7 +66,7 @@ class MeshPredictionSchemeParallelogram template bool MeshPredictionSchemeParallelogram:: Encode(const DataTypeT *in_data, CorrType *out_corr, int size, - int num_components, const PointIndex *entry_to_point_id_map) { + int num_components, const PointIndex * /* entry_to_point_id_map */) { this->transform().InitializeEncoding(in_data, size, num_components); std::unique_ptr pred_vals(new DataTypeT[num_components]()); @@ -117,8 +117,8 @@ bool MeshPredictionSchemeParallelogram:: template bool MeshPredictionSchemeParallelogram:: - Decode(const CorrType *in_corr, DataTypeT *out_data, int size, - int num_components, const PointIndex *entry_to_point_id_map) { + Decode(const CorrType *in_corr, DataTypeT *out_data, int /* size */, + int num_components, const PointIndex * /* entry_to_point_id_map */) { this->transform().InitializeDecoding(num_components); const CornerTable *const table = this->mesh_data().corner_table(); @@ -130,7 +130,8 @@ bool MeshPredictionSchemeParallelogram:: // Restore the first value. this->transform().ComputeOriginalValue(pred_vals.get(), in_corr, out_data, 0); - for (int p = 1; p < this->mesh_data().data_to_corner_map()->size(); ++p) { + const int corner_map_size = this->mesh_data().data_to_corner_map()->size(); + for (int p = 1; p < corner_map_size; ++p) { const CornerIndex corner_id = this->mesh_data().data_to_corner_map()->at(p); int vert_opp = p, vert_next = p, vert_prev = p; const CornerIndex opp_corner = table->Opposite(corner_id); diff --git a/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords.h b/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords.h index ffd78c4..776e46c 100644 --- a/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords.h +++ b/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords.h @@ -73,6 +73,7 @@ class MeshPredictionSchemeTexCoords GeometryAttribute::Type GetParentAttributeType(int i) const override { DCHECK_EQ(i, 0); + (void)i; return GeometryAttribute::POSITION; } @@ -137,15 +138,16 @@ bool MeshPredictionSchemeTexCoords::Encode( template bool MeshPredictionSchemeTexCoords::Decode( - const CorrType *in_corr, DataTypeT *out_data, int size, int num_components, - const PointIndex *entry_to_point_id_map) { + const CorrType *in_corr, DataTypeT *out_data, int /* size */, + int num_components, const PointIndex *entry_to_point_id_map) { num_components_ = num_components; entry_to_point_id_map_ = entry_to_point_id_map; predicted_value_ = std::unique_ptr(new DataTypeT[num_components]); this->transform().InitializeDecoding(num_components); - for (int p = 0; p < this->mesh_data().data_to_corner_map()->size(); ++p) { + const int corner_map_size = this->mesh_data().data_to_corner_map()->size(); + for (int p = 0; p < corner_map_size; ++p) { const CornerIndex corner_id = this->mesh_data().data_to_corner_map()->at(p); ComputePredictedValue(corner_id, out_data, p); @@ -226,8 +228,8 @@ void MeshPredictionSchemeTexCoords:: const Vector2f 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]; - predicted_value_[1] = p_uv[1]; + predicted_value_[0] = static_cast(p_uv[0]); + predicted_value_[1] = static_cast(p_uv[1]); return; } @@ -317,11 +319,11 @@ void MeshPredictionSchemeTexCoords:: } if (std::is_integral::value) { // Round the predicted value for integer types. - predicted_value_[0] = floor(predicted_uv[0] + 0.5); - predicted_value_[1] = floor(predicted_uv[1] + 0.5); + predicted_value_[0] = static_cast(floor(predicted_uv[0] + 0.5)); + predicted_value_[1] = static_cast(floor(predicted_uv[1] + 0.5)); } else { - predicted_value_[0] = predicted_uv[0]; - predicted_value_[1] = predicted_uv[1]; + predicted_value_[0] = static_cast(predicted_uv[0]); + predicted_value_[1] = static_cast(predicted_uv[1]); } return; } diff --git a/compression/attributes/prediction_schemes/prediction_scheme.h b/compression/attributes/prediction_schemes/prediction_scheme.h index 69a3ed6..d52733b 100644 --- a/compression/attributes/prediction_schemes/prediction_scheme.h +++ b/compression/attributes/prediction_schemes/prediction_scheme.h @@ -60,12 +60,14 @@ class PredictionScheme int GetNumParentAttributes() const override { return 0; } // Returns the type of each of the parent attribute. - GeometryAttribute::Type GetParentAttributeType(int i) const override { + GeometryAttribute::Type GetParentAttributeType(int /* i */) const override { return GeometryAttribute::INVALID; } // Sets the required parent attribute. - bool SetParentAttribute(const PointAttribute *att) override { return false; } + bool SetParentAttribute(const PointAttribute * /* att */) override { + return false; + } bool AreCorrectionsPositive() override { return transform_.AreCorrectionsPositive(); diff --git a/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform.h b/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform.h index 128cb6b..9e0edee 100644 --- a/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform.h +++ b/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform.h @@ -17,6 +17,7 @@ #include +#include "compression/attributes/normal_compression_utils.h" #include "compression/attributes/prediction_schemes/prediction_scheme.h" #include "core/macros.h" #include "core/vector_d.h" @@ -119,44 +120,15 @@ class PredictionSchemeNormalOctahedronTransform out_orig_vals[1] = orig[1]; } - Point2 InvertRepresentation(Point2 p) const { - DataType sign_x = 0; - DataType sign_y = 0; - if (p[0] >= 0 && p[1] >= 0) { - sign_x = 1; - sign_y = 1; - } else if (p[0] <= 0 && p[1] <= 0) { - sign_x = -1; - sign_y = -1; - } else { - sign_x = (p[0] > 0) ? 1 : -1; - sign_y = (p[1] > 0) ? 1 : -1; - } - - const Point2 t = Point2(sign_x * max_value_, sign_y * max_value_); - p = 2 * p - t; - if (sign_x * sign_y >= 0) { - p = Point2(-p[1], -p[0]); - } else { - p = Point2(p[1], p[0]); - } - p = (p + t) / 2; - return p; - } - - bool IsInDiamond(const Point2 &p) const { - return std::abs(p[0]) + std::abs(p[1]) <= max_value_; - } - private: Point2 ComputeCorrection(Point2 orig, Point2 pred) const { const Point2 t(max_value_, max_value_); orig = orig - t; pred = pred - t; - if (!IsInDiamond(pred)) { - orig = InvertRepresentation(orig); - pred = InvertRepresentation(pred); + if (!IsInDiamond( max_value_, pred[0], pred[1])) { + InvertRepresentation(max_value_, &orig[0], &orig[1]); + InvertRepresentation(max_value_, &pred[0], &pred[1]); } Point2 corr = orig - pred; @@ -169,15 +141,15 @@ class PredictionSchemeNormalOctahedronTransform const Point2 t(max_value_, max_value_); pred = pred - t; - const bool pred_is_in_diamond = IsInDiamond(pred); + const bool pred_is_in_diamond = IsInDiamond( max_value_, pred[0], pred[1]); if (!pred_is_in_diamond) { - pred = InvertRepresentation(pred); + InvertRepresentation(max_value_, &pred[0], &pred[1]); } Point2 orig = pred + corr; orig[0] = ModMax(orig[0]); orig[1] = ModMax(orig[1]); if (!pred_is_in_diamond) { - orig = InvertRepresentation(orig); + InvertRepresentation(max_value_, &orig[0], &orig[1]); } orig = orig + t; return orig; diff --git a/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_test.cc b/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_test.cc new file mode 100644 index 0000000..1466a76 --- /dev/null +++ b/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_test.cc @@ -0,0 +1,63 @@ +// Copyright 2016 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 "compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform.h" +#include "core/draco_test_base.h" + +namespace { + +class PredictionSchemeNormalOctahedronTransformTest : public ::testing::Test { + protected: + typedef draco::PredictionSchemeNormalOctahedronTransform Transform; + typedef Transform::Point2 Point2; + + void TestComputeCorrection(const Transform &transform, const int32_t &ox, + const int32_t &oy, const int32_t &px, + const int32_t &py, const int32_t &cx, + const int32_t &cy) { + const int32_t o[2] = {ox + 7, oy + 7}; + const int32_t p[2] = {px + 7, py + 7}; + int32_t corr[2] = {500, 500}; + transform.ComputeCorrection(o, p, corr, 0); + ASSERT_EQ(corr[0], (cx + 15) % 15); + ASSERT_EQ(corr[1], (cy + 15) % 15); + } +}; + +TEST_F(PredictionSchemeNormalOctahedronTransformTest, Init) { + const Transform transform(15); + ASSERT_TRUE(transform.AreCorrectionsPositive()); +} + +TEST_F(PredictionSchemeNormalOctahedronTransformTest, ComputeCorrections) { + const Transform transform(15); + // checks inside diamond + TestComputeCorrection(transform, 0, 0, 0, 0, 0, 0); + TestComputeCorrection(transform, 1, 1, 1, 1, 0, 0); + TestComputeCorrection(transform, 3, 4, 1, 1, 2, 3); + TestComputeCorrection(transform, -1, -1, -1, -1, 0, 0); + TestComputeCorrection(transform, -3, -4, -1, -1, -2, -3); + // checks outside diamond + TestComputeCorrection(transform, 4, 4, 4, 4, 0, 0); + TestComputeCorrection(transform, 5, 6, 4, 4, -2, -1); + TestComputeCorrection(transform, 3, 2, 4, 4, 2, 1); + // checks on outer edges + TestComputeCorrection(transform, 7, 7, 4, 4, -3, -3); + TestComputeCorrection(transform, 6, 7, 4, 4, -3, -2); + TestComputeCorrection(transform, -6, 7, 4, 4, -3, -2); + TestComputeCorrection(transform, 7, 6, 4, 4, -2, -3); + TestComputeCorrection(transform, 7, -6, 4, 4, -2, -3); +} + +} // namespace diff --git a/compression/attributes/prediction_schemes/prediction_scheme_transform.h b/compression/attributes/prediction_schemes/prediction_scheme_transform.h index 4517623..e0a2593 100644 --- a/compression/attributes/prediction_schemes/prediction_scheme_transform.h +++ b/compression/attributes/prediction_schemes/prediction_scheme_transform.h @@ -40,7 +40,7 @@ class PredictionSchemeTransform { // Performs any custom initialization of the trasnform for the encoder. // |size| = total number of values in |orig_data| (i.e., number of entries * // number of components). - void InitializeEncoding(const DataTypeT *orig_data, int size, + void InitializeEncoding(const DataTypeT * /* orig_data */, int /* size */, int num_components) { num_components_ = num_components; } @@ -79,10 +79,10 @@ class PredictionSchemeTransform { } // Encode any transform specific data. - bool EncodeTransformData(EncoderBuffer *buffer) { return true; } + bool EncodeTransformData(EncoderBuffer * /* buffer */) { return true; } // Decodes any transform specific data. Called before Initialize() method. - bool DecodeTransformData(DecoderBuffer *buffer) { return true; } + bool DecodeTransformData(DecoderBuffer * /* buffer */) { return true; } // Should return true if all corrected values are guaranteed to be positive. bool AreCorrectionsPositive() const { return false; } diff --git a/compression/attributes/sequential_attribute_decoders_controller.cc b/compression/attributes/sequential_attribute_decoders_controller.cc index 7c589a1..d1dc5cf 100644 --- a/compression/attributes/sequential_attribute_decoders_controller.cc +++ b/compression/attributes/sequential_attribute_decoders_controller.cc @@ -13,7 +13,7 @@ // limitations under the License. // #include "compression/attributes/sequential_attribute_decoders_controller.h" -#include "compression/attributes/mesh_normal_attribute_decoder.h" +#include "compression/attributes/sequential_normal_attribute_decoder.h" #include "compression/attributes/sequential_quantization_attribute_decoder.h" #include "compression/config/compression_shared.h" @@ -75,8 +75,8 @@ SequentialAttributeDecodersController::CreateSequentialDecoder( return std::unique_ptr( new SequentialQuantizationAttributeDecoder()); case SEQUENTIAL_ATTRIBUTE_ENCODER_NORMALS: - return std::unique_ptr( - new MeshNormalAttributeDecoder()); + return std::unique_ptr( + new SequentialNormalAttributeDecoder()); default: break; } diff --git a/compression/attributes/sequential_attribute_encoder.cc b/compression/attributes/sequential_attribute_encoder.cc index d352813..0fa0a30 100644 --- a/compression/attributes/sequential_attribute_encoder.cc +++ b/compression/attributes/sequential_attribute_encoder.cc @@ -54,7 +54,7 @@ bool SequentialAttributeEncoder::EncodeValues( const std::unique_ptr value_data_ptr(new uint8_t[entry_size]); uint8_t *const value_data = value_data_ptr.get(); // Encode all attribute values in their native raw format. - for (int i = 0; i < point_ids.size(); ++i) { + for (uint32_t i = 0; i < point_ids.size(); ++i) { const AttributeValueIndex entry_id = attribute_->mapped_index(point_ids[i]); attribute_->GetValue(entry_id, value_data); out_buffer->Encode(value_data, entry_size); diff --git a/compression/attributes/sequential_attribute_encoders_controller.cc b/compression/attributes/sequential_attribute_encoders_controller.cc index 2e01137..b4c2ea7 100644 --- a/compression/attributes/sequential_attribute_encoders_controller.cc +++ b/compression/attributes/sequential_attribute_encoders_controller.cc @@ -13,7 +13,7 @@ // limitations under the License. // #include "compression/attributes/sequential_attribute_encoders_controller.h" -#include "compression/attributes/mesh_normal_attribute_encoder.h" +#include "compression/attributes/sequential_normal_attribute_encoder.h" #include "compression/attributes/sequential_quantization_attribute_encoder.h" #include "compression/point_cloud/point_cloud_encoder.h" @@ -47,7 +47,7 @@ bool SequentialAttributeEncodersController::EncodeAttributesEncoderData( if (!AttributesEncoder::EncodeAttributesEncoderData(out_buffer)) return false; // Encode a unique id of every sequential encoder. - for (int i = 0; i < sequential_encoders_.size(); ++i) { + for (uint32_t i = 0; i < sequential_encoders_.size(); ++i) { out_buffer->Encode(sequential_encoders_[i]->GetUniqueId()); } return true; @@ -57,7 +57,7 @@ bool SequentialAttributeEncodersController::EncodeAttributes( EncoderBuffer *buffer) { if (!sequencer_ || !sequencer_->GenerateSequence(&point_ids_)) return false; - for (int i = 0; i < sequential_encoders_.size(); ++i) { + for (uint32_t i = 0; i < sequential_encoders_.size(); ++i) { if (!sequential_encoders_[i]->Encode(point_ids_, buffer)) return false; } @@ -95,7 +95,7 @@ SequentialAttributeEncodersController::CreateSequentialEncoder(int i) { // We currently only support normals with float coordinates // and must be quantized. return std::unique_ptr( - new MeshNormalAttributeEncoder()); + new SequentialNormalAttributeEncoder()); } else { return std::unique_ptr( new SequentialQuantizationAttributeEncoder()); diff --git a/compression/attributes/sequential_integer_attribute_decoder.cc b/compression/attributes/sequential_integer_attribute_decoder.cc index 2358216..60c2ad6 100644 --- a/compression/attributes/sequential_integer_attribute_decoder.cc +++ b/compression/attributes/sequential_integer_attribute_decoder.cc @@ -90,7 +90,7 @@ bool SequentialIntegerAttributeDecoder::DecodeIntegerValues( if (!in_buffer->Decode(values_.data(), sizeof(int32_t) * values_.size())) return false; } else { - for (int i = 0; i < values_.size(); ++i) { + for (uint32_t i = 0; i < values_.size(); ++i) { in_buffer->Decode(&values_[i], num_bytes); } } @@ -151,7 +151,7 @@ void SequentialIntegerAttributeDecoder::StoreTypedValues(uint32_t num_values) { new AttributeTypeT[num_components]); int val_id = 0; int out_byte_pos = 0; - for (int i = 0; i < num_values; ++i) { + for (uint32_t i = 0; i < num_values; ++i) { for (int c = 0; c < num_components; ++c) { const AttributeTypeT value = static_cast(values_[val_id++]); diff --git a/compression/attributes/sequential_integer_attribute_encoder.cc b/compression/attributes/sequential_integer_attribute_encoder.cc index 27f2389..1f03620 100644 --- a/compression/attributes/sequential_integer_attribute_encoder.cc +++ b/compression/attributes/sequential_integer_attribute_encoder.cc @@ -110,7 +110,7 @@ bool SequentialIntegerAttributeEncoder::EncodeValues( // To compute the maximum bit-length, first OR all values. uint32_t masked_value = 0; - for (int i = 0; i < values_.size(); ++i) { + for (uint32_t i = 0; i < values_.size(); ++i) { masked_value |= values_[i]; } // Compute the msb of the ORed value. @@ -126,7 +126,7 @@ bool SequentialIntegerAttributeEncoder::EncodeValues( if (num_bytes == sizeof(decltype(values_)::value_type)) { out_buffer->Encode(values_.data(), sizeof(int32_t) * values_.size()); } else { - for (int i = 0; i < values_.size(); ++i) { + for (uint32_t i = 0; i < values_.size(); ++i) { out_buffer->Encode(&values_[i], num_bytes); } } diff --git a/compression/attributes/sequential_integer_attribute_encoding_test.cc b/compression/attributes/sequential_integer_attribute_encoding_test.cc new file mode 100644 index 0000000..719d5e8 --- /dev/null +++ b/compression/attributes/sequential_integer_attribute_encoding_test.cc @@ -0,0 +1,61 @@ +// Copyright 2016 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 + +#include "compression/attributes/sequential_integer_attribute_decoder.h" +#include "compression/attributes/sequential_integer_attribute_encoder.h" +#include "core/draco_test_base.h" + +namespace draco { + +class SequentialIntegerAttributeEncodingTest : public ::testing::Test { + protected: +}; + +TEST_F(SequentialIntegerAttributeEncodingTest, DoesCompress) { + // This test verifies that IntegerEncoding encodes and decodes the given data. + const std::vector values{1, 8, 7, 5, 5, 5, 9, + 155, -6, -9, 9, 125, 1, 0}; + GeometryAttribute ga; + PointAttribute pa; + pa.Init(GeometryAttribute::GENERIC, nullptr, 1, DT_INT32, false, 4, 0); + pa.Reset(values.size()); + pa.SetIdentityMapping(); + for (uint32_t i = 0; i < values.size(); ++i) { + pa.SetAttributeValue(AttributeValueIndex(i), &values[i]); + } + // List of point ids from 0 to point_ids.size() - 1. + std::vector point_ids(values.size()); + std::iota(point_ids.begin(), point_ids.end(), 0); + + EncoderBuffer out_buf; + SequentialIntegerAttributeEncoder ie; + ASSERT_TRUE(ie.InitializeStandalone(&pa)); + ASSERT_TRUE(ie.Encode(point_ids, &out_buf)); + + DecoderBuffer in_buf; + in_buf.Init(out_buf.data(), out_buf.size()); + SequentialIntegerAttributeDecoder id; + ASSERT_TRUE(id.InitializeStandalone(&pa)); + ASSERT_TRUE(id.Decode(point_ids, &in_buf)); + + for (uint32_t i = 0; i < values.size(); ++i) { + int32_t entry_val; + pa.GetValue(AttributeValueIndex(i), &entry_val); + ASSERT_EQ(entry_val, values[i]); + } +} + +} // namespace draco diff --git a/compression/attributes/mesh_normal_attribute_decoder.cc b/compression/attributes/sequential_normal_attribute_decoder.cc similarity index 81% rename from compression/attributes/mesh_normal_attribute_decoder.cc rename to compression/attributes/sequential_normal_attribute_decoder.cc index dce35fe..b162534 100644 --- a/compression/attributes/mesh_normal_attribute_decoder.cc +++ b/compression/attributes/sequential_normal_attribute_decoder.cc @@ -12,16 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. // -#include "compression/attributes/mesh_normal_attribute_decoder.h" +#include "compression/attributes/sequential_normal_attribute_decoder.h" #include "compression/attributes/normal_compression_utils.h" namespace draco { -MeshNormalAttributeDecoder::MeshNormalAttributeDecoder() +SequentialNormalAttributeDecoder::SequentialNormalAttributeDecoder() : quantization_bits_(-1) {} -bool MeshNormalAttributeDecoder::Initialize(PointCloudDecoder *decoder, - int attribute_id) { +bool SequentialNormalAttributeDecoder::Initialize(PointCloudDecoder *decoder, + int attribute_id) { if (!SequentialIntegerAttributeDecoder::Initialize(decoder, attribute_id)) return false; // Currently, this encoder works only for 3-component normal vectors. @@ -30,7 +30,7 @@ bool MeshNormalAttributeDecoder::Initialize(PointCloudDecoder *decoder, return true; } -bool MeshNormalAttributeDecoder::DecodeIntegerValues( +bool SequentialNormalAttributeDecoder::DecodeIntegerValues( const std::vector &point_ids, DecoderBuffer *in_buffer) { uint8_t quantization_bits; if (!in_buffer->Decode(&quantization_bits)) @@ -40,7 +40,7 @@ bool MeshNormalAttributeDecoder::DecodeIntegerValues( in_buffer); } -bool MeshNormalAttributeDecoder::StoreValues(uint32_t num_points) { +bool SequentialNormalAttributeDecoder::StoreValues(uint32_t num_points) { // Convert all quantized values back to floats. const int32_t max_quantized_value = (1 << quantization_bits_) - 1; const float max_quantized_value_f = static_cast(max_quantized_value); @@ -50,7 +50,7 @@ bool MeshNormalAttributeDecoder::StoreValues(uint32_t num_points) { float att_val[3]; int quant_val_id = 0; int out_byte_pos = 0; - for (int i = 0; i < num_points; ++i) { + for (uint32_t i = 0; i < num_points; ++i) { const int32_t s = values()->at(quant_val_id++); const int32_t t = values()->at(quant_val_id++); QuantizedOctaherdalCoordsToUnitVector(s, t, max_quantized_value_f, att_val); diff --git a/compression/attributes/mesh_normal_attribute_decoder.h b/compression/attributes/sequential_normal_attribute_decoder.h similarity index 82% rename from compression/attributes/mesh_normal_attribute_decoder.h rename to compression/attributes/sequential_normal_attribute_decoder.h index b1033ff..192fe55 100644 --- a/compression/attributes/mesh_normal_attribute_decoder.h +++ b/compression/attributes/sequential_normal_attribute_decoder.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. // -#ifndef DRACO_COMPRESSION_ATTRIBUTES_MESH_NORMAL_ATTRIBUTE_DECODER_H_ -#define DRACO_COMPRESSION_ATTRIBUTES_MESH_NORMAL_ATTRIBUTE_DECODER_H_ +#ifndef DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_NORMAL_ATTRIBUTE_DECODER_H_ +#define DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_NORMAL_ATTRIBUTE_DECODER_H_ #include "compression/attributes/prediction_schemes/prediction_scheme_decoder_factory.h" #include "compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform.h" @@ -21,11 +21,11 @@ namespace draco { -// Decoder for attributes encoded with MeshNormalAttributeEncoder. -// TODO(hemmer): rename to SequentialNormalAttributeDecoder -class MeshNormalAttributeDecoder : public SequentialIntegerAttributeDecoder { +// Decoder for attributes encoded with SequentialNormalAttributeEncoder. +class SequentialNormalAttributeDecoder + : public SequentialIntegerAttributeDecoder { public: - MeshNormalAttributeDecoder(); + SequentialNormalAttributeDecoder(); bool Initialize(PointCloudDecoder *decoder, int attribute_id) override; protected: @@ -61,4 +61,4 @@ class MeshNormalAttributeDecoder : public SequentialIntegerAttributeDecoder { } // namespace draco -#endif // DRACO_COMPRESSION_ATTRIBUTES_MESH_NORMAL_ATTRIBUTE_DECODER_H_ +#endif // DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_NORMAL_ATTRIBUTE_DECODER_H_ diff --git a/compression/attributes/mesh_normal_attribute_encoder.cc b/compression/attributes/sequential_normal_attribute_encoder.cc similarity index 85% rename from compression/attributes/mesh_normal_attribute_encoder.cc rename to compression/attributes/sequential_normal_attribute_encoder.cc index d6f2baf..60762aa 100644 --- a/compression/attributes/mesh_normal_attribute_encoder.cc +++ b/compression/attributes/sequential_normal_attribute_encoder.cc @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. // -#include "compression/attributes/mesh_normal_attribute_encoder.h" +#include "compression/attributes/sequential_normal_attribute_encoder.h" #include "compression/attributes/normal_compression_utils.h" namespace draco { -bool MeshNormalAttributeEncoder::Initialize(PointCloudEncoder *encoder, - int attribute_id) { +bool SequentialNormalAttributeEncoder::Initialize(PointCloudEncoder *encoder, + int attribute_id) { if (!SequentialIntegerAttributeEncoder::Initialize(encoder, attribute_id)) return false; // Currently this encoder works only for 3-component normal vectors. @@ -27,7 +27,7 @@ bool MeshNormalAttributeEncoder::Initialize(PointCloudEncoder *encoder, return true; } -bool MeshNormalAttributeEncoder::PrepareValues( +bool SequentialNormalAttributeEncoder::PrepareValues( const std::vector &point_ids) { // Quantize all encoded values. const int quantization_bits = encoder()->options()->GetAttributeInt( @@ -39,7 +39,7 @@ bool MeshNormalAttributeEncoder::PrepareValues( values()->clear(); float att_val[3]; values()->reserve(point_ids.size() * 2); - for (int i = 0; i < point_ids.size(); ++i) { + for (uint32_t i = 0; i < point_ids.size(); ++i) { const AttributeValueIndex att_id = attribute()->mapped_index(point_ids[i]); attribute()->GetValue(att_id, att_val); // Encode the vector into a s and t octaherdal coordinates. diff --git a/compression/attributes/mesh_normal_attribute_encoder.h b/compression/attributes/sequential_normal_attribute_encoder.h similarity index 83% rename from compression/attributes/mesh_normal_attribute_encoder.h rename to compression/attributes/sequential_normal_attribute_encoder.h index 3ed24d5..62d35cb 100644 --- a/compression/attributes/mesh_normal_attribute_encoder.h +++ b/compression/attributes/sequential_normal_attribute_encoder.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. // -#ifndef DRACO_COMPRESSION_ATTRIBUTES_MESH_NORMAL_ATTRIBUTE_ENCODER_H_ -#define DRACO_COMPRESSION_ATTRIBUTES_MESH_NORMAL_ATTRIBUTE_ENCODER_H_ +#ifndef DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_NORMAL_ATTRIBUTE_ENCODER_H_ +#define DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_NORMAL_ATTRIBUTE_ENCODER_H_ #include "compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h" #include "compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform.h" @@ -27,8 +27,8 @@ namespace draco { // in a better compression rate under the same accuracy settings. Note that this // encoder doesn't preserve the lengths of input vectors, therefore it will not // work correctly when the input values are not normalized. -// TODO(hemmer): rename to SequentialNormalAttributeEncoder -class MeshNormalAttributeEncoder : public SequentialIntegerAttributeEncoder { +class SequentialNormalAttributeEncoder + : public SequentialIntegerAttributeEncoder { public: uint8_t GetUniqueId() const override { return SEQUENTIAL_ATTRIBUTE_ENCODER_NORMALS; @@ -40,7 +40,7 @@ class MeshNormalAttributeEncoder : public SequentialIntegerAttributeEncoder { bool PrepareValues(const std::vector &point_ids) override; std::unique_ptr> - CreateIntPredictionScheme(PredictionSchemeMethod method) override { + CreateIntPredictionScheme(PredictionSchemeMethod /* method */) override { typedef PredictionSchemeNormalOctahedronTransform Transform; const int32_t quantization_bits = encoder()->options()->GetAttributeInt( attribute_id(), "quantization_bits", -1); @@ -53,4 +53,4 @@ class MeshNormalAttributeEncoder : public SequentialIntegerAttributeEncoder { } // namespace draco -#endif // DRACO_COMPRESSION_ATTRIBUTES_MESH_NORMAL_ATTRIBUTE_ENCODER_H_ +#endif // DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_NORMAL_ATTRIBUTE_ENCODER_H_ diff --git a/compression/attributes/sequential_quantization_attribute_decoder.cc b/compression/attributes/sequential_quantization_attribute_decoder.cc index 50b890e..8eda484 100644 --- a/compression/attributes/sequential_quantization_attribute_decoder.cc +++ b/compression/attributes/sequential_quantization_attribute_decoder.cc @@ -71,7 +71,7 @@ bool SequentialQuantizationAttributeDecoder::DequantizeValues( int out_byte_pos = 0; Dequantizer dequantizer; dequantizer.Init(max_value_dif_, max_quantized_value); - for (int i = 0; i < num_values; ++i) { + for (uint32_t i = 0; i < num_values; ++i) { for (int c = 0; c < num_components; ++c) { float value = dequantizer.DequantizeFloat(values()->at(quant_val_id++)); value = value + min_value_[c]; diff --git a/compression/attributes/sequential_quantization_attribute_encoder.cc b/compression/attributes/sequential_quantization_attribute_encoder.cc index 780f01d..d9c5020 100644 --- a/compression/attributes/sequential_quantization_attribute_encoder.cc +++ b/compression/attributes/sequential_quantization_attribute_encoder.cc @@ -125,7 +125,7 @@ bool SequentialQuantizationAttributeEncoder::QuantizeValues( const uint32_t max_quantized_value = (1 << (quantization_bits)) - 1; Quantizer quantizer; quantizer.Init(max_value_dif_, max_quantized_value); - for (int i = 0; i < point_ids.size(); ++i) { + for (uint32_t i = 0; i < point_ids.size(); ++i) { const AttributeValueIndex att_id = attrib->mapped_index(point_ids[i]); attribute()->GetValue(att_id, att_val.get()); for (int c = 0; c < num_components; ++c) { diff --git a/compression/config/encoder_options.cc b/compression/config/encoder_options.cc index f18a3e4..a42cd93 100644 --- a/compression/config/encoder_options.cc +++ b/compression/config/encoder_options.cc @@ -32,14 +32,14 @@ EncoderOptions::EncoderOptions() {} void EncoderOptions::SetGlobalOptions(const Options &o) { global_options_ = o; } void EncoderOptions::SetAttributeOptions(int32_t att_id, const Options &o) { - if (attribute_options_.size() <= att_id) { + if (attribute_options_.size() <= static_cast(att_id)) { attribute_options_.resize(att_id + 1); } attribute_options_[att_id] = o; } Options *EncoderOptions::GetAttributeOptions(int32_t att_id) { - if (attribute_options_.size() <= att_id) { + if (attribute_options_.size() <= static_cast(att_id)) { attribute_options_.resize(att_id + 1); } return &attribute_options_[att_id]; @@ -95,7 +95,7 @@ void EncoderOptions::SetGlobalString(const std::string &name, void EncoderOptions::SetAttributeInt(int32_t att_id, const std::string &name, int val) { - if (att_id >= attribute_options_.size()) { + if (att_id >= static_cast(attribute_options_.size())) { attribute_options_.resize(att_id + 1); } attribute_options_[att_id].SetInt(name, val); @@ -103,7 +103,7 @@ void EncoderOptions::SetAttributeInt(int32_t att_id, const std::string &name, void EncoderOptions::SetAttributeBool(int32_t att_id, const std::string &name, bool val) { - if (att_id >= attribute_options_.size()) { + if (att_id >= static_cast(attribute_options_.size())) { attribute_options_.resize(att_id + 1); } attribute_options_[att_id].SetBool(name, val); @@ -111,7 +111,7 @@ void EncoderOptions::SetAttributeBool(int32_t att_id, const std::string &name, void EncoderOptions::SetAttributeString(int32_t att_id, const std::string &name, const std::string &val) { - if (att_id >= attribute_options_.size()) { + if (att_id >= static_cast(attribute_options_.size())) { attribute_options_.resize(att_id + 1); } attribute_options_[att_id].SetString(name, val); @@ -119,7 +119,7 @@ void EncoderOptions::SetAttributeString(int32_t att_id, const std::string &name, int EncoderOptions::GetAttributeInt(int32_t att_id, const std::string &name, int default_val) const { - if (att_id < attribute_options_.size()) { + if (att_id < static_cast(attribute_options_.size())) { if (attribute_options_[att_id].IsOptionSet(name)) return attribute_options_[att_id].GetInt(name, default_val); } @@ -128,7 +128,7 @@ int EncoderOptions::GetAttributeInt(int32_t att_id, const std::string &name, bool EncoderOptions::GetAttributeBool(int32_t att_id, const std::string &name, bool default_val) const { - if (att_id < attribute_options_.size()) { + if (att_id < static_cast(attribute_options_.size())) { if (attribute_options_[att_id].IsOptionSet(name)) return attribute_options_[att_id].GetBool(name, default_val); } @@ -138,7 +138,7 @@ bool EncoderOptions::GetAttributeBool(int32_t att_id, const std::string &name, std::string EncoderOptions::GetAttributeString( int32_t att_id, const std::string &name, const std::string &default_val) const { - if (att_id < attribute_options_.size()) { + if (att_id < static_cast(attribute_options_.size())) { if (attribute_options_[att_id].IsOptionSet(name)) return attribute_options_[att_id].GetString(name, default_val); } diff --git a/compression/encode.cc b/compression/encode.cc index 17f91d3..45c24ef 100644 --- a/compression/encode.cc +++ b/compression/encode.cc @@ -57,7 +57,9 @@ bool EncodePointCloudToBuffer(const PointCloud &pc, const EncoderOptions &options, EncoderBuffer *out_buffer) { std::unique_ptr encoder; - if (options.GetSpeed() < 10 && pc.num_attributes() == 1) { + const int encoding_method = options.GetGlobalInt("encoding_method", -1); + if (encoding_method == POINT_CLOUD_KD_TREE_ENCODING || + (options.GetSpeed() < 10 && pc.num_attributes() == 1)) { const PointAttribute *const att = pc.attribute(0); bool create_kd_tree_encoder = true; // Kd-Tree encoder can be currently used only under following conditions: @@ -76,9 +78,13 @@ bool EncodePointCloudToBuffer(const PointCloud &pc, options.GetAttributeInt(0, "quantization_bits", -1) <= 0) create_kd_tree_encoder = false; // Quantization not enabled. if (create_kd_tree_encoder) { - // Create kD-tree encoder. + // Create kD-tree encoder (all checks passed). encoder = std::unique_ptr(new PointCloudKdTreeEncoder()); + } else if (encoding_method == POINT_CLOUD_KD_TREE_ENCODING) { + // Encoding method was explicitly specified but we cannot use it for the + // given input (some of the checks above failed). + return false; } } if (!encoder) { @@ -143,6 +149,10 @@ void SetUseBuiltInAttributeCompression(EncoderOptions *options, bool enabled) { enabled); } +void SetEncodingMethod(EncoderOptions *options, int encoding_method) { + options->GetGlobalOptions()->SetInt("encoding_method", encoding_method); +} + void SetNamedAttributePredictionScheme(EncoderOptions *options, const PointCloud &pc, GeometryAttribute::Type type, diff --git a/compression/encode.h b/compression/encode.h index bdc0a4f..12a2c6e 100644 --- a/compression/encode.h +++ b/compression/encode.h @@ -73,6 +73,23 @@ void SetAttributeQuantization(Options *options, int quantization_bits); // Default: [true]. void SetUseBuiltInAttributeCompression(EncoderOptions *options, bool enabled); +// Sets the desired encoding method for a given geometry. By default, encoding +// method is selected based on the properties of the input geometry and based on +// the other options selected in the used EncoderOptions (such as desired +// encoding and decoding speed). This function should be called only when a +// specific method is required. +// +// |encoding_method| can be one of the following as defined in +// compression/config/compression_shared.h : +// POINT_CLOUD_SEQUENTIAL_ENCODING +// POINT_CLOUD_KD_TREE_ENCODING +// MESH_SEQUENTIAL_ENCODING +// MESH_EDGEBREAKER_ENCODING +// +// If the selected method cannot be used for the given input, the subsequent +// call of EncodePointCloudToBuffer or EncodeMeshToBuffer is going to fail. +void SetEncodingMethod(EncoderOptions *options, int encoding_method); + // Sets the desired prediction method for a given attribute. By default, // prediction scheme is selected automatically by the encoder using other // provided options (such as speed) and input geometry type (mesh, point cloud). diff --git a/compression/mesh/mesh_decoder.h b/compression/mesh/mesh_decoder.h index 40ed7b8..6138bea 100644 --- a/compression/mesh/mesh_decoder.h +++ b/compression/mesh/mesh_decoder.h @@ -41,14 +41,14 @@ class MeshDecoder : public PointCloudDecoder { // Returns the attribute connectivity data or nullptr if it does not exist. virtual const MeshAttributeCornerTable *GetAttributeCornerTable( - int att_id) const { + int /* att_id */) const { return nullptr; } // Returns the decoding data for a given attribute or nullptr when the data // does not exist. virtual const MeshAttributeIndicesEncodingData *GetAttributeEncodingData( - int att_id) const { + int /* att_id */) const { return nullptr; } diff --git a/compression/mesh/mesh_edgebreaker_decoder_impl.cc b/compression/mesh/mesh_edgebreaker_decoder_impl.cc index 65e9b06..bc7718c 100644 --- a/compression/mesh/mesh_edgebreaker_decoder_impl.cc +++ b/compression/mesh/mesh_edgebreaker_decoder_impl.cc @@ -39,17 +39,6 @@ namespace draco { // For more description about how the edges are used, see comment inside // ZipConnectivity() method. -// The initial status of all edges. -static constexpr CornerIndex EDGEBREAKER_FREE_EDGE_DEFAULT(kInvalidCornerIndex); - -// A mark assigned to free edge that was part of a face encoded as -// with TOPOLOGY_C (or the init face). -static constexpr CornerIndex EDGEBREAKER_FREE_EDGE_A(kInvalidCornerIndex + 1); - -// A mark assigned to free edge that was created when processing faces -// with other topologies. -static constexpr CornerIndex EDGEBREAKER_FREE_EDGE_B(kInvalidCornerIndex + 2); - template MeshEdgeBreakerDecoderImpl::MeshEdgeBreakerDecoderImpl() : decoder_(nullptr), @@ -71,7 +60,7 @@ template const MeshAttributeCornerTable * MeshEdgeBreakerDecoderImpl::GetAttributeCornerTable( int att_id) const { - for (int i = 0; i < attribute_data_.size(); ++i) { + for (uint32_t i = 0; i < attribute_data_.size(); ++i) { const AttributesDecoder *const dec = decoder_->attributes_decoder(attribute_data_[i].decoder_id); for (int j = 0; j < dec->num_attributes(); ++j) { @@ -89,7 +78,7 @@ template const MeshAttributeIndicesEncodingData * MeshEdgeBreakerDecoderImpl::GetAttributeEncodingData( int att_id) const { - for (int i = 0; i < attribute_data_.size(); ++i) { + for (uint32_t i = 0; i < attribute_data_.size(); ++i) { const AttributesDecoder *const dec = decoder_->attributes_decoder(attribute_data_[i].decoder_id); for (int j = 0; j < dec->num_attributes(); ++j) { @@ -295,7 +284,7 @@ bool MeshEdgeBreakerDecoderImpl::DecodeConnectivity() { // Decode attribute connectivity. // Prepare data structure for decoding non-position attribute connectivites. - for (int i = 0; i < attribute_data_.size(); ++i) { + for (uint32_t i = 0; i < attribute_data_.size(); ++i) { attribute_data_[i].connectivity_data.InitEmpty(corner_table_.get()); // Add all seams. for (int32_t c : attribute_data_[i].attribute_seam_corners) { @@ -307,7 +296,7 @@ bool MeshEdgeBreakerDecoderImpl::DecodeConnectivity() { pos_encoding_data_.vertex_to_encoded_attribute_value_index_map.resize( corner_table_->num_vertices()); - for (int i = 0; i < attribute_data_.size(); ++i) { + for (uint32_t i = 0; i < attribute_data_.size(); ++i) { // For non-position attributes, preallocate the vertex to value mapping // using the maximum number of vertices from the base corner table and the // attribute corner table (since the attribute decoder may use either of @@ -621,7 +610,7 @@ MeshEdgeBreakerDecoderImpl::DecodeHoleAndTopologySplitEvents( uint32_t num_topology_splits; if (!decoder_buffer->Decode(&num_topology_splits)) return -1; - for (int i = 0; i < num_topology_splits; ++i) { + for (uint32_t i = 0; i < num_topology_splits; ++i) { TopologySplitEventData event_data; if (!decoder_buffer->Decode(&event_data.split_symbol_id)) return -1; @@ -637,7 +626,7 @@ MeshEdgeBreakerDecoderImpl::DecodeHoleAndTopologySplitEvents( uint32_t num_hole_events; if (!decoder_buffer->Decode(&num_hole_events)) return -1; - for (int i = 0; i < num_hole_events; ++i) { + for (uint32_t i = 0; i < num_hole_events; ++i) { HoleEventData event_data; if (!decoder_buffer->Decode(&event_data)) return -1; @@ -658,13 +647,13 @@ bool MeshEdgeBreakerDecoderImpl< if (opp_corner < 0) { // Don't decode attribute seams on boundary edges (every boundary edge // is automatically an attribute seam). - for (int32_t i = 0; i < attribute_data_.size(); ++i) { + for (uint32_t i = 0; i < attribute_data_.size(); ++i) { attribute_data_[i].attribute_seam_corners.push_back(corners[c].value()); } continue; } - for (int32_t i = 0; i < attribute_data_.size(); ++i) { + for (uint32_t i = 0; i < attribute_data_.size(); ++i) { const bool is_seam = traversal_decoder_.DecodeAttributeSeam(i); if (is_seam) attribute_data_[i].attribute_seam_corners.push_back(corners[c].value()); @@ -724,7 +713,7 @@ bool MeshEdgeBreakerDecoderImpl::AssignPointsToCorners() { } else { // If we are not on the boundary we need to find the first seam (of any // attribute). - for (int i = 0; i < attribute_data_.size(); ++i) { + for (uint32_t i = 0; i < attribute_data_.size(); ++i) { if (!attribute_data_[i].connectivity_data.IsCornerOnSeam(c)) continue; // No seam for this attribute, ignore it. // Else there needs to be at least one seam edge. @@ -762,7 +751,7 @@ bool MeshEdgeBreakerDecoderImpl::AssignPointsToCorners() { c = corner_table_->SwingRight(c); while (c >= 0 && c != deduplication_first_corner) { bool attribute_seam = false; - for (int i = 0; i < attribute_data_.size(); ++i) { + for (uint32_t i = 0; i < attribute_data_.size(); ++i) { if (attribute_data_[i].connectivity_data.Vertex(c) != attribute_data_[i].connectivity_data.Vertex(prev_c)) { // Attribute index changed from the previous corner. We need to add a diff --git a/compression/mesh/mesh_edgebreaker_encoder_impl.cc b/compression/mesh/mesh_edgebreaker_encoder_impl.cc index c701278..e6c372e 100644 --- a/compression/mesh/mesh_edgebreaker_encoder_impl.cc +++ b/compression/mesh/mesh_edgebreaker_encoder_impl.cc @@ -52,7 +52,7 @@ template const MeshAttributeCornerTable * MeshEdgeBreakerEncoderImpl::GetAttributeCornerTable( int att_id) const { - for (int i = 0; i < attribute_data_.size(); ++i) { + for (uint32_t i = 0; i < attribute_data_.size(); ++i) { if (attribute_data_[i].attribute_index == att_id) { if (attribute_data_[i].is_connectivity_used) return &attribute_data_[i].connectivity_data; @@ -66,7 +66,7 @@ template const MeshAttributeIndicesEncodingData * MeshEdgeBreakerEncoderImpl::GetAttributeEncodingData( int att_id) const { - for (int i = 0; i < attribute_data_.size(); ++i) { + for (uint32_t i = 0; i < attribute_data_.size(); ++i) { if (attribute_data_[i].attribute_index == att_id) return &attribute_data_[i].encoding_data; } @@ -84,7 +84,7 @@ bool MeshEdgeBreakerEncoderImpl::GenerateAttributesEncoder( const PointAttribute *const att = GetEncoder()->point_cloud()->attribute(att_id); int32_t att_data_id = -1; - for (int i = 0; i < attribute_data_.size(); ++i) { + for (uint32_t i = 0; i < attribute_data_.size(); ++i) { if (attribute_data_[i].attribute_index == att_id) { att_data_id = i; break; @@ -348,7 +348,7 @@ bool MeshEdgeBreakerEncoderImpl::EncodeConnectivity() { // Encode topology split events. uint32_t num_events = topology_split_event_data_.size(); encoder_->buffer()->Encode(num_events); - for (int i = 0; i < num_events; ++i) { + for (uint32_t i = 0; i < num_events; ++i) { // TODO(ostava): We can do a better encoding of the event data but it's not // really needed for now. const TopologySplitEventData &event_data = topology_split_event_data_[i]; @@ -361,7 +361,7 @@ bool MeshEdgeBreakerEncoderImpl::EncodeConnectivity() { // Encode hole events data. num_events = hole_event_data_.size(); encoder_->buffer()->Encode(num_events); - for (int i = 0; i < num_events; ++i) { + for (uint32_t i = 0; i < num_events; ++i) { // TODO(ostava): We can do a better encoding of the event data but it's not // really needed for now. // This should be also made platform independent. @@ -653,7 +653,7 @@ int MeshEdgeBreakerEncoderImpl::GetSplitSymbolIdOnFace( template void MeshEdgeBreakerEncoderImpl< TraversalEncoder>::CheckAndStoreTopologySplitEvent(int src_symbol_id, - int src_face_id, + int /* src_face_id */, EdgeFaceName src_edge, int neighbor_face_id) { const int symbol_id = GetSplitSymbolIdOnFace(neighbor_face_id); @@ -719,7 +719,7 @@ bool MeshEdgeBreakerEncoderImpl< if (opp_corner < 0) continue; // Don't encode attribute seams on boundary edges. - for (int32_t i = 0; i < attribute_data_.size(); ++i) { + for (uint32_t i = 0; i < attribute_data_.size(); ++i) { if (attribute_data_[i].connectivity_data.IsCornerOppositeToSeamEdge( corners[c])) { traversal_encoder_.EncodeAttributeSeam(i, true); diff --git a/compression/mesh/mesh_edgebreaker_traversal_decoder.h b/compression/mesh/mesh_edgebreaker_traversal_decoder.h index a466268..f9d9c8b 100644 --- a/compression/mesh/mesh_edgebreaker_traversal_decoder.h +++ b/compression/mesh/mesh_edgebreaker_traversal_decoder.h @@ -40,7 +40,7 @@ class MeshEdgeBreakerTraversalDecoder { // Used to tell the decoder what is the number of expected decoded vertices. // Ignored by default. - void SetNumEncodedVertices(int num_vertices) {} + void SetNumEncodedVertices(int /* num_vertices */) {} // Set the number of non-position attribute data for which we need to decode // the connectivity. @@ -76,30 +76,30 @@ class MeshEdgeBreakerTraversalDecoder { // Returns the configuration of a new initial face. inline bool DecodeStartFaceConfiguration() { uint32_t face_configuration; - start_face_buffer_.DecodeBits32(1, &face_configuration); + start_face_buffer_.DecodeLeastSignificantBits32(1, &face_configuration); return face_configuration; } // Returns the next edgebreaker symbol that was reached during the traversal. inline uint32_t DecodeSymbol() { uint32_t symbol; - buffer_.DecodeBits32(1, &symbol); + buffer_.DecodeLeastSignificantBits32(1, &symbol); if (symbol == TOPOLOGY_C) { return symbol; } // Else decode two additional bits. uint32_t symbol_suffix; - buffer_.DecodeBits32(2, &symbol_suffix); + buffer_.DecodeLeastSignificantBits32(2, &symbol_suffix); symbol |= (symbol_suffix << 1); return symbol; } // Called whenever a new active corner is set in the decoder. - inline void NewActiveCornerReached(CornerIndex corner) {} + inline void NewActiveCornerReached(CornerIndex /* corner */) {} // Called whenever |source| vertex is about to be merged into the |dest| // vertex. - inline void MergeVertices(VertexIndex dest, VertexIndex source) {} + inline void MergeVertices(VertexIndex /* dest */, VertexIndex /* source */) {} // Returns true if there is an attribute seam for the next processed pair // of visited faces. diff --git a/compression/mesh/mesh_edgebreaker_traversal_encoder.h b/compression/mesh/mesh_edgebreaker_traversal_encoder.h index c690808..3bc560e 100644 --- a/compression/mesh/mesh_edgebreaker_traversal_encoder.h +++ b/compression/mesh/mesh_edgebreaker_traversal_encoder.h @@ -55,12 +55,12 @@ class MeshEdgeBreakerTraversalEncoder { // Called when a traversal starts from a new initial face. inline void EncodeStartFaceConfiguration(bool interior) { - start_face_buffer_.EncodeBits32(interior ? 1 : 0, 1); + start_face_buffer_.EncodeLeastSignificantBits32(1, interior ? 1 : 0); } // Called when a new corner is reached during the traversal. No-op for the // default encoder. - inline void NewCornerReached(CornerIndex corner) {} + inline void NewCornerReached(CornerIndex /* corner */) {} // Called whenever a new symbol is reached during the edgebreaker traversal. inline void EncodeSymbol(EdgeBreakerTopologyBitPattern symbol) { @@ -84,8 +84,8 @@ class MeshEdgeBreakerTraversalEncoder { traversal_buffer_.StartBitEncoding( encoder_impl_->GetEncoder()->mesh()->num_faces() * 3, true); for (int i = symbols_.size() - 1; i >= 0; --i) { - traversal_buffer_.EncodeBits32( - symbols_[i], edge_breaker_topology_bit_pattern_length[symbols_[i]]); + traversal_buffer_.EncodeLeastSignificantBits32( + edge_breaker_topology_bit_pattern_length[symbols_[i]], symbols_[i]); } traversal_buffer_.EndBitEncoding(); traversal_buffer_.Encode(start_face_buffer_.data(), diff --git a/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h b/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h index 8b3d8ac..3a19faf 100644 --- a/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h +++ b/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h @@ -40,7 +40,7 @@ class MeshEdgeBreakerTraversalPredictiveEncoder corner_table_ = encoder->GetCornerTable(); // Initialize valences of all vertices. vertex_valences_.resize(corner_table_->num_vertices()); - for (int i = 0; i < vertex_valences_.size(); ++i) { + for (uint32_t i = 0; i < vertex_valences_.size(); ++i) { vertex_valences_[i] = corner_table_->Valence(VertexIndex(i)); } } diff --git a/compression/mesh/mesh_encoder.h b/compression/mesh/mesh_encoder.h index d8e97d5..42a5b18 100644 --- a/compression/mesh/mesh_encoder.h +++ b/compression/mesh/mesh_encoder.h @@ -42,14 +42,14 @@ class MeshEncoder : public PointCloudEncoder { // Returns the attribute connectivity data or nullptr if it does not exist. virtual const MeshAttributeCornerTable *GetAttributeCornerTable( - int att_id) const { + int /* att_id */) const { return nullptr; } // Returns the encoding data for a given attribute or nullptr when the data // does not exist. virtual const MeshAttributeIndicesEncodingData *GetAttributeEncodingData( - int att_id) const { + int /* att_id */) const { return nullptr; } diff --git a/compression/mesh/mesh_encoder_test.cc b/compression/mesh/mesh_encoder_test.cc new file mode 100644 index 0000000..a171ff4 --- /dev/null +++ b/compression/mesh/mesh_encoder_test.cc @@ -0,0 +1,93 @@ +// Copyright 2016 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 "compression/mesh/mesh_encoder.h" + +#include "compression/encode.h" +#include "core/draco_test_base.h" +#include "core/draco_test_utils.h" +#include "io/obj_decoder.h" + +namespace draco { + +class MeshEncoderTest : public ::testing::TestWithParam { + protected: + MeshEncoderTest() {} + + // Fills out_method with id of the encoding method used for the test. + // Returns false if the encoding method is not set properly. + bool GetMethod(MeshEncoderMethod *out_method) const { + if (strcmp(GetParam(), "sequential") == 0) { + *out_method = MESH_SEQUENTIAL_ENCODING; + return true; + } + if (strcmp(GetParam(), "edgebreaker") == 0) { + *out_method = MESH_EDGEBREAKER_ENCODING; + return true; + } + return false; + } + std::unique_ptr DecodeObj(const std::string &file_name) const { + const std::string path = GetTestFileFullPath(file_name); + std::unique_ptr mesh(new Mesh()); + ObjDecoder decoder; + if (!decoder.DecodeFromFile(path, mesh.get())) + return nullptr; + return mesh; + } +}; + +TEST_P(MeshEncoderTest, EncodeGoldenMesh) { + // This test verifies that a given set of meshes are encoded to an expected + // output. This is useful for catching bugs in code changes that are not + // supposed to change the encoding. + // The test is expected to fail when the encoding is modified. In such case, + // the golden files need to be updated to reflect the changes. + MeshEncoderMethod method; + ASSERT_TRUE(GetMethod(&method)) + << "Test is run for an unknown encoding method"; + + const std::string file_name = "test_nm.obj"; + std::string golden_file_name = file_name; + golden_file_name += '.'; + golden_file_name += GetParam(); + golden_file_name += ".out"; + const std::unique_ptr mesh(DecodeObj(file_name)); + ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name; + + EncoderOptions options = CreateDefaultEncoderOptions(); + EncoderBuffer buffer; + ASSERT_TRUE(EncodeMeshToBuffer(*mesh.get(), options, &buffer)) + << "Failed encoding test mesh " << file_name << " with method " + << GetParam(); + + if (!FLAGS_update_golden_files) { + EXPECT_TRUE( + CompareGoldenFile(golden_file_name, buffer.data(), buffer.size())) + << "Encoded data is different from the golden file. Please verify that " + "the" + " encoding works as expected and update the golden file if necessary" + " (run the test with --update_golden_files flag)."; + } else { + // Save the files into the local folder. + EXPECT_TRUE( + GenerateGoldenFile(golden_file_name, buffer.data(), buffer.size())) + << "Failed to generate new golden file for " << file_name; + } +} + +INSTANTIATE_TEST_CASE_P(MeshEncoderTests, MeshEncoderTest, + ::testing::Values("sequential", "edgebreaker")); + +} // namespace draco diff --git a/compression/point_cloud/algorithms/float_points_kd_tree_encoder.h b/compression/point_cloud/algorithms/float_points_kd_tree_encoder.h index 9493133..9bcce8e 100644 --- a/compression/point_cloud/algorithms/float_points_kd_tree_encoder.h +++ b/compression/point_cloud/algorithms/float_points_kd_tree_encoder.h @@ -43,7 +43,7 @@ namespace draco { // there are more leading zeros, which is then compressed better by the // arithmetic encoding. -// TODO(hemmer): Make name consistent with other point cloud encoders. +// TODO(hemmer): Remove class because it duplicates quantization code. class FloatPointsKdTreeEncoder { public: FloatPointsKdTreeEncoder(); diff --git a/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.cc b/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.cc index ce8c508..d27417f 100644 --- a/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.cc +++ b/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.cc @@ -16,8 +16,6 @@ #include "compression/point_cloud/algorithms/point_cloud_types.h" -// TODO(hemmer): make independent from dimension 3 - namespace draco { template class IntegerPointsKdTreeDecoder; @@ -32,4 +30,16 @@ template class IntegerPointsKdTreeDecoder; template class IntegerPointsKdTreeDecoder; template class IntegerPointsKdTreeDecoder; +template class IntegerPointsKdTreeDecoder; +template class IntegerPointsKdTreeDecoder; +template class IntegerPointsKdTreeDecoder; +template class IntegerPointsKdTreeDecoder; +template class IntegerPointsKdTreeDecoder; +template class IntegerPointsKdTreeDecoder; +template class IntegerPointsKdTreeDecoder; +template class IntegerPointsKdTreeDecoder; +template class IntegerPointsKdTreeDecoder; +template class IntegerPointsKdTreeDecoder; +template class IntegerPointsKdTreeDecoder; + } // namespace draco diff --git a/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.h b/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.h index 10c6346..f1dabc0 100644 --- a/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.h +++ b/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.h @@ -85,7 +85,6 @@ struct IntegerPointsKdTreeDecoderCompressionPolicy<10> // Decodes a point cloud encoded by IntegerPointsKdTreeEncoder. // |PointDiT| is a type representing a point with uint32_t coordinates. // must provide construction from three uint32_t and operator[]. -// TODO(hemmer): compression_level_t_t template class IntegerPointsKdTreeDecoder { typedef IntegerPointsKdTreeDecoderCompressionPolicy @@ -104,11 +103,11 @@ class IntegerPointsKdTreeDecoder { bool DecodePoints(DecoderBuffer *buffer, OutputIteratorT oit); private: - // For the sack of readability of code, we decided to make this exception + // For the sake of readability of code, we decided to make this exception // from the naming scheme. static constexpr int D = PointTraits::Dimension(); - uint32_t GetAxis(uint32_t num_remaining_points, PointDiT base, + uint32_t GetAxis(uint32_t num_remaining_points, const PointDiT &base, std::array levels, uint32_t last_axis); template @@ -117,12 +116,12 @@ class IntegerPointsKdTreeDecoder { OutputIteratorT oit); void DecodeNumber(int nbits, uint32_t *value) { - numbers_decoder_.DecodeBits32(nbits, value); + numbers_decoder_.DecodeLeastSignificantBits32(nbits, value); } struct DecodingStatus { DecodingStatus( - uint32_t num_remaining_points_, PointDiT old_base_, + uint32_t num_remaining_points_, const PointDiT &old_base_, std::array::Dimension()> levels_, uint32_t last_axis_) : num_remaining_points(num_remaining_points_), @@ -175,7 +174,7 @@ bool IntegerPointsKdTreeDecoder::DecodePoints( template uint32_t IntegerPointsKdTreeDecoder::GetAxis( - uint32_t num_remaining_points, PointDiT base, + uint32_t num_remaining_points, const PointDiT & /* base */, std::array levels, uint32_t last_axis) { if (!Policy::select_axis) return DRACO_INCREMENT_MOD(last_axis, D); @@ -188,7 +187,7 @@ uint32_t IntegerPointsKdTreeDecoder::GetAxis( } } } else { - axis_decoder_.DecodeBits32(4, &best_axis); + axis_decoder_.DecodeLeastSignificantBits32(4, &best_axis); } return best_axis; @@ -219,7 +218,7 @@ void IntegerPointsKdTreeDecoder::OctreeDecode( // All axes have been fully subdivided, just output points. if ((bit_length_ - level) == 0) { - for (int i = 0; i < num_remaining_points; i++) { + for (int i = 0; i < static_cast(num_remaining_points); i++) { *oit++ = old_base; } continue; @@ -238,14 +237,14 @@ void IntegerPointsKdTreeDecoder::OctreeDecode( num_remaining_bits[i] = bit_length_ - levels[axes[i]]; } - for (int i = 0; i < num_remaining_points; ++i) { + for (uint32_t i = 0; i < num_remaining_points; ++i) { // Get remaining bits, mind the carry if not starting at x. PointDiT p = PointTraits::Origin(); - for (int i = 0; i < D; i++) { - if (num_remaining_bits[i]) - remaining_bits_decoder_.DecodeBits32(num_remaining_bits[i], - &p[axes[i]]); - p[axes[i]] = old_base[axes[i]] | p[axes[i]]; + for (int j = 0; j < static_cast(D); j++) { + if (num_remaining_bits[j]) + remaining_bits_decoder_.DecodeLeastSignificantBits32( + num_remaining_bits[j], &p[axes[j]]); + p[axes[j]] = old_base[axes[j]] | p[axes[j]]; } *oit++ = p; } diff --git a/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.cc b/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.cc index b589e48..bbcab6a 100644 --- a/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.cc +++ b/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.cc @@ -16,8 +16,6 @@ #include "compression/point_cloud/algorithms/point_cloud_types.h" -// TODO(hemmer): make independent from dimension 3 - namespace draco { template class IntegerPointsKdTreeEncoder; @@ -32,4 +30,16 @@ template class IntegerPointsKdTreeEncoder; template class IntegerPointsKdTreeEncoder; template class IntegerPointsKdTreeEncoder; +template class IntegerPointsKdTreeEncoder; +template class IntegerPointsKdTreeEncoder; +template class IntegerPointsKdTreeEncoder; +template class IntegerPointsKdTreeEncoder; +template class IntegerPointsKdTreeEncoder; +template class IntegerPointsKdTreeEncoder; +template class IntegerPointsKdTreeEncoder; +template class IntegerPointsKdTreeEncoder; +template class IntegerPointsKdTreeEncoder; +template class IntegerPointsKdTreeEncoder; +template class IntegerPointsKdTreeEncoder; + } // namespace draco diff --git a/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.h b/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.h index d8c496c..d89637d 100644 --- a/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.h +++ b/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.h @@ -137,7 +137,7 @@ class IntegerPointsKdTreeEncoder { static constexpr int D = PointTraits::Dimension(); template uint32_t GetAxis(RandomAccessIteratorT begin, RandomAccessIteratorT end, - PointDiT old_base, std::array levels, + const PointDiT &old_base, std::array levels, uint32_t last_axis); template @@ -156,14 +156,14 @@ class IntegerPointsKdTreeEncoder { }; void EncodeNumber(int nbits, uint32_t value) { - numbers_encoder_.EncodeBits32(nbits, value); + numbers_encoder_.EncodeLeastSignificantBits32(nbits, value); } template struct EncodingStatus { EncodingStatus( RandomAccessIteratorT begin_, RandomAccessIteratorT end_, - PointDiT old_base_, + const PointDiT &old_base_, std::array::Dimension()> levels_, uint32_t last_axis_) : begin(begin_), @@ -224,8 +224,9 @@ bool IntegerPointsKdTreeEncoder::EncodePoints( template template uint32_t IntegerPointsKdTreeEncoder::GetAxis( - RandomAccessIteratorT begin, RandomAccessIteratorT end, PointDiT old_base, - std::array levels, uint32_t last_axis) { + RandomAccessIteratorT begin, RandomAccessIteratorT end, + const PointDiT &old_base, std::array levels, + uint32_t last_axis) { if (!Policy::select_axis) return DRACO_INCREMENT_MOD(last_axis, D); @@ -237,7 +238,6 @@ uint32_t IntegerPointsKdTreeEncoder::GetAxis( DCHECK_EQ(true, end - begin != 0); - // TODO(hemmer): Try to find the optimal value for this cut off. uint32_t best_axis = 0; if (end - begin < 64) { for (uint32_t axis = 1; axis < D; ++axis) { @@ -281,7 +281,7 @@ uint32_t IntegerPointsKdTreeEncoder::GetAxis( } } } - axis_encoder_.EncodeBits32(4, best_axis); + axis_encoder_.EncodeLeastSignificantBits32(4, best_axis); } return best_axis; @@ -332,12 +332,13 @@ void IntegerPointsKdTreeEncoder::OctreeEncode( num_remaining_bits[i] = bit_length_ - levels[axes[i]]; } - for (int i = 0; i < num_remaining_points; ++i) { + for (uint32_t i = 0; i < num_remaining_points; ++i) { const PointDiT &p = *(begin + i); - for (int i = 0; i < D; i++) { - if (num_remaining_bits[i]) - remaining_bits_encoder_.EncodeBits32(num_remaining_bits[i], - p[axes[i]]); + for (int j = 0; j < D; j++) { + if (num_remaining_bits[j]) { + remaining_bits_encoder_.EncodeLeastSignificantBits32( + num_remaining_bits[j], p[axes[j]]); + } } } continue; diff --git a/compression/point_cloud/point_cloud_decoder.h b/compression/point_cloud/point_cloud_decoder.h index 8f523bf..f214244 100644 --- a/compression/point_cloud/point_cloud_decoder.h +++ b/compression/point_cloud/point_cloud_decoder.h @@ -35,7 +35,7 @@ class PointCloudDecoder { void SetAttributesDecoder(int att_decoder_id, std::unique_ptr decoder) { - if (att_decoder_id >= attributes_decoders_.size()) + if (att_decoder_id >= static_cast(attributes_decoders_.size())) attributes_decoders_.resize(att_decoder_id + 1); attributes_decoders_[att_decoder_id] = std::move(decoder); } diff --git a/compression/point_cloud/point_cloud_encoder.cc b/compression/point_cloud/point_cloud_encoder.cc index 39cd01f..cd382a3 100644 --- a/compression/point_cloud/point_cloud_encoder.cc +++ b/compression/point_cloud/point_cloud_encoder.cc @@ -91,7 +91,7 @@ bool PointCloudEncoder::GenerateAttributesEncoders() { return false; } attribute_to_encoder_map_.resize(point_cloud_->num_attributes()); - for (int i = 0; i < attributes_encoders_.size(); ++i) { + for (uint32_t i = 0; i < attributes_encoders_.size(); ++i) { for (int j = 0; j < attributes_encoders_[i]->num_attributes(); ++j) { attribute_to_encoder_map_[attributes_encoders_[i]->GetAttributeId(j)] = i; } @@ -143,11 +143,11 @@ bool PointCloudEncoder::RearrangeAttributesEncoders() { // but it will require changes in the current API. attributes_encoder_ids_order_.resize(attributes_encoders_.size()); std::vector is_encoder_processed(attributes_encoders_.size(), false); - int num_processed_encoders = 0; + uint32_t num_processed_encoders = 0; while (num_processed_encoders < attributes_encoders_.size()) { // Flagged when any of the encoder get processed. bool encoder_processed = false; - for (int i = 0; i < attributes_encoders_.size(); ++i) { + for (uint32_t i = 0; i < attributes_encoders_.size(); ++i) { if (is_encoder_processed[i]) continue; // Encoder already processed. // Check if all parent encoders are already processed. @@ -156,7 +156,7 @@ bool PointCloudEncoder::RearrangeAttributesEncoders() { const int32_t att_id = attributes_encoders_[i]->GetAttributeId(p); for (int ap = 0; ap < attributes_encoders_[i]->NumParentAttributes(att_id); ++ap) { - const int32_t parent_att_id = + const uint32_t parent_att_id = attributes_encoders_[i]->GetParentAttributeId(att_id, ap); const int32_t parent_encoder_id = attribute_to_encoder_map_[parent_att_id]; @@ -188,7 +188,8 @@ bool PointCloudEncoder::RearrangeAttributesEncoders() { std::vector is_attribute_processed(point_cloud_->num_attributes(), false); int num_processed_attributes; - for (int ae_order = 0; ae_order < attributes_encoders_.size(); ++ae_order) { + for (uint32_t ae_order = 0; ae_order < attributes_encoders_.size(); + ++ae_order) { const int ae = attributes_encoder_ids_order_[ae_order]; const int32_t num_encoder_attributes = attributes_encoders_[ae]->num_attributes(); diff --git a/compression/point_cloud/point_cloud_encoder.h b/compression/point_cloud/point_cloud_encoder.h index 66d7353..be293b2 100644 --- a/compression/point_cloud/point_cloud_encoder.h +++ b/compression/point_cloud/point_cloud_encoder.h @@ -101,7 +101,7 @@ class PointCloudEncoder { // Encodes any data that is necessary to recreate a given attribute encoder. // Note: this is called in order in which the attribute encoders are going to // be encoded. - virtual bool EncodeAttributesEncoderIdentifier(int32_t att_encoder_id) { + virtual bool EncodeAttributesEncoderIdentifier(int32_t /* att_encoder_id */) { return true; } diff --git a/compression/point_cloud/point_cloud_kd_tree_encoding_test.cc b/compression/point_cloud/point_cloud_kd_tree_encoding_test.cc new file mode 100644 index 0000000..7ae2a3b --- /dev/null +++ b/compression/point_cloud/point_cloud_kd_tree_encoding_test.cc @@ -0,0 +1,119 @@ +// Copyright 2016 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 "compression/point_cloud/point_cloud_kd_tree_decoder.h" +#include "compression/point_cloud/point_cloud_kd_tree_encoder.h" +#include "core/draco_test_base.h" +#include "core/draco_test_utils.h" +#include "core/vector_d.h" +#include "io/obj_decoder.h" +#include "point_cloud/point_cloud_builder.h" + +namespace draco { + +class PointCloudKdTreeEncodingTest : public ::testing::Test { + protected: + std::unique_ptr DecodeObj(const std::string &file_name) const { + const std::string path = GetTestFileFullPath(file_name); + ObjDecoder decoder; + std::unique_ptr pc(new PointCloud()); + if (!decoder.DecodeFromFile(path, pc.get())) + return nullptr; + return pc; + } + + void ComparePointClouds(const PointCloud &p0, const PointCloud &p1) const { + ASSERT_EQ(p0.num_points(), p1.num_points()); + ASSERT_EQ(p0.num_attributes(), p1.num_attributes()); + // Currently works only with one attribute. + ASSERT_EQ(p0.num_attributes(), 1); + ASSERT_EQ(p0.attribute(0)->components_count(), 3); + std::vector> points0, points1; + for (PointIndex i(0); i < p0.num_points(); ++i) { + VectorD pos0, pos1; + p0.attribute(0)->ConvertValue(p0.attribute(0)->mapped_index(i), &pos0[0]); + p1.attribute(0)->ConvertValue(p1.attribute(0)->mapped_index(i), &pos1[0]); + points0.push_back(pos0); + points1.push_back(pos1); + } + // To compare the point clouds we sort points from both inputs separately, + // and then we compare all matching points one by one. + // TODO(ostava): Note that this is not guaranteed to work for quantized + // point clouds because the order of points may actually change because + // of the quantization. The test should be make more robust to handle such + // case. + std::sort(points0.begin(), points0.end()); + std::sort(points1.begin(), points1.end()); + for (uint32_t i = 0; i < points0.size(); ++i) { + ASSERT_LE((points0[i] - points1[i]).SquaredNorm(), 1e-2); + } + } + + void TestKdTreeEncoding(const PointCloud &pc) { + EncoderBuffer buffer; + PointCloudKdTreeEncoder encoder; + EncoderOptions options = EncoderOptions::CreateDefaultOptions(); + options.SetGlobalInt("quantization_bits", 12); + encoder.SetPointCloud(pc); + ASSERT_TRUE(encoder.Encode(options, &buffer)); + + DecoderBuffer dec_buffer; + dec_buffer.Init(buffer.data(), buffer.size()); + PointCloudKdTreeDecoder decoder; + + std::unique_ptr out_pc(new PointCloud()); + ASSERT_TRUE(decoder.Decode(&dec_buffer, out_pc.get())); + + ComparePointClouds(pc, *out_pc.get()); + } + + void TestFloatEncoding(const std::string &file_name) { + std::unique_ptr pc = DecodeObj(file_name); + ASSERT_NE(pc, nullptr); + + TestKdTreeEncoding(*pc.get()); + } +}; + +TEST_F(PointCloudKdTreeEncodingTest, TestFloatKdTreeEncoding) { + TestFloatEncoding("cube_subd.obj"); +} + +TEST_F(PointCloudKdTreeEncodingTest, TestIntKdTreeEncoding) { + constexpr int num_points = 120; + std::vector> points(num_points); + for (int i = 0; i < num_points; ++i) { + std::array pos; + // Generate some pseudo-random points. + pos[0] = 8 * ((i * 7) % 127); + pos[1] = 13 * ((i * 3) % 321); + pos[2] = 29 * ((i * 19) % 450); + points[i] = pos; + } + + PointCloudBuilder builder; + builder.Start(num_points); + const int att_id = + builder.AddAttribute(GeometryAttribute::POSITION, 3, DT_UINT32); + for (PointIndex i(0); i < num_points; ++i) { + builder.SetAttributeValueForPoint(att_id, PointIndex(i), + &(points[i.value()])[0]); + } + std::unique_ptr pc = builder.Finalize(false); + ASSERT_NE(pc, nullptr); + + TestKdTreeEncoding(*pc.get()); +} + +} // namespace draco diff --git a/compression/point_cloud/point_cloud_sequential_encoding_test.cc b/compression/point_cloud/point_cloud_sequential_encoding_test.cc new file mode 100644 index 0000000..75994ab --- /dev/null +++ b/compression/point_cloud/point_cloud_sequential_encoding_test.cc @@ -0,0 +1,62 @@ +// Copyright 2016 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 "compression/point_cloud/point_cloud_sequential_decoder.h" +#include "compression/point_cloud/point_cloud_sequential_encoder.h" +#include "core/draco_test_base.h" +#include "core/draco_test_utils.h" +#include "io/obj_decoder.h" + +namespace draco { + +class PointCloudSequentialEncodingTest : public ::testing::Test { + protected: + std::unique_ptr DecodeObj(const std::string &file_name) const { + const std::string path = GetTestFileFullPath(file_name); + ObjDecoder decoder; + std::unique_ptr pc(new PointCloud()); + if (!decoder.DecodeFromFile(path, pc.get())) + return nullptr; + return pc; + } + + void TestEncoding(const std::string &file_name) { + std::unique_ptr pc = DecodeObj(file_name); + ASSERT_NE(pc, nullptr); + + EncoderBuffer buffer; + PointCloudSequentialEncoder encoder; + EncoderOptions options = EncoderOptions::CreateDefaultOptions(); + encoder.SetPointCloud(*pc.get()); + ASSERT_TRUE(encoder.Encode(options, &buffer)); + + DecoderBuffer dec_buffer; + dec_buffer.Init(buffer.data(), buffer.size()); + PointCloudSequentialDecoder decoder; + + std::unique_ptr out_pc(new PointCloud()); + ASSERT_TRUE(decoder.Decode(&dec_buffer, out_pc.get())); + + ASSERT_EQ(out_pc->num_points(), pc->num_points()); + } +}; + +TEST_F(PointCloudSequentialEncodingTest, DoesEncodeAndDecode) { + TestEncoding("test_nm.obj"); +} + +// TODO(ostava): Test the reusability of a single instance of the encoder and +// decoder class. + +} // namespace draco diff --git a/core/adaptive_rans_coding.cc b/core/adaptive_rans_coding.cc index a43effa..e42cee4 100644 --- a/core/adaptive_rans_coding.cc +++ b/core/adaptive_rans_coding.cc @@ -88,7 +88,6 @@ void AdaptiveRAnsBitDecoder::StartDecoding(DecoderBuffer *source_buffer) { source_buffer->Advance(size_in_bytes); } -// TODO(hemmer): Consider moving these to the .h file. bool AdaptiveRAnsBitDecoder::DecodeNextBit() { const uint8_t p0 = clamp_probability(p0_f_); const bool bit = static_cast(rabs_read(&ans_decoder_, p0)); @@ -96,7 +95,8 @@ bool AdaptiveRAnsBitDecoder::DecodeNextBit() { return bit; } -void AdaptiveRAnsBitDecoder::DecodeBits32(int nbits, uint32_t *value) { +void AdaptiveRAnsBitDecoder::DecodeLeastSignificantBits32(int nbits, + uint32_t *value) { DCHECK_EQ(true, nbits <= 32); DCHECK_EQ(true, nbits > 0); diff --git a/core/adaptive_rans_coding.h b/core/adaptive_rans_coding.h index 912d663..3858283 100644 --- a/core/adaptive_rans_coding.h +++ b/core/adaptive_rans_coding.h @@ -38,7 +38,7 @@ class AdaptiveRAnsBitEncoder { // Encode |nibts| of |value|, starting from the least significant bit. // |nbits| must be > 0 and <= 32. - void EncodeBits32(int nbits, uint32_t value) { + void EncodeLeastSignificantBits32(int nbits, uint32_t value) { DCHECK_EQ(true, nbits <= 32); DCHECK_EQ(true, nbits > 0); uint32_t selector = (1 << (nbits - 1)); @@ -72,7 +72,7 @@ class AdaptiveRAnsBitDecoder { // Decode the next |nbits| and return the sequence in |value|. |nbits| must be // > 0 and <= 32. - void DecodeBits32(int nbits, uint32_t *value); + void DecodeLeastSignificantBits32(int nbits, uint32_t *value); void EndDecoding() {} diff --git a/core/ans.h b/core/ans.h index 1828d79..9c97baf 100644 --- a/core/ans.h +++ b/core/ans.h @@ -86,7 +86,7 @@ static uint32_t mem_get_le24(const void *vmem) { return val; } -static uint32_t mem_get_le32(const void *vmem) { +static inline uint32_t mem_get_le32(const void *vmem) { uint32_t val; const uint8_t *mem = (const uint8_t *)vmem; @@ -472,11 +472,11 @@ class RAnsDecoder { probability_table_.resize(num_symbols); uint32_t cum_prob = 0; uint32_t act_prob = 0; - for (int i = 0; i < num_symbols; ++i) { + for (uint32_t i = 0; i < num_symbols; ++i) { probability_table_[i].prob = token_probs[i]; probability_table_[i].cum_prob = cum_prob; cum_prob += token_probs[i]; - for (int j = act_prob; j < cum_prob; ++j) { + for (uint32_t j = act_prob; j < cum_prob; ++j) { lut_table_[j] = i; } act_prob = cum_prob; diff --git a/core/bit_coder.cc b/core/bit_coder.cc index 5d05347..1f11815 100644 --- a/core/bit_coder.cc +++ b/core/bit_coder.cc @@ -17,8 +17,7 @@ // TODO(fgalligan): Remove this file. namespace draco { -BitEncoder::BitEncoder(char *data, size_t length) - : bit_buffer_(data), bit_buffer_length_(length), bit_offset_(0) {} +BitEncoder::BitEncoder(char *data) : bit_buffer_(data), bit_offset_(0) {} BitDecoder::BitDecoder() : bit_buffer_(nullptr), bit_buffer_end_(nullptr), bit_offset_(0) {} diff --git a/core/bit_coder.h b/core/bit_coder.h index ea3acb6..a9d762a 100644 --- a/core/bit_coder.h +++ b/core/bit_coder.h @@ -27,9 +27,8 @@ namespace draco { // Class to encode bits to a bit buffer. class BitEncoder { public: - // |data| is the buffer to write the bits into. |length| is the size of the - // buffer. - BitEncoder(char *data, size_t length); + // |data| is the buffer to write the bits into. + explicit BitEncoder(char *data); // Write |nbits| of |data| into the bit buffer. void PutBits(uint32_t data, int32_t nbits) { @@ -45,7 +44,7 @@ class BitEncoder { // TODO(fgalligan): Remove this function once we know we do not need the // old API anymore. // This is a function of an old API, that currently does nothing. - void Flush(int left_over_bit_value) {} + void Flush(int /* left_over_bit_value */) {} // Return the number of bits required to store the given number static uint32_t BitsRequired(uint32_t x) { @@ -69,7 +68,6 @@ class BitEncoder { } char *bit_buffer_; - size_t bit_buffer_length_; size_t bit_offset_; }; @@ -98,7 +96,7 @@ class BitDecoder { inline uint32_t EnsureBits(int k) { DCHECK_LE(k, 24); - DCHECK_LE(k, AvailBits()); + DCHECK_LE(static_cast(k), AvailBits()); uint32_t buf = 0; for (int i = 0; i < k; ++i) { diff --git a/core/bit_coder_test.cc b/core/bit_coder_test.cc new file mode 100644 index 0000000..a2c48b5 --- /dev/null +++ b/core/bit_coder_test.cc @@ -0,0 +1,113 @@ +// Copyright 2016 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 "core/bit_coder.h" + +#include "core/draco_test_base.h" + +namespace draco { + +class BitDecoderTest : public ::testing::Test {}; + +class BitEncoderTest : public ::testing::Test {}; + +TEST_F(BitDecoderTest, TestBitCodersByteAligned) { + constexpr int buffer_size = 32; + char buffer[buffer_size]; + BitEncoder encoder(buffer); + const uint8_t data[] = {0x76, 0x54, 0x32, 0x10, 0x76, 0x54, 0x32, 0x10}; + const int bytes_to_encode = sizeof(data); + + for (int i = 0; i < bytes_to_encode; ++i) { + encoder.PutBits(data[i], sizeof(data[i]) * 8); + ASSERT_EQ((i + 1) * sizeof(data[i]) * 8, encoder.Bits()); + } + + BitDecoder decoder; + decoder.reset(static_cast(buffer), bytes_to_encode); + for (int i = 0; i < bytes_to_encode; ++i) { + uint32_t x = 0; + ASSERT_TRUE(decoder.GetBits(8, &x)); + ASSERT_EQ(x, data[i]); + } + + ASSERT_EQ(bytes_to_encode * 8u, decoder.BitsDecoded()); +} + +TEST_F(BitDecoderTest, TestBitCodersNonByte) { + constexpr int buffer_size = 32; + char buffer[buffer_size]; + BitEncoder encoder(buffer); + const uint8_t data[] = {0x76, 0x54, 0x32, 0x10, 0x76, 0x54, 0x32, 0x10}; + const uint32_t bits_to_encode = 51; + const int bytes_to_encode = (bits_to_encode / 8) + 1; + + for (int i = 0; i < bytes_to_encode; ++i) { + const int num_bits = (encoder.Bits() + 8 <= bits_to_encode) + ? 8 + : bits_to_encode - encoder.Bits(); + encoder.PutBits(data[i], num_bits); + } + + BitDecoder decoder; + decoder.reset(static_cast(buffer), bytes_to_encode); + int64_t bits_to_decode = encoder.Bits(); + for (int i = 0; i < bytes_to_encode; ++i) { + uint32_t x = 0; + const int num_bits = (bits_to_decode > 8) ? 8 : bits_to_decode; + ASSERT_TRUE(decoder.GetBits(num_bits, &x)); + const int bits_to_shift = 8 - num_bits; + const uint8_t test_byte = + ((data[i] << bits_to_shift) & 0xff) >> bits_to_shift; + ASSERT_EQ(x, test_byte); + bits_to_decode -= 8; + } + + ASSERT_EQ(bits_to_encode, decoder.BitsDecoded()); +} + +TEST_F(BitDecoderTest, TestSingleBits) { + const int data = 0xaaaa; + + BitDecoder decoder; + decoder.reset(static_cast(&data), sizeof(data)); + + for (uint32_t i = 0; i < 16; ++i) { + uint32_t x = 0; + ASSERT_TRUE(decoder.GetBits(1, &x)); + ASSERT_EQ(x, (i % 2)); + } + + ASSERT_EQ(16u, decoder.BitsDecoded()); +} + +TEST_F(BitDecoderTest, TestMultipleBits) { + const uint8_t data[] = {0x76, 0x54, 0x32, 0x10, 0x76, 0x54, 0x32, 0x10}; + + BitDecoder decoder; + decoder.reset(static_cast(data), sizeof(data)); + + uint32_t x = 0; + for (uint32_t i = 0; i < 2; ++i) { + ASSERT_TRUE(decoder.GetBits(16, &x)); + ASSERT_EQ(x, 0x5476u); + ASSERT_EQ(16 + (i * 32), decoder.BitsDecoded()); + + ASSERT_TRUE(decoder.GetBits(16, &x)); + ASSERT_EQ(x, 0x1032u); + ASSERT_EQ(32 + (i * 32), decoder.BitsDecoded()); + } +} + +} // namespace draco diff --git a/core/data_buffer.cc b/core/data_buffer.cc index 2a3ed48..a37a597 100644 --- a/core/data_buffer.cc +++ b/core/data_buffer.cc @@ -34,7 +34,7 @@ void DataBuffer::Update(const void *data, int64_t size, int64_t offset) { // If no data is provided, just resize the buffer. data_.resize(size + offset); } else { - if (size + offset > data_.size()) + if (size + offset > static_cast(data_.size())) data_.resize(size + offset); const uint8_t *const byte_data = static_cast(data); std::copy(byte_data, byte_data + size, data_.data() + offset); diff --git a/core/decoder_buffer.h b/core/decoder_buffer.h index 4ebd2d8..e915291 100644 --- a/core/decoder_buffer.h +++ b/core/decoder_buffer.h @@ -49,7 +49,7 @@ class DecoderBuffer { // Decodes up to 32 bits into out_val. Can be called only in between // StartBitDecoding and EndBitDeoding. Otherwise returns false. - bool DecodeBits32(int nbits, uint32_t *out_value) { + bool DecodeLeastSignificantBits32(int nbits, uint32_t *out_value) { if (!bit_decoder_active()) return false; bit_decoder_.GetBits(nbits, out_value); @@ -68,7 +68,7 @@ class DecoderBuffer { } bool Decode(void *out_data, size_t size_to_decode) { - if (data_size_ < pos_ + size_to_decode) + if (data_size_ < static_cast(pos_ + size_to_decode)) return false; // Buffer overflow. memcpy(out_data, (data_ + pos_), size_to_decode); pos_ += size_to_decode; @@ -79,14 +79,14 @@ class DecoderBuffer { template bool Peek(T *out_val) { const size_t size_to_decode = sizeof(T); - if (data_size_ < pos_ + size_to_decode) + if (data_size_ < static_cast(pos_ + size_to_decode)) return false; // Buffer overflow. memcpy(out_val, (data_ + pos_), size_to_decode); return true; } bool Peek(void *out_data, size_t size_to_peek) { - if (data_size_ < pos_ + size_to_peek) + if (data_size_ < static_cast(pos_ + size_to_peek)) return false; // Buffer overflow. memcpy(out_data, (data_ + pos_), size_to_peek); return true; diff --git a/core/direct_bit_coding.h b/core/direct_bit_coding.h index 3a87f8a..20b5267 100644 --- a/core/direct_bit_coding.h +++ b/core/direct_bit_coding.h @@ -46,7 +46,7 @@ class DirectBitEncoder { // Encode |nibts| of |value|, starting from the least significant bit. // |nbits| must be > 0 and <= 32. - void EncodeBits32(int nbits, uint32_t value) { + void EncodeLeastSignificantBits32(int nbits, uint32_t value) { DCHECK_EQ(true, nbits <= 32); DCHECK_EQ(true, nbits > 0); @@ -107,10 +107,10 @@ class DirectBitDecoder { // Decode the next |nbits| and return the sequence in |value|. |nbits| must be // > 0 and <= 32. - void DecodeBits32(int nbits, uint32_t *value) { + void DecodeLeastSignificantBits32(int nbits, uint32_t *value) { DCHECK_EQ(true, nbits <= 32); DCHECK_EQ(true, nbits > 0); - const uint32_t remaining = 32 - num_used_bits_; + const int remaining = 32 - num_used_bits_; if (nbits <= remaining) { *value = (*pos_ << num_used_bits_) >> (32 - nbits); num_used_bits_ += nbits; diff --git a/core/draco_test_base.h b/core/draco_test_base.h new file mode 100644 index 0000000..f5c9d75 --- /dev/null +++ b/core/draco_test_base.h @@ -0,0 +1,11 @@ +// Wrapper for including googletest indirectly. Useful when the location of the +// googletest sources must change depending on build environment and repository +// source location. +#ifndef DRACO_CORE_DRACO_TEST_BASE_H_ +#define DRACO_CORE_DRACO_TEST_BASE_H_ + +static bool FLAGS_update_golden_files; +#include "gtest/gtest.h" +#include "testing/draco_test_config.h" + +#endif // DRACO_CORE_DRACO_TEST_BASE_H_ diff --git a/core/draco_test_utils.cc b/core/draco_test_utils.cc new file mode 100644 index 0000000..52b6503 --- /dev/null +++ b/core/draco_test_utils.cc @@ -0,0 +1,81 @@ +// Copyright 2016 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 "core/draco_test_utils.h" + +#include + +#include "core/macros.h" +#include "draco_test_base.h" + +namespace draco { + +namespace { +static constexpr char kTestDataDir[] = DRACO_TEST_DATA_DIR; +} // namespace + +std::string GetTestFileFullPath(const std::string &file_name) { + return std::string(kTestDataDir) + std::string("/") + file_name; +} + +bool GenerateGoldenFile(const std::string &golden_file_name, const void *data, + int data_size) { + // TODO(ostava): This will work only when the test is executed locally + // from blaze-bin/ folder. We should look for ways how to + // make it work when it's run using the "blaze test" command. + const std::string path = GetTestFileFullPath(golden_file_name); + std::ofstream file(path, std::ios::binary); + if (!file) + return false; + file.write(static_cast(data), data_size); + file.close(); + return true; +} + +bool CompareGoldenFile(const std::string &golden_file_name, const void *data, + int data_size) { + const std::string golden_path = GetTestFileFullPath(golden_file_name); + std::ifstream in_file(golden_path); + if (!in_file || data_size < 0) + return false; + const char *const data_c8 = static_cast(data); + constexpr int buffer_size = 1024; + char buffer[buffer_size]; + size_t extracted_size = 0; + size_t remaining_data_size = data_size; + int offset = 0; + while ((extracted_size = in_file.read(buffer, buffer_size).gcount()) > 0) { + if (remaining_data_size <= 0) + break; // Input and golden sizes are different. + size_t size_to_check = extracted_size; + if (remaining_data_size < size_to_check) + size_to_check = remaining_data_size; + for (uint32_t i = 0; i < size_to_check; ++i) { + if (buffer[i] != data_c8[offset++]) { + LOG(INFO) << "Test output differed from golden file at byte " + << offset - 1; + return false; + } + } + remaining_data_size -= extracted_size; + } + if (remaining_data_size != extracted_size) { + // Both of these values should be 0 at the end. + LOG(INFO) << "Test output size differed from golden file size"; + return false; + } + return true; +} + +} // namespace draco diff --git a/core/draco_test_utils.h b/core/draco_test_utils.h new file mode 100644 index 0000000..51f4c3f --- /dev/null +++ b/core/draco_test_utils.h @@ -0,0 +1,39 @@ +// Copyright 2016 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_CORE_DRACO_TEST_UTILS_H_ +#define DRACO_CORE_DRACO_TEST_UTILS_H_ + +#include "core/draco_test_base.h" + +namespace draco { + + +// Returns the full path to a given test file. +std::string GetTestFileFullPath(const std::string &file_name); + +// Generates a new golden file and saves it into the correct folder. +// Returns false if the file couldn't be created. +bool GenerateGoldenFile(const std::string &golden_file_name, const void *data, + int data_size); + +// Compare a golden file content with the input data. +// Function will log the first byte position where the data differ. +// Returns false if there are any differences. +bool CompareGoldenFile(const std::string &golden_file_name, const void *data, + int data_size); + +} // namespace draco + +#endif // DRACO_CORE_DRACO_TEST_UTILS_H_ diff --git a/core/draco_tests.cc b/core/draco_tests.cc new file mode 100644 index 0000000..7380e52 --- /dev/null +++ b/core/draco_tests.cc @@ -0,0 +1,6 @@ +#include "core/draco_test_base.h" + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/core/draco_version.h b/core/draco_version.h index 0b40aa1..48b9403 100644 --- a/core/draco_version.h +++ b/core/draco_version.h @@ -18,7 +18,7 @@ namespace draco { // Draco version is comprised of ... -static const char kDracoVersion[] = "0.9.0"; +static const char kDracoVersion[] = "0.9.1"; const char *Version() { return kDracoVersion; } diff --git a/core/encoder_buffer.cc b/core/encoder_buffer.cc index 778a03f..8675a2e 100644 --- a/core/encoder_buffer.cc +++ b/core/encoder_buffer.cc @@ -46,8 +46,8 @@ bool EncoderBuffer::StartBitEncoding(int64_t required_bits, bool encode_size) { buffer_.resize(buffer_start_size + required_bytes); // Get the buffer data pointer for the bit encoder. const char *const data = buffer_.data() + buffer_start_size; - bit_encoder_ = std::unique_ptr( - new BitEncoder(const_cast(data), required_bytes)); + bit_encoder_ = + std::unique_ptr(new BitEncoder(const_cast(data))); return true; } diff --git a/core/encoder_buffer.h b/core/encoder_buffer.h index 2428050..cda527d 100644 --- a/core/encoder_buffer.h +++ b/core/encoder_buffer.h @@ -45,14 +45,14 @@ class EncoderBuffer { // Encode up to 32 bits into the buffer. Can be called only in between // StartBitEncoding and EndBitEncoding. Otherwise returns false. - // TODO(hemmer): Swap arguments to make it consistent with DecoderBuffer. - bool EncodeBits32(uint32_t value, int nbits) { + bool EncodeLeastSignificantBits32(int nbits, uint32_t value) { if (!bit_encoder_active()) return false; bit_encoder_->PutBits(value, nbits); return true; } + public: // Encode an arbitrary data type. // Can be used only when we are not encoding a bit-sequence. // Returns false when the value couldn't be encoded. diff --git a/core/folded_bit32_coding.h b/core/folded_bit32_coding.h index 7f9ea09..e0b3851 100644 --- a/core/folded_bit32_coding.h +++ b/core/folded_bit32_coding.h @@ -48,7 +48,7 @@ class FoldedBit32Encoder { // Encode |nbits| of |value|, starting from the least significant bit. // |nbits| must be > 0 and <= 32. - void EncodeBits32(int nbits, uint32_t value) { + void EncodeLeastSignificantBits32(int nbits, uint32_t value) { uint32_t selector = 1 << (nbits - 1); for (int i = 0; i < nbits; i++) { const bool bit = (value & selector); @@ -96,7 +96,7 @@ class FoldedBit32Decoder { // Decode the next |nbits| and return the sequence in |value|. |nbits| must be // > 0 and <= 32. - void DecodeBits32(int nbits, uint32_t *value) { + void DecodeLeastSignificantBits32(int nbits, uint32_t *value) { uint32_t result = 0; for (int i = 0; i < nbits; ++i) { const bool bit = folded_number_decoders_[i].DecodeNextBit(); diff --git a/core/hash_utils.cc b/core/hash_utils.cc index a2b4728..6f2b691 100644 --- a/core/hash_utils.cc +++ b/core/hash_utils.cc @@ -32,14 +32,14 @@ uint64_t FingerprintString(const char *s, size_t len) { uint64_t new_hash = seed; if (num_chars_left > 7) { - const int off = i * 8; - new_hash = static_cast(s[off]) << 56 | - static_cast(s[off + 1]) << 48 | - static_cast(s[off + 2]) << 40 | - static_cast(s[off + 3]) << 32 | - static_cast(s[off + 4]) << 24 | - static_cast(s[off + 5]) << 16 | - static_cast(s[off + 6]) << 8 | s[off + 7]; + const int off2 = i * 8; + new_hash = static_cast(s[off2]) << 56 | + static_cast(s[off2 + 1]) << 48 | + static_cast(s[off2 + 2]) << 40 | + static_cast(s[off2 + 3]) << 32 | + static_cast(s[off2 + 4]) << 24 | + static_cast(s[off2 + 5]) << 16 | + static_cast(s[off2 + 6]) << 8 | s[off2 + 7]; } else { for (int j = 0; j < num_chars_left; ++j) { new_hash |= static_cast(s[off + j]) diff --git a/core/hash_utils.h b/core/hash_utils.h index 45503f9..3d7ad50 100644 --- a/core/hash_utils.h +++ b/core/hash_utils.h @@ -47,7 +47,7 @@ template struct HashArray { size_t operator()(const T &a) const { size_t hash = 79; // Magic number. - for (int i = 0; i < std::tuple_size::value; ++i) { + for (unsigned int i = 0; i < std::tuple_size::value; ++i) { hash = HashCombine(hash, ValueHash(a[i])); } return hash; diff --git a/core/macros.h b/core/macros.h index f1a3da9..c7c1de5 100644 --- a/core/macros.h +++ b/core/macros.h @@ -52,6 +52,10 @@ namespace draco { #define FALLTHROUGH_INTENDED void(0); #endif +#ifndef LOG +#define LOG(...) std::cout +#endif + #ifndef VLOG #define VLOG(...) std::cout #endif diff --git a/core/math_utils_test.cc b/core/math_utils_test.cc new file mode 100644 index 0000000..1741be7 --- /dev/null +++ b/core/math_utils_test.cc @@ -0,0 +1,4 @@ +#include "core/math_utils.h" +#include "core/draco_test_base.h" + +TEST(MathUtils, Mod) { EXPECT_EQ(DRACO_INCREMENT_MOD(1, 1 << 1), 0); } diff --git a/core/quantization_utils_test.cc b/core/quantization_utils_test.cc new file mode 100644 index 0000000..0d04182 --- /dev/null +++ b/core/quantization_utils_test.cc @@ -0,0 +1,51 @@ +// Copyright 2016 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 "core/quantization_utils.h" + +#include "core/draco_test_base.h" + +namespace draco { + +class QuantizationUtilsTest : public ::testing::Test {}; + +TEST_F(QuantizationUtilsTest, TestQuantizer) { + Quantizer quantizer; + quantizer.Init(10.f, 255); + EXPECT_EQ(quantizer.QuantizeFloat(0.f), 0); + EXPECT_EQ(quantizer.QuantizeFloat(10.f), 255); + EXPECT_EQ(quantizer.QuantizeFloat(-10.f), -255); + EXPECT_EQ(quantizer.QuantizeFloat(4.999f), 127); + EXPECT_EQ(quantizer.QuantizeFloat(5.f), 128); + EXPECT_EQ(quantizer.QuantizeFloat(-4.9999f), -127); + EXPECT_EQ(quantizer.QuantizeFloat(-5.f), -128); + EXPECT_EQ(quantizer.QuantizeFloat(-5.0001f), -128); + + // Out of range quantization. + // The behavior is technically undefined, but both quantizer and dequantizer + // should still work correctly unless the quantized values overflow. + EXPECT_LT(quantizer.QuantizeFloat(-15.f), -255); + EXPECT_GT(quantizer.QuantizeFloat(15.f), 255); +} + +TEST_F(QuantizationUtilsTest, TestDequantizer) { + Dequantizer dequantizer; + dequantizer.Init(10.f, 255); + EXPECT_EQ(dequantizer.DequantizeFloat(0), 0.f); + EXPECT_EQ(dequantizer.DequantizeFloat(255), 10.f); + EXPECT_EQ(dequantizer.DequantizeFloat(-255), -10.f); + EXPECT_EQ(dequantizer.DequantizeFloat(128), 10.f * (128.f / 255.f)); +} + +} // namespace draco diff --git a/core/rans_coding.cc b/core/rans_coding.cc index b30ebb4..48e43f8 100644 --- a/core/rans_coding.cc +++ b/core/rans_coding.cc @@ -40,7 +40,7 @@ void RAnsBitEncoder::EncodeBit(bool bit) { } } -void RAnsBitEncoder::EncodeBits32(int nbits, uint32_t value) { +void RAnsBitEncoder::EncodeLeastSignificantBits32(int nbits, uint32_t value) { DCHECK_EQ(true, nbits <= 32); DCHECK_EQ(true, nbits > 0); @@ -87,7 +87,6 @@ void RAnsBitEncoder::EndEncoding(EncoderBuffer *target_buffer) { zero_prob += (zero_prob == 0); // Space for 32 bit integer and some extra space. - // TODO(hemmer): Find out if this is really safe. std::vector buffer((bits_.size() + 8) * 8); AnsCoder ans_coder; ans_write_init(&ans_coder, buffer.data()); @@ -142,7 +141,7 @@ bool RAnsBitDecoder::DecodeNextBit() { return bit > 0; } -void RAnsBitDecoder::DecodeBits32(int nbits, uint32_t *value) { +void RAnsBitDecoder::DecodeLeastSignificantBits32(int nbits, uint32_t *value) { DCHECK_EQ(true, nbits <= 32); DCHECK_EQ(true, nbits > 0); diff --git a/core/rans_coding.h b/core/rans_coding.h index d220396..3a962d1 100644 --- a/core/rans_coding.h +++ b/core/rans_coding.h @@ -40,7 +40,7 @@ class RAnsBitEncoder { // Encode |nibts| of |value|, starting from the least significant bit. // |nbits| must be > 0 and <= 32. - void EncodeBits32(int nbits, uint32_t value); + void EncodeLeastSignificantBits32(int nbits, uint32_t value); // Ends the bit encoding and stores the result into the target_buffer. void EndEncoding(EncoderBuffer *target_buffer); @@ -68,7 +68,7 @@ class RAnsBitDecoder { // Decode the next |nbits| and return the sequence in |value|. |nbits| must be // > 0 and <= 32. - void DecodeBits32(int nbits, uint32_t *value); + void DecodeLeastSignificantBits32(int nbits, uint32_t *value); void EndDecoding() {} diff --git a/core/rans_coding_test.cc b/core/rans_coding_test.cc new file mode 100644 index 0000000..12b2a42 --- /dev/null +++ b/core/rans_coding_test.cc @@ -0,0 +1,7 @@ +#include "core/rans_coding.h" +#include "core/adaptive_rans_coding.h" +#include "core/draco_test_base.h" + +// Just including rans_coding.h and adaptive_rans_coding.h gets an asan error +// when compiling (blaze test :rans_coding_test --config=asan) +TEST(RansCodingTest, LinkerTest) {} diff --git a/core/rans_symbol_decoder.h b/core/rans_symbol_decoder.h index 621f321..a067c86 100644 --- a/core/rans_symbol_decoder.h +++ b/core/rans_symbol_decoder.h @@ -56,7 +56,7 @@ bool RAnsSymbolDecoder::Create(DecoderBuffer *buffer) { return false; probability_table_.resize(num_symbols_); // Decode the table. - for (int i = 0; i < num_symbols_; ++i) { + for (uint32_t i = 0; i < num_symbols_; ++i) { uint32_t prob = 0; uint8_t byte_prob = 0; // Decode the first byte and extract the number of extra bytes we need to diff --git a/core/rans_symbol_encoder.h b/core/rans_symbol_encoder.h index c7e8b1b..a319f95 100644 --- a/core/rans_symbol_encoder.h +++ b/core/rans_symbol_encoder.h @@ -145,13 +145,13 @@ bool RAnsSymbolEncoder::Create( return false; // Most frequent symbol would be empty. break; } - const int32_t new_prob = + const int32_t new_prob = static_cast( floor(act_rel_error_d * - static_cast(probability_table_[symbol_id].prob)); + static_cast(probability_table_[symbol_id].prob))); int32_t fix = probability_table_[symbol_id].prob - new_prob; - if (fix == 0) + if (fix == 0u) fix = 1; - if (fix >= probability_table_[symbol_id].prob) + if (fix >= static_cast(probability_table_[symbol_id].prob)) fix = probability_table_[symbol_id].prob - 1; if (fix > error) fix = error; @@ -198,7 +198,7 @@ void RAnsSymbolEncoder::EncodeTable( buffer->Encode(num_symbols_); // Use varint encoding for the probabilities (first two bits represent the // number of bytes used - 1). - for (int i = 0; i < num_symbols_; ++i) { + for (uint32_t i = 0; i < num_symbols_; ++i) { const uint32_t prob = probability_table_[i].prob; int num_extra_bytes = 0; if (prob >= (1 << 6)) { diff --git a/core/symbol_coding_test.cc b/core/symbol_coding_test.cc new file mode 100644 index 0000000..4cdbe73 --- /dev/null +++ b/core/symbol_coding_test.cc @@ -0,0 +1,119 @@ +// Copyright 2016 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 "core/decoder_buffer.h" +#include "core/draco_test_base.h" +#include "core/encoder_buffer.h" +#include "core/symbol_decoding.h" +#include "core/symbol_encoding.h" + +namespace draco { + +class SymbolCodingTest : public ::testing::Test { + protected: + SymbolCodingTest() {} +}; + +TEST_F(SymbolCodingTest, TestLargeNumbers) { + // This test verifies that SymbolCoding successfully encodes an array of large + // numbers. + const uint32_t in[] = {12345678, 1223333, 111, 5}; + const int num_values = sizeof(in) / sizeof(uint32_t); + EncoderBuffer eb; + ASSERT_TRUE(EncodeSymbols(in, num_values, 1, &eb)); + + std::vector out; + out.resize(num_values); + DecoderBuffer db; + db.Init(eb.data(), eb.size()); + ASSERT_TRUE(DecodeSymbols(num_values, 1, &db, &out[0])); + for (int i = 0; i < num_values; ++i) { + EXPECT_EQ(in[i], out[i]); + } +} + +TEST_F(SymbolCodingTest, TestManyNumbers) { + // This test verifies that SymbolCoding successfully encodes an array of + // several numbers that repeat many times. + + // Value/frequency pairs. + const std::pair in[] = { + {12, 1500}, {1025, 31000}, {7, 1}, {9, 5}, {0, 6432}}; + + const int num_pairs = sizeof(in) / sizeof(std::pair); + + std::vector in_values; + for (int i = 0; i < num_pairs; ++i) { + in_values.insert(in_values.end(), in[i].second, in[i].first); + } + EncoderBuffer eb; + ASSERT_TRUE(EncodeSymbols(in_values.data(), in_values.size(), 1, &eb)); + std::vector out_values; + out_values.resize(in_values.size()); + DecoderBuffer db; + db.Init(eb.data(), eb.size()); + ASSERT_TRUE(DecodeSymbols(in_values.size(), 1, &db, &out_values[0])); + for (uint32_t i = 0; i < in_values.size(); ++i) { + ASSERT_EQ(in_values[i], out_values[i]); + } +} + +TEST_F(SymbolCodingTest, TestEmpty) { + // This test verifies that SymbolCoding successfully encodes an empty array. + EncoderBuffer eb; + ASSERT_TRUE(EncodeSymbols(nullptr, 0, 1, &eb)); + DecoderBuffer db; + db.Init(eb.data(), eb.size()); + ASSERT_TRUE(DecodeSymbols(0, 1, &db, nullptr)); +} + +TEST_F(SymbolCodingTest, TestOneSymbol) { + // This test verifies that SymbolCoding successfully encodes an a single + // symbol. + EncoderBuffer eb; + const std::vector in(1200, 0); + ASSERT_TRUE(EncodeSymbols(in.data(), in.size(), 1, &eb)); + + std::vector out(in.size()); + DecoderBuffer db; + db.Init(eb.data(), eb.size()); + ASSERT_TRUE(DecodeSymbols(in.size(), 1, &db, &out[0])); + for (uint32_t i = 0; i < in.size(); ++i) { + ASSERT_EQ(in[i], out[i]); + } +} + +TEST_F(SymbolCodingTest, TestBitLengthsl) { + // This test verifies that SymbolCoding successfully encodes symbols of + // various bitlengths + EncoderBuffer eb; + std::vector in; + constexpr int bit_lengths = 18; + for (int i = 0; i < bit_lengths; ++i) { + in.push_back(1 << i); + } + std::vector out(in.size()); + for (int i = 0; i < bit_lengths; ++i) { + eb.Clear(); + ASSERT_TRUE(EncodeSymbols(in.data(), i + 1, 1, &eb)); + DecoderBuffer db; + db.Init(eb.data(), eb.size()); + ASSERT_TRUE(DecodeSymbols(i + 1, 1, &db, &out[0])); + for (int j = 0; j < i + 1; ++j) { + ASSERT_EQ(in[j], out[j]); + } + } +} + +} // namespace draco diff --git a/core/symbol_decoding.cc b/core/symbol_decoding.cc index f35ef83..ba1f222 100644 --- a/core/symbol_decoding.cc +++ b/core/symbol_decoding.cc @@ -83,7 +83,7 @@ bool DecodeTaggedSymbols(int num_values, int num_components, // Decode the actual value. for (int j = 0; j < num_components; ++j) { uint32_t val; - if (!src_buffer->DecodeBits32(bit_length, &val)) + if (!src_buffer->DecodeLeastSignificantBits32(bit_length, &val)) return false; out_values[value_id++] = val; } diff --git a/core/symbol_encoding.cc b/core/symbol_encoding.cc index 0a4b42b..688d877 100644 --- a/core/symbol_encoding.cc +++ b/core/symbol_encoding.cc @@ -98,7 +98,7 @@ bool EncodeSymbols(const uint32_t *symbols, int num_values, int num_components, // Compute the total bit length used by all values. This will be used for // computing a heuristic that chooses the optimal entropy encoding scheme. uint64_t total_bit_length = 0; - for (int64_t i = 0; i < bit_lengths.size(); ++i) { + for (size_t i = 0; i < bit_lengths.size(); ++i) { total_bit_length += bit_lengths[i]; } @@ -106,13 +106,13 @@ bool EncodeSymbols(const uint32_t *symbols, int num_values, int num_components, // The average number of bits necessary for encoding a single entry value. const int64_t average_bit_length = - ceil(static_cast(total_bit_length) / - static_cast(num_component_values)); + static_cast(ceil(static_cast(total_bit_length) / + static_cast(num_component_values))); // The estimated average number of bits necessary for encoding a single // bit-length tag. - int64_t average_bits_per_tag = + int64_t average_bits_per_tag = static_cast( ceil(static_cast(bits::MostSignificantBit(average_bit_length)) / - static_cast(num_components)); + static_cast(num_components))); if (average_bits_per_tag <= 0) average_bits_per_tag = 1; @@ -162,7 +162,7 @@ bool EncodeTaggedSymbols(const uint32_t *symbols, int num_values, // Compute the frequencies from input data. // Maximum integer value for the values across all components. - for (int i = 0; i < bit_lengths.size(); ++i) { + for (size_t i = 0; i < bit_lengths.size(); ++i) { // Update the frequency of the associated entry id. ++frequencies[bit_lengths[i]]; } @@ -193,7 +193,8 @@ bool EncodeTaggedSymbols(const uint32_t *symbols, int num_values, const int j = num_values - num_components - i; const int value_bit_length = bit_lengths[j / num_components]; for (int c = 0; c < num_components; ++c) { - value_buffer.EncodeBits32(symbols[j + c], value_bit_length); + value_buffer.EncodeLeastSignificantBits32(value_bit_length, + symbols[j + c]); } } } else { @@ -203,7 +204,7 @@ bool EncodeTaggedSymbols(const uint32_t *symbols, int num_values, tag_encoder.EncodeSymbol(bit_length); // Now encode all values using the stored bit_length. for (int j = 0; j < num_components; ++j) { - value_buffer.EncodeBits32(symbols[i + j], bit_length); + value_buffer.EncodeLeastSignificantBits32(bit_length, symbols[i + j]); } } } diff --git a/core/vector_d_test.cc b/core/vector_d_test.cc new file mode 100644 index 0000000..2079a7d --- /dev/null +++ b/core/vector_d_test.cc @@ -0,0 +1,72 @@ +// Copyright 2016 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 "core/vector_d.h" + +#include "core/draco_test_base.h" + +namespace { + +using draco::Vector3f; + +class VectorDTest : public ::testing::Test { + protected: +}; + +TEST_F(VectorDTest, TestOperators) { + { + const Vector3f v; + ASSERT_EQ(v[0], 0); + ASSERT_EQ(v[1], 0); + ASSERT_EQ(v[2], 0); + } + const Vector3f v(1, 2, 3); + ASSERT_EQ(v[0], 1); + ASSERT_EQ(v[1], 2); + ASSERT_EQ(v[2], 3); + + Vector3f w = v; + bool comp = (v == w); + ASSERT_TRUE(comp); + comp = (v != w); + ASSERT_TRUE(!comp); + ASSERT_EQ(w[0], 1); + ASSERT_EQ(w[1], 2); + ASSERT_EQ(w[2], 3); + + w = -v; + ASSERT_EQ(w[0], -1); + ASSERT_EQ(w[1], -2); + ASSERT_EQ(w[2], -3); + + w = v + v; + ASSERT_EQ(w[0], 2); + ASSERT_EQ(w[1], 4); + ASSERT_EQ(w[2], 6); + + w = w - v; + ASSERT_EQ(w[0], 1); + ASSERT_EQ(w[1], 2); + ASSERT_EQ(w[2], 3); + + w = v * float(2); + ASSERT_EQ(w[0], 2); + ASSERT_EQ(w[1], 4); + ASSERT_EQ(w[2], 6); + + ASSERT_EQ(v.SquaredNorm(), 14); + ASSERT_EQ(v.Dot(v), 14); +} + +} // namespace diff --git a/io/obj_decoder.cc b/io/obj_decoder.cc index 55b6897..67c2e2f 100644 --- a/io/obj_decoder.cc +++ b/io/obj_decoder.cc @@ -360,7 +360,7 @@ bool ObjDecoder::ParseMaterialLib(bool *error) { return true; } -bool ObjDecoder::ParseMaterial(bool *error) { +bool ObjDecoder::ParseMaterial(bool * /* error */) { if (counting_mode_) return false; // Skip when we are counting definitions. if (material_att_id_ < 0) @@ -458,7 +458,7 @@ bool ObjDecoder::ParseMaterialFile(const std::string &file_name, bool *error) { return true; } -bool ObjDecoder::ParseMaterialFileDefinition(bool *error) { +bool ObjDecoder::ParseMaterialFileDefinition(bool * /* error */) { char c; parser::SkipWhitespace(buffer()); if (!buffer()->Peek(&c)) { diff --git a/io/obj_decoder_test.cc b/io/obj_decoder_test.cc new file mode 100644 index 0000000..45f0d89 --- /dev/null +++ b/io/obj_decoder_test.cc @@ -0,0 +1,52 @@ +// Copyright 2016 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 + +#include "core/draco_test_base.h" +#include "core/draco_test_utils.h" +#include "io/obj_decoder.h" + +namespace draco { + +class ObjDecoderTest : public ::testing::Test { + protected: + template + std::unique_ptr DecodeObj(const std::string &file_name) const { + const std::string path = GetTestFileFullPath(file_name); + ObjDecoder decoder; + std::unique_ptr geometry(new Geometry()); + if (!decoder.DecodeFromFile(path, geometry.get())) + return nullptr; + return geometry; + } + + void test_decoding(const std::string &file_name) { + const std::unique_ptr mesh(DecodeObj(file_name)); + ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name; + ASSERT_GT(mesh->num_faces(), 0); + + const std::unique_ptr pc(DecodeObj(file_name)); + ASSERT_NE(pc, nullptr) << "Failed to load test model " << file_name; + ASSERT_GT(pc->num_points(), 0u); + } +}; + +TEST_F(ObjDecoderTest, ExtraVertexOBJ) { + const std::string file_name = "extra_vertex.obj"; + test_decoding(file_name); +} + + +} // namespace draco diff --git a/io/parser_utils.cc b/io/parser_utils.cc index cb26d79..063703c 100644 --- a/io/parser_utils.cc +++ b/io/parser_utils.cc @@ -17,6 +17,7 @@ #include #include #include +#include namespace draco { namespace parser { diff --git a/io/ply_decoder_test.cc b/io/ply_decoder_test.cc new file mode 100644 index 0000000..cbd95ca --- /dev/null +++ b/io/ply_decoder_test.cc @@ -0,0 +1,51 @@ +// Copyright 2016 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 "io/ply_decoder.h" + +#include "core/draco_test_base.h" +#include "core/draco_test_utils.h" + +namespace draco { + +class PlyDecoderTest : public ::testing::Test { + protected: + template + std::unique_ptr DecodePly(const std::string &file_name) const { + const std::string path = GetTestFileFullPath(file_name); + PlyDecoder decoder; + std::unique_ptr geometry(new Geometry()); + if (!decoder.DecodeFromFile(path, geometry.get())) + return nullptr; + return geometry; + } + + void test_decoding_method(const std::string &file_name, int num_faces, + uint32_t num_points) { + const std::unique_ptr mesh(DecodePly(file_name)); + ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name; + ASSERT_EQ(mesh->num_faces(), num_faces); + + const std::unique_ptr pc(DecodePly(file_name)); + ASSERT_NE(pc, nullptr) << "Failed to load test model " << file_name; + ASSERT_EQ(pc->num_points(), num_points); + } +}; + +TEST_F(PlyDecoderTest, TestPlyDecoding) { + const std::string file_name = "test_pos_color.ply"; + test_decoding_method(file_name, 224, 114); +} + +} // namespace draco diff --git a/io/ply_property_reader.h b/io/ply_property_reader.h index b67a453..8f718ea 100644 --- a/io/ply_property_reader.h +++ b/io/ply_property_reader.h @@ -15,6 +15,8 @@ #ifndef DRACO_IO_PLY_PROPERTY_READER_H_ #define DRACO_IO_PLY_PROPERTY_READER_H_ +#include + #include "io/ply_reader.h" namespace draco { diff --git a/io/ply_reader.cc b/io/ply_reader.cc index a2e0375..14f1974 100644 --- a/io/ply_reader.cc +++ b/io/ply_reader.cc @@ -172,7 +172,7 @@ bool PlyReader::ParseProperty(DecoderBuffer *buffer) { } bool PlyReader::ParsePropertiesData(DecoderBuffer *buffer) { - for (int i = 0; i < elements_.size(); ++i) { + for (int i = 0; i < static_cast(elements_.size()); ++i) { if (format_ == kLittleEndian) { if (!ParseElementData(buffer, i)) { return false; diff --git a/io/ply_reader_test.cc b/io/ply_reader_test.cc new file mode 100644 index 0000000..c6649b5 --- /dev/null +++ b/io/ply_reader_test.cc @@ -0,0 +1,142 @@ +// Copyright 2016 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 "io/ply_reader.h" + +#include + +#include "core/draco_test_base.h" +#include "core/draco_test_utils.h" +#include "io/ply_property_reader.h" + +namespace draco { + +class PlyReaderTest : public ::testing::Test { + protected: + std::vector ReadPlyFile(const std::string &file_name) const { + const std::string path = GetTestFileFullPath(file_name); + std::ifstream file(path.c_str(), std::ios::binary); + if (!file) + return std::vector(); + auto is_size = file.tellg(); + file.seekg(0, std::ios::end); + is_size = file.tellg() - is_size; + file.seekg(0, std::ios::beg); + std::vector data(is_size); + file.read(&data[0], is_size); + return data; + } +}; + +TEST_F(PlyReaderTest, TestReader) { + const std::string file_name = "test_pos_color.ply"; + const std::vector data = ReadPlyFile(file_name); + DecoderBuffer buf; + buf.Init(data.data(), data.size()); + PlyReader reader; + ASSERT_TRUE(reader.Read(&buf)); + ASSERT_EQ(reader.num_elements(), 2); + ASSERT_EQ(reader.element(0).num_properties(), 7); + ASSERT_EQ(reader.element(1).num_properties(), 1); + ASSERT_TRUE(reader.element(1).property(0).is_list()); + + ASSERT_TRUE(reader.element(0).GetPropertyByName("red") != nullptr); + const PlyProperty *const prop = reader.element(0).GetPropertyByName("red"); + PlyPropertyReader reader_uint8(prop); + PlyPropertyReader reader_uint32(prop); + PlyPropertyReader reader_float(prop); + for (int i = 0; i < reader.element(0).num_entries(); ++i) { + ASSERT_EQ(reader_uint8.ReadValue(i), reader_uint32.ReadValue(i)); + ASSERT_EQ(reader_uint8.ReadValue(i), reader_float.ReadValue(i)); + } +} + +TEST_F(PlyReaderTest, TestReaderAscii) { + const std::string file_name = "test_pos_color.ply"; + const std::vector data = ReadPlyFile(file_name); + DecoderBuffer buf; + buf.Init(data.data(), data.size()); + PlyReader reader; + ASSERT_TRUE(reader.Read(&buf)); + + const std::string file_name_ascii = "test_pos_color_ascii.ply"; + const std::vector data_ascii = ReadPlyFile(file_name_ascii); + buf.Init(data_ascii.data(), data_ascii.size()); + PlyReader reader_ascii; + ASSERT_TRUE(reader_ascii.Read(&buf)); + ASSERT_EQ(reader.num_elements(), reader_ascii.num_elements()); + ASSERT_EQ(reader.element(0).num_properties(), + reader_ascii.element(0).num_properties()); + + ASSERT_TRUE(reader.element(0).GetPropertyByName("x") != nullptr); + const PlyProperty *const prop = reader.element(0).GetPropertyByName("x"); + const PlyProperty *const prop_ascii = + reader_ascii.element(0).GetPropertyByName("x"); + PlyPropertyReader reader_float(prop); + PlyPropertyReader reader_float_ascii(prop_ascii); + for (int i = 0; i < reader.element(0).num_entries(); ++i) { + ASSERT_NEAR(reader_float.ReadValue(i), reader_float_ascii.ReadValue(i), + 1e-4f); + } +} + +TEST_F(PlyReaderTest, TestReaderExtraWhitespace) { + const std::string file_name = "test_extra_whitespace.ply"; + const std::vector data = ReadPlyFile(file_name); + DecoderBuffer buf; + buf.Init(data.data(), data.size()); + PlyReader reader; + ASSERT_TRUE(reader.Read(&buf)); + + ASSERT_EQ(reader.num_elements(), 2); + ASSERT_EQ(reader.element(0).num_properties(), 7); + ASSERT_EQ(reader.element(1).num_properties(), 1); + ASSERT_TRUE(reader.element(1).property(0).is_list()); + + ASSERT_TRUE(reader.element(0).GetPropertyByName("red") != nullptr); + const PlyProperty *const prop = reader.element(0).GetPropertyByName("red"); + PlyPropertyReader reader_uint8(prop); + PlyPropertyReader reader_uint32(prop); + PlyPropertyReader reader_float(prop); + for (int i = 0; i < reader.element(0).num_entries(); ++i) { + ASSERT_EQ(reader_uint8.ReadValue(i), reader_uint32.ReadValue(i)); + ASSERT_EQ(reader_uint8.ReadValue(i), reader_float.ReadValue(i)); + } +} + +TEST_F(PlyReaderTest, TestReaderMoreDataTypes) { + const std::string file_name = "test_more_datatypes.ply"; + const std::vector data = ReadPlyFile(file_name); + DecoderBuffer buf; + buf.Init(data.data(), data.size()); + PlyReader reader; + ASSERT_TRUE(reader.Read(&buf)); + + ASSERT_EQ(reader.num_elements(), 2); + ASSERT_EQ(reader.element(0).num_properties(), 7); + ASSERT_EQ(reader.element(1).num_properties(), 1); + ASSERT_TRUE(reader.element(1).property(0).is_list()); + + ASSERT_TRUE(reader.element(0).GetPropertyByName("red") != nullptr); + const PlyProperty *const prop = reader.element(0).GetPropertyByName("red"); + PlyPropertyReader reader_uint8(prop); + PlyPropertyReader reader_uint32(prop); + PlyPropertyReader reader_float(prop); + for (int i = 0; i < reader.element(0).num_entries(); ++i) { + ASSERT_EQ(reader_uint8.ReadValue(i), reader_uint32.ReadValue(i)); + ASSERT_EQ(reader_uint8.ReadValue(i), reader_float.ReadValue(i)); + } +} + +} // namespace draco diff --git a/io/point_cloud_io_test.cc b/io/point_cloud_io_test.cc new file mode 100644 index 0000000..ad36097 --- /dev/null +++ b/io/point_cloud_io_test.cc @@ -0,0 +1,64 @@ +// Copyright 2016 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 "io/point_cloud_io.h" + +#include + +#include "core/draco_test_base.h" +#include "core/draco_test_utils.h" +#include "io/obj_decoder.h" + +namespace draco { + +class IoPointCloudIoTest : public ::testing::Test { + protected: + std::unique_ptr DecodeObj(const std::string &file_name) const { + const std::string path = GetTestFileFullPath(file_name); + std::unique_ptr pc(new PointCloud()); + ObjDecoder decoder; + if (!decoder.DecodeFromFile(path, pc.get())) + return nullptr; + return pc; + } + + void test_compression_method(PointCloudEncodingMethod method, + const std::string &file_name) { + const std::unique_ptr pc(DecodeObj(file_name)); + ASSERT_NE(pc, nullptr) << "Failed to load test model " << file_name; + + std::stringstream ss; + WritePointCloudIntoStream(pc.get(), ss, method); + ASSERT_TRUE(ss.good()); + + std::unique_ptr decoded_pc; + ReadPointCloudFromStream(&decoded_pc, ss); + ASSERT_TRUE(ss.good()); + } +}; + +TEST_F(IoPointCloudIoTest, EncodeWithBinary) { + test_compression_method(POINT_CLOUD_SEQUENTIAL_ENCODING, "test_nm.obj"); + test_compression_method(POINT_CLOUD_SEQUENTIAL_ENCODING, "sphere.obj"); +} + +TEST_F(IoPointCloudIoTest, ObjFileInput) { + // Tests whether loading obj point clouds from files works as expected. + const std::unique_ptr pc = + ReadPointCloudFromFile(GetTestFileFullPath("test_nm.obj")); + ASSERT_NE(pc, nullptr) << "Failed to load the obj point cloud."; + EXPECT_EQ(pc->num_points(), 97u) << "Obj point cloud not loaded properly."; +} + +} // namespace draco diff --git a/mesh/corner_table.cc b/mesh/corner_table.cc index 459fea0..97e5af3 100644 --- a/mesh/corner_table.cc +++ b/mesh/corner_table.cc @@ -67,7 +67,7 @@ bool CornerTable::ComputeOppositeCorners(int *num_vertices) { num_corners_on_vertices.reserve(num_corners()); for (CornerIndex c(0); c < num_corners(); ++c) { const VertexIndex v1 = Vertex(c); - if (v1.value() >= num_corners_on_vertices.size()) + if (v1.value() >= static_cast(num_corners_on_vertices.size())) num_corners_on_vertices.resize(v1.value() + 1, 0); // For each corner there is always exactly one outgoing half-edge attached // to its vertex. @@ -93,7 +93,7 @@ bool CornerTable::ComputeOppositeCorners(int *num_vertices) { // vertices. std::vector vertex_offset(num_corners_on_vertices.size()); int offset = 0; - for (int i = 0; i < num_corners_on_vertices.size(); ++i) { + for (size_t i = 0; i < num_corners_on_vertices.size(); ++i) { vertex_offset[i] = offset; offset += num_corners_on_vertices[i]; } @@ -120,7 +120,7 @@ bool CornerTable::ComputeOppositeCorners(int *num_vertices) { // The maximum number of half-edges attached to the sink vertex. const int num_corners_on_vert = num_corners_on_vertices[sink_v.value()]; // Where to look for the first half-edge on the sink vertex. - int offset = vertex_offset[sink_v.value()]; + offset = vertex_offset[sink_v.value()]; for (int i = 0; i < num_corners_on_vert; ++i, ++offset) { const VertexIndex other_v = vertex_edges[offset].sink_vert; if (other_v < 0) @@ -148,7 +148,7 @@ bool CornerTable::ComputeOppositeCorners(int *num_vertices) { // No opposite corner found. Insert the new edge const int num_corners_on_source_vert = num_corners_on_vertices[source_v.value()]; - int offset = vertex_offset[source_v.value()]; + offset = vertex_offset[source_v.value()]; for (int i = 0; i < num_corners_on_source_vert; ++i, ++offset) { // Find the first unused half-edge slot on the source vertex. if (vertex_edges[offset].sink_vert < 0) { diff --git a/mesh/corner_table.h b/mesh/corner_table.h index 4350286..3fcfd92 100644 --- a/mesh/corner_table.h +++ b/mesh/corner_table.h @@ -211,7 +211,7 @@ class CornerTable { const FaceIndex face = Face(corner_id); faces_[face][LocalIndex(corner_id)] = vert_id; if (vert_id >= 0) { - if (vertex_corners_.size() <= vert_id.value()) + if (vertex_corners_.size() <= static_cast(vert_id.value())) vertex_corners_.resize(vert_id.value() + 1); vertex_corners_[vert_id] = corner_id; } diff --git a/mesh/mesh.h b/mesh/mesh.h index 25befed..166966e 100644 --- a/mesh/mesh.h +++ b/mesh/mesh.h @@ -61,13 +61,13 @@ class Mesh : public PointCloud { FaceIndex::ValueType num_faces() const { return faces_.size(); } const Face &face(FaceIndex face_id) const { DCHECK_LE(0, face_id.value()); - DCHECK_LT(face_id.value(), faces_.size()); + DCHECK_LT(face_id.value(), static_cast(faces_.size())); return faces_[face_id]; } void SetAttribute(int att_id, std::unique_ptr pa) override { PointCloud::SetAttribute(att_id, std::move(pa)); - if (attribute_data_.size() <= att_id) { + if (static_cast(attribute_data_.size()) <= att_id) { attribute_data_.resize(att_id + 1); } } diff --git a/mesh/mesh_cleanup.cc b/mesh/mesh_cleanup.cc index f442122..3091875 100644 --- a/mesh/mesh_cleanup.cc +++ b/mesh/mesh_cleanup.cc @@ -67,7 +67,7 @@ bool MeshCleanup::operator()(Mesh *mesh, const MeshCleanupOptions &options) { const PointIndex::ValueType num_original_points = mesh->num_points(); // Map from old points to the new ones. IndexTypeVector point_map(num_original_points); - if (num_new_points < mesh->num_points()) { + if (num_new_points < static_cast(mesh->num_points())) { // Some of the points were removed. We need to remap the old points to the // new ones. num_new_points = 0; @@ -118,7 +118,7 @@ bool MeshCleanup::operator()(Mesh *mesh, const MeshCleanupOptions &options) { bool att_indices_changed = false; // If there are some unused attribute entries, remap the attribute values // in the attribute buffer. - if (num_used_entries < att->size()) { + if (num_used_entries < static_cast(att->size())) { att_index_map.resize(att->size()); num_used_entries = 0; for (AttributeValueIndex i(0); i < att->size(); ++i) { @@ -143,7 +143,7 @@ bool MeshCleanup::operator()(Mesh *mesh, const MeshCleanupOptions &options) { if (att->is_mapping_identity()) { // The mapping was identity. It'll remain identity only if the // number of point and attribute indices is still the same. - if (num_used_entries != mesh->num_points()) { + if (num_used_entries != static_cast(mesh->num_points())) { // We need to create an explicit mapping. // First we need to initialize the explicit map to the original // number of points to recreate the original identity map. diff --git a/mesh/mesh_cleanup_test.cc b/mesh/mesh_cleanup_test.cc new file mode 100644 index 0000000..1bab5e5 --- /dev/null +++ b/mesh/mesh_cleanup_test.cc @@ -0,0 +1,131 @@ +// Copyright 2016 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 "mesh/mesh_cleanup.h" + +#include "core/draco_test_base.h" +#include "core/vector_d.h" +#include "mesh/triangle_soup_mesh_builder.h" + +namespace draco { + +class MeshCleanupTest : public ::testing::Test {}; + +TEST_F(MeshCleanupTest, TestDegneratedFaces) { + // This test verifies that the mesh cleanup tools removes degenerated faces. + TriangleSoupMeshBuilder mb; + mb.Start(2); + const int pos_att_id = + mb.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32); + // clang-format off + 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()); + 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, 0.f, 0.f).data()); + // clang-format on + + std::unique_ptr mesh = mb.Finalize(); + ASSERT_NE(mesh, nullptr) << "Failed to build the test mesh."; + ASSERT_EQ(mesh->num_faces(), 2) << "Wrong number of faces in the input mesh."; + MeshCleanupOptions cleanup_options; + MeshCleanup cleanup; + ASSERT_TRUE(cleanup(mesh.get(), cleanup_options)) + << "Failed to cleanup the mesh."; + ASSERT_EQ(mesh->num_faces(), 1) << "Failed to remove degenerated faces."; +} + +TEST_F(MeshCleanupTest, TestDegneratedFacesAndIsolatedVertices) { + // This test verifies that the mesh cleanup tools removes degenerated faces + // and isolated vertices. + TriangleSoupMeshBuilder mb; + mb.Start(2); + const int pos_att_id = + mb.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32); + // clang-format off + 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()); + mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(1), + Vector3f(10.f, 1.f, 0.f).data(), + Vector3f(1.f, 0.f, 0.f).data(), + Vector3f(10.f, 1.f, 0.f).data()); + // clang-format on + + std::unique_ptr mesh = mb.Finalize(); + ASSERT_NE(mesh, nullptr) << "Failed to build the test mesh."; + ASSERT_EQ(mesh->num_faces(), 2) << "Wrong number of faces in the input mesh."; + ASSERT_EQ(mesh->num_points(), 4u) + << "Wrong number of point ids in the input mesh."; + const MeshCleanupOptions cleanup_options; + MeshCleanup cleanup; + ASSERT_TRUE(cleanup(mesh.get(), cleanup_options)) + << "Failed to cleanup the mesh."; + ASSERT_EQ(mesh->num_faces(), 1) << "Failed to remove degenerated faces."; + ASSERT_EQ(mesh->num_points(), 3u) + << "Failed to remove isolated attribute indices."; +} + +TEST_F(MeshCleanupTest, TestAttributes) { + TriangleSoupMeshBuilder mb; + mb.Start(2); + const int pos_att_id = + mb.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32); + const int generic_att_id = + mb.AddAttribute(GeometryAttribute::GENERIC, 2, DT_FLOAT32); + // clang-format off + 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()); + mb.SetAttributeValuesForFace(generic_att_id, FaceIndex(0), + Vector2f(0.f, 0.f).data(), + Vector2f(0.f, 0.f).data(), + Vector2f(0.f, 0.f).data()); + + mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(1), + Vector3f(10.f, 1.f, 0.f).data(), + Vector3f(1.f, 0.f, 0.f).data(), + Vector3f(10.f, 1.f, 0.f).data()); + mb.SetAttributeValuesForFace(generic_att_id, FaceIndex(1), + Vector2f(1.f, 0.f).data(), + Vector2f(1.f, 0.f).data(), + Vector2f(1.f, 0.f).data()); + // clang-format on + + std::unique_ptr mesh = mb.Finalize(); + ASSERT_NE(mesh, nullptr) << "Failed to build the test mesh."; + ASSERT_EQ(mesh->num_faces(), 2) << "Wrong number of faces in the input mesh."; + ASSERT_EQ(mesh->num_points(), 5u) + << "Wrong number of point ids in the input mesh."; + ASSERT_EQ(mesh->attribute(1)->size(), 2u) + << "Wrong number of generic attribute entries."; + const MeshCleanupOptions cleanup_options; + MeshCleanup cleanup; + ASSERT_TRUE(cleanup(mesh.get(), cleanup_options)) + << "Failed to cleanup the mesh."; + ASSERT_EQ(mesh->num_faces(), 1) << "Failed to remove degenerated faces."; + ASSERT_EQ(mesh->num_points(), 3u) + << "Failed to remove isolated attribute indices."; + ASSERT_EQ(mesh->attribute(0)->size(), 3u) + << "Wrong number of unique positions after cleanup."; + ASSERT_EQ(mesh->attribute(1)->size(), 1u) + << "Wrong number of generic attribute entries after cleanup."; +} + +} // namespace draco diff --git a/mesh/mesh_test.cc b/mesh/mesh_test.cc new file mode 100644 index 0000000..31def58 --- /dev/null +++ b/mesh/mesh_test.cc @@ -0,0 +1,47 @@ +// Copyright 2016 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 "mesh/mesh.h" +#include "core/draco_test_base.h" + +namespace draco { + +class MeshTest : public ::testing::Test {}; + +TEST_F(MeshTest, CtorTest) { + // This test verifies that Mesh Ctor does the job. + const Mesh mesh; + ASSERT_EQ(mesh.num_points(), 0u); + ASSERT_EQ(mesh.GetNamedAttributeId(GeometryAttribute::POSITION), -1); + ASSERT_EQ(mesh.GetNamedAttribute(GeometryAttribute::POSITION), nullptr); + ASSERT_EQ(mesh.num_attributes(), 0); + ASSERT_EQ(mesh.num_faces(), 0); +} + +TEST_F(MeshTest, GenTinyMesh) { + // TODO(hemmer): create a simple mesh builder class to facilitate testing. + // This test checks properties of a tiny Mesh, i.e., initialized by hand. + + // TODO(hemmer): interface makes it impossible to do further testing + // Builder functions are all protected, no access to the mesh from the + // outside. + // Mesh::Face f1 {{1,2,3}}; + // Mesh::Face f2 {{3, 4, 5}}; + // MeshBuilder builder; + // builder.Start(2); + // builder.SetNumVertices(6); +} + +} // namespace draco diff --git a/mesh/triangle_soup_mesh_builder.cc b/mesh/triangle_soup_mesh_builder.cc index 6d276b1..2490706 100644 --- a/mesh/triangle_soup_mesh_builder.cc +++ b/mesh/triangle_soup_mesh_builder.cc @@ -70,7 +70,7 @@ std::unique_ptr TriangleSoupMeshBuilder::Finalize() { return nullptr; // Also deduplicate vertex indices. mesh_->DeduplicatePointIds(); - for (int i = 0; i < attribute_element_types_.size(); ++i) { + for (size_t i = 0; i < attribute_element_types_.size(); ++i) { if (attribute_element_types_[i] >= 0) { mesh_->SetAttributeElementType(i, static_cast( attribute_element_types_[i])); diff --git a/point_cloud/point_cloud.cc b/point_cloud/point_cloud.cc index 8a119b9..3b2e8ae 100644 --- a/point_cloud/point_cloud.cc +++ b/point_cloud/point_cloud.cc @@ -53,7 +53,8 @@ const PointAttribute *PointCloud::GetNamedAttribute( const PointAttribute *PointCloud::GetNamedAttributeByCustomId( GeometryAttribute::Type type, uint16_t custom_id) const { - for (int att_id = 0; att_id < named_attribute_index_[type].size(); ++att_id) { + for (size_t att_id = 0; att_id < named_attribute_index_[type].size(); + ++att_id) { if (attributes_[named_attribute_index_[type][att_id]]->custom_id() == custom_id) return attributes_[named_attribute_index_[type][att_id]].get(); @@ -90,7 +91,7 @@ int PointCloud::AddAttribute( void PointCloud::SetAttribute(int att_id, std::unique_ptr pa) { DCHECK(att_id >= 0); - if (attributes_.size() <= att_id) { + if (static_cast(attributes_.size()) <= att_id) { attributes_.resize(att_id + 1); } if (pa->attribute_type() < GeometryAttribute::NAMED_ATTRIBUTES_COUNT) { diff --git a/point_cloud/point_cloud.h b/point_cloud/point_cloud.h index 4919661..9f3589f 100644 --- a/point_cloud/point_cloud.h +++ b/point_cloud/point_cloud.h @@ -52,7 +52,7 @@ class PointCloud { int32_t num_attributes() const { return attributes_.size(); } const PointAttribute *attribute(int32_t att_id) const { DCHECK_LE(0, att_id); - DCHECK_LT(att_id, attributes_.size()); + DCHECK_LT(att_id, static_cast(attributes_.size())); return attributes_[att_id].get(); } @@ -60,7 +60,7 @@ class PointCloud { // maintain the attribute's consistency with draco::PointCloud. PointAttribute *attribute(int32_t att_id) { DCHECK_LE(0, att_id); - DCHECK_LT(att_id, attributes_.size()); + DCHECK_LT(att_id, static_cast(attributes_.size())); return attributes_[att_id].get(); } @@ -116,12 +116,13 @@ struct PointCloudHasher { hash = HashCombine(pc.attributes_.size(), hash); for (int i = 0; i < GeometryAttribute::NAMED_ATTRIBUTES_COUNT; ++i) { hash = HashCombine(pc.named_attribute_index_[i].size(), hash); - for (int j = 0; j < pc.named_attribute_index_[i].size(); ++j) { + for (int j = 0; j < static_cast(pc.named_attribute_index_[i].size()); + ++j) { hash = HashCombine(pc.named_attribute_index_[i][j], hash); } } // Hash attributes. - for (int i = 0; i < pc.attributes_.size(); ++i) { + for (int i = 0; i < static_cast(pc.attributes_.size()); ++i) { PointAttributeHasher att_hasher; hash = HashCombine(att_hasher(*pc.attributes_[i]), hash); } diff --git a/point_cloud/point_cloud_builder.cc b/point_cloud/point_cloud_builder.cc new file mode 100644 index 0000000..e23f83c --- /dev/null +++ b/point_cloud/point_cloud_builder.cc @@ -0,0 +1,71 @@ +// Copyright 2016 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 "point_cloud/point_cloud_builder.h" + +namespace draco { + +PointCloudBuilder::PointCloudBuilder() {} + +void PointCloudBuilder::Start(PointIndex::ValueType num_points) { + point_cloud_ = std::unique_ptr(new PointCloud()); + point_cloud_->set_num_points(num_points); +} + +int PointCloudBuilder::AddAttribute(GeometryAttribute::Type attribute_type, + int8_t num_components, DataType data_type) { + GeometryAttribute ga; + ga.Init(attribute_type, nullptr, num_components, data_type, false, + DataTypeLength(data_type) * num_components, 0); + return point_cloud_->AddAttribute(ga, true, point_cloud_->num_points()); +} + +void PointCloudBuilder::SetAttributeValueForPoint(int att_id, + PointIndex point_index, + const void *attribute_value) { + PointAttribute *const att = point_cloud_->attribute(att_id); + att->SetAttributeValue(att->mapped_index(point_index), attribute_value); +} + +void PointCloudBuilder::SetAttributeValuesForAllPoints( + int att_id, const void *attribute_values, int stride) { + PointAttribute *const att = point_cloud_->attribute(att_id); + const int data_stride = + DataTypeLength(att->data_type()) * att->components_count(); + if (stride == 0) + stride = data_stride; + if (stride == data_stride) { + // Fast copy path. + att->buffer()->Write(0, attribute_values, + point_cloud_->num_points() * data_stride); + } else { + // Copy attribute entries one by one. + for (PointIndex i(0); i < point_cloud_->num_points(); ++i) { + att->SetAttributeValue( + att->mapped_index(i), + static_cast(attribute_values) + stride * i.value()); + } + } +} + +std::unique_ptr PointCloudBuilder::Finalize( + bool deduplicate_points) { + if (deduplicate_points) { + point_cloud_->DeduplicateAttributeValues(); + point_cloud_->DeduplicatePointIds(); + } + return std::move(point_cloud_); +} + +} // namespace draco diff --git a/point_cloud/point_cloud_builder.h b/point_cloud/point_cloud_builder.h new file mode 100644 index 0000000..9dc07c6 --- /dev/null +++ b/point_cloud/point_cloud_builder.h @@ -0,0 +1,80 @@ +// Copyright 2016 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_POINT_CLOUD_POINT_CLOUD_BUILDER_H_ +#define DRACO_POINT_CLOUD_POINT_CLOUD_BUILDER_H_ + +#include "point_cloud/point_cloud.h" + +namespace draco { + +// A helper class for constructing PointCloud instances from other data sources. +// Usage: +// PointCloudBuilder builder; +// // Initialize the builder for a given number of points (required). +// builder.Start(num_points); +// // Specify desired attributes. +// int pos_att_id = +// builder.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32); +// // Add attribute values. +// for (PointIndex i(0); i < num_points; ++i) { +// builder.SetAttributeValueForPoint(pos_att_id, i, input_pos[i.value()]); +// } +// // Get the final PointCloud. +// constexpr bool deduplicate_points = false; +// std::unique_ptr pc = builder.Finalize(deduplicate_points); + +class PointCloudBuilder { + public: + PointCloudBuilder(); + + // Starts collecting point cloud data. + // The bahavior of other functions is undefined before this method is called. + void Start(PointIndex::ValueType num_points); + + int AddAttribute(GeometryAttribute::Type attribute_type, + int8_t num_components, DataType data_type); + + // Sets attribute value for a specific point. + // |attribute_value| must contain data in the format specified by the + // AddAttribute method. + void SetAttributeValueForPoint(int att_id, PointIndex point_index, + const void *attribute_value); + + // Sets attribute values for all points. All the values must be stored in the + // input |attribute_values| buffer. |stride| can be used to define the byte + // offset between two consecutive attribute values. If |stride| is set to 0, + // the stride is automatically computed based on the format of the given + // attribute. + void SetAttributeValuesForAllPoints(int att_id, const void *attribute_values, + int stride); + + // Finalizes the PointCloud or returns nullptr on error. + // If |deduplicate_points| is set to true, the following happens: + // 1. Attribute values with duplicate entries are deduplicated. + // 2. Point ids that are mapped to the same attribute values are + // deduplicated. + // Therefore, if |deduplicate_points| is true the final PointCloud can have + // a different number of point from the value specified in the Start method. + // Once this function is called, the builder becomes invalid and cannot be + // used until the method Start() is called again. + std::unique_ptr Finalize(bool deduplicate_points); + + private: + std::unique_ptr point_cloud_; +}; + +} // namespace draco + +#endif // DRACO_POINT_CLOUD_POINT_CLOUD_BUILDER_H_ diff --git a/point_cloud/point_cloud_builder_test.cc b/point_cloud/point_cloud_builder_test.cc new file mode 100644 index 0000000..58c009e --- /dev/null +++ b/point_cloud/point_cloud_builder_test.cc @@ -0,0 +1,171 @@ +// Copyright 2016 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 "point_cloud/point_cloud_builder.h" + +#include "core/draco_test_base.h" + +namespace draco { + +class PointCloudBuilderTest : public ::testing::Test { + protected: + // Test data. + // clang-format off + std::vector pos_data_ = {10.f, 0.f, 1.f, + 11.f, 1.f, 2.f, + 12.f, 2.f, 8.f, + 13.f, 4.f, 7.f, + 14.f, 5.f, 6.f, + 15.f, 6.f, 5.f, + 16.f, 1.f, 3.f, + 17.f, 1.f, 2.f, + 11.f, 1.f, 2.f, + 10.f, 0.f, 1.f}; + std::vector intensity_data_ = {100, + 200, + 500, + 700, + 400, + 400, + 400, + 100, + 100, + 100}; + // clang-format on +}; + +TEST_F(PointCloudBuilderTest, IndividualTest_NoDedup) { + // This test verifies that PointCloudBuilder can construct point cloud using + // SetAttributeValueForPoint API without deduplication. + PointCloudBuilder builder; + builder.Start(10); + const int pos_att_id = + builder.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32); + const int intensity_att_id = + builder.AddAttribute(GeometryAttribute::GENERIC, 1, DT_INT16); + for (PointIndex i(0); i < 10; ++i) { + builder.SetAttributeValueForPoint(pos_att_id, i, + pos_data_.data() + 3 * i.value()); + builder.SetAttributeValueForPoint(intensity_att_id, i, + intensity_data_.data() + i.value()); + } + std::unique_ptr res = builder.Finalize(false); + ASSERT_TRUE(res != nullptr); + ASSERT_EQ(res->num_points(), 10u); +} + +TEST_F(PointCloudBuilderTest, IndividualTest_Dedup) { + // This test verifies that PointCloudBuilder can construct point cloud using + // SetAttributeValueForPoint API with deduplication. + PointCloudBuilder builder; + builder.Start(10); + const int pos_att_id = + builder.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32); + const int intensity_att_id = + builder.AddAttribute(GeometryAttribute::GENERIC, 1, DT_INT16); + for (PointIndex i(0); i < 10; ++i) { + builder.SetAttributeValueForPoint(pos_att_id, i, + pos_data_.data() + 3 * i.value()); + builder.SetAttributeValueForPoint(intensity_att_id, i, + intensity_data_.data() + i.value()); + } + std::unique_ptr res = builder.Finalize(true); + ASSERT_TRUE(res != nullptr); + ASSERT_EQ(res->num_points(), 9u); +} + +TEST_F(PointCloudBuilderTest, BatchTest) { + // This test verifies that PointCloudBuilder can construct point cloud using + // SetAttributeValuesForAllPoints API. + PointCloudBuilder builder; + builder.Start(10); + const int pos_att_id = + builder.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32); + const int intensity_att_id = + builder.AddAttribute(GeometryAttribute::GENERIC, 1, DT_INT16); + builder.SetAttributeValuesForAllPoints(pos_att_id, pos_data_.data(), 0); + builder.SetAttributeValuesForAllPoints(intensity_att_id, + intensity_data_.data(), 0); + std::unique_ptr res = builder.Finalize(false); + ASSERT_TRUE(res != nullptr); + ASSERT_EQ(res->num_points(), 10u); + for (PointIndex i(0); i < 10; ++i) { + float pos_val[3]; + res->attribute(pos_att_id)->GetMappedValue(i, pos_val); + for (int c = 0; c < 3; ++c) { + ASSERT_EQ(pos_val[c], pos_data_[3 * i.value() + c]); + } + int16_t int_val; + res->attribute(intensity_att_id)->GetMappedValue(i, &int_val); + ASSERT_EQ(intensity_data_[i.value()], int_val); + } +} + +TEST_F(PointCloudBuilderTest, MultiUse) { + // This test verifies that PointCloudBuilder can be used multiple times + PointCloudBuilder builder; + { + builder.Start(10); + const int pos_att_id = + builder.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32); + const int intensity_att_id = + builder.AddAttribute(GeometryAttribute::GENERIC, 1, DT_INT16); + builder.SetAttributeValuesForAllPoints(pos_att_id, pos_data_.data(), 0); + builder.SetAttributeValuesForAllPoints(intensity_att_id, + intensity_data_.data(), 0); + std::unique_ptr res = builder.Finalize(false); + ASSERT_TRUE(res != nullptr); + ASSERT_EQ(res->num_points(), 10u); + for (PointIndex i(0); i < 10; ++i) { + float pos_val[3]; + res->attribute(pos_att_id)->GetMappedValue(i, pos_val); + for (int c = 0; c < 3; ++c) { + ASSERT_EQ(pos_val[c], pos_data_[3 * i.value() + c]); + } + int16_t int_val; + res->attribute(intensity_att_id)->GetMappedValue(i, &int_val); + ASSERT_EQ(intensity_data_[i.value()], int_val); + } + } + + { + // Use only a sub-set of data (offseted to avoid possible reuse of old + // data). + builder.Start(4); + const int pos_att_id = + builder.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32); + const int intensity_att_id = + builder.AddAttribute(GeometryAttribute::GENERIC, 1, DT_INT16); + constexpr int offset = 5; + builder.SetAttributeValuesForAllPoints(pos_att_id, + pos_data_.data() + 3 * offset, 0); + builder.SetAttributeValuesForAllPoints(intensity_att_id, + intensity_data_.data() + offset, 0); + std::unique_ptr res = builder.Finalize(false); + ASSERT_TRUE(res != nullptr); + ASSERT_EQ(res->num_points(), 4u); + for (PointIndex i(0); i < 4; ++i) { + float pos_val[3]; + res->attribute(pos_att_id)->GetMappedValue(i, pos_val); + for (int c = 0; c < 3; ++c) { + ASSERT_EQ(pos_val[c], pos_data_[3 * (i.value() + offset) + c]); + } + int16_t int_val; + res->attribute(intensity_att_id)->GetMappedValue(i, &int_val); + ASSERT_EQ(intensity_data_[i.value() + offset], int_val); + } + } +} + +} // namespace draco diff --git a/testdata/cube_subd.obj b/testdata/cube_subd.obj new file mode 100644 index 0000000..9c32d34 --- /dev/null +++ b/testdata/cube_subd.obj @@ -0,0 +1,4626 @@ +#### +# +# OBJ File Generated by Meshlab +# +#### +# Object cube_subd.obj +# +# Vertices: 1538 +# Faces: 3072 +# +#### +v 0.000000 0.000000 0.000000 +v 0.000000 0.000000 1.000000 +v 0.000000 1.000000 0.000000 +v 0.000000 1.000000 1.000000 +v 1.000000 0.000000 0.000000 +v 1.000000 0.000000 1.000000 +v 1.000000 1.000000 0.000000 +v 1.000000 1.000000 1.000000 +v 0.500000 0.500000 0.000000 +v 1.000000 0.500000 0.000000 +v 0.500000 0.000000 0.000000 +v 0.000000 0.500000 0.000000 +v 0.500000 1.000000 0.000000 +v 0.000000 0.500000 0.500000 +v 0.000000 0.500000 1.000000 +v 0.000000 1.000000 0.500000 +v 0.000000 0.000000 0.500000 +v 0.500000 1.000000 0.500000 +v 1.000000 1.000000 0.500000 +v 0.500000 1.000000 1.000000 +v 1.000000 0.500000 0.500000 +v 1.000000 0.000000 0.500000 +v 1.000000 0.500000 1.000000 +v 0.500000 0.000000 0.500000 +v 0.500000 0.000000 1.000000 +v 0.500000 0.500000 1.000000 +v 0.750000 0.500000 0.000000 +v 0.750000 0.250000 0.000000 +v 0.500000 0.250000 0.000000 +v 0.250000 0.750000 0.000000 +v 0.500000 0.750000 0.000000 +v 0.250000 0.500000 0.000000 +v 0.000000 0.500000 0.750000 +v 0.000000 0.750000 0.750000 +v 0.000000 0.750000 0.500000 +v 0.000000 0.250000 0.500000 +v 0.000000 0.500000 0.250000 +v 0.000000 0.250000 0.250000 +v 0.750000 1.000000 0.500000 +v 0.750000 1.000000 0.250000 +v 0.500000 1.000000 0.250000 +v 0.250000 1.000000 0.750000 +v 0.500000 1.000000 0.750000 +v 0.250000 1.000000 0.500000 +v 1.000000 0.500000 0.250000 +v 1.000000 0.250000 0.500000 +v 1.000000 0.250000 0.250000 +v 1.000000 0.750000 0.500000 +v 1.000000 0.750000 0.750000 +v 1.000000 0.500000 0.750000 +v 0.750000 0.000000 0.250000 +v 0.750000 0.000000 0.500000 +v 0.500000 0.000000 0.250000 +v 0.500000 0.000000 0.750000 +v 0.250000 0.000000 0.750000 +v 0.250000 0.000000 0.500000 +v 0.750000 0.250000 1.000000 +v 0.750000 0.500000 1.000000 +v 0.500000 0.250000 1.000000 +v 0.500000 0.750000 1.000000 +v 0.250000 0.750000 1.000000 +v 0.250000 0.500000 1.000000 +v 0.250000 0.250000 0.000000 +v 0.250000 0.000000 0.000000 +v 0.750000 0.750000 0.000000 +v 1.000000 0.750000 0.000000 +v 1.000000 0.250000 0.000000 +v 0.750000 0.000000 0.000000 +v 0.000000 0.250000 0.000000 +v 0.000000 0.750000 0.000000 +v 0.250000 1.000000 0.000000 +v 0.750000 1.000000 0.000000 +v 0.000000 0.750000 0.250000 +v 0.000000 1.000000 0.250000 +v 0.000000 0.250000 0.750000 +v 0.000000 0.250000 1.000000 +v 0.000000 0.750000 1.000000 +v 0.000000 1.000000 0.750000 +v 0.000000 0.000000 0.250000 +v 0.000000 0.000000 0.750000 +v 0.250000 1.000000 0.250000 +v 0.750000 1.000000 0.750000 +v 1.000000 1.000000 0.750000 +v 1.000000 1.000000 0.250000 +v 0.250000 1.000000 1.000000 +v 0.750000 1.000000 1.000000 +v 1.000000 0.000000 0.250000 +v 1.000000 0.750000 0.250000 +v 1.000000 0.250000 0.750000 +v 1.000000 0.000000 0.750000 +v 1.000000 0.250000 1.000000 +v 1.000000 0.750000 1.000000 +v 0.250000 0.000000 0.250000 +v 0.750000 0.000000 0.750000 +v 0.750000 0.000000 1.000000 +v 0.250000 0.000000 1.000000 +v 0.250000 0.250000 1.000000 +v 0.750000 0.750000 1.000000 +v 0.750000 0.375000 0.000000 +v 0.625000 0.250000 0.000000 +v 0.625000 0.375000 0.000000 +v 0.375000 0.750000 0.000000 +v 0.375000 0.625000 0.000000 +v 0.250000 0.625000 0.000000 +v 0.000000 0.625000 0.750000 +v 0.000000 0.750000 0.625000 +v 0.000000 0.625000 0.625000 +v 0.000000 0.375000 0.375000 +v 0.000000 0.375000 0.250000 +v 0.000000 0.250000 0.375000 +v 0.750000 1.000000 0.375000 +v 0.625000 1.000000 0.250000 +v 0.625000 1.000000 0.375000 +v 0.375000 1.000000 0.750000 +v 0.375000 1.000000 0.625000 +v 0.250000 1.000000 0.625000 +v 1.000000 0.375000 0.375000 +v 1.000000 0.250000 0.375000 +v 1.000000 0.375000 0.250000 +v 1.000000 0.750000 0.625000 +v 1.000000 0.625000 0.750000 +v 1.000000 0.625000 0.625000 +v 0.750000 0.000000 0.375000 +v 0.625000 0.000000 0.375000 +v 0.625000 0.000000 0.250000 +v 0.375000 0.000000 0.750000 +v 0.250000 0.000000 0.625000 +v 0.375000 0.000000 0.625000 +v 0.750000 0.375000 1.000000 +v 0.625000 0.375000 1.000000 +v 0.625000 0.250000 1.000000 +v 0.375000 0.750000 1.000000 +v 0.250000 0.625000 1.000000 +v 0.375000 0.625000 1.000000 +v 0.375000 0.250000 0.000000 +v 0.375000 0.125000 0.000000 +v 0.250000 0.125000 0.000000 +v 0.875000 0.750000 0.000000 +v 0.875000 0.625000 0.000000 +v 0.750000 0.625000 0.000000 +v 0.875000 0.250000 0.000000 +v 0.875000 0.125000 0.000000 +v 0.750000 0.125000 0.000000 +v 0.125000 0.375000 0.000000 +v 0.250000 0.375000 0.000000 +v 0.125000 0.250000 0.000000 +v 0.125000 0.875000 0.000000 +v 0.250000 0.875000 0.000000 +v 0.125000 0.750000 0.000000 +v 0.625000 0.875000 0.000000 +v 0.750000 0.875000 0.000000 +v 0.625000 0.750000 0.000000 +v 0.000000 0.750000 0.375000 +v 0.000000 0.875000 0.375000 +v 0.000000 0.875000 0.250000 +v 0.000000 0.250000 0.875000 +v 0.000000 0.375000 0.875000 +v 0.000000 0.375000 0.750000 +v 0.000000 0.750000 0.875000 +v 0.000000 0.875000 0.875000 +v 0.000000 0.875000 0.750000 +v 0.000000 0.125000 0.250000 +v 0.000000 0.250000 0.125000 +v 0.000000 0.125000 0.125000 +v 0.000000 0.125000 0.750000 +v 0.000000 0.250000 0.625000 +v 0.000000 0.125000 0.625000 +v 0.000000 0.625000 0.250000 +v 0.000000 0.750000 0.125000 +v 0.000000 0.625000 0.125000 +v 0.375000 1.000000 0.250000 +v 0.375000 1.000000 0.125000 +v 0.250000 1.000000 0.125000 +v 0.875000 1.000000 0.750000 +v 0.875000 1.000000 0.625000 +v 0.750000 1.000000 0.625000 +v 0.875000 1.000000 0.250000 +v 0.875000 1.000000 0.125000 +v 0.750000 1.000000 0.125000 +v 0.125000 1.000000 0.375000 +v 0.250000 1.000000 0.375000 +v 0.125000 1.000000 0.250000 +v 0.125000 1.000000 0.875000 +v 0.250000 1.000000 0.875000 +v 0.125000 1.000000 0.750000 +v 0.625000 1.000000 0.875000 +v 0.750000 1.000000 0.875000 +v 0.625000 1.000000 0.750000 +v 1.000000 0.250000 0.125000 +v 1.000000 0.125000 0.250000 +v 1.000000 0.125000 0.125000 +v 1.000000 0.750000 0.125000 +v 1.000000 0.625000 0.250000 +v 1.000000 0.625000 0.125000 +v 1.000000 0.250000 0.625000 +v 1.000000 0.125000 0.750000 +v 1.000000 0.125000 0.625000 +v 1.000000 0.375000 0.750000 +v 1.000000 0.375000 0.875000 +v 1.000000 0.250000 0.875000 +v 1.000000 0.875000 0.250000 +v 1.000000 0.875000 0.375000 +v 1.000000 0.750000 0.375000 +v 1.000000 0.875000 0.750000 +v 1.000000 0.875000 0.875000 +v 1.000000 0.750000 0.875000 +v 0.375000 0.000000 0.125000 +v 0.375000 0.000000 0.250000 +v 0.250000 0.000000 0.125000 +v 0.875000 0.000000 0.125000 +v 0.875000 0.000000 0.250000 +v 0.750000 0.000000 0.125000 +v 0.875000 0.000000 0.625000 +v 0.875000 0.000000 0.750000 +v 0.750000 0.000000 0.625000 +v 0.250000 0.000000 0.375000 +v 0.125000 0.000000 0.375000 +v 0.125000 0.000000 0.250000 +v 0.750000 0.000000 0.875000 +v 0.625000 0.000000 0.875000 +v 0.625000 0.000000 0.750000 +v 0.250000 0.000000 0.875000 +v 0.125000 0.000000 0.875000 +v 0.125000 0.000000 0.750000 +v 0.375000 0.125000 1.000000 +v 0.375000 0.250000 1.000000 +v 0.250000 0.125000 1.000000 +v 0.875000 0.125000 1.000000 +v 0.875000 0.250000 1.000000 +v 0.750000 0.125000 1.000000 +v 0.875000 0.625000 1.000000 +v 0.875000 0.750000 1.000000 +v 0.750000 0.625000 1.000000 +v 0.250000 0.375000 1.000000 +v 0.125000 0.375000 1.000000 +v 0.125000 0.250000 1.000000 +v 0.750000 0.875000 1.000000 +v 0.625000 0.875000 1.000000 +v 0.625000 0.750000 1.000000 +v 0.250000 0.875000 1.000000 +v 0.125000 0.875000 1.000000 +v 0.125000 0.750000 1.000000 +v 0.625000 0.500000 0.000000 +v 0.500000 0.375000 0.000000 +v 0.875000 0.500000 0.000000 +v 0.875000 0.375000 0.000000 +v 0.625000 0.125000 0.000000 +v 0.500000 0.125000 0.000000 +v 0.125000 0.625000 0.000000 +v 0.125000 0.500000 0.000000 +v 0.375000 0.875000 0.000000 +v 0.500000 0.875000 0.000000 +v 0.500000 0.625000 0.000000 +v 0.375000 0.500000 0.000000 +v 0.000000 0.500000 0.625000 +v 0.000000 0.625000 0.500000 +v 0.000000 0.500000 0.875000 +v 0.000000 0.625000 0.875000 +v 0.000000 0.875000 0.625000 +v 0.000000 0.875000 0.500000 +v 0.000000 0.125000 0.500000 +v 0.000000 0.125000 0.375000 +v 0.000000 0.375000 0.500000 +v 0.000000 0.500000 0.375000 +v 0.000000 0.500000 0.125000 +v 0.000000 0.375000 0.125000 +v 0.625000 1.000000 0.500000 +v 0.500000 1.000000 0.375000 +v 0.875000 1.000000 0.500000 +v 0.875000 1.000000 0.375000 +v 0.625000 1.000000 0.125000 +v 0.500000 1.000000 0.125000 +v 0.125000 1.000000 0.625000 +v 0.125000 1.000000 0.500000 +v 0.375000 1.000000 0.875000 +v 0.500000 1.000000 0.875000 +v 0.500000 1.000000 0.625000 +v 0.375000 1.000000 0.500000 +v 1.000000 0.500000 0.125000 +v 1.000000 0.375000 0.125000 +v 1.000000 0.500000 0.375000 +v 1.000000 0.375000 0.500000 +v 1.000000 0.125000 0.500000 +v 1.000000 0.125000 0.375000 +v 1.000000 0.625000 0.500000 +v 1.000000 0.500000 0.625000 +v 1.000000 0.875000 0.500000 +v 1.000000 0.875000 0.625000 +v 1.000000 0.625000 0.875000 +v 1.000000 0.500000 0.875000 +v 0.625000 0.000000 0.125000 +v 0.500000 0.000000 0.125000 +v 0.875000 0.000000 0.375000 +v 0.875000 0.000000 0.500000 +v 0.625000 0.000000 0.500000 +v 0.500000 0.000000 0.375000 +v 0.500000 0.000000 0.625000 +v 0.375000 0.000000 0.500000 +v 0.500000 0.000000 0.875000 +v 0.375000 0.000000 0.875000 +v 0.125000 0.000000 0.625000 +v 0.125000 0.000000 0.500000 +v 0.625000 0.125000 1.000000 +v 0.500000 0.125000 1.000000 +v 0.875000 0.375000 1.000000 +v 0.875000 0.500000 1.000000 +v 0.625000 0.500000 1.000000 +v 0.500000 0.375000 1.000000 +v 0.500000 0.625000 1.000000 +v 0.375000 0.500000 1.000000 +v 0.500000 0.875000 1.000000 +v 0.375000 0.875000 1.000000 +v 0.125000 0.625000 1.000000 +v 0.125000 0.500000 1.000000 +v 0.125000 0.125000 0.000000 +v 0.125000 0.000000 0.000000 +v 0.375000 0.375000 0.000000 +v 0.375000 0.000000 0.000000 +v 0.625000 0.625000 0.000000 +v 0.875000 0.875000 0.000000 +v 1.000000 0.875000 0.000000 +v 1.000000 0.625000 0.000000 +v 0.625000 0.000000 0.000000 +v 1.000000 0.375000 0.000000 +v 1.000000 0.125000 0.000000 +v 0.875000 0.000000 0.000000 +v 0.000000 0.125000 0.000000 +v 0.000000 0.375000 0.000000 +v 0.000000 0.625000 0.000000 +v 0.000000 0.875000 0.000000 +v 0.125000 1.000000 0.000000 +v 0.375000 1.000000 0.000000 +v 0.625000 1.000000 0.000000 +v 0.875000 1.000000 0.000000 +v 0.000000 0.875000 0.125000 +v 0.000000 1.000000 0.125000 +v 0.000000 0.625000 0.375000 +v 0.000000 1.000000 0.375000 +v 0.000000 0.375000 0.625000 +v 0.000000 0.125000 0.875000 +v 0.000000 0.125000 1.000000 +v 0.000000 0.375000 1.000000 +v 0.000000 1.000000 0.625000 +v 0.000000 0.625000 1.000000 +v 0.000000 0.875000 1.000000 +v 0.000000 1.000000 0.875000 +v 0.000000 0.000000 0.125000 +v 0.000000 0.000000 0.375000 +v 0.000000 0.000000 0.625000 +v 0.000000 0.000000 0.875000 +v 0.125000 1.000000 0.125000 +v 0.375000 1.000000 0.375000 +v 0.625000 1.000000 0.625000 +v 0.875000 1.000000 0.875000 +v 1.000000 1.000000 0.875000 +v 1.000000 1.000000 0.625000 +v 1.000000 1.000000 0.375000 +v 1.000000 1.000000 0.125000 +v 0.125000 1.000000 1.000000 +v 0.375000 1.000000 1.000000 +v 0.625000 1.000000 1.000000 +v 0.875000 1.000000 1.000000 +v 1.000000 0.000000 0.125000 +v 1.000000 0.000000 0.375000 +v 1.000000 0.875000 0.125000 +v 1.000000 0.625000 0.375000 +v 1.000000 0.000000 0.625000 +v 1.000000 0.375000 0.625000 +v 1.000000 0.125000 0.875000 +v 1.000000 0.000000 0.875000 +v 1.000000 0.125000 1.000000 +v 1.000000 0.375000 1.000000 +v 1.000000 0.625000 1.000000 +v 1.000000 0.875000 1.000000 +v 0.125000 0.000000 0.125000 +v 0.375000 0.000000 0.375000 +v 0.625000 0.000000 0.625000 +v 0.875000 0.000000 0.875000 +v 0.875000 0.000000 1.000000 +v 0.625000 0.000000 1.000000 +v 0.375000 0.000000 1.000000 +v 0.125000 0.000000 1.000000 +v 0.125000 0.125000 1.000000 +v 0.375000 0.375000 1.000000 +v 0.625000 0.625000 1.000000 +v 0.875000 0.875000 1.000000 +v 0.687500 0.312500 0.000000 +v 0.625000 0.312500 0.000000 +v 0.687500 0.375000 0.000000 +v 0.375000 0.687500 0.000000 +v 0.312500 0.625000 0.000000 +v 0.312500 0.687500 0.000000 +v 0.000000 0.687500 0.687500 +v 0.000000 0.687500 0.625000 +v 0.000000 0.625000 0.687500 +v 0.000000 0.375000 0.312500 +v 0.000000 0.312500 0.312500 +v 0.000000 0.312500 0.375000 +v 0.687500 1.000000 0.312500 +v 0.625000 1.000000 0.312500 +v 0.687500 1.000000 0.375000 +v 0.375000 1.000000 0.687500 +v 0.312500 1.000000 0.625000 +v 0.312500 1.000000 0.687500 +v 1.000000 0.312500 0.375000 +v 1.000000 0.312500 0.312500 +v 1.000000 0.375000 0.312500 +v 1.000000 0.687500 0.687500 +v 1.000000 0.625000 0.687500 +v 1.000000 0.687500 0.625000 +v 0.687500 0.000000 0.375000 +v 0.625000 0.000000 0.312500 +v 0.687500 0.000000 0.312500 +v 0.312500 0.000000 0.687500 +v 0.312500 0.000000 0.625000 +v 0.375000 0.000000 0.687500 +v 0.687500 0.375000 1.000000 +v 0.625000 0.312500 1.000000 +v 0.687500 0.312500 1.000000 +v 0.312500 0.687500 1.000000 +v 0.312500 0.625000 1.000000 +v 0.375000 0.687500 1.000000 +v 0.375000 0.187500 0.000000 +v 0.312500 0.125000 0.000000 +v 0.312500 0.187500 0.000000 +v 0.875000 0.687500 0.000000 +v 0.812500 0.625000 0.000000 +v 0.812500 0.687500 0.000000 +v 0.875000 0.187500 0.000000 +v 0.812500 0.125000 0.000000 +v 0.812500 0.187500 0.000000 +v 0.187500 0.375000 0.000000 +v 0.187500 0.312500 0.000000 +v 0.125000 0.312500 0.000000 +v 0.187500 0.875000 0.000000 +v 0.187500 0.812500 0.000000 +v 0.125000 0.812500 0.000000 +v 0.687500 0.875000 0.000000 +v 0.687500 0.812500 0.000000 +v 0.625000 0.812500 0.000000 +v 0.000000 0.812500 0.375000 +v 0.000000 0.875000 0.312500 +v 0.000000 0.812500 0.312500 +v 0.000000 0.312500 0.875000 +v 0.000000 0.375000 0.812500 +v 0.000000 0.312500 0.812500 +v 0.000000 0.812500 0.875000 +v 0.000000 0.875000 0.812500 +v 0.000000 0.812500 0.812500 +v 0.000000 0.187500 0.187500 +v 0.000000 0.187500 0.125000 +v 0.000000 0.125000 0.187500 +v 0.000000 0.187500 0.687500 +v 0.000000 0.187500 0.625000 +v 0.000000 0.125000 0.687500 +v 0.000000 0.687500 0.187500 +v 0.000000 0.687500 0.125000 +v 0.000000 0.625000 0.187500 +v 0.375000 1.000000 0.187500 +v 0.312500 1.000000 0.125000 +v 0.312500 1.000000 0.187500 +v 0.875000 1.000000 0.687500 +v 0.812500 1.000000 0.625000 +v 0.812500 1.000000 0.687500 +v 0.875000 1.000000 0.187500 +v 0.812500 1.000000 0.125000 +v 0.812500 1.000000 0.187500 +v 0.187500 1.000000 0.375000 +v 0.187500 1.000000 0.312500 +v 0.125000 1.000000 0.312500 +v 0.187500 1.000000 0.875000 +v 0.187500 1.000000 0.812500 +v 0.125000 1.000000 0.812500 +v 0.687500 1.000000 0.875000 +v 0.687500 1.000000 0.812500 +v 0.625000 1.000000 0.812500 +v 1.000000 0.187500 0.187500 +v 1.000000 0.125000 0.187500 +v 1.000000 0.187500 0.125000 +v 1.000000 0.687500 0.187500 +v 1.000000 0.625000 0.187500 +v 1.000000 0.687500 0.125000 +v 1.000000 0.187500 0.687500 +v 1.000000 0.125000 0.687500 +v 1.000000 0.187500 0.625000 +v 1.000000 0.375000 0.812500 +v 1.000000 0.312500 0.875000 +v 1.000000 0.312500 0.812500 +v 1.000000 0.875000 0.312500 +v 1.000000 0.812500 0.375000 +v 1.000000 0.812500 0.312500 +v 1.000000 0.875000 0.812500 +v 1.000000 0.812500 0.875000 +v 1.000000 0.812500 0.812500 +v 0.375000 0.000000 0.187500 +v 0.312500 0.000000 0.187500 +v 0.312500 0.000000 0.125000 +v 0.875000 0.000000 0.187500 +v 0.812500 0.000000 0.187500 +v 0.812500 0.000000 0.125000 +v 0.875000 0.000000 0.687500 +v 0.812500 0.000000 0.687500 +v 0.812500 0.000000 0.625000 +v 0.187500 0.000000 0.375000 +v 0.125000 0.000000 0.312500 +v 0.187500 0.000000 0.312500 +v 0.687500 0.000000 0.875000 +v 0.625000 0.000000 0.812500 +v 0.687500 0.000000 0.812500 +v 0.187500 0.000000 0.875000 +v 0.125000 0.000000 0.812500 +v 0.187500 0.000000 0.812500 +v 0.375000 0.187500 1.000000 +v 0.312500 0.187500 1.000000 +v 0.312500 0.125000 1.000000 +v 0.875000 0.187500 1.000000 +v 0.812500 0.187500 1.000000 +v 0.812500 0.125000 1.000000 +v 0.875000 0.687500 1.000000 +v 0.812500 0.687500 1.000000 +v 0.812500 0.625000 1.000000 +v 0.187500 0.375000 1.000000 +v 0.125000 0.312500 1.000000 +v 0.187500 0.312500 1.000000 +v 0.687500 0.875000 1.000000 +v 0.625000 0.812500 1.000000 +v 0.687500 0.812500 1.000000 +v 0.187500 0.875000 1.000000 +v 0.125000 0.812500 1.000000 +v 0.187500 0.812500 1.000000 +v 0.625000 0.437500 0.000000 +v 0.562500 0.375000 0.000000 +v 0.562500 0.437500 0.000000 +v 0.875000 0.437500 0.000000 +v 0.812500 0.375000 0.000000 +v 0.812500 0.437500 0.000000 +v 0.625000 0.187500 0.000000 +v 0.562500 0.125000 0.000000 +v 0.562500 0.187500 0.000000 +v 0.187500 0.625000 0.000000 +v 0.187500 0.562500 0.000000 +v 0.125000 0.562500 0.000000 +v 0.437500 0.875000 0.000000 +v 0.437500 0.812500 0.000000 +v 0.375000 0.812500 0.000000 +v 0.437500 0.625000 0.000000 +v 0.437500 0.562500 0.000000 +v 0.375000 0.562500 0.000000 +v 0.000000 0.562500 0.625000 +v 0.000000 0.625000 0.562500 +v 0.000000 0.562500 0.562500 +v 0.000000 0.562500 0.875000 +v 0.000000 0.625000 0.812500 +v 0.000000 0.562500 0.812500 +v 0.000000 0.812500 0.625000 +v 0.000000 0.875000 0.562500 +v 0.000000 0.812500 0.562500 +v 0.000000 0.187500 0.437500 +v 0.000000 0.187500 0.375000 +v 0.000000 0.125000 0.437500 +v 0.000000 0.437500 0.437500 +v 0.000000 0.437500 0.375000 +v 0.000000 0.375000 0.437500 +v 0.000000 0.437500 0.187500 +v 0.000000 0.437500 0.125000 +v 0.000000 0.375000 0.187500 +v 0.625000 1.000000 0.437500 +v 0.562500 1.000000 0.375000 +v 0.562500 1.000000 0.437500 +v 0.875000 1.000000 0.437500 +v 0.812500 1.000000 0.375000 +v 0.812500 1.000000 0.437500 +v 0.625000 1.000000 0.187500 +v 0.562500 1.000000 0.125000 +v 0.562500 1.000000 0.187500 +v 0.187500 1.000000 0.625000 +v 0.187500 1.000000 0.562500 +v 0.125000 1.000000 0.562500 +v 0.437500 1.000000 0.875000 +v 0.437500 1.000000 0.812500 +v 0.375000 1.000000 0.812500 +v 0.437500 1.000000 0.625000 +v 0.437500 1.000000 0.562500 +v 0.375000 1.000000 0.562500 +v 1.000000 0.437500 0.187500 +v 1.000000 0.375000 0.187500 +v 1.000000 0.437500 0.125000 +v 1.000000 0.437500 0.437500 +v 1.000000 0.375000 0.437500 +v 1.000000 0.437500 0.375000 +v 1.000000 0.187500 0.437500 +v 1.000000 0.125000 0.437500 +v 1.000000 0.187500 0.375000 +v 1.000000 0.625000 0.562500 +v 1.000000 0.562500 0.625000 +v 1.000000 0.562500 0.562500 +v 1.000000 0.875000 0.562500 +v 1.000000 0.812500 0.625000 +v 1.000000 0.812500 0.562500 +v 1.000000 0.625000 0.812500 +v 1.000000 0.562500 0.875000 +v 1.000000 0.562500 0.812500 +v 0.625000 0.000000 0.187500 +v 0.562500 0.000000 0.187500 +v 0.562500 0.000000 0.125000 +v 0.875000 0.000000 0.437500 +v 0.812500 0.000000 0.437500 +v 0.812500 0.000000 0.375000 +v 0.625000 0.000000 0.437500 +v 0.562500 0.000000 0.437500 +v 0.562500 0.000000 0.375000 +v 0.437500 0.000000 0.625000 +v 0.375000 0.000000 0.562500 +v 0.437500 0.000000 0.562500 +v 0.437500 0.000000 0.875000 +v 0.375000 0.000000 0.812500 +v 0.437500 0.000000 0.812500 +v 0.187500 0.000000 0.625000 +v 0.125000 0.000000 0.562500 +v 0.187500 0.000000 0.562500 +v 0.625000 0.187500 1.000000 +v 0.562500 0.187500 1.000000 +v 0.562500 0.125000 1.000000 +v 0.875000 0.437500 1.000000 +v 0.812500 0.437500 1.000000 +v 0.812500 0.375000 1.000000 +v 0.625000 0.437500 1.000000 +v 0.562500 0.437500 1.000000 +v 0.562500 0.375000 1.000000 +v 0.437500 0.625000 1.000000 +v 0.375000 0.562500 1.000000 +v 0.437500 0.562500 1.000000 +v 0.437500 0.875000 1.000000 +v 0.375000 0.812500 1.000000 +v 0.437500 0.812500 1.000000 +v 0.187500 0.625000 1.000000 +v 0.125000 0.562500 1.000000 +v 0.187500 0.562500 1.000000 +v 0.187500 0.125000 0.000000 +v 0.187500 0.062500 0.000000 +v 0.125000 0.062500 0.000000 +v 0.437500 0.375000 0.000000 +v 0.437500 0.312500 0.000000 +v 0.375000 0.312500 0.000000 +v 0.437500 0.125000 0.000000 +v 0.437500 0.062500 0.000000 +v 0.375000 0.062500 0.000000 +v 0.687500 0.625000 0.000000 +v 0.687500 0.562500 0.000000 +v 0.625000 0.562500 0.000000 +v 0.937500 0.875000 0.000000 +v 0.937500 0.812500 0.000000 +v 0.875000 0.812500 0.000000 +v 0.937500 0.625000 0.000000 +v 0.937500 0.562500 0.000000 +v 0.875000 0.562500 0.000000 +v 0.687500 0.125000 0.000000 +v 0.687500 0.062500 0.000000 +v 0.625000 0.062500 0.000000 +v 0.937500 0.375000 0.000000 +v 0.937500 0.312500 0.000000 +v 0.875000 0.312500 0.000000 +v 0.937500 0.125000 0.000000 +v 0.937500 0.062500 0.000000 +v 0.875000 0.062500 0.000000 +v 0.062500 0.187500 0.000000 +v 0.125000 0.187500 0.000000 +v 0.062500 0.125000 0.000000 +v 0.062500 0.437500 0.000000 +v 0.125000 0.437500 0.000000 +v 0.062500 0.375000 0.000000 +v 0.312500 0.437500 0.000000 +v 0.375000 0.437500 0.000000 +v 0.312500 0.375000 0.000000 +v 0.062500 0.687500 0.000000 +v 0.125000 0.687500 0.000000 +v 0.062500 0.625000 0.000000 +v 0.062500 0.937500 0.000000 +v 0.125000 0.937500 0.000000 +v 0.062500 0.875000 0.000000 +v 0.312500 0.937500 0.000000 +v 0.375000 0.937500 0.000000 +v 0.312500 0.875000 0.000000 +v 0.562500 0.687500 0.000000 +v 0.625000 0.687500 0.000000 +v 0.562500 0.625000 0.000000 +v 0.562500 0.937500 0.000000 +v 0.625000 0.937500 0.000000 +v 0.562500 0.875000 0.000000 +v 0.812500 0.937500 0.000000 +v 0.875000 0.937500 0.000000 +v 0.812500 0.875000 0.000000 +v 0.000000 0.875000 0.187500 +v 0.000000 0.937500 0.187500 +v 0.000000 0.937500 0.125000 +v 0.000000 0.625000 0.437500 +v 0.000000 0.687500 0.437500 +v 0.000000 0.687500 0.375000 +v 0.000000 0.875000 0.437500 +v 0.000000 0.937500 0.437500 +v 0.000000 0.937500 0.375000 +v 0.000000 0.375000 0.687500 +v 0.000000 0.437500 0.687500 +v 0.000000 0.437500 0.625000 +v 0.000000 0.125000 0.937500 +v 0.000000 0.187500 0.937500 +v 0.000000 0.187500 0.875000 +v 0.000000 0.375000 0.937500 +v 0.000000 0.437500 0.937500 +v 0.000000 0.437500 0.875000 +v 0.000000 0.875000 0.687500 +v 0.000000 0.937500 0.687500 +v 0.000000 0.937500 0.625000 +v 0.000000 0.625000 0.937500 +v 0.000000 0.687500 0.937500 +v 0.000000 0.687500 0.875000 +v 0.000000 0.875000 0.937500 +v 0.000000 0.937500 0.937500 +v 0.000000 0.937500 0.875000 +v 0.000000 0.062500 0.125000 +v 0.000000 0.125000 0.062500 +v 0.000000 0.062500 0.062500 +v 0.000000 0.062500 0.375000 +v 0.000000 0.125000 0.312500 +v 0.000000 0.062500 0.312500 +v 0.000000 0.312500 0.125000 +v 0.000000 0.375000 0.062500 +v 0.000000 0.312500 0.062500 +v 0.000000 0.062500 0.625000 +v 0.000000 0.125000 0.562500 +v 0.000000 0.062500 0.562500 +v 0.000000 0.062500 0.875000 +v 0.000000 0.125000 0.812500 +v 0.000000 0.062500 0.812500 +v 0.000000 0.312500 0.625000 +v 0.000000 0.375000 0.562500 +v 0.000000 0.312500 0.562500 +v 0.000000 0.562500 0.125000 +v 0.000000 0.625000 0.062500 +v 0.000000 0.562500 0.062500 +v 0.000000 0.562500 0.375000 +v 0.000000 0.625000 0.312500 +v 0.000000 0.562500 0.312500 +v 0.000000 0.812500 0.125000 +v 0.000000 0.875000 0.062500 +v 0.000000 0.812500 0.062500 +v 0.187500 1.000000 0.125000 +v 0.187500 1.000000 0.062500 +v 0.125000 1.000000 0.062500 +v 0.437500 1.000000 0.375000 +v 0.437500 1.000000 0.312500 +v 0.375000 1.000000 0.312500 +v 0.437500 1.000000 0.125000 +v 0.437500 1.000000 0.062500 +v 0.375000 1.000000 0.062500 +v 0.687500 1.000000 0.625000 +v 0.687500 1.000000 0.562500 +v 0.625000 1.000000 0.562500 +v 0.937500 1.000000 0.875000 +v 0.937500 1.000000 0.812500 +v 0.875000 1.000000 0.812500 +v 0.937500 1.000000 0.625000 +v 0.937500 1.000000 0.562500 +v 0.875000 1.000000 0.562500 +v 0.687500 1.000000 0.125000 +v 0.687500 1.000000 0.062500 +v 0.625000 1.000000 0.062500 +v 0.937500 1.000000 0.375000 +v 0.937500 1.000000 0.312500 +v 0.875000 1.000000 0.312500 +v 0.937500 1.000000 0.125000 +v 0.937500 1.000000 0.062500 +v 0.875000 1.000000 0.062500 +v 0.062500 1.000000 0.187500 +v 0.125000 1.000000 0.187500 +v 0.062500 1.000000 0.125000 +v 0.062500 1.000000 0.437500 +v 0.125000 1.000000 0.437500 +v 0.062500 1.000000 0.375000 +v 0.312500 1.000000 0.437500 +v 0.375000 1.000000 0.437500 +v 0.312500 1.000000 0.375000 +v 0.062500 1.000000 0.687500 +v 0.125000 1.000000 0.687500 +v 0.062500 1.000000 0.625000 +v 0.062500 1.000000 0.937500 +v 0.125000 1.000000 0.937500 +v 0.062500 1.000000 0.875000 +v 0.312500 1.000000 0.937500 +v 0.375000 1.000000 0.937500 +v 0.312500 1.000000 0.875000 +v 0.562500 1.000000 0.687500 +v 0.625000 1.000000 0.687500 +v 0.562500 1.000000 0.625000 +v 0.562500 1.000000 0.937500 +v 0.625000 1.000000 0.937500 +v 0.562500 1.000000 0.875000 +v 0.812500 1.000000 0.937500 +v 0.875000 1.000000 0.937500 +v 0.812500 1.000000 0.875000 +v 1.000000 0.125000 0.062500 +v 1.000000 0.062500 0.125000 +v 1.000000 0.062500 0.062500 +v 1.000000 0.375000 0.062500 +v 1.000000 0.312500 0.125000 +v 1.000000 0.312500 0.062500 +v 1.000000 0.125000 0.312500 +v 1.000000 0.062500 0.375000 +v 1.000000 0.062500 0.312500 +v 1.000000 0.625000 0.062500 +v 1.000000 0.562500 0.125000 +v 1.000000 0.562500 0.062500 +v 1.000000 0.875000 0.062500 +v 1.000000 0.812500 0.125000 +v 1.000000 0.812500 0.062500 +v 1.000000 0.625000 0.312500 +v 1.000000 0.562500 0.375000 +v 1.000000 0.562500 0.312500 +v 1.000000 0.125000 0.562500 +v 1.000000 0.062500 0.625000 +v 1.000000 0.062500 0.562500 +v 1.000000 0.375000 0.562500 +v 1.000000 0.312500 0.625000 +v 1.000000 0.312500 0.562500 +v 1.000000 0.125000 0.812500 +v 1.000000 0.062500 0.875000 +v 1.000000 0.062500 0.812500 +v 1.000000 0.187500 0.875000 +v 1.000000 0.187500 0.937500 +v 1.000000 0.125000 0.937500 +v 1.000000 0.437500 0.625000 +v 1.000000 0.437500 0.687500 +v 1.000000 0.375000 0.687500 +v 1.000000 0.437500 0.875000 +v 1.000000 0.437500 0.937500 +v 1.000000 0.375000 0.937500 +v 1.000000 0.687500 0.375000 +v 1.000000 0.687500 0.437500 +v 1.000000 0.625000 0.437500 +v 1.000000 0.937500 0.125000 +v 1.000000 0.937500 0.187500 +v 1.000000 0.875000 0.187500 +v 1.000000 0.937500 0.375000 +v 1.000000 0.937500 0.437500 +v 1.000000 0.875000 0.437500 +v 1.000000 0.687500 0.875000 +v 1.000000 0.687500 0.937500 +v 1.000000 0.625000 0.937500 +v 1.000000 0.937500 0.625000 +v 1.000000 0.937500 0.687500 +v 1.000000 0.875000 0.687500 +v 1.000000 0.937500 0.875000 +v 1.000000 0.937500 0.937500 +v 1.000000 0.875000 0.937500 +v 0.187500 0.000000 0.062500 +v 0.187500 0.000000 0.125000 +v 0.125000 0.000000 0.062500 +v 0.437500 0.000000 0.062500 +v 0.437500 0.000000 0.125000 +v 0.375000 0.000000 0.062500 +v 0.437500 0.000000 0.312500 +v 0.437500 0.000000 0.375000 +v 0.375000 0.000000 0.312500 +v 0.687500 0.000000 0.062500 +v 0.687500 0.000000 0.125000 +v 0.625000 0.000000 0.062500 +v 0.937500 0.000000 0.062500 +v 0.937500 0.000000 0.125000 +v 0.875000 0.000000 0.062500 +v 0.937500 0.000000 0.312500 +v 0.937500 0.000000 0.375000 +v 0.875000 0.000000 0.312500 +v 0.687500 0.000000 0.562500 +v 0.687500 0.000000 0.625000 +v 0.625000 0.000000 0.562500 +v 0.937500 0.000000 0.562500 +v 0.937500 0.000000 0.625000 +v 0.875000 0.000000 0.562500 +v 0.937500 0.000000 0.812500 +v 0.937500 0.000000 0.875000 +v 0.875000 0.000000 0.812500 +v 0.125000 0.000000 0.187500 +v 0.062500 0.000000 0.187500 +v 0.062500 0.000000 0.125000 +v 0.375000 0.000000 0.437500 +v 0.312500 0.000000 0.437500 +v 0.312500 0.000000 0.375000 +v 0.125000 0.000000 0.437500 +v 0.062500 0.000000 0.437500 +v 0.062500 0.000000 0.375000 +v 0.625000 0.000000 0.687500 +v 0.562500 0.000000 0.687500 +v 0.562500 0.000000 0.625000 +v 0.875000 0.000000 0.937500 +v 0.812500 0.000000 0.937500 +v 0.812500 0.000000 0.875000 +v 0.625000 0.000000 0.937500 +v 0.562500 0.000000 0.937500 +v 0.562500 0.000000 0.875000 +v 0.125000 0.000000 0.687500 +v 0.062500 0.000000 0.687500 +v 0.062500 0.000000 0.625000 +v 0.375000 0.000000 0.937500 +v 0.312500 0.000000 0.937500 +v 0.312500 0.000000 0.875000 +v 0.125000 0.000000 0.937500 +v 0.062500 0.000000 0.937500 +v 0.062500 0.000000 0.875000 +v 0.187500 0.062500 1.000000 +v 0.187500 0.125000 1.000000 +v 0.125000 0.062500 1.000000 +v 0.437500 0.062500 1.000000 +v 0.437500 0.125000 1.000000 +v 0.375000 0.062500 1.000000 +v 0.437500 0.312500 1.000000 +v 0.437500 0.375000 1.000000 +v 0.375000 0.312500 1.000000 +v 0.687500 0.062500 1.000000 +v 0.687500 0.125000 1.000000 +v 0.625000 0.062500 1.000000 +v 0.937500 0.062500 1.000000 +v 0.937500 0.125000 1.000000 +v 0.875000 0.062500 1.000000 +v 0.937500 0.312500 1.000000 +v 0.937500 0.375000 1.000000 +v 0.875000 0.312500 1.000000 +v 0.687500 0.562500 1.000000 +v 0.687500 0.625000 1.000000 +v 0.625000 0.562500 1.000000 +v 0.937500 0.562500 1.000000 +v 0.937500 0.625000 1.000000 +v 0.875000 0.562500 1.000000 +v 0.937500 0.812500 1.000000 +v 0.937500 0.875000 1.000000 +v 0.875000 0.812500 1.000000 +v 0.125000 0.187500 1.000000 +v 0.062500 0.187500 1.000000 +v 0.062500 0.125000 1.000000 +v 0.375000 0.437500 1.000000 +v 0.312500 0.437500 1.000000 +v 0.312500 0.375000 1.000000 +v 0.125000 0.437500 1.000000 +v 0.062500 0.437500 1.000000 +v 0.062500 0.375000 1.000000 +v 0.625000 0.687500 1.000000 +v 0.562500 0.687500 1.000000 +v 0.562500 0.625000 1.000000 +v 0.875000 0.937500 1.000000 +v 0.812500 0.937500 1.000000 +v 0.812500 0.875000 1.000000 +v 0.625000 0.937500 1.000000 +v 0.562500 0.937500 1.000000 +v 0.562500 0.875000 1.000000 +v 0.125000 0.687500 1.000000 +v 0.062500 0.687500 1.000000 +v 0.062500 0.625000 1.000000 +v 0.375000 0.937500 1.000000 +v 0.312500 0.937500 1.000000 +v 0.312500 0.875000 1.000000 +v 0.125000 0.937500 1.000000 +v 0.062500 0.937500 1.000000 +v 0.062500 0.875000 1.000000 +v 0.750000 0.437500 0.000000 +v 0.687500 0.437500 0.000000 +v 0.750000 0.312500 0.000000 +v 0.687500 0.250000 0.000000 +v 0.562500 0.250000 0.000000 +v 0.562500 0.312500 0.000000 +v 0.312500 0.750000 0.000000 +v 0.250000 0.687500 0.000000 +v 0.437500 0.750000 0.000000 +v 0.437500 0.687500 0.000000 +v 0.312500 0.562500 0.000000 +v 0.250000 0.562500 0.000000 +v 0.000000 0.562500 0.750000 +v 0.000000 0.562500 0.687500 +v 0.000000 0.687500 0.750000 +v 0.000000 0.750000 0.687500 +v 0.000000 0.750000 0.562500 +v 0.000000 0.687500 0.562500 +v 0.000000 0.312500 0.437500 +v 0.000000 0.250000 0.437500 +v 0.000000 0.437500 0.312500 +v 0.000000 0.437500 0.250000 +v 0.000000 0.312500 0.250000 +v 0.000000 0.250000 0.312500 +v 0.750000 1.000000 0.437500 +v 0.687500 1.000000 0.437500 +v 0.750000 1.000000 0.312500 +v 0.687500 1.000000 0.250000 +v 0.562500 1.000000 0.250000 +v 0.562500 1.000000 0.312500 +v 0.312500 1.000000 0.750000 +v 0.250000 1.000000 0.687500 +v 0.437500 1.000000 0.750000 +v 0.437500 1.000000 0.687500 +v 0.312500 1.000000 0.562500 +v 0.250000 1.000000 0.562500 +v 1.000000 0.437500 0.312500 +v 1.000000 0.437500 0.250000 +v 1.000000 0.312500 0.437500 +v 1.000000 0.250000 0.437500 +v 1.000000 0.250000 0.312500 +v 1.000000 0.312500 0.250000 +v 1.000000 0.750000 0.562500 +v 1.000000 0.687500 0.562500 +v 1.000000 0.750000 0.687500 +v 1.000000 0.687500 0.750000 +v 1.000000 0.562500 0.750000 +v 1.000000 0.562500 0.687500 +v 0.750000 0.000000 0.312500 +v 0.687500 0.000000 0.250000 +v 0.750000 0.000000 0.437500 +v 0.687500 0.000000 0.437500 +v 0.562500 0.000000 0.312500 +v 0.562500 0.000000 0.250000 +v 0.437500 0.000000 0.750000 +v 0.437500 0.000000 0.687500 +v 0.312500 0.000000 0.750000 +v 0.250000 0.000000 0.687500 +v 0.250000 0.000000 0.562500 +v 0.312500 0.000000 0.562500 +v 0.750000 0.312500 1.000000 +v 0.687500 0.250000 1.000000 +v 0.750000 0.437500 1.000000 +v 0.687500 0.437500 1.000000 +v 0.562500 0.312500 1.000000 +v 0.562500 0.250000 1.000000 +v 0.437500 0.750000 1.000000 +v 0.437500 0.687500 1.000000 +v 0.312500 0.750000 1.000000 +v 0.250000 0.687500 1.000000 +v 0.250000 0.562500 1.000000 +v 0.312500 0.562500 1.000000 +v 0.312500 0.250000 0.000000 +v 0.250000 0.187500 0.000000 +v 0.437500 0.250000 0.000000 +v 0.437500 0.187500 0.000000 +v 0.312500 0.062500 0.000000 +v 0.250000 0.062500 0.000000 +v 0.812500 0.750000 0.000000 +v 0.750000 0.687500 0.000000 +v 0.937500 0.750000 0.000000 +v 0.937500 0.687500 0.000000 +v 0.812500 0.562500 0.000000 +v 0.750000 0.562500 0.000000 +v 0.812500 0.250000 0.000000 +v 0.750000 0.187500 0.000000 +v 0.937500 0.250000 0.000000 +v 0.937500 0.187500 0.000000 +v 0.812500 0.062500 0.000000 +v 0.750000 0.062500 0.000000 +v 0.062500 0.312500 0.000000 +v 0.062500 0.250000 0.000000 +v 0.187500 0.437500 0.000000 +v 0.250000 0.437500 0.000000 +v 0.250000 0.312500 0.000000 +v 0.187500 0.250000 0.000000 +v 0.062500 0.812500 0.000000 +v 0.062500 0.750000 0.000000 +v 0.187500 0.937500 0.000000 +v 0.250000 0.937500 0.000000 +v 0.250000 0.812500 0.000000 +v 0.187500 0.750000 0.000000 +v 0.562500 0.812500 0.000000 +v 0.562500 0.750000 0.000000 +v 0.687500 0.937500 0.000000 +v 0.750000 0.937500 0.000000 +v 0.750000 0.812500 0.000000 +v 0.687500 0.750000 0.000000 +v 0.000000 0.750000 0.312500 +v 0.000000 0.812500 0.250000 +v 0.000000 0.750000 0.437500 +v 0.000000 0.812500 0.437500 +v 0.000000 0.937500 0.312500 +v 0.000000 0.937500 0.250000 +v 0.000000 0.250000 0.812500 +v 0.000000 0.312500 0.750000 +v 0.000000 0.250000 0.937500 +v 0.000000 0.312500 0.937500 +v 0.000000 0.437500 0.812500 +v 0.000000 0.437500 0.750000 +v 0.000000 0.750000 0.812500 +v 0.000000 0.812500 0.750000 +v 0.000000 0.750000 0.937500 +v 0.000000 0.812500 0.937500 +v 0.000000 0.937500 0.812500 +v 0.000000 0.937500 0.750000 +v 0.000000 0.062500 0.250000 +v 0.000000 0.062500 0.187500 +v 0.000000 0.187500 0.250000 +v 0.000000 0.250000 0.187500 +v 0.000000 0.250000 0.062500 +v 0.000000 0.187500 0.062500 +v 0.000000 0.062500 0.750000 +v 0.000000 0.062500 0.687500 +v 0.000000 0.187500 0.750000 +v 0.000000 0.250000 0.687500 +v 0.000000 0.250000 0.562500 +v 0.000000 0.187500 0.562500 +v 0.000000 0.562500 0.250000 +v 0.000000 0.562500 0.187500 +v 0.000000 0.687500 0.250000 +v 0.000000 0.750000 0.187500 +v 0.000000 0.750000 0.062500 +v 0.000000 0.687500 0.062500 +v 0.312500 1.000000 0.250000 +v 0.250000 1.000000 0.187500 +v 0.437500 1.000000 0.250000 +v 0.437500 1.000000 0.187500 +v 0.312500 1.000000 0.062500 +v 0.250000 1.000000 0.062500 +v 0.812500 1.000000 0.750000 +v 0.750000 1.000000 0.687500 +v 0.937500 1.000000 0.750000 +v 0.937500 1.000000 0.687500 +v 0.812500 1.000000 0.562500 +v 0.750000 1.000000 0.562500 +v 0.812500 1.000000 0.250000 +v 0.750000 1.000000 0.187500 +v 0.937500 1.000000 0.250000 +v 0.937500 1.000000 0.187500 +v 0.812500 1.000000 0.062500 +v 0.750000 1.000000 0.062500 +v 0.062500 1.000000 0.312500 +v 0.062500 1.000000 0.250000 +v 0.187500 1.000000 0.437500 +v 0.250000 1.000000 0.437500 +v 0.250000 1.000000 0.312500 +v 0.187500 1.000000 0.250000 +v 0.062500 1.000000 0.812500 +v 0.062500 1.000000 0.750000 +v 0.187500 1.000000 0.937500 +v 0.250000 1.000000 0.937500 +v 0.250000 1.000000 0.812500 +v 0.187500 1.000000 0.750000 +v 0.562500 1.000000 0.812500 +v 0.562500 1.000000 0.750000 +v 0.687500 1.000000 0.937500 +v 0.750000 1.000000 0.937500 +v 0.750000 1.000000 0.812500 +v 0.687500 1.000000 0.750000 +v 1.000000 0.250000 0.062500 +v 1.000000 0.187500 0.062500 +v 1.000000 0.250000 0.187500 +v 1.000000 0.187500 0.250000 +v 1.000000 0.062500 0.250000 +v 1.000000 0.062500 0.187500 +v 1.000000 0.750000 0.062500 +v 1.000000 0.687500 0.062500 +v 1.000000 0.750000 0.187500 +v 1.000000 0.687500 0.250000 +v 1.000000 0.562500 0.250000 +v 1.000000 0.562500 0.187500 +v 1.000000 0.250000 0.562500 +v 1.000000 0.187500 0.562500 +v 1.000000 0.250000 0.687500 +v 1.000000 0.187500 0.750000 +v 1.000000 0.062500 0.750000 +v 1.000000 0.062500 0.687500 +v 1.000000 0.312500 0.750000 +v 1.000000 0.250000 0.812500 +v 1.000000 0.437500 0.750000 +v 1.000000 0.437500 0.812500 +v 1.000000 0.312500 0.937500 +v 1.000000 0.250000 0.937500 +v 1.000000 0.812500 0.250000 +v 1.000000 0.750000 0.312500 +v 1.000000 0.937500 0.250000 +v 1.000000 0.937500 0.312500 +v 1.000000 0.812500 0.437500 +v 1.000000 0.750000 0.437500 +v 1.000000 0.812500 0.750000 +v 1.000000 0.750000 0.812500 +v 1.000000 0.937500 0.750000 +v 1.000000 0.937500 0.812500 +v 1.000000 0.812500 0.937500 +v 1.000000 0.750000 0.937500 +v 0.312500 0.000000 0.062500 +v 0.250000 0.000000 0.062500 +v 0.437500 0.000000 0.187500 +v 0.437500 0.000000 0.250000 +v 0.312500 0.000000 0.250000 +v 0.250000 0.000000 0.187500 +v 0.812500 0.000000 0.062500 +v 0.750000 0.000000 0.062500 +v 0.937500 0.000000 0.187500 +v 0.937500 0.000000 0.250000 +v 0.812500 0.000000 0.250000 +v 0.750000 0.000000 0.187500 +v 0.812500 0.000000 0.562500 +v 0.750000 0.000000 0.562500 +v 0.937500 0.000000 0.687500 +v 0.937500 0.000000 0.750000 +v 0.812500 0.000000 0.750000 +v 0.750000 0.000000 0.687500 +v 0.250000 0.000000 0.312500 +v 0.187500 0.000000 0.250000 +v 0.250000 0.000000 0.437500 +v 0.187500 0.000000 0.437500 +v 0.062500 0.000000 0.312500 +v 0.062500 0.000000 0.250000 +v 0.750000 0.000000 0.812500 +v 0.687500 0.000000 0.750000 +v 0.750000 0.000000 0.937500 +v 0.687500 0.000000 0.937500 +v 0.562500 0.000000 0.812500 +v 0.562500 0.000000 0.750000 +v 0.250000 0.000000 0.812500 +v 0.187500 0.000000 0.750000 +v 0.250000 0.000000 0.937500 +v 0.187500 0.000000 0.937500 +v 0.062500 0.000000 0.812500 +v 0.062500 0.000000 0.750000 +v 0.312500 0.062500 1.000000 +v 0.250000 0.062500 1.000000 +v 0.437500 0.187500 1.000000 +v 0.437500 0.250000 1.000000 +v 0.312500 0.250000 1.000000 +v 0.250000 0.187500 1.000000 +v 0.812500 0.062500 1.000000 +v 0.750000 0.062500 1.000000 +v 0.937500 0.187500 1.000000 +v 0.937500 0.250000 1.000000 +v 0.812500 0.250000 1.000000 +v 0.750000 0.187500 1.000000 +v 0.812500 0.562500 1.000000 +v 0.750000 0.562500 1.000000 +v 0.937500 0.687500 1.000000 +v 0.937500 0.750000 1.000000 +v 0.812500 0.750000 1.000000 +v 0.750000 0.687500 1.000000 +v 0.250000 0.312500 1.000000 +v 0.187500 0.250000 1.000000 +v 0.250000 0.437500 1.000000 +v 0.187500 0.437500 1.000000 +v 0.062500 0.312500 1.000000 +v 0.062500 0.250000 1.000000 +v 0.750000 0.812500 1.000000 +v 0.687500 0.750000 1.000000 +v 0.750000 0.937500 1.000000 +v 0.687500 0.937500 1.000000 +v 0.562500 0.812500 1.000000 +v 0.562500 0.750000 1.000000 +v 0.250000 0.812500 1.000000 +v 0.187500 0.750000 1.000000 +v 0.250000 0.937500 1.000000 +v 0.187500 0.937500 1.000000 +v 0.062500 0.812500 1.000000 +v 0.062500 0.750000 1.000000 +v 0.562500 0.500000 0.000000 +v 0.500000 0.437500 0.000000 +v 0.687500 0.500000 0.000000 +v 0.500000 0.312500 0.000000 +v 0.812500 0.500000 0.000000 +v 0.937500 0.500000 0.000000 +v 0.937500 0.437500 0.000000 +v 0.812500 0.312500 0.000000 +v 0.500000 0.187500 0.000000 +v 0.687500 0.187500 0.000000 +v 0.562500 0.062500 0.000000 +v 0.500000 0.062500 0.000000 +v 0.062500 0.562500 0.000000 +v 0.062500 0.500000 0.000000 +v 0.187500 0.687500 0.000000 +v 0.187500 0.500000 0.000000 +v 0.312500 0.812500 0.000000 +v 0.437500 0.937500 0.000000 +v 0.500000 0.937500 0.000000 +v 0.500000 0.812500 0.000000 +v 0.312500 0.500000 0.000000 +v 0.500000 0.687500 0.000000 +v 0.500000 0.562500 0.000000 +v 0.437500 0.500000 0.000000 +v 0.000000 0.500000 0.562500 +v 0.000000 0.562500 0.500000 +v 0.000000 0.500000 0.687500 +v 0.000000 0.687500 0.500000 +v 0.000000 0.500000 0.812500 +v 0.000000 0.500000 0.937500 +v 0.000000 0.562500 0.937500 +v 0.000000 0.687500 0.812500 +v 0.000000 0.812500 0.500000 +v 0.000000 0.812500 0.687500 +v 0.000000 0.937500 0.562500 +v 0.000000 0.937500 0.500000 +v 0.000000 0.062500 0.500000 +v 0.000000 0.062500 0.437500 +v 0.000000 0.187500 0.500000 +v 0.000000 0.187500 0.312500 +v 0.000000 0.312500 0.500000 +v 0.000000 0.437500 0.500000 +v 0.000000 0.500000 0.437500 +v 0.000000 0.500000 0.312500 +v 0.000000 0.312500 0.187500 +v 0.000000 0.500000 0.187500 +v 0.000000 0.500000 0.062500 +v 0.000000 0.437500 0.062500 +v 0.562500 1.000000 0.500000 +v 0.500000 1.000000 0.437500 +v 0.687500 1.000000 0.500000 +v 0.500000 1.000000 0.312500 +v 0.812500 1.000000 0.500000 +v 0.937500 1.000000 0.500000 +v 0.937500 1.000000 0.437500 +v 0.812500 1.000000 0.312500 +v 0.500000 1.000000 0.187500 +v 0.687500 1.000000 0.187500 +v 0.562500 1.000000 0.062500 +v 0.500000 1.000000 0.062500 +v 0.062500 1.000000 0.562500 +v 0.062500 1.000000 0.500000 +v 0.187500 1.000000 0.687500 +v 0.187500 1.000000 0.500000 +v 0.312500 1.000000 0.812500 +v 0.437500 1.000000 0.937500 +v 0.500000 1.000000 0.937500 +v 0.500000 1.000000 0.812500 +v 0.312500 1.000000 0.500000 +v 0.500000 1.000000 0.687500 +v 0.500000 1.000000 0.562500 +v 0.437500 1.000000 0.500000 +v 1.000000 0.500000 0.062500 +v 1.000000 0.437500 0.062500 +v 1.000000 0.500000 0.187500 +v 1.000000 0.312500 0.187500 +v 1.000000 0.500000 0.312500 +v 1.000000 0.500000 0.437500 +v 1.000000 0.437500 0.500000 +v 1.000000 0.312500 0.500000 +v 1.000000 0.187500 0.312500 +v 1.000000 0.187500 0.500000 +v 1.000000 0.062500 0.500000 +v 1.000000 0.062500 0.437500 +v 1.000000 0.562500 0.500000 +v 1.000000 0.500000 0.562500 +v 1.000000 0.687500 0.500000 +v 1.000000 0.500000 0.687500 +v 1.000000 0.812500 0.500000 +v 1.000000 0.937500 0.500000 +v 1.000000 0.937500 0.562500 +v 1.000000 0.812500 0.687500 +v 1.000000 0.500000 0.812500 +v 1.000000 0.687500 0.812500 +v 1.000000 0.562500 0.937500 +v 1.000000 0.500000 0.937500 +v 0.562500 0.000000 0.062500 +v 0.500000 0.000000 0.062500 +v 0.687500 0.000000 0.187500 +v 0.500000 0.000000 0.187500 +v 0.812500 0.000000 0.312500 +v 0.937500 0.000000 0.437500 +v 0.937500 0.000000 0.500000 +v 0.812500 0.000000 0.500000 +v 0.500000 0.000000 0.312500 +v 0.687500 0.000000 0.500000 +v 0.562500 0.000000 0.500000 +v 0.500000 0.000000 0.437500 +v 0.500000 0.000000 0.562500 +v 0.437500 0.000000 0.500000 +v 0.500000 0.000000 0.687500 +v 0.312500 0.000000 0.500000 +v 0.500000 0.000000 0.812500 +v 0.500000 0.000000 0.937500 +v 0.437500 0.000000 0.937500 +v 0.312500 0.000000 0.812500 +v 0.187500 0.000000 0.500000 +v 0.187500 0.000000 0.687500 +v 0.062500 0.000000 0.562500 +v 0.062500 0.000000 0.500000 +v 0.562500 0.062500 1.000000 +v 0.500000 0.062500 1.000000 +v 0.687500 0.187500 1.000000 +v 0.500000 0.187500 1.000000 +v 0.812500 0.312500 1.000000 +v 0.937500 0.437500 1.000000 +v 0.937500 0.500000 1.000000 +v 0.812500 0.500000 1.000000 +v 0.500000 0.312500 1.000000 +v 0.687500 0.500000 1.000000 +v 0.562500 0.500000 1.000000 +v 0.500000 0.437500 1.000000 +v 0.500000 0.562500 1.000000 +v 0.437500 0.500000 1.000000 +v 0.500000 0.687500 1.000000 +v 0.312500 0.500000 1.000000 +v 0.500000 0.812500 1.000000 +v 0.500000 0.937500 1.000000 +v 0.437500 0.937500 1.000000 +v 0.312500 0.812500 1.000000 +v 0.187500 0.500000 1.000000 +v 0.187500 0.687500 1.000000 +v 0.062500 0.562500 1.000000 +v 0.062500 0.500000 1.000000 +v 0.062500 0.062500 0.000000 +v 0.062500 0.000000 0.000000 +v 0.187500 0.187500 0.000000 +v 0.187500 0.000000 0.000000 +v 0.312500 0.312500 0.000000 +v 0.437500 0.437500 0.000000 +v 0.312500 0.000000 0.000000 +v 0.437500 0.000000 0.000000 +v 0.562500 0.562500 0.000000 +v 0.687500 0.687500 0.000000 +v 0.812500 0.812500 0.000000 +v 0.937500 0.937500 0.000000 +v 1.000000 0.937500 0.000000 +v 1.000000 0.812500 0.000000 +v 1.000000 0.687500 0.000000 +v 1.000000 0.562500 0.000000 +v 0.562500 0.000000 0.000000 +v 0.687500 0.000000 0.000000 +v 1.000000 0.437500 0.000000 +v 1.000000 0.312500 0.000000 +v 0.812500 0.000000 0.000000 +v 1.000000 0.187500 0.000000 +v 1.000000 0.062500 0.000000 +v 0.937500 0.000000 0.000000 +v 0.000000 0.062500 0.000000 +v 0.000000 0.187500 0.000000 +v 0.000000 0.312500 0.000000 +v 0.000000 0.437500 0.000000 +v 0.000000 0.562500 0.000000 +v 0.000000 0.687500 0.000000 +v 0.000000 0.812500 0.000000 +v 0.000000 0.937500 0.000000 +v 0.062500 1.000000 0.000000 +v 0.187500 1.000000 0.000000 +v 0.312500 1.000000 0.000000 +v 0.437500 1.000000 0.000000 +v 0.562500 1.000000 0.000000 +v 0.687500 1.000000 0.000000 +v 0.812500 1.000000 0.000000 +v 0.937500 1.000000 0.000000 +v 0.000000 0.937500 0.062500 +v 0.000000 1.000000 0.062500 +v 0.000000 0.812500 0.187500 +v 0.000000 1.000000 0.187500 +v 0.000000 0.687500 0.312500 +v 0.000000 0.562500 0.437500 +v 0.000000 1.000000 0.312500 +v 0.000000 1.000000 0.437500 +v 0.000000 0.437500 0.562500 +v 0.000000 0.312500 0.687500 +v 0.000000 0.187500 0.812500 +v 0.000000 0.062500 0.937500 +v 0.000000 0.062500 1.000000 +v 0.000000 0.187500 1.000000 +v 0.000000 0.312500 1.000000 +v 0.000000 0.437500 1.000000 +v 0.000000 1.000000 0.562500 +v 0.000000 1.000000 0.687500 +v 0.000000 0.562500 1.000000 +v 0.000000 0.687500 1.000000 +v 0.000000 1.000000 0.812500 +v 0.000000 0.812500 1.000000 +v 0.000000 0.937500 1.000000 +v 0.000000 1.000000 0.937500 +v 0.000000 0.000000 0.062500 +v 0.000000 0.000000 0.187500 +v 0.000000 0.000000 0.312500 +v 0.000000 0.000000 0.437500 +v 0.000000 0.000000 0.562500 +v 0.000000 0.000000 0.687500 +v 0.000000 0.000000 0.812500 +v 0.000000 0.000000 0.937500 +v 0.062500 1.000000 0.062500 +v 0.187500 1.000000 0.187500 +v 0.312500 1.000000 0.312500 +v 0.437500 1.000000 0.437500 +v 0.562500 1.000000 0.562500 +v 0.687500 1.000000 0.687500 +v 0.812500 1.000000 0.812500 +v 0.937500 1.000000 0.937500 +v 1.000000 1.000000 0.937500 +v 1.000000 1.000000 0.812500 +v 1.000000 1.000000 0.687500 +v 1.000000 1.000000 0.562500 +v 1.000000 1.000000 0.437500 +v 1.000000 1.000000 0.312500 +v 1.000000 1.000000 0.187500 +v 1.000000 1.000000 0.062500 +v 0.062500 1.000000 1.000000 +v 0.187500 1.000000 1.000000 +v 0.312500 1.000000 1.000000 +v 0.437500 1.000000 1.000000 +v 0.562500 1.000000 1.000000 +v 0.687500 1.000000 1.000000 +v 0.812500 1.000000 1.000000 +v 0.937500 1.000000 1.000000 +v 1.000000 0.000000 0.062500 +v 1.000000 0.000000 0.187500 +v 1.000000 0.000000 0.312500 +v 1.000000 0.000000 0.437500 +v 1.000000 0.937500 0.062500 +v 1.000000 0.812500 0.187500 +v 1.000000 0.687500 0.312500 +v 1.000000 0.562500 0.437500 +v 1.000000 0.000000 0.562500 +v 1.000000 0.000000 0.687500 +v 1.000000 0.437500 0.562500 +v 1.000000 0.312500 0.687500 +v 1.000000 0.000000 0.812500 +v 1.000000 0.187500 0.812500 +v 1.000000 0.062500 0.937500 +v 1.000000 0.000000 0.937500 +v 1.000000 0.062500 1.000000 +v 1.000000 0.187500 1.000000 +v 1.000000 0.312500 1.000000 +v 1.000000 0.437500 1.000000 +v 1.000000 0.562500 1.000000 +v 1.000000 0.687500 1.000000 +v 1.000000 0.812500 1.000000 +v 1.000000 0.937500 1.000000 +v 0.062500 0.000000 0.062500 +v 0.187500 0.000000 0.187500 +v 0.312500 0.000000 0.312500 +v 0.437500 0.000000 0.437500 +v 0.562500 0.000000 0.562500 +v 0.687500 0.000000 0.687500 +v 0.812500 0.000000 0.812500 +v 0.937500 0.000000 0.937500 +v 0.937500 0.000000 1.000000 +v 0.812500 0.000000 1.000000 +v 0.687500 0.000000 1.000000 +v 0.562500 0.000000 1.000000 +v 0.437500 0.000000 1.000000 +v 0.312500 0.000000 1.000000 +v 0.187500 0.000000 1.000000 +v 0.062500 0.000000 1.000000 +v 0.062500 0.062500 1.000000 +v 0.187500 0.187500 1.000000 +v 0.312500 0.312500 1.000000 +v 0.437500 0.437500 1.000000 +v 0.562500 0.562500 1.000000 +v 0.687500 0.687500 1.000000 +v 0.812500 0.812500 1.000000 +v 0.937500 0.937500 1.000000 +# 1538 vertices, 0 vertices normals + +f 387 388 389 +f 390 391 392 +f 393 394 395 +f 396 397 398 +f 399 400 401 +f 402 403 404 +f 405 406 407 +f 408 409 410 +f 411 412 413 +f 414 415 416 +f 417 418 419 +f 420 421 422 +f 423 424 425 +f 426 427 428 +f 429 430 431 +f 432 433 434 +f 435 436 437 +f 438 439 440 +f 441 442 443 +f 444 445 446 +f 447 448 449 +f 450 451 452 +f 453 454 455 +f 456 457 458 +f 459 460 461 +f 462 463 464 +f 465 466 467 +f 468 469 470 +f 471 472 473 +f 474 475 476 +f 477 478 479 +f 480 481 482 +f 483 484 485 +f 486 487 488 +f 489 490 491 +f 492 493 494 +f 495 496 497 +f 498 499 500 +f 501 502 503 +f 504 505 506 +f 507 508 509 +f 510 511 512 +f 513 514 515 +f 516 517 518 +f 519 520 521 +f 522 523 524 +f 525 526 527 +f 528 529 530 +f 531 532 533 +f 534 535 536 +f 537 538 539 +f 540 541 542 +f 543 544 545 +f 546 547 548 +f 549 550 551 +f 552 553 554 +f 555 556 557 +f 558 559 560 +f 561 562 563 +f 564 565 566 +f 567 568 569 +f 570 571 572 +f 573 574 575 +f 576 577 578 +f 579 580 581 +f 582 583 584 +f 585 586 587 +f 588 589 590 +f 591 592 593 +f 594 595 596 +f 597 598 599 +f 600 601 602 +f 603 604 605 +f 606 607 608 +f 609 610 611 +f 612 613 614 +f 615 616 617 +f 618 619 620 +f 621 622 623 +f 624 625 626 +f 627 628 629 +f 630 631 632 +f 633 634 635 +f 636 637 638 +f 639 640 641 +f 642 643 644 +f 645 646 647 +f 648 649 650 +f 651 652 653 +f 654 655 656 +f 657 658 659 +f 660 661 662 +f 663 664 665 +f 666 667 668 +f 669 670 671 +f 672 673 674 +f 675 676 677 +f 678 679 680 +f 681 682 683 +f 684 685 686 +f 687 688 689 +f 690 691 692 +f 693 694 695 +f 696 697 698 +f 699 700 701 +f 702 703 704 +f 705 706 707 +f 708 709 710 +f 711 712 713 +f 714 715 716 +f 717 718 719 +f 720 721 722 +f 723 724 725 +f 726 727 728 +f 729 730 731 +f 732 733 734 +f 735 736 737 +f 738 739 740 +f 741 742 743 +f 744 745 746 +f 747 748 749 +f 750 751 752 +f 753 754 755 +f 756 757 758 +f 759 760 761 +f 762 763 764 +f 765 766 767 +f 768 769 770 +f 771 772 773 +f 774 775 776 +f 777 778 779 +f 780 781 782 +f 783 784 785 +f 786 787 788 +f 789 790 791 +f 792 793 794 +f 795 796 797 +f 798 799 800 +f 801 802 803 +f 804 805 806 +f 807 808 809 +f 810 811 812 +f 813 814 815 +f 816 817 818 +f 819 820 821 +f 822 823 824 +f 825 826 827 +f 828 829 830 +f 831 832 833 +f 834 835 836 +f 837 838 839 +f 840 841 842 +f 843 844 845 +f 846 847 848 +f 849 850 851 +f 852 853 854 +f 855 856 857 +f 858 859 860 +f 861 862 863 +f 864 865 866 +f 867 868 869 +f 870 871 872 +f 873 874 875 +f 876 877 878 +f 879 880 881 +f 882 883 884 +f 885 886 887 +f 888 889 890 +f 891 892 893 +f 894 895 896 +f 897 898 899 +f 900 901 902 +f 903 904 905 +f 906 907 908 +f 909 910 911 +f 912 913 914 +f 915 916 917 +f 918 919 920 +f 921 922 923 +f 924 925 926 +f 927 928 929 +f 930 931 932 +f 933 934 935 +f 936 937 938 +f 939 940 941 +f 942 943 944 +f 945 946 947 +f 948 949 950 +f 951 952 953 +f 954 955 956 +f 957 958 959 +f 960 961 962 +f 963 389 964 +f 965 966 387 +f 388 967 968 +f 969 392 970 +f 971 972 390 +f 391 973 974 +f 975 395 976 +f 977 978 393 +f 394 979 980 +f 981 398 982 +f 983 984 396 +f 397 985 986 +f 987 401 988 +f 989 990 399 +f 400 991 992 +f 993 404 994 +f 995 996 402 +f 403 997 998 +f 999 407 1000 +f 1001 1002 405 +f 406 1003 1004 +f 1005 410 1006 +f 1007 1008 408 +f 409 1009 1010 +f 1011 413 1012 +f 1013 1014 411 +f 412 1015 1016 +f 1017 416 1018 +f 1019 1020 414 +f 415 1021 1022 +f 1023 419 1024 +f 1025 1026 417 +f 418 1027 1028 +f 1029 422 1030 +f 1031 1032 420 +f 421 1033 1034 +f 1035 425 1036 +f 1037 1038 423 +f 424 1039 1040 +f 1041 428 1042 +f 1043 1044 426 +f 427 1045 1046 +f 1047 431 1048 +f 1049 1050 429 +f 430 1051 1052 +f 1053 434 1054 +f 1055 1056 432 +f 433 1057 1058 +f 1059 437 1060 +f 1061 1062 435 +f 436 1063 1064 +f 1065 440 1066 +f 1067 1068 438 +f 439 1069 1070 +f 1071 443 1072 +f 1073 1074 441 +f 442 1075 1076 +f 1077 446 1078 +f 1079 1080 444 +f 445 1081 1082 +f 1083 449 1084 +f 1085 1086 447 +f 448 1087 1088 +f 1089 452 1090 +f 1091 1092 450 +f 451 1093 1094 +f 1095 455 1096 +f 1097 1098 453 +f 454 1099 1100 +f 1101 458 1102 +f 1103 1104 456 +f 457 1105 1106 +f 1107 461 1108 +f 1109 1110 459 +f 460 1111 1112 +f 1113 464 1114 +f 1115 1116 462 +f 463 1117 1118 +f 1119 467 1120 +f 1121 1122 465 +f 466 1123 1124 +f 1125 470 1126 +f 1127 1128 468 +f 469 1129 1130 +f 1131 473 1132 +f 1133 1134 471 +f 472 1135 1136 +f 1137 476 1138 +f 1139 1140 474 +f 475 1141 1142 +f 1143 479 1144 +f 1145 1146 477 +f 478 1147 1148 +f 1149 482 1150 +f 1151 1152 480 +f 481 1153 1154 +f 1155 485 1156 +f 1157 1158 483 +f 484 1159 1160 +f 1161 488 1162 +f 1163 1164 486 +f 487 1165 1166 +f 1167 491 1168 +f 1169 1170 489 +f 490 1171 1172 +f 1173 494 1174 +f 1175 1176 492 +f 493 1177 1178 +f 1179 497 1180 +f 1181 1182 495 +f 496 1183 1184 +f 1185 500 1186 +f 1187 1188 498 +f 499 1189 1190 +f 1191 503 1192 +f 1193 1194 501 +f 502 1195 1196 +f 1197 506 1198 +f 1199 1200 504 +f 505 1201 1202 +f 1203 509 1204 +f 1205 1206 507 +f 508 1207 1208 +f 1209 512 1210 +f 1211 1212 510 +f 511 1213 1214 +f 1215 515 1216 +f 1217 1218 513 +f 514 1219 1220 +f 1221 518 1222 +f 1223 1224 516 +f 517 1225 1226 +f 1227 521 1228 +f 1229 1230 519 +f 520 1231 1232 +f 1233 524 1234 +f 1235 1236 522 +f 523 1237 1238 +f 1239 527 1240 +f 1241 1242 525 +f 526 1243 1244 +f 1245 530 1246 +f 1247 1248 528 +f 529 1249 1250 +f 1251 533 1252 +f 1253 964 531 +f 532 968 1254 +f 1255 536 963 +f 1256 1257 534 +f 535 1258 965 +f 967 539 1259 +f 966 1260 537 +f 538 1261 1262 +f 1263 542 1264 +f 1265 970 540 +f 541 974 1266 +f 1267 545 969 +f 1268 1269 543 +f 544 1270 971 +f 973 548 1271 +f 972 1272 546 +f 547 1273 1274 +f 1275 551 1276 +f 1277 976 549 +f 550 980 1278 +f 1279 554 975 +f 1280 1281 552 +f 553 1282 977 +f 979 557 1283 +f 978 1284 555 +f 556 1285 1286 +f 1287 560 1288 +f 1289 982 558 +f 559 986 1290 +f 1291 563 981 +f 1292 1293 561 +f 562 1294 983 +f 985 566 1295 +f 984 1296 564 +f 565 1297 1298 +f 1299 569 1300 +f 1301 988 567 +f 568 992 1302 +f 1303 572 987 +f 1304 1305 570 +f 571 1306 989 +f 991 575 1307 +f 990 1308 573 +f 574 1309 1310 +f 1311 578 1312 +f 1313 994 576 +f 577 998 1314 +f 1315 581 993 +f 1316 1317 579 +f 580 1318 995 +f 997 584 1319 +f 996 1320 582 +f 583 1321 1322 +f 1323 587 1324 +f 1325 1000 585 +f 586 1004 1326 +f 1327 590 999 +f 1328 1329 588 +f 589 1330 1001 +f 1003 593 1331 +f 1002 1332 591 +f 592 1333 1334 +f 1335 596 1336 +f 1337 1006 594 +f 595 1010 1338 +f 1339 599 1005 +f 1340 1341 597 +f 598 1342 1007 +f 1009 602 1343 +f 1008 1344 600 +f 601 1345 1346 +f 1347 605 1348 +f 1349 1012 603 +f 604 1016 1350 +f 1351 608 1011 +f 1352 1353 606 +f 607 1354 1013 +f 1015 611 1355 +f 1014 1356 609 +f 610 1357 1358 +f 1359 614 1360 +f 1361 1018 612 +f 613 1022 1362 +f 1363 617 1017 +f 1364 1365 615 +f 616 1366 1019 +f 1021 620 1367 +f 1020 1368 618 +f 619 1369 1370 +f 1371 623 1372 +f 1373 1024 621 +f 622 1028 1374 +f 1375 626 1023 +f 1376 1377 624 +f 625 1378 1025 +f 1027 629 1379 +f 1026 1380 627 +f 628 1381 1382 +f 1383 632 1384 +f 1385 1030 630 +f 631 1034 1386 +f 1387 635 1029 +f 1388 1389 633 +f 634 1390 1031 +f 1033 638 1391 +f 1032 1392 636 +f 637 1393 1394 +f 1395 641 1396 +f 1397 1036 639 +f 640 1040 1398 +f 1399 644 1035 +f 1400 1252 642 +f 643 1254 1037 +f 1039 647 1401 +f 1038 1259 645 +f 646 1262 1402 +f 1403 650 1251 +f 1404 1042 648 +f 649 1046 1253 +f 1405 653 1041 +f 1406 1407 651 +f 652 1408 1043 +f 1045 656 1255 +f 1044 1409 654 +f 655 1410 1256 +f 1261 659 1411 +f 1260 1048 657 +f 658 1052 1412 +f 1258 662 1047 +f 1257 1413 660 +f 661 1414 1049 +f 1051 665 1415 +f 1050 1416 663 +f 664 1417 1418 +f 1419 668 1395 +f 1420 1054 666 +f 667 1058 1397 +f 1421 671 1053 +f 1422 1264 669 +f 670 1266 1055 +f 1057 674 1399 +f 1056 1271 672 +f 673 1274 1400 +f 1423 677 1263 +f 1424 1060 675 +f 676 1064 1265 +f 1425 680 1059 +f 1426 1427 678 +f 679 1428 1061 +f 1063 683 1267 +f 1062 1429 681 +f 682 1430 1268 +f 1273 686 1403 +f 1272 1066 684 +f 685 1070 1404 +f 1270 689 1065 +f 1269 1431 687 +f 688 1432 1067 +f 1069 692 1405 +f 1068 1433 690 +f 691 1434 1406 +f 1435 695 1436 +f 1437 1072 693 +f 694 1076 1438 +f 1439 698 1071 +f 1440 1276 696 +f 697 1278 1073 +f 1075 701 1441 +f 1074 1283 699 +f 700 1286 1442 +f 1443 704 1275 +f 1444 1078 702 +f 703 1082 1277 +f 1445 707 1077 +f 1446 1447 705 +f 706 1448 1079 +f 1081 710 1279 +f 1080 1449 708 +f 709 1450 1280 +f 1285 713 1451 +f 1284 1084 711 +f 712 1088 1452 +f 1282 716 1083 +f 1281 1453 714 +f 715 1454 1085 +f 1087 719 1455 +f 1086 1456 717 +f 718 1457 1458 +f 1459 722 1419 +f 1460 1090 720 +f 721 1094 1420 +f 1461 725 1089 +f 1462 1288 723 +f 724 1290 1091 +f 1093 728 1421 +f 1092 1295 726 +f 727 1298 1422 +f 1463 731 1287 +f 1464 1096 729 +f 730 1100 1289 +f 1465 734 1095 +f 1466 1446 732 +f 733 1445 1097 +f 1099 737 1291 +f 1098 1444 735 +f 736 1443 1292 +f 1297 740 1423 +f 1296 1102 738 +f 739 1106 1424 +f 1294 743 1101 +f 1293 1440 741 +f 742 1439 1103 +f 1105 746 1425 +f 1104 1437 744 +f 745 1435 1426 +f 1467 749 1427 +f 1468 1108 747 +f 748 1112 1428 +f 1469 752 1107 +f 1470 1300 750 +f 751 1302 1109 +f 1111 755 1429 +f 1110 1307 753 +f 754 1310 1430 +f 1471 758 1299 +f 1472 1114 756 +f 757 1118 1301 +f 1473 761 1113 +f 1474 1475 759 +f 760 1476 1115 +f 1117 764 1303 +f 1116 1477 762 +f 763 1478 1304 +f 1309 767 1431 +f 1308 1120 765 +f 766 1124 1432 +f 1306 770 1119 +f 1305 1479 768 +f 769 1480 1121 +f 1123 773 1433 +f 1122 1481 771 +f 772 1482 1434 +f 1436 776 1467 +f 1438 1126 774 +f 775 1130 1468 +f 1441 779 1125 +f 1442 1312 777 +f 778 1314 1127 +f 1129 782 1469 +f 1128 1319 780 +f 781 1322 1470 +f 1451 785 1311 +f 1452 1132 783 +f 784 1136 1313 +f 1455 788 1131 +f 1458 1483 786 +f 787 1484 1133 +f 1135 791 1315 +f 1134 1485 789 +f 790 1486 1316 +f 1321 794 1471 +f 1320 1138 792 +f 793 1142 1472 +f 1318 797 1137 +f 1317 1487 795 +f 796 1488 1139 +f 1141 800 1473 +f 1140 1489 798 +f 799 1490 1474 +f 1417 803 1491 +f 1416 1144 801 +f 802 1148 1492 +f 1414 806 1143 +f 1413 1324 804 +f 805 1326 1145 +f 1147 809 1493 +f 1146 1331 807 +f 808 1334 1494 +f 1410 812 1323 +f 1409 1150 810 +f 811 1154 1325 +f 1408 815 1149 +f 1407 1495 813 +f 814 1496 1151 +f 1153 818 1327 +f 1152 1497 816 +f 817 1498 1328 +f 1333 821 1499 +f 1332 1156 819 +f 820 1160 1500 +f 1330 824 1155 +f 1329 1501 822 +f 823 1502 1157 +f 1159 827 1503 +f 1158 1504 825 +f 826 1505 1506 +f 1505 830 1507 +f 1504 1162 828 +f 829 1166 1508 +f 1502 833 1161 +f 1501 1336 831 +f 832 1338 1163 +f 1165 836 1509 +f 1164 1343 834 +f 835 1346 1510 +f 1498 839 1335 +f 1497 1168 837 +f 838 1172 1337 +f 1496 842 1167 +f 1495 1482 840 +f 841 1481 1169 +f 1171 845 1339 +f 1170 1480 843 +f 844 1479 1340 +f 1345 848 1511 +f 1344 1174 846 +f 847 1178 1512 +f 1342 851 1173 +f 1341 1478 849 +f 850 1477 1175 +f 1177 854 1513 +f 1176 1476 852 +f 853 1475 1514 +f 1396 857 1515 +f 1398 1180 855 +f 856 1184 1516 +f 1401 860 1179 +f 1402 1348 858 +f 859 1350 1181 +f 1183 863 1517 +f 1182 1355 861 +f 862 1358 1518 +f 1411 866 1347 +f 1412 1186 864 +f 865 1190 1349 +f 1415 869 1185 +f 1418 1491 867 +f 868 1492 1187 +f 1189 872 1351 +f 1188 1493 870 +f 871 1494 1352 +f 1357 875 1519 +f 1356 1192 873 +f 874 1196 1520 +f 1354 878 1191 +f 1353 1499 876 +f 877 1500 1193 +f 1195 881 1521 +f 1194 1503 879 +f 880 1506 1522 +f 1515 884 1459 +f 1516 1198 882 +f 883 1202 1460 +f 1517 887 1197 +f 1518 1360 885 +f 886 1362 1199 +f 1201 890 1461 +f 1200 1367 888 +f 889 1370 1462 +f 1519 893 1359 +f 1520 1204 891 +f 892 1208 1361 +f 1521 896 1203 +f 1522 1523 894 +f 895 1524 1205 +f 1207 899 1363 +f 1206 1525 897 +f 898 1526 1364 +f 1369 902 1463 +f 1368 1210 900 +f 901 1214 1464 +f 1366 905 1209 +f 1365 1527 903 +f 904 1528 1211 +f 1213 908 1465 +f 1212 1529 906 +f 907 1530 1466 +f 1530 911 1531 +f 1529 1216 909 +f 910 1220 1532 +f 1528 914 1215 +f 1527 1372 912 +f 913 1374 1217 +f 1219 917 1533 +f 1218 1379 915 +f 916 1382 1534 +f 1526 920 1371 +f 1525 1222 918 +f 919 1226 1373 +f 1524 923 1221 +f 1523 1507 921 +f 922 1508 1223 +f 1225 926 1375 +f 1224 1509 924 +f 925 1510 1376 +f 1381 929 1535 +f 1380 1228 927 +f 928 1232 1536 +f 1378 932 1227 +f 1377 1511 930 +f 931 1512 1229 +f 1231 935 1537 +f 1230 1513 933 +f 934 1514 1538 +f 1531 938 1447 +f 1532 1234 936 +f 937 1238 1448 +f 1533 941 1233 +f 1534 1384 939 +f 940 1386 1235 +f 1237 944 1449 +f 1236 1391 942 +f 943 1394 1450 +f 1535 947 1383 +f 1536 1240 945 +f 946 1244 1385 +f 1537 950 1239 +f 1538 1490 948 +f 949 1489 1241 +f 1243 953 1387 +f 1242 1488 951 +f 952 1487 1388 +f 1393 956 1453 +f 1392 1246 954 +f 955 1250 1454 +f 1390 959 1245 +f 1389 1486 957 +f 958 1485 1247 +f 1249 962 1456 +f 1248 1484 960 +f 961 1483 1457 +f 99 387 389 +f 387 100 388 +f 389 388 101 +f 102 390 392 +f 390 103 391 +f 392 391 104 +f 105 393 395 +f 393 106 394 +f 395 394 107 +f 108 396 398 +f 396 109 397 +f 398 397 110 +f 111 399 401 +f 399 112 400 +f 401 400 113 +f 114 402 404 +f 402 115 403 +f 404 403 116 +f 117 405 407 +f 405 118 406 +f 407 406 119 +f 120 408 410 +f 408 121 409 +f 410 409 122 +f 123 411 413 +f 411 124 412 +f 413 412 125 +f 126 414 416 +f 414 127 415 +f 416 415 128 +f 129 417 419 +f 417 130 418 +f 419 418 131 +f 132 420 422 +f 420 133 421 +f 422 421 134 +f 135 423 425 +f 423 136 424 +f 425 424 137 +f 138 426 428 +f 426 139 427 +f 428 427 140 +f 141 429 431 +f 429 142 430 +f 431 430 143 +f 144 432 434 +f 432 145 433 +f 434 433 146 +f 147 435 437 +f 435 148 436 +f 437 436 149 +f 150 438 440 +f 438 151 439 +f 440 439 152 +f 153 441 443 +f 441 154 442 +f 443 442 155 +f 156 444 446 +f 444 157 445 +f 446 445 158 +f 159 447 449 +f 447 160 448 +f 449 448 161 +f 162 450 452 +f 450 163 451 +f 452 451 164 +f 165 453 455 +f 453 166 454 +f 455 454 167 +f 168 456 458 +f 456 169 457 +f 458 457 170 +f 171 459 461 +f 459 172 460 +f 461 460 173 +f 174 462 464 +f 462 175 463 +f 464 463 176 +f 177 465 467 +f 465 178 466 +f 467 466 179 +f 180 468 470 +f 468 181 469 +f 470 469 182 +f 183 471 473 +f 471 184 472 +f 473 472 185 +f 186 474 476 +f 474 187 475 +f 476 475 188 +f 189 477 479 +f 477 190 478 +f 479 478 191 +f 192 480 482 +f 480 193 481 +f 482 481 194 +f 195 483 485 +f 483 196 484 +f 485 484 197 +f 198 486 488 +f 486 199 487 +f 488 487 200 +f 201 489 491 +f 489 202 490 +f 491 490 203 +f 204 492 494 +f 492 205 493 +f 494 493 206 +f 207 495 497 +f 495 208 496 +f 497 496 209 +f 210 498 500 +f 498 211 499 +f 500 499 212 +f 213 501 503 +f 501 214 502 +f 503 502 215 +f 216 504 506 +f 504 217 505 +f 506 505 218 +f 219 507 509 +f 507 220 508 +f 509 508 221 +f 222 510 512 +f 510 223 511 +f 512 511 224 +f 225 513 515 +f 513 226 514 +f 515 514 227 +f 228 516 518 +f 516 229 517 +f 518 517 230 +f 231 519 521 +f 519 232 520 +f 521 520 233 +f 234 522 524 +f 522 235 523 +f 524 523 236 +f 237 525 527 +f 525 238 526 +f 527 526 239 +f 240 528 530 +f 528 241 529 +f 530 529 242 +f 243 531 533 +f 531 101 532 +f 533 532 244 +f 245 534 536 +f 534 246 535 +f 536 535 99 +f 100 537 539 +f 537 247 538 +f 539 538 248 +f 249 540 542 +f 540 104 541 +f 542 541 250 +f 251 543 545 +f 543 252 544 +f 545 544 102 +f 103 546 548 +f 546 253 547 +f 548 547 254 +f 255 549 551 +f 549 107 550 +f 551 550 256 +f 257 552 554 +f 552 258 553 +f 554 553 105 +f 106 555 557 +f 555 259 556 +f 557 556 260 +f 261 558 560 +f 558 110 559 +f 560 559 262 +f 263 561 563 +f 561 264 562 +f 563 562 108 +f 109 564 566 +f 564 265 565 +f 566 565 266 +f 267 567 569 +f 567 113 568 +f 569 568 268 +f 269 570 572 +f 570 270 571 +f 572 571 111 +f 112 573 575 +f 573 271 574 +f 575 574 272 +f 273 576 578 +f 576 116 577 +f 578 577 274 +f 275 579 581 +f 579 276 580 +f 581 580 114 +f 115 582 584 +f 582 277 583 +f 584 583 278 +f 279 585 587 +f 585 119 586 +f 587 586 280 +f 281 588 590 +f 588 282 589 +f 590 589 117 +f 118 591 593 +f 591 283 592 +f 593 592 284 +f 285 594 596 +f 594 122 595 +f 596 595 286 +f 287 597 599 +f 597 288 598 +f 599 598 120 +f 121 600 602 +f 600 289 601 +f 602 601 290 +f 291 603 605 +f 603 125 604 +f 605 604 292 +f 293 606 608 +f 606 294 607 +f 608 607 123 +f 124 609 611 +f 609 295 610 +f 611 610 296 +f 297 612 614 +f 612 128 613 +f 614 613 298 +f 299 615 617 +f 615 300 616 +f 617 616 126 +f 127 618 620 +f 618 301 619 +f 620 619 302 +f 303 621 623 +f 621 131 622 +f 623 622 304 +f 305 624 626 +f 624 306 625 +f 626 625 129 +f 130 627 629 +f 627 307 628 +f 629 628 308 +f 309 630 632 +f 630 134 631 +f 632 631 310 +f 311 633 635 +f 633 312 634 +f 635 634 132 +f 133 636 638 +f 636 313 637 +f 638 637 314 +f 315 639 641 +f 639 137 640 +f 641 640 316 +f 317 642 644 +f 642 244 643 +f 644 643 135 +f 136 645 647 +f 645 248 646 +f 647 646 318 +f 319 648 650 +f 648 140 649 +f 650 649 243 +f 320 651 653 +f 651 321 652 +f 653 652 138 +f 139 654 656 +f 654 322 655 +f 656 655 245 +f 247 657 659 +f 657 143 658 +f 659 658 323 +f 246 660 662 +f 660 324 661 +f 662 661 141 +f 142 663 665 +f 663 325 664 +f 665 664 326 +f 327 666 668 +f 666 146 667 +f 668 667 315 +f 328 669 671 +f 669 250 670 +f 671 670 144 +f 145 672 674 +f 672 254 673 +f 674 673 317 +f 329 675 677 +f 675 149 676 +f 677 676 249 +f 330 678 680 +f 678 331 679 +f 680 679 147 +f 148 681 683 +f 681 332 682 +f 683 682 251 +f 253 684 686 +f 684 152 685 +f 686 685 319 +f 252 687 689 +f 687 333 688 +f 689 688 150 +f 151 690 692 +f 690 334 691 +f 692 691 320 +f 335 693 695 +f 693 155 694 +f 695 694 336 +f 337 696 698 +f 696 256 697 +f 698 697 153 +f 154 699 701 +f 699 260 700 +f 701 700 338 +f 339 702 704 +f 702 158 703 +f 704 703 255 +f 340 705 707 +f 705 341 706 +f 707 706 156 +f 157 708 710 +f 708 342 709 +f 710 709 257 +f 259 711 713 +f 711 161 712 +f 713 712 343 +f 258 714 716 +f 714 344 715 +f 716 715 159 +f 160 717 719 +f 717 345 718 +f 719 718 346 +f 347 720 722 +f 720 164 721 +f 722 721 327 +f 348 723 725 +f 723 262 724 +f 725 724 162 +f 163 726 728 +f 726 266 727 +f 728 727 328 +f 349 729 731 +f 729 167 730 +f 731 730 261 +f 350 732 734 +f 732 340 733 +f 734 733 165 +f 166 735 737 +f 735 339 736 +f 737 736 263 +f 265 738 740 +f 738 170 739 +f 740 739 329 +f 264 741 743 +f 741 337 742 +f 743 742 168 +f 169 744 746 +f 744 335 745 +f 746 745 330 +f 351 747 749 +f 747 173 748 +f 749 748 331 +f 352 750 752 +f 750 268 751 +f 752 751 171 +f 172 753 755 +f 753 272 754 +f 755 754 332 +f 353 756 758 +f 756 176 757 +f 758 757 267 +f 354 759 761 +f 759 355 760 +f 761 760 174 +f 175 762 764 +f 762 356 763 +f 764 763 269 +f 271 765 767 +f 765 179 766 +f 767 766 333 +f 270 768 770 +f 768 357 769 +f 770 769 177 +f 178 771 773 +f 771 358 772 +f 773 772 334 +f 336 774 776 +f 774 182 775 +f 776 775 351 +f 338 777 779 +f 777 274 778 +f 779 778 180 +f 181 780 782 +f 780 278 781 +f 782 781 352 +f 343 783 785 +f 783 185 784 +f 785 784 273 +f 346 786 788 +f 786 359 787 +f 788 787 183 +f 184 789 791 +f 789 360 790 +f 791 790 275 +f 277 792 794 +f 792 188 793 +f 794 793 353 +f 276 795 797 +f 795 361 796 +f 797 796 186 +f 187 798 800 +f 798 362 799 +f 800 799 354 +f 325 801 803 +f 801 191 802 +f 803 802 363 +f 324 804 806 +f 804 280 805 +f 806 805 189 +f 190 807 809 +f 807 284 808 +f 809 808 364 +f 322 810 812 +f 810 194 811 +f 812 811 279 +f 321 813 815 +f 813 365 814 +f 815 814 192 +f 193 816 818 +f 816 366 817 +f 818 817 281 +f 283 819 821 +f 819 197 820 +f 821 820 367 +f 282 822 824 +f 822 368 823 +f 824 823 195 +f 196 825 827 +f 825 369 826 +f 827 826 370 +f 369 828 830 +f 828 200 829 +f 830 829 371 +f 368 831 833 +f 831 286 832 +f 833 832 198 +f 199 834 836 +f 834 290 835 +f 836 835 372 +f 366 837 839 +f 837 203 838 +f 839 838 285 +f 365 840 842 +f 840 358 841 +f 842 841 201 +f 202 843 845 +f 843 357 844 +f 845 844 287 +f 289 846 848 +f 846 206 847 +f 848 847 373 +f 288 849 851 +f 849 356 850 +f 851 850 204 +f 205 852 854 +f 852 355 853 +f 854 853 374 +f 316 855 857 +f 855 209 856 +f 857 856 375 +f 318 858 860 +f 858 292 859 +f 860 859 207 +f 208 861 863 +f 861 296 862 +f 863 862 376 +f 323 864 866 +f 864 212 865 +f 866 865 291 +f 326 867 869 +f 867 363 868 +f 869 868 210 +f 211 870 872 +f 870 364 871 +f 872 871 293 +f 295 873 875 +f 873 215 874 +f 875 874 377 +f 294 876 878 +f 876 367 877 +f 878 877 213 +f 214 879 881 +f 879 370 880 +f 881 880 378 +f 375 882 884 +f 882 218 883 +f 884 883 347 +f 376 885 887 +f 885 298 886 +f 887 886 216 +f 217 888 890 +f 888 302 889 +f 890 889 348 +f 377 891 893 +f 891 221 892 +f 893 892 297 +f 378 894 896 +f 894 379 895 +f 896 895 219 +f 220 897 899 +f 897 380 898 +f 899 898 299 +f 301 900 902 +f 900 224 901 +f 902 901 349 +f 300 903 905 +f 903 381 904 +f 905 904 222 +f 223 906 908 +f 906 382 907 +f 908 907 350 +f 382 909 911 +f 909 227 910 +f 911 910 383 +f 381 912 914 +f 912 304 913 +f 914 913 225 +f 226 915 917 +f 915 308 916 +f 917 916 384 +f 380 918 920 +f 918 230 919 +f 920 919 303 +f 379 921 923 +f 921 371 922 +f 923 922 228 +f 229 924 926 +f 924 372 925 +f 926 925 305 +f 307 927 929 +f 927 233 928 +f 929 928 385 +f 306 930 932 +f 930 373 931 +f 932 931 231 +f 232 933 935 +f 933 374 934 +f 935 934 386 +f 383 936 938 +f 936 236 937 +f 938 937 341 +f 384 939 941 +f 939 310 940 +f 941 940 234 +f 235 942 944 +f 942 314 943 +f 944 943 342 +f 385 945 947 +f 945 239 946 +f 947 946 309 +f 386 948 950 +f 948 362 949 +f 950 949 237 +f 238 951 953 +f 951 361 952 +f 953 952 311 +f 313 954 956 +f 954 242 955 +f 956 955 344 +f 312 957 959 +f 957 360 958 +f 959 958 240 +f 241 960 962 +f 960 359 961 +f 962 961 345 +f 27 963 964 +f 963 99 389 +f 964 389 101 +f 99 965 387 +f 965 28 966 +f 387 966 100 +f 101 388 968 +f 388 100 967 +f 968 967 29 +f 30 969 970 +f 969 102 392 +f 970 392 104 +f 102 971 390 +f 971 31 972 +f 390 972 103 +f 104 391 974 +f 391 103 973 +f 974 973 32 +f 33 975 976 +f 975 105 395 +f 976 395 107 +f 105 977 393 +f 977 34 978 +f 393 978 106 +f 107 394 980 +f 394 106 979 +f 980 979 35 +f 36 981 982 +f 981 108 398 +f 982 398 110 +f 108 983 396 +f 983 37 984 +f 396 984 109 +f 110 397 986 +f 397 109 985 +f 986 985 38 +f 39 987 988 +f 987 111 401 +f 988 401 113 +f 111 989 399 +f 989 40 990 +f 399 990 112 +f 113 400 992 +f 400 112 991 +f 992 991 41 +f 42 993 994 +f 993 114 404 +f 994 404 116 +f 114 995 402 +f 995 43 996 +f 402 996 115 +f 116 403 998 +f 403 115 997 +f 998 997 44 +f 45 999 1000 +f 999 117 407 +f 1000 407 119 +f 117 1001 405 +f 1001 46 1002 +f 405 1002 118 +f 119 406 1004 +f 406 118 1003 +f 1004 1003 47 +f 48 1005 1006 +f 1005 120 410 +f 1006 410 122 +f 120 1007 408 +f 1007 49 1008 +f 408 1008 121 +f 122 409 1010 +f 409 121 1009 +f 1010 1009 50 +f 51 1011 1012 +f 1011 123 413 +f 1012 413 125 +f 123 1013 411 +f 1013 52 1014 +f 411 1014 124 +f 125 412 1016 +f 412 124 1015 +f 1016 1015 53 +f 54 1017 1018 +f 1017 126 416 +f 1018 416 128 +f 126 1019 414 +f 1019 55 1020 +f 414 1020 127 +f 128 415 1022 +f 415 127 1021 +f 1022 1021 56 +f 57 1023 1024 +f 1023 129 419 +f 1024 419 131 +f 129 1025 417 +f 1025 58 1026 +f 417 1026 130 +f 131 418 1028 +f 418 130 1027 +f 1028 1027 59 +f 60 1029 1030 +f 1029 132 422 +f 1030 422 134 +f 132 1031 420 +f 1031 61 1032 +f 420 1032 133 +f 134 421 1034 +f 421 133 1033 +f 1034 1033 62 +f 63 1035 1036 +f 1035 135 425 +f 1036 425 137 +f 135 1037 423 +f 1037 29 1038 +f 423 1038 136 +f 137 424 1040 +f 424 136 1039 +f 1040 1039 64 +f 65 1041 1042 +f 1041 138 428 +f 1042 428 140 +f 138 1043 426 +f 1043 66 1044 +f 426 1044 139 +f 140 427 1046 +f 427 139 1045 +f 1046 1045 27 +f 28 1047 1048 +f 1047 141 431 +f 1048 431 143 +f 141 1049 429 +f 1049 67 1050 +f 429 1050 142 +f 143 430 1052 +f 430 142 1051 +f 1052 1051 68 +f 69 1053 1054 +f 1053 144 434 +f 1054 434 146 +f 144 1055 432 +f 1055 32 1056 +f 432 1056 145 +f 146 433 1058 +f 433 145 1057 +f 1058 1057 63 +f 70 1059 1060 +f 1059 147 437 +f 1060 437 149 +f 147 1061 435 +f 1061 71 1062 +f 435 1062 148 +f 149 436 1064 +f 436 148 1063 +f 1064 1063 30 +f 31 1065 1066 +f 1065 150 440 +f 1066 440 152 +f 150 1067 438 +f 1067 72 1068 +f 438 1068 151 +f 152 439 1070 +f 439 151 1069 +f 1070 1069 65 +f 73 1071 1072 +f 1071 153 443 +f 1072 443 155 +f 153 1073 441 +f 1073 35 1074 +f 441 1074 154 +f 155 442 1076 +f 442 154 1075 +f 1076 1075 74 +f 75 1077 1078 +f 1077 156 446 +f 1078 446 158 +f 156 1079 444 +f 1079 76 1080 +f 444 1080 157 +f 158 445 1082 +f 445 157 1081 +f 1082 1081 33 +f 34 1083 1084 +f 1083 159 449 +f 1084 449 161 +f 159 1085 447 +f 1085 77 1086 +f 447 1086 160 +f 161 448 1088 +f 448 160 1087 +f 1088 1087 78 +f 79 1089 1090 +f 1089 162 452 +f 1090 452 164 +f 162 1091 450 +f 1091 38 1092 +f 450 1092 163 +f 164 451 1094 +f 451 163 1093 +f 1094 1093 69 +f 80 1095 1096 +f 1095 165 455 +f 1096 455 167 +f 165 1097 453 +f 1097 75 1098 +f 453 1098 166 +f 167 454 1100 +f 454 166 1099 +f 1100 1099 36 +f 37 1101 1102 +f 1101 168 458 +f 1102 458 170 +f 168 1103 456 +f 1103 73 1104 +f 456 1104 169 +f 170 457 1106 +f 457 169 1105 +f 1106 1105 70 +f 81 1107 1108 +f 1107 171 461 +f 1108 461 173 +f 171 1109 459 +f 1109 41 1110 +f 459 1110 172 +f 173 460 1112 +f 460 172 1111 +f 1112 1111 71 +f 82 1113 1114 +f 1113 174 464 +f 1114 464 176 +f 174 1115 462 +f 1115 83 1116 +f 462 1116 175 +f 176 463 1118 +f 463 175 1117 +f 1118 1117 39 +f 40 1119 1120 +f 1119 177 467 +f 1120 467 179 +f 177 1121 465 +f 1121 84 1122 +f 465 1122 178 +f 179 466 1124 +f 466 178 1123 +f 1124 1123 72 +f 74 1125 1126 +f 1125 180 470 +f 1126 470 182 +f 180 1127 468 +f 1127 44 1128 +f 468 1128 181 +f 182 469 1130 +f 469 181 1129 +f 1130 1129 81 +f 78 1131 1132 +f 1131 183 473 +f 1132 473 185 +f 183 1133 471 +f 1133 85 1134 +f 471 1134 184 +f 185 472 1136 +f 472 184 1135 +f 1136 1135 42 +f 43 1137 1138 +f 1137 186 476 +f 1138 476 188 +f 186 1139 474 +f 1139 86 1140 +f 474 1140 187 +f 188 475 1142 +f 475 187 1141 +f 1142 1141 82 +f 67 1143 1144 +f 1143 189 479 +f 1144 479 191 +f 189 1145 477 +f 1145 47 1146 +f 477 1146 190 +f 191 478 1148 +f 478 190 1147 +f 1148 1147 87 +f 66 1149 1150 +f 1149 192 482 +f 1150 482 194 +f 192 1151 480 +f 1151 88 1152 +f 480 1152 193 +f 194 481 1154 +f 481 193 1153 +f 1154 1153 45 +f 46 1155 1156 +f 1155 195 485 +f 1156 485 197 +f 195 1157 483 +f 1157 89 1158 +f 483 1158 196 +f 197 484 1160 +f 484 196 1159 +f 1160 1159 90 +f 89 1161 1162 +f 1161 198 488 +f 1162 488 200 +f 198 1163 486 +f 1163 50 1164 +f 486 1164 199 +f 200 487 1166 +f 487 199 1165 +f 1166 1165 91 +f 88 1167 1168 +f 1167 201 491 +f 1168 491 203 +f 201 1169 489 +f 1169 84 1170 +f 489 1170 202 +f 203 490 1172 +f 490 202 1171 +f 1172 1171 48 +f 49 1173 1174 +f 1173 204 494 +f 1174 494 206 +f 204 1175 492 +f 1175 83 1176 +f 492 1176 205 +f 206 493 1178 +f 493 205 1177 +f 1178 1177 92 +f 64 1179 1180 +f 1179 207 497 +f 1180 497 209 +f 207 1181 495 +f 1181 53 1182 +f 495 1182 208 +f 209 496 1184 +f 496 208 1183 +f 1184 1183 93 +f 68 1185 1186 +f 1185 210 500 +f 1186 500 212 +f 210 1187 498 +f 1187 87 1188 +f 498 1188 211 +f 212 499 1190 +f 499 211 1189 +f 1190 1189 51 +f 52 1191 1192 +f 1191 213 503 +f 1192 503 215 +f 213 1193 501 +f 1193 90 1194 +f 501 1194 214 +f 215 502 1196 +f 502 214 1195 +f 1196 1195 94 +f 93 1197 1198 +f 1197 216 506 +f 1198 506 218 +f 216 1199 504 +f 1199 56 1200 +f 504 1200 217 +f 218 505 1202 +f 505 217 1201 +f 1202 1201 79 +f 94 1203 1204 +f 1203 219 509 +f 1204 509 221 +f 219 1205 507 +f 1205 95 1206 +f 507 1206 220 +f 221 508 1208 +f 508 220 1207 +f 1208 1207 54 +f 55 1209 1210 +f 1209 222 512 +f 1210 512 224 +f 222 1211 510 +f 1211 96 1212 +f 510 1212 223 +f 224 511 1214 +f 511 223 1213 +f 1214 1213 80 +f 96 1215 1216 +f 1215 225 515 +f 1216 515 227 +f 225 1217 513 +f 1217 59 1218 +f 513 1218 226 +f 227 514 1220 +f 514 226 1219 +f 1220 1219 97 +f 95 1221 1222 +f 1221 228 518 +f 1222 518 230 +f 228 1223 516 +f 1223 91 1224 +f 516 1224 229 +f 230 517 1226 +f 517 229 1225 +f 1226 1225 57 +f 58 1227 1228 +f 1227 231 521 +f 1228 521 233 +f 231 1229 519 +f 1229 92 1230 +f 519 1230 232 +f 233 520 1232 +f 520 232 1231 +f 1232 1231 98 +f 97 1233 1234 +f 1233 234 524 +f 1234 524 236 +f 234 1235 522 +f 1235 62 1236 +f 522 1236 235 +f 236 523 1238 +f 523 235 1237 +f 1238 1237 76 +f 98 1239 1240 +f 1239 237 527 +f 1240 527 239 +f 237 1241 525 +f 1241 86 1242 +f 525 1242 238 +f 239 526 1244 +f 526 238 1243 +f 1244 1243 60 +f 61 1245 1246 +f 1245 240 530 +f 1246 530 242 +f 240 1247 528 +f 1247 85 1248 +f 528 1248 241 +f 242 529 1250 +f 529 241 1249 +f 1250 1249 77 +f 9 1251 1252 +f 1251 243 533 +f 1252 533 244 +f 243 1253 531 +f 1253 27 964 +f 531 964 101 +f 244 532 1254 +f 532 101 968 +f 1254 968 29 +f 27 1255 963 +f 1255 245 536 +f 963 536 99 +f 245 1256 534 +f 1256 10 1257 +f 534 1257 246 +f 99 535 965 +f 535 246 1258 +f 965 1258 28 +f 29 967 1259 +f 967 100 539 +f 1259 539 248 +f 100 966 537 +f 966 28 1260 +f 537 1260 247 +f 248 538 1262 +f 538 247 1261 +f 1262 1261 11 +f 12 1263 1264 +f 1263 249 542 +f 1264 542 250 +f 249 1265 540 +f 1265 30 970 +f 540 970 104 +f 250 541 1266 +f 541 104 974 +f 1266 974 32 +f 30 1267 969 +f 1267 251 545 +f 969 545 102 +f 251 1268 543 +f 1268 13 1269 +f 543 1269 252 +f 102 544 971 +f 544 252 1270 +f 971 1270 31 +f 32 973 1271 +f 973 103 548 +f 1271 548 254 +f 103 972 546 +f 972 31 1272 +f 546 1272 253 +f 254 547 1274 +f 547 253 1273 +f 1274 1273 9 +f 14 1275 1276 +f 1275 255 551 +f 1276 551 256 +f 255 1277 549 +f 1277 33 976 +f 549 976 107 +f 256 550 1278 +f 550 107 980 +f 1278 980 35 +f 33 1279 975 +f 1279 257 554 +f 975 554 105 +f 257 1280 552 +f 1280 15 1281 +f 552 1281 258 +f 105 553 977 +f 553 258 1282 +f 977 1282 34 +f 35 979 1283 +f 979 106 557 +f 1283 557 260 +f 106 978 555 +f 978 34 1284 +f 555 1284 259 +f 260 556 1286 +f 556 259 1285 +f 1286 1285 16 +f 17 1287 1288 +f 1287 261 560 +f 1288 560 262 +f 261 1289 558 +f 1289 36 982 +f 558 982 110 +f 262 559 1290 +f 559 110 986 +f 1290 986 38 +f 36 1291 981 +f 1291 263 563 +f 981 563 108 +f 263 1292 561 +f 1292 14 1293 +f 561 1293 264 +f 108 562 983 +f 562 264 1294 +f 983 1294 37 +f 38 985 1295 +f 985 109 566 +f 1295 566 266 +f 109 984 564 +f 984 37 1296 +f 564 1296 265 +f 266 565 1298 +f 565 265 1297 +f 1298 1297 12 +f 18 1299 1300 +f 1299 267 569 +f 1300 569 268 +f 267 1301 567 +f 1301 39 988 +f 567 988 113 +f 268 568 1302 +f 568 113 992 +f 1302 992 41 +f 39 1303 987 +f 1303 269 572 +f 987 572 111 +f 269 1304 570 +f 1304 19 1305 +f 570 1305 270 +f 111 571 989 +f 571 270 1306 +f 989 1306 40 +f 41 991 1307 +f 991 112 575 +f 1307 575 272 +f 112 990 573 +f 990 40 1308 +f 573 1308 271 +f 272 574 1310 +f 574 271 1309 +f 1310 1309 13 +f 16 1311 1312 +f 1311 273 578 +f 1312 578 274 +f 273 1313 576 +f 1313 42 994 +f 576 994 116 +f 274 577 1314 +f 577 116 998 +f 1314 998 44 +f 42 1315 993 +f 1315 275 581 +f 993 581 114 +f 275 1316 579 +f 1316 20 1317 +f 579 1317 276 +f 114 580 995 +f 580 276 1318 +f 995 1318 43 +f 44 997 1319 +f 997 115 584 +f 1319 584 278 +f 115 996 582 +f 996 43 1320 +f 582 1320 277 +f 278 583 1322 +f 583 277 1321 +f 1322 1321 18 +f 10 1323 1324 +f 1323 279 587 +f 1324 587 280 +f 279 1325 585 +f 1325 45 1000 +f 585 1000 119 +f 280 586 1326 +f 586 119 1004 +f 1326 1004 47 +f 45 1327 999 +f 1327 281 590 +f 999 590 117 +f 281 1328 588 +f 1328 21 1329 +f 588 1329 282 +f 117 589 1001 +f 589 282 1330 +f 1001 1330 46 +f 47 1003 1331 +f 1003 118 593 +f 1331 593 284 +f 118 1002 591 +f 1002 46 1332 +f 591 1332 283 +f 284 592 1334 +f 592 283 1333 +f 1334 1333 22 +f 21 1335 1336 +f 1335 285 596 +f 1336 596 286 +f 285 1337 594 +f 1337 48 1006 +f 594 1006 122 +f 286 595 1338 +f 595 122 1010 +f 1338 1010 50 +f 48 1339 1005 +f 1339 287 599 +f 1005 599 120 +f 287 1340 597 +f 1340 19 1341 +f 597 1341 288 +f 120 598 1007 +f 598 288 1342 +f 1007 1342 49 +f 50 1009 1343 +f 1009 121 602 +f 1343 602 290 +f 121 1008 600 +f 1008 49 1344 +f 600 1344 289 +f 290 601 1346 +f 601 289 1345 +f 1346 1345 23 +f 11 1347 1348 +f 1347 291 605 +f 1348 605 292 +f 291 1349 603 +f 1349 51 1012 +f 603 1012 125 +f 292 604 1350 +f 604 125 1016 +f 1350 1016 53 +f 51 1351 1011 +f 1351 293 608 +f 1011 608 123 +f 293 1352 606 +f 1352 22 1353 +f 606 1353 294 +f 123 607 1013 +f 607 294 1354 +f 1013 1354 52 +f 53 1015 1355 +f 1015 124 611 +f 1355 611 296 +f 124 1014 609 +f 1014 52 1356 +f 609 1356 295 +f 296 610 1358 +f 610 295 1357 +f 1358 1357 24 +f 24 1359 1360 +f 1359 297 614 +f 1360 614 298 +f 297 1361 612 +f 1361 54 1018 +f 612 1018 128 +f 298 613 1362 +f 613 128 1022 +f 1362 1022 56 +f 54 1363 1017 +f 1363 299 617 +f 1017 617 126 +f 299 1364 615 +f 1364 25 1365 +f 615 1365 300 +f 126 616 1019 +f 616 300 1366 +f 1019 1366 55 +f 56 1021 1367 +f 1021 127 620 +f 1367 620 302 +f 127 1020 618 +f 1020 55 1368 +f 618 1368 301 +f 302 619 1370 +f 619 301 1369 +f 1370 1369 17 +f 25 1371 1372 +f 1371 303 623 +f 1372 623 304 +f 303 1373 621 +f 1373 57 1024 +f 621 1024 131 +f 304 622 1374 +f 622 131 1028 +f 1374 1028 59 +f 57 1375 1023 +f 1375 305 626 +f 1023 626 129 +f 305 1376 624 +f 1376 23 1377 +f 624 1377 306 +f 129 625 1025 +f 625 306 1378 +f 1025 1378 58 +f 59 1027 1379 +f 1027 130 629 +f 1379 629 308 +f 130 1026 627 +f 1026 58 1380 +f 627 1380 307 +f 308 628 1382 +f 628 307 1381 +f 1382 1381 26 +f 26 1383 1384 +f 1383 309 632 +f 1384 632 310 +f 309 1385 630 +f 1385 60 1030 +f 630 1030 134 +f 310 631 1386 +f 631 134 1034 +f 1386 1034 62 +f 60 1387 1029 +f 1387 311 635 +f 1029 635 132 +f 311 1388 633 +f 1388 20 1389 +f 633 1389 312 +f 132 634 1031 +f 634 312 1390 +f 1031 1390 61 +f 62 1033 1391 +f 1033 133 638 +f 1391 638 314 +f 133 1032 636 +f 1032 61 1392 +f 636 1392 313 +f 314 637 1394 +f 637 313 1393 +f 1394 1393 15 +f 1 1395 1396 +f 1395 315 641 +f 1396 641 316 +f 315 1397 639 +f 1397 63 1036 +f 639 1036 137 +f 316 640 1398 +f 640 137 1040 +f 1398 1040 64 +f 63 1399 1035 +f 1399 317 644 +f 1035 644 135 +f 317 1400 642 +f 1400 9 1252 +f 642 1252 244 +f 135 643 1037 +f 643 244 1254 +f 1037 1254 29 +f 64 1039 1401 +f 1039 136 647 +f 1401 647 318 +f 136 1038 645 +f 1038 29 1259 +f 645 1259 248 +f 318 646 1402 +f 646 248 1262 +f 1402 1262 11 +f 9 1403 1251 +f 1403 319 650 +f 1251 650 243 +f 319 1404 648 +f 1404 65 1042 +f 648 1042 140 +f 243 649 1253 +f 649 140 1046 +f 1253 1046 27 +f 65 1405 1041 +f 1405 320 653 +f 1041 653 138 +f 320 1406 651 +f 1406 7 1407 +f 651 1407 321 +f 138 652 1043 +f 652 321 1408 +f 1043 1408 66 +f 27 1045 1255 +f 1045 139 656 +f 1255 656 245 +f 139 1044 654 +f 1044 66 1409 +f 654 1409 322 +f 245 655 1256 +f 655 322 1410 +f 1256 1410 10 +f 11 1261 1411 +f 1261 247 659 +f 1411 659 323 +f 247 1260 657 +f 1260 28 1048 +f 657 1048 143 +f 323 658 1412 +f 658 143 1052 +f 1412 1052 68 +f 28 1258 1047 +f 1258 246 662 +f 1047 662 141 +f 246 1257 660 +f 1257 10 1413 +f 660 1413 324 +f 141 661 1049 +f 661 324 1414 +f 1049 1414 67 +f 68 1051 1415 +f 1051 142 665 +f 1415 665 326 +f 142 1050 663 +f 1050 67 1416 +f 663 1416 325 +f 326 664 1418 +f 664 325 1417 +f 1418 1417 5 +f 1 1419 1395 +f 1419 327 668 +f 1395 668 315 +f 327 1420 666 +f 1420 69 1054 +f 666 1054 146 +f 315 667 1397 +f 667 146 1058 +f 1397 1058 63 +f 69 1421 1053 +f 1421 328 671 +f 1053 671 144 +f 328 1422 669 +f 1422 12 1264 +f 669 1264 250 +f 144 670 1055 +f 670 250 1266 +f 1055 1266 32 +f 63 1057 1399 +f 1057 145 674 +f 1399 674 317 +f 145 1056 672 +f 1056 32 1271 +f 672 1271 254 +f 317 673 1400 +f 673 254 1274 +f 1400 1274 9 +f 12 1423 1263 +f 1423 329 677 +f 1263 677 249 +f 329 1424 675 +f 1424 70 1060 +f 675 1060 149 +f 249 676 1265 +f 676 149 1064 +f 1265 1064 30 +f 70 1425 1059 +f 1425 330 680 +f 1059 680 147 +f 330 1426 678 +f 1426 3 1427 +f 678 1427 331 +f 147 679 1061 +f 679 331 1428 +f 1061 1428 71 +f 30 1063 1267 +f 1063 148 683 +f 1267 683 251 +f 148 1062 681 +f 1062 71 1429 +f 681 1429 332 +f 251 682 1268 +f 682 332 1430 +f 1268 1430 13 +f 9 1273 1403 +f 1273 253 686 +f 1403 686 319 +f 253 1272 684 +f 1272 31 1066 +f 684 1066 152 +f 319 685 1404 +f 685 152 1070 +f 1404 1070 65 +f 31 1270 1065 +f 1270 252 689 +f 1065 689 150 +f 252 1269 687 +f 1269 13 1431 +f 687 1431 333 +f 150 688 1067 +f 688 333 1432 +f 1067 1432 72 +f 65 1069 1405 +f 1069 151 692 +f 1405 692 320 +f 151 1068 690 +f 1068 72 1433 +f 690 1433 334 +f 320 691 1406 +f 691 334 1434 +f 1406 1434 7 +f 3 1435 1436 +f 1435 335 695 +f 1436 695 336 +f 335 1437 693 +f 1437 73 1072 +f 693 1072 155 +f 336 694 1438 +f 694 155 1076 +f 1438 1076 74 +f 73 1439 1071 +f 1439 337 698 +f 1071 698 153 +f 337 1440 696 +f 1440 14 1276 +f 696 1276 256 +f 153 697 1073 +f 697 256 1278 +f 1073 1278 35 +f 74 1075 1441 +f 1075 154 701 +f 1441 701 338 +f 154 1074 699 +f 1074 35 1283 +f 699 1283 260 +f 338 700 1442 +f 700 260 1286 +f 1442 1286 16 +f 14 1443 1275 +f 1443 339 704 +f 1275 704 255 +f 339 1444 702 +f 1444 75 1078 +f 702 1078 158 +f 255 703 1277 +f 703 158 1082 +f 1277 1082 33 +f 75 1445 1077 +f 1445 340 707 +f 1077 707 156 +f 340 1446 705 +f 1446 2 1447 +f 705 1447 341 +f 156 706 1079 +f 706 341 1448 +f 1079 1448 76 +f 33 1081 1279 +f 1081 157 710 +f 1279 710 257 +f 157 1080 708 +f 1080 76 1449 +f 708 1449 342 +f 257 709 1280 +f 709 342 1450 +f 1280 1450 15 +f 16 1285 1451 +f 1285 259 713 +f 1451 713 343 +f 259 1284 711 +f 1284 34 1084 +f 711 1084 161 +f 343 712 1452 +f 712 161 1088 +f 1452 1088 78 +f 34 1282 1083 +f 1282 258 716 +f 1083 716 159 +f 258 1281 714 +f 1281 15 1453 +f 714 1453 344 +f 159 715 1085 +f 715 344 1454 +f 1085 1454 77 +f 78 1087 1455 +f 1087 160 719 +f 1455 719 346 +f 160 1086 717 +f 1086 77 1456 +f 717 1456 345 +f 346 718 1458 +f 718 345 1457 +f 1458 1457 4 +f 1 1459 1419 +f 1459 347 722 +f 1419 722 327 +f 347 1460 720 +f 1460 79 1090 +f 720 1090 164 +f 327 721 1420 +f 721 164 1094 +f 1420 1094 69 +f 79 1461 1089 +f 1461 348 725 +f 1089 725 162 +f 348 1462 723 +f 1462 17 1288 +f 723 1288 262 +f 162 724 1091 +f 724 262 1290 +f 1091 1290 38 +f 69 1093 1421 +f 1093 163 728 +f 1421 728 328 +f 163 1092 726 +f 1092 38 1295 +f 726 1295 266 +f 328 727 1422 +f 727 266 1298 +f 1422 1298 12 +f 17 1463 1287 +f 1463 349 731 +f 1287 731 261 +f 349 1464 729 +f 1464 80 1096 +f 729 1096 167 +f 261 730 1289 +f 730 167 1100 +f 1289 1100 36 +f 80 1465 1095 +f 1465 350 734 +f 1095 734 165 +f 350 1466 732 +f 1466 2 1446 +f 732 1446 340 +f 165 733 1097 +f 733 340 1445 +f 1097 1445 75 +f 36 1099 1291 +f 1099 166 737 +f 1291 737 263 +f 166 1098 735 +f 1098 75 1444 +f 735 1444 339 +f 263 736 1292 +f 736 339 1443 +f 1292 1443 14 +f 12 1297 1423 +f 1297 265 740 +f 1423 740 329 +f 265 1296 738 +f 1296 37 1102 +f 738 1102 170 +f 329 739 1424 +f 739 170 1106 +f 1424 1106 70 +f 37 1294 1101 +f 1294 264 743 +f 1101 743 168 +f 264 1293 741 +f 1293 14 1440 +f 741 1440 337 +f 168 742 1103 +f 742 337 1439 +f 1103 1439 73 +f 70 1105 1425 +f 1105 169 746 +f 1425 746 330 +f 169 1104 744 +f 1104 73 1437 +f 744 1437 335 +f 330 745 1426 +f 745 335 1435 +f 1426 1435 3 +f 3 1467 1427 +f 1467 351 749 +f 1427 749 331 +f 351 1468 747 +f 1468 81 1108 +f 747 1108 173 +f 331 748 1428 +f 748 173 1112 +f 1428 1112 71 +f 81 1469 1107 +f 1469 352 752 +f 1107 752 171 +f 352 1470 750 +f 1470 18 1300 +f 750 1300 268 +f 171 751 1109 +f 751 268 1302 +f 1109 1302 41 +f 71 1111 1429 +f 1111 172 755 +f 1429 755 332 +f 172 1110 753 +f 1110 41 1307 +f 753 1307 272 +f 332 754 1430 +f 754 272 1310 +f 1430 1310 13 +f 18 1471 1299 +f 1471 353 758 +f 1299 758 267 +f 353 1472 756 +f 1472 82 1114 +f 756 1114 176 +f 267 757 1301 +f 757 176 1118 +f 1301 1118 39 +f 82 1473 1113 +f 1473 354 761 +f 1113 761 174 +f 354 1474 759 +f 1474 8 1475 +f 759 1475 355 +f 174 760 1115 +f 760 355 1476 +f 1115 1476 83 +f 39 1117 1303 +f 1117 175 764 +f 1303 764 269 +f 175 1116 762 +f 1116 83 1477 +f 762 1477 356 +f 269 763 1304 +f 763 356 1478 +f 1304 1478 19 +f 13 1309 1431 +f 1309 271 767 +f 1431 767 333 +f 271 1308 765 +f 1308 40 1120 +f 765 1120 179 +f 333 766 1432 +f 766 179 1124 +f 1432 1124 72 +f 40 1306 1119 +f 1306 270 770 +f 1119 770 177 +f 270 1305 768 +f 1305 19 1479 +f 768 1479 357 +f 177 769 1121 +f 769 357 1480 +f 1121 1480 84 +f 72 1123 1433 +f 1123 178 773 +f 1433 773 334 +f 178 1122 771 +f 1122 84 1481 +f 771 1481 358 +f 334 772 1434 +f 772 358 1482 +f 1434 1482 7 +f 3 1436 1467 +f 1436 336 776 +f 1467 776 351 +f 336 1438 774 +f 1438 74 1126 +f 774 1126 182 +f 351 775 1468 +f 775 182 1130 +f 1468 1130 81 +f 74 1441 1125 +f 1441 338 779 +f 1125 779 180 +f 338 1442 777 +f 1442 16 1312 +f 777 1312 274 +f 180 778 1127 +f 778 274 1314 +f 1127 1314 44 +f 81 1129 1469 +f 1129 181 782 +f 1469 782 352 +f 181 1128 780 +f 1128 44 1319 +f 780 1319 278 +f 352 781 1470 +f 781 278 1322 +f 1470 1322 18 +f 16 1451 1311 +f 1451 343 785 +f 1311 785 273 +f 343 1452 783 +f 1452 78 1132 +f 783 1132 185 +f 273 784 1313 +f 784 185 1136 +f 1313 1136 42 +f 78 1455 1131 +f 1455 346 788 +f 1131 788 183 +f 346 1458 786 +f 1458 4 1483 +f 786 1483 359 +f 183 787 1133 +f 787 359 1484 +f 1133 1484 85 +f 42 1135 1315 +f 1135 184 791 +f 1315 791 275 +f 184 1134 789 +f 1134 85 1485 +f 789 1485 360 +f 275 790 1316 +f 790 360 1486 +f 1316 1486 20 +f 18 1321 1471 +f 1321 277 794 +f 1471 794 353 +f 277 1320 792 +f 1320 43 1138 +f 792 1138 188 +f 353 793 1472 +f 793 188 1142 +f 1472 1142 82 +f 43 1318 1137 +f 1318 276 797 +f 1137 797 186 +f 276 1317 795 +f 1317 20 1487 +f 795 1487 361 +f 186 796 1139 +f 796 361 1488 +f 1139 1488 86 +f 82 1141 1473 +f 1141 187 800 +f 1473 800 354 +f 187 1140 798 +f 1140 86 1489 +f 798 1489 362 +f 354 799 1474 +f 799 362 1490 +f 1474 1490 8 +f 5 1417 1491 +f 1417 325 803 +f 1491 803 363 +f 325 1416 801 +f 1416 67 1144 +f 801 1144 191 +f 363 802 1492 +f 802 191 1148 +f 1492 1148 87 +f 67 1414 1143 +f 1414 324 806 +f 1143 806 189 +f 324 1413 804 +f 1413 10 1324 +f 804 1324 280 +f 189 805 1145 +f 805 280 1326 +f 1145 1326 47 +f 87 1147 1493 +f 1147 190 809 +f 1493 809 364 +f 190 1146 807 +f 1146 47 1331 +f 807 1331 284 +f 364 808 1494 +f 808 284 1334 +f 1494 1334 22 +f 10 1410 1323 +f 1410 322 812 +f 1323 812 279 +f 322 1409 810 +f 1409 66 1150 +f 810 1150 194 +f 279 811 1325 +f 811 194 1154 +f 1325 1154 45 +f 66 1408 1149 +f 1408 321 815 +f 1149 815 192 +f 321 1407 813 +f 1407 7 1495 +f 813 1495 365 +f 192 814 1151 +f 814 365 1496 +f 1151 1496 88 +f 45 1153 1327 +f 1153 193 818 +f 1327 818 281 +f 193 1152 816 +f 1152 88 1497 +f 816 1497 366 +f 281 817 1328 +f 817 366 1498 +f 1328 1498 21 +f 22 1333 1499 +f 1333 283 821 +f 1499 821 367 +f 283 1332 819 +f 1332 46 1156 +f 819 1156 197 +f 367 820 1500 +f 820 197 1160 +f 1500 1160 90 +f 46 1330 1155 +f 1330 282 824 +f 1155 824 195 +f 282 1329 822 +f 1329 21 1501 +f 822 1501 368 +f 195 823 1157 +f 823 368 1502 +f 1157 1502 89 +f 90 1159 1503 +f 1159 196 827 +f 1503 827 370 +f 196 1158 825 +f 1158 89 1504 +f 825 1504 369 +f 370 826 1506 +f 826 369 1505 +f 1506 1505 6 +f 6 1505 1507 +f 1505 369 830 +f 1507 830 371 +f 369 1504 828 +f 1504 89 1162 +f 828 1162 200 +f 371 829 1508 +f 829 200 1166 +f 1508 1166 91 +f 89 1502 1161 +f 1502 368 833 +f 1161 833 198 +f 368 1501 831 +f 1501 21 1336 +f 831 1336 286 +f 198 832 1163 +f 832 286 1338 +f 1163 1338 50 +f 91 1165 1509 +f 1165 199 836 +f 1509 836 372 +f 199 1164 834 +f 1164 50 1343 +f 834 1343 290 +f 372 835 1510 +f 835 290 1346 +f 1510 1346 23 +f 21 1498 1335 +f 1498 366 839 +f 1335 839 285 +f 366 1497 837 +f 1497 88 1168 +f 837 1168 203 +f 285 838 1337 +f 838 203 1172 +f 1337 1172 48 +f 88 1496 1167 +f 1496 365 842 +f 1167 842 201 +f 365 1495 840 +f 1495 7 1482 +f 840 1482 358 +f 201 841 1169 +f 841 358 1481 +f 1169 1481 84 +f 48 1171 1339 +f 1171 202 845 +f 1339 845 287 +f 202 1170 843 +f 1170 84 1480 +f 843 1480 357 +f 287 844 1340 +f 844 357 1479 +f 1340 1479 19 +f 23 1345 1511 +f 1345 289 848 +f 1511 848 373 +f 289 1344 846 +f 1344 49 1174 +f 846 1174 206 +f 373 847 1512 +f 847 206 1178 +f 1512 1178 92 +f 49 1342 1173 +f 1342 288 851 +f 1173 851 204 +f 288 1341 849 +f 1341 19 1478 +f 849 1478 356 +f 204 850 1175 +f 850 356 1477 +f 1175 1477 83 +f 92 1177 1513 +f 1177 205 854 +f 1513 854 374 +f 205 1176 852 +f 1176 83 1476 +f 852 1476 355 +f 374 853 1514 +f 853 355 1475 +f 1514 1475 8 +f 1 1396 1515 +f 1396 316 857 +f 1515 857 375 +f 316 1398 855 +f 1398 64 1180 +f 855 1180 209 +f 375 856 1516 +f 856 209 1184 +f 1516 1184 93 +f 64 1401 1179 +f 1401 318 860 +f 1179 860 207 +f 318 1402 858 +f 1402 11 1348 +f 858 1348 292 +f 207 859 1181 +f 859 292 1350 +f 1181 1350 53 +f 93 1183 1517 +f 1183 208 863 +f 1517 863 376 +f 208 1182 861 +f 1182 53 1355 +f 861 1355 296 +f 376 862 1518 +f 862 296 1358 +f 1518 1358 24 +f 11 1411 1347 +f 1411 323 866 +f 1347 866 291 +f 323 1412 864 +f 1412 68 1186 +f 864 1186 212 +f 291 865 1349 +f 865 212 1190 +f 1349 1190 51 +f 68 1415 1185 +f 1415 326 869 +f 1185 869 210 +f 326 1418 867 +f 1418 5 1491 +f 867 1491 363 +f 210 868 1187 +f 868 363 1492 +f 1187 1492 87 +f 51 1189 1351 +f 1189 211 872 +f 1351 872 293 +f 211 1188 870 +f 1188 87 1493 +f 870 1493 364 +f 293 871 1352 +f 871 364 1494 +f 1352 1494 22 +f 24 1357 1519 +f 1357 295 875 +f 1519 875 377 +f 295 1356 873 +f 1356 52 1192 +f 873 1192 215 +f 377 874 1520 +f 874 215 1196 +f 1520 1196 94 +f 52 1354 1191 +f 1354 294 878 +f 1191 878 213 +f 294 1353 876 +f 1353 22 1499 +f 876 1499 367 +f 213 877 1193 +f 877 367 1500 +f 1193 1500 90 +f 94 1195 1521 +f 1195 214 881 +f 1521 881 378 +f 214 1194 879 +f 1194 90 1503 +f 879 1503 370 +f 378 880 1522 +f 880 370 1506 +f 1522 1506 6 +f 1 1515 1459 +f 1515 375 884 +f 1459 884 347 +f 375 1516 882 +f 1516 93 1198 +f 882 1198 218 +f 347 883 1460 +f 883 218 1202 +f 1460 1202 79 +f 93 1517 1197 +f 1517 376 887 +f 1197 887 216 +f 376 1518 885 +f 1518 24 1360 +f 885 1360 298 +f 216 886 1199 +f 886 298 1362 +f 1199 1362 56 +f 79 1201 1461 +f 1201 217 890 +f 1461 890 348 +f 217 1200 888 +f 1200 56 1367 +f 888 1367 302 +f 348 889 1462 +f 889 302 1370 +f 1462 1370 17 +f 24 1519 1359 +f 1519 377 893 +f 1359 893 297 +f 377 1520 891 +f 1520 94 1204 +f 891 1204 221 +f 297 892 1361 +f 892 221 1208 +f 1361 1208 54 +f 94 1521 1203 +f 1521 378 896 +f 1203 896 219 +f 378 1522 894 +f 1522 6 1523 +f 894 1523 379 +f 219 895 1205 +f 895 379 1524 +f 1205 1524 95 +f 54 1207 1363 +f 1207 220 899 +f 1363 899 299 +f 220 1206 897 +f 1206 95 1525 +f 897 1525 380 +f 299 898 1364 +f 898 380 1526 +f 1364 1526 25 +f 17 1369 1463 +f 1369 301 902 +f 1463 902 349 +f 301 1368 900 +f 1368 55 1210 +f 900 1210 224 +f 349 901 1464 +f 901 224 1214 +f 1464 1214 80 +f 55 1366 1209 +f 1366 300 905 +f 1209 905 222 +f 300 1365 903 +f 1365 25 1527 +f 903 1527 381 +f 222 904 1211 +f 904 381 1528 +f 1211 1528 96 +f 80 1213 1465 +f 1213 223 908 +f 1465 908 350 +f 223 1212 906 +f 1212 96 1529 +f 906 1529 382 +f 350 907 1466 +f 907 382 1530 +f 1466 1530 2 +f 2 1530 1531 +f 1530 382 911 +f 1531 911 383 +f 382 1529 909 +f 1529 96 1216 +f 909 1216 227 +f 383 910 1532 +f 910 227 1220 +f 1532 1220 97 +f 96 1528 1215 +f 1528 381 914 +f 1215 914 225 +f 381 1527 912 +f 1527 25 1372 +f 912 1372 304 +f 225 913 1217 +f 913 304 1374 +f 1217 1374 59 +f 97 1219 1533 +f 1219 226 917 +f 1533 917 384 +f 226 1218 915 +f 1218 59 1379 +f 915 1379 308 +f 384 916 1534 +f 916 308 1382 +f 1534 1382 26 +f 25 1526 1371 +f 1526 380 920 +f 1371 920 303 +f 380 1525 918 +f 1525 95 1222 +f 918 1222 230 +f 303 919 1373 +f 919 230 1226 +f 1373 1226 57 +f 95 1524 1221 +f 1524 379 923 +f 1221 923 228 +f 379 1523 921 +f 1523 6 1507 +f 921 1507 371 +f 228 922 1223 +f 922 371 1508 +f 1223 1508 91 +f 57 1225 1375 +f 1225 229 926 +f 1375 926 305 +f 229 1224 924 +f 1224 91 1509 +f 924 1509 372 +f 305 925 1376 +f 925 372 1510 +f 1376 1510 23 +f 26 1381 1535 +f 1381 307 929 +f 1535 929 385 +f 307 1380 927 +f 1380 58 1228 +f 927 1228 233 +f 385 928 1536 +f 928 233 1232 +f 1536 1232 98 +f 58 1378 1227 +f 1378 306 932 +f 1227 932 231 +f 306 1377 930 +f 1377 23 1511 +f 930 1511 373 +f 231 931 1229 +f 931 373 1512 +f 1229 1512 92 +f 98 1231 1537 +f 1231 232 935 +f 1537 935 386 +f 232 1230 933 +f 1230 92 1513 +f 933 1513 374 +f 386 934 1538 +f 934 374 1514 +f 1538 1514 8 +f 2 1531 1447 +f 1531 383 938 +f 1447 938 341 +f 383 1532 936 +f 1532 97 1234 +f 936 1234 236 +f 341 937 1448 +f 937 236 1238 +f 1448 1238 76 +f 97 1533 1233 +f 1533 384 941 +f 1233 941 234 +f 384 1534 939 +f 1534 26 1384 +f 939 1384 310 +f 234 940 1235 +f 940 310 1386 +f 1235 1386 62 +f 76 1237 1449 +f 1237 235 944 +f 1449 944 342 +f 235 1236 942 +f 1236 62 1391 +f 942 1391 314 +f 342 943 1450 +f 943 314 1394 +f 1450 1394 15 +f 26 1535 1383 +f 1535 385 947 +f 1383 947 309 +f 385 1536 945 +f 1536 98 1240 +f 945 1240 239 +f 309 946 1385 +f 946 239 1244 +f 1385 1244 60 +f 98 1537 1239 +f 1537 386 950 +f 1239 950 237 +f 386 1538 948 +f 1538 8 1490 +f 948 1490 362 +f 237 949 1241 +f 949 362 1489 +f 1241 1489 86 +f 60 1243 1387 +f 1243 238 953 +f 1387 953 311 +f 238 1242 951 +f 1242 86 1488 +f 951 1488 361 +f 311 952 1388 +f 952 361 1487 +f 1388 1487 20 +f 15 1393 1453 +f 1393 313 956 +f 1453 956 344 +f 313 1392 954 +f 1392 61 1246 +f 954 1246 242 +f 344 955 1454 +f 955 242 1250 +f 1454 1250 77 +f 61 1390 1245 +f 1390 312 959 +f 1245 959 240 +f 312 1389 957 +f 1389 20 1486 +f 957 1486 360 +f 240 958 1247 +f 958 360 1485 +f 1247 1485 85 +f 77 1249 1456 +f 1249 241 962 +f 1456 962 345 +f 241 1248 960 +f 1248 85 1484 +f 960 1484 359 +f 345 961 1457 +f 961 359 1483 +f 1457 1483 4 +# 3072 faces, 0 coords texture + +# End of File \ No newline at end of file diff --git a/testdata/extra_vertex.obj b/testdata/extra_vertex.obj new file mode 100644 index 0000000..bfe7f1d --- /dev/null +++ b/testdata/extra_vertex.obj @@ -0,0 +1,42 @@ +#### +# +# OBJ File Generated by Meshlab +# +#### +# Object test_nm.obj +# +# Vertices: 10 +# Faces: 3 +# +#### +vn 0.000000 0.000000 0.000000 +v 0.382683 0.923880 0.000000 +vn 0.000000 0.000000 0.000000 +v 0.353553 0.923880 0.146447 +vn 0.000000 0.000000 0.000000 +v 0.270598 0.923880 0.270598 +vn 0.000000 0.000000 0.000000 +v 0.146447 0.923880 0.353553 +vn 0.000000 0.000000 0.000000 +v 0.000000 0.923880 0.382683 +vn 0.000000 0.000000 0.000000 +v -0.146447 0.923880 0.353553 +vn 0.000000 0.000000 0.000000 +v -0.270598 0.923880 0.270598 +vn 0.000000 0.000000 0.000000 +v -0.353553 0.923880 0.146447 +vn 0.000000 0.000000 0.000000 +v -0.382683 0.923880 0.000000 +vn 0.000000 0.000000 0.000000 +v -0.382683 0.923880 0.000023 + + +# 10 vertices, 0 vertices normals + +f 1//1 2//2 3//3 +f 4//4 5//5 6//6 +f 7//7 8//8 9//9 + +# 3 faces, 0 coords texture + +# End of File \ No newline at end of file diff --git a/testdata/one_face_123.obj b/testdata/one_face_123.obj new file mode 100644 index 0000000..0a5bfb8 --- /dev/null +++ b/testdata/one_face_123.obj @@ -0,0 +1,11 @@ +v 1.00 0.10 0.00 +v 2.00 0.01 0.00 +v 3.00 0.11 0.00 + +# 3 vertices, 0 vertices normals + +f 1 2 3 + +# 1 faces, 0 coords texture + +# End of File \ No newline at end of file diff --git a/testdata/one_face_321.obj b/testdata/one_face_321.obj new file mode 100644 index 0000000..29a25ec --- /dev/null +++ b/testdata/one_face_321.obj @@ -0,0 +1,11 @@ +v 1.00 0.10 0.00 +v 2.00 0.01 0.00 +v 3.00 0.11 0.00 + +# 3 vertices, 0 vertices normals + +f 3 2 1 + +# 1 faces, 0 coords texture + +# End of File \ No newline at end of file diff --git a/testdata/sphere.obj b/testdata/sphere.obj new file mode 100644 index 0000000..5b4cd69 --- /dev/null +++ b/testdata/sphere.obj @@ -0,0 +1,459 @@ +# Exported from Wings 3D 1.2 +mtllib sphere.mtl +o sphere1 +#114 vertices, 224 faces +v 0.38268343 0.92387953 0.0000000e+0 +v 0.35355339 0.92387953 0.14644661 +v 0.27059805 0.92387953 0.27059805 +v 0.14644661 0.92387953 0.35355339 +v 2.3432602e-17 0.92387953 0.38268343 +v -0.14644661 0.92387953 0.35355339 +v -0.27059805 0.92387953 0.27059805 +v -0.35355339 0.92387953 0.14644661 +v -0.38268343 0.92387953 4.6865204e-17 +v -0.35355339 0.92387953 -0.14644661 +v -0.27059805 0.92387953 -0.27059805 +v -0.14644661 0.92387953 -0.35355339 +v -7.0297806e-17 0.92387953 -0.38268343 +v 0.14644661 0.92387953 -0.35355339 +v 0.27059805 0.92387953 -0.27059805 +v 0.35355339 0.92387953 -0.14644661 +v 0.70710678 0.70710678 0.0000000e+0 +v 0.65328148 0.70710678 0.27059805 +v 0.50000000 0.70710678 0.50000000 +v 0.27059805 0.70710678 0.65328148 +v 4.3297803e-17 0.70710678 0.70710678 +v -0.27059805 0.70710678 0.65328148 +v -0.50000000 0.70710678 0.50000000 +v -0.65328148 0.70710678 0.27059805 +v -0.70710678 0.70710678 8.6595606e-17 +v -0.65328148 0.70710678 -0.27059805 +v -0.50000000 0.70710678 -0.50000000 +v -0.27059805 0.70710678 -0.65328148 +v -1.2989341e-16 0.70710678 -0.70710678 +v 0.27059805 0.70710678 -0.65328148 +v 0.50000000 0.70710678 -0.50000000 +v 0.65328148 0.70710678 -0.27059805 +v 0.92387953 0.38268343 0.0000000e+0 +v 0.85355339 0.38268343 0.35355339 +v 0.65328148 0.38268343 0.65328148 +v 0.35355339 0.38268343 0.85355339 +v 5.6571306e-17 0.38268343 0.92387953 +v -0.35355339 0.38268343 0.85355339 +v -0.65328148 0.38268343 0.65328148 +v -0.85355339 0.38268343 0.35355339 +v -0.92387953 0.38268343 1.1314261e-16 +v -0.85355339 0.38268343 -0.35355339 +v -0.65328148 0.38268343 -0.65328148 +v -0.35355339 0.38268343 -0.85355339 +v -1.6971392e-16 0.38268343 -0.92387953 +v 0.35355339 0.38268343 -0.85355339 +v 0.65328148 0.38268343 -0.65328148 +v 0.85355339 0.38268343 -0.35355339 +v 1.00000000 6.1232340e-17 0.0000000e+0 +v 0.92387953 6.1232340e-17 0.38268343 +v 0.70710678 6.1232340e-17 0.70710678 +v 0.38268343 6.1232340e-17 0.92387953 +v 6.1232340e-17 6.1232340e-17 1.00000000 +v -0.38268343 6.1232340e-17 0.92387953 +v -0.70710678 6.1232340e-17 0.70710678 +v -0.92387953 6.1232340e-17 0.38268343 +v -1.00000000 6.1232340e-17 1.2246468e-16 +v -0.92387953 6.1232340e-17 -0.38268343 +v -0.70710678 6.1232340e-17 -0.70710678 +v -0.38268343 6.1232340e-17 -0.92387953 +v -1.8369702e-16 6.1232340e-17 -1.00000000 +v 0.38268343 6.1232340e-17 -0.92387953 +v 0.70710678 6.1232340e-17 -0.70710678 +v 0.92387953 6.1232340e-17 -0.38268343 +v 0.92387953 -0.38268343 0.0000000e+0 +v 0.85355339 -0.38268343 0.35355339 +v 0.65328148 -0.38268343 0.65328148 +v 0.35355339 -0.38268343 0.85355339 +v 5.6571306e-17 -0.38268343 0.92387953 +v -0.35355339 -0.38268343 0.85355339 +v -0.65328148 -0.38268343 0.65328148 +v -0.85355339 -0.38268343 0.35355339 +v -0.92387953 -0.38268343 1.1314261e-16 +v -0.85355339 -0.38268343 -0.35355339 +v -0.65328148 -0.38268343 -0.65328148 +v -0.35355339 -0.38268343 -0.85355339 +v -1.6971392e-16 -0.38268343 -0.92387953 +v 0.35355339 -0.38268343 -0.85355339 +v 0.65328148 -0.38268343 -0.65328148 +v 0.85355339 -0.38268343 -0.35355339 +v 0.70710678 -0.70710678 0.0000000e+0 +v 0.65328148 -0.70710678 0.27059805 +v 0.50000000 -0.70710678 0.50000000 +v 0.27059805 -0.70710678 0.65328148 +v 4.3297803e-17 -0.70710678 0.70710678 +v -0.27059805 -0.70710678 0.65328148 +v -0.50000000 -0.70710678 0.50000000 +v -0.65328148 -0.70710678 0.27059805 +v -0.70710678 -0.70710678 8.6595606e-17 +v -0.65328148 -0.70710678 -0.27059805 +v -0.50000000 -0.70710678 -0.50000000 +v -0.27059805 -0.70710678 -0.65328148 +v -1.2989341e-16 -0.70710678 -0.70710678 +v 0.27059805 -0.70710678 -0.65328148 +v 0.50000000 -0.70710678 -0.50000000 +v 0.65328148 -0.70710678 -0.27059805 +v 0.38268343 -0.92387953 0.0000000e+0 +v 0.35355339 -0.92387953 0.14644661 +v 0.27059805 -0.92387953 0.27059805 +v 0.14644661 -0.92387953 0.35355339 +v 2.3432602e-17 -0.92387953 0.38268343 +v -0.14644661 -0.92387953 0.35355339 +v -0.27059805 -0.92387953 0.27059805 +v -0.35355339 -0.92387953 0.14644661 +v -0.38268343 -0.92387953 4.6865204e-17 +v -0.35355339 -0.92387953 -0.14644661 +v -0.27059805 -0.92387953 -0.27059805 +v -0.14644661 -0.92387953 -0.35355339 +v -7.0297806e-17 -0.92387953 -0.38268343 +v 0.14644661 -0.92387953 -0.35355339 +v 0.27059805 -0.92387953 -0.27059805 +v 0.35355339 -0.92387953 -0.14644661 +v 0.0000000e+0 1.00000000 0.0000000e+0 +v 0.0000000e+0 -1.00000000 0.0000000e+0 +vn 0.44254783 0.89674490 -8.0352547e-17 +vn 0.35310199 0.92408176 0.14625963 +vn 0.31292857 0.89674490 0.31292857 +vn 0.14625963 0.92408176 0.35310199 +vn 2.3633102e-18 0.89674490 0.44254783 +vn -0.14625963 0.92408176 0.35310199 +vn -0.31292857 0.89674490 0.31292857 +vn -0.35310199 0.92408176 0.14625963 +vn -0.44254783 0.89674490 2.5996412e-17 +vn -0.35310199 0.92408176 -0.14625963 +vn -0.31292857 0.89674490 -0.31292857 +vn -0.14625963 0.92408176 -0.35310199 +vn -3.7812963e-17 0.89674490 -0.44254783 +vn 0.14625963 0.92408176 -0.35310199 +vn 0.31292857 0.89674490 -0.31292857 +vn 0.35310199 0.92408176 -0.14625963 +vn 0.70658450 0.70762868 1.3574215e-16 +vn 0.65279895 0.70762868 0.27039818 +vn 0.49963069 0.70762868 0.49963069 +vn 0.27039818 0.70762868 0.65279895 +vn 7.1443235e-18 0.70762868 0.70658450 +vn -0.27039818 0.70762868 0.65279895 +vn -0.49963069 0.70762868 0.49963069 +vn -0.65279895 0.70762868 0.27039818 +vn -0.70658450 0.70762868 1.1430918e-16 +vn -0.65279895 0.70762868 -0.27039818 +vn -0.49963069 0.70762868 -0.49963069 +vn -0.27039818 0.70762868 -0.65279895 +vn 1.4288647e-17 0.70762868 -0.70658450 +vn 0.27039818 0.70762868 -0.65279895 +vn 0.49963069 0.70762868 -0.49963069 +vn 0.65279895 0.70762868 -0.27039818 +vn 0.92368212 0.38315969 -8.6274346e-17 +vn 0.85337100 0.38315969 0.35347784 +vn 0.65314189 0.38315969 0.65314189 +vn 0.35347784 0.38315969 0.85337100 +vn 1.0784293e-17 0.38315969 0.92368212 +vn -0.35347784 0.38315969 0.85337100 +vn -0.65314189 0.38315969 0.65314189 +vn -0.85337100 0.38315969 0.35347784 +vn -0.92368212 0.38315969 1.4738534e-16 +vn -0.85337100 0.38315969 -0.35347784 +vn -0.65314189 0.38315969 -0.65314189 +vn -0.35347784 0.38315969 -0.85337100 +vn -4.3137173e-17 0.38315969 -0.92368212 +vn 0.35347784 0.38315969 -0.85337100 +vn 0.65314189 0.38315969 -0.65314189 +vn 0.85337100 0.38315969 -0.35347784 +vn 1.00000000 0.0000000e+0 -1.0812319e-16 +vn 0.92387953 -1.8020531e-17 0.38268343 +vn 0.70710678 0.0000000e+0 0.70710678 +vn 0.38268343 3.6041063e-17 0.92387953 +vn 0.0000000e+0 7.2082126e-18 1.00000000 +vn -0.38268343 1.8020531e-17 0.92387953 +vn -0.70710678 1.4416425e-17 0.70710678 +vn -0.92387953 3.6041063e-18 0.38268343 +vn -1.00000000 -7.2082126e-18 1.4416425e-16 +vn -0.92387953 -5.4061594e-17 -0.38268343 +vn -0.70710678 -8.6498551e-17 -0.70710678 +vn -0.38268343 -1.4416425e-17 -0.92387953 +vn -3.6761884e-16 7.2082126e-18 -1.00000000 +vn 0.38268343 0.0000000e+0 -0.92387953 +vn 0.70710678 1.4416425e-17 -0.70710678 +vn 0.92387953 -3.6041063e-17 -0.38268343 +vn 0.92368212 -0.38315969 -1.4019581e-16 +vn 0.85337100 -0.38315969 0.35347784 +vn 0.65314189 -0.38315969 0.65314189 +vn 0.35347784 -0.38315969 0.85337100 +vn 7.1895288e-18 -0.38315969 0.92368212 +vn -0.35347784 -0.38315969 0.85337100 +vn -0.65314189 -0.38315969 0.65314189 +vn -0.85337100 -0.38315969 0.35347784 +vn -0.92368212 -0.38315969 1.5816963e-16 +vn -0.85337100 -0.38315969 -0.35347784 +vn -0.65314189 -0.38315969 -0.65314189 +vn -0.35347784 -0.38315969 -0.85337100 +vn 7.1895288e-18 -0.38315969 -0.92368212 +vn 0.35347784 -0.38315969 -0.85337100 +vn 0.65314189 -0.38315969 -0.65314189 +vn 0.85337100 -0.38315969 -0.35347784 +vn 0.70658450 -0.70762868 -6.7871073e-17 +vn 0.65279895 -0.70762868 0.27039818 +vn 0.49963069 -0.70762868 0.49963069 +vn 0.27039818 -0.70762868 0.65279895 +vn 1.0716485e-17 -0.70762868 0.70658450 +vn -0.27039818 -0.70762868 0.65279895 +vn -0.49963069 -0.70762868 0.49963069 +vn -0.65279895 -0.70762868 0.27039818 +vn -0.70658450 -0.70762868 1.1430918e-16 +vn -0.65279895 -0.70762868 -0.27039818 +vn -0.49963069 -0.70762868 -0.49963069 +vn -0.27039818 -0.70762868 -0.65279895 +vn 3.5721617e-18 -0.70762868 -0.70658450 +vn 0.27039818 -0.70762868 -0.65279895 +vn 0.49963069 -0.70762868 -0.49963069 +vn 0.65279895 -0.70762868 -0.27039818 +vn 0.44254783 -0.89674490 2.1033461e-16 +vn 0.35310199 -0.92408176 0.14625963 +vn 0.31292857 -0.89674490 0.31292857 +vn 0.14625963 -0.92408176 0.35310199 +vn 2.3633102e-17 -0.89674490 0.44254783 +vn -0.14625963 -0.92408176 0.35310199 +vn -0.31292857 -0.89674490 0.31292857 +vn -0.35310199 -0.92408176 0.14625963 +vn -0.44254783 -0.89674490 5.4356134e-17 +vn -0.35310199 -0.92408176 -0.14625963 +vn -0.31292857 -0.89674490 -0.31292857 +vn -0.14625963 -0.92408176 -0.35310199 +vn -3.5449653e-17 -0.89674490 -0.44254783 +vn 0.14625963 -0.92408176 -0.35310199 +vn 0.31292857 -0.89674490 -0.31292857 +vn 0.35310199 -0.92408176 -0.14625963 +vn -7.0801599e-18 1.00000000 -4.3365979e-17 +vn -8.8501998e-18 -1.00000000 -6.2393909e-17 +g sphere1_default +usemtl default +s 1 +f 1//1 18//18 17//17 +f 1//1 32//32 16//16 +f 1//1 113//113 2//2 +f 2//2 18//18 1//1 +f 2//2 113//113 3//3 +f 3//3 18//18 2//2 +f 3//3 20//20 19//19 +f 3//3 113//113 4//4 +f 4//4 20//20 3//3 +f 4//4 113//113 5//5 +f 5//5 20//20 4//4 +f 5//5 22//22 21//21 +f 5//5 113//113 6//6 +f 6//6 22//22 5//5 +f 6//6 113//113 7//7 +f 7//7 22//22 6//6 +f 7//7 24//24 23//23 +f 7//7 113//113 8//8 +f 8//8 24//24 7//7 +f 8//8 113//113 9//9 +f 9//9 24//24 8//8 +f 9//9 26//26 25//25 +f 9//9 113//113 10//10 +f 10//10 26//26 9//9 +f 10//10 113//113 11//11 +f 11//11 26//26 10//10 +f 11//11 28//28 27//27 +f 11//11 113//113 12//12 +f 12//12 28//28 11//11 +f 12//12 113//113 13//13 +f 13//13 28//28 12//12 +f 13//13 30//30 29//29 +f 13//13 113//113 14//14 +f 14//14 30//30 13//13 +f 14//14 113//113 15//15 +f 15//15 30//30 14//14 +f 15//15 32//32 31//31 +f 15//15 113//113 16//16 +f 16//16 32//32 15//15 +f 16//16 113//113 1//1 +f 17//17 32//32 1//1 +f 17//17 33//33 32//32 +f 18//18 33//33 17//17 +f 18//18 35//35 34//34 +f 19//19 18//18 3//3 +f 19//19 35//35 18//18 +f 20//20 35//35 19//19 +f 20//20 37//37 36//36 +f 21//21 20//20 5//5 +f 21//21 37//37 20//20 +f 22//22 37//37 21//21 +f 22//22 39//39 38//38 +f 23//23 22//22 7//7 +f 23//23 39//39 22//22 +f 24//24 39//39 23//23 +f 24//24 41//41 40//40 +f 25//25 24//24 9//9 +f 25//25 41//41 24//24 +f 26//26 41//41 25//25 +f 26//26 43//43 42//42 +f 27//27 26//26 11//11 +f 27//27 43//43 26//26 +f 28//28 43//43 27//27 +f 28//28 45//45 44//44 +f 29//29 28//28 13//13 +f 29//29 45//45 28//28 +f 30//30 45//45 29//29 +f 30//30 47//47 46//46 +f 31//31 30//30 15//15 +f 31//31 47//47 30//30 +f 32//32 33//33 48//48 +f 32//32 47//47 31//31 +f 33//33 50//50 49//49 +f 33//33 64//64 48//48 +f 34//34 33//33 18//18 +f 34//34 50//50 33//33 +f 35//35 50//50 34//34 +f 35//35 52//52 51//51 +f 36//36 35//35 20//20 +f 36//36 52//52 35//35 +f 37//37 52//52 36//36 +f 37//37 54//54 53//53 +f 38//38 37//37 22//22 +f 38//38 54//54 37//37 +f 39//39 54//54 38//38 +f 39//39 56//56 55//55 +f 40//40 39//39 24//24 +f 40//40 56//56 39//39 +f 41//41 56//56 40//40 +f 41//41 58//58 57//57 +f 42//42 41//41 26//26 +f 42//42 58//58 41//41 +f 43//43 58//58 42//42 +f 43//43 60//60 59//59 +f 44//44 43//43 28//28 +f 44//44 60//60 43//43 +f 45//45 60//60 44//44 +f 45//45 62//62 61//61 +f 46//46 45//45 30//30 +f 46//46 62//62 45//45 +f 47//47 62//62 46//46 +f 47//47 64//64 63//63 +f 48//48 47//47 32//32 +f 48//48 64//64 47//47 +f 49//49 64//64 33//33 +f 49//49 65//65 64//64 +f 50//50 65//65 49//49 +f 50//50 67//67 66//66 +f 51//51 50//50 35//35 +f 51//51 67//67 50//50 +f 52//52 67//67 51//51 +f 52//52 69//69 68//68 +f 53//53 52//52 37//37 +f 53//53 69//69 52//52 +f 54//54 69//69 53//53 +f 54//54 71//71 70//70 +f 55//55 54//54 39//39 +f 55//55 71//71 54//54 +f 56//56 71//71 55//55 +f 56//56 73//73 72//72 +f 57//57 56//56 41//41 +f 57//57 73//73 56//56 +f 58//58 73//73 57//57 +f 58//58 75//75 74//74 +f 59//59 58//58 43//43 +f 59//59 75//75 58//58 +f 60//60 75//75 59//59 +f 60//60 77//77 76//76 +f 61//61 60//60 45//45 +f 61//61 77//77 60//60 +f 62//62 77//77 61//61 +f 62//62 79//79 78//78 +f 63//63 62//62 47//47 +f 63//63 79//79 62//62 +f 64//64 65//65 80//80 +f 64//64 79//79 63//63 +f 65//65 82//82 81//81 +f 65//65 96//96 80//80 +f 66//66 65//65 50//50 +f 66//66 82//82 65//65 +f 67//67 82//82 66//66 +f 67//67 84//84 83//83 +f 68//68 67//67 52//52 +f 68//68 84//84 67//67 +f 69//69 84//84 68//68 +f 69//69 86//86 85//85 +f 70//70 69//69 54//54 +f 70//70 86//86 69//69 +f 71//71 86//86 70//70 +f 71//71 88//88 87//87 +f 72//72 71//71 56//56 +f 72//72 88//88 71//71 +f 73//73 88//88 72//72 +f 73//73 90//90 89//89 +f 74//74 73//73 58//58 +f 74//74 90//90 73//73 +f 75//75 90//90 74//74 +f 75//75 92//92 91//91 +f 76//76 75//75 60//60 +f 76//76 92//92 75//75 +f 77//77 92//92 76//76 +f 77//77 94//94 93//93 +f 78//78 77//77 62//62 +f 78//78 94//94 77//77 +f 79//79 94//94 78//78 +f 79//79 96//96 95//95 +f 80//80 79//79 64//64 +f 80//80 96//96 79//79 +f 81//81 96//96 65//65 +f 81//81 97//97 96//96 +f 82//82 97//97 81//81 +f 82//82 99//99 98//98 +f 83//83 82//82 67//67 +f 83//83 99//99 82//82 +f 84//84 99//99 83//83 +f 84//84 101//101 100//100 +f 85//85 84//84 69//69 +f 85//85 101//101 84//84 +f 86//86 101//101 85//85 +f 86//86 103//103 102//102 +f 87//87 86//86 71//71 +f 87//87 103//103 86//86 +f 88//88 103//103 87//87 +f 88//88 105//105 104//104 +f 89//89 88//88 73//73 +f 89//89 105//105 88//88 +f 90//90 105//105 89//89 +f 90//90 107//107 106//106 +f 91//91 90//90 75//75 +f 91//91 107//107 90//90 +f 92//92 107//107 91//91 +f 92//92 109//109 108//108 +f 93//93 92//92 77//77 +f 93//93 109//109 92//92 +f 94//94 109//109 93//93 +f 94//94 111//111 110//110 +f 95//95 94//94 79//79 +f 95//95 111//111 94//94 +f 96//96 97//97 112//112 +f 96//96 111//111 95//95 +f 97//97 114//114 112//112 +f 98//98 97//97 82//82 +f 98//98 114//114 97//97 +f 99//99 114//114 98//98 +f 100//100 99//99 84//84 +f 100//100 114//114 99//99 +f 101//101 114//114 100//100 +f 102//102 101//101 86//86 +f 102//102 114//114 101//101 +f 103//103 114//114 102//102 +f 104//104 103//103 88//88 +f 104//104 114//114 103//103 +f 105//105 114//114 104//104 +f 106//106 105//105 90//90 +f 106//106 114//114 105//105 +f 107//107 114//114 106//106 +f 108//108 107//107 92//92 +f 108//108 114//114 107//107 +f 109//109 114//114 108//108 +f 110//110 109//109 94//94 +f 110//110 114//114 109//109 +f 111//111 114//114 110//110 +f 112//112 111//111 96//96 +f 112//112 114//114 111//111 diff --git a/testdata/test_extra_whitespace.ply b/testdata/test_extra_whitespace.ply new file mode 100644 index 0000000..0991ef9 --- /dev/null +++ b/testdata/test_extra_whitespace.ply @@ -0,0 +1,18 @@ +ply +format ascii 1.0 +comment manual generated + element vertex 3 +property float x +property float y +property float z +property uchar red +property uchar green +property uchar blue +property uchar alpha + element face 1 +property list uchar int vertex_indices +end_header +0.25 0.1 0 209 0 122 255 +0.5 0.9 0 77 0 205 255 +0.75 0.1 0 0 0 255 255 +3 0 1 2 diff --git a/testdata/test_more_datatypes.ply b/testdata/test_more_datatypes.ply new file mode 100644 index 0000000..0ac06ea --- /dev/null +++ b/testdata/test_more_datatypes.ply @@ -0,0 +1,18 @@ +ply +format ascii 1.0 +comment manual generated +element vertex 3 +property float32 x +property float32 y +property float z +property uchar red +property uint8 green +property uchar blue +property uint8 alpha +element face 1 +property list uchar int32 vertex_indices +end_header +0.25 0.1 0 209 0 122 255 +0.5 0.9 0 77 0 205 255 +0.75 0.1 0 0 0 255 255 +3 0 1 2 diff --git a/testdata/test_nm.obj b/testdata/test_nm.obj new file mode 100644 index 0000000..8521688 --- /dev/null +++ b/testdata/test_nm.obj @@ -0,0 +1,414 @@ +#### +# +# OBJ File Generated by Meshlab +# +#### +# Object test_nm.obj +# +# Vertices: 114 +# Faces: 170 +# +#### +vn 0.000000 0.000000 0.000000 +v 0.382683 0.923880 0.000000 +vn 0.000000 0.000000 0.000000 +v 0.353553 0.923880 0.146447 +vn 0.000000 0.000000 0.000000 +v 0.270598 0.923880 0.270598 +vn 0.000000 0.000000 0.000000 +v 0.146447 0.923880 0.353553 +vn 0.000000 0.000000 0.000000 +v 0.000000 0.923880 0.382683 +vn 0.000000 0.000000 0.000000 +v -0.146447 0.923880 0.353553 +vn 0.000000 0.000000 0.000000 +v -0.270598 0.923880 0.270598 +vn 0.000000 0.000000 0.000000 +v -0.353553 0.923880 0.146447 +vn 0.000000 0.000000 0.000000 +v -0.382683 0.923880 0.000000 +vn 0.000000 0.000000 0.000000 +v -0.353553 0.923880 -0.146447 +vn 0.000000 0.000000 0.000000 +v -0.270598 0.923880 -0.270598 +vn 0.000000 0.000000 0.000000 +v -0.146447 0.923880 -0.353553 +vn 0.000000 0.000000 0.000000 +v -0.000000 0.923880 -0.382683 +vn 0.000000 0.000000 0.000000 +v 0.146447 0.923880 -0.353553 +vn 0.000000 0.000000 0.000000 +v 0.270598 0.923880 -0.270598 +vn 0.000000 0.000000 0.000000 +v 0.353553 0.923880 -0.146447 +vn 2.755306 1.841035 0.000000 +v 0.707107 0.707107 0.000000 +vn 2.545569 1.841030 1.054412 +v 0.653281 0.707107 0.270598 +vn 1.948297 1.841028 1.948297 +v 0.500000 0.707107 0.500000 +vn 1.054413 1.841030 2.545568 +v 0.270598 0.707107 0.653281 +vn 0.000000 1.841035 2.755306 +v 0.000000 0.707107 0.707107 +vn -1.054412 1.841030 2.545569 +v -0.270598 0.707107 0.653281 +vn -1.948297 1.841028 1.948297 +v -0.500000 0.707107 0.500000 +vn -2.545568 1.841030 1.054413 +v -0.653281 0.707107 0.270598 +vn -2.755306 1.841035 0.000000 +v -0.707107 0.707107 0.000000 +vn -2.545569 1.841030 -1.054412 +v -0.653281 0.707107 -0.270598 +vn -1.948297 1.841028 -1.948297 +v -0.500000 0.707107 -0.500000 +vn -1.054413 1.841030 -2.545568 +v -0.270598 0.707107 -0.653281 +vn 0.000000 1.841035 -2.755306 +v -0.000000 0.707107 -0.707107 +vn 1.054412 1.841030 -2.545569 +v 0.270598 0.707107 -0.653281 +vn 1.948297 1.841028 -1.948297 +v 0.500000 0.707107 -0.500000 +vn 2.545568 1.841030 -1.054413 +v 0.653281 0.707107 -0.270598 +vn 5.496461 2.219063 0.000000 +v 0.923880 0.382683 0.000000 +vn 3.765099 1.910999 1.226114 +v 0.853553 0.382683 0.353553 +vn 2.134970 1.756963 2.352807 +v 0.653281 0.382683 0.653281 +vn 1.226114 1.910999 3.765099 +v 0.353553 0.382683 0.853553 +vn 0.000000 2.219063 5.496461 +v 0.000000 0.382683 0.923880 +vn -2.103414 2.219069 5.078070 +v -0.353553 0.382683 0.853553 +vn -3.886589 2.219068 3.886589 +v -0.653281 0.382683 0.653281 +vn -5.078070 2.219069 2.103414 +v -0.853553 0.382683 0.353553 +vn -3.947707 1.910998 0.000000 +v -0.923880 0.382683 0.000000 +vn -5.078070 2.219069 -2.103414 +v -0.853553 0.382683 -0.353553 +vn -3.886589 2.219068 -3.886589 +v -0.653281 0.382683 -0.653281 +vn -2.103414 2.219069 -5.078070 +v -0.353553 0.382683 -0.853553 +vn 0.000000 2.219063 -5.496461 +v -0.000000 0.382683 -0.923880 +vn 2.103414 2.219069 -5.078070 +v 0.353553 0.382683 -0.853553 +vn 3.886589 2.219068 -3.886589 +v 0.653281 0.382683 -0.653281 +vn 5.078070 2.219069 -2.103414 +v 0.853553 0.382683 -0.353553 +vn 5.901844 0.000000 0.000000 +v 1.000000 0.000000 0.000000 +vn 4.201753 -0.293491 1.422756 +v 0.923880 0.000000 0.382683 +vn 2.922393 -0.293491 3.337447 +v 0.707107 0.000000 0.707107 +vn 1.861405 -0.139455 4.858237 +v 0.382683 0.000000 0.923880 +vn 0.000000 0.000000 5.901843 +v 0.000000 0.000000 1.000000 +vn -2.258536 0.000000 5.452585 +v -0.382683 0.000000 0.923880 +vn -4.173226 -0.000000 4.173227 +v -0.707107 0.000000 0.707107 +vn -4.678209 -0.154032 2.104504 +v -0.923880 0.000000 0.382683 +vn -2.950922 -0.586972 0.000000 +v -1.000000 0.000000 0.000000 +vn -4.083860 -0.014577 -1.707372 +v -0.923880 0.000000 -0.382683 +vn -4.173227 -0.000000 -4.173226 +v -0.707107 0.000000 -0.707107 +vn -2.258537 -0.000000 -5.452585 +v -0.382683 0.000000 -0.923880 +vn 0.000000 0.000000 -5.901843 +v -0.000000 0.000000 -1.000000 +vn 2.258536 0.000000 -5.452585 +v 0.382683 0.000000 -0.923880 +vn 4.173226 -0.000000 -4.173227 +v 0.707107 0.000000 -0.707107 +vn 5.452586 -0.000000 -2.258537 +v 0.923880 0.000000 -0.382683 +vn 5.496461 -2.219063 -0.000000 +v 0.923880 -0.382683 0.000000 +vn 5.078070 -2.219069 2.103414 +v 0.853553 -0.382683 0.353553 +vn 3.886589 -2.219068 3.886589 +v 0.653281 -0.382683 0.653281 +vn 2.103414 -2.219069 5.078070 +v 0.353553 -0.382683 0.853553 +vn 0.000000 -2.219063 5.496460 +v 0.000000 -0.382683 0.923880 +vn -2.103414 -2.219069 5.078070 +v -0.353553 -0.382683 0.853553 +vn -3.886589 -2.219068 3.886589 +v -0.653281 -0.382683 0.653281 +vn -5.078070 -2.219069 2.103414 +v -0.853553 -0.382683 0.353553 +vn -5.496460 -2.219063 0.000000 +v -0.923880 -0.382683 0.000000 +vn -3.765099 -1.910999 -1.226114 +v -0.853553 -0.382683 -0.353553 +vn -3.230105 -2.065033 -3.447940 +v -0.653281 -0.382683 -0.653281 +vn -2.103414 -2.219069 -5.078070 +v -0.353553 -0.382683 -0.853553 +vn 0.000000 -2.219063 -5.496460 +v -0.000000 -0.382683 -0.923880 +vn 2.103414 -2.219069 -5.078070 +v 0.353553 -0.382683 -0.853553 +vn 3.886589 -2.219068 -3.886589 +v 0.653281 -0.382683 -0.653281 +vn 5.078070 -2.219069 -2.103414 +v 0.853553 -0.382683 -0.353553 +vn 4.310172 -4.168058 -0.000000 +v 0.707107 -0.707107 0.000000 +vn 3.982084 -4.168060 1.649437 +v 0.653281 -0.707107 0.270598 +vn 3.047755 -4.168053 3.047756 +v 0.500000 -0.707107 0.500000 +vn 1.649437 -4.168060 3.982084 +v 0.270598 -0.707107 0.653281 +vn 0.000000 -4.168058 4.310172 +v 0.000000 -0.707107 0.707107 +vn -1.649437 -4.168060 3.982084 +v -0.270598 -0.707107 0.653281 +vn -3.047756 -4.168053 3.047755 +v -0.500000 -0.707107 0.500000 +vn -3.982084 -4.168060 1.649437 +v -0.653281 -0.707107 0.270598 +vn -4.310172 -4.168058 0.000000 +v -0.707107 -0.707107 0.000000 +vn -3.982084 -4.168060 -1.649437 +v -0.653281 -0.707107 -0.270598 +vn -3.047755 -4.168053 -3.047756 +v -0.500000 -0.707107 -0.500000 +vn -1.649437 -4.168060 -3.982084 +v -0.270598 -0.707107 -0.653281 +vn -0.000000 -4.168058 -4.310172 +v -0.000000 -0.707107 -0.707107 +vn 1.649437 -4.168060 -3.982084 +v 0.270598 -0.707107 -0.653281 +vn 3.047756 -4.168053 -3.047755 +v 0.500000 -0.707107 -0.500000 +vn 3.982084 -4.168060 -1.649437 +v 0.653281 -0.707107 -0.270598 +vn 2.452126 -5.567174 0.000000 +v 0.382683 -0.923880 0.000000 +vn 2.265471 -5.567173 0.938388 +v 0.353553 -0.923880 0.146447 +vn 1.733915 -5.567173 1.733915 +v 0.270598 -0.923880 0.270598 +vn 0.938388 -5.567173 2.265471 +v 0.146447 -0.923880 0.353553 +vn -0.000000 -5.567174 2.452126 +v 0.000000 -0.923880 0.382683 +vn -0.938388 -5.567173 2.265471 +v -0.146447 -0.923880 0.353553 +vn -1.733915 -5.567173 1.733915 +v -0.270598 -0.923880 0.270598 +vn -2.265471 -5.567173 0.938388 +v -0.353553 -0.923880 0.146447 +vn -2.452126 -5.567174 -0.000000 +v -0.382683 -0.923880 0.000000 +vn -2.265471 -5.567173 -0.938388 +v -0.353553 -0.923880 -0.146447 +vn -1.733915 -5.567173 -1.733915 +v -0.270598 -0.923880 -0.270598 +vn -0.938388 -5.567173 -2.265471 +v -0.146447 -0.923880 -0.353553 +vn 0.000000 -5.567174 -2.452126 +v -0.000000 -0.923880 -0.382683 +vn 0.938388 -5.567173 -2.265471 +v 0.146447 -0.923880 -0.353553 +vn 1.733915 -5.567173 -1.733915 +v 0.270598 -0.923880 -0.270598 +vn 2.265471 -5.567173 -0.938388 +v 0.353553 -0.923880 -0.146447 +vn 0.000000 0.000000 0.000000 +v 0.000000 1.000000 0.000000 +vn -0.000000 -6.038007 0.000000 +v 0.000000 -1.000000 0.000000 +# 114 vertices, 0 vertices normals + +f 17//17 33//33 32//32 +f 18//18 33//33 17//17 +f 18//18 35//35 34//34 +f 19//19 35//35 18//18 +f 20//20 35//35 19//19 +f 20//20 37//37 36//36 +f 21//21 37//37 20//20 +f 22//22 37//37 21//21 +f 22//22 39//39 38//38 +f 23//23 39//39 22//22 +f 24//24 39//39 23//23 +f 24//24 41//41 40//40 +f 25//25 41//41 24//24 +f 26//26 41//41 25//25 +f 26//26 43//43 42//42 +f 27//27 43//43 26//26 +f 28//28 43//43 27//27 +f 28//28 45//45 44//44 +f 29//29 45//45 28//28 +f 30//30 45//45 29//29 +f 30//30 47//47 46//46 +f 31//31 47//47 30//30 +f 32//32 33//33 48//48 +f 32//32 47//47 31//31 +f 33//33 50//50 49//49 +f 33//33 64//64 48//48 +f 34//34 33//33 18//18 +f 34//34 50//50 33//33 +f 35//35 52//52 51//51 +f 36//36 35//35 20//20 +f 37//37 52//52 36//36 +f 37//37 54//54 53//53 +f 38//38 37//37 22//22 +f 38//38 54//54 37//37 +f 39//39 54//54 38//38 +f 39//39 56//56 55//55 +f 40//40 39//39 24//24 +f 40//40 56//56 39//39 +f 41//41 56//56 40//40 +f 42//42 41//41 26//26 +f 42//42 58//58 41//41 +f 43//43 58//58 42//42 +f 43//43 60//60 59//59 +f 44//44 43//43 28//28 +f 44//44 60//60 43//43 +f 45//45 60//60 44//44 +f 45//45 62//62 61//61 +f 46//46 45//45 30//30 +f 46//46 62//62 45//45 +f 47//47 62//62 46//46 +f 47//47 64//64 63//63 +f 48//48 47//47 32//32 +f 48//48 64//64 47//47 +f 49//49 64//64 33//33 +f 49//49 65//65 64//64 +f 50//50 65//65 49//49 +f 50//50 67//67 66//66 +f 51//51 67//67 50//50 +f 52//52 67//67 51//51 +f 52//52 69//69 68//68 +f 53//53 52//52 37//37 +f 53//53 69//69 52//52 +f 54//54 69//69 53//53 +f 54//54 71//71 70//70 +f 55//55 54//54 39//39 +f 55//55 71//71 54//54 +f 56//56 71//71 55//55 +f 56//56 73//73 72//72 +f 57//57 73//73 56//56 +f 58//58 73//73 57//57 +f 59//59 58//58 43//43 +f 59//59 75//75 58//58 +f 60//60 75//75 59//59 +f 60//60 77//77 76//76 +f 61//61 60//60 45//45 +f 61//61 77//77 60//60 +f 62//62 77//77 61//61 +f 62//62 79//79 78//78 +f 63//63 62//62 47//47 +f 63//63 79//79 62//62 +f 64//64 65//65 80//80 +f 64//64 79//79 63//63 +f 65//65 82//82 81//81 +f 65//65 96//96 80//80 +f 66//66 65//65 50//50 +f 66//66 82//82 65//65 +f 67//67 82//82 66//66 +f 67//67 84//84 83//83 +f 68//68 67//67 52//52 +f 68//68 84//84 67//67 +f 69//69 84//84 68//68 +f 69//69 86//86 85//85 +f 70//70 69//69 54//54 +f 70//70 86//86 69//69 +f 71//71 86//86 70//70 +f 71//71 88//88 87//87 +f 72//72 71//71 56//56 +f 72//72 88//88 71//71 +f 73//73 88//88 72//72 +f 73//73 90//90 89//89 +f 74//74 73//73 58//58 +f 74//74 90//90 73//73 +f 75//75 90//90 74//74 +f 75//75 92//92 91//91 +f 76//76 75//75 60//60 +f 76//76 92//92 75//75 +f 77//77 92//92 76//76 +f 77//77 94//94 93//93 +f 78//78 77//77 62//62 +f 78//78 94//94 77//77 +f 79//79 94//94 78//78 +f 79//79 96//96 95//95 +f 80//80 79//79 64//64 +f 80//80 96//96 79//79 +f 81//81 96//96 65//65 +f 81//81 97//97 96//96 +f 82//82 97//97 81//81 +f 82//82 99//99 98//98 +f 83//83 82//82 67//67 +f 83//83 99//99 82//82 +f 84//84 99//99 83//83 +f 84//84 101//101 100//100 +f 85//85 84//84 69//69 +f 85//85 101//101 84//84 +f 86//86 101//101 85//85 +f 86//86 103//103 102//102 +f 87//87 86//86 71//71 +f 87//87 103//103 86//86 +f 88//88 103//103 87//87 +f 88//88 105//105 104//104 +f 89//89 88//88 73//73 +f 89//89 105//105 88//88 +f 90//90 105//105 89//89 +f 90//90 107//107 106//106 +f 91//91 90//90 75//75 +f 91//91 107//107 90//90 +f 92//92 107//107 91//91 +f 92//92 109//109 108//108 +f 93//93 92//92 77//77 +f 93//93 109//109 92//92 +f 94//94 109//109 93//93 +f 94//94 111//111 110//110 +f 95//95 94//94 79//79 +f 95//95 111//111 94//94 +f 96//96 97//97 112//112 +f 96//96 111//111 95//95 +f 97//97 114//114 112//112 +f 98//98 97//97 82//82 +f 98//98 114//114 97//97 +f 99//99 114//114 98//98 +f 100//100 99//99 84//84 +f 100//100 114//114 99//99 +f 101//101 114//114 100//100 +f 102//102 101//101 86//86 +f 102//102 114//114 101//101 +f 103//103 114//114 102//102 +f 104//104 103//103 88//88 +f 104//104 114//114 103//103 +f 105//105 114//114 104//104 +f 106//106 105//105 90//90 +f 106//106 114//114 105//105 +f 107//107 114//114 106//106 +f 108//108 107//107 92//92 +f 108//108 114//114 107//107 +f 109//109 114//114 108//108 +f 110//110 109//109 94//94 +f 110//110 114//114 109//109 +f 111//111 114//114 110//110 +f 112//112 111//111 96//96 +f 112//112 114//114 111//111 +# 170 faces, 0 coords texture + +# End of File \ No newline at end of file diff --git a/testdata/test_nm.obj.edgebreaker.out b/testdata/test_nm.obj.edgebreaker.out new file mode 100644 index 0000000000000000000000000000000000000000..9e4fddc4e9d157f3794a3471fbd1ff4826f63638 GIT binary patch literal 2546 zcmY+DUx-yz6voetlYas)O5}?QR-oidA*rBt_tr6x1cg8$3o46f$W&0Y%<`bjfL(eB zbcjZU944Vq`@{I4cK43KH;bC0hZ#(3M8rQcO5z|Px_;}fec2t@cdzs9Z+&~MZ=JjH zrRD8wTjVK# z`TMuQ(bdcQ&&@nOY3=P>?{su@^!D}dP)*HyFhRCmuasQCMNhF;i&kN&i3l$fd#%^Mn@}&fN)5Jp81-()6_-(+3Zx zuRPPIzEpp+*G6xR*-D=F%PzRqODy@W!P9f?3tX-TPqV}a=A=GQKQTML5OXemHnlQb zdB)f8z;v`nV&;(ftbHXuJJrCkzIBGYgJ(RQoB7cEe&t+yz*BnV+dzky{0y&VxvE7j zJz8y?YYsTQnkR~<8N+9#T)D#8U5Z-`ngb{Et?4y&amEaYc~ZUXxjuDpE`0jZSoYA! zZ_d1gBYJ@!&hRhmD}7^V!%6-qA6+!F9?8}JEuLldz)$@uPA;C~9rtpl={e!*liqDM z+MgVx8sL>17XK6>-@J7pteX3Ml-=#RKP?h3ShCga9Qrth*E?gJ^Sj$pX^-DY{BEV) z%XhkDSyP z9nr%Fmwdn%{1Kl#z9iq^*}Y|k2YBYE`fEB-t}0C*Jo$|8>OXVH<`OoJZ;gk0?u0eB z4~73u?2WJevH9%$+EBPUyeyt?pTW0NjQnsges+3xeZjXA{q&l8P(D`c^ux7j+*cg< z@PudQxiWm#cdi(9tADQ4kMHQg2aiAafG^1(-ji>w;*0rNX1MxjeD&9KBKMe|;K}Fs z=;<}Fe7Y@8{pQtpu~AzowMD6woRZq}9&KJaH@O!bo!io5yW_#0JNjSv;@Xq(;#b$* zy}MmuZ2hxw%CYN;cN^X^-sQ4f)smXESF6p_liW|uo?gXkC!Ra;-jk2M==qm>C;R!A zFS)l4%<$*mxaWQmoLy@0R)gk{i-!58+5_9W{PvaI^}lAFa#v*r+{g3L# z`TMuQ(bdcQ&&@nOY3=P>?{su@^!D}dP)*HyFhRCmuasQCMNhF;i&kN&i3l$fd#%^Mn@}&fN)5Jp81-()6_-(+3Zx zuRPPIzEpp+*G6xR*-D=F%PzRqODy@W!P9f?3tX-TPqV}a=A=GQKQTML5OXemHnlQb zdB)f8z;v`nV&;(ftbHXuJJrCkzIBGYgJ(RQoB7cEe&t+yz*BnV+dzky{0y&VxvE7j zJz8y?YYsTQnkR~<8N+9#T)D#8U5Z-`ngb{Et?4y&amEaYc~ZUXxjuDpE`0jZSoYA! zZ_d1gBYJ@!&hRhmD}7^V!%6-qA6+!F9?8}JEuLldz)$@uPA;C~9rtpl={e!*liqDM z+MgVx8sL>17XK6>-@J7pteX3Ml-=#RKP?h3ShCga9Qrth*E?gJ^Sj$pX^-DY{BEV) z%XhkDSyP z9nr%Fmwdn%{1Kl#z9iq^*}Y|k2YBYE`fEB-t}0C*Jo$|8>OXVH<`OoJZ;gk0?u0eB z4~73u?2WJevH9%$+EBPUyeyt?pTW0NjQnsges+3xeZjXA{q&l8P(D`c^ux7j+*cg< z@PudQxiWm#cdi(9tADQ4kMHQg2aiAafG^1(-ji>w;*0rNX1MxjeD&9KBKMe|;K}Fs z=;<}Fe7Y@8{pQtpu~AzowMD6woRZq}9&KJaH@O!bo!io5yW_#0JNjSv;@Xq(;#b$* zy}MmuZ2hxw%CYN;cN^X^-sQ4f)smXESF6p_liW|uo?gXkC!Ra;-jk2M==qm>C;R!A zFS)l4%<$*mxaWQmoLy@0R)gk{i-!58+5_9W{PvaI^}lAFa#v*r+{g3Lyxw9dSz(K*8Yk4xw*XFtQ^|4XW!nPmFc{mH>%BivOllo z4&Kch&HQX-XlPsiLSueGv{;#{&Fjpwy~bj%aqd6k%*0H!QEBMl?kP*wbR*B}z2=G9 znY`Dl))r=}-N%}$PUMy0;nG9ZW=}UOOJ~mNg_fRsd{(bIk)Q6@E053Q)ycfkKk(N# zqsKm|jb-}zDLdEsd--Max_<^n_h&zLe*X9)GrzN})4^vw>&CKQIu|Ryu7^42&+H_5 zE9J2Enb{9(pP6%D?K5Aj93E_KJy_0LStl#+H2b=MofR%zC=zH;fOnj`z88>it2ObBer`@?w9xykGmwoO9;Ak&gD6ubf)8f%T=_&&PWd z`~Cddm(Dj|{%Y5S;{GX5&u1<@zq#~&K0nLOj|~rfGML_<_kcNb>3v4b%=EXFQyqMA z)*bnLQ-{w>`_GTDUU@6!%!giJ_W!gm75I__8W@9*%^ zyxdPWFZ%25FZ%WQ>3z9>PygiQ{@L&MEBfbrbJ0KdPdWW}^P>Om{?hxU`TDz)-k_kn92e~UBUeSXMZX8`~p-elzD3=bJeP=DqnJz_rBPc?7`? z(i+Kdw?15-U_l?Q0=Te@9|OrUfD4J20XIoEN={hDvB8DJdjOKI}7Rd?ARsvi|yayn;S_&>CUIyGIt(Bax>>7XziT3~`{Zeot z@iO3cX`SSRWvc)#B;Es%Tq^|^5-$Vpl3%6_48#2ZBp5+AfI9^q zlpc_Rk}zBeK!Op}0k}qBi}a8b^n&3Q01}K~;1%iN1SGBzcSzbQ1u!Og8C76-@Bqs& zf(8Ig!z21I3{VBB2M>^71PuT#3mB2MNkJ7D9y~yT5i|g}EZ{NeQ7Nbb!-EG%FoFgE zmjyf_JuU@RV0iEV2}aNW;Ie?Hq$j1I3Jeb(Ai)S409+QZU3yvys=)Bz0TPU0=zz-t znDwX>3>?E>41fe9=mn@G*r5*?K?8uB19nQ!NI?}C9y~yT5i|g}EZ|vbmlRZi;lTqW z7(oMo%L1O0c1uAO7#=)8f)O+TxGdm#X^#|Cf#JaeBp5*hfXf12koHPJ6&M~oK!Oo8 z0Jtn*pY);>RDt2a10)zh1Axl{UXu1pK@}JtJV1gG419<5asm=Jhx@AZiWI=Ogs`NHBs10G9gs`NHBs10G9=vlxC%%3Jeb( zAi)S409+POlirbnDlj~FfCM9G0B~7AU7C}EDlj~FfCM8L_;G1I0g0Q#Jta9|`WT)y zK!Onr9dJJk9K+)UNHBsq1DL^#F+5R#1S1$N;1(DwhKC4{U +#include #include #include "compression/encode.h" @@ -71,7 +73,7 @@ int StringToInt(const std::string &s) { void PrintOptions(const draco::PointCloud &pc, const Options &options) { printf("Encoder options:\n"); - printf(" Compression level = %d\n", 10 - options.compression_level); + printf(" Compression level = %d\n", options.compression_level); if (options.pos_quantization_bits <= 0) { printf(" Positions: No quantization\n"); } else { @@ -118,8 +120,8 @@ int EncodePointCloudToFile(const draco::PointCloud &pc, return -1; } out_file.write(buffer.data(), buffer.size()); - printf("Encoded point cloud saved to %s (%ld ms to encode)\n", file.c_str(), - timer.GetInMs()); + printf("Encoded point cloud saved to %s (%" PRId64 " ms to encode)\n", + file.c_str(), timer.GetInMs()); printf("\nEncoded size = %zu bytes\n\n", buffer.size()); return 0; } @@ -143,7 +145,7 @@ int EncodeMeshToFile(const draco::Mesh &mesh, return -1; } out_file.write(buffer.data(), buffer.size()); - printf("Encoded mesh saved to %s (%ld ms to encode)\n", file.c_str(), + printf("Encoded mesh saved to %s (%" PRId64 " ms to encode)\n", file.c_str(), timer.GetInMs()); printf("\nEncoded size = %zu bytes\n\n", buffer.size()); return 0;