Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt11
-rwxr-xr-xbuild_files/build_environment/install_deps.sh2
-rw-r--r--build_files/cmake/config/blender_full.cmake1
-rw-r--r--build_files/cmake/config/blender_lite.cmake1
-rw-r--r--build_files/cmake/config/blender_release.cmake1
-rw-r--r--build_files/cmake/macros.cmake14
-rw-r--r--extern/CMakeLists.txt4
-rw-r--r--extern/audaspace/include/file/FileManager.h1
-rw-r--r--extern/binreloc/CMakeLists.txt5
-rw-r--r--extern/bullet2/CMakeLists.txt5
-rw-r--r--extern/ceres/CMakeLists.txt2
-rwxr-xr-xextern/ceres/bundle.sh2
-rw-r--r--extern/clew/CMakeLists.txt5
-rw-r--r--extern/cuew/CMakeLists.txt5
-rw-r--r--extern/curve_fit_nd/CMakeLists.txt5
-rw-r--r--extern/curve_fit_nd/intern/curve_fit_cubic.c2
-rw-r--r--extern/draco/CMakeLists.txt29
-rw-r--r--extern/draco/dracoenc/AUTHORS7
-rw-r--r--extern/draco/dracoenc/CMakeLists.txt188
-rw-r--r--extern/draco/dracoenc/LICENSE202
-rw-r--r--extern/draco/dracoenc/cmake/DracoConfig.cmake3
-rw-r--r--extern/draco/dracoenc/cmake/FindDraco.cmake58
-rw-r--r--extern/draco/dracoenc/cmake/compiler_flags.cmake216
-rw-r--r--extern/draco/dracoenc/cmake/compiler_tests.cmake124
-rw-r--r--extern/draco/dracoenc/cmake/draco_features.cmake57
-rw-r--r--extern/draco/dracoenc/cmake/draco_test_config.h.cmake13
-rw-r--r--extern/draco/dracoenc/cmake/draco_version.cc.cmake21
-rw-r--r--extern/draco/dracoenc/cmake/draco_version.h.cmake21
-rw-r--r--extern/draco/dracoenc/cmake/msvc_runtime.cmake14
-rw-r--r--extern/draco/dracoenc/cmake/sanitizers.cmake19
-rw-r--r--extern/draco/dracoenc/cmake/toolchains/arm-ios-common.cmake13
-rw-r--r--extern/draco/dracoenc/cmake/toolchains/arm64-android-ndk-libcpp.cmake12
-rw-r--r--extern/draco/dracoenc/cmake/toolchains/arm64-ios.cmake14
-rw-r--r--extern/draco/dracoenc/cmake/toolchains/arm64-linux-gcc.cmake18
-rw-r--r--extern/draco/dracoenc/cmake/toolchains/armv7-android-ndk-libcpp.cmake12
-rw-r--r--extern/draco/dracoenc/cmake/toolchains/armv7-ios.cmake14
-rw-r--r--extern/draco/dracoenc/cmake/toolchains/armv7-linux-gcc.cmake24
-rw-r--r--extern/draco/dracoenc/cmake/toolchains/armv7s-ios.cmake14
-rw-r--r--extern/draco/dracoenc/cmake/toolchains/x86-android-ndk-libcpp.cmake12
-rw-r--r--extern/draco/dracoenc/cmake/toolchains/x86_64-android-ndk-libcpp.cmake12
-rw-r--r--extern/draco/dracoenc/cmake/util.cmake74
-rw-r--r--extern/draco/dracoenc/src/draco/animation/keyframe_animation.cc55
-rw-r--r--extern/draco/dracoenc/src/draco/animation/keyframe_animation.h108
-rw-r--r--extern/draco/dracoenc/src/draco/animation/keyframe_animation_decoder.cc29
-rw-r--r--extern/draco/dracoenc/src/draco/animation/keyframe_animation_decoder.h34
-rw-r--r--extern/draco/dracoenc/src/draco/animation/keyframe_animation_encoder.cc28
-rw-r--r--extern/draco/dracoenc/src/draco/animation/keyframe_animation_encoder.h39
-rw-r--r--extern/draco/dracoenc/src/draco/animation/keyframe_animation_encoding_test.cc168
-rw-r--r--extern/draco/dracoenc/src/draco/animation/keyframe_animation_test.cc102
-rw-r--r--extern/draco/dracoenc/src/draco/attributes/attribute_octahedron_transform.cc86
-rw-r--r--extern/draco/dracoenc/src/draco/attributes/attribute_octahedron_transform.h60
-rw-r--r--extern/draco/dracoenc/src/draco/attributes/attribute_quantization_transform.cc173
-rw-r--r--extern/draco/dracoenc/src/draco/attributes/attribute_quantization_transform.h78
-rw-r--r--extern/draco/dracoenc/src/draco/attributes/attribute_transform.cc44
-rw-r--r--extern/draco/dracoenc/src/draco/attributes/attribute_transform.h46
-rw-r--r--extern/draco/dracoenc/src/draco/attributes/attribute_transform_data.h71
-rw-r--r--extern/draco/dracoenc/src/draco/attributes/attribute_transform_type.h30
-rw-r--r--extern/draco/dracoenc/src/draco/attributes/geometry_attribute.cc91
-rw-r--r--extern/draco/dracoenc/src/draco/attributes/geometry_attribute.h304
-rw-r--r--extern/draco/dracoenc/src/draco/attributes/geometry_indices.h54
-rw-r--r--extern/draco/dracoenc/src/draco/attributes/point_attribute.cc205
-rw-r--r--extern/draco/dracoenc/src/draco/attributes/point_attribute.h186
-rw-r--r--extern/draco/dracoenc/src/draco/attributes/point_attribute_test.cc129
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/attributes_decoder.cc97
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/attributes_decoder.h94
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/attributes_decoder_interface.h62
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/attributes_encoder.cc49
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/attributes_encoder.h149
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_decoder.cc515
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_decoder.h46
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_encoder.cc289
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_encoder.h51
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_shared.h28
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/linear_sequencer.h50
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/mesh_attribute_indices_encoding_data.h58
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/normal_compression_utils.h335
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/point_d_vector.h275
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/point_d_vector_test.cc359
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/points_sequencer.h63
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_decoder.h227
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h410
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_shared.h34
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_data.h72
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h46
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_encoder.h46
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_decoder.h163
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_encoder.h175
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_area.h110
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_base.h94
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_decoder.h127
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_encoder.h133
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_decoder.h98
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_encoder.h111
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h72
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_decoder.h335
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_encoder.h313
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_decoder.h131
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_encoder.h129
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h252
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoder.h89
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoder_factory.h186
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoder_interface.h53
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoding_transform.h65
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_delta_decoder.h65
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_delta_encoder.h69
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder.h89
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc70
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h132
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_interface.h55
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoding_transform.h77
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_factory.h83
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_interface.h60
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_decoding_transform.h113
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_encoding_transform.h116
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h101
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_test.cc192
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_decoding_transform.h102
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_encoding_transform.h105
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_base.h89
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_test.cc71
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_decoding_transform.h67
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_encoding_transform.h78
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_transform_base.h116
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoder.cc113
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoder.h87
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoders_controller.cc136
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoders_controller.h60
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoder.cc104
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoder.h133
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoders_controller.cc143
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoders_controller.h110
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_decoder.cc213
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_decoder.h76
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_encoder.cc225
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_encoder.h67
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_encoding_test.cc67
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_decoder.cc89
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_decoder.h83
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_encoder.cc50
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_encoder.h82
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_decoder.cc112
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_decoder.h57
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_encoder.cc74
-rw-r--r--extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_encoder.h52
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_coding_shared.h43
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_decoder.cc67
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_decoder.h54
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_encoder.cc59
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_encoder.h61
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_decoder.cc50
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_decoder.h90
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_encoder.cc39
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_encoder.h89
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/folded_integer_bit_decoder.h76
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/folded_integer_bit_encoder.h82
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_decoder.cc77
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_decoder.h56
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_encoder.cc123
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_encoder.h57
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/rans_coding_test.cc9
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_decoder.cc47
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_decoder.h36
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_encoder.cc30
-rw-r--r--extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_encoder.h36
-rw-r--r--extern/draco/dracoenc/src/draco/compression/config/compression_shared.h153
-rw-r--r--extern/draco/dracoenc/src/draco/compression/config/decoder_options.h34
-rw-r--r--extern/draco/dracoenc/src/draco/compression/config/decoder_options_test.cc67
-rw-r--r--extern/draco/dracoenc/src/draco/compression/config/draco_options.h244
-rw-r--r--extern/draco/dracoenc/src/draco/compression/config/encoder_options.h97
-rw-r--r--extern/draco/dracoenc/src/draco/compression/config/encoding_features.h39
-rw-r--r--extern/draco/dracoenc/src/draco/compression/decode.cc132
-rw-r--r--extern/draco/dracoenc/src/draco/compression/decode.h81
-rw-r--r--extern/draco/dracoenc/src/draco/compression/decode_test.cc196
-rw-r--r--extern/draco/dracoenc/src/draco/compression/encode.cc95
-rw-r--r--extern/draco/dracoenc/src/draco/compression/encode.h140
-rw-r--r--extern/draco/dracoenc/src/draco/compression/encode_base.h121
-rw-r--r--extern/draco/dracoenc/src/draco/compression/encode_test.cc293
-rw-r--r--extern/draco/dracoenc/src/draco/compression/entropy/ans.h514
-rw-r--r--extern/draco/dracoenc/src/draco/compression/entropy/rans_symbol_coding.h54
-rw-r--r--extern/draco/dracoenc/src/draco/compression/entropy/rans_symbol_decoder.h153
-rw-r--r--extern/draco/dracoenc/src/draco/compression/entropy/rans_symbol_encoder.h280
-rw-r--r--extern/draco/dracoenc/src/draco/compression/entropy/shannon_entropy.cc143
-rw-r--r--extern/draco/dracoenc/src/draco/compression/entropy/shannon_entropy.h110
-rw-r--r--extern/draco/dracoenc/src/draco/compression/entropy/shannon_entropy_test.cc56
-rw-r--r--extern/draco/dracoenc/src/draco/compression/entropy/symbol_coding_test.cc170
-rw-r--r--extern/draco/dracoenc/src/draco/compression/entropy/symbol_decoding.cc171
-rw-r--r--extern/draco/dracoenc/src/draco/compression/entropy/symbol_decoding.h29
-rw-r--r--extern/draco/dracoenc/src/draco/compression/entropy/symbol_encoding.cc370
-rw-r--r--extern/draco/dracoenc/src/draco/compression/entropy/symbol_encoding.h47
-rw-r--r--extern/draco/dracoenc/src/draco/compression/expert_encode.cc171
-rw-r--r--extern/draco/dracoenc/src/draco/compression/expert_encode.h147
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_decoder.cc35
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_decoder.h68
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_decoder_helpers.h84
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder.cc67
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder.h55
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl.cc1150
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl.h226
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl_interface.h47
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder.cc184
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder.h73
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder_impl.cc830
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder_impl.h210
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder_impl_interface.h57
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoding_test.cc247
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_shared.h131
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_decoder.h193
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_encoder.h139
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_predictive_decoder.h132
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h171
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_valence_decoder.h202
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_valence_encoder.h223
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder.cc34
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder.h84
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder_helpers.h81
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder_test.cc93
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_decoder.cc150
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_decoder.h39
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_encoder.cc131
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_encoder.h57
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/traverser/depth_first_traverser.h169
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/traverser/max_prediction_degree_traverser.h223
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/traverser/mesh_attribute_indices_encoding_observer.h76
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/traverser/mesh_traversal_sequencer.h110
-rw-r--r--extern/draco/dracoenc/src/draco/compression/mesh/traverser/traverser_base.h85
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.cc26
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.h305
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.cc26
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.h365
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_decoder.cc143
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_decoder.h119
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_encoder.cc94
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_encoder.h124
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.cc45
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.h299
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.cc45
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.h397
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/point_cloud_compression_method.h34
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/point_cloud_types.h75
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/quantize_points_3.h83
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/queuing_policy.h75
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_decoder.cc167
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_decoder.h116
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_encoder.cc284
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_encoder.h158
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_decoder.cc38
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_decoder.h31
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_encoder.cc42
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_encoder.h45
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_encoding_test.cc456
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_decoder.cc41
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_decoder.h33
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_encoder.cc49
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_encoder.h43
-rw-r--r--extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_encoding_test.cc90
-rw-r--r--extern/draco/dracoenc/src/draco/core/bit_utils.cc36
-rw-r--r--extern/draco/dracoenc/src/draco/core/bit_utils.h123
-rw-r--r--extern/draco/dracoenc/src/draco/core/bounding_box.cc23
-rw-r--r--extern/draco/dracoenc/src/draco/core/bounding_box.h52
-rw-r--r--extern/draco/dracoenc/src/draco/core/buffer_bit_coding_test.cc116
-rw-r--r--extern/draco/dracoenc/src/draco/core/cycle_timer.cc49
-rw-r--r--extern/draco/dracoenc/src/draco/core/cycle_timer.h50
-rw-r--r--extern/draco/dracoenc/src/draco/core/data_buffer.cc55
-rw-r--r--extern/draco/dracoenc/src/draco/core/data_buffer.h82
-rw-r--r--extern/draco/dracoenc/src/draco/core/decoder_buffer.cc70
-rw-r--r--extern/draco/dracoenc/src/draco/core/decoder_buffer.h210
-rw-r--r--extern/draco/dracoenc/src/draco/core/divide.cc88
-rw-r--r--extern/draco/dracoenc/src/draco/core/divide.h41
-rw-r--r--extern/draco/dracoenc/src/draco/core/draco_index_type.h183
-rw-r--r--extern/draco/dracoenc/src/draco/core/draco_index_type_vector.h77
-rw-r--r--extern/draco/dracoenc/src/draco/core/draco_test_base.h11
-rw-r--r--extern/draco/dracoenc/src/draco/core/draco_test_utils.cc83
-rw-r--r--extern/draco/dracoenc/src/draco/core/draco_test_utils.h65
-rw-r--r--extern/draco/dracoenc/src/draco/core/draco_tests.cc6
-rw-r--r--extern/draco/dracoenc/src/draco/core/draco_types.cc44
-rw-r--r--extern/draco/dracoenc/src/draco/core/draco_types.h46
-rw-r--r--extern/draco/dracoenc/src/draco/core/draco_version.h27
-rw-r--r--extern/draco/dracoenc/src/draco/core/encoder_buffer.cc90
-rw-r--r--extern/draco/dracoenc/src/draco/core/encoder_buffer.h148
-rw-r--r--extern/draco/dracoenc/src/draco/core/hash_utils.cc57
-rw-r--r--extern/draco/dracoenc/src/draco/core/hash_utils.h64
-rw-r--r--extern/draco/dracoenc/src/draco/core/macros.h96
-rw-r--r--extern/draco/dracoenc/src/draco/core/math_utils.h52
-rw-r--r--extern/draco/dracoenc/src/draco/core/math_utils_test.cc19
-rw-r--r--extern/draco/dracoenc/src/draco/core/options.cc83
-rw-r--r--extern/draco/dracoenc/src/draco/core/options.h140
-rw-r--r--extern/draco/dracoenc/src/draco/core/quantization_utils.cc41
-rw-r--r--extern/draco/dracoenc/src/draco/core/quantization_utils.h81
-rw-r--r--extern/draco/dracoenc/src/draco/core/quantization_utils_test.cc91
-rw-r--r--extern/draco/dracoenc/src/draco/core/status.h75
-rw-r--r--extern/draco/dracoenc/src/draco/core/status_test.cc38
-rw-r--r--extern/draco/dracoenc/src/draco/core/statusor.h81
-rw-r--r--extern/draco/dracoenc/src/draco/core/varint_decoding.h59
-rw-r--r--extern/draco/dracoenc/src/draco/core/varint_encoding.h57
-rw-r--r--extern/draco/dracoenc/src/draco/core/vector_d.h260
-rw-r--r--extern/draco/dracoenc/src/draco/core/vector_d_test.cc205
-rw-r--r--extern/draco/dracoenc/src/draco/draco_features.h8
-rw-r--r--extern/draco/dracoenc/src/draco/io/mesh_io.cc74
-rw-r--r--extern/draco/dracoenc/src/draco/io/mesh_io.h94
-rw-r--r--extern/draco/dracoenc/src/draco/io/obj_decoder.cc691
-rw-r--r--extern/draco/dracoenc/src/draco/io/obj_decoder.h130
-rw-r--r--extern/draco/dracoenc/src/draco/io/obj_decoder_test.cc190
-rw-r--r--extern/draco/dracoenc/src/draco/io/obj_encoder.cc314
-rw-r--r--extern/draco/dracoenc/src/draco/io/obj_encoder.h90
-rw-r--r--extern/draco/dracoenc/src/draco/io/obj_encoder_test.cc106
-rw-r--r--extern/draco/dracoenc/src/draco/io/parser_utils.cc232
-rw-r--r--extern/draco/dracoenc/src/draco/io/parser_utils.h64
-rw-r--r--extern/draco/dracoenc/src/draco/io/ply_decoder.cc285
-rw-r--r--extern/draco/dracoenc/src/draco/io/ply_decoder.h69
-rw-r--r--extern/draco/dracoenc/src/draco/io/ply_decoder_test.cc87
-rw-r--r--extern/draco/dracoenc/src/draco/io/ply_encoder.cc201
-rw-r--r--extern/draco/dracoenc/src/draco/io/ply_encoder.h54
-rw-r--r--extern/draco/dracoenc/src/draco/io/ply_property_reader.h96
-rw-r--r--extern/draco/dracoenc/src/draco/io/ply_property_writer.h94
-rw-r--r--extern/draco/dracoenc/src/draco/io/ply_reader.cc301
-rw-r--r--extern/draco/dracoenc/src/draco/io/ply_reader.h151
-rw-r--r--extern/draco/dracoenc/src/draco/io/ply_reader_test.cc142
-rw-r--r--extern/draco/dracoenc/src/draco/io/point_cloud_io.cc59
-rw-r--r--extern/draco/dracoenc/src/draco/io/point_cloud_io.h89
-rw-r--r--extern/draco/dracoenc/src/draco/io/point_cloud_io_test.cc115
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/corner_table.cc312
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/corner_table.h364
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/corner_table_iterators.h281
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/mesh.cc43
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/mesh.h151
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/mesh_are_equivalent.cc192
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/mesh_are_equivalent.h71
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/mesh_are_equivalent_test.cc99
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/mesh_attribute_corner_table.cc202
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/mesh_attribute_corner_table.h177
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/mesh_cleanup.cc185
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/mesh_cleanup.h43
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/mesh_cleanup_test.cc131
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/mesh_misc_functions.cc58
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/mesh_misc_functions.h61
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/mesh_stripifier.cc99
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/mesh_stripifier.h252
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/triangle_soup_mesh_builder.cc85
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/triangle_soup_mesh_builder.h64
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/triangle_soup_mesh_builder_test.cc197
-rw-r--r--extern/draco/dracoenc/src/draco/mesh/valence_cache.h136
-rw-r--r--extern/draco/dracoenc/src/draco/metadata/geometry_metadata.cc40
-rw-r--r--extern/draco/dracoenc/src/draco/metadata/geometry_metadata.h126
-rw-r--r--extern/draco/dracoenc/src/draco/metadata/metadata.cc130
-rw-r--r--extern/draco/dracoenc/src/draco/metadata/metadata.h190
-rw-r--r--extern/draco/dracoenc/src/draco/metadata/metadata_decoder.cc103
-rw-r--r--extern/draco/dracoenc/src/draco/metadata/metadata_decoder.h42
-rw-r--r--extern/draco/dracoenc/src/draco/metadata/metadata_encoder.cc93
-rw-r--r--extern/draco/dracoenc/src/draco/metadata/metadata_encoder.h41
-rw-r--r--extern/draco/dracoenc/src/draco/metadata/metadata_encoder_test.cc165
-rw-r--r--extern/draco/dracoenc/src/draco/metadata/metadata_test.cc156
-rw-r--r--extern/draco/dracoenc/src/draco/point_cloud/point_cloud.cc255
-rw-r--r--extern/draco/dracoenc/src/draco/point_cloud/point_cloud.h232
-rw-r--r--extern/draco/dracoenc/src/draco/point_cloud/point_cloud_builder.cc73
-rw-r--r--extern/draco/dracoenc/src/draco/point_cloud/point_cloud_builder.h80
-rw-r--r--extern/draco/dracoenc/src/draco/point_cloud/point_cloud_builder_test.cc171
-rw-r--r--extern/draco/dracoenc/src/draco/point_cloud/point_cloud_test.cc131
-rw-r--r--extern/draco/dracoenc/src/draco/tools/draco_decoder.cc176
-rw-r--r--extern/draco/dracoenc/src/draco/tools/draco_encoder.cc373
-rw-r--r--extern/draco/src/draco-compressor.cpp346
-rw-r--r--extern/gflags/CMakeLists.txt5
-rw-r--r--extern/glew-es/CMakeLists.txt5
-rw-r--r--extern/glew/CMakeLists.txt5
-rw-r--r--extern/glog/CMakeLists.txt5
-rw-r--r--extern/gtest/CMakeLists.txt5
-rw-r--r--extern/lzma/CMakeLists.txt5
-rw-r--r--extern/lzo/CMakeLists.txt5
-rw-r--r--extern/rangetree/CMakeLists.txt5
-rw-r--r--extern/sdlew/CMakeLists.txt5
-rw-r--r--extern/wcwidth/CMakeLists.txt5
-rw-r--r--extern/xdnd/CMakeLists.txt5
-rw-r--r--intern/audaspace/CMakeLists.txt5
-rw-r--r--intern/clog/CMakeLists.txt5
-rw-r--r--intern/cycles/blender/CMakeLists.txt5
-rw-r--r--intern/cycles/render/mesh.cpp2
-rw-r--r--intern/dualcon/CMakeLists.txt5
-rw-r--r--intern/eigen/CMakeLists.txt5
-rw-r--r--intern/elbeem/CMakeLists.txt5
-rw-r--r--intern/ghost/CMakeLists.txt5
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm2
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp4
-rw-r--r--intern/glew-mx/CMakeLists.txt5
-rw-r--r--intern/guardedalloc/CMakeLists.txt7
-rw-r--r--intern/iksolver/CMakeLists.txt5
-rw-r--r--intern/itasc/CMakeLists.txt5
-rw-r--r--intern/libmv/CMakeLists.txt4
-rwxr-xr-xintern/libmv/bundle.sh4
-rw-r--r--intern/locale/CMakeLists.txt5
-rw-r--r--intern/memutil/CMakeLists.txt5
-rw-r--r--intern/mikktspace/CMakeLists.txt5
-rw-r--r--intern/numaapi/CMakeLists.txt5
-rw-r--r--intern/opencolorio/CMakeLists.txt5
-rw-r--r--intern/opensubdiv/CMakeLists.txt5
-rw-r--r--intern/openvdb/CMakeLists.txt5
-rw-r--r--intern/rigidbody/CMakeLists.txt5
-rw-r--r--intern/smoke/CMakeLists.txt5
-rw-r--r--intern/string/CMakeLists.txt5
-rw-r--r--intern/utfconv/CMakeLists.txt5
-rw-r--r--readme.rst41
-rw-r--r--release/datafiles/preview.blendbin0 -> 1494238 bytes
-rw-r--r--release/datafiles/preview_cycles.blendbin1278812 -> 0 bytes
-rw-r--r--release/scripts/presets/keyconfig/industry_compatible.py91
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py18
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py3597
-rw-r--r--release/scripts/startup/bl_app_templates_system/Sculpting/__init__.py2
-rw-r--r--release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py2
-rw-r--r--release/scripts/startup/bl_operators/node.py14
-rw-r--r--release/scripts/startup/bl_operators/object.py6
-rw-r--r--release/scripts/startup/bl_operators/object_quick_effects.py11
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_smart_project.py8
-rw-r--r--release/scripts/startup/bl_operators/wm.py4
-rw-r--r--release/scripts/startup/bl_ui/properties_data_armature.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_data_camera.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_cloth.py4
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_common.py12
-rw-r--r--release/scripts/startup/bl_ui/space_text.py2
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py48
-rw-r--r--release/scripts/startup/bl_ui/space_topbar.py12
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py104
-rw-r--r--release/scripts/templates_py/ui_previews_dynamic_enum.py6
-rw-r--r--source/blender/alembic/CMakeLists.txt5
-rw-r--r--source/blender/alembic/intern/abc_exporter.cc36
-rw-r--r--source/blender/alembic/intern/abc_exporter.h4
-rw-r--r--source/blender/alembic/intern/abc_mball.cc48
-rw-r--r--source/blender/alembic/intern/abc_mball.h18
-rw-r--r--source/blender/alembic/intern/abc_mesh.cc9
-rw-r--r--source/blender/alembic/intern/abc_mesh.h1
-rw-r--r--source/blender/alembic/intern/abc_object.cc10
-rw-r--r--source/blender/alembic/intern/abc_util.cc5
-rw-r--r--source/blender/alembic/intern/abc_util.h2
-rw-r--r--source/blender/alembic/intern/alembic_capi.cc2
-rw-r--r--source/blender/avi/CMakeLists.txt5
-rw-r--r--source/blender/blenfont/CMakeLists.txt5
-rw-r--r--source/blender/blenfont/intern/blf_font.c3
-rw-r--r--source/blender/blenkernel/BKE_armature.h8
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h22
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h29
-rw-r--r--source/blender/blenkernel/BKE_modifier.h2
-rw-r--r--source/blender/blenkernel/BKE_scene.h1
-rw-r--r--source/blender/blenkernel/CMakeLists.txt5
-rw-r--r--source/blender/blenkernel/intern/action.c2
-rw-r--r--source/blender/blenkernel/intern/armature.c405
-rw-r--r--source/blender/blenkernel/intern/armature_update.c205
-rw-r--r--source/blender/blenkernel/intern/collection.c2
-rw-r--r--source/blender/blenkernel/intern/constraint.c118
-rw-r--r--source/blender/blenkernel/intern/crazyspace.c7
-rw-r--r--source/blender/blenkernel/intern/font.c11
-rw-r--r--source/blender/blenkernel/intern/gpencil.c50
-rw-r--r--source/blender/blenkernel/intern/image.c10
-rw-r--r--source/blender/blenkernel/intern/key.c4
-rw-r--r--source/blender/blenkernel/intern/library_query.c6
-rw-r--r--source/blender/blenkernel/intern/mesh.c1
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c9
-rw-r--r--source/blender/blenkernel/intern/mesh_validate.c11
-rw-r--r--source/blender/blenkernel/intern/modifier.c2
-rw-r--r--source/blender/blenkernel/intern/object.c14
-rw-r--r--source/blender/blenkernel/intern/object_update.c8
-rw-r--r--source/blender/blenkernel/intern/pbvh.c2
-rw-r--r--source/blender/blenkernel/intern/scene.c31
-rw-r--r--source/blender/blenkernel/intern/softbody.c5
-rw-r--r--source/blender/blenkernel/intern/tracking_stabilize.c13
-rw-r--r--source/blender/blenlib/BLI_math_matrix.h7
-rw-r--r--source/blender/blenlib/BLI_string.h48
-rw-r--r--source/blender/blenlib/CMakeLists.txt5
-rw-r--r--source/blender/blenlib/intern/BLI_ghash.c4
-rw-r--r--source/blender/blenlib/intern/BLI_mempool.c2
-rw-r--r--source/blender/blenlib/intern/edgehash.c2
-rw-r--r--source/blender/blenlib/intern/listbase.c2
-rw-r--r--source/blender/blenlib/intern/math_matrix.c76
-rw-r--r--source/blender/blenlib/intern/path_util.c12
-rw-r--r--source/blender/blenlib/intern/threads.c12
-rw-r--r--source/blender/blenlib/intern/winstuff_dir.c7
-rw-r--r--source/blender/blenloader/CMakeLists.txt5
-rw-r--r--source/blender/blenloader/intern/readfile.c8
-rw-r--r--source/blender/blenloader/intern/versioning_280.c111
-rw-r--r--source/blender/blentranslation/CMakeLists.txt5
-rw-r--r--source/blender/bmesh/CMakeLists.txt5
-rw-r--r--source/blender/collada/BCAnimationSampler.cpp6
-rw-r--r--source/blender/collada/CMakeLists.txt5
-rw-r--r--source/blender/compositor/CMakeLists.txt5
-rw-r--r--source/blender/depsgraph/CMakeLists.txt5
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc50
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc4
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc1
-rw-r--r--source/blender/draw/CMakeLists.txt5
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_cache_utils.c42
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_utils.c2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.c9
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl3
-rw-r--r--source/blender/draw/engines/workbench/workbench_deferred.c2
-rw-r--r--source/blender/draw/engines/workbench/workbench_forward.c10
-rw-r--r--source/blender/draw/engines/workbench/workbench_render.c4
-rw-r--r--source/blender/draw/intern/DRW_render.h4
-rw-r--r--source/blender/draw/intern/draw_armature.c13
-rw-r--r--source/blender/draw/intern/draw_cache.c3
-rw-r--r--source/blender/draw/intern/draw_cache_impl_displist.c4
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c8
-rw-r--r--source/blender/draw/intern/draw_manager.c8
-rw-r--r--source/blender/draw/modes/edit_armature_mode.c5
-rw-r--r--source/blender/draw/modes/edit_mesh_mode.c6
-rw-r--r--source/blender/draw/modes/object_mode.c15
-rw-r--r--source/blender/draw/modes/overlay_mode.c4
-rw-r--r--source/blender/draw/modes/pose_mode.c15
-rw-r--r--source/blender/editors/animation/CMakeLists.txt5
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c4
-rw-r--r--source/blender/editors/animation/anim_intern.h2
-rw-r--r--source/blender/editors/animation/anim_ops.c2
-rw-r--r--source/blender/editors/animation/keyframes_general.c5
-rw-r--r--source/blender/editors/animation/keyframing.c157
-rw-r--r--source/blender/editors/animation/keyingsets.c34
-rw-r--r--source/blender/editors/armature/CMakeLists.txt5
-rw-r--r--source/blender/editors/armature/armature_select.c4
-rw-r--r--source/blender/editors/armature/armature_skinning.c2
-rw-r--r--source/blender/editors/armature/armature_utils.c17
-rw-r--r--source/blender/editors/armature/pose_lib.c2
-rw-r--r--source/blender/editors/armature/pose_slide.c4
-rw-r--r--source/blender/editors/armature/pose_utils.c4
-rw-r--r--source/blender/editors/curve/CMakeLists.txt5
-rw-r--r--source/blender/editors/curve/editcurve.c2
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt7
-rw-r--r--source/blender/editors/gizmo_library/CMakeLists.txt5
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c2
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c2
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt7
-rw-r--r--source/blender/editors/gpencil/annotate_draw.c5
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c26
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_armature.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_brush.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_ops_versioning.c (renamed from source/blender/editors/gpencil/gpencil_old.c)68
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c6
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c26
-rw-r--r--source/blender/editors/include/ED_datafiles.h4
-rw-r--r--source/blender/editors/include/ED_keyframing.h9
-rw-r--r--source/blender/editors/include/ED_numinput.h21
-rw-r--r--source/blender/editors/include/ED_transform.h4
-rw-r--r--source/blender/editors/include/ED_view3d.h7
-rw-r--r--source/blender/editors/include/UI_interface.h6
-rw-r--r--source/blender/editors/interface/CMakeLists.txt5
-rw-r--r--source/blender/editors/interface/interface_align.c2
-rw-r--r--source/blender/editors/interface/interface_region_popover.c2
-rw-r--r--source/blender/editors/interface/interface_templates.c10
-rw-r--r--source/blender/editors/interface/interface_utils.c2
-rw-r--r--source/blender/editors/io/CMakeLists.txt5
-rw-r--r--source/blender/editors/lattice/CMakeLists.txt5
-rw-r--r--source/blender/editors/lattice/editlattice_tools.c10
-rw-r--r--source/blender/editors/mask/CMakeLists.txt5
-rw-r--r--source/blender/editors/mesh/CMakeLists.txt5
-rw-r--r--source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c2
-rw-r--r--source/blender/editors/mesh/editmesh_polybuild.c4
-rw-r--r--source/blender/editors/mesh/editmesh_select.c18
-rw-r--r--source/blender/editors/mesh/editmesh_select_similar.c2
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c2
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c6
-rw-r--r--source/blender/editors/mesh/mesh_mirror.c6
-rw-r--r--source/blender/editors/metaball/CMakeLists.txt5
-rw-r--r--source/blender/editors/object/CMakeLists.txt5
-rw-r--r--source/blender/editors/object/object_relations.c2
-rw-r--r--source/blender/editors/object/object_vgroup.c2
-rw-r--r--source/blender/editors/physics/CMakeLists.txt5
-rw-r--r--source/blender/editors/physics/particle_edit.c8
-rw-r--r--source/blender/editors/render/CMakeLists.txt5
-rw-r--r--source/blender/editors/render/render_preview.c76
-rw-r--r--source/blender/editors/scene/CMakeLists.txt5
-rw-r--r--source/blender/editors/screen/CMakeLists.txt5
-rw-r--r--source/blender/editors/screen/area.c6
-rw-r--r--source/blender/editors/screen/glutil.c7
-rw-r--r--source/blender/editors/screen/screen_edit.c6
-rw-r--r--source/blender/editors/screen/screen_ops.c23
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt5
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c6
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c10
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c8
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c2
-rw-r--r--source/blender/editors/sound/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_action/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_action/action_draw.c37
-rw-r--r--source/blender/editors/space_action/space_action.c1
-rw-r--r--source/blender/editors/space_api/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_clip/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_clip/clip_editor.c2
-rw-r--r--source/blender/editors/space_console/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_file/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_file/filesel.c20
-rw-r--r--source/blender/editors/space_graph/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_image/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_info/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_nla/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c2
-rw-r--r--source/blender/editors/space_nla/nla_edit.c4
-rw-r--r--source/blender/editors/space_node/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_node/node_relationships.c2
-rw-r--r--source/blender/editors/space_outliner/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c2
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c2
-rw-r--r--source/blender/editors/space_script/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_sequencer/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c8
-rw-r--r--source/blender/editors/space_statusbar/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_text/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_text/text_draw.c18
-rw-r--r--source/blender/editors/space_text/text_format_lua.c12
-rw-r--r--source/blender/editors/space_text/text_format_pov.c12
-rw-r--r--source/blender/editors/space_text/text_format_pov_ini.c6
-rw-r--r--source/blender/editors/space_text/text_format_py.c6
-rw-r--r--source/blender/editors/space_topbar/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_userpref/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_view3d/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c11
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c1
-rw-r--r--source/blender/editors/space_view3d/view3d_draw_legacy.c7
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c14
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_armature.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_camera.c21
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_empty.c7
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_forcefield.c7
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_light.c17
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_navigate.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_ruler.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_iterators.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_project.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c14
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c9
-rw-r--r--source/blender/editors/transform/CMakeLists.txt5
-rw-r--r--source/blender/editors/transform/transform.c90
-rw-r--r--source/blender/editors/transform/transform_constraints.c2
-rw-r--r--source/blender/editors/transform/transform_conversions.c32
-rw-r--r--source/blender/editors/transform/transform_generics.c2
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c195
-rw-r--r--source/blender/editors/transform/transform_ops.c55
-rw-r--r--source/blender/editors/transform/transform_snap_object.c28
-rw-r--r--source/blender/editors/undo/CMakeLists.txt5
-rw-r--r--source/blender/editors/util/CMakeLists.txt5
-rw-r--r--source/blender/editors/uvedit/CMakeLists.txt5
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c8
-rw-r--r--source/blender/freestyle/CMakeLists.txt5
-rw-r--r--source/blender/gpencil_modifiers/CMakeLists.txt5
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c2
-rw-r--r--source/blender/gpu/CMakeLists.txt5
-rw-r--r--source/blender/gpu/GPU_framebuffer.h2
-rw-r--r--source/blender/gpu/GPU_immediate.h6
-rw-r--r--source/blender/gpu/GPU_legacy_stubs.h2
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c2
-rw-r--r--source/blender/gpu/intern/gpu_immediate_util.c2
-rw-r--r--source/blender/gpu/intern/gpu_uniformbuffer.c2
-rw-r--r--source/blender/ikplugin/CMakeLists.txt5
-rw-r--r--source/blender/imbuf/CMakeLists.txt5
-rw-r--r--source/blender/imbuf/intern/cineon/CMakeLists.txt5
-rw-r--r--source/blender/imbuf/intern/dds/CMakeLists.txt5
-rw-r--r--source/blender/imbuf/intern/oiio/CMakeLists.txt5
-rw-r--r--source/blender/imbuf/intern/openexr/CMakeLists.txt5
-rw-r--r--source/blender/makesdna/DNA_anim_types.h2
-rw-r--r--source/blender/makesdna/DNA_material_types.h18
-rw-r--r--source/blender/makesdna/DNA_object_force_types.h11
-rw-r--r--source/blender/makesdna/DNA_object_types.h2
-rw-r--r--source/blender/makesdna/DNA_scene_types.h10
-rw-r--r--source/blender/makesdna/DNA_space_types.h2
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h151
-rw-r--r--source/blender/makesdna/intern/CMakeLists.txt14
-rw-r--r--source/blender/makesdna/intern/dna_genfile.c23
-rw-r--r--source/blender/makesdna/intern/makesdna.c2
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt5
-rw-r--r--source/blender/makesrna/intern/rna_access.c2
-rw-r--r--source/blender/makesrna/intern/rna_brush.c4
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c8
-rw-r--r--source/blender/makesrna/intern/rna_material.c12
-rw-r--r--source/blender/makesrna/intern/rna_object.c11
-rw-r--r--source/blender/makesrna/intern/rna_object_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_pose_api.c6
-rw-r--r--source/blender/makesrna/intern/rna_render.c4
-rw-r--r--source/blender/makesrna/intern/rna_scene.c22
-rw-r--r--source/blender/makesrna/intern/rna_space.c86
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c2
-rw-r--r--source/blender/modifiers/CMakeLists.txt5
-rw-r--r--source/blender/nodes/CMakeLists.txt5
-rw-r--r--source/blender/nodes/NOD_static_types.h425
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_scale.c2
-rw-r--r--source/blender/physics/CMakeLists.txt5
-rw-r--r--source/blender/physics/intern/implicit_blender.c12
-rw-r--r--source/blender/python/bmesh/CMakeLists.txt5
-rw-r--r--source/blender/python/generic/CMakeLists.txt5
-rw-r--r--source/blender/python/gpu/CMakeLists.txt5
-rw-r--r--source/blender/python/intern/CMakeLists.txt5
-rw-r--r--source/blender/python/intern/bpy_rna.c2
-rw-r--r--source/blender/python/mathutils/CMakeLists.txt5
-rw-r--r--source/blender/render/CMakeLists.txt5
-rw-r--r--source/blender/render/extern/include/RE_engine.h1
-rw-r--r--source/blender/render/intern/source/multires_bake.c13
-rw-r--r--source/blender/shader_fx/CMakeLists.txt5
-rw-r--r--source/blender/windowmanager/CMakeLists.txt5
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo.c12
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c15
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c21
-rw-r--r--source/blender/windowmanager/intern/wm.c9
-rw-r--r--source/blender/windowmanager/intern/wm_cursors.c25
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c54
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c36
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c324
-rw-r--r--source/blender/windowmanager/intern/wm_files.c65
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c18
-rw-r--r--source/blender/windowmanager/intern/wm_gesture.c52
-rw-r--r--source/blender/windowmanager/intern/wm_gesture_ops.c33
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c14
-rw-r--r--source/blender/windowmanager/intern/wm_jobs.c99
-rw-r--r--source/blender/windowmanager/intern/wm_keymap.c206
-rw-r--r--source/blender/windowmanager/intern/wm_keymap_utils.c6
-rw-r--r--source/blender/windowmanager/intern/wm_menu_type.c6
-rw-r--r--source/blender/windowmanager/intern/wm_operator_props.c15
-rw-r--r--source/blender/windowmanager/intern/wm_operator_type.c26
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c155
-rw-r--r--source/blender/windowmanager/intern/wm_panel_type.c6
-rw-r--r--source/blender/windowmanager/intern/wm_playanim.c91
-rw-r--r--source/blender/windowmanager/intern/wm_stereo.c30
-rw-r--r--source/blender/windowmanager/intern/wm_subwindow.c8
-rw-r--r--source/blender/windowmanager/intern/wm_window.c115
-rw-r--r--source/creator/CMakeLists.txt34
-rw-r--r--source/creator/creator.c4
-rw-r--r--tests/gtests/testing/CMakeLists.txt5
728 files changed, 49051 insertions, 2329 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6a8a7a7fcfa..2f5b8240c88 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -344,6 +344,7 @@ option(WITH_LZMA "Enable best LZMA compression, (used for pointcache)"
if(UNIX AND NOT APPLE)
option(WITH_SYSTEM_LZO "Use the system LZO library" OFF)
endif()
+option(WITH_DRACO "Enable Draco mesh compression Python module (used for glTF)" ON)
# Camera/motion tracking
option(WITH_LIBMV "Enable Libmv structure from motion library" ON)
@@ -636,6 +637,12 @@ endif()
if(NOT WITH_PYTHON)
set(WITH_CYCLES OFF)
+ set(WITH_DRACO OFF)
+endif()
+
+if(WITH_DRACO AND NOT WITH_PYTHON_INSTALL)
+ message(STATUS "WITH_DRACO requires WITH_PYTHON_INSTALL to be ON, disabling WITH_DRACO for now")
+ set(WITH_DRACO OFF)
endif()
# enable boost for cycles, audaspace or i18n
@@ -1374,6 +1381,8 @@ if(CMAKE_COMPILER_IS_GNUCC)
ADD_CHECK_C_COMPILER_FLAG(C_REMOVE_STRICT_FLAGS C_WARN_NO_SWITCH -Wno-switch)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_CLASS_MEMACCESS -Wno-class-memaccess)
+ ADD_CHECK_CXX_COMPILER_FLAG(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_COMMENT -Wno-comment)
+ ADD_CHECK_CXX_COMPILER_FLAG(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_UNUSED_TYPEDEFS -Wno-unused-local-typedefs)
if(CMAKE_COMPILER_IS_GNUCC AND (NOT "${CMAKE_C_COMPILER_VERSION}" VERSION_LESS "7.0"))
ADD_CHECK_C_COMPILER_FLAG(C_REMOVE_STRICT_FLAGS C_WARN_NO_IMPLICIT_FALLTHROUGH -Wno-implicit-fallthrough)
@@ -1426,6 +1435,8 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
ADD_CHECK_CXX_COMPILER_FLAG(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_NON_VIRTUAL_DTOR -Wno-non-virtual-dtor)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_UNUSED_MACROS -Wno-unused-macros)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_REORDER -Wno-reorder)
+ ADD_CHECK_CXX_COMPILER_FLAG(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_COMMENT -Wno-comment)
+ ADD_CHECK_CXX_COMPILER_FLAG(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_UNUSED_TYPEDEFS -Wno-unused-local-typedefs)
elseif(CMAKE_C_COMPILER_ID MATCHES "Intel")
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index dfa82fe4da4..c54af829b38 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -2629,7 +2629,7 @@ compile_FFmpeg() {
./configure --cc="gcc -Wl,--as-needed" \
--extra-ldflags="-pthread -static-libgcc" \
--prefix=$_inst --enable-static \
- --disable-ffplay --disable-ffserver --disable-doc \
+ --disable-ffplay --disable-doc \
--enable-gray \
--enable-avfilter --disable-vdpau \
--disable-bzlib --disable-libgsm --disable-libspeex \
diff --git a/build_files/cmake/config/blender_full.cmake b/build_files/cmake/config/blender_full.cmake
index 9dffc01cf2a..aed3381cbbe 100644
--- a/build_files/cmake/config/blender_full.cmake
+++ b/build_files/cmake/config/blender_full.cmake
@@ -12,6 +12,7 @@ set(WITH_CODEC_FFMPEG ON CACHE BOOL "" FORCE)
set(WITH_CODEC_SNDFILE ON CACHE BOOL "" FORCE)
set(WITH_CYCLES ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_OSL ON CACHE BOOL "" FORCE)
+set(WITH_DRACO ON CACHE BOOL "" FORCE)
set(WITH_FFTW3 ON CACHE BOOL "" FORCE)
set(WITH_LIBMV ON CACHE BOOL "" FORCE)
set(WITH_LIBMV_SCHUR_SPECIALIZATIONS ON CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake
index d440dbf2821..b85176d37f3 100644
--- a/build_files/cmake/config/blender_lite.cmake
+++ b/build_files/cmake/config/blender_lite.cmake
@@ -17,6 +17,7 @@ set(WITH_CODEC_FFMPEG OFF CACHE BOOL "" FORCE)
set(WITH_CODEC_SNDFILE OFF CACHE BOOL "" FORCE)
set(WITH_CYCLES OFF CACHE BOOL "" FORCE)
set(WITH_CYCLES_OSL OFF CACHE BOOL "" FORCE)
+set(WITH_DRACO OFF CACHE BOOL "" FORCE)
set(WITH_FFTW3 OFF CACHE BOOL "" FORCE)
set(WITH_LIBMV OFF CACHE BOOL "" FORCE)
set(WITH_LLVM OFF CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake
index 5c19aaa5ad4..cf0be8d6b3f 100644
--- a/build_files/cmake/config/blender_release.cmake
+++ b/build_files/cmake/config/blender_release.cmake
@@ -13,6 +13,7 @@ set(WITH_CODEC_FFMPEG ON CACHE BOOL "" FORCE)
set(WITH_CODEC_SNDFILE ON CACHE BOOL "" FORCE)
set(WITH_CYCLES ON CACHE BOOL "" FORCE)
set(WITH_CYCLES_OSL ON CACHE BOOL "" FORCE)
+set(WITH_DRACO ON CACHE BOOL "" FORCE)
set(WITH_FFTW3 ON CACHE BOOL "" FORCE)
set(WITH_LIBMV ON CACHE BOOL "" FORCE)
set(WITH_LIBMV_SCHUR_SPECIALIZATIONS ON CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake
index 2a95a9445bb..ea29a0f5233 100644
--- a/build_files/cmake/macros.cmake
+++ b/build_files/cmake/macros.cmake
@@ -222,6 +222,7 @@ function(blender_add_lib__impl
sources
includes
includes_sys
+ libraries
)
# message(STATUS "Configuring library ${name}")
@@ -233,6 +234,13 @@ function(blender_add_lib__impl
add_library(${name} ${sources})
+ # Use for testing 'BLENDER_SORTED_LIBS' removal.
+ if(DEFINED WITHOUT_SORTED_LIBS AND WITHOUT_SORTED_LIBS)
+ if (NOT "${libraries}" STREQUAL "")
+ target_link_libraries(${name} "${libraries}")
+ endif()
+ endif()
+
# works fine without having the includes
# listed is helpful for IDE's (QtCreator/MSVC)
blender_source_group("${sources}")
@@ -257,11 +265,12 @@ function(blender_add_lib_nolist
sources
includes
includes_sys
+ libraries
)
add_cc_flags_custom_test(${name} PARENT_SCOPE)
- blender_add_lib__impl(${name} "${sources}" "${includes}" "${includes_sys}")
+ blender_add_lib__impl(${name} "${sources}" "${includes}" "${includes_sys}" "${libraries}")
endfunction()
function(blender_add_lib
@@ -269,11 +278,12 @@ function(blender_add_lib
sources
includes
includes_sys
+ libraries
)
add_cc_flags_custom_test(${name} PARENT_SCOPE)
- blender_add_lib__impl(${name} "${sources}" "${includes}" "${includes_sys}")
+ blender_add_lib__impl(${name} "${sources}" "${includes}" "${includes_sys}" "${libraries}")
set_property(GLOBAL APPEND PROPERTY BLENDER_LINK_LIBS ${name})
endfunction()
diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt
index 2b18be71941..450f5e64351 100644
--- a/extern/CMakeLists.txt
+++ b/extern/CMakeLists.txt
@@ -41,6 +41,10 @@ if(WITH_BULLET)
endif()
endif()
+if(WITH_DRACO)
+ add_subdirectory(draco)
+endif()
+
# now only available in a branch
#if(WITH_MOD_CLOTH_ELTOPO)
# add_subdirectory(eltopo)
diff --git a/extern/audaspace/include/file/FileManager.h b/extern/audaspace/include/file/FileManager.h
index 03943ea8ae0..56708607ea6 100644
--- a/extern/audaspace/include/file/FileManager.h
+++ b/extern/audaspace/include/file/FileManager.h
@@ -27,6 +27,7 @@
#include <list>
#include <memory>
+#include <string>
AUD_NAMESPACE_BEGIN
diff --git a/extern/binreloc/CMakeLists.txt b/extern/binreloc/CMakeLists.txt
index 524b884450c..766939b4228 100644
--- a/extern/binreloc/CMakeLists.txt
+++ b/extern/binreloc/CMakeLists.txt
@@ -32,7 +32,10 @@ set(SRC
include/binreloc.h
)
+set(LIB
+)
+
add_definitions(-DENABLE_BINRELOC)
-blender_add_lib(extern_binreloc "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(extern_binreloc "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/bullet2/CMakeLists.txt b/extern/bullet2/CMakeLists.txt
index 7a19b553fdb..b9c21bd831f 100644
--- a/extern/bullet2/CMakeLists.txt
+++ b/extern/bullet2/CMakeLists.txt
@@ -407,9 +407,12 @@ set(SRC
src/Bullet-C-Api.h
)
+set(LIB
+)
+
if(CMAKE_COMPILER_IS_GNUCXX)
# needed for gcc 4.6+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive")
endif()
-blender_add_lib(extern_bullet "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(extern_bullet "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/ceres/CMakeLists.txt b/extern/ceres/CMakeLists.txt
index 3a9ade5510a..0531eb71ba5 100644
--- a/extern/ceres/CMakeLists.txt
+++ b/extern/ceres/CMakeLists.txt
@@ -319,4 +319,4 @@ if(WITH_OPENMP)
)
endif()
-blender_add_lib(extern_ceres "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(extern_ceres "${SRC}" "${INC}" "${INC_SYS}" "")
diff --git a/extern/ceres/bundle.sh b/extern/ceres/bundle.sh
index 1c9a2e729e5..e8212aa1d41 100755
--- a/extern/ceres/bundle.sh
+++ b/extern/ceres/bundle.sh
@@ -165,5 +165,5 @@ if(WITH_OPENMP)
)
endif()
-blender_add_lib(extern_ceres "\${SRC}" "\${INC}" "\${INC_SYS}")
+blender_add_lib(extern_ceres "\${SRC}" "\${INC}" "\${INC_SYS}" "")
EOF
diff --git a/extern/clew/CMakeLists.txt b/extern/clew/CMakeLists.txt
index 940db3d7194..806771cb7f4 100644
--- a/extern/clew/CMakeLists.txt
+++ b/extern/clew/CMakeLists.txt
@@ -32,6 +32,9 @@ set(SRC
src/clew.c
)
+set(LIB
+)
+
add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS)
-blender_add_lib(extern_clew "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(extern_clew "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/cuew/CMakeLists.txt b/extern/cuew/CMakeLists.txt
index 1fc5c8f6978..45d10e29799 100644
--- a/extern/cuew/CMakeLists.txt
+++ b/extern/cuew/CMakeLists.txt
@@ -33,4 +33,7 @@ set(SRC
include/cuew.h
)
-blender_add_lib(extern_cuew "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(extern_cuew "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/curve_fit_nd/CMakeLists.txt b/extern/curve_fit_nd/CMakeLists.txt
index cc9efe1c470..f9264f9b890 100644
--- a/extern/curve_fit_nd/CMakeLists.txt
+++ b/extern/curve_fit_nd/CMakeLists.txt
@@ -36,4 +36,7 @@ set(SRC
intern/generic_heap.h
)
-blender_add_lib(extern_curve_fit_nd "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(extern_curve_fit_nd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/curve_fit_nd/intern/curve_fit_cubic.c b/extern/curve_fit_nd/intern/curve_fit_cubic.c
index ed855d34b08..0005cbe5a93 100644
--- a/extern/curve_fit_nd/intern/curve_fit_cubic.c
+++ b/extern/curve_fit_nd/intern/curve_fit_cubic.c
@@ -462,7 +462,7 @@ static double points_calc_circumference_factor(
* We could try support this but will likely cause extreme >1 scales which could cause other issues. */
// assert(angle >= len_tangent);
double factor = (angle / len_tangent);
- assert(factor < (M_PI / 2) + (DBL_EPSILON * 10));
+ assert(factor < (M_PI / 2) + 1e-6);
return factor;
}
else {
diff --git a/extern/draco/CMakeLists.txt b/extern/draco/CMakeLists.txt
new file mode 100644
index 00000000000..c51af24c9a4
--- /dev/null
+++ b/extern/draco/CMakeLists.txt
@@ -0,0 +1,29 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2019, Blender Foundation
+# All rights reserved.
+# ***** END GPL LICENSE BLOCK *****
+
+set(CMAKE_CXX_STANDARD 14)
+
+# Build Draco library.
+add_subdirectory(dracoenc)
+
+# Build blender-draco-exporter module.
+add_library(extern_draco SHARED src/draco-compressor.cpp)
+target_include_directories(extern_draco PUBLIC dracoenc/src)
+target_link_libraries(extern_draco PUBLIC dracoenc)
diff --git a/extern/draco/dracoenc/AUTHORS b/extern/draco/dracoenc/AUTHORS
new file mode 100644
index 00000000000..67f63a67129
--- /dev/null
+++ b/extern/draco/dracoenc/AUTHORS
@@ -0,0 +1,7 @@
+# This is the list of Draco authors for copyright purposes.
+#
+# This does not necessarily list everyone who has contributed code, since in
+# some cases, their employer may be the copyright holder. To see the full list
+# of contributors, see the revision history in source control.
+Google Inc.
+and other contributors
diff --git a/extern/draco/dracoenc/CMakeLists.txt b/extern/draco/dracoenc/CMakeLists.txt
new file mode 100644
index 00000000000..04f54ab9293
--- /dev/null
+++ b/extern/draco/dracoenc/CMakeLists.txt
@@ -0,0 +1,188 @@
+remove_strict_flags()
+
+set(SRC
+ src/draco/animation/keyframe_animation.cc
+ src/draco/animation/keyframe_animation_encoder.cc
+ src/draco/animation/keyframe_animation_encoder.h
+ src/draco/animation/keyframe_animation.h
+ src/draco/attributes/attribute_octahedron_transform.cc
+ src/draco/attributes/attribute_octahedron_transform.h
+ src/draco/attributes/attribute_quantization_transform.cc
+ src/draco/attributes/attribute_quantization_transform.h
+ src/draco/attributes/attribute_transform.cc
+ src/draco/attributes/attribute_transform_data.h
+ src/draco/attributes/attribute_transform.h
+ src/draco/attributes/attribute_transform_type.h
+ src/draco/attributes/geometry_attribute.cc
+ src/draco/attributes/geometry_attribute.h
+ src/draco/attributes/geometry_indices.h
+ src/draco/attributes/point_attribute.cc
+ src/draco/attributes/point_attribute.h
+ src/draco/compression/attributes/attributes_encoder.cc
+ src/draco/compression/attributes/attributes_encoder.h
+ src/draco/compression/attributes/kd_tree_attributes_encoder.cc
+ src/draco/compression/attributes/kd_tree_attributes_encoder.h
+ src/draco/compression/attributes/linear_sequencer.h
+ src/draco/compression/attributes/points_sequencer.h
+ src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h
+ src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_shared.h
+ src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_data.h
+ src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_encoder.h
+ src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_encoder.h
+ src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_area.h
+ src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_base.h
+ src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_encoder.h
+ src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_encoder.h
+ src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h
+ src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_encoder.h
+ src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_encoder.h
+ src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h
+ src/draco/compression/attributes/prediction_schemes/prediction_scheme_delta_encoder.h
+ src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc
+ src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h
+ src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder.h
+ src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_interface.h
+ src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoding_transform.h
+ src/draco/compression/attributes/prediction_schemes/prediction_scheme_factory.h
+ src/draco/compression/attributes/prediction_schemes/prediction_scheme_interface.h
+ src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_encoding_transform.h
+ src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h
+ src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_encoding_transform.h
+ src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_base.h
+ src/draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_encoding_transform.h
+ src/draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_transform_base.h
+ src/draco/compression/attributes/sequential_attribute_encoder.cc
+ src/draco/compression/attributes/sequential_attribute_encoder.h
+ src/draco/compression/attributes/sequential_attribute_encoders_controller.cc
+ src/draco/compression/attributes/sequential_attribute_encoders_controller.h
+ src/draco/compression/attributes/sequential_integer_attribute_encoder.cc
+ src/draco/compression/attributes/sequential_integer_attribute_encoder.h
+ src/draco/compression/attributes/sequential_normal_attribute_encoder.cc
+ src/draco/compression/attributes/sequential_normal_attribute_encoder.h
+ src/draco/compression/attributes/sequential_quantization_attribute_encoder.cc
+ src/draco/compression/attributes/sequential_quantization_attribute_encoder.h
+ src/draco/compression/bit_coders/adaptive_rans_bit_coding_shared.h
+ src/draco/compression/bit_coders/adaptive_rans_bit_encoder.cc
+ src/draco/compression/bit_coders/adaptive_rans_bit_encoder.h
+ src/draco/compression/bit_coders/direct_bit_encoder.cc
+ src/draco/compression/bit_coders/direct_bit_encoder.h
+ src/draco/compression/bit_coders/folded_integer_bit_encoder.h
+ src/draco/compression/bit_coders/rans_bit_encoder.cc
+ src/draco/compression/bit_coders/rans_bit_encoder.h
+ src/draco/compression/bit_coders/symbol_bit_encoder.cc
+ src/draco/compression/bit_coders/symbol_bit_encoder.h
+ src/draco/compression/config/compression_shared.h
+ src/draco/compression/config/draco_options.h
+ src/draco/compression/config/encoder_options.h
+ src/draco/compression/config/encoding_features.h
+ src/draco/compression/encode_base.h
+ src/draco/compression/encode.cc
+ src/draco/compression/encode.h
+ src/draco/compression/entropy/ans.h
+ src/draco/compression/entropy/rans_symbol_coding.h
+ src/draco/compression/entropy/rans_symbol_encoder.h
+ src/draco/compression/entropy/shannon_entropy.cc
+ src/draco/compression/entropy/shannon_entropy.h
+ src/draco/compression/entropy/symbol_encoding.cc
+ src/draco/compression/entropy/symbol_encoding.h
+ src/draco/compression/expert_encode.cc
+ src/draco/compression/expert_encode.h
+ src/draco/compression/mesh/mesh_edgebreaker_encoder.cc
+ src/draco/compression/mesh/mesh_edgebreaker_encoder.h
+ src/draco/compression/mesh/mesh_edgebreaker_encoder_impl.cc
+ src/draco/compression/mesh/mesh_edgebreaker_encoder_impl.h
+ src/draco/compression/mesh/mesh_edgebreaker_encoder_impl_interface.h
+ src/draco/compression/mesh/mesh_edgebreaker_shared.h
+ src/draco/compression/mesh/mesh_edgebreaker_traversal_encoder.h
+ src/draco/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h
+ src/draco/compression/mesh/mesh_edgebreaker_traversal_valence_encoder.h
+ src/draco/compression/mesh/mesh_encoder.cc
+ src/draco/compression/mesh/mesh_encoder.h
+ src/draco/compression/mesh/mesh_encoder_helpers.h
+ src/draco/compression/mesh/mesh_sequential_encoder.cc
+ src/draco/compression/mesh/mesh_sequential_encoder.h
+ src/draco/compression/mesh/traverser/depth_first_traverser.h
+ src/draco/compression/mesh/traverser/max_prediction_degree_traverser.h
+ src/draco/compression/mesh/traverser/mesh_attribute_indices_encoding_observer.h
+ src/draco/compression/mesh/traverser/mesh_traversal_sequencer.h
+ src/draco/compression/mesh/traverser/traverser_base.h
+ src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.cc
+ src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.h
+ src/draco/compression/point_cloud/algorithms/float_points_tree_encoder.cc
+ src/draco/compression/point_cloud/algorithms/float_points_tree_encoder.h
+ src/draco/compression/point_cloud/algorithms/point_cloud_compression_method.h
+ src/draco/compression/point_cloud/algorithms/point_cloud_types.h
+ src/draco/compression/point_cloud/algorithms/quantize_points_3.h
+ src/draco/compression/point_cloud/algorithms/queuing_policy.h
+ src/draco/compression/point_cloud/point_cloud_encoder.cc
+ src/draco/compression/point_cloud/point_cloud_encoder.h
+ src/draco/compression/point_cloud/point_cloud_kd_tree_encoder.cc
+ src/draco/compression/point_cloud/point_cloud_kd_tree_encoder.h
+ src/draco/compression/point_cloud/point_cloud_sequential_encoder.cc
+ src/draco/compression/point_cloud/point_cloud_sequential_encoder.h
+ src/draco/core/bit_utils.cc
+ src/draco/core/bit_utils.h
+ src/draco/core/bounding_box.cc
+ src/draco/core/bounding_box.h
+ src/draco/core/cycle_timer.cc
+ src/draco/core/cycle_timer.h
+ src/draco/core/data_buffer.cc
+ src/draco/core/data_buffer.h
+ src/draco/core/divide.cc
+ src/draco/core/divide.h
+ src/draco/core/draco_index_type.h
+ src/draco/core/draco_index_type_vector.h
+ src/draco/core/draco_types.cc
+ src/draco/core/draco_types.h
+ src/draco/core/encoder_buffer.cc
+ src/draco/core/encoder_buffer.h
+ src/draco/core/hash_utils.cc
+ src/draco/core/hash_utils.h
+ src/draco/core/macros.h
+ src/draco/core/math_utils.h
+ src/draco/core/options.cc
+ src/draco/core/options.h
+ src/draco/core/quantization_utils.cc
+ src/draco/core/quantization_utils.h
+ src/draco/core/status.h
+ src/draco/core/statusor.h
+ src/draco/core/varint_encoding.h
+ src/draco/core/vector_d.h
+ src/draco/mesh/corner_table.cc
+ src/draco/mesh/corner_table.h
+ src/draco/mesh/corner_table_iterators.h
+ src/draco/mesh/mesh_are_equivalent.cc
+ src/draco/mesh/mesh_are_equivalent.h
+ src/draco/mesh/mesh_attribute_corner_table.cc
+ src/draco/mesh/mesh_attribute_corner_table.h
+ src/draco/mesh/mesh.cc
+ src/draco/mesh/mesh_cleanup.cc
+ src/draco/mesh/mesh_cleanup.h
+ src/draco/mesh/mesh.h
+ src/draco/mesh/mesh_misc_functions.cc
+ src/draco/mesh/mesh_misc_functions.h
+ src/draco/mesh/mesh_stripifier.cc
+ src/draco/mesh/mesh_stripifier.h
+ src/draco/mesh/triangle_soup_mesh_builder.cc
+ src/draco/mesh/triangle_soup_mesh_builder.h
+ src/draco/mesh/valence_cache.h
+ src/draco/metadata/geometry_metadata.cc
+ src/draco/metadata/geometry_metadata.h
+ src/draco/metadata/metadata.cc
+ src/draco/metadata/metadata_encoder.cc
+ src/draco/metadata/metadata_encoder.h
+ src/draco/metadata/metadata.h
+ src/draco/point_cloud/point_cloud_builder.cc
+ src/draco/point_cloud/point_cloud_builder.h
+ src/draco/point_cloud/point_cloud.cc
+ src/draco/point_cloud/point_cloud.h
+)
+
+set(LIB
+)
+
+set(INC
+ src
+)
+
+blender_add_lib(dracoenc "${SRC}" "${INC}" "" "${LIB}")
diff --git a/extern/draco/dracoenc/LICENSE b/extern/draco/dracoenc/LICENSE
new file mode 100644
index 00000000000..7a4a3ea2424
--- /dev/null
+++ b/extern/draco/dracoenc/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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. \ No newline at end of file
diff --git a/extern/draco/dracoenc/cmake/DracoConfig.cmake b/extern/draco/dracoenc/cmake/DracoConfig.cmake
new file mode 100644
index 00000000000..be5e1faefe2
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/DracoConfig.cmake
@@ -0,0 +1,3 @@
+@PACKAGE_INIT@
+set_and_check(draco_INCLUDE_DIR "@PACKAGE_draco_include_install_dir@")
+set_and_check(draco_LIBRARY_DIR "@PACKAGE_draco_lib_install_dir@")
diff --git a/extern/draco/dracoenc/cmake/FindDraco.cmake b/extern/draco/dracoenc/cmake/FindDraco.cmake
new file mode 100644
index 00000000000..5f27bf29390
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/FindDraco.cmake
@@ -0,0 +1,58 @@
+# Finddraco
+#
+# Locates draco and sets the following variables:
+#
+# draco_FOUND
+# draco_INCLUDE_DIRS
+# draco_LIBARY_DIRS
+# draco_LIBRARIES
+# draco_VERSION_STRING
+#
+# draco_FOUND is set to YES only when all other variables are successfully
+# configured.
+
+unset(draco_FOUND)
+unset(draco_INCLUDE_DIRS)
+unset(draco_LIBRARY_DIRS)
+unset(draco_LIBRARIES)
+unset(draco_VERSION_STRING)
+
+mark_as_advanced(draco_FOUND)
+mark_as_advanced(draco_INCLUDE_DIRS)
+mark_as_advanced(draco_LIBRARY_DIRS)
+mark_as_advanced(draco_LIBRARIES)
+mark_as_advanced(draco_VERSION_STRING)
+
+set(draco_version_file_no_prefix "draco/src/draco/core/draco_version.h")
+
+# Set draco_INCLUDE_DIRS
+find_path(draco_INCLUDE_DIRS NAMES "${draco_version_file_no_prefix}")
+
+# Extract the version string from draco_version.h.
+if (draco_INCLUDE_DIRS)
+ set(draco_version_file
+ "${draco_INCLUDE_DIRS}/draco/src/draco/core/draco_version.h")
+ file(STRINGS "${draco_version_file}" draco_version
+ REGEX "kdracoVersion")
+ list(GET draco_version 0 draco_version)
+ string(REPLACE "static const char kdracoVersion[] = " "" draco_version
+ "${draco_version}")
+ string(REPLACE ";" "" draco_version "${draco_version}")
+ string(REPLACE "\"" "" draco_version "${draco_version}")
+ set(draco_VERSION_STRING ${draco_version})
+endif ()
+
+# Find the library.
+if (BUILD_SHARED_LIBS)
+ find_library(draco_LIBRARIES NAMES draco.dll libdraco.dylib libdraco.so)
+else ()
+ find_library(draco_LIBRARIES NAMES draco.lib libdraco.a)
+endif ()
+
+# Store path to library.
+get_filename_component(draco_LIBRARY_DIRS ${draco_LIBRARIES} DIRECTORY)
+
+if (draco_INCLUDE_DIRS AND draco_LIBRARY_DIRS AND draco_LIBRARIES AND
+ draco_VERSION_STRING)
+ set(draco_FOUND YES)
+endif ()
diff --git a/extern/draco/dracoenc/cmake/compiler_flags.cmake b/extern/draco/dracoenc/cmake/compiler_flags.cmake
new file mode 100644
index 00000000000..d842a8ab1f4
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/compiler_flags.cmake
@@ -0,0 +1,216 @@
+if (NOT DRACO_CMAKE_COMPILER_FLAGS_CMAKE_)
+set(DRACO_CMAKE_COMPILER_FLAGS_CMAKE_ 1)
+
+include(CheckCCompilerFlag)
+include(CheckCXXCompilerFlag)
+include("${draco_root}/cmake/compiler_tests.cmake")
+
+# Strings used to cache failed C/CXX flags.
+set(DRACO_FAILED_C_FLAGS)
+set(DRACO_FAILED_CXX_FLAGS)
+
+# Checks C compiler for support of $c_flag. Adds $c_flag to $CMAKE_C_FLAGS when
+# the compile test passes. Caches $c_flag in $DRACO_FAILED_C_FLAGS when the test
+# fails.
+macro (add_c_flag_if_supported c_flag)
+ unset(C_FLAG_FOUND CACHE)
+ string(FIND "${CMAKE_C_FLAGS}" "${c_flag}" C_FLAG_FOUND)
+ unset(C_FLAG_FAILED CACHE)
+ string(FIND "${DRACO_FAILED_C_FLAGS}" "${c_flag}" C_FLAG_FAILED)
+
+ if (${C_FLAG_FOUND} EQUAL -1 AND ${C_FLAG_FAILED} EQUAL -1)
+ unset(C_FLAG_SUPPORTED CACHE)
+ message("Checking C compiler flag support for: " ${c_flag})
+ check_c_compiler_flag("${c_flag}" C_FLAG_SUPPORTED)
+ if (${C_FLAG_SUPPORTED})
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${c_flag}" CACHE STRING "")
+ else ()
+ set(DRACO_FAILED_C_FLAGS "${DRACO_FAILED_C_FLAGS} ${c_flag}" CACHE STRING
+ "" FORCE)
+ endif ()
+ endif ()
+endmacro ()
+
+# Checks C++ compiler for support of $cxx_flag. Adds $cxx_flag to
+# $CMAKE_CXX_FLAGS when the compile test passes. Caches $c_flag in
+# $DRACO_FAILED_CXX_FLAGS when the test fails.
+macro (add_cxx_flag_if_supported cxx_flag)
+ unset(CXX_FLAG_FOUND CACHE)
+ string(FIND "${CMAKE_CXX_FLAGS}" "${cxx_flag}" CXX_FLAG_FOUND)
+ unset(CXX_FLAG_FAILED CACHE)
+ string(FIND "${DRACO_FAILED_CXX_FLAGS}" "${cxx_flag}" CXX_FLAG_FAILED)
+
+ if (${CXX_FLAG_FOUND} EQUAL -1 AND ${CXX_FLAG_FAILED} EQUAL -1)
+ unset(CXX_FLAG_SUPPORTED CACHE)
+ message("Checking CXX compiler flag support for: " ${cxx_flag})
+ check_cxx_compiler_flag("${cxx_flag}" CXX_FLAG_SUPPORTED)
+ if (${CXX_FLAG_SUPPORTED})
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${cxx_flag}" CACHE STRING "")
+ else()
+ set(DRACO_FAILED_CXX_FLAGS "${DRACO_FAILED_CXX_FLAGS} ${cxx_flag}" CACHE
+ STRING "" FORCE)
+ endif ()
+ endif ()
+endmacro ()
+
+# Convenience method for adding a flag to both the C and C++ compiler command
+# lines.
+macro (add_compiler_flag_if_supported flag)
+ add_c_flag_if_supported(${flag})
+ add_cxx_flag_if_supported(${flag})
+endmacro ()
+
+# Checks C compiler for support of $c_flag and terminates generation when
+# support is not present.
+macro (require_c_flag c_flag update_c_flags)
+ unset(C_FLAG_FOUND CACHE)
+ string(FIND "${CMAKE_C_FLAGS}" "${c_flag}" C_FLAG_FOUND)
+
+ if (${C_FLAG_FOUND} EQUAL -1)
+ unset(HAVE_C_FLAG CACHE)
+ message("Checking C compiler flag support for: " ${c_flag})
+ check_c_compiler_flag("${c_flag}" HAVE_C_FLAG)
+ if (NOT ${HAVE_C_FLAG})
+ message(FATAL_ERROR
+ "${PROJECT_NAME} requires support for C flag: ${c_flag}.")
+ endif ()
+ if (${update_c_flags})
+ set(CMAKE_C_FLAGS "${c_flag} ${CMAKE_C_FLAGS}" CACHE STRING "" FORCE)
+ endif ()
+ endif ()
+endmacro ()
+
+# Checks CXX compiler for support of $cxx_flag and terminates generation when
+# support is not present.
+macro (require_cxx_flag cxx_flag update_cxx_flags)
+ unset(CXX_FLAG_FOUND CACHE)
+ string(FIND "${CMAKE_CXX_FLAGS}" "${cxx_flag}" CXX_FLAG_FOUND)
+
+ if (${CXX_FLAG_FOUND} EQUAL -1)
+ unset(HAVE_CXX_FLAG CACHE)
+ message("Checking CXX compiler flag support for: " ${cxx_flag})
+ check_cxx_compiler_flag("${cxx_flag}" HAVE_CXX_FLAG)
+ if (NOT ${HAVE_CXX_FLAG})
+ message(FATAL_ERROR
+ "${PROJECT_NAME} requires support for CXX flag: ${cxx_flag}.")
+ endif ()
+ if (${update_cxx_flags})
+ set(CMAKE_CXX_FLAGS "${cxx_flag} ${CMAKE_CXX_FLAGS}" CACHE STRING ""
+ FORCE)
+ endif ()
+ endif ()
+endmacro ()
+
+# Checks for support of $flag by both the C and CXX compilers. Terminates
+# generation when support is not present in both compilers.
+macro (require_compiler_flag flag update_cmake_flags)
+ require_c_flag(${flag} ${update_cmake_flags})
+ require_cxx_flag(${flag} ${update_cmake_flags})
+endmacro ()
+
+# Checks only non-MSVC targets for support of $c_flag and terminates generation
+# when support is not present.
+macro (require_c_flag_nomsvc c_flag update_c_flags)
+ if (NOT MSVC)
+ require_c_flag(${c_flag} ${update_c_flags})
+ endif ()
+endmacro ()
+
+# Checks only non-MSVC targets for support of $cxx_flag and terminates
+# generation when support is not present.
+macro (require_cxx_flag_nomsvc cxx_flag update_cxx_flags)
+ if (NOT MSVC)
+ require_cxx_flag(${cxx_flag} ${update_cxx_flags})
+ endif ()
+endmacro ()
+
+# Checks only non-MSVC targets for support of $flag by both the C and CXX
+# compilers. Terminates generation when support is not present in both
+# compilers.
+macro (require_compiler_flag_nomsvc flag update_cmake_flags)
+ require_c_flag_nomsvc(${flag} ${update_cmake_flags})
+ require_cxx_flag_nomsvc(${flag} ${update_cmake_flags})
+endmacro ()
+
+# Adds $flag to assembler command line.
+macro (append_as_flag flag)
+ unset(AS_FLAG_FOUND CACHE)
+ string(FIND "${DRACO_AS_FLAGS}" "${flag}" AS_FLAG_FOUND)
+
+ if (${AS_FLAG_FOUND} EQUAL -1)
+ set(DRACO_AS_FLAGS "${DRACO_AS_FLAGS} ${flag}")
+ endif ()
+endmacro ()
+
+# Adds $flag to the C compiler command line.
+macro (append_c_flag flag)
+ unset(C_FLAG_FOUND CACHE)
+ string(FIND "${CMAKE_C_FLAGS}" "${flag}" C_FLAG_FOUND)
+
+ if (${C_FLAG_FOUND} EQUAL -1)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}")
+ endif ()
+endmacro ()
+
+# Adds $flag to the CXX compiler command line.
+macro (append_cxx_flag flag)
+ unset(CXX_FLAG_FOUND CACHE)
+ string(FIND "${CMAKE_CXX_FLAGS}" "${flag}" CXX_FLAG_FOUND)
+
+ if (${CXX_FLAG_FOUND} EQUAL -1)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}")
+ endif ()
+endmacro ()
+
+# Adds $flag to the C and CXX compiler command lines.
+macro (append_compiler_flag flag)
+ append_c_flag(${flag})
+ append_cxx_flag(${flag})
+endmacro ()
+
+# Adds $flag to the executable linker command line.
+macro (append_exe_linker_flag flag)
+ unset(LINKER_FLAG_FOUND CACHE)
+ string(FIND "${CMAKE_EXE_LINKER_FLAGS}" "${flag}" LINKER_FLAG_FOUND)
+
+ if (${LINKER_FLAG_FOUND} EQUAL -1)
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${flag}")
+ endif ()
+endmacro ()
+
+# Adds $flag to the link flags for $target.
+function (append_link_flag_to_target target flags)
+ unset(target_link_flags)
+ get_target_property(target_link_flags ${target} LINK_FLAGS)
+
+ if (target_link_flags)
+ unset(link_flag_found)
+ string(FIND "${target_link_flags}" "${flags}" link_flag_found)
+
+ if (NOT ${link_flag_found} EQUAL -1)
+ return()
+ endif ()
+
+ set(target_link_flags "${target_link_flags} ${flags}")
+ else ()
+ set(target_link_flags "${flags}")
+ endif ()
+
+ set_target_properties(${target} PROPERTIES LINK_FLAGS ${target_link_flags})
+endfunction ()
+
+# Adds $flag to executable linker flags, and makes sure C/CXX builds still work.
+macro (require_linker_flag flag)
+ append_exe_linker_flag(${flag})
+
+ unset(c_passed)
+ draco_check_c_compiles("LINKER_FLAG_C_TEST(${flag})" "" c_passed)
+ unset(cxx_passed)
+ draco_check_cxx_compiles("LINKER_FLAG_CXX_TEST(${flag})" "" cxx_passed)
+
+ if (NOT c_passed OR NOT cxx_passed)
+ message(FATAL_ERROR "Linker flag test for ${flag} failed.")
+ endif ()
+endmacro ()
+
+endif () # DRACO_CMAKE_COMPILER_FLAGS_CMAKE_
diff --git a/extern/draco/dracoenc/cmake/compiler_tests.cmake b/extern/draco/dracoenc/cmake/compiler_tests.cmake
new file mode 100644
index 00000000000..e529ba11253
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/compiler_tests.cmake
@@ -0,0 +1,124 @@
+if (NOT DRACO_CMAKE_COMPILER_TESTS_CMAKE_)
+set(DRACO_CMAKE_COMPILER_TESTS_CMAKE_ 1)
+
+include(CheckCSourceCompiles)
+include(CheckCXXSourceCompiles)
+
+# The basic main() macro used in all compile tests.
+set(DRACO_C_MAIN "\nint main(void) { return 0; }")
+set(DRACO_CXX_MAIN "\nint main() { return 0; }")
+
+# Strings containing the names of passed and failed tests.
+set(DRACO_C_PASSED_TESTS)
+set(DRACO_C_FAILED_TESTS)
+set(DRACO_CXX_PASSED_TESTS)
+set(DRACO_CXX_FAILED_TESTS)
+
+macro(draco_push_var var new_value)
+ set(SAVED_${var} ${var})
+ set(${var} ${new_value})
+endmacro ()
+
+macro(draco_pop_var var)
+ set(var ${SAVED_${var}})
+ unset(SAVED_${var})
+endmacro ()
+
+# Confirms $test_source compiles and stores $test_name in one of
+# $DRACO_C_PASSED_TESTS or $DRACO_C_FAILED_TESTS depending on out come. When the
+# test passes $result_var is set to 1. When it fails $result_var is unset.
+# The test is not run if the test name is found in either of the passed or
+# failed test variables.
+macro(draco_check_c_compiles test_name test_source result_var)
+ unset(C_TEST_PASSED CACHE)
+ unset(C_TEST_FAILED CACHE)
+ string(FIND "${DRACO_C_PASSED_TESTS}" "${test_name}" C_TEST_PASSED)
+ string(FIND "${DRACO_C_FAILED_TESTS}" "${test_name}" C_TEST_FAILED)
+ if (${C_TEST_PASSED} EQUAL -1 AND ${C_TEST_FAILED} EQUAL -1)
+ unset(C_TEST_COMPILED CACHE)
+ message("Running C compiler test: ${test_name}")
+ check_c_source_compiles("${test_source} ${DRACO_C_MAIN}" C_TEST_COMPILED)
+ set(${result_var} ${C_TEST_COMPILED})
+
+ if (${C_TEST_COMPILED})
+ set(DRACO_C_PASSED_TESTS "${DRACO_C_PASSED_TESTS} ${test_name}")
+ else ()
+ set(DRACO_C_FAILED_TESTS "${DRACO_C_FAILED_TESTS} ${test_name}")
+ message("C Compiler test ${test_name} failed.")
+ endif ()
+ elseif (NOT ${C_TEST_PASSED} EQUAL -1)
+ set(${result_var} 1)
+ else () # ${C_TEST_FAILED} NOT EQUAL -1
+ unset(${result_var})
+ endif ()
+endmacro ()
+
+# Confirms $test_source compiles and stores $test_name in one of
+# $DRACO_CXX_PASSED_TESTS or $DRACO_CXX_FAILED_TESTS depending on out come. When
+# the test passes $result_var is set to 1. When it fails $result_var is unset.
+# The test is not run if the test name is found in either of the passed or
+# failed test variables.
+macro(draco_check_cxx_compiles test_name test_source result_var)
+ unset(CXX_TEST_PASSED CACHE)
+ unset(CXX_TEST_FAILED CACHE)
+ string(FIND "${DRACO_CXX_PASSED_TESTS}" "${test_name}" CXX_TEST_PASSED)
+ string(FIND "${DRACO_CXX_FAILED_TESTS}" "${test_name}" CXX_TEST_FAILED)
+ if (${CXX_TEST_PASSED} EQUAL -1 AND ${CXX_TEST_FAILED} EQUAL -1)
+ unset(CXX_TEST_COMPILED CACHE)
+ message("Running CXX compiler test: ${test_name}")
+ check_cxx_source_compiles("${test_source} ${DRACO_CXX_MAIN}"
+ CXX_TEST_COMPILED)
+ set(${result_var} ${CXX_TEST_COMPILED})
+
+ if (${CXX_TEST_COMPILED})
+ set(DRACO_CXX_PASSED_TESTS "${DRACO_CXX_PASSED_TESTS} ${test_name}")
+ else ()
+ set(DRACO_CXX_FAILED_TESTS "${DRACO_CXX_FAILED_TESTS} ${test_name}")
+ message("CXX Compiler test ${test_name} failed.")
+ endif ()
+ elseif (NOT ${CXX_TEST_PASSED} EQUAL -1)
+ set(${result_var} 1)
+ else () # ${CXX_TEST_FAILED} NOT EQUAL -1
+ unset(${result_var})
+ endif ()
+endmacro ()
+
+# Convenience macro that confirms $test_source compiles as C and C++.
+# $result_var is set to 1 when both tests are successful, and 0 when one or both
+# tests fail.
+# Note: This macro is intended to be used to write to result variables that
+# are expanded via configure_file(). $result_var is set to 1 or 0 to allow
+# direct usage of the value in generated source files.
+macro(draco_check_source_compiles test_name test_source result_var)
+ unset(C_PASSED)
+ unset(CXX_PASSED)
+ draco_check_c_compiles(${test_name} ${test_source} C_PASSED)
+ draco_check_cxx_compiles(${test_name} ${test_source} CXX_PASSED)
+ if (${C_PASSED} AND ${CXX_PASSED})
+ set(${result_var} 1)
+ else ()
+ set(${result_var} 0)
+ endif ()
+endmacro ()
+
+# When inline support is detected for the current compiler the supported
+# inlining keyword is written to $result in caller scope.
+macro (draco_get_inline result)
+ draco_check_source_compiles("inline_check_1"
+ "static inline void macro(void) {}"
+ HAVE_INLINE_1)
+ if (HAVE_INLINE_1 EQUAL 1)
+ set(${result} "inline")
+ return()
+ endif ()
+
+ # Check __inline.
+ draco_check_source_compiles("inline_check_2"
+ "static __inline void macro(void) {}"
+ HAVE_INLINE_2)
+ if (HAVE_INLINE_2 EQUAL 1)
+ set(${result} "__inline")
+ endif ()
+endmacro ()
+
+endif () # DRACO_CMAKE_COMPILER_TESTS_CMAKE_
diff --git a/extern/draco/dracoenc/cmake/draco_features.cmake b/extern/draco/dracoenc/cmake/draco_features.cmake
new file mode 100644
index 00000000000..057b0b07ecd
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/draco_features.cmake
@@ -0,0 +1,57 @@
+if (NOT DRACO_CMAKE_DRACO_FEATURES_CMAKE_)
+set(DRACO_CMAKE_DRACO_FEATURES_CMAKE_ 1)
+
+set(draco_features_file_name "${draco_build_dir}/draco/draco_features.h")
+set(draco_features_list)
+
+# Macro that handles tracking of Draco preprocessor symbols for the purpose of
+# producing draco_features.h.
+#
+# draco_enable_feature(FEATURE <feature_name> [TARGETS <target_name>])
+# FEATURE is required. It should be a Draco preprocessor symbol.
+# TARGETS is optional. It can be one or more draco targets.
+#
+# When the TARGETS argument is not present the preproc symbol is added to
+# draco_features.h. When it is draco_features.h is unchanged, and
+# target_compile_options() is called for each target specified.
+macro (draco_enable_feature)
+ set(def_flags)
+ set(def_single_arg_opts FEATURE)
+ set(def_multi_arg_opts TARGETS)
+ cmake_parse_arguments(DEF "${def_flags}" "${def_single_arg_opts}"
+ "${def_multi_arg_opts}" ${ARGN})
+ if ("${DEF_FEATURE}" STREQUAL "")
+ message(FATAL_ERROR "Empty FEATURE passed to draco_enable_feature().")
+ endif ()
+
+ # Do nothing/return early if $DEF_FEATURE is already in the list.
+ list(FIND draco_features_list ${DEF_FEATURE} df_index)
+ if (NOT df_index EQUAL -1)
+ return ()
+ endif ()
+
+ list(LENGTH DEF_TARGETS df_targets_list_length)
+ if (${df_targets_list_length} EQUAL 0)
+ list(APPEND draco_features_list ${DEF_FEATURE})
+ else ()
+ foreach (target ${DEF_TARGETS})
+ target_compile_definitions(${target} PRIVATE ${DEF_FEATURE})
+ endforeach ()
+ endif ()
+endmacro ()
+
+# Function for generating draco_features.h.
+function (draco_generate_features_h)
+ file(WRITE "${draco_features_file_name}"
+ "// GENERATED FILE -- DO NOT EDIT\n\n"
+ "#ifndef DRACO_FEATURES_H_\n"
+ "#define DRACO_FEATURES_H_\n\n")
+
+ foreach (feature ${draco_features_list})
+ file(APPEND "${draco_features_file_name}" "#define ${feature}\n")
+ endforeach ()
+
+ file(APPEND "${draco_features_file_name}" "\n#endif // DRACO_FEATURES_H_")
+endfunction ()
+
+endif () # DRACO_CMAKE_DRACO_FEATURES_CMAKE_
diff --git a/extern/draco/dracoenc/cmake/draco_test_config.h.cmake b/extern/draco/dracoenc/cmake/draco_test_config.h.cmake
new file mode 100644
index 00000000000..77a574123fd
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/draco_test_config.h.cmake
@@ -0,0 +1,13 @@
+#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}"
+#define DRACO_TEST_TEMP_DIR "${DRACO_TEST_TEMP_DIR}"
+
+#endif // DRACO_TESTING_DRACO_TEST_CONFIG_H_
diff --git a/extern/draco/dracoenc/cmake/draco_version.cc.cmake b/extern/draco/dracoenc/cmake/draco_version.cc.cmake
new file mode 100644
index 00000000000..921df7de77d
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/draco_version.cc.cmake
@@ -0,0 +1,21 @@
+// If this file is named draco_version.cc.cmake:
+// This file is used as input at cmake generation time.
+
+// If this file is named draco_version.cc:
+// GENERATED FILE, DO NOT EDIT. SEE ABOVE.
+#include "draco_version.h"
+
+static const char kDracoGitHash[] = "${draco_git_hash}";
+static const char kDracoGitDesc[] = "${draco_git_desc}";
+
+const char *draco_git_hash() {
+ return kDracoGitHash;
+}
+
+const char *draco_git_version() {
+ return kDracoGitDesc;
+}
+
+const char* draco_version() {
+ return draco::Version();
+}
diff --git a/extern/draco/dracoenc/cmake/draco_version.h.cmake b/extern/draco/dracoenc/cmake/draco_version.h.cmake
new file mode 100644
index 00000000000..506423ed34c
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/draco_version.h.cmake
@@ -0,0 +1,21 @@
+// If this file is named draco_version.h.cmake:
+// This file is used as input at cmake generation time.
+
+// If this file is named draco_version.h:
+// GENERATED FILE, DO NOT EDIT. SEE ABOVE.
+#ifndef DRACO_DRACO_VERSION_H_
+#define DRACO_DRACO_VERSION_H_
+
+#include "draco/core/draco_version.h"
+
+// Returns git hash of Draco git repository.
+const char *draco_git_hash();
+
+// Returns the output of the git describe command when run from the Draco git
+// repository.
+const char *draco_git_version();
+
+// Returns the version string from core/draco_version.h.
+const char* draco_version();
+
+#endif // DRACO_DRACO_VERSION_H_
diff --git a/extern/draco/dracoenc/cmake/msvc_runtime.cmake b/extern/draco/dracoenc/cmake/msvc_runtime.cmake
new file mode 100644
index 00000000000..ca8de08f9a2
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/msvc_runtime.cmake
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 3.2)
+
+if (MSVC)
+ # Use statically linked versions of the MS standard libraries.
+ if (NOT "${MSVC_RUNTIME}" STREQUAL "dll")
+ foreach (flag_var
+ CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
+ CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+ if (${flag_var} MATCHES "/MD")
+ string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
+ endif ()
+ endforeach ()
+ endif ()
+endif ()
diff --git a/extern/draco/dracoenc/cmake/sanitizers.cmake b/extern/draco/dracoenc/cmake/sanitizers.cmake
new file mode 100644
index 00000000000..e966cd85d52
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/sanitizers.cmake
@@ -0,0 +1,19 @@
+if (NOT DRACO_CMAKE_SANITIZERS_CMAKE_)
+set(DRACO_CMAKE_SANITIZERS_CMAKE_ 1)
+
+if (MSVC OR NOT SANITIZE)
+ return ()
+endif ()
+
+include("${draco_root}/cmake/compiler_flags.cmake")
+
+string(TOLOWER ${SANITIZE} SANITIZE)
+
+# Require the sanitizer requested.
+require_linker_flag("-fsanitize=${SANITIZE}")
+require_compiler_flag("-fsanitize=${SANITIZE}" YES)
+
+# Make callstacks accurate.
+require_compiler_flag("-fno-omit-frame-pointer -fno-optimize-sibling-calls" YES)
+
+endif() # DRACO_CMAKE_SANITIZERS_CMAKE_
diff --git a/extern/draco/dracoenc/cmake/toolchains/arm-ios-common.cmake b/extern/draco/dracoenc/cmake/toolchains/arm-ios-common.cmake
new file mode 100644
index 00000000000..48f5ce5e68c
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/toolchains/arm-ios-common.cmake
@@ -0,0 +1,13 @@
+if (NOT DRACO_CMAKE_TOOLCHAINS_ARM_IOS_COMMON_CMAKE_)
+set(DRACO_CMAKE_ARM_IOS_COMMON_CMAKE_ 1)
+
+set(CMAKE_SYSTEM_NAME "Darwin")
+set(CMAKE_OSX_SYSROOT iphoneos)
+set(CMAKE_C_COMPILER clang)
+set(CMAKE_C_COMPILER_ARG1 "-arch ${CMAKE_SYSTEM_PROCESSOR}")
+set(CMAKE_CXX_COMPILER clang++)
+set(CMAKE_CXX_COMPILER_ARG1 "-arch ${CMAKE_SYSTEM_PROCESSOR}")
+
+# TODO(tomfinegan): Handle bit code embedding.
+
+endif () # DRACO_CMAKE_TOOLCHAINS_ARM_IOS_COMMON_CMAKE_
diff --git a/extern/draco/dracoenc/cmake/toolchains/arm64-android-ndk-libcpp.cmake b/extern/draco/dracoenc/cmake/toolchains/arm64-android-ndk-libcpp.cmake
new file mode 100644
index 00000000000..bd044199063
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/toolchains/arm64-android-ndk-libcpp.cmake
@@ -0,0 +1,12 @@
+if (NOT DRACO_CMAKE_TOOLCHAINS_ARM64_ANDROID_NDK_LIBCPP_CMAKE_)
+set(DRACO_CMAKE_TOOLCHAINS_ARM64_ANDROID_NDK_LIBCPP_CMAKE_ 1)
+
+include("${CMAKE_CURRENT_LIST_DIR}/../util.cmake")
+
+set(CMAKE_SYSTEM_NAME Android)
+set(CMAKE_ANDROID_ARCH_ABI arm64-v8a)
+require_variable(CMAKE_ANDROID_NDK)
+set_variable_if_unset(CMAKE_SYSTEM_VERSION 21)
+set_variable_if_unset(CMAKE_ANDROID_STL_TYPE c++_static)
+
+endif () # DRACO_CMAKE_TOOLCHAINS_ARM64_ANDROID_NDK_LIBCPP_CMAKE_
diff --git a/extern/draco/dracoenc/cmake/toolchains/arm64-ios.cmake b/extern/draco/dracoenc/cmake/toolchains/arm64-ios.cmake
new file mode 100644
index 00000000000..0d4909e1be3
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/toolchains/arm64-ios.cmake
@@ -0,0 +1,14 @@
+if (NOT DRACO_CMAKE_TOOLCHAINS_ARM64_IOS_CMAKE_)
+set(DRACO_CMAKE_TOOLCHAINS_ARM64_IOS_CMAKE_ 1)
+
+if (XCODE)
+ # TODO(tomfinegan): Handle arm builds in Xcode.
+ message(FATAL_ERROR "This toolchain does not support Xcode.")
+endif ()
+
+set(CMAKE_SYSTEM_PROCESSOR "arm64")
+set(CMAKE_OSX_ARCHITECTURES "arm64")
+
+include("${CMAKE_CURRENT_LIST_DIR}/arm-ios-common.cmake")
+
+endif () # DRACO_CMAKE_TOOLCHAINS_ARM64_IOS_CMAKE_
diff --git a/extern/draco/dracoenc/cmake/toolchains/arm64-linux-gcc.cmake b/extern/draco/dracoenc/cmake/toolchains/arm64-linux-gcc.cmake
new file mode 100644
index 00000000000..3bab482855c
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/toolchains/arm64-linux-gcc.cmake
@@ -0,0 +1,18 @@
+if (NOT DRACO_CMAKE_TOOLCHAINS_ARM64_LINUX_GCC_CMAKE_)
+set(DRACO_CMAKE_TOOLCHAINS_ARM64_LINUX_GCC_CMAKE_ 1)
+
+set(CMAKE_SYSTEM_NAME "Linux")
+
+if ("${CROSS}" STREQUAL "")
+ # Default the cross compiler prefix to something known to work.
+ set(CROSS aarch64-linux-gnu-)
+endif ()
+
+set(CMAKE_C_COMPILER ${CROSS}gcc)
+set(CMAKE_CXX_COMPILER ${CROSS}g++)
+set(AS_EXECUTABLE ${CROSS}as)
+set(CMAKE_C_COMPILER_ARG1 "-march=armv8-a")
+set(CMAKE_CXX_COMPILER_ARG1 "-march=armv8-a")
+set(CMAKE_SYSTEM_PROCESSOR "arm64")
+
+endif () # DRACO_CMAKE_TOOLCHAINS_ARM64_LINUX_GCC_CMAKE_
diff --git a/extern/draco/dracoenc/cmake/toolchains/armv7-android-ndk-libcpp.cmake b/extern/draco/dracoenc/cmake/toolchains/armv7-android-ndk-libcpp.cmake
new file mode 100644
index 00000000000..fd50defd82e
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/toolchains/armv7-android-ndk-libcpp.cmake
@@ -0,0 +1,12 @@
+if (NOT DRACO_CMAKE_TOOLCHAINS_ARMV7_ANDROID_NDK_LIBCPP_CMAKE_)
+set(DRACO_CMAKE_TOOLCHAINS_ARMV7_ANDROID_NDK_LIBCPP_CMAKE_ 1)
+
+include("${CMAKE_CURRENT_LIST_DIR}/../util.cmake")
+
+set(CMAKE_SYSTEM_NAME Android)
+set(CMAKE_ANDROID_ARCH_ABI armeabi-v7a)
+require_variable(CMAKE_ANDROID_NDK)
+set_variable_if_unset(CMAKE_SYSTEM_VERSION 18)
+set_variable_if_unset(CMAKE_ANDROID_STL_TYPE c++_static)
+
+endif () # DRACO_CMAKE_TOOLCHAINS_ARMV7_ANDROID_NDK_LIBCPP_CMAKE_
diff --git a/extern/draco/dracoenc/cmake/toolchains/armv7-ios.cmake b/extern/draco/dracoenc/cmake/toolchains/armv7-ios.cmake
new file mode 100644
index 00000000000..61d67872917
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/toolchains/armv7-ios.cmake
@@ -0,0 +1,14 @@
+if (NOT DRACO_CMAKE_TOOLCHAINS_ARMV7_IOS_CMAKE_)
+set(DRACO_CMAKE_TOOLCHAINS_ARMV7_IOS_CMAKE_ 1)
+
+if (XCODE)
+ # TODO(tomfinegan): Handle arm builds in Xcode.
+ message(FATAL_ERROR "This toolchain does not support Xcode.")
+endif ()
+
+set(CMAKE_SYSTEM_PROCESSOR "armv7")
+set(CMAKE_OSX_ARCHITECTURES "armv7")
+
+include("${CMAKE_CURRENT_LIST_DIR}/arm-ios-common.cmake")
+
+endif () # DRACO_CMAKE_TOOLCHAINS_ARMV7_IOS_CMAKE_
diff --git a/extern/draco/dracoenc/cmake/toolchains/armv7-linux-gcc.cmake b/extern/draco/dracoenc/cmake/toolchains/armv7-linux-gcc.cmake
new file mode 100644
index 00000000000..e0f850f4270
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/toolchains/armv7-linux-gcc.cmake
@@ -0,0 +1,24 @@
+if (NOT DRACO_CMAKE_TOOLCHAINS_ARMV7_LINUX_GCC_CMAKE_)
+set(DRACO_CMAKE_TOOLCHAINS_ARMV7_LINUX_GCC_CMAKE_ 1)
+
+set(CMAKE_SYSTEM_NAME "Linux")
+
+if ("${CROSS}" STREQUAL "")
+ # Default the cross compiler prefix to something known to work.
+ set(CROSS arm-linux-gnueabihf-)
+endif ()
+
+if (NOT ${CROSS} MATCHES hf-$)
+ set(DRACO_EXTRA_TOOLCHAIN_FLAGS "-mfloat-abi=softfp")
+endif ()
+
+set(CMAKE_C_COMPILER ${CROSS}gcc)
+set(CMAKE_CXX_COMPILER ${CROSS}g++)
+set(AS_EXECUTABLE ${CROSS}as)
+set(CMAKE_C_COMPILER_ARG1
+ "-march=armv7-a -mfpu=neon ${DRACO_EXTRA_TOOLCHAIN_FLAGS}")
+set(CMAKE_CXX_COMPILER_ARG1
+ "-march=armv7-a -mfpu=neon ${DRACO_EXTRA_TOOLCHAIN_FLAGS}")
+set(CMAKE_SYSTEM_PROCESSOR "armv7")
+
+endif () # DRACO_CMAKE_TOOLCHAINS_ARMV7_LINUX_GCC_CMAKE_
diff --git a/extern/draco/dracoenc/cmake/toolchains/armv7s-ios.cmake b/extern/draco/dracoenc/cmake/toolchains/armv7s-ios.cmake
new file mode 100644
index 00000000000..45097936bcb
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/toolchains/armv7s-ios.cmake
@@ -0,0 +1,14 @@
+if (NOT DRACO_CMAKE_TOOLCHAINS_ARMV7S_IOS_CMAKE_)
+set(DRACO_CMAKE_TOOLCHAINS_ARMV7S_IOS_CMAKE_ 1)
+
+if (XCODE)
+ # TODO(tomfinegan): Handle arm builds in Xcode.
+ message(FATAL_ERROR "This toolchain does not support Xcode.")
+endif ()
+
+set(CMAKE_SYSTEM_PROCESSOR "armv7s")
+set(CMAKE_OSX_ARCHITECTURES "armv7s")
+
+include("${CMAKE_CURRENT_LIST_DIR}/arm-ios-common.cmake")
+
+endif () # DRACO_CMAKE_TOOLCHAINS_ARMV7S_IOS_CMAKE_
diff --git a/extern/draco/dracoenc/cmake/toolchains/x86-android-ndk-libcpp.cmake b/extern/draco/dracoenc/cmake/toolchains/x86-android-ndk-libcpp.cmake
new file mode 100644
index 00000000000..7bb3717971f
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/toolchains/x86-android-ndk-libcpp.cmake
@@ -0,0 +1,12 @@
+if (NOT DRACO_CMAKE_TOOLCHAINS_X86_ANDROID_NDK_LIBCPP_CMAKE_)
+set(DRACO_CMAKE_TOOLCHAINS_X86_ANDROID_NDK_LIBCPP_CMAKE_ 1)
+
+include("${CMAKE_CURRENT_LIST_DIR}/../util.cmake")
+
+set(CMAKE_SYSTEM_NAME Android)
+set(CMAKE_ANDROID_ARCH_ABI x86)
+require_variable(CMAKE_ANDROID_NDK)
+set_variable_if_unset(CMAKE_SYSTEM_VERSION 18)
+set_variable_if_unset(CMAKE_ANDROID_STL_TYPE c++_static)
+
+endif () # DRACO_CMAKE_TOOLCHAINS_X86_ANDROID_NDK_LIBCPP_CMAKE_
diff --git a/extern/draco/dracoenc/cmake/toolchains/x86_64-android-ndk-libcpp.cmake b/extern/draco/dracoenc/cmake/toolchains/x86_64-android-ndk-libcpp.cmake
new file mode 100644
index 00000000000..3b86b9d6682
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/toolchains/x86_64-android-ndk-libcpp.cmake
@@ -0,0 +1,12 @@
+if (NOT DRACO_CMAKE_TOOLCHAINS_X86_64_ANDROID_NDK_LIBCPP_CMAKE_)
+set(DRACO_CMAKE_TOOLCHAINS_X86_64_ANDROID_NDK_LIBCPP_CMAKE_ 1)
+
+include("${CMAKE_CURRENT_LIST_DIR}/../util.cmake")
+
+set(CMAKE_SYSTEM_NAME Android)
+set(CMAKE_ANDROID_ARCH_ABI x86_64)
+require_variable(CMAKE_ANDROID_NDK)
+set_variable_if_unset(CMAKE_SYSTEM_VERSION 21)
+set_variable_if_unset(CMAKE_ANDROID_STL_TYPE c++_static)
+
+endif () # DRACO_CMAKE_TOOLCHAINS_X86_64_ANDROID_NDK_LIBCPP_CMAKE_
diff --git a/extern/draco/dracoenc/cmake/util.cmake b/extern/draco/dracoenc/cmake/util.cmake
new file mode 100644
index 00000000000..252761fe6fb
--- /dev/null
+++ b/extern/draco/dracoenc/cmake/util.cmake
@@ -0,0 +1,74 @@
+if (NOT DRACO_CMAKE_UTIL_CMAKE_)
+set(DRACO_CMAKE_UTIL_CMAKE_ 1)
+
+# Creates dummy source file in $draco_build_dir named $basename.$extension and
+# returns the full path to the dummy source file via the $out_file_path
+# parameter.
+function (create_dummy_source_file basename extension out_file_path)
+ set(dummy_source_file "${draco_build_dir}/${basename}.${extension}")
+ file(WRITE "${dummy_source_file}"
+ "// Generated file. DO NOT EDIT!\n"
+ "// ${target_name} needs a ${extension} file to force link language, \n"
+ "// or to silence a harmless CMake warning: Ignore me.\n"
+ "void ${target_name}_dummy_function(void) {}\n")
+ set(${out_file_path} ${dummy_source_file} PARENT_SCOPE)
+endfunction ()
+
+# Convenience function for adding a dummy source file to $target_name using
+# $extension as the file extension. Wraps create_dummy_source_file().
+function (add_dummy_source_file_to_target target_name extension)
+ create_dummy_source_file("${target_name}" "${extension}" "dummy_source_file")
+ target_sources(${target_name} PRIVATE ${dummy_source_file})
+endfunction ()
+
+# Extracts the version number from $version_file and returns it to the user via
+# $version_string_out_var. This is achieved by finding the first instance of
+# the kDracoVersion variable and then removing everything but the string literal
+# assigned to the variable. Quotes and semicolon are stripped from the returned
+# string.
+function (extract_version_string version_file version_string_out_var)
+ file(STRINGS "${version_file}" draco_version REGEX "kDracoVersion")
+ list(GET draco_version 0 draco_version)
+ string(REPLACE "static const char kDracoVersion[] = " "" draco_version
+ "${draco_version}")
+ string(REPLACE ";" "" draco_version "${draco_version}")
+ string(REPLACE "\"" "" draco_version "${draco_version}")
+ set("${version_string_out_var}" "${draco_version}" PARENT_SCOPE)
+endfunction ()
+
+# Sets CMake compiler launcher to $launcher_name when $launcher_name is found in
+# $PATH. Warns user about ignoring build flag $launcher_flag when $launcher_name
+# is not found in $PATH.
+function (set_compiler_launcher launcher_flag launcher_name)
+ find_program(launcher_path "${launcher_name}")
+ if (launcher_path)
+ set(CMAKE_C_COMPILER_LAUNCHER "${launcher_path}" PARENT_SCOPE)
+ set(CMAKE_CXX_COMPILER_LAUNCHER "${launcher_path}" PARENT_SCOPE)
+ message("--- Using ${launcher_name} as compiler launcher.")
+ else ()
+ message(WARNING
+ "--- Cannot find ${launcher_name}, ${launcher_flag} ignored.")
+ endif ()
+endfunction ()
+
+# Terminates CMake execution when $var_name is unset in the environment. Sets
+# CMake variable to the value of the environment variable when the variable is
+# present in the environment.
+macro(require_variable var_name)
+ if ("$ENV{${var_name}}" STREQUAL "")
+ message(FATAL_ERROR "${var_name} must be set in environment.")
+ endif ()
+ set_variable_if_unset(${var_name} "")
+endmacro ()
+
+# Sets $var_name to $default_value if not already set in the environment.
+macro (set_variable_if_unset var_name default_value)
+ if (NOT "$ENV{${var_name}}" STREQUAL "")
+ set(${var_name} $ENV{${var_name}})
+ else ()
+ set(${var_name} ${default_value})
+ endif ()
+endmacro ()
+
+endif() # DRACO_CMAKE_UTIL_CMAKE_
+
diff --git a/extern/draco/dracoenc/src/draco/animation/keyframe_animation.cc b/extern/draco/dracoenc/src/draco/animation/keyframe_animation.cc
new file mode 100644
index 00000000000..05ae3b1184e
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/animation/keyframe_animation.cc
@@ -0,0 +1,55 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/animation/keyframe_animation.h"
+
+namespace draco {
+
+KeyframeAnimation::KeyframeAnimation() {}
+
+bool KeyframeAnimation::SetTimestamps(
+ const std::vector<TimestampType> &timestamp) {
+ // Already added attributes.
+ const int32_t num_frames = timestamp.size();
+ if (num_attributes() > 0) {
+ // Timestamp attribute could be added only once.
+ if (timestamps()->size()) {
+ return false;
+ } else {
+ // Check if the number of frames is consistent with
+ // the existing keyframes.
+ if (num_frames != num_points())
+ return false;
+ }
+ } else {
+ // This is the first attribute.
+ set_num_frames(num_frames);
+ }
+
+ // Add attribute for time stamp data.
+ std::unique_ptr<PointAttribute> timestamp_att =
+ std::unique_ptr<PointAttribute>(new PointAttribute());
+ timestamp_att->Init(GeometryAttribute::GENERIC, nullptr, 1, DT_FLOAT32, false,
+ sizeof(float), 0);
+ timestamp_att->SetIdentityMapping();
+ timestamp_att->Reset(num_frames);
+ for (PointIndex i(0); i < num_frames; ++i) {
+ timestamp_att->SetAttributeValue(timestamp_att->mapped_index(i),
+ &timestamp[i.value()]);
+ }
+ this->SetAttribute(kTimestampId, std::move(timestamp_att));
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/animation/keyframe_animation.h b/extern/draco/dracoenc/src/draco/animation/keyframe_animation.h
new file mode 100644
index 00000000000..0e8b111aaea
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/animation/keyframe_animation.h
@@ -0,0 +1,108 @@
+// Copyright 2017 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_ANIMATION_KEYFRAME_ANIMATION_H_
+#define DRACO_ANIMATION_KEYFRAME_ANIMATION_H_
+
+#include <vector>
+
+#include "draco/point_cloud/point_cloud.h"
+
+namespace draco {
+
+// Class for holding keyframe animation data. It will have two or more
+// attributes as a point cloud. The first attribute is always the timestamp
+// of the animation. Each KeyframeAnimation could have multiple animations with
+// the same number of frames. Each animation will be treated as a point
+// attribute.
+class KeyframeAnimation : public PointCloud {
+ public:
+ // Force time stamp to be float type.
+ using TimestampType = float;
+
+ KeyframeAnimation();
+
+ // Animation must have only one timestamp attribute.
+ // This function must be called before adding any animation data.
+ // Returns false if timestamp already exists.
+ bool SetTimestamps(const std::vector<TimestampType> &timestamp);
+
+ // Returns an id for the added animation data. This id will be used to
+ // identify this animation.
+ // Returns -1 if error, e.g. number of frames is not consistent.
+ // Type |T| should be consistent with |DataType|, e.g:
+ // float - DT_FLOAT32,
+ // int32_t - DT_INT32, ...
+ template <typename T>
+ int32_t AddKeyframes(DataType data_type, uint32_t num_components,
+ const std::vector<T> &data);
+
+ const PointAttribute *timestamps() const {
+ return GetAttributeByUniqueId(kTimestampId);
+ }
+ const PointAttribute *keyframes(int32_t animation_id) const {
+ return GetAttributeByUniqueId(animation_id);
+ }
+
+ // Number of frames should be equal to number points in the point cloud.
+ void set_num_frames(int32_t num_frames) { set_num_points(num_frames); }
+ int32_t num_frames() const { return static_cast<int32_t>(num_points()); }
+
+ int32_t num_animations() const { return num_attributes() - 1; }
+
+ private:
+ // Attribute id of timestamp is fixed to 0.
+ static constexpr int32_t kTimestampId = 0;
+};
+
+template <typename T>
+int32_t KeyframeAnimation::AddKeyframes(DataType data_type,
+ uint32_t num_components,
+ const std::vector<T> &data) {
+ // TODO(draco-eng): Verify T is consistent with |data_type|.
+ if (num_components == 0)
+ return -1;
+ // If timestamps is not added yet, then reserve attribute 0 for timestamps.
+ if (!num_attributes()) {
+ // Add a temporary attribute with 0 points to fill attribute id 0.
+ std::unique_ptr<PointAttribute> temp_att =
+ std::unique_ptr<PointAttribute>(new PointAttribute());
+ temp_att->Init(GeometryAttribute::GENERIC, nullptr, num_components,
+ data_type, false, DataTypeLength(data_type), 0);
+ temp_att->Reset(0);
+ this->AddAttribute(std::move(temp_att));
+
+ set_num_frames(data.size() / num_components);
+ }
+
+ if (data.size() != num_components * num_frames())
+ return -1;
+
+ std::unique_ptr<PointAttribute> keyframe_att =
+ std::unique_ptr<PointAttribute>(new PointAttribute());
+ keyframe_att->Init(GeometryAttribute::GENERIC, nullptr, num_components,
+ data_type, false, DataTypeLength(data_type), 0);
+ keyframe_att->SetIdentityMapping();
+ keyframe_att->Reset(num_frames());
+ const size_t stride = num_components;
+ for (PointIndex i(0); i < num_frames(); ++i) {
+ keyframe_att->SetAttributeValue(keyframe_att->mapped_index(i),
+ &data[i.value() * stride]);
+ }
+ return this->AddAttribute(std::move(keyframe_att));
+}
+
+} // namespace draco
+
+#endif // DRACO_ANIMATION_KEYFRAME_ANIMATION_H_
diff --git a/extern/draco/dracoenc/src/draco/animation/keyframe_animation_decoder.cc b/extern/draco/dracoenc/src/draco/animation/keyframe_animation_decoder.cc
new file mode 100644
index 00000000000..8c0e71f62fa
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/animation/keyframe_animation_decoder.cc
@@ -0,0 +1,29 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/animation/keyframe_animation_decoder.h"
+
+namespace draco {
+
+Status KeyframeAnimationDecoder::Decode(const DecoderOptions &options,
+ DecoderBuffer *in_buffer,
+ KeyframeAnimation *animation) {
+ const auto status = PointCloudSequentialDecoder::Decode(
+ options, in_buffer, static_cast<PointCloud *>(animation));
+ if (!status.ok())
+ return status;
+ return OkStatus();
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/animation/keyframe_animation_decoder.h b/extern/draco/dracoenc/src/draco/animation/keyframe_animation_decoder.h
new file mode 100644
index 00000000000..fdf086b3a38
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/animation/keyframe_animation_decoder.h
@@ -0,0 +1,34 @@
+// Copyright 2017 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_ANIMATION_KEYFRAME_ANIMATION_DECODER_H_
+#define DRACO_ANIMATION_KEYFRAME_ANIMATION_DECODER_H_
+
+#include "draco/animation/keyframe_animation.h"
+#include "draco/compression/point_cloud/point_cloud_sequential_decoder.h"
+
+namespace draco {
+
+// Class for decoding keyframe animation.
+class KeyframeAnimationDecoder : private PointCloudSequentialDecoder {
+ public:
+ KeyframeAnimationDecoder(){};
+
+ Status Decode(const DecoderOptions &options, DecoderBuffer *in_buffer,
+ KeyframeAnimation *animation);
+};
+
+} // namespace draco
+
+#endif // DRACO_ANIMATION_KEYFRAME_ANIMATION_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/animation/keyframe_animation_encoder.cc b/extern/draco/dracoenc/src/draco/animation/keyframe_animation_encoder.cc
new file mode 100644
index 00000000000..f7d84f3106a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/animation/keyframe_animation_encoder.cc
@@ -0,0 +1,28 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/animation/keyframe_animation_encoder.h"
+
+namespace draco {
+
+KeyframeAnimationEncoder::KeyframeAnimationEncoder() {}
+
+Status KeyframeAnimationEncoder::EncodeKeyframeAnimation(
+ const KeyframeAnimation &animation, const EncoderOptions &options,
+ EncoderBuffer *out_buffer) {
+ SetPointCloud(animation);
+ return Encode(options, out_buffer);
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/animation/keyframe_animation_encoder.h b/extern/draco/dracoenc/src/draco/animation/keyframe_animation_encoder.h
new file mode 100644
index 00000000000..6096c79fa12
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/animation/keyframe_animation_encoder.h
@@ -0,0 +1,39 @@
+// Copyright 2017 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_ANIMATION_KEYFRAME_ANIMATION_ENCODER_H_
+#define DRACO_ANIMATION_KEYFRAME_ANIMATION_ENCODER_H_
+
+#include "draco/animation/keyframe_animation.h"
+#include "draco/compression/point_cloud/point_cloud_sequential_encoder.h"
+
+namespace draco {
+
+// Class for encoding keyframe animation. It takes KeyframeAnimation as a
+// PointCloud and compress it. It's mostly a wrapper around PointCloudEncoder so
+// that the animation module could be separated from geometry compression when
+// exposed to developers.
+class KeyframeAnimationEncoder : private PointCloudSequentialEncoder {
+ public:
+ KeyframeAnimationEncoder();
+
+ // Encode an animation to a buffer.
+ Status EncodeKeyframeAnimation(const KeyframeAnimation &animation,
+ const EncoderOptions &options,
+ EncoderBuffer *out_buffer);
+};
+
+} // namespace draco
+
+#endif // DRACO_ANIMATION_KEYFRAME_ANIMATION_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/animation/keyframe_animation_encoding_test.cc b/extern/draco/dracoenc/src/draco/animation/keyframe_animation_encoding_test.cc
new file mode 100644
index 00000000000..4a6491f9d0d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/animation/keyframe_animation_encoding_test.cc
@@ -0,0 +1,168 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/animation/keyframe_animation.h"
+#include "draco/animation/keyframe_animation_decoder.h"
+#include "draco/animation/keyframe_animation_encoder.h"
+#include "draco/core/draco_test_base.h"
+#include "draco/core/draco_test_utils.h"
+
+namespace draco {
+
+class KeyframeAnimationEncodingTest : public ::testing::Test {
+ protected:
+ KeyframeAnimationEncodingTest() {}
+
+ bool CreateAndAddTimestamps(int32_t num_frames) {
+ timestamps_.resize(num_frames);
+ for (int i = 0; i < timestamps_.size(); ++i)
+ timestamps_[i] = static_cast<draco::KeyframeAnimation::TimestampType>(i);
+ return keyframe_animation_.SetTimestamps(timestamps_);
+ }
+
+ int32_t CreateAndAddAnimationData(int32_t num_frames,
+ uint32_t num_components) {
+ // Create and add animation data with.
+ animation_data_.resize(num_frames * num_components);
+ for (int i = 0; i < animation_data_.size(); ++i)
+ animation_data_[i] = static_cast<float>(i);
+ return keyframe_animation_.AddKeyframes(draco::DT_FLOAT32, num_components,
+ animation_data_);
+ }
+
+ template <int num_components_t>
+ void CompareAnimationData(const KeyframeAnimation &animation0,
+ const KeyframeAnimation &animation1,
+ bool quantized) {
+ ASSERT_EQ(animation0.num_frames(), animation1.num_frames());
+ ASSERT_EQ(animation0.num_animations(), animation1.num_animations());
+
+ if (quantized) {
+ // TODO(hemmer) : Add test for stable quantization.
+ // Quantization will result in slightly different values.
+ // Skip comparing values.
+ return;
+ }
+
+ // Compare time stamp.
+ const auto timestamp_att0 = animation0.timestamps();
+ const auto timestamp_att1 = animation0.timestamps();
+ for (int i = 0; i < animation0.num_frames(); ++i) {
+ std::array<float, 1> att_value0;
+ std::array<float, 1> att_value1;
+ ASSERT_TRUE((timestamp_att0->GetValue<float, 1>(
+ draco::AttributeValueIndex(i), &att_value0)));
+ ASSERT_TRUE((timestamp_att1->GetValue<float, 1>(
+ draco::AttributeValueIndex(i), &att_value1)));
+ ASSERT_FLOAT_EQ(att_value0[0], att_value1[0]);
+ }
+
+ for (int animation_id = 1; animation_id < animation0.num_animations();
+ ++animation_id) {
+ // Compare keyframe data.
+ const auto keyframe_att0 = animation0.keyframes(animation_id);
+ const auto keyframe_att1 = animation1.keyframes(animation_id);
+ ASSERT_EQ(keyframe_att0->num_components(),
+ keyframe_att1->num_components());
+ for (int i = 0; i < animation0.num_frames(); ++i) {
+ std::array<float, num_components_t> att_value0;
+ std::array<float, num_components_t> att_value1;
+ ASSERT_TRUE((keyframe_att0->GetValue<float, num_components_t>(
+ draco::AttributeValueIndex(i), &att_value0)));
+ ASSERT_TRUE((keyframe_att1->GetValue<float, num_components_t>(
+ draco::AttributeValueIndex(i), &att_value1)));
+ for (int j = 0; j < att_value0.size(); ++j) {
+ ASSERT_FLOAT_EQ(att_value0[j], att_value1[j]);
+ }
+ }
+ }
+ }
+
+ template <int num_components_t>
+ void TestKeyframeAnimationEncoding() {
+ TestKeyframeAnimationEncoding<num_components_t>(false);
+ }
+
+ template <int num_components_t>
+ void TestKeyframeAnimationEncoding(bool quantized) {
+ // Encode animation class.
+ draco::EncoderBuffer buffer;
+ draco::KeyframeAnimationEncoder encoder;
+ EncoderOptions options = EncoderOptions::CreateDefaultOptions();
+ if (quantized) {
+ // Set quantization for timestamps.
+ options.SetAttributeInt(0, "quantization_bits", 20);
+ // Set quantization for keyframes.
+ for (int i = 1; i <= keyframe_animation_.num_animations(); ++i) {
+ options.SetAttributeInt(i, "quantization_bits", 20);
+ }
+ }
+
+ ASSERT_TRUE(
+ encoder.EncodeKeyframeAnimation(keyframe_animation_, options, &buffer)
+ .ok());
+
+ draco::DecoderBuffer dec_decoder;
+ draco::KeyframeAnimationDecoder decoder;
+ DecoderBuffer dec_buffer;
+ dec_buffer.Init(buffer.data(), buffer.size());
+
+ // Decode animation class.
+ std::unique_ptr<KeyframeAnimation> decoded_animation(
+ new KeyframeAnimation());
+ DecoderOptions dec_options;
+ ASSERT_TRUE(
+ decoder.Decode(dec_options, &dec_buffer, decoded_animation.get()).ok());
+
+ // Verify if animation before and after compression is identical.
+ CompareAnimationData<num_components_t>(keyframe_animation_,
+ *decoded_animation, quantized);
+ }
+
+ draco::KeyframeAnimation keyframe_animation_;
+ std::vector<draco::KeyframeAnimation::TimestampType> timestamps_;
+ std::vector<float> animation_data_;
+};
+
+TEST_F(KeyframeAnimationEncodingTest, OneComponent) {
+ const int num_frames = 1;
+ ASSERT_TRUE(CreateAndAddTimestamps(num_frames));
+ ASSERT_EQ(CreateAndAddAnimationData(num_frames, 1), 1);
+ TestKeyframeAnimationEncoding<1>();
+}
+
+TEST_F(KeyframeAnimationEncodingTest, ManyComponents) {
+ const int num_frames = 100;
+ ASSERT_TRUE(CreateAndAddTimestamps(num_frames));
+ ASSERT_EQ(CreateAndAddAnimationData(num_frames, 100), 1);
+ TestKeyframeAnimationEncoding<100>();
+}
+
+TEST_F(KeyframeAnimationEncodingTest, ManyComponentsWithQuantization) {
+ const int num_frames = 100;
+ ASSERT_TRUE(CreateAndAddTimestamps(num_frames));
+ ASSERT_EQ(CreateAndAddAnimationData(num_frames, 4), 1);
+ // Test compression with quantization.
+ TestKeyframeAnimationEncoding<4>(true);
+}
+
+TEST_F(KeyframeAnimationEncodingTest, MultipleAnimations) {
+ const int num_frames = 5;
+ ASSERT_TRUE(CreateAndAddTimestamps(num_frames));
+ ASSERT_EQ(CreateAndAddAnimationData(num_frames, 3), 1);
+ ASSERT_EQ(CreateAndAddAnimationData(num_frames, 3), 2);
+ TestKeyframeAnimationEncoding<3>();
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/animation/keyframe_animation_test.cc b/extern/draco/dracoenc/src/draco/animation/keyframe_animation_test.cc
new file mode 100644
index 00000000000..bc92b25ffc4
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/animation/keyframe_animation_test.cc
@@ -0,0 +1,102 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/animation/keyframe_animation.h"
+
+#include "draco/core/draco_test_base.h"
+
+namespace {
+
+class KeyframeAnimationTest : public ::testing::Test {
+ protected:
+ KeyframeAnimationTest() {}
+
+ bool CreateAndAddTimestamps(int32_t num_frames) {
+ timestamps_.resize(num_frames);
+ for (int i = 0; i < timestamps_.size(); ++i)
+ timestamps_[i] = static_cast<draco::KeyframeAnimation::TimestampType>(i);
+ return keyframe_animation_.SetTimestamps(timestamps_);
+ }
+
+ int32_t CreateAndAddAnimationData(int32_t num_frames,
+ uint32_t num_components) {
+ // Create and add animation data with.
+ animation_data_.resize(num_frames * num_components);
+ for (int i = 0; i < animation_data_.size(); ++i)
+ animation_data_[i] = static_cast<float>(i);
+ return keyframe_animation_.AddKeyframes(draco::DT_FLOAT32, num_components,
+ animation_data_);
+ }
+
+ template <int num_components_t>
+ void CompareAnimationData() {
+ // Compare time stamp.
+ const auto timestamp_att = keyframe_animation_.timestamps();
+ for (int i = 0; i < timestamps_.size(); ++i) {
+ std::array<float, 1> att_value;
+ ASSERT_TRUE((timestamp_att->GetValue<float, 1>(
+ draco::AttributeValueIndex(i), &att_value)));
+ ASSERT_FLOAT_EQ(att_value[0], i);
+ }
+
+ // Compare keyframe data.
+ const auto keyframe_att = keyframe_animation_.keyframes(1);
+ for (int i = 0; i < animation_data_.size() / num_components_t; ++i) {
+ std::array<float, num_components_t> att_value;
+ ASSERT_TRUE((keyframe_att->GetValue<float, num_components_t>(
+ draco::AttributeValueIndex(i), &att_value)));
+ for (int j = 0; j < num_components_t; ++j) {
+ ASSERT_FLOAT_EQ(att_value[j], i * num_components_t + j);
+ }
+ }
+ }
+
+ template <int num_components_t>
+ void TestKeyframeAnimation(int32_t num_frames) {
+ ASSERT_TRUE(CreateAndAddTimestamps(num_frames));
+ ASSERT_EQ(CreateAndAddAnimationData(num_frames, num_components_t), 1);
+ CompareAnimationData<num_components_t>();
+ }
+
+ draco::KeyframeAnimation keyframe_animation_;
+ std::vector<draco::KeyframeAnimation::TimestampType> timestamps_;
+ std::vector<float> animation_data_;
+};
+
+// Test animation with 1 component and 10 frames.
+TEST_F(KeyframeAnimationTest, OneComponent) { TestKeyframeAnimation<1>(10); }
+
+// Test animation with 4 component and 10 frames.
+TEST_F(KeyframeAnimationTest, FourComponent) { TestKeyframeAnimation<4>(10); }
+
+// Test adding animation data before timestamp.
+TEST_F(KeyframeAnimationTest, AddingAnimationFirst) {
+ ASSERT_EQ(CreateAndAddAnimationData(5, 1), 1);
+ ASSERT_TRUE(CreateAndAddTimestamps(5));
+}
+
+// Test adding timestamp more than once.
+TEST_F(KeyframeAnimationTest, ErrorAddingTimestampsTwice) {
+ ASSERT_TRUE(CreateAndAddTimestamps(5));
+ ASSERT_FALSE(CreateAndAddTimestamps(5));
+}
+// Test animation with multiple animation data.
+TEST_F(KeyframeAnimationTest, MultipleAnimationData) {
+ const int num_frames = 5;
+ ASSERT_TRUE(CreateAndAddTimestamps(num_frames));
+ ASSERT_EQ(CreateAndAddAnimationData(num_frames, 1), 1);
+ ASSERT_EQ(CreateAndAddAnimationData(num_frames, 2), 2);
+}
+
+} // namespace
diff --git a/extern/draco/dracoenc/src/draco/attributes/attribute_octahedron_transform.cc b/extern/draco/dracoenc/src/draco/attributes/attribute_octahedron_transform.cc
new file mode 100644
index 00000000000..e1180a48dda
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/attributes/attribute_octahedron_transform.cc
@@ -0,0 +1,86 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "draco/attributes/attribute_octahedron_transform.h"
+
+#include "draco/attributes/attribute_transform_type.h"
+#include "draco/compression/attributes/normal_compression_utils.h"
+
+namespace draco {
+
+bool AttributeOctahedronTransform::InitFromAttribute(
+ const PointAttribute &attribute) {
+ const AttributeTransformData *const transform_data =
+ attribute.GetAttributeTransformData();
+ if (!transform_data ||
+ transform_data->transform_type() != ATTRIBUTE_OCTAHEDRON_TRANSFORM)
+ return false; // Wrong transform type.
+ quantization_bits_ = transform_data->GetParameterValue<int32_t>(0);
+ return true;
+}
+
+void AttributeOctahedronTransform::CopyToAttributeTransformData(
+ AttributeTransformData *out_data) const {
+ out_data->set_transform_type(ATTRIBUTE_OCTAHEDRON_TRANSFORM);
+ out_data->AppendParameterValue(quantization_bits_);
+}
+
+void AttributeOctahedronTransform::SetParameters(int quantization_bits) {
+ quantization_bits_ = quantization_bits;
+}
+
+bool AttributeOctahedronTransform::EncodeParameters(
+ EncoderBuffer *encoder_buffer) const {
+ if (is_initialized()) {
+ encoder_buffer->Encode(static_cast<uint8_t>(quantization_bits_));
+ return true;
+ }
+ return false;
+}
+
+std::unique_ptr<PointAttribute>
+AttributeOctahedronTransform::GeneratePortableAttribute(
+ const PointAttribute &attribute, const std::vector<PointIndex> &point_ids,
+ int num_points) const {
+ DRACO_DCHECK(is_initialized());
+
+ // Allocate portable attribute.
+ const int num_entries = static_cast<int>(point_ids.size());
+ std::unique_ptr<PointAttribute> portable_attribute =
+ InitPortableAttribute(num_entries, 2, num_points, attribute, true);
+
+ // Quantize all values in the order given by point_ids into portable
+ // attribute.
+ int32_t *const portable_attribute_data = reinterpret_cast<int32_t *>(
+ portable_attribute->GetAddress(AttributeValueIndex(0)));
+ float att_val[3];
+ int32_t dst_index = 0;
+ OctahedronToolBox converter;
+ if (!converter.SetQuantizationBits(quantization_bits_))
+ return nullptr;
+ for (uint32_t i = 0; i < point_ids.size(); ++i) {
+ const AttributeValueIndex att_val_id = attribute.mapped_index(point_ids[i]);
+ attribute.GetValue(att_val_id, att_val);
+ // Encode the vector into a s and t octahedral coordinates.
+ int32_t s, t;
+ converter.FloatVectorToQuantizedOctahedralCoords(att_val, &s, &t);
+ portable_attribute_data[dst_index++] = s;
+ portable_attribute_data[dst_index++] = t;
+ }
+
+ return portable_attribute;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/attributes/attribute_octahedron_transform.h b/extern/draco/dracoenc/src/draco/attributes/attribute_octahedron_transform.h
new file mode 100644
index 00000000000..6e4e74284f0
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/attributes/attribute_octahedron_transform.h
@@ -0,0 +1,60 @@
+// Copyright 2017 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_ATTRIBUTES_ATTRIBUTE_OCTAHEDRON_TRANSFORM_H_
+#define DRACO_ATTRIBUTES_ATTRIBUTE_OCTAHEDRON_TRANSFORM_H_
+
+#include "draco/attributes/attribute_transform.h"
+#include "draco/attributes/point_attribute.h"
+#include "draco/core/encoder_buffer.h"
+
+namespace draco {
+
+// Attribute transform for attributes transformed to octahedral coordinates.
+class AttributeOctahedronTransform : public AttributeTransform {
+ public:
+ AttributeOctahedronTransform() : quantization_bits_(-1) {}
+
+ // Return attribute transform type.
+ AttributeTransformType Type() const override {
+ return ATTRIBUTE_OCTAHEDRON_TRANSFORM;
+ }
+ // Try to init transform from attribute.
+ bool InitFromAttribute(const PointAttribute &attribute) override;
+ // Copy parameter values into the provided AttributeTransformData instance.
+ void CopyToAttributeTransformData(
+ AttributeTransformData *out_data) const override;
+
+ // Set number of quantization bits.
+ void SetParameters(int quantization_bits);
+
+ // Encode relevant parameters into buffer.
+ bool EncodeParameters(EncoderBuffer *encoder_buffer) const;
+
+ bool is_initialized() const { return quantization_bits_ != -1; }
+ int32_t quantization_bits() const { return quantization_bits_; }
+
+ // Create portable attribute.
+ std::unique_ptr<PointAttribute> GeneratePortableAttribute(
+ const PointAttribute &attribute, const std::vector<PointIndex> &point_ids,
+ int num_points) const;
+
+ private:
+ int32_t quantization_bits_;
+};
+
+} // namespace draco
+
+#endif // DRACO_ATTRIBUTES_ATTRIBUTE_OCTAHEDRON_TRANSFORM_H_
diff --git a/extern/draco/dracoenc/src/draco/attributes/attribute_quantization_transform.cc b/extern/draco/dracoenc/src/draco/attributes/attribute_quantization_transform.cc
new file mode 100644
index 00000000000..41193f1452f
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/attributes/attribute_quantization_transform.cc
@@ -0,0 +1,173 @@
+
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/attributes/attribute_quantization_transform.h"
+
+#include "draco/attributes/attribute_transform_type.h"
+#include "draco/core/quantization_utils.h"
+
+namespace draco {
+
+bool AttributeQuantizationTransform::InitFromAttribute(
+ const PointAttribute &attribute) {
+ const AttributeTransformData *const transform_data =
+ attribute.GetAttributeTransformData();
+ if (!transform_data ||
+ transform_data->transform_type() != ATTRIBUTE_QUANTIZATION_TRANSFORM)
+ return false; // Wrong transform type.
+ int32_t byte_offset = 0;
+ quantization_bits_ = transform_data->GetParameterValue<int32_t>(byte_offset);
+ byte_offset += 4;
+ min_values_.resize(attribute.num_components());
+ for (int i = 0; i < attribute.num_components(); ++i) {
+ min_values_[i] = transform_data->GetParameterValue<float>(byte_offset);
+ byte_offset += 4;
+ }
+ range_ = transform_data->GetParameterValue<float>(byte_offset);
+ return true;
+}
+
+// Copy parameter values into the provided AttributeTransformData instance.
+void AttributeQuantizationTransform::CopyToAttributeTransformData(
+ AttributeTransformData *out_data) const {
+ out_data->set_transform_type(ATTRIBUTE_QUANTIZATION_TRANSFORM);
+ out_data->AppendParameterValue(quantization_bits_);
+ for (int i = 0; i < min_values_.size(); ++i) {
+ out_data->AppendParameterValue(min_values_[i]);
+ }
+ out_data->AppendParameterValue(range_);
+}
+
+void AttributeQuantizationTransform::SetParameters(int quantization_bits,
+ const float *min_values,
+ int num_components,
+ float range) {
+ quantization_bits_ = quantization_bits;
+ min_values_.assign(min_values, min_values + num_components);
+ range_ = range;
+}
+
+bool AttributeQuantizationTransform::ComputeParameters(
+ const PointAttribute &attribute, const int quantization_bits) {
+ if (quantization_bits_ != -1) {
+ return false; // already initialized.
+ }
+ quantization_bits_ = quantization_bits;
+
+ const int num_components = attribute.num_components();
+ range_ = 0.f;
+ min_values_ = std::vector<float>(num_components, 0.f);
+ const std::unique_ptr<float[]> max_values(new float[num_components]);
+ const std::unique_ptr<float[]> att_val(new float[num_components]);
+ // Compute minimum values and max value difference.
+ attribute.GetValue(AttributeValueIndex(0), att_val.get());
+ attribute.GetValue(AttributeValueIndex(0), min_values_.data());
+ attribute.GetValue(AttributeValueIndex(0), max_values.get());
+
+ for (AttributeValueIndex i(1); i < static_cast<uint32_t>(attribute.size());
+ ++i) {
+ attribute.GetValue(i, att_val.get());
+ for (int c = 0; c < num_components; ++c) {
+ if (min_values_[c] > att_val[c])
+ min_values_[c] = att_val[c];
+ if (max_values[c] < att_val[c])
+ max_values[c] = att_val[c];
+ }
+ }
+ for (int c = 0; c < num_components; ++c) {
+ const float dif = max_values[c] - min_values_[c];
+ if (dif > range_)
+ range_ = dif;
+ }
+
+ return true;
+}
+
+bool AttributeQuantizationTransform::EncodeParameters(
+ EncoderBuffer *encoder_buffer) const {
+ if (is_initialized()) {
+ encoder_buffer->Encode(min_values_.data(),
+ sizeof(float) * min_values_.size());
+ encoder_buffer->Encode(range_);
+ encoder_buffer->Encode(static_cast<uint8_t>(quantization_bits_));
+ return true;
+ }
+ return false;
+}
+
+std::unique_ptr<PointAttribute>
+AttributeQuantizationTransform::GeneratePortableAttribute(
+ const PointAttribute &attribute, int num_points) const {
+ DRACO_DCHECK(is_initialized());
+
+ // Allocate portable attribute.
+ const int num_entries = num_points;
+ const int num_components = attribute.num_components();
+ std::unique_ptr<PointAttribute> portable_attribute =
+ InitPortableAttribute(num_entries, num_components, 0, attribute, true);
+
+ // Quantize all values using the order given by point_ids.
+ int32_t *const portable_attribute_data = reinterpret_cast<int32_t *>(
+ portable_attribute->GetAddress(AttributeValueIndex(0)));
+ const uint32_t max_quantized_value = (1 << (quantization_bits_)) - 1;
+ Quantizer quantizer;
+ quantizer.Init(range(), max_quantized_value);
+ int32_t dst_index = 0;
+ const std::unique_ptr<float[]> att_val(new float[num_components]);
+ for (PointIndex i(0); i < num_points; ++i) {
+ const AttributeValueIndex att_val_id = attribute.mapped_index(i);
+ attribute.GetValue(att_val_id, att_val.get());
+ for (int c = 0; c < num_components; ++c) {
+ const float value = (att_val[c] - min_values()[c]);
+ const int32_t q_val = quantizer.QuantizeFloat(value);
+ portable_attribute_data[dst_index++] = q_val;
+ }
+ }
+ return portable_attribute;
+}
+
+std::unique_ptr<PointAttribute>
+AttributeQuantizationTransform::GeneratePortableAttribute(
+ const PointAttribute &attribute, const std::vector<PointIndex> &point_ids,
+ int num_points) const {
+ DRACO_DCHECK(is_initialized());
+
+ // Allocate portable attribute.
+ const int num_entries = static_cast<int>(point_ids.size());
+ const int num_components = attribute.num_components();
+ std::unique_ptr<PointAttribute> portable_attribute = InitPortableAttribute(
+ num_entries, num_components, num_points, attribute, true);
+
+ // Quantize all values using the order given by point_ids.
+ int32_t *const portable_attribute_data = reinterpret_cast<int32_t *>(
+ portable_attribute->GetAddress(AttributeValueIndex(0)));
+ const uint32_t max_quantized_value = (1 << (quantization_bits_)) - 1;
+ Quantizer quantizer;
+ quantizer.Init(range(), max_quantized_value);
+ int32_t dst_index = 0;
+ const std::unique_ptr<float[]> att_val(new float[num_components]);
+ for (uint32_t i = 0; i < point_ids.size(); ++i) {
+ const AttributeValueIndex att_val_id = attribute.mapped_index(point_ids[i]);
+ attribute.GetValue(att_val_id, att_val.get());
+ for (int c = 0; c < num_components; ++c) {
+ const float value = (att_val[c] - min_values()[c]);
+ const int32_t q_val = quantizer.QuantizeFloat(value);
+ portable_attribute_data[dst_index++] = q_val;
+ }
+ }
+ return portable_attribute;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/attributes/attribute_quantization_transform.h b/extern/draco/dracoenc/src/draco/attributes/attribute_quantization_transform.h
new file mode 100644
index 00000000000..934856f2db7
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/attributes/attribute_quantization_transform.h
@@ -0,0 +1,78 @@
+// Copyright 2017 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_ATTRIBUTES_ATTRIBUTE_QUANTIZATION_TRANSFORM_H_
+#define DRACO_ATTRIBUTES_ATTRIBUTE_QUANTIZATION_TRANSFORM_H_
+
+#include <vector>
+
+#include "draco/attributes/attribute_transform.h"
+#include "draco/attributes/point_attribute.h"
+#include "draco/core/encoder_buffer.h"
+
+namespace draco {
+
+// Attribute transform for quantized attributes.
+class AttributeQuantizationTransform : public AttributeTransform {
+ public:
+ AttributeQuantizationTransform() : quantization_bits_(-1), range_(0.f) {}
+ // Return attribute transform type.
+ AttributeTransformType Type() const override {
+ return ATTRIBUTE_QUANTIZATION_TRANSFORM;
+ }
+ // Try to init transform from attribute.
+ bool InitFromAttribute(const PointAttribute &attribute) override;
+ // Copy parameter values into the provided AttributeTransformData instance.
+ void CopyToAttributeTransformData(
+ AttributeTransformData *out_data) const override;
+
+ void SetParameters(int quantization_bits, const float *min_values,
+ int num_components, float range);
+
+ bool ComputeParameters(const PointAttribute &attribute,
+ const int quantization_bits);
+
+ // Encode relevant parameters into buffer.
+ bool EncodeParameters(EncoderBuffer *encoder_buffer) const;
+
+ int32_t quantization_bits() const { return quantization_bits_; }
+ float min_value(int axis) const { return min_values_[axis]; }
+ const std::vector<float> &min_values() const { return min_values_; }
+ float range() const { return range_; }
+ bool is_initialized() const { return quantization_bits_ != -1; }
+
+ // Create portable attribute using 1:1 mapping between points in the input and
+ // output attribute.
+ std::unique_ptr<PointAttribute> GeneratePortableAttribute(
+ const PointAttribute &attribute, int num_points) const;
+
+ // Create portable attribute using custom mapping between input and output
+ // points.
+ std::unique_ptr<PointAttribute> GeneratePortableAttribute(
+ const PointAttribute &attribute, const std::vector<PointIndex> &point_ids,
+ int num_points) const;
+
+ private:
+ int32_t quantization_bits_;
+
+ // Minimal dequantized value for each component of the attribute.
+ std::vector<float> min_values_;
+
+ // Bounds of the dequantized attribute (max delta over all components).
+ float range_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_ATTRIBUTE_DEQUANTIZATION_TRANSFORM_H_
diff --git a/extern/draco/dracoenc/src/draco/attributes/attribute_transform.cc b/extern/draco/dracoenc/src/draco/attributes/attribute_transform.cc
new file mode 100644
index 00000000000..55af630ac07
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/attributes/attribute_transform.cc
@@ -0,0 +1,44 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/attributes/attribute_transform.h"
+
+namespace draco {
+
+bool AttributeTransform::TransferToAttribute(PointAttribute *attribute) const {
+ std::unique_ptr<AttributeTransformData> transform_data(
+ new AttributeTransformData());
+ this->CopyToAttributeTransformData(transform_data.get());
+ attribute->SetAttributeTransformData(std::move(transform_data));
+ return true;
+}
+
+std::unique_ptr<PointAttribute> AttributeTransform::InitPortableAttribute(
+ int num_entries, int num_components, int num_points,
+ const PointAttribute &attribute, bool is_unsigned) const {
+ const DataType dt = is_unsigned ? DT_UINT32 : DT_INT32;
+ GeometryAttribute va;
+ va.Init(attribute.attribute_type(), nullptr, num_components, dt, false,
+ num_components * DataTypeLength(dt), 0);
+ std::unique_ptr<PointAttribute> portable_attribute(new PointAttribute(va));
+ portable_attribute->Reset(num_entries);
+ if (num_points) {
+ portable_attribute->SetExplicitMapping(num_points);
+ } else {
+ portable_attribute->SetIdentityMapping();
+ }
+ return portable_attribute;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/attributes/attribute_transform.h b/extern/draco/dracoenc/src/draco/attributes/attribute_transform.h
new file mode 100644
index 00000000000..d746fbf6eea
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/attributes/attribute_transform.h
@@ -0,0 +1,46 @@
+// Copyright 2017 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_ATTRIBUTES_ATTRIBUTE_TRANSFORM_H_
+#define DRACO_ATTRIBUTES_ATTRIBUTE_TRANSFORM_H_
+
+#include "draco/attributes/attribute_transform_data.h"
+#include "draco/attributes/point_attribute.h"
+
+namespace draco {
+
+// Virtual base class for various attribute transforms, enforcing common
+// interface where possible.
+class AttributeTransform {
+ public:
+ virtual ~AttributeTransform() = default;
+
+ // Return attribute transform type.
+ virtual AttributeTransformType Type() const = 0;
+ // Try to init transform from attribute.
+ virtual bool InitFromAttribute(const PointAttribute &attribute) = 0;
+ // Copy parameter values into the provided AttributeTransformData instance.
+ virtual void CopyToAttributeTransformData(
+ AttributeTransformData *out_data) const = 0;
+ bool TransferToAttribute(PointAttribute *attribute) const;
+
+ protected:
+ std::unique_ptr<PointAttribute> InitPortableAttribute(
+ int num_entries, int num_components, int num_points,
+ const PointAttribute &attribute, bool is_unsigned) const;
+};
+
+} // namespace draco
+
+#endif // DRACO_ATTRIBUTES_ATTRIBUTE_OCTAHEDRON_TRANSFORM_H_
diff --git a/extern/draco/dracoenc/src/draco/attributes/attribute_transform_data.h b/extern/draco/dracoenc/src/draco/attributes/attribute_transform_data.h
new file mode 100644
index 00000000000..96ed073200d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/attributes/attribute_transform_data.h
@@ -0,0 +1,71 @@
+// Copyright 2017 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_ATTRIBUTES_ATTRIBUTE_TRANSFORM_DATA_H_
+#define DRACO_ATTRIBUTES_ATTRIBUTE_TRANSFORM_DATA_H_
+
+#include <memory>
+
+#include "draco/attributes/attribute_transform_type.h"
+#include "draco/core/data_buffer.h"
+
+namespace draco {
+
+// Class for holding parameter values for an attribute transform of a
+// PointAttribute. This can be for example quantization data for an attribute
+// that holds quantized values. This class provides only a basic storage for
+// attribute transform parameters and it should be accessed only through wrapper
+// classes for a specific transform (e.g. AttributeQuantizationTransform).
+class AttributeTransformData {
+ public:
+ AttributeTransformData() : transform_type_(ATTRIBUTE_INVALID_TRANSFORM) {}
+ AttributeTransformData(const AttributeTransformData &data) = default;
+
+ // Returns the type of the attribute transform that is described by the class.
+ AttributeTransformType transform_type() const { return transform_type_; }
+ void set_transform_type(AttributeTransformType type) {
+ transform_type_ = type;
+ }
+
+ // Returns a parameter value on a given |byte_offset|.
+ template <typename DataTypeT>
+ DataTypeT GetParameterValue(int byte_offset) const {
+ DataTypeT out_data;
+ buffer_.Read(byte_offset, &out_data, sizeof(DataTypeT));
+ return out_data;
+ }
+
+ // Sets a parameter value on a given |byte_offset|.
+ template <typename DataTypeT>
+ void SetParameterValue(int byte_offset, const DataTypeT &in_data) {
+ if (byte_offset + sizeof(DataTypeT) > buffer_.data_size()) {
+ buffer_.Resize(byte_offset + sizeof(DataTypeT));
+ }
+ buffer_.Write(byte_offset, &in_data, sizeof(DataTypeT));
+ }
+
+ // Sets a parameter value at the end of the |buffer_|.
+ template <typename DataTypeT>
+ void AppendParameterValue(const DataTypeT &in_data) {
+ SetParameterValue(static_cast<int>(buffer_.data_size()), in_data);
+ }
+
+ private:
+ AttributeTransformType transform_type_;
+ DataBuffer buffer_;
+};
+
+} // namespace draco
+
+#endif // DRACO_ATTRIBUTES_ATTRIBUTE_TRANSFORM_DATA_H_
diff --git a/extern/draco/dracoenc/src/draco/attributes/attribute_transform_type.h b/extern/draco/dracoenc/src/draco/attributes/attribute_transform_type.h
new file mode 100644
index 00000000000..51ce6f333b4
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/attributes/attribute_transform_type.h
@@ -0,0 +1,30 @@
+// Copyright 2017 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_ATTRIBUTES_ATTRIBUTE_TRANSFORM_TYPE_H_
+#define DRACO_ATTRIBUTES_ATTRIBUTE_TRANSFORM_TYPE_H_
+
+namespace draco {
+
+// List of all currently supported attribute transforms.
+enum AttributeTransformType {
+ ATTRIBUTE_INVALID_TRANSFORM = -1,
+ ATTRIBUTE_NO_TRANSFORM = 0,
+ ATTRIBUTE_QUANTIZATION_TRANSFORM = 1,
+ ATTRIBUTE_OCTAHEDRON_TRANSFORM = 2,
+};
+
+} // namespace draco
+
+#endif // DRACO_ATTRIBUTES_ATTRIBUTE_TRANSFORM_TYPE_H_
diff --git a/extern/draco/dracoenc/src/draco/attributes/geometry_attribute.cc b/extern/draco/dracoenc/src/draco/attributes/geometry_attribute.cc
new file mode 100644
index 00000000000..914a85d9dc2
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/attributes/geometry_attribute.cc
@@ -0,0 +1,91 @@
+// 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 "draco/attributes/geometry_attribute.h"
+
+using std::array;
+
+namespace draco {
+
+GeometryAttribute::GeometryAttribute()
+ : buffer_(nullptr),
+ num_components_(1),
+ data_type_(DT_FLOAT32),
+ byte_stride_(0),
+ byte_offset_(0),
+ attribute_type_(INVALID),
+ unique_id_(0) {}
+
+void GeometryAttribute::Init(GeometryAttribute::Type attribute_type,
+ DataBuffer *buffer, int8_t num_components,
+ DataType data_type, bool normalized,
+ int64_t byte_stride, int64_t byte_offset) {
+ buffer_ = buffer;
+ if (buffer) {
+ buffer_descriptor_.buffer_id = buffer->buffer_id();
+ buffer_descriptor_.buffer_update_count = buffer->update_count();
+ }
+ num_components_ = num_components;
+ data_type_ = data_type;
+ normalized_ = normalized;
+ byte_stride_ = byte_stride;
+ byte_offset_ = byte_offset;
+ attribute_type_ = attribute_type;
+}
+
+bool GeometryAttribute::CopyFrom(const GeometryAttribute &src_att) {
+ if (buffer_ == nullptr || src_att.buffer_ == nullptr)
+ return false;
+ buffer_->Update(src_att.buffer_->data(), src_att.buffer_->data_size());
+ num_components_ = src_att.num_components_;
+ data_type_ = src_att.data_type_;
+ normalized_ = src_att.normalized_;
+ byte_stride_ = src_att.byte_stride_;
+ byte_offset_ = src_att.byte_offset_;
+ attribute_type_ = src_att.attribute_type_;
+ buffer_descriptor_ = src_att.buffer_descriptor_;
+ return true;
+}
+
+bool GeometryAttribute::operator==(const GeometryAttribute &va) const {
+ if (attribute_type_ != va.attribute_type_)
+ return false;
+ // It's OK to compare just the buffer descriptors here. We don't need to
+ // compare the buffers themselves.
+ if (buffer_descriptor_.buffer_id != va.buffer_descriptor_.buffer_id)
+ return false;
+ if (buffer_descriptor_.buffer_update_count !=
+ va.buffer_descriptor_.buffer_update_count)
+ return false;
+ if (num_components_ != va.num_components_)
+ return false;
+ if (data_type_ != va.data_type_)
+ return false;
+ if (byte_stride_ != va.byte_stride_)
+ return false;
+ if (byte_offset_ != va.byte_offset_)
+ return false;
+ return true;
+}
+
+void GeometryAttribute::ResetBuffer(DataBuffer *buffer, int64_t byte_stride,
+ int64_t byte_offset) {
+ buffer_ = buffer;
+ buffer_descriptor_.buffer_id = buffer->buffer_id();
+ buffer_descriptor_.buffer_update_count = buffer->update_count();
+ byte_stride_ = byte_stride;
+ byte_offset_ = byte_offset;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/attributes/geometry_attribute.h b/extern/draco/dracoenc/src/draco/attributes/geometry_attribute.h
new file mode 100644
index 00000000000..7be40fe2f65
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/attributes/geometry_attribute.h
@@ -0,0 +1,304 @@
+// 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_ATTRIBUTES_GEOMETRY_ATTRIBUTE_H_
+#define DRACO_ATTRIBUTES_GEOMETRY_ATTRIBUTE_H_
+
+#include <array>
+#include <limits>
+
+#include "draco/attributes/geometry_indices.h"
+#include "draco/core/data_buffer.h"
+#include "draco/core/hash_utils.h"
+
+namespace draco {
+
+// The class provides access to a specific attribute which is stored in a
+// DataBuffer, such as normals or coordinates. However, the GeometryAttribute
+// class does not own the buffer and the buffer itself may store other data
+// unrelated to this attribute (such as data for other attributes in which case
+// we can have multiple GeometryAttributes accessing one buffer). Typically,
+// all attributes for a point (or corner, face) are stored in one block, which
+// is advantageous in terms of memory access. The length of the entire block is
+// given by the byte_stride, the position where the attribute starts is given by
+// the byte_offset, the actual number of bytes that the attribute occupies is
+// given by the data_type and the number of components.
+class GeometryAttribute {
+ public:
+ // Supported attribute types.
+ enum Type {
+ INVALID = -1,
+ // Named attributes start here. The difference between named and generic
+ // attributes is that for named attributes we know their purpose and we
+ // can apply some special methods when dealing with them (e.g. during
+ // encoding).
+ POSITION = 0,
+ NORMAL,
+ COLOR,
+ TEX_COORD,
+ // A special id used to mark attributes that are not assigned to any known
+ // predefined use case. Such attributes are often used for a shader specific
+ // data.
+ GENERIC,
+ // Total number of different attribute types.
+ // Always keep behind all named attributes.
+ NAMED_ATTRIBUTES_COUNT,
+ };
+
+ GeometryAttribute();
+ // Initializes and enables the attribute.
+ void Init(Type attribute_type, DataBuffer *buffer, int8_t num_components,
+ DataType data_type, bool normalized, int64_t byte_stride,
+ int64_t byte_offset);
+ bool IsValid() const { return buffer_ != nullptr; }
+
+ // Copies data from the source attribute to the this attribute.
+ // This attribute must have a valid buffer allocated otherwise the operation
+ // is going to fail and return false.
+ bool CopyFrom(const GeometryAttribute &src_att);
+
+ // Function for getting a attribute value with a specific format.
+ // Unsafe. Caller must ensure the accessed memory is valid.
+ // T is the attribute data type.
+ // att_components_t is the number of attribute components.
+ template <typename T, int att_components_t>
+ std::array<T, att_components_t> GetValue(
+ AttributeValueIndex att_index) const {
+ // Byte address of the attribute index.
+ const int64_t byte_pos = byte_offset_ + byte_stride_ * att_index.value();
+ std::array<T, att_components_t> out;
+ buffer_->Read(byte_pos, &(out[0]), sizeof(out));
+ return out;
+ }
+
+ // Function for getting a attribute value with a specific format.
+ // T is the attribute data type.
+ // att_components_t is the number of attribute components.
+ template <typename T, int att_components_t>
+ bool GetValue(AttributeValueIndex att_index,
+ std::array<T, att_components_t> *out) const {
+ // Byte address of the attribute index.
+ const int64_t byte_pos = byte_offset_ + byte_stride_ * att_index.value();
+ // Check we are not reading past end of data.
+ if (byte_pos + sizeof(*out) > buffer_->data_size())
+ return false;
+ buffer_->Read(byte_pos, &((*out)[0]), sizeof(*out));
+ return true;
+ }
+
+ // Returns the byte position of the attribute entry in the data buffer.
+ inline int64_t GetBytePos(AttributeValueIndex att_index) const {
+ return byte_offset_ + byte_stride_ * att_index.value();
+ }
+
+ inline const uint8_t *GetAddress(AttributeValueIndex att_index) const {
+ const int64_t byte_pos = GetBytePos(att_index);
+ return buffer_->data() + byte_pos;
+ }
+ inline uint8_t *GetAddress(AttributeValueIndex att_index) {
+ const int64_t byte_pos = GetBytePos(att_index);
+ return buffer_->data() + byte_pos;
+ }
+
+ // Fills out_data with the raw value of the requested attribute entry.
+ // out_data must be at least byte_stride_ long.
+ void GetValue(AttributeValueIndex att_index, void *out_data) const {
+ const int64_t byte_pos = byte_offset_ + byte_stride_ * att_index.value();
+ buffer_->Read(byte_pos, out_data, byte_stride_);
+ }
+
+ // DEPRECATED: Use
+ // ConvertValue(AttributeValueIndex att_id,
+ // int out_num_components,
+ // OutT *out_val);
+ //
+ // Function for conversion of a attribute to a specific output format.
+ // OutT is the desired data type of the attribute.
+ // out_att_components_t is the number of components of the output format.
+ // Returns false when the conversion failed.
+ template <typename OutT, int out_att_components_t>
+ bool ConvertValue(AttributeValueIndex att_id, OutT *out_val) const {
+ return ConvertValue(att_id, out_att_components_t, out_val);
+ }
+
+ // Function for conversion of a attribute to a specific output format.
+ // |out_val| needs to be able to store |out_num_components| values.
+ // OutT is the desired data type of the attribute.
+ // Returns false when the conversion failed.
+ template <typename OutT>
+ bool ConvertValue(AttributeValueIndex att_id, int8_t out_num_components,
+ OutT *out_val) const {
+ if (out_val == nullptr)
+ return false;
+ switch (data_type_) {
+ case DT_INT8:
+ return ConvertTypedValue<int8_t, OutT>(att_id, out_num_components,
+ out_val);
+ case DT_UINT8:
+ return ConvertTypedValue<uint8_t, OutT>(att_id, out_num_components,
+ out_val);
+ case DT_INT16:
+ return ConvertTypedValue<int16_t, OutT>(att_id, out_num_components,
+ out_val);
+ case DT_UINT16:
+ return ConvertTypedValue<uint16_t, OutT>(att_id, out_num_components,
+ out_val);
+ case DT_INT32:
+ return ConvertTypedValue<int32_t, OutT>(att_id, out_num_components,
+ out_val);
+ case DT_UINT32:
+ return ConvertTypedValue<uint32_t, OutT>(att_id, out_num_components,
+ out_val);
+ case DT_INT64:
+ return ConvertTypedValue<int64_t, OutT>(att_id, out_num_components,
+ out_val);
+ case DT_UINT64:
+ return ConvertTypedValue<uint64_t, OutT>(att_id, out_num_components,
+ out_val);
+ case DT_FLOAT32:
+ return ConvertTypedValue<float, OutT>(att_id, out_num_components,
+ out_val);
+ case DT_FLOAT64:
+ return ConvertTypedValue<double, OutT>(att_id, out_num_components,
+ out_val);
+ case DT_BOOL:
+ return ConvertTypedValue<bool, OutT>(att_id, out_num_components,
+ out_val);
+ default:
+ // Wrong attribute type.
+ return false;
+ }
+ }
+
+ // Function for conversion of a attribute to a specific output format.
+ // The |out_value| must be able to store all components of a single attribute
+ // entry.
+ // OutT is the desired data type of the attribute.
+ // Returns false when the conversion failed.
+ template <typename OutT>
+ bool ConvertValue(AttributeValueIndex att_index, OutT *out_value) const {
+ return ConvertValue<OutT>(att_index, num_components_, out_value);
+ }
+
+ bool operator==(const GeometryAttribute &va) const;
+
+ // Returns the type of the attribute indicating the nature of the attribute.
+ Type attribute_type() const { return attribute_type_; }
+ void set_attribute_type(Type type) { attribute_type_ = type; }
+ // Returns the data type that is stored in the attribute.
+ DataType data_type() const { return data_type_; }
+ // Returns the number of components that are stored for each entry.
+ // For position attribute this is usually three (x,y,z),
+ // while texture coordinates have two components (u,v).
+ int8_t num_components() const { return num_components_; }
+ // Indicates whether the data type should be normalized before interpretation,
+ // that is, it should be divided by the max value of the data type.
+ bool normalized() const { return normalized_; }
+ // The buffer storing the entire data of the attribute.
+ const DataBuffer *buffer() const { return buffer_; }
+ // Returns the number of bytes between two attribute entries, this is, at
+ // least size of the data types times number of components.
+ int64_t byte_stride() const { return byte_stride_; }
+ // The offset where the attribute starts within the block of size byte_stride.
+ int64_t byte_offset() const { return byte_offset_; }
+ void set_byte_offset(int64_t byte_offset) { byte_offset_ = byte_offset; }
+ DataBufferDescriptor buffer_descriptor() const { return buffer_descriptor_; }
+ uint32_t unique_id() const { return unique_id_; }
+ void set_unique_id(uint32_t id) { unique_id_ = id; }
+
+ protected:
+ // Sets a new internal storage for the attribute.
+ void ResetBuffer(DataBuffer *buffer, int64_t byte_stride,
+ int64_t byte_offset);
+
+ private:
+ // Function for conversion of an attribute to a specific output format given a
+ // format of the stored attribute.
+ // T is the stored attribute data type.
+ // OutT is the desired data type of the attribute.
+ template <typename T, typename OutT>
+ bool ConvertTypedValue(AttributeValueIndex att_id, int8_t out_num_components,
+ OutT *out_value) const {
+ const uint8_t *src_address = GetAddress(att_id);
+
+ // Convert all components available in both the original and output formats.
+ for (int i = 0; i < std::min(num_components_, out_num_components); ++i) {
+ const T in_value = *reinterpret_cast<const T *>(src_address);
+ out_value[i] = static_cast<OutT>(in_value);
+ // When converting integer to floating point, normalize the value if
+ // necessary.
+ if (std::is_integral<T>::value && std::is_floating_point<OutT>::value &&
+ normalized_) {
+ out_value[i] /= static_cast<OutT>(std::numeric_limits<T>::max());
+ }
+ // TODO(ostava): Add handling of normalized attributes when converting
+ // between different integer representations. If the attribute is
+ // normalized, integer values should be converted as if they represent 0-1
+ // range. E.g. when we convert uint16 to uint8, the range <0, 2^16 - 1>
+ // should be converted to range <0, 2^8 - 1>.
+ src_address += sizeof(T);
+ }
+ // Fill empty data for unused output components if needed.
+ for (int i = num_components_; i < out_num_components; ++i) {
+ out_value[i] = static_cast<OutT>(0);
+ }
+ return true;
+ }
+
+ DataBuffer *buffer_;
+ // The buffer descriptor is stored at the time the buffer is attached to this
+ // attribute. The purpose is to detect if any changes happened to the buffer
+ // since the time it was attached.
+ DataBufferDescriptor buffer_descriptor_;
+ int8_t num_components_;
+ DataType data_type_;
+ bool normalized_;
+ int64_t byte_stride_;
+ int64_t byte_offset_;
+
+ Type attribute_type_;
+
+ // Unique id of this attribute. No two attributes could have the same unique
+ // id. It is used to identify each attribute, especially when there are
+ // multiple attribute of the same type in a point cloud.
+ uint32_t unique_id_;
+
+ friend struct GeometryAttributeHasher;
+};
+
+// Hashing support
+
+// Function object for using Attribute as a hash key.
+struct GeometryAttributeHasher {
+ size_t operator()(const GeometryAttribute &va) const {
+ size_t hash = HashCombine(va.buffer_descriptor_.buffer_id,
+ va.buffer_descriptor_.buffer_update_count);
+ hash = HashCombine(va.num_components_, hash);
+ hash = HashCombine((int8_t)va.data_type_, hash);
+ hash = HashCombine((int8_t)va.attribute_type_, hash);
+ hash = HashCombine(va.byte_stride_, hash);
+ return HashCombine(va.byte_offset_, hash);
+ }
+};
+
+// Function object for using GeometryAttribute::Type as a hash key.
+struct GeometryAttributeTypeHasher {
+ size_t operator()(const GeometryAttribute::Type &at) const {
+ return static_cast<size_t>(at);
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_ATTRIBUTES_GEOMETRY_ATTRIBUTE_H_
diff --git a/extern/draco/dracoenc/src/draco/attributes/geometry_indices.h b/extern/draco/dracoenc/src/draco/attributes/geometry_indices.h
new file mode 100644
index 00000000000..5244056f0aa
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/attributes/geometry_indices.h
@@ -0,0 +1,54 @@
+// 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_ATTRIBUTES_GEOMETRY_INDICES_H_
+#define DRACO_ATTRIBUTES_GEOMETRY_INDICES_H_
+
+#include <inttypes.h>
+
+#include <limits>
+
+#include "draco/core/draco_index_type.h"
+
+namespace draco {
+
+// Index of an attribute value entry stored in a GeometryAttribute.
+DEFINE_NEW_DRACO_INDEX_TYPE(uint32_t, AttributeValueIndex)
+// Index of a point in a PointCloud.
+DEFINE_NEW_DRACO_INDEX_TYPE(uint32_t, PointIndex)
+// Vertex index in a Mesh or CornerTable.
+DEFINE_NEW_DRACO_INDEX_TYPE(uint32_t, VertexIndex);
+// Corner index that identifies a corner in a Mesh or CornerTable.
+DEFINE_NEW_DRACO_INDEX_TYPE(uint32_t, CornerIndex);
+// Face index for Mesh and CornerTable.
+DEFINE_NEW_DRACO_INDEX_TYPE(uint32_t, FaceIndex);
+
+// Constants denoting invalid indices.
+static constexpr AttributeValueIndex kInvalidAttributeValueIndex(
+ std::numeric_limits<uint32_t>::max());
+static constexpr PointIndex kInvalidPointIndex(
+ std::numeric_limits<uint32_t>::max());
+static constexpr VertexIndex kInvalidVertexIndex(
+ std::numeric_limits<uint32_t>::max());
+static constexpr CornerIndex kInvalidCornerIndex(
+ std::numeric_limits<uint32_t>::max());
+static constexpr FaceIndex kInvalidFaceIndex(
+ std::numeric_limits<uint32_t>::max());
+
+// TODO(ostava): Add strongly typed indices for attribute id and unique
+// attribute id.
+
+} // namespace draco
+
+#endif // DRACO_ATTRIBUTES_GEOMETRY_INDICES_H_
diff --git a/extern/draco/dracoenc/src/draco/attributes/point_attribute.cc b/extern/draco/dracoenc/src/draco/attributes/point_attribute.cc
new file mode 100644
index 00000000000..4428f332b93
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/attributes/point_attribute.cc
@@ -0,0 +1,205 @@
+// 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 "draco/attributes/point_attribute.h"
+
+#include <unordered_map>
+
+using std::unordered_map;
+
+// Shortcut for typed conditionals.
+template <bool B, class T, class F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+
+namespace draco {
+
+PointAttribute::PointAttribute()
+ : num_unique_entries_(0), identity_mapping_(false) {}
+
+PointAttribute::PointAttribute(const GeometryAttribute &att)
+ : GeometryAttribute(att),
+ num_unique_entries_(0),
+ identity_mapping_(false) {}
+
+void PointAttribute::CopyFrom(const PointAttribute &src_att) {
+ if (buffer() == nullptr) {
+ // If the destination attribute doesn't have a valid buffer, create it.
+ attribute_buffer_ = std::unique_ptr<DataBuffer>(new DataBuffer());
+ ResetBuffer(attribute_buffer_.get(), 0, 0);
+ }
+ if (!GeometryAttribute::CopyFrom(src_att))
+ return;
+ identity_mapping_ = src_att.identity_mapping_;
+ num_unique_entries_ = src_att.num_unique_entries_;
+ indices_map_ = src_att.indices_map_;
+ if (src_att.attribute_transform_data_) {
+ attribute_transform_data_ = std::unique_ptr<AttributeTransformData>(
+ new AttributeTransformData(*src_att.attribute_transform_data_.get()));
+ } else {
+ attribute_transform_data_ = nullptr;
+ }
+}
+
+bool PointAttribute::Reset(size_t num_attribute_values) {
+ if (attribute_buffer_ == nullptr) {
+ attribute_buffer_ = std::unique_ptr<DataBuffer>(new DataBuffer());
+ }
+ const int64_t entry_size = DataTypeLength(data_type()) * num_components();
+ if (!attribute_buffer_->Update(nullptr, num_attribute_values * entry_size))
+ return false;
+ // Assign the new buffer to the parent attribute.
+ ResetBuffer(attribute_buffer_.get(), entry_size, 0);
+ num_unique_entries_ = static_cast<uint32_t>(num_attribute_values);
+ return true;
+}
+
+#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
+AttributeValueIndex::ValueType PointAttribute::DeduplicateValues(
+ const GeometryAttribute &in_att) {
+ return DeduplicateValues(in_att, AttributeValueIndex(0));
+}
+
+AttributeValueIndex::ValueType PointAttribute::DeduplicateValues(
+ const GeometryAttribute &in_att, AttributeValueIndex in_att_offset) {
+ AttributeValueIndex::ValueType unique_vals = 0;
+ switch (in_att.data_type()) {
+ // Currently we support only float, uint8, and uint16 arguments.
+ case DT_FLOAT32:
+ unique_vals = DeduplicateTypedValues<float>(in_att, in_att_offset);
+ break;
+ case DT_INT8:
+ unique_vals = DeduplicateTypedValues<int8_t>(in_att, in_att_offset);
+ break;
+ case DT_UINT8:
+ case DT_BOOL:
+ unique_vals = DeduplicateTypedValues<uint8_t>(in_att, in_att_offset);
+ break;
+ case DT_UINT16:
+ unique_vals = DeduplicateTypedValues<uint16_t>(in_att, in_att_offset);
+ break;
+ case DT_INT16:
+ unique_vals = DeduplicateTypedValues<int16_t>(in_att, in_att_offset);
+ break;
+ case DT_UINT32:
+ unique_vals = DeduplicateTypedValues<uint32_t>(in_att, in_att_offset);
+ break;
+ case DT_INT32:
+ unique_vals = DeduplicateTypedValues<int32_t>(in_att, in_att_offset);
+ break;
+ default:
+ return -1; // Unsupported data type.
+ }
+ if (unique_vals == 0)
+ return -1; // Unexpected error.
+ return unique_vals;
+}
+
+// Helper function for calling UnifyDuplicateAttributes<T,num_components_t>
+// with the correct template arguments.
+// Returns the number of unique attribute values.
+template <typename T>
+AttributeValueIndex::ValueType PointAttribute::DeduplicateTypedValues(
+ const GeometryAttribute &in_att, AttributeValueIndex in_att_offset) {
+ // Select the correct method to call based on the number of attribute
+ // components.
+ switch (in_att.num_components()) {
+ case 1:
+ return DeduplicateFormattedValues<T, 1>(in_att, in_att_offset);
+ case 2:
+ return DeduplicateFormattedValues<T, 2>(in_att, in_att_offset);
+ case 3:
+ return DeduplicateFormattedValues<T, 3>(in_att, in_att_offset);
+ case 4:
+ return DeduplicateFormattedValues<T, 4>(in_att, in_att_offset);
+ default:
+ return 0;
+ }
+}
+
+template <typename T, int num_components_t>
+AttributeValueIndex::ValueType PointAttribute::DeduplicateFormattedValues(
+ const GeometryAttribute &in_att, AttributeValueIndex in_att_offset) {
+ // We want to detect duplicates using a hash map but we cannot hash floating
+ // point numbers directly so bit-copy floats to the same sized integers and
+ // hash them.
+
+ // First we need to determine which int type to use (1, 2, 4 or 8 bytes).
+ // Note, this is done at compile time using std::conditional struct.
+ // Conditional is in form <bool-expression, true, false>. If bool-expression
+ // is true the "true" branch is used and vice versa. All at compile time.
+ typedef conditional_t<sizeof(T) == 1, uint8_t,
+ conditional_t<sizeof(T) == 2, uint16_t,
+ conditional_t<sizeof(T) == 4, uint32_t,
+ /*else*/ uint64_t>>>
+ HashType;
+
+ AttributeValueIndex unique_vals(0);
+ typedef std::array<T, num_components_t> AttributeValue;
+ typedef std::array<HashType, num_components_t> AttributeHashableValue;
+ // Hash map storing index of the first attribute with a given value.
+ unordered_map<AttributeHashableValue, AttributeValueIndex,
+ HashArray<AttributeHashableValue>>
+ value_to_index_map;
+ AttributeValue att_value;
+ AttributeHashableValue hashable_value;
+ IndexTypeVector<AttributeValueIndex, AttributeValueIndex> value_map(
+ num_unique_entries_);
+ for (AttributeValueIndex i(0); i < num_unique_entries_; ++i) {
+ const AttributeValueIndex att_pos = i + in_att_offset;
+ att_value = in_att.GetValue<T, num_components_t>(att_pos);
+ // Convert the value to hashable type. Bit-copy real attributes to integers.
+ memcpy(&(hashable_value[0]), &(att_value[0]), sizeof(att_value));
+
+ // Check if the given attribute value has been used before already.
+ auto it = value_to_index_map.find(hashable_value);
+ if (it != value_to_index_map.end()) {
+ // Duplicated value found. Update index mapping.
+ value_map[i] = it->second;
+ } else {
+ // New unique value.
+ // Update the hash map with a new entry pointing to the latest unique
+ // vertex index.
+ value_to_index_map.insert(
+ std::pair<AttributeHashableValue, AttributeValueIndex>(hashable_value,
+ unique_vals));
+ // Add the unique value to the mesh builder.
+ SetAttributeValue(unique_vals, &att_value);
+ // Update index mapping.
+ value_map[i] = unique_vals;
+
+ ++unique_vals;
+ }
+ }
+ if (unique_vals == num_unique_entries_)
+ return unique_vals.value(); // Nothing has changed.
+ if (is_mapping_identity()) {
+ // Change identity mapping to the explicit one.
+ // The number of points is equal to the number of old unique values.
+ SetExplicitMapping(num_unique_entries_);
+ // Update the explicit map.
+ for (uint32_t i = 0; i < num_unique_entries_; ++i) {
+ SetPointMapEntry(PointIndex(i), value_map[AttributeValueIndex(i)]);
+ }
+ } else {
+ // Update point to value map using the mapping between old and new values.
+ for (PointIndex i(0); i < static_cast<uint32_t>(indices_map_.size()); ++i) {
+ SetPointMapEntry(i, value_map[indices_map_[i]]);
+ }
+ }
+ num_unique_entries_ = unique_vals.value();
+ return num_unique_entries_;
+}
+#endif
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/attributes/point_attribute.h b/extern/draco/dracoenc/src/draco/attributes/point_attribute.h
new file mode 100644
index 00000000000..dffde50356d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/attributes/point_attribute.h
@@ -0,0 +1,186 @@
+// 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_ATTRIBUTES_POINT_ATTRIBUTE_H_
+#define DRACO_ATTRIBUTES_POINT_ATTRIBUTE_H_
+
+#include <memory>
+
+#include "draco/draco_features.h"
+
+#include "draco/attributes/attribute_transform_data.h"
+#include "draco/attributes/geometry_attribute.h"
+#include "draco/core/draco_index_type_vector.h"
+#include "draco/core/hash_utils.h"
+#include "draco/core/macros.h"
+
+namespace draco {
+
+// Class for storing point specific data about each attribute. In general,
+// multiple points stored in a point cloud can share the same attribute value
+// and this class provides the necessary mapping between point ids and attribute
+// value ids.
+class PointAttribute : public GeometryAttribute {
+ public:
+ PointAttribute();
+ explicit PointAttribute(const GeometryAttribute &att);
+
+ // Make sure the move constructor is defined (needed for better performance
+ // when new attributes are added to PointCloud).
+ PointAttribute(PointAttribute &&attribute) = default;
+ PointAttribute &operator=(PointAttribute &&attribute) = default;
+
+ // Copies attribute data from the provided |src_att| attribute.
+ void CopyFrom(const PointAttribute &src_att);
+
+ // Prepares the attribute storage for the specified number of entries.
+ bool Reset(size_t num_attribute_values);
+
+ size_t size() const { return num_unique_entries_; }
+ AttributeValueIndex mapped_index(PointIndex point_index) const {
+ if (identity_mapping_)
+ return AttributeValueIndex(point_index.value());
+ return indices_map_[point_index];
+ }
+ DataBuffer *buffer() const { return attribute_buffer_.get(); }
+ bool is_mapping_identity() const { return identity_mapping_; }
+ size_t indices_map_size() const {
+ if (is_mapping_identity())
+ return 0;
+ return indices_map_.size();
+ }
+
+ const uint8_t *GetAddressOfMappedIndex(PointIndex point_index) const {
+ return GetAddress(mapped_index(point_index));
+ }
+
+ // Sets the new number of unique attribute entries for the attribute.
+ void Resize(size_t new_num_unique_entries) {
+ num_unique_entries_ = static_cast<uint32_t>(new_num_unique_entries);
+ }
+
+ // Functions for setting the type of mapping between point indices and
+ // attribute entry ids.
+ // This function sets the mapping to implicit, where point indices are equal
+ // to attribute entry indices.
+ void SetIdentityMapping() {
+ identity_mapping_ = true;
+ indices_map_.clear();
+ }
+ // This function sets the mapping to be explicitly using the indices_map_
+ // array that needs to be initialized by the caller.
+ void SetExplicitMapping(size_t num_points) {
+ identity_mapping_ = false;
+ indices_map_.resize(num_points, kInvalidAttributeValueIndex);
+ }
+
+ // Set an explicit map entry for a specific point index.
+ void SetPointMapEntry(PointIndex point_index,
+ AttributeValueIndex entry_index) {
+ DRACO_DCHECK(!identity_mapping_);
+ indices_map_[point_index] = entry_index;
+ }
+
+ // Sets a value of an attribute entry. The input value must be allocated to
+ // cover all components of a single attribute entry.
+ void SetAttributeValue(AttributeValueIndex entry_index, const void *value) {
+ const int64_t byte_pos = entry_index.value() * byte_stride();
+ buffer()->Write(byte_pos, value, byte_stride());
+ }
+
+ // Same as GeometryAttribute::GetValue(), but using point id as the input.
+ // Mapping to attribute value index is performed automatically.
+ void GetMappedValue(PointIndex point_index, void *out_data) const {
+ return GetValue(mapped_index(point_index), out_data);
+ }
+
+#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
+ // Deduplicate |in_att| values into |this| attribute. |in_att| can be equal
+ // to |this|.
+ // Returns -1 if the deduplication failed.
+ AttributeValueIndex::ValueType DeduplicateValues(
+ const GeometryAttribute &in_att);
+
+ // Same as above but the values read from |in_att| are sampled with the
+ // provided offset |in_att_offset|.
+ AttributeValueIndex::ValueType DeduplicateValues(
+ const GeometryAttribute &in_att, AttributeValueIndex in_att_offset);
+#endif
+
+ // Set attribute transform data for the attribute. The data is used to store
+ // the type and parameters of the transform that is applied on the attribute
+ // data (optional).
+ void SetAttributeTransformData(
+ std::unique_ptr<AttributeTransformData> transform_data) {
+ attribute_transform_data_ = std::move(transform_data);
+ }
+ const AttributeTransformData *GetAttributeTransformData() const {
+ return attribute_transform_data_.get();
+ }
+
+ private:
+#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
+ template <typename T>
+ AttributeValueIndex::ValueType DeduplicateTypedValues(
+ const GeometryAttribute &in_att, AttributeValueIndex in_att_offset);
+ template <typename T, int COMPONENTS_COUNT>
+ AttributeValueIndex::ValueType DeduplicateFormattedValues(
+ const GeometryAttribute &in_att, AttributeValueIndex in_att_offset);
+#endif
+
+ // Data storage for attribute values. GeometryAttribute itself doesn't own its
+ // buffer so we need to allocate it here.
+ std::unique_ptr<DataBuffer> attribute_buffer_;
+
+ // Mapping between point ids and attribute value ids.
+ IndexTypeVector<PointIndex, AttributeValueIndex> indices_map_;
+ AttributeValueIndex::ValueType num_unique_entries_;
+ // Flag when the mapping between point ids and attribute values is identity.
+ bool identity_mapping_;
+
+ // If an attribute contains transformed data (e.g. quantized), we can specify
+ // the attribute transform here and use it to transform the attribute back to
+ // its original format.
+ std::unique_ptr<AttributeTransformData> attribute_transform_data_;
+
+ friend struct PointAttributeHasher;
+};
+
+// Hash functor for the PointAttribute class.
+struct PointAttributeHasher {
+ size_t operator()(const PointAttribute &attribute) const {
+ GeometryAttributeHasher base_hasher;
+ size_t hash = base_hasher(attribute);
+ hash = HashCombine(attribute.identity_mapping_, hash);
+ hash = HashCombine(attribute.num_unique_entries_, hash);
+ hash = HashCombine(attribute.indices_map_.size(), hash);
+ if (attribute.indices_map_.size() > 0) {
+ const uint64_t indices_hash = FingerprintString(
+ reinterpret_cast<const char *>(attribute.indices_map_.data()),
+ attribute.indices_map_.size());
+ hash = HashCombine(indices_hash, hash);
+ }
+ if (attribute.attribute_buffer_ != nullptr) {
+ const uint64_t buffer_hash = FingerprintString(
+ reinterpret_cast<const char *>(attribute.attribute_buffer_->data()),
+ attribute.attribute_buffer_->data_size());
+ hash = HashCombine(buffer_hash, hash);
+ }
+ return hash;
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_ATTRIBUTES_POINT_ATTRIBUTE_H_
diff --git a/extern/draco/dracoenc/src/draco/attributes/point_attribute_test.cc b/extern/draco/dracoenc/src/draco/attributes/point_attribute_test.cc
new file mode 100644
index 00000000000..183003abea4
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/attributes/point_attribute_test.cc
@@ -0,0 +1,129 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/attributes/point_attribute.h"
+
+#include "draco/core/draco_test_base.h"
+
+namespace {
+
+class PointAttributeTest : public ::testing::Test {
+ protected:
+ PointAttributeTest() {}
+};
+
+TEST_F(PointAttributeTest, TestCopy) {
+ // This test verifies that PointAttribute can copy data from another point
+ // attribute.
+ draco::GeometryAttribute pos_att;
+ pos_att.Init(draco::GeometryAttribute::POSITION, nullptr, 1, draco::DT_INT32,
+ false, 4, 0);
+ draco::PointAttribute pa(pos_att);
+ pa.SetIdentityMapping();
+ pa.Reset(10);
+ for (int32_t i = 0; i < 10; ++i) {
+ pa.SetAttributeValue(draco::AttributeValueIndex(i), &i);
+ }
+
+ draco::PointAttribute other_pa;
+ other_pa.CopyFrom(pa);
+
+ draco::PointAttributeHasher hasher;
+ ASSERT_EQ(hasher(pa), hasher(other_pa));
+
+ // The hash function does not actually compute the hash from attribute values,
+ // so ensure the data got copied correctly as well.
+ for (int32_t i = 0; i < 10; ++i) {
+ int32_t data;
+ other_pa.GetValue(draco::AttributeValueIndex(i), &data);
+ ASSERT_EQ(data, i);
+ }
+}
+
+TEST_F(PointAttributeTest, TestGetValueFloat) {
+ draco::GeometryAttribute pos_att;
+ pos_att.Init(draco::GeometryAttribute::POSITION, nullptr, 3,
+ draco::DT_FLOAT32, false, 4, 0);
+ draco::PointAttribute pa(pos_att);
+ pa.SetIdentityMapping();
+ pa.Reset(5);
+ float points[3];
+ for (int32_t i = 0; i < 5; ++i) {
+ points[0] = i * 3.0;
+ points[1] = (i * 3.0) + 1.0;
+ points[2] = (i * 3.0) + 2.0;
+ pa.SetAttributeValue(draco::AttributeValueIndex(i), &points);
+ }
+
+ for (int32_t i = 0; i < 5; ++i) {
+ pa.GetValue(draco::AttributeValueIndex(i), &points);
+ ASSERT_FLOAT_EQ(points[0], i * 3.0);
+ ASSERT_FLOAT_EQ(points[1], (i * 3.0) + 1.0);
+ ASSERT_FLOAT_EQ(points[2], (i * 3.0) + 2.0);
+ }
+}
+
+TEST_F(PointAttributeTest, TestGetArray) {
+ draco::GeometryAttribute pos_att;
+ pos_att.Init(draco::GeometryAttribute::POSITION, nullptr, 3,
+ draco::DT_FLOAT32, false, 4, 0);
+ draco::PointAttribute pa(pos_att);
+ pa.SetIdentityMapping();
+ pa.Reset(5);
+ float points[3];
+ for (int32_t i = 0; i < 5; ++i) {
+ points[0] = i * 3.0;
+ points[1] = (i * 3.0) + 1.0;
+ points[2] = (i * 3.0) + 2.0;
+ pa.SetAttributeValue(draco::AttributeValueIndex(i), &points);
+ }
+
+ for (int32_t i = 0; i < 5; ++i) {
+ std::array<float, 3> att_value;
+ att_value = pa.GetValue<float, 3>(draco::AttributeValueIndex(i));
+ ASSERT_FLOAT_EQ(att_value[0], i * 3.0);
+ ASSERT_FLOAT_EQ(att_value[1], (i * 3.0) + 1.0);
+ ASSERT_FLOAT_EQ(att_value[2], (i * 3.0) + 2.0);
+ }
+ for (int32_t i = 0; i < 5; ++i) {
+ std::array<float, 3> att_value;
+ EXPECT_TRUE(
+ (pa.GetValue<float, 3>(draco::AttributeValueIndex(i), &att_value)));
+ ASSERT_FLOAT_EQ(att_value[0], i * 3.0);
+ ASSERT_FLOAT_EQ(att_value[1], (i * 3.0) + 1.0);
+ ASSERT_FLOAT_EQ(att_value[2], (i * 3.0) + 2.0);
+ }
+}
+
+TEST_F(PointAttributeTest, TestArrayReadError) {
+ draco::GeometryAttribute pos_att;
+ pos_att.Init(draco::GeometryAttribute::POSITION, nullptr, 3,
+ draco::DT_FLOAT32, false, 4, 0);
+ draco::PointAttribute pa(pos_att);
+ pa.SetIdentityMapping();
+ pa.Reset(5);
+ float points[3];
+ for (int32_t i = 0; i < 5; ++i) {
+ points[0] = i * 3.0;
+ points[1] = (i * 3.0) + 1.0;
+ points[2] = (i * 3.0) + 2.0;
+ pa.SetAttributeValue(draco::AttributeValueIndex(i), &points);
+ }
+
+ std::array<float, 3> att_value;
+ EXPECT_FALSE(
+ (pa.GetValue<float, 3>(draco::AttributeValueIndex(5), &att_value)));
+}
+
+} // namespace
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/attributes_decoder.cc b/extern/draco/dracoenc/src/draco/compression/attributes/attributes_decoder.cc
new file mode 100644
index 00000000000..eb42edea06a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/attributes_decoder.cc
@@ -0,0 +1,97 @@
+// 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 "draco/compression/attributes/attributes_decoder.h"
+
+#include "draco/core/varint_decoding.h"
+
+namespace draco {
+
+AttributesDecoder::AttributesDecoder()
+ : point_cloud_decoder_(nullptr), point_cloud_(nullptr) {}
+
+bool AttributesDecoder::Init(PointCloudDecoder *decoder, PointCloud *pc) {
+ point_cloud_decoder_ = decoder;
+ point_cloud_ = pc;
+ return true;
+}
+
+bool AttributesDecoder::DecodeAttributesDecoderData(DecoderBuffer *in_buffer) {
+ // Decode and create attributes.
+ uint32_t num_attributes;
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (point_cloud_decoder_->bitstream_version() <
+ DRACO_BITSTREAM_VERSION(2, 0)) {
+ if (!in_buffer->Decode(&num_attributes))
+ return false;
+ } else
+#endif
+ {
+ if (!DecodeVarint(&num_attributes, in_buffer))
+ return false;
+ }
+ if (num_attributes == 0)
+ return false;
+ point_attribute_ids_.resize(num_attributes);
+ PointCloud *pc = point_cloud_;
+ for (uint32_t i = 0; i < num_attributes; ++i) {
+ // Decode attribute descriptor data.
+ uint8_t att_type, data_type, num_components, normalized;
+ if (!in_buffer->Decode(&att_type))
+ return false;
+ if (!in_buffer->Decode(&data_type))
+ return false;
+ if (!in_buffer->Decode(&num_components))
+ return false;
+ if (!in_buffer->Decode(&normalized))
+ return false;
+ if (data_type <= DT_INVALID || data_type >= DT_TYPES_COUNT)
+ return false;
+ const DataType draco_dt = static_cast<DataType>(data_type);
+
+ // Add the attribute to the point cloud
+ GeometryAttribute ga;
+ ga.Init(static_cast<GeometryAttribute::Type>(att_type), nullptr,
+ num_components, draco_dt, normalized > 0,
+ DataTypeLength(draco_dt) * num_components, 0);
+ uint32_t unique_id;
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (point_cloud_decoder_->bitstream_version() <
+ DRACO_BITSTREAM_VERSION(1, 3)) {
+ uint16_t custom_id;
+ if (!in_buffer->Decode(&custom_id))
+ return false;
+ // TODO(draco-eng): Add "custom_id" to attribute metadata.
+ unique_id = static_cast<uint32_t>(custom_id);
+ ga.set_unique_id(unique_id);
+ } else
+#endif
+ {
+ DecodeVarint(&unique_id, in_buffer);
+ ga.set_unique_id(unique_id);
+ }
+ const int att_id = pc->AddAttribute(
+ std::unique_ptr<PointAttribute>(new PointAttribute(ga)));
+ pc->attribute(att_id)->set_unique_id(unique_id);
+ point_attribute_ids_[i] = att_id;
+
+ // Update the inverse map.
+ if (att_id >= static_cast<int32_t>(point_attribute_to_local_id_map_.size()))
+ point_attribute_to_local_id_map_.resize(att_id + 1, -1);
+ point_attribute_to_local_id_map_[att_id] = i;
+ }
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/attributes_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/attributes_decoder.h
new file mode 100644
index 00000000000..9c6e9fe5e4d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/attributes_decoder.h
@@ -0,0 +1,94 @@
+// 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_COMPRESSION_ATTRIBUTES_ATTRIBUTES_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_ATTRIBUTES_DECODER_H_
+
+#include <vector>
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/attributes/attributes_decoder_interface.h"
+#include "draco/compression/point_cloud/point_cloud_decoder.h"
+#include "draco/core/decoder_buffer.h"
+#include "draco/point_cloud/point_cloud.h"
+
+namespace draco {
+
+// Base class for decoding one or more attributes that were encoded with a
+// matching AttributesEncoder. It is a basic implementation of
+// AttributesDecoderInterface that provides functionality that is shared between
+// all AttributesDecoders.
+class AttributesDecoder : public AttributesDecoderInterface {
+ public:
+ AttributesDecoder();
+ virtual ~AttributesDecoder() = default;
+
+ // Called after all attribute decoders are created. It can be used to perform
+ // any custom initialization.
+ bool Init(PointCloudDecoder *decoder, PointCloud *pc) override;
+
+ // Decodes any attribute decoder specific data from the |in_buffer|.
+ bool DecodeAttributesDecoderData(DecoderBuffer *in_buffer) override;
+
+ int32_t GetAttributeId(int i) const override {
+ return point_attribute_ids_[i];
+ }
+ int32_t GetNumAttributes() const override {
+ return static_cast<int32_t>(point_attribute_ids_.size());
+ }
+ PointCloudDecoder *GetDecoder() const override {
+ return point_cloud_decoder_;
+ }
+
+ // Decodes attribute data from the source buffer.
+ bool DecodeAttributes(DecoderBuffer *in_buffer) override {
+ if (!DecodePortableAttributes(in_buffer))
+ return false;
+ if (!DecodeDataNeededByPortableTransforms(in_buffer))
+ return false;
+ if (!TransformAttributesToOriginalFormat())
+ return false;
+ return true;
+ }
+
+ protected:
+ int32_t GetLocalIdForPointAttribute(int32_t point_attribute_id) const {
+ const int id_map_size =
+ static_cast<int>(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];
+ }
+ virtual bool DecodePortableAttributes(DecoderBuffer *in_buffer) = 0;
+ virtual bool DecodeDataNeededByPortableTransforms(DecoderBuffer *in_buffer) {
+ return true;
+ }
+ virtual bool TransformAttributesToOriginalFormat() { return true; }
+
+ private:
+ // List of attribute ids that need to be decoded with this decoder.
+ std::vector<int32_t> point_attribute_ids_;
+
+ // Map between point attribute id and the local id (i.e., the inverse of the
+ // |point_attribute_ids_|.
+ std::vector<int32_t> point_attribute_to_local_id_map_;
+
+ PointCloudDecoder *point_cloud_decoder_;
+ PointCloud *point_cloud_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_ATTRIBUTES_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/attributes_decoder_interface.h b/extern/draco/dracoenc/src/draco/compression/attributes/attributes_decoder_interface.h
new file mode 100644
index 00000000000..8e5cf52ac3b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/attributes_decoder_interface.h
@@ -0,0 +1,62 @@
+// Copyright 2017 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_COMPRESSION_ATTRIBUTES_ATTRIBUTES_DECODER_INTERFACE_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_ATTRIBUTES_DECODER_INTERFACE_H_
+
+#include <vector>
+
+#include "draco/core/decoder_buffer.h"
+#include "draco/point_cloud/point_cloud.h"
+
+namespace draco {
+
+class PointCloudDecoder;
+
+// Interface class for decoding one or more attributes that were encoded with a
+// matching AttributesEncoder. It provides only the basic interface
+// that is used by the PointCloudDecoder. The actual decoding must be
+// implemented in derived classes using the DecodeAttributes() method.
+class AttributesDecoderInterface {
+ public:
+ AttributesDecoderInterface() = default;
+ virtual ~AttributesDecoderInterface() = default;
+
+ // Called after all attribute decoders are created. It can be used to perform
+ // any custom initialization.
+ virtual bool Init(PointCloudDecoder *decoder, PointCloud *pc) = 0;
+
+ // Decodes any attribute decoder specific data from the |in_buffer|.
+ virtual bool DecodeAttributesDecoderData(DecoderBuffer *in_buffer) = 0;
+
+ // Decode attribute data from the source buffer. Needs to be implemented by
+ // the derived classes.
+ virtual bool DecodeAttributes(DecoderBuffer *in_buffer) = 0;
+
+ virtual int32_t GetAttributeId(int i) const = 0;
+ virtual int32_t GetNumAttributes() const = 0;
+ virtual PointCloudDecoder *GetDecoder() const = 0;
+
+ // Returns an attribute containing data processed by the attribute transform.
+ // (see TransformToPortableFormat() method). This data is guaranteed to be
+ // same for encoder and decoder and it can be used by predictors.
+ virtual const PointAttribute *GetPortableAttribute(
+ int32_t /* point_attribute_id */) {
+ return nullptr;
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_ATTRIBUTES_DECODER_INTERFACE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/attributes_encoder.cc b/extern/draco/dracoenc/src/draco/compression/attributes/attributes_encoder.cc
new file mode 100644
index 00000000000..797c62f30aa
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/attributes_encoder.cc
@@ -0,0 +1,49 @@
+// 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 "draco/compression/attributes/attributes_encoder.h"
+
+#include "draco/core/varint_encoding.h"
+
+namespace draco {
+
+AttributesEncoder::AttributesEncoder()
+ : point_cloud_encoder_(nullptr), point_cloud_(nullptr) {}
+
+AttributesEncoder::AttributesEncoder(int att_id) : AttributesEncoder() {
+ AddAttributeId(att_id);
+}
+
+bool AttributesEncoder::Init(PointCloudEncoder *encoder, const PointCloud *pc) {
+ point_cloud_encoder_ = encoder;
+ point_cloud_ = pc;
+ return true;
+}
+
+bool AttributesEncoder::EncodeAttributesEncoderData(EncoderBuffer *out_buffer) {
+ // Encode data about all attributes.
+ EncodeVarint(num_attributes(), out_buffer);
+ for (uint32_t i = 0; i < num_attributes(); ++i) {
+ const int32_t att_id = point_attribute_ids_[i];
+ const PointAttribute *const pa = point_cloud_->attribute(att_id);
+ out_buffer->Encode(static_cast<uint8_t>(pa->attribute_type()));
+ out_buffer->Encode(static_cast<uint8_t>(pa->data_type()));
+ out_buffer->Encode(static_cast<uint8_t>(pa->num_components()));
+ out_buffer->Encode(static_cast<uint8_t>(pa->normalized()));
+ EncodeVarint(pa->unique_id(), out_buffer);
+ }
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/attributes_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/attributes_encoder.h
new file mode 100644
index 00000000000..09d10109803
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/attributes_encoder.h
@@ -0,0 +1,149 @@
+// 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_COMPRESSION_ATTRIBUTES_ATTRIBUTES_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_ATTRIBUTES_ENCODER_H_
+
+#include "draco/attributes/point_attribute.h"
+#include "draco/core/encoder_buffer.h"
+#include "draco/point_cloud/point_cloud.h"
+
+namespace draco {
+
+class PointCloudEncoder;
+
+// Base class for encoding one or more attributes of a PointCloud (or other
+// geometry). This base class provides only the basic interface that is used
+// by the PointCloudEncoder.
+class AttributesEncoder {
+ public:
+ AttributesEncoder();
+ // Constructs an attribute encoder associated with a given point attribute.
+ explicit AttributesEncoder(int point_attrib_id);
+ virtual ~AttributesEncoder() = default;
+
+ // Called after all attribute encoders are created. It can be used to perform
+ // any custom initialization, including setting up attribute dependencies.
+ // Note: no data should be encoded in this function, because the decoder may
+ // process encoders in a different order from the decoder.
+ virtual bool Init(PointCloudEncoder *encoder, const PointCloud *pc);
+
+ // Encodes data needed by the target attribute decoder.
+ virtual bool EncodeAttributesEncoderData(EncoderBuffer *out_buffer);
+
+ // Returns a unique identifier of the given encoder type, that is used during
+ // decoding to construct the corresponding attribute decoder.
+ virtual uint8_t GetUniqueId() const = 0;
+
+ // Encode attribute data to the target buffer.
+ virtual bool EncodeAttributes(EncoderBuffer *out_buffer) {
+ if (!TransformAttributesToPortableFormat())
+ return false;
+ if (!EncodePortableAttributes(out_buffer))
+ return false;
+ // Encode data needed by portable transforms after the attribute is encoded.
+ // This corresponds to the order in which the data is going to be decoded by
+ // the decoder.
+ if (!EncodeDataNeededByPortableTransforms(out_buffer))
+ return false;
+ return true;
+ }
+
+ // 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 {
+ return 0;
+ }
+
+ 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;
+ }
+
+ // Returns an attribute containing data processed by the attribute transform.
+ // (see TransformToPortableFormat() method). This data is guaranteed to be
+ // encoded losslessly and it can be safely used for predictors.
+ virtual const PointAttribute *GetPortableAttribute(
+ int32_t /* point_attribute_id */) {
+ return nullptr;
+ }
+
+ void AddAttributeId(int32_t id) {
+ point_attribute_ids_.push_back(id);
+ if (id >= static_cast<int32_t>(point_attribute_to_local_id_map_.size()))
+ point_attribute_to_local_id_map_.resize(id + 1, -1);
+ point_attribute_to_local_id_map_[id] =
+ static_cast<int32_t>(point_attribute_ids_.size()) - 1;
+ }
+
+ // Sets new attribute point ids (replacing the existing ones).
+ void SetAttributeIds(const std::vector<int32_t> &point_attribute_ids) {
+ point_attribute_ids_.clear();
+ point_attribute_to_local_id_map_.clear();
+ for (int32_t att_id : point_attribute_ids) {
+ AddAttributeId(att_id);
+ }
+ }
+
+ int32_t GetAttributeId(int i) const { return point_attribute_ids_[i]; }
+ uint32_t num_attributes() const {
+ return static_cast<uint32_t>(point_attribute_ids_.size());
+ }
+ PointCloudEncoder *encoder() const { return point_cloud_encoder_; }
+
+ protected:
+ // Transforms the input attribute data into a form that should be losslessly
+ // encoded (transform itself can be lossy).
+ virtual bool TransformAttributesToPortableFormat() { return true; }
+
+ // Losslessly encodes data of all portable attributes.
+ // Precondition: All attributes must have been transformed into portable
+ // format at this point (see TransformAttributesToPortableFormat() method).
+ virtual bool EncodePortableAttributes(EncoderBuffer *out_buffer) = 0;
+
+ // Encodes any data needed to revert the transform to portable format for each
+ // attribute (e.g. data needed for dequantization of quantized values).
+ virtual bool EncodeDataNeededByPortableTransforms(EncoderBuffer *out_buffer) {
+ return true;
+ }
+
+ int32_t GetLocalIdForPointAttribute(int32_t point_attribute_id) const {
+ const int id_map_size =
+ static_cast<int>(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];
+ }
+
+ private:
+ // List of attribute ids that need to be encoded with this encoder.
+ std::vector<int32_t> point_attribute_ids_;
+
+ // Map between point attribute id and the local id (i.e., the inverse of the
+ // |point_attribute_ids_|.
+ std::vector<int32_t> point_attribute_to_local_id_map_;
+
+ PointCloudEncoder *point_cloud_encoder_;
+ const PointCloud *point_cloud_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_ATTRIBUTES_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_decoder.cc b/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_decoder.cc
new file mode 100644
index 00000000000..4faa8ab29b5
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_decoder.cc
@@ -0,0 +1,515 @@
+// 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 "draco/compression/attributes/kd_tree_attributes_decoder.h"
+#include "draco/compression/attributes/kd_tree_attributes_shared.h"
+#include "draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.h"
+#include "draco/compression/point_cloud/algorithms/float_points_tree_decoder.h"
+#include "draco/compression/point_cloud/point_cloud_decoder.h"
+#include "draco/core/draco_types.h"
+#include "draco/core/varint_decoding.h"
+
+namespace draco {
+
+// attribute, offset_dimensionality, data_type, data_size, num_components
+using AttributeTuple =
+ std::tuple<PointAttribute *, uint32_t, DataType, uint32_t, uint32_t>;
+
+// Output iterator that is used to decode values directly into the data buffer
+// of the modified PointAttribute.
+// The extension of this iterator beyond the DT_UINT32 concerns itself only with
+// the size of the data for efficiency, not the type. DataType is conveyed in
+// but is an unused field populated for any future logic/special casing.
+// DT_UINT32 and all other 4-byte types are naturally supported from the size of
+// data in the kd tree encoder. DT_UINT16 and DT_UINT8 are supported by way
+// of byte copies into a temporary memory buffer.
+template <class CoeffT>
+class PointAttributeVectorOutputIterator {
+ typedef PointAttributeVectorOutputIterator<CoeffT> Self;
+
+ public:
+ PointAttributeVectorOutputIterator(
+ PointAttributeVectorOutputIterator &&that) = default;
+
+ explicit PointAttributeVectorOutputIterator(
+ const std::vector<AttributeTuple> &atts)
+ : attributes_(atts), point_id_(0) {
+ DRACO_DCHECK_GE(atts.size(), 1);
+ uint32_t required_decode_bytes = 0;
+ for (auto index = 0; index < attributes_.size(); index++) {
+ const AttributeTuple &att = attributes_[index];
+ required_decode_bytes = (std::max)(required_decode_bytes,
+ std::get<3>(att) * std::get<4>(att));
+ }
+ memory_.resize(required_decode_bytes);
+ data_ = memory_.data();
+ }
+
+ const Self &operator++() {
+ ++point_id_;
+ return *this;
+ }
+
+ // We do not want to do ANY copying of this constructor so this particular
+ // operator is disabled for performance reasons.
+ // Self operator++(int) {
+ // Self copy = *this;
+ // ++point_id_;
+ // return copy;
+ // }
+
+ Self &operator*() { return *this; }
+ // Still needed in some cases.
+ // TODO(hemmer): remove.
+ // hardcoded to 3 based on legacy usage.
+ const Self &operator=(const VectorD<CoeffT, 3> &val) {
+ DRACO_DCHECK_EQ(attributes_.size(), 1); // Expect only ONE attribute.
+ AttributeTuple &att = attributes_[0];
+ PointAttribute *attribute = std::get<0>(att);
+ const uint32_t &offset = std::get<1>(att);
+ DRACO_DCHECK_EQ(offset, 0); // expected to be zero
+ attribute->SetAttributeValue(attribute->mapped_index(point_id_),
+ &val[0] + offset);
+ return *this;
+ }
+ // Additional operator taking std::vector as argument.
+ const Self &operator=(const std::vector<CoeffT> &val) {
+ for (auto index = 0; index < attributes_.size(); index++) {
+ AttributeTuple &att = attributes_[index];
+ PointAttribute *attribute = std::get<0>(att);
+ const uint32_t &offset = std::get<1>(att);
+ const uint32_t &data_size = std::get<3>(att);
+ const uint32_t &num_components = std::get<4>(att);
+ const uint32_t *data_source = val.data() + offset;
+ if (data_size != 4) { // handle uint16_t, uint8_t
+ // selectively copy data bytes
+ uint8_t *data_counter = data_;
+ for (uint32_t index = 0; index < num_components;
+ index += 1, data_counter += data_size) {
+ std::memcpy(data_counter, data_source + index, data_size);
+ }
+ // redirect to copied data
+ data_source = reinterpret_cast<uint32_t *>(data_);
+ }
+ const AttributeValueIndex avi = attribute->mapped_index(point_id_);
+ if (avi >= static_cast<uint32_t>(attribute->size()))
+ return *this;
+ attribute->SetAttributeValue(avi, data_source);
+ }
+ return *this;
+ }
+
+ private:
+ // preallocated memory for buffering different data sizes. Never reallocated.
+ std::vector<uint8_t> memory_;
+ uint8_t *data_;
+ std::vector<AttributeTuple> attributes_;
+ PointIndex point_id_;
+
+ // NO COPY
+ PointAttributeVectorOutputIterator(
+ const PointAttributeVectorOutputIterator &that) = delete;
+ PointAttributeVectorOutputIterator &operator=(
+ PointAttributeVectorOutputIterator const &) = delete;
+};
+
+KdTreeAttributesDecoder::KdTreeAttributesDecoder() {}
+
+bool KdTreeAttributesDecoder::DecodePortableAttributes(
+ DecoderBuffer *in_buffer) {
+ if (in_buffer->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 3)) {
+ // Old bitstream does everything in the
+ // DecodeDataNeededByPortableTransforms() method.
+ return true;
+ }
+ uint8_t compression_level = 0;
+ if (!in_buffer->Decode(&compression_level))
+ return false;
+ const int32_t num_points = GetDecoder()->point_cloud()->num_points();
+
+ // Decode data using the kd tree decoding into integer (portable) attributes.
+ // We first need to go over all attributes and create a new portable storage
+ // for those attributes that need it (floating point attributes that have to
+ // be dequantized after decoding).
+
+ const int num_attributes = GetNumAttributes();
+ uint32_t total_dimensionality = 0; // position is a required dimension
+ std::vector<AttributeTuple> atts(num_attributes);
+
+ for (int i = 0; i < GetNumAttributes(); ++i) {
+ const int att_id = GetAttributeId(i);
+ PointAttribute *const att = GetDecoder()->point_cloud()->attribute(att_id);
+ // All attributes have the same number of values and identity mapping
+ // between PointIndex and AttributeValueIndex.
+ att->Reset(num_points);
+ att->SetIdentityMapping();
+
+ PointAttribute *target_att = nullptr;
+ if (att->data_type() == DT_UINT32 || att->data_type() == DT_UINT16 ||
+ att->data_type() == DT_UINT8) {
+ // We can decode to these attributes directly.
+ target_att = att;
+ } else if (att->data_type() == DT_INT32 || att->data_type() == DT_INT16 ||
+ att->data_type() == DT_INT8) {
+ // Prepare storage for data that is used to convert unsigned values back
+ // to the signed ones.
+ for (int c = 0; c < att->num_components(); ++c) {
+ min_signed_values_.push_back(0);
+ }
+ target_att = att;
+ } else if (att->data_type() == DT_FLOAT32) {
+ // Create a portable attribute that will hold the decoded data. We will
+ // dequantize the decoded data to the final attribute later on.
+ const int num_components = att->num_components();
+ GeometryAttribute va;
+ va.Init(att->attribute_type(), nullptr, num_components, DT_UINT32, false,
+ num_components * DataTypeLength(DT_UINT32), 0);
+ std::unique_ptr<PointAttribute> port_att(new PointAttribute(va));
+ port_att->SetIdentityMapping();
+ port_att->Reset(num_points);
+ quantized_portable_attributes_.push_back(std::move(port_att));
+ target_att = quantized_portable_attributes_.back().get();
+ } else {
+ // Unsupported type.
+ return false;
+ }
+ // Add attribute to the output iterator used by the core algorithm.
+ const DataType data_type = target_att->data_type();
+ const uint32_t data_size = (std::max)(0, DataTypeLength(data_type));
+ const uint32_t num_components = target_att->num_components();
+ atts[i] = std::make_tuple(target_att, total_dimensionality, data_type,
+ data_size, num_components);
+ total_dimensionality += num_components;
+ }
+ PointAttributeVectorOutputIterator<uint32_t> out_it(atts);
+
+ switch (compression_level) {
+ case 0: {
+ DynamicIntegerPointsKdTreeDecoder<0> decoder(total_dimensionality);
+ if (!decoder.DecodePoints(in_buffer, out_it))
+ return false;
+ break;
+ }
+ case 1: {
+ DynamicIntegerPointsKdTreeDecoder<1> decoder(total_dimensionality);
+ if (!decoder.DecodePoints(in_buffer, out_it))
+ return false;
+ break;
+ }
+ case 2: {
+ DynamicIntegerPointsKdTreeDecoder<2> decoder(total_dimensionality);
+ if (!decoder.DecodePoints(in_buffer, out_it))
+ return false;
+ break;
+ }
+ case 3: {
+ DynamicIntegerPointsKdTreeDecoder<3> decoder(total_dimensionality);
+ if (!decoder.DecodePoints(in_buffer, out_it))
+ return false;
+ break;
+ }
+ case 4: {
+ DynamicIntegerPointsKdTreeDecoder<4> decoder(total_dimensionality);
+ if (!decoder.DecodePoints(in_buffer, out_it))
+ return false;
+ break;
+ }
+ case 5: {
+ DynamicIntegerPointsKdTreeDecoder<5> decoder(total_dimensionality);
+ if (!decoder.DecodePoints(in_buffer, out_it))
+ return false;
+ break;
+ }
+ case 6: {
+ DynamicIntegerPointsKdTreeDecoder<6> decoder(total_dimensionality);
+ if (!decoder.DecodePoints(in_buffer, out_it))
+ return false;
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool KdTreeAttributesDecoder::DecodeDataNeededByPortableTransforms(
+ DecoderBuffer *in_buffer) {
+ if (in_buffer->bitstream_version() >= DRACO_BITSTREAM_VERSION(2, 3)) {
+ // Decode quantization data for each attribute that need it.
+ // TODO(ostava): This should be moved to AttributeQuantizationTransform.
+ std::vector<float> min_value;
+ for (int i = 0; i < GetNumAttributes(); ++i) {
+ const int att_id = GetAttributeId(i);
+ const PointAttribute *const att =
+ GetDecoder()->point_cloud()->attribute(att_id);
+ if (att->data_type() == DT_FLOAT32) {
+ const int num_components = att->num_components();
+ min_value.resize(num_components);
+ if (!in_buffer->Decode(&min_value[0], sizeof(float) * num_components))
+ return false;
+ float max_value_dif;
+ if (!in_buffer->Decode(&max_value_dif))
+ return false;
+ uint8_t quantization_bits;
+ if (!in_buffer->Decode(&quantization_bits) || quantization_bits > 31)
+ return false;
+ AttributeQuantizationTransform transform;
+ transform.SetParameters(quantization_bits, min_value.data(),
+ num_components, max_value_dif);
+ const int num_transforms =
+ static_cast<int>(attribute_quantization_transforms_.size());
+ if (!transform.TransferToAttribute(
+ quantized_portable_attributes_[num_transforms].get()))
+ return false;
+ attribute_quantization_transforms_.push_back(transform);
+ }
+ }
+
+ // Decode transform data for signed integer attributes.
+ for (int i = 0; i < min_signed_values_.size(); ++i) {
+ int32_t val;
+ DecodeVarint(&val, in_buffer);
+ min_signed_values_[i] = val;
+ }
+ return true;
+ }
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ // Handle old bitstream
+ // Figure out the total dimensionality of the point cloud
+ const uint32_t attribute_count = GetNumAttributes();
+ uint32_t total_dimensionality = 0; // position is a required dimension
+ std::vector<AttributeTuple> atts(attribute_count);
+ for (auto attribute_index = 0;
+ static_cast<uint32_t>(attribute_index) < attribute_count;
+ attribute_index += 1) // increment the dimensionality as needed...
+ {
+ const int att_id = GetAttributeId(attribute_index);
+ PointAttribute *const att = GetDecoder()->point_cloud()->attribute(att_id);
+ const DataType data_type = att->data_type();
+ const uint32_t data_size = (std::max)(0, DataTypeLength(data_type));
+ const uint32_t num_components = att->num_components();
+ atts[attribute_index] = std::make_tuple(
+ att, total_dimensionality, data_type, data_size, num_components);
+ // everything is treated as 32bit in the encoder.
+ total_dimensionality += num_components;
+ }
+
+ const int att_id = GetAttributeId(0);
+ PointAttribute *const att = GetDecoder()->point_cloud()->attribute(att_id);
+ att->SetIdentityMapping();
+ // Decode method
+ uint8_t method;
+ if (!in_buffer->Decode(&method))
+ return false;
+ if (method == KdTreeAttributesEncodingMethod::kKdTreeQuantizationEncoding) {
+ uint8_t compression_level = 0;
+ if (!in_buffer->Decode(&compression_level))
+ return false;
+ uint32_t num_points = 0;
+ if (!in_buffer->Decode(&num_points))
+ return false;
+ att->Reset(num_points);
+ FloatPointsTreeDecoder decoder;
+ PointAttributeVectorOutputIterator<float> out_it(atts);
+ if (!decoder.DecodePointCloud(in_buffer, out_it))
+ return false;
+ } else if (method == KdTreeAttributesEncodingMethod::kKdTreeIntegerEncoding) {
+ uint8_t compression_level = 0;
+ if (!in_buffer->Decode(&compression_level))
+ return false;
+ if (6 < compression_level) {
+ LOGE("KdTreeAttributesDecoder: compression level %i not supported.\n",
+ compression_level);
+ return false;
+ }
+
+ uint32_t num_points;
+ if (!in_buffer->Decode(&num_points))
+ return false;
+
+ for (auto attribute_index = 0;
+ static_cast<uint32_t>(attribute_index) < attribute_count;
+ attribute_index += 1) {
+ const int att_id = GetAttributeId(attribute_index);
+ PointAttribute *const attr =
+ GetDecoder()->point_cloud()->attribute(att_id);
+ attr->Reset(num_points);
+ attr->SetIdentityMapping();
+ };
+
+ PointAttributeVectorOutputIterator<uint32_t> out_it(atts);
+
+ switch (compression_level) {
+ case 0: {
+ DynamicIntegerPointsKdTreeDecoder<0> decoder(total_dimensionality);
+ if (!decoder.DecodePoints(in_buffer, out_it))
+ return false;
+ break;
+ }
+ case 1: {
+ DynamicIntegerPointsKdTreeDecoder<1> decoder(total_dimensionality);
+ if (!decoder.DecodePoints(in_buffer, out_it))
+ return false;
+ break;
+ }
+ case 2: {
+ DynamicIntegerPointsKdTreeDecoder<2> decoder(total_dimensionality);
+ if (!decoder.DecodePoints(in_buffer, out_it))
+ return false;
+ break;
+ }
+ case 3: {
+ DynamicIntegerPointsKdTreeDecoder<3> decoder(total_dimensionality);
+ if (!decoder.DecodePoints(in_buffer, out_it))
+ return false;
+ break;
+ }
+ case 4: {
+ DynamicIntegerPointsKdTreeDecoder<4> decoder(total_dimensionality);
+ if (!decoder.DecodePoints(in_buffer, out_it))
+ return false;
+ break;
+ }
+ case 5: {
+ DynamicIntegerPointsKdTreeDecoder<5> decoder(total_dimensionality);
+ if (!decoder.DecodePoints(in_buffer, out_it))
+ return false;
+ break;
+ }
+ case 6: {
+ DynamicIntegerPointsKdTreeDecoder<6> decoder(total_dimensionality);
+ if (!decoder.DecodePoints(in_buffer, out_it))
+ return false;
+ break;
+ }
+ default:
+ return false;
+ }
+ } else {
+ // Invalid method.
+ return false;
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+
+template <typename SignedDataTypeT>
+bool KdTreeAttributesDecoder::TransformAttributeBackToSignedType(
+ PointAttribute *att, int num_processed_signed_components) {
+ typedef typename std::make_unsigned<SignedDataTypeT>::type UnsignedType;
+ std::vector<UnsignedType> unsigned_val(att->num_components());
+ std::vector<SignedDataTypeT> signed_val(att->num_components());
+
+ for (AttributeValueIndex avi(0); avi < static_cast<uint32_t>(att->size());
+ ++avi) {
+ att->GetValue(avi, &unsigned_val[0]);
+ for (int c = 0; c < att->num_components(); ++c) {
+ // Up-cast |unsigned_val| to int32_t to ensure we don't overflow it for
+ // smaller data types.
+ signed_val[c] = static_cast<SignedDataTypeT>(
+ static_cast<int32_t>(unsigned_val[c]) +
+ min_signed_values_[num_processed_signed_components + c]);
+ }
+ att->SetAttributeValue(avi, &signed_val[0]);
+ }
+ return true;
+}
+
+bool KdTreeAttributesDecoder::TransformAttributesToOriginalFormat() {
+ if (quantized_portable_attributes_.empty() && min_signed_values_.empty()) {
+ return true;
+ }
+ int num_processed_quantized_attributes = 0;
+ int num_processed_signed_components = 0;
+ // Dequantize attributes that needed it.
+ for (int i = 0; i < GetNumAttributes(); ++i) {
+ const int att_id = GetAttributeId(i);
+ PointAttribute *const att = GetDecoder()->point_cloud()->attribute(att_id);
+ if (att->data_type() == DT_INT32 || att->data_type() == DT_INT16 ||
+ att->data_type() == DT_INT8) {
+ std::vector<uint32_t> unsigned_val(att->num_components());
+ std::vector<int32_t> signed_val(att->num_components());
+ // Values are stored as unsigned in the attribute, make them signed again.
+ if (att->data_type() == DT_INT32) {
+ if (!TransformAttributeBackToSignedType<int32_t>(
+ att, num_processed_signed_components))
+ return false;
+ } else if (att->data_type() == DT_INT16) {
+ if (!TransformAttributeBackToSignedType<int16_t>(
+ att, num_processed_signed_components))
+ return false;
+ } else if (att->data_type() == DT_INT8) {
+ if (!TransformAttributeBackToSignedType<int8_t>(
+ att, num_processed_signed_components))
+ return false;
+ }
+ num_processed_signed_components += att->num_components();
+ } else if (att->data_type() == DT_FLOAT32) {
+ // TODO(ostava): This code should be probably moved out to attribute
+ // transform and shared with the SequentialQuantizationAttributeDecoder.
+
+ const PointAttribute *const src_att =
+ quantized_portable_attributes_[num_processed_quantized_attributes]
+ .get();
+
+ const AttributeQuantizationTransform &transform =
+ attribute_quantization_transforms_
+ [num_processed_quantized_attributes];
+
+ num_processed_quantized_attributes++;
+
+ if (GetDecoder()->options()->GetAttributeBool(
+ att->attribute_type(), "skip_attribute_transform", false)) {
+ // Attribute transform should not be performed. In this case, we replace
+ // the output geometry attribute with the portable attribute.
+ // TODO(ostava): We can potentially avoid this copy by introducing a new
+ // mechanism that would allow to use the final attributes as portable
+ // attributes for predictors that may need them.
+ att->CopyFrom(*src_att);
+ continue;
+ }
+
+ // Convert all quantized values back to floats.
+ const int32_t max_quantized_value =
+ (1u << static_cast<uint32_t>(transform.quantization_bits())) - 1;
+ const int num_components = att->num_components();
+ const int entry_size = sizeof(float) * num_components;
+ const std::unique_ptr<float[]> att_val(new float[num_components]);
+ int quant_val_id = 0;
+ int out_byte_pos = 0;
+ Dequantizer dequantizer;
+ if (!dequantizer.Init(transform.range(), max_quantized_value))
+ return false;
+ const uint32_t *const portable_attribute_data =
+ reinterpret_cast<const uint32_t *>(
+ src_att->GetAddress(AttributeValueIndex(0)));
+ for (uint32_t i = 0; i < src_att->size(); ++i) {
+ for (int c = 0; c < num_components; ++c) {
+ float value = dequantizer.DequantizeFloat(
+ portable_attribute_data[quant_val_id++]);
+ value = value + transform.min_value(c);
+ att_val[c] = value;
+ }
+ // Store the floating point value into the attribute buffer.
+ att->buffer()->Write(out_byte_pos, att_val.get(), entry_size);
+ out_byte_pos += entry_size;
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_decoder.h
new file mode 100644
index 00000000000..87338d6b0d4
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_decoder.h
@@ -0,0 +1,46 @@
+// 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_COMPRESSION_ATTRIBUTES_KD_TREE_ATTRIBUTES_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_KD_TREE_ATTRIBUTES_DECODER_H_
+
+#include "draco/attributes/attribute_quantization_transform.h"
+#include "draco/compression/attributes/attributes_decoder.h"
+
+namespace draco {
+
+// Decodes attributes encoded with the KdTreeAttributesEncoder.
+class KdTreeAttributesDecoder : public AttributesDecoder {
+ public:
+ KdTreeAttributesDecoder();
+
+ protected:
+ bool DecodePortableAttributes(DecoderBuffer *in_buffer) override;
+ bool DecodeDataNeededByPortableTransforms(DecoderBuffer *in_buffer) override;
+ bool TransformAttributesToOriginalFormat() override;
+
+ private:
+ template <typename SignedDataTypeT>
+ bool TransformAttributeBackToSignedType(PointAttribute *att,
+ int num_processed_signed_components);
+
+ std::vector<AttributeQuantizationTransform>
+ attribute_quantization_transforms_;
+ std::vector<int32_t> min_signed_values_;
+ std::vector<std::unique_ptr<PointAttribute>> quantized_portable_attributes_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_KD_TREE_ATTRIBUTES_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_encoder.cc b/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_encoder.cc
new file mode 100644
index 00000000000..f2a8af23bfc
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_encoder.cc
@@ -0,0 +1,289 @@
+// 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 "draco/compression/attributes/kd_tree_attributes_encoder.h"
+#include "draco/compression/attributes/kd_tree_attributes_shared.h"
+#include "draco/compression/attributes/point_d_vector.h"
+#include "draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.h"
+#include "draco/compression/point_cloud/algorithms/float_points_tree_encoder.h"
+#include "draco/compression/point_cloud/point_cloud_encoder.h"
+#include "draco/core/varint_encoding.h"
+
+namespace draco {
+
+KdTreeAttributesEncoder::KdTreeAttributesEncoder() : num_components_(0) {}
+
+KdTreeAttributesEncoder::KdTreeAttributesEncoder(int att_id)
+ : AttributesEncoder(att_id), num_components_(0) {}
+
+bool KdTreeAttributesEncoder::TransformAttributesToPortableFormat() {
+ // Convert any of the input attributes into a format that can be processed by
+ // the kd tree encoder (quantization of floating attributes for now).
+ const size_t num_points = encoder()->point_cloud()->num_points();
+ int num_components = 0;
+ for (uint32_t i = 0; i < num_attributes(); ++i) {
+ const int att_id = GetAttributeId(i);
+ const PointAttribute *const att =
+ encoder()->point_cloud()->attribute(att_id);
+ num_components += att->num_components();
+ }
+ num_components_ = num_components;
+
+ // Go over all attributes and quantize them if needed.
+ for (uint32_t i = 0; i < num_attributes(); ++i) {
+ const int att_id = GetAttributeId(i);
+ const PointAttribute *const att =
+ encoder()->point_cloud()->attribute(att_id);
+ if (att->data_type() == DT_FLOAT32) {
+ // Quantization path.
+ AttributeQuantizationTransform attribute_quantization_transform;
+ const int quantization_bits = encoder()->options()->GetAttributeInt(
+ att_id, "quantization_bits", -1);
+ if (quantization_bits < 1)
+ return false;
+ if (encoder()->options()->IsAttributeOptionSet(att_id,
+ "quantization_origin") &&
+ encoder()->options()->IsAttributeOptionSet(att_id,
+ "quantization_range")) {
+ // Quantization settings are explicitly specified in the provided
+ // options.
+ std::vector<float> quantization_origin(att->num_components());
+ encoder()->options()->GetAttributeVector(att_id, "quantization_origin",
+ att->num_components(),
+ &quantization_origin[0]);
+ const float range = encoder()->options()->GetAttributeFloat(
+ att_id, "quantization_range", 1.f);
+ attribute_quantization_transform.SetParameters(
+ quantization_bits, quantization_origin.data(),
+ att->num_components(), range);
+ } else {
+ // Compute quantization settings from the attribute values.
+ attribute_quantization_transform.ComputeParameters(*att,
+ quantization_bits);
+ }
+ attribute_quantization_transforms_.push_back(
+ attribute_quantization_transform);
+ // Store the quantized attribute in an array that will be used when we do
+ // the actual encoding of the data.
+ quantized_portable_attributes_.push_back(
+ attribute_quantization_transform.GeneratePortableAttribute(
+ *att, static_cast<int>(num_points)));
+ } else if (att->data_type() == DT_INT32 || att->data_type() == DT_INT16 ||
+ att->data_type() == DT_INT8) {
+ // For signed types, find the minimum value for each component. These
+ // values are going to be used to transform the attribute values to
+ // unsigned integers that can be processed by the core kd tree algorithm.
+ std::vector<int32_t> min_value(att->num_components(),
+ std::numeric_limits<int32_t>::max());
+ std::vector<int32_t> act_value(att->num_components());
+ for (AttributeValueIndex avi(0); avi < static_cast<uint32_t>(att->size());
+ ++avi) {
+ att->ConvertValue<int32_t>(avi, &act_value[0]);
+ for (int c = 0; c < att->num_components(); ++c) {
+ if (min_value[c] > act_value[c])
+ min_value[c] = act_value[c];
+ }
+ }
+ for (int c = 0; c < att->num_components(); ++c) {
+ min_signed_values_.push_back(min_value[c]);
+ }
+ }
+ }
+ return true;
+}
+
+bool KdTreeAttributesEncoder::EncodeDataNeededByPortableTransforms(
+ EncoderBuffer *out_buffer) {
+ // Store quantization settings for all attributes that need it.
+ for (int i = 0; i < attribute_quantization_transforms_.size(); ++i) {
+ attribute_quantization_transforms_[i].EncodeParameters(out_buffer);
+ }
+
+ // Encode data needed for transforming signed integers to unsigned ones.
+ for (int i = 0; i < min_signed_values_.size(); ++i) {
+ EncodeVarint<int32_t>(min_signed_values_[i], out_buffer);
+ }
+ return true;
+}
+
+bool KdTreeAttributesEncoder::EncodePortableAttributes(
+ EncoderBuffer *out_buffer) {
+ // Encode the data using the kd tree encoder algorithm. The data is first
+ // copied to a PointDVector that provides all the API expected by the core
+ // encoding algorithm.
+
+ // We limit the maximum value of compression_level to 6 as we don't currently
+ // have viable algorithms for higher compression levels.
+ uint8_t compression_level =
+ std::min(10 - encoder()->options()->GetSpeed(), 6);
+ DRACO_DCHECK_LE(compression_level, 6);
+
+ if (compression_level == 6 && num_components_ > 15) {
+ // Don't use compression level for CL >= 6. Axis selection is currently
+ // encoded using 4 bits.
+ compression_level = 5;
+ }
+
+ out_buffer->Encode(compression_level);
+
+ // Init PointDVector. The number of dimensions is equal to the total number
+ // of dimensions across all attributes.
+ const int num_points = encoder()->point_cloud()->num_points();
+ PointDVector<uint32_t> point_vector(num_points, num_components_);
+
+ int num_processed_components = 0;
+ int num_processed_quantized_attributes = 0;
+ int num_processed_signed_components = 0;
+ // Copy data to the point vector.
+ for (uint32_t i = 0; i < num_attributes(); ++i) {
+ const int att_id = GetAttributeId(i);
+ const PointAttribute *const att =
+ encoder()->point_cloud()->attribute(att_id);
+ const PointAttribute *source_att = nullptr;
+ if (att->data_type() == DT_UINT32 || att->data_type() == DT_UINT16 ||
+ att->data_type() == DT_UINT8 || att->data_type() == DT_INT32 ||
+ att->data_type() == DT_INT16 || att->data_type() == DT_INT8) {
+ // Use the original attribute.
+ source_att = att;
+ } else if (att->data_type() == DT_FLOAT32) {
+ // Use the portable (quantized) attribute instead.
+ source_att =
+ quantized_portable_attributes_[num_processed_quantized_attributes]
+ .get();
+ num_processed_quantized_attributes++;
+ } else {
+ // Unsupported data type.
+ return false;
+ }
+
+ if (source_att == nullptr)
+ return false;
+
+ // Copy source_att to the vector.
+ if (source_att->data_type() == DT_UINT32) {
+ // If the data type is the same as the one used by the point vector, we
+ // can directly copy individual elements.
+ for (PointIndex pi(0); pi < num_points; ++pi) {
+ const AttributeValueIndex avi = source_att->mapped_index(pi);
+ const uint8_t *const att_value_address = source_att->GetAddress(avi);
+ point_vector.CopyAttribute(source_att->num_components(),
+ num_processed_components, pi.value(),
+ att_value_address);
+ }
+ } else if (source_att->data_type() == DT_INT32 ||
+ source_att->data_type() == DT_INT16 ||
+ source_att->data_type() == DT_INT8) {
+ // Signed values need to be converted to unsigned before they are stored
+ // in the point vector.
+ std::vector<int32_t> signed_point(source_att->num_components());
+ std::vector<uint32_t> unsigned_point(source_att->num_components());
+ for (PointIndex pi(0); pi < num_points; ++pi) {
+ const AttributeValueIndex avi = source_att->mapped_index(pi);
+ source_att->ConvertValue<int32_t>(avi, &signed_point[0]);
+ for (int c = 0; c < source_att->num_components(); ++c) {
+ unsigned_point[c] =
+ signed_point[c] -
+ min_signed_values_[num_processed_signed_components + c];
+ }
+
+ point_vector.CopyAttribute(source_att->num_components(),
+ num_processed_components, pi.value(),
+ &unsigned_point[0]);
+ }
+ num_processed_signed_components += source_att->num_components();
+ } else {
+ // If the data type of the attribute is different, we have to convert the
+ // value before we put it to the point vector.
+ std::vector<uint32_t> point(source_att->num_components());
+ for (PointIndex pi(0); pi < num_points; ++pi) {
+ const AttributeValueIndex avi = source_att->mapped_index(pi);
+ source_att->ConvertValue<uint32_t>(avi, &point[0]);
+ point_vector.CopyAttribute(source_att->num_components(),
+ num_processed_components, pi.value(),
+ point.data());
+ }
+ }
+ num_processed_components += source_att->num_components();
+ }
+
+ // Compute the maximum bit length needed for the kd tree encoding.
+ int num_bits = 0;
+ const uint32_t *data = point_vector[0];
+ for (int i = 0; i < num_points * num_components_; ++i) {
+ if (data[i] > 0) {
+ const int msb = MostSignificantBit(data[i]) + 1;
+ if (msb > num_bits) {
+ num_bits = msb;
+ }
+ }
+ }
+
+ switch (compression_level) {
+ case 6: {
+ DynamicIntegerPointsKdTreeEncoder<6> points_encoder(num_components_);
+ if (!points_encoder.EncodePoints(point_vector.begin(), point_vector.end(),
+ num_bits, out_buffer))
+ return false;
+ break;
+ }
+ case 5: {
+ DynamicIntegerPointsKdTreeEncoder<5> points_encoder(num_components_);
+ if (!points_encoder.EncodePoints(point_vector.begin(), point_vector.end(),
+ num_bits, out_buffer))
+ return false;
+ break;
+ }
+ case 4: {
+ DynamicIntegerPointsKdTreeEncoder<4> points_encoder(num_components_);
+ if (!points_encoder.EncodePoints(point_vector.begin(), point_vector.end(),
+ num_bits, out_buffer))
+ return false;
+ break;
+ }
+ case 3: {
+ DynamicIntegerPointsKdTreeEncoder<3> points_encoder(num_components_);
+ if (!points_encoder.EncodePoints(point_vector.begin(), point_vector.end(),
+ num_bits, out_buffer))
+ return false;
+ break;
+ }
+ case 2: {
+ DynamicIntegerPointsKdTreeEncoder<2> points_encoder(num_components_);
+ if (!points_encoder.EncodePoints(point_vector.begin(), point_vector.end(),
+ num_bits, out_buffer))
+ return false;
+ break;
+ }
+ case 1: {
+ DynamicIntegerPointsKdTreeEncoder<1> points_encoder(num_components_);
+ if (!points_encoder.EncodePoints(point_vector.begin(), point_vector.end(),
+ num_bits, out_buffer))
+ return false;
+ break;
+ }
+ case 0: {
+ DynamicIntegerPointsKdTreeEncoder<0> points_encoder(num_components_);
+ if (!points_encoder.EncodePoints(point_vector.begin(), point_vector.end(),
+ num_bits, out_buffer))
+ return false;
+ break;
+ }
+ // Compression level and/or encoding speed seem wrong.
+ default:
+ return false;
+ }
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_encoder.h
new file mode 100644
index 00000000000..8b4c4e2faab
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_encoder.h
@@ -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.
+//
+#ifndef DRACO_COMPRESSION_ATTRIBUTES_POINT_CLOUD_KD_TREE_ATTRIBUTES_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_POINT_CLOUD_KD_TREE_ATTRIBUTES_ENCODER_H_
+
+#include "draco/attributes/attribute_quantization_transform.h"
+#include "draco/compression/attributes/attributes_encoder.h"
+#include "draco/compression/config/compression_shared.h"
+
+namespace draco {
+
+// Encodes all attributes of a given PointCloud using one of the available
+// Kd-tree compression methods.
+// See compression/point_cloud/point_cloud_kd_tree_encoder.h for more details.
+class KdTreeAttributesEncoder : public AttributesEncoder {
+ public:
+ KdTreeAttributesEncoder();
+ explicit KdTreeAttributesEncoder(int att_id);
+
+ uint8_t GetUniqueId() const override { return KD_TREE_ATTRIBUTE_ENCODER; }
+
+ protected:
+ bool TransformAttributesToPortableFormat() override;
+ bool EncodePortableAttributes(EncoderBuffer *out_buffer) override;
+ bool EncodeDataNeededByPortableTransforms(EncoderBuffer *out_buffer) override;
+
+ private:
+ std::vector<AttributeQuantizationTransform>
+ attribute_quantization_transforms_;
+ // Min signed values are used to transform signed integers into unsigned ones
+ // (by subtracting the min signed value for each component).
+ std::vector<int32_t> min_signed_values_;
+ std::vector<std::unique_ptr<PointAttribute>> quantized_portable_attributes_;
+ int num_components_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_POINT_CLOUD_KD_TREE_ATTRIBUTES_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_shared.h b/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_shared.h
new file mode 100644
index 00000000000..94841a91d99
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/kd_tree_attributes_shared.h
@@ -0,0 +1,28 @@
+// 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_COMPRESSION_ATTRIBUTES_KD_TREE_ATTRIBUTES_SHARED_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_KD_TREE_ATTRIBUTES_SHARED_H_
+
+namespace draco {
+
+// Defines types of kD-tree compression
+enum KdTreeAttributesEncodingMethod {
+ kKdTreeQuantizationEncoding = 0,
+ kKdTreeIntegerEncoding
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_KD_TREE_ATTRIBUTES_SHARED_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/linear_sequencer.h b/extern/draco/dracoenc/src/draco/compression/attributes/linear_sequencer.h
new file mode 100644
index 00000000000..dc4dfffe9d5
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/linear_sequencer.h
@@ -0,0 +1,50 @@
+// 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_COMPRESSION_ATTRIBUTES_LINEAR_SEQUENCER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_LINEAR_SEQUENCER_H_
+
+#include "draco/compression/attributes/points_sequencer.h"
+
+namespace draco {
+
+// A simple sequencer that generates a linear sequence [0, num_points - 1].
+// I.e., the order of the points is preserved for the input data.
+class LinearSequencer : public PointsSequencer {
+ public:
+ explicit LinearSequencer(int32_t num_points) : num_points_(num_points) {}
+
+ bool UpdatePointToAttributeIndexMapping(PointAttribute *attribute) override {
+ attribute->SetIdentityMapping();
+ return true;
+ }
+
+ protected:
+ bool GenerateSequenceInternal() override {
+ if (num_points_ < 0)
+ return false;
+ out_point_ids()->resize(num_points_);
+ for (int i = 0; i < num_points_; ++i) {
+ out_point_ids()->at(i) = PointIndex(i);
+ }
+ return true;
+ }
+
+ private:
+ int32_t num_points_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_LINEAR_SEQUENCER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/mesh_attribute_indices_encoding_data.h b/extern/draco/dracoenc/src/draco/compression/attributes/mesh_attribute_indices_encoding_data.h
new file mode 100644
index 00000000000..9a358e44733
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/mesh_attribute_indices_encoding_data.h
@@ -0,0 +1,58 @@
+// 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_COMPRESSION_ATTRIBUTES_MESH_ATTRIBUTE_INDICES_ENCODING_DATA_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_MESH_ATTRIBUTE_INDICES_ENCODING_DATA_H_
+
+#include <inttypes.h>
+
+#include <vector>
+
+#include "draco/attributes/geometry_indices.h"
+
+namespace draco {
+
+// Data used for encoding and decoding of mesh attributes.
+struct MeshAttributeIndicesEncodingData {
+ MeshAttributeIndicesEncodingData() : num_values(0) {}
+
+ void Init(int num_vertices) {
+ vertex_to_encoded_attribute_value_index_map.resize(num_vertices);
+
+ // We expect to store one value for each vertex.
+ encoded_attribute_value_index_to_corner_map.reserve(num_vertices);
+ }
+
+ // Array for storing the corner ids in the order their associated attribute
+ // entries were encoded/decoded. For every encoded attribute value entry we
+ // store exactly one corner. I.e., this is the mapping between an encoded
+ // attribute entry ids and corner ids. This map is needed for example by
+ // prediction schemes. Note that not all corners are included in this map,
+ // e.g., if multiple corners share the same attribute value, only one of these
+ // corners will be usually included.
+ std::vector<CornerIndex> encoded_attribute_value_index_to_corner_map;
+
+ // Map for storing encoding order of attribute entries for each vertex.
+ // i.e. Mapping between vertices and their corresponding attribute entry ids
+ // that are going to be used by the decoder.
+ // -1 if an attribute entry hasn't been encoded/decoded yet.
+ std::vector<int32_t> vertex_to_encoded_attribute_value_index_map;
+
+ // Total number of encoded/decoded attribute entries.
+ int num_values;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_MESH_ATTRIBUTE_INDICES_ENCODING_DATA_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/normal_compression_utils.h b/extern/draco/dracoenc/src/draco/compression/attributes/normal_compression_utils.h
new file mode 100644
index 00000000000..4e6ff1a2134
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/normal_compression_utils.h
@@ -0,0 +1,335 @@
+// 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.
+//
+// Utilities for converting unit vectors to octahedral coordinates and back.
+// For more details about octahedral coordinates, see for example Cigolle
+// et al.'14 “A Survey of Efficient Representations for Independent Unit
+// Vectors”.
+//
+// In short this is motivated by an octahedron inscribed into a sphere. The
+// direction of the normal vector can be defined by a point on the octahedron.
+// On the right hemisphere (x > 0) this point is projected onto the x = 0 plane,
+// that is, the right side of the octahedron forms a diamond like shape. The
+// left side of the octahedron is also projected onto the x = 0 plane, however,
+// in this case we flap the triangles of the diamond outward. Afterwards we
+// shift the resulting square such that all values are positive.
+//
+// Important values in this file:
+// * q: number of quantization bits
+// * max_quantized_value: the max value representable with q bits (odd)
+// * max_value: max value of the diamond = max_quantized_value - 1 (even)
+// * center_value: center of the diamond after shift
+//
+// Note that the parameter space is somewhat periodic, e.g. (0, 0) ==
+// (max_value, max_value), which is also why the diamond is one smaller than the
+// maximal representable value in order to have an odd range of values.
+
+#ifndef DRACO_COMPRESSION_ATTRIBUTES_NORMAL_COMPRESSION_UTILS_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_NORMAL_COMPRESSION_UTILS_H_
+
+#include <inttypes.h>
+#include <algorithm>
+#include <cmath>
+
+#include "draco/core/macros.h"
+
+namespace draco {
+
+class OctahedronToolBox {
+ public:
+ OctahedronToolBox()
+ : quantization_bits_(-1),
+ max_quantized_value_(-1),
+ max_value_(-1),
+ center_value_(-1) {}
+
+ bool SetQuantizationBits(int32_t q) {
+ if (q < 2 || q > 30)
+ return false;
+ quantization_bits_ = q;
+ max_quantized_value_ = (1 << quantization_bits_) - 1;
+ max_value_ = max_quantized_value_ - 1;
+ center_value_ = max_value_ / 2;
+ return true;
+ }
+ bool IsInitialized() const { return quantization_bits_ != -1; }
+
+ // Convert all edge points in the top left and bottom right quadrants to
+ // their corresponding position in the bottom left and top right quadrants.
+ // Convert all corner edge points to the top right corner.
+ inline void CanonicalizeOctahedralCoords(int32_t s, int32_t t, int32_t *out_s,
+ int32_t *out_t) const {
+ if ((s == 0 && t == 0) || (s == 0 && t == max_value_) ||
+ (s == max_value_ && t == 0)) {
+ s = max_value_;
+ t = max_value_;
+ } else if (s == 0 && t > center_value_) {
+ t = center_value_ - (t - center_value_);
+ } else if (s == max_value_ && t < center_value_) {
+ t = center_value_ + (center_value_ - t);
+ } else if (t == max_value_ && s < center_value_) {
+ s = center_value_ + (center_value_ - s);
+ } else if (t == 0 && s > center_value_) {
+ s = center_value_ - (s - center_value_);
+ }
+
+ *out_s = s;
+ *out_t = t;
+ }
+
+ // Converts an integer vector to octahedral coordinates.
+ // Precondition: |int_vec| abs sum must equal center value.
+ inline void IntegerVectorToQuantizedOctahedralCoords(const int32_t *int_vec,
+ int32_t *out_s,
+ int32_t *out_t) const {
+ DRACO_DCHECK_EQ(
+ std::abs(int_vec[0]) + std::abs(int_vec[1]) + std::abs(int_vec[2]),
+ center_value_);
+ int32_t s, t;
+ if (int_vec[0] >= 0) {
+ // Right hemisphere.
+ s = (int_vec[1] + center_value_);
+ t = (int_vec[2] + center_value_);
+ } else {
+ // Left hemisphere.
+ if (int_vec[1] < 0) {
+ s = std::abs(int_vec[2]);
+ } else {
+ s = (max_value_ - std::abs(int_vec[2]));
+ }
+ if (int_vec[2] < 0) {
+ t = std::abs(int_vec[1]);
+ } else {
+ t = (max_value_ - std::abs(int_vec[1]));
+ }
+ }
+ CanonicalizeOctahedralCoords(s, t, out_s, out_t);
+ }
+
+ template <class T>
+ void FloatVectorToQuantizedOctahedralCoords(const T *vector, int32_t *out_s,
+ int32_t *out_t) const {
+ const double abs_sum = std::abs(static_cast<double>(vector[0])) +
+ std::abs(static_cast<double>(vector[1])) +
+ std::abs(static_cast<double>(vector[2]));
+
+ // Adjust values such that abs sum equals 1.
+ double scaled_vector[3];
+ if (abs_sum > 1e-6) {
+ // Scale needed to project the vector to the surface of an octahedron.
+ const double scale = 1.0 / abs_sum;
+ scaled_vector[0] = vector[0] * scale;
+ scaled_vector[1] = vector[1] * scale;
+ scaled_vector[2] = vector[2] * scale;
+ } else {
+ scaled_vector[0] = 1.0;
+ scaled_vector[1] = 0;
+ scaled_vector[2] = 0;
+ }
+
+ // Scale vector such that the sum equals the center value.
+ int32_t int_vec[3];
+ int_vec[0] =
+ static_cast<int32_t>(floor(scaled_vector[0] * center_value_ + 0.5));
+ int_vec[1] =
+ static_cast<int32_t>(floor(scaled_vector[1] * center_value_ + 0.5));
+ // Make sure the sum is exactly the center value.
+ int_vec[2] = center_value_ - std::abs(int_vec[0]) - std::abs(int_vec[1]);
+ if (int_vec[2] < 0) {
+ // If the sum of first two coordinates is too large, we need to decrease
+ // the length of one of the coordinates.
+ if (int_vec[1] > 0) {
+ int_vec[1] += int_vec[2];
+ } else {
+ int_vec[1] -= int_vec[2];
+ }
+ int_vec[2] = 0;
+ }
+ // Take care of the sign.
+ if (scaled_vector[2] < 0)
+ int_vec[2] *= -1;
+
+ IntegerVectorToQuantizedOctahedralCoords(int_vec, out_s, out_t);
+ }
+
+ // Normalize |vec| such that its abs sum is equal to the center value;
+ template <class T>
+ void CanonicalizeIntegerVector(T *vec) const {
+ static_assert(std::is_integral<T>::value, "T must be an integral type.");
+ static_assert(std::is_signed<T>::value, "T must be a signed type.");
+ const int64_t abs_sum = static_cast<int64_t>(std::abs(vec[0])) +
+ static_cast<int64_t>(std::abs(vec[1])) +
+ static_cast<int64_t>(std::abs(vec[2]));
+
+ if (abs_sum == 0) {
+ vec[0] = center_value_; // vec[1] == v[2] == 0
+ } else {
+ vec[0] =
+ (static_cast<int64_t>(vec[0]) * static_cast<int64_t>(center_value_)) /
+ abs_sum;
+ vec[1] =
+ (static_cast<int64_t>(vec[1]) * static_cast<int64_t>(center_value_)) /
+ abs_sum;
+ if (vec[2] >= 0) {
+ vec[2] = center_value_ - std::abs(vec[0]) - std::abs(vec[1]);
+ } else {
+ vec[2] = -(center_value_ - std::abs(vec[0]) - std::abs(vec[1]));
+ }
+ }
+ }
+
+ template <typename T>
+ void OctaherdalCoordsToUnitVector(T in_s, T in_t, T *out_vector) const {
+ DRACO_DCHECK_GE(in_s, 0);
+ DRACO_DCHECK_GE(in_t, 0);
+ DRACO_DCHECK_LE(in_s, 1);
+ DRACO_DCHECK_LE(in_t, 1);
+ T s = in_s;
+ T t = in_t;
+ T spt = s + t;
+ T smt = s - t;
+ T x_sign = 1.0;
+ if (spt >= 0.5 && spt <= 1.5 && smt >= -0.5 && smt <= 0.5) {
+ // Right hemisphere. Don't do anything.
+ } else {
+ // Left hemisphere.
+ x_sign = -1.0;
+ if (spt <= 0.5) {
+ s = 0.5 - in_t;
+ t = 0.5 - in_s;
+ } else if (spt >= 1.5) {
+ s = 1.5 - in_t;
+ t = 1.5 - in_s;
+ } else if (smt <= -0.5) {
+ s = in_t - 0.5;
+ t = in_s + 0.5;
+ } else {
+ s = in_t + 0.5;
+ t = in_s - 0.5;
+ }
+ spt = s + t;
+ smt = s - t;
+ }
+ const T y = 2.0 * s - 1.0;
+ const T z = 2.0 * t - 1.0;
+ const T x = std::min(std::min(2.0 * spt - 1.0, 3.0 - 2.0 * spt),
+ std::min(2.0 * smt + 1.0, 1.0 - 2.0 * smt)) *
+ x_sign;
+ // Normalize the computed vector.
+ const T normSquared = x * x + y * y + z * z;
+ if (normSquared < 1e-6) {
+ out_vector[0] = 0;
+ out_vector[1] = 0;
+ out_vector[2] = 0;
+ } else {
+ const T d = 1.0 / std::sqrt(normSquared);
+ out_vector[0] = x * d;
+ out_vector[1] = y * d;
+ out_vector[2] = z * d;
+ }
+ }
+
+ template <typename T>
+ void QuantizedOctaherdalCoordsToUnitVector(int32_t in_s, int32_t in_t,
+ T *out_vector) const {
+ T scale = 1.0 / static_cast<T>(max_value_);
+ OctaherdalCoordsToUnitVector(in_s * scale, in_t * scale, out_vector);
+ }
+
+ // |s| and |t| are expected to be signed values.
+ inline bool IsInDiamond(const int32_t &s, const int32_t &t) const {
+ // Expect center already at origin.
+ DRACO_DCHECK_LE(s, center_value_);
+ DRACO_DCHECK_LE(t, center_value_);
+ DRACO_DCHECK_GE(s, -center_value_);
+ DRACO_DCHECK_GE(t, -center_value_);
+ return std::abs(s) + std::abs(t) <= center_value_;
+ }
+
+ void InvertDiamond(int32_t *s, int32_t *t) const {
+ // Expect center already at origin.
+ DRACO_DCHECK_LE(*s, center_value_);
+ DRACO_DCHECK_LE(*t, center_value_);
+ DRACO_DCHECK_GE(*s, -center_value_);
+ DRACO_DCHECK_GE(*t, -center_value_);
+ int32_t sign_s = 0;
+ int32_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 int32_t corner_point_s = sign_s * center_value_;
+ const int32_t corner_point_t = sign_t * center_value_;
+ *s = 2 * *s - corner_point_s;
+ *t = 2 * *t - corner_point_t;
+ if (sign_s * sign_t >= 0) {
+ int32_t temp = *s;
+ *s = -*t;
+ *t = -temp;
+ } else {
+ std::swap(*s, *t);
+ }
+ *s = (*s + corner_point_s) / 2;
+ *t = (*t + corner_point_t) / 2;
+ }
+
+ void InvertDirection(int32_t *s, int32_t *t) const {
+ // Expect center already at origin.
+ DRACO_DCHECK_LE(*s, center_value_);
+ DRACO_DCHECK_LE(*t, center_value_);
+ DRACO_DCHECK_GE(*s, -center_value_);
+ DRACO_DCHECK_GE(*t, -center_value_);
+ *s *= -1;
+ *t *= -1;
+ this->InvertDiamond(s, t);
+ }
+
+ // For correction values.
+ int32_t ModMax(int32_t x) const {
+ if (x > this->center_value())
+ return x - this->max_quantized_value();
+ if (x < -this->center_value())
+ return x + this->max_quantized_value();
+ return x;
+ }
+
+ // For correction values.
+ int32_t MakePositive(int32_t x) const {
+ DRACO_DCHECK_LE(x, this->center_value() * 2);
+ if (x < 0)
+ return x + this->max_quantized_value();
+ return x;
+ }
+
+ int32_t quantization_bits() const { return quantization_bits_; }
+ int32_t max_quantized_value() const { return max_quantized_value_; }
+ int32_t max_value() const { return max_value_; }
+ int32_t center_value() const { return center_value_; }
+
+ private:
+ int32_t quantization_bits_;
+ int32_t max_quantized_value_;
+ int32_t max_value_;
+ int32_t center_value_;
+};
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_NORMAL_COMPRESSION_UTILS_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/point_d_vector.h b/extern/draco/dracoenc/src/draco/compression/attributes/point_d_vector.h
new file mode 100644
index 00000000000..4148770fe77
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/point_d_vector.h
@@ -0,0 +1,275 @@
+// Copyright 2018 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_COMPRESSION_ATTRIBUTES_POINT_D_VECTOR_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_POINT_D_VECTOR_H_
+
+#include <cstring>
+#include <memory>
+#include <vector>
+#include "draco/core/macros.h"
+
+namespace draco {
+
+// The main class of this file is PointDVector providing an interface similar to
+// std::vector<PointD> for arbitrary number of dimensions (without a template
+// argument). PointDVectorIterator is a random access iterator, which allows for
+// compatibility with existing algorithms. PseudoPointD provides for a view on
+// the individual items in a contiguous block of memory, which is compatible
+// with the swap function and is returned by a dereference of
+// PointDVectorIterator. Swap functions provide for compatibility/specialization
+// that allows these classes to work with currently utilized STL functions.
+
+// This class allows for swap functionality from the RandomIterator
+// It seems problematic to bring this inside PointDVector due to templating.
+template <typename internal_t>
+class PseudoPointD {
+ public:
+ PseudoPointD(internal_t *mem, internal_t dimension)
+ : mem_(mem), dimension_(dimension) {}
+
+ // Specifically copies referenced memory
+ void swap(PseudoPointD &other) noexcept {
+ for (auto dim = 0; dim < dimension_; dim += 1)
+ std::swap(mem_[dim], other.mem_[dim]);
+ }
+
+ PseudoPointD(const PseudoPointD &other)
+ : mem_(other.mem_), dimension_(other.dimension_) {}
+
+ const internal_t &operator[](const size_t &n) const {
+ DRACO_DCHECK_LT(n, dimension_);
+ return mem_[n];
+ }
+ internal_t &operator[](const size_t &n) {
+ DRACO_DCHECK_LT(n, dimension_);
+ return mem_[n];
+ }
+
+ bool operator==(const PseudoPointD &other) const {
+ for (auto dim = 0; dim < dimension_; dim += 1)
+ if (mem_[dim] != other.mem_[dim])
+ return false;
+ return true;
+ }
+ bool operator!=(const PseudoPointD &other) const {
+ return !this->operator==(other);
+ }
+
+ private:
+ internal_t *const mem_;
+ const internal_t dimension_;
+};
+
+// It seems problematic to bring this inside PointDVector due to templating.
+template <typename internal_t>
+void swap(draco::PseudoPointD<internal_t> &&a,
+ draco::PseudoPointD<internal_t> &&b) noexcept {
+ a.swap(b);
+};
+template <typename internal_t>
+void swap(draco::PseudoPointD<internal_t> &a,
+ draco::PseudoPointD<internal_t> &b) noexcept {
+ a.swap(b);
+};
+
+template <typename internal_t>
+class PointDVector {
+ public:
+ PointDVector(const uint32_t n_items, const uint32_t dimensionality)
+ : n_items_(n_items),
+ dimensionality_(dimensionality),
+ item_size_bytes_(dimensionality * sizeof(internal_t)),
+ data_(n_items * dimensionality),
+ data0_(data_.data()) {}
+ // random access iterator
+ class PointDVectorIterator
+ : public std::iterator<std::random_access_iterator_tag, size_t, size_t> {
+ friend class PointDVector;
+
+ public:
+ // std::iter_swap is called inside of std::partition and needs this
+ // specialized support
+ PseudoPointD<internal_t> operator*() const {
+ return PseudoPointD<internal_t>(vec_->data0_ + item_ * dimensionality_,
+ dimensionality_);
+ }
+ const PointDVectorIterator &operator++() {
+ item_ += 1;
+ return *this;
+ }
+ const PointDVectorIterator &operator--() {
+ item_ -= 1;
+ return *this;
+ }
+ PointDVectorIterator operator++(int32_t) {
+ PointDVectorIterator copy(*this);
+ item_ += 1;
+ return copy;
+ }
+ PointDVectorIterator operator--(int32_t) {
+ PointDVectorIterator copy(*this);
+ item_ -= 1;
+ return copy;
+ }
+ PointDVectorIterator &operator=(const PointDVectorIterator &other) {
+ this->item_ = other.item_;
+ return *this;
+ }
+
+ bool operator==(const PointDVectorIterator &ref) const {
+ return item_ == ref.item_;
+ }
+ bool operator!=(const PointDVectorIterator &ref) const {
+ return item_ != ref.item_;
+ }
+ bool operator<(const PointDVectorIterator &ref) const {
+ return item_ < ref.item_;
+ }
+ bool operator>(const PointDVectorIterator &ref) const {
+ return item_ > ref.item_;
+ }
+ bool operator<=(const PointDVectorIterator &ref) const {
+ return item_ <= ref.item_;
+ }
+ bool operator>=(const PointDVectorIterator &ref) const {
+ return item_ >= ref.item_;
+ }
+
+ PointDVectorIterator operator+(const int32_t &add) const {
+ PointDVectorIterator copy(vec_, item_ + add);
+ return copy;
+ }
+ PointDVectorIterator &operator+=(const int32_t &add) {
+ item_ += add;
+ return *this;
+ }
+ PointDVectorIterator operator-(const int32_t &sub) const {
+ PointDVectorIterator copy(vec_, item_ - sub);
+ return copy;
+ }
+ size_t operator-(const PointDVectorIterator &sub) const {
+ return (item_ - sub.item_);
+ }
+
+ PointDVectorIterator &operator-=(const int32_t &sub) {
+ item_ -= sub;
+ return *this;
+ }
+
+ internal_t *operator[](const size_t &n) const {
+ return vec_->data0_ + (item_ + n) * dimensionality_;
+ }
+
+ protected:
+ explicit PointDVectorIterator(PointDVector *vec, size_t start_item)
+ : item_(start_item), vec_(vec), dimensionality_(vec->dimensionality_) {}
+
+ private:
+ size_t item_; // this counts the item that should be referenced.
+ PointDVector *const vec_; // the thing that we're iterating on
+ const uint32_t dimensionality_; // local copy from vec_
+ };
+
+ PointDVectorIterator begin() { return PointDVectorIterator(this, 0); }
+ PointDVectorIterator end() { return PointDVectorIterator(this, n_items_); }
+
+ // operator[] allows for unprotected user-side usage of operator[] on the
+ // return value AS IF it were a natively indexable type like Point3*
+ internal_t *operator[](const uint32_t index) {
+ DRACO_DCHECK_LT(index, n_items_);
+ return data0_ + index * dimensionality_;
+ }
+ const internal_t *operator[](const uint32_t index) const {
+ DRACO_DCHECK_LT(index, n_items_);
+ return data0_ + index * dimensionality_;
+ }
+
+ uint32_t size() const { return n_items_; }
+ size_t GetBufferSize() const { return data_.size(); }
+
+ // copy a single contiguous 'item' from one PointDVector into this one.
+ void CopyItem(const PointDVector &source, const internal_t source_index,
+ const internal_t destination_index) {
+ DRACO_DCHECK(&source != this ||
+ (&source == this && source_index != destination_index));
+ DRACO_DCHECK_LT(destination_index, n_items_);
+ DRACO_DCHECK_LT(source_index, source.n_items_);
+
+ // DRACO_DCHECK_EQ(source.n_items_, n_items_); // not technically necessary
+ DRACO_DCHECK_EQ(source.dimensionality_, dimensionality_);
+
+ const internal_t *ref = source[source_index];
+ internal_t *const dest = this->operator[](destination_index);
+ std::memcpy(dest, ref, item_size_bytes_);
+ }
+
+ // Copy data directly off of an attribute buffer interleaved into internal
+ // memory.
+ void CopyAttribute(
+ // The dimensionality of the attribute being integrated
+ const internal_t attribute_dimensionality,
+ // The offset in dimensions to insert this attribute.
+ const internal_t offset_dimensionality, const internal_t index,
+ // The direct pointer to the data
+ const void *const attribute_item_data) {
+ // chunk copy
+ const size_t copy_size = sizeof(internal_t) * attribute_dimensionality;
+
+ // a multiply and add can be optimized away with an iterator
+ std::memcpy(data0_ + index * dimensionality_ + offset_dimensionality,
+ attribute_item_data, copy_size);
+ }
+ // Copy data off of a contiguous buffer interleaved into internal memory
+ void CopyAttribute(
+ // The dimensionality of the attribute being integrated
+ const internal_t attribute_dimensionality,
+ // The offset in dimensions to insert this attribute.
+ const internal_t offset_dimensionality,
+ const internal_t *const attribute_mem) {
+ DRACO_DCHECK_LT(offset_dimensionality,
+ dimensionality_ - attribute_dimensionality);
+ // degenerate case block copy the whole buffer.
+ if (dimensionality_ == attribute_dimensionality) {
+ DRACO_DCHECK_EQ(offset_dimensionality, 0);
+ const size_t copy_size =
+ sizeof(internal_t) * attribute_dimensionality * n_items_;
+ std::memcpy(data0_, attribute_mem, copy_size);
+ } else { // chunk copy
+ const size_t copy_size = sizeof(internal_t) * attribute_dimensionality;
+ internal_t *internal_data;
+ const internal_t *attribute_data;
+ internal_t item;
+ for (internal_data = data0_ + offset_dimensionality,
+ attribute_data = attribute_mem, item = 0;
+ item < n_items_; internal_data += dimensionality_,
+ attribute_data += attribute_dimensionality, item += 1) {
+ std::memcpy(internal_data, attribute_data, copy_size);
+ }
+ }
+ }
+
+ private:
+ // internal parameters.
+ const uint32_t n_items_;
+ const uint32_t dimensionality_; // The dimension of the points in the buffer
+ const uint32_t item_size_bytes_;
+ std::vector<internal_t> data_; // contiguously stored data. Never resized.
+ internal_t *const data0_; // raw pointer to base data.
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_POINT_D_VECTOR_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/point_d_vector_test.cc b/extern/draco/dracoenc/src/draco/compression/attributes/point_d_vector_test.cc
new file mode 100644
index 00000000000..bff10392c3d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/point_d_vector_test.cc
@@ -0,0 +1,359 @@
+// Copyright 2018 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/compression/attributes/point_d_vector.h"
+#include "draco/compression/point_cloud/algorithms/point_cloud_types.h"
+#include "draco/core/draco_test_base.h"
+
+namespace draco {
+
+class PointDVectorTest : public ::testing::Test {
+ protected:
+ template <typename PT>
+ void TestIntegrity() {}
+ template <typename PT>
+ void TestSize() {
+ for (uint32_t n_items = 0; n_items <= 10; ++n_items) {
+ for (uint32_t dimensionality = 1; dimensionality <= 10;
+ ++dimensionality) {
+ draco::PointDVector<PT> var(n_items, dimensionality);
+ ASSERT_EQ(n_items, var.size());
+ ASSERT_EQ(n_items * dimensionality, var.GetBufferSize());
+ }
+ }
+ }
+ template <typename PT>
+ void TestContentsContiguous() {
+ for (uint32_t n_items = 1; n_items <= 1000; n_items *= 10) {
+ for (uint32_t dimensionality = 1; dimensionality < 10;
+ dimensionality += 2) {
+ for (uint32_t att_dimensionality = 1;
+ att_dimensionality <= dimensionality; att_dimensionality += 2) {
+ for (uint32_t offset_dimensionality = 0;
+ offset_dimensionality < dimensionality - att_dimensionality;
+ ++offset_dimensionality) {
+ PointDVector<PT> var(n_items, dimensionality);
+
+ std::vector<PT> att(n_items * att_dimensionality);
+ for (PT val = 0; val < n_items; val += 1) {
+ for (PT att_dim = 0; att_dim < att_dimensionality; att_dim += 1) {
+ att[val * att_dimensionality + att_dim] = val;
+ }
+ }
+ const PT *const attribute_data = att.data();
+
+ var.CopyAttribute(att_dimensionality, offset_dimensionality,
+ attribute_data);
+
+ for (PT val = 0; val < n_items; val += 1) {
+ for (PT att_dim = 0; att_dim < att_dimensionality; att_dim += 1) {
+ ASSERT_EQ(var[val][offset_dimensionality + att_dim], val);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ template <typename PT>
+ void TestContentsDiscrete() {
+ for (uint32_t n_items = 1; n_items <= 1000; n_items *= 10) {
+ for (uint32_t dimensionality = 1; dimensionality < 10;
+ dimensionality += 2) {
+ for (uint32_t att_dimensionality = 1;
+ att_dimensionality <= dimensionality; att_dimensionality += 2) {
+ for (uint32_t offset_dimensionality = 0;
+ offset_dimensionality < dimensionality - att_dimensionality;
+ ++offset_dimensionality) {
+ PointDVector<PT> var(n_items, dimensionality);
+
+ std::vector<PT> att(n_items * att_dimensionality);
+ for (PT val = 0; val < n_items; val += 1) {
+ for (PT att_dim = 0; att_dim < att_dimensionality; att_dim += 1) {
+ att[val * att_dimensionality + att_dim] = val;
+ }
+ }
+ const PT *const attribute_data = att.data();
+
+ for (PT item = 0; item < n_items; item += 1) {
+ var.CopyAttribute(att_dimensionality, offset_dimensionality, item,
+ attribute_data + item * att_dimensionality);
+ }
+
+ for (PT val = 0; val < n_items; val += 1) {
+ for (PT att_dim = 0; att_dim < att_dimensionality; att_dim += 1) {
+ ASSERT_EQ(var[val][offset_dimensionality + att_dim], val);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ template <typename PT>
+ void TestContentsCopy() {
+ for (uint32_t n_items = 1; n_items <= 1000; n_items *= 10) {
+ for (uint32_t dimensionality = 1; dimensionality < 10;
+ dimensionality += 2) {
+ for (uint32_t att_dimensionality = 1;
+ att_dimensionality <= dimensionality; att_dimensionality += 2) {
+ for (uint32_t offset_dimensionality = 0;
+ offset_dimensionality < dimensionality - att_dimensionality;
+ ++offset_dimensionality) {
+ PointDVector<PT> var(n_items, dimensionality);
+ PointDVector<PT> dest(n_items, dimensionality);
+
+ std::vector<PT> att(n_items * att_dimensionality);
+ for (PT val = 0; val < n_items; val += 1) {
+ for (PT att_dim = 0; att_dim < att_dimensionality; att_dim += 1) {
+ att[val * att_dimensionality + att_dim] = val;
+ }
+ }
+ const PT *const attribute_data = att.data();
+
+ var.CopyAttribute(att_dimensionality, offset_dimensionality,
+ attribute_data);
+
+ for (PT item = 0; item < n_items; item += 1) {
+ dest.CopyItem(var, item, item);
+ }
+
+ for (PT val = 0; val < n_items; val += 1) {
+ for (PT att_dim = 0; att_dim < att_dimensionality; att_dim += 1) {
+ ASSERT_EQ(var[val][offset_dimensionality + att_dim], val);
+ ASSERT_EQ(dest[val][offset_dimensionality + att_dim], val);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ template <typename PT>
+ void TestIterator() {
+ for (uint32_t n_items = 1; n_items <= 1000; n_items *= 10) {
+ for (uint32_t dimensionality = 1; dimensionality < 10;
+ dimensionality += 2) {
+ for (uint32_t att_dimensionality = 1;
+ att_dimensionality <= dimensionality; att_dimensionality += 2) {
+ for (uint32_t offset_dimensionality = 0;
+ offset_dimensionality < dimensionality - att_dimensionality;
+ ++offset_dimensionality) {
+ PointDVector<PT> var(n_items, dimensionality);
+ PointDVector<PT> dest(n_items, dimensionality);
+
+ std::vector<PT> att(n_items * att_dimensionality);
+ for (PT val = 0; val < n_items; val += 1) {
+ for (PT att_dim = 0; att_dim < att_dimensionality; att_dim += 1) {
+ att[val * att_dimensionality + att_dim] = val;
+ }
+ }
+ const PT *const attribute_data = att.data();
+
+ var.CopyAttribute(att_dimensionality, offset_dimensionality,
+ attribute_data);
+
+ for (PT item = 0; item < n_items; item += 1) {
+ dest.CopyItem(var, item, item);
+ }
+
+ auto V0 = var.begin();
+ auto VE = var.end();
+ auto D0 = dest.begin();
+ auto DE = dest.end();
+
+ while (V0 != VE && D0 != DE) {
+ ASSERT_EQ(*D0, *V0); // compare PseudoPointD
+ // verify elemental values
+ for (auto index = 0; index < dimensionality; index += 1) {
+ ASSERT_EQ((*D0)[index], (*V0)[index]);
+ }
+ ++V0;
+ ++D0;
+ }
+
+ for (PT val = 0; val < n_items; val += 1) {
+ for (PT att_dim = 0; att_dim < att_dimensionality; att_dim += 1) {
+ ASSERT_EQ(var[val][offset_dimensionality + att_dim], val);
+ ASSERT_EQ(dest[val][offset_dimensionality + att_dim], val);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ template <typename PT>
+ void TestPoint3Iterator() {
+ for (uint32_t n_items = 1; n_items <= 1000; n_items *= 10) {
+ const uint32_t dimensionality = 3;
+ // for (uint32_t dimensionality = 1; dimensionality < 10;
+ // dimensionality += 2) {
+ const uint32_t att_dimensionality = 3;
+ // for (uint32_t att_dimensionality = 1;
+ // att_dimensionality <= dimensionality; att_dimensionality += 2) {
+ for (uint32_t offset_dimensionality = 0;
+ offset_dimensionality < dimensionality - att_dimensionality;
+ ++offset_dimensionality) {
+ PointDVector<PT> var(n_items, dimensionality);
+ PointDVector<PT> dest(n_items, dimensionality);
+
+ std::vector<PT> att(n_items * att_dimensionality);
+ std::vector<draco::Point3ui> att3(n_items);
+ for (PT val = 0; val < n_items; val += 1) {
+ att3[val][0] = val;
+ att3[val][1] = val;
+ att3[val][2] = val;
+ for (PT att_dim = 0; att_dim < att_dimensionality; att_dim += 1) {
+ att[val * att_dimensionality + att_dim] = val;
+ }
+ }
+ const PT *const attribute_data = att.data();
+
+ var.CopyAttribute(att_dimensionality, offset_dimensionality,
+ attribute_data);
+
+ for (PT item = 0; item < n_items; item += 1) {
+ dest.CopyItem(var, item, item);
+ }
+
+ auto aV0 = att3.begin();
+ auto aVE = att3.end();
+ auto V0 = var.begin();
+ auto VE = var.end();
+ auto D0 = dest.begin();
+ auto DE = dest.end();
+
+ while (aV0 != aVE && V0 != VE && D0 != DE) {
+ ASSERT_EQ(*D0, *V0); // compare PseudoPointD
+ // verify elemental values
+ for (auto index = 0; index < dimensionality; index += 1) {
+ ASSERT_EQ((*D0)[index], (*V0)[index]);
+ ASSERT_EQ((*D0)[index], (*aV0)[index]);
+ ASSERT_EQ((*aV0)[index], (*V0)[index]);
+ }
+ ++aV0;
+ ++V0;
+ ++D0;
+ }
+
+ for (PT val = 0; val < n_items; val += 1) {
+ for (PT att_dim = 0; att_dim < att_dimensionality; att_dim += 1) {
+ ASSERT_EQ(var[val][offset_dimensionality + att_dim], val);
+ ASSERT_EQ(dest[val][offset_dimensionality + att_dim], val);
+ }
+ }
+ }
+ }
+ }
+
+ void TestPseudoPointDSwap() {
+ draco::Point3ui val = {0, 1, 2};
+ draco::Point3ui dest = {10, 11, 12};
+ draco::PseudoPointD<uint32_t> val_src1(&val[0], 3);
+ draco::PseudoPointD<uint32_t> dest_src1(&dest[0], 3);
+
+ ASSERT_EQ(val_src1[0], 0);
+ ASSERT_EQ(val_src1[1], 1);
+ ASSERT_EQ(val_src1[2], 2);
+ ASSERT_EQ(dest_src1[0], 10);
+ ASSERT_EQ(dest_src1[1], 11);
+ ASSERT_EQ(dest_src1[2], 12);
+
+ ASSERT_NE(val_src1, dest_src1);
+
+ swap(val_src1, dest_src1);
+
+ ASSERT_EQ(dest_src1[0], 0);
+ ASSERT_EQ(dest_src1[1], 1);
+ ASSERT_EQ(dest_src1[2], 2);
+ ASSERT_EQ(val_src1[0], 10);
+ ASSERT_EQ(val_src1[1], 11);
+ ASSERT_EQ(val_src1[2], 12);
+
+ ASSERT_NE(val_src1, dest_src1);
+ }
+ void TestPseudoPointDEquality() {
+ draco::Point3ui val = {0, 1, 2};
+ draco::Point3ui dest = {0, 1, 2};
+ draco::PseudoPointD<uint32_t> val_src1(&val[0], 3);
+ draco::PseudoPointD<uint32_t> val_src2(&val[0], 3);
+ draco::PseudoPointD<uint32_t> dest_src1(&dest[0], 3);
+ draco::PseudoPointD<uint32_t> dest_src2(&dest[0], 3);
+
+ ASSERT_EQ(val_src1, val_src1);
+ ASSERT_EQ(val_src1, val_src2);
+ ASSERT_EQ(dest_src1, val_src1);
+ ASSERT_EQ(dest_src1, val_src2);
+ ASSERT_EQ(val_src2, val_src1);
+ ASSERT_EQ(val_src2, val_src2);
+ ASSERT_EQ(dest_src2, val_src1);
+ ASSERT_EQ(dest_src2, val_src2);
+
+ for (auto i = 0; i < 3; i++) {
+ ASSERT_EQ(val_src1[i], val_src1[i]);
+ ASSERT_EQ(val_src1[i], val_src2[i]);
+ ASSERT_EQ(dest_src1[i], val_src1[i]);
+ ASSERT_EQ(dest_src1[i], val_src2[i]);
+ ASSERT_EQ(val_src2[i], val_src1[i]);
+ ASSERT_EQ(val_src2[i], val_src2[i]);
+ ASSERT_EQ(dest_src2[i], val_src1[i]);
+ ASSERT_EQ(dest_src2[i], val_src2[i]);
+ }
+ }
+ void TestPseudoPointDInequality() {
+ draco::Point3ui val = {0, 1, 2};
+ draco::Point3ui dest = {1, 2, 3};
+ draco::PseudoPointD<uint32_t> val_src1(&val[0], 3);
+ draco::PseudoPointD<uint32_t> val_src2(&val[0], 3);
+ draco::PseudoPointD<uint32_t> dest_src1(&dest[0], 3);
+ draco::PseudoPointD<uint32_t> dest_src2(&dest[0], 3);
+
+ ASSERT_EQ(val_src1, val_src1);
+ ASSERT_EQ(val_src1, val_src2);
+ ASSERT_NE(dest_src1, val_src1);
+ ASSERT_NE(dest_src1, val_src2);
+ ASSERT_EQ(val_src2, val_src1);
+ ASSERT_EQ(val_src2, val_src2);
+ ASSERT_NE(dest_src2, val_src1);
+ ASSERT_NE(dest_src2, val_src2);
+
+ for (auto i = 0; i < 3; i++) {
+ ASSERT_EQ(val_src1[i], val_src1[i]);
+ ASSERT_EQ(val_src1[i], val_src2[i]);
+ ASSERT_NE(dest_src1[i], val_src1[i]);
+ ASSERT_NE(dest_src1[i], val_src2[i]);
+ ASSERT_EQ(val_src2[i], val_src1[i]);
+ ASSERT_EQ(val_src2[i], val_src2[i]);
+ ASSERT_NE(dest_src2[i], val_src1[i]);
+ ASSERT_NE(dest_src2[i], val_src2[i]);
+ }
+ }
+};
+
+TEST_F(PointDVectorTest, VectorTest) {
+ TestSize<uint32_t>();
+ TestContentsDiscrete<uint32_t>();
+ TestContentsContiguous<uint32_t>();
+ TestContentsCopy<uint32_t>();
+ TestIterator<uint32_t>();
+ TestPoint3Iterator<uint32_t>();
+}
+TEST_F(PointDVectorTest, PseudoPointDTest) {
+ TestPseudoPointDSwap();
+ TestPseudoPointDEquality();
+ TestPseudoPointDInequality();
+}
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/points_sequencer.h b/extern/draco/dracoenc/src/draco/compression/attributes/points_sequencer.h
new file mode 100644
index 00000000000..2f4f7e16d11
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/points_sequencer.h
@@ -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.
+//
+#ifndef DRACO_COMPRESSION_ATTRIBUTES_POINTS_SEQUENCER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_POINTS_SEQUENCER_H_
+
+#include <vector>
+
+#include "draco/attributes/point_attribute.h"
+
+namespace draco {
+
+// Class for generating a sequence of point ids that can be used to encode
+// or decode attribute values in a specific order.
+// See sequential_attribute_encoders/decoders_controller.h for more details.
+class PointsSequencer {
+ public:
+ PointsSequencer() : out_point_ids_(nullptr) {}
+ virtual ~PointsSequencer() = default;
+
+ // Fills the |out_point_ids| with the generated sequence of point ids.
+ bool GenerateSequence(std::vector<PointIndex> *out_point_ids) {
+ out_point_ids_ = out_point_ids;
+ return GenerateSequenceInternal();
+ }
+
+ // Appends a point to the sequence.
+ void AddPointId(PointIndex point_id) { out_point_ids_->push_back(point_id); }
+
+ // Sets the correct mapping between point ids and value ids. I.e., the inverse
+ // of the |out_point_ids|. In general, |out_point_ids_| does not contain
+ // 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 * /* attr */) {
+ return false;
+ }
+
+ protected:
+ // Method that needs to be implemented by the derived classes. The
+ // implementation is responsible for filling |out_point_ids_| with the valid
+ // sequence of point ids.
+ virtual bool GenerateSequenceInternal() = 0;
+ std::vector<PointIndex> *out_point_ids() const { return out_point_ids_; }
+
+ private:
+ std::vector<PointIndex> *out_point_ids_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_POINTS_SEQUENCER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_decoder.h
new file mode 100644
index 00000000000..b64b23d25de
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_decoder.h
@@ -0,0 +1,227 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_CONSTRAINED_MULTI_PARALLELOGRAM_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_CONSTRAINED_MULTI_PARALLELOGRAM_DECODER_H_
+
+#include <algorithm>
+#include <cmath>
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_shared.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h"
+#include "draco/compression/bit_coders/rans_bit_decoder.h"
+#include "draco/core/varint_decoding.h"
+
+namespace draco {
+
+// Decoder for predictions encoded with the constrained multi-parallelogram
+// encoder. See the corresponding encoder for more details about the prediction
+// method.
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeConstrainedMultiParallelogramDecoder
+ : public MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT> {
+ public:
+ using CorrType =
+ typename PredictionSchemeDecoder<DataTypeT, TransformT>::CorrType;
+ using CornerTable = typename MeshDataT::CornerTable;
+
+ explicit MeshPredictionSchemeConstrainedMultiParallelogramDecoder(
+ const PointAttribute *attribute)
+ : MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT>(
+ attribute),
+ selected_mode_(Mode::OPTIMAL_MULTI_PARALLELOGRAM) {}
+ MeshPredictionSchemeConstrainedMultiParallelogramDecoder(
+ const PointAttribute *attribute, const TransformT &transform,
+ const MeshDataT &mesh_data)
+ : MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT>(
+ attribute, transform, mesh_data),
+ selected_mode_(Mode::OPTIMAL_MULTI_PARALLELOGRAM) {}
+
+ bool ComputeOriginalValues(const CorrType *in_corr, DataTypeT *out_data,
+ int size, int num_components,
+ const PointIndex *entry_to_point_id_map) override;
+
+ bool DecodePredictionData(DecoderBuffer *buffer) override;
+
+ PredictionSchemeMethod GetPredictionMethod() const override {
+ return MESH_PREDICTION_CONSTRAINED_MULTI_PARALLELOGRAM;
+ }
+
+ bool IsInitialized() const override {
+ return this->mesh_data().IsInitialized();
+ }
+
+ private:
+ typedef constrained_multi_parallelogram::Mode Mode;
+ static constexpr int kMaxNumParallelograms =
+ constrained_multi_parallelogram::kMaxNumParallelograms;
+ // Crease edges are used to store whether any given edge should be used for
+ // parallelogram prediction or not. New values are added in the order in which
+ // the edges are processed. For better compression, the flags are stored in
+ // in separate contexts based on the number of available parallelograms at a
+ // given vertex.
+ std::vector<bool> is_crease_edge_[kMaxNumParallelograms];
+ Mode selected_mode_;
+};
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeConstrainedMultiParallelogramDecoder<
+ DataTypeT, TransformT, MeshDataT>::
+ ComputeOriginalValues(const CorrType *in_corr, DataTypeT *out_data,
+ int /* size */, int num_components,
+ const PointIndex * /* entry_to_point_id_map */) {
+ this->transform().Init(num_components);
+
+ // Predicted values for all simple parallelograms encountered at any given
+ // vertex.
+ std::vector<DataTypeT> pred_vals[kMaxNumParallelograms];
+ for (int i = 0; i < kMaxNumParallelograms; ++i) {
+ pred_vals[i].resize(num_components, 0);
+ }
+ this->transform().ComputeOriginalValue(pred_vals[0].data(), in_corr,
+ out_data);
+
+ const CornerTable *const table = this->mesh_data().corner_table();
+ const std::vector<int32_t> *const vertex_to_data_map =
+ this->mesh_data().vertex_to_data_map();
+
+ // Current position in the |is_crease_edge_| array for each context.
+ std::vector<int> is_crease_edge_pos(kMaxNumParallelograms, 0);
+
+ // Used to store predicted value for multi-parallelogram prediction.
+ std::vector<DataTypeT> multi_pred_vals(num_components);
+
+ const int corner_map_size =
+ static_cast<int>(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);
+
+ CornerIndex corner_id(start_corner_id);
+ int num_parallelograms = 0;
+ bool first_pass = true;
+ while (corner_id != kInvalidCornerIndex) {
+ if (ComputeParallelogramPrediction(
+ p, corner_id, table, *vertex_to_data_map, out_data,
+ num_components, &(pred_vals[num_parallelograms][0]))) {
+ // Parallelogram prediction applied and stored in
+ // |pred_vals[num_parallelograms]|
+ ++num_parallelograms;
+ // Stop processing when we reach the maximum number of allowed
+ // parallelograms.
+ if (num_parallelograms == kMaxNumParallelograms)
+ break;
+ }
+
+ // Proceed to the next corner attached to the vertex. First swing left
+ // and if we reach a boundary, swing right from the start corner.
+ if (first_pass) {
+ corner_id = table->SwingLeft(corner_id);
+ } else {
+ corner_id = table->SwingRight(corner_id);
+ }
+ if (corner_id == start_corner_id) {
+ break;
+ }
+ if (corner_id == kInvalidCornerIndex && first_pass) {
+ first_pass = false;
+ corner_id = table->SwingRight(start_corner_id);
+ }
+ }
+
+ // Check which of the available parallelograms are actually used and compute
+ // the final predicted value.
+ int num_used_parallelograms = 0;
+ if (num_parallelograms > 0) {
+ for (int i = 0; i < num_components; ++i) {
+ multi_pred_vals[i] = 0;
+ }
+ // Check which parallelograms are actually used.
+ for (int i = 0; i < num_parallelograms; ++i) {
+ const int context = num_parallelograms - 1;
+ const int pos = is_crease_edge_pos[context]++;
+ if (is_crease_edge_[context].size() <= pos)
+ return false;
+ const bool is_crease = is_crease_edge_[context][pos];
+ if (!is_crease) {
+ ++num_used_parallelograms;
+ for (int j = 0; j < num_components; ++j) {
+ multi_pred_vals[j] += pred_vals[i][j];
+ }
+ }
+ }
+ }
+ const int dst_offset = p * num_components;
+ if (num_used_parallelograms == 0) {
+ // No parallelogram was valid.
+ // We use the last decoded point as a reference.
+ const int src_offset = (p - 1) * num_components;
+ this->transform().ComputeOriginalValue(
+ out_data + src_offset, in_corr + dst_offset, out_data + dst_offset);
+ } else {
+ // Compute the correction from the predicted value.
+ for (int c = 0; c < num_components; ++c) {
+ multi_pred_vals[c] /= num_used_parallelograms;
+ }
+ this->transform().ComputeOriginalValue(
+ multi_pred_vals.data(), in_corr + dst_offset, out_data + dst_offset);
+ }
+ }
+ return true;
+}
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeConstrainedMultiParallelogramDecoder<
+ DataTypeT, TransformT, MeshDataT>::DecodePredictionData(DecoderBuffer
+ *buffer) {
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (buffer->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ // Decode prediction mode.
+ uint8_t mode;
+ if (!buffer->Decode(&mode)) {
+ return false;
+ }
+
+ if (mode != Mode::OPTIMAL_MULTI_PARALLELOGRAM) {
+ // Unsupported mode.
+ return false;
+ }
+ }
+#endif
+
+ // Encode selected edges using separate rans bit coder for each context.
+ for (int i = 0; i < kMaxNumParallelograms; ++i) {
+ uint32_t num_flags;
+ DecodeVarint<uint32_t>(&num_flags, buffer);
+ if (num_flags > 0) {
+ is_crease_edge_[i].resize(num_flags);
+ RAnsBitDecoder decoder;
+ if (!decoder.StartDecoding(buffer))
+ return false;
+ for (uint32_t j = 0; j < num_flags; ++j) {
+ is_crease_edge_[i][j] = decoder.DecodeNextBit();
+ }
+ decoder.EndDecoding();
+ }
+ }
+ return MeshPredictionSchemeDecoder<DataTypeT, TransformT,
+ MeshDataT>::DecodePredictionData(buffer);
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_CONSTRAINED_MULTI_PARALLELOGRAM_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h
new file mode 100644
index 00000000000..455c2ceb5ed
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h
@@ -0,0 +1,410 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_CONSTRAINED_MULTI_PARALLELOGRAM_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_CONSTRAINED_MULTI_PARALLELOGRAM_ENCODER_H_
+
+#include <algorithm>
+#include <cmath>
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_shared.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_encoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h"
+#include "draco/compression/bit_coders/rans_bit_encoder.h"
+#include "draco/compression/entropy/shannon_entropy.h"
+#include "draco/core/varint_encoding.h"
+
+namespace draco {
+
+// Compared to standard multi-parallelogram, constrained multi-parallelogram can
+// explicitly select which of the available parallelograms are going to be used
+// for the prediction by marking crease edges between two triangles. This
+// requires storing extra data, but it allows the predictor to avoid using
+// parallelograms that would lead to poor predictions. For improved efficiency,
+// our current implementation limits the maximum number of used parallelograms
+// to four, which covers >95% of the cases (on average, there are only two
+// parallelograms available for any given vertex).
+// All bits of the explicitly chosen configuration are stored together in a
+// single context chosen by the total number of parallelograms available to
+// choose from.
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeConstrainedMultiParallelogramEncoder
+ : public MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT> {
+ public:
+ using CorrType =
+ typename PredictionSchemeEncoder<DataTypeT, TransformT>::CorrType;
+ using CornerTable = typename MeshDataT::CornerTable;
+
+ explicit MeshPredictionSchemeConstrainedMultiParallelogramEncoder(
+ const PointAttribute *attribute)
+ : MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT>(
+ attribute),
+ selected_mode_(Mode::OPTIMAL_MULTI_PARALLELOGRAM) {}
+ MeshPredictionSchemeConstrainedMultiParallelogramEncoder(
+ const PointAttribute *attribute, const TransformT &transform,
+ const MeshDataT &mesh_data)
+ : MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT>(
+ attribute, transform, mesh_data),
+ selected_mode_(Mode::OPTIMAL_MULTI_PARALLELOGRAM) {}
+
+ bool ComputeCorrectionValues(
+ const DataTypeT *in_data, CorrType *out_corr, int size,
+ int num_components, const PointIndex *entry_to_point_id_map) override;
+
+ bool EncodePredictionData(EncoderBuffer *buffer) override;
+
+ PredictionSchemeMethod GetPredictionMethod() const override {
+ return MESH_PREDICTION_CONSTRAINED_MULTI_PARALLELOGRAM;
+ }
+
+ bool IsInitialized() const override {
+ return this->mesh_data().IsInitialized();
+ }
+
+ private:
+ // Function used to compute number of bits needed to store overhead of the
+ // predictor. In this case, we consider overhead to be all bits that mark
+ // whether a parallelogram should be used for prediction or not. The input
+ // to this method is the total number of parallelograms that were evaluated so
+ // far(total_parallelogram), and the number of parallelograms we decided to
+ // use for prediction (total_used_parallelograms).
+ // Returns number of bits required to store the overhead.
+ int64_t ComputeOverheadBits(int64_t total_used_parallelograms,
+ int64_t total_parallelogram) const {
+ // For now we assume RAns coding for the bits where the total required size
+ // is directly correlated to the binary entropy of the input stream.
+ // TODO(ostava): This should be generalized in case we use other binary
+ // coding scheme.
+ const double entropy = ComputeBinaryShannonEntropy(
+ static_cast<uint32_t>(total_parallelogram),
+ static_cast<uint32_t>(total_used_parallelograms));
+
+ // Round up to the nearest full bit.
+ return static_cast<int64_t>(
+ ceil(static_cast<double>(total_parallelogram) * entropy));
+ }
+
+ // Struct that contains data used for measuring the error of each available
+ // parallelogram configuration.
+ struct Error {
+ Error() : num_bits(0), residual_error(0) {}
+
+ // Primary metric: number of bits required to store the data as a result of
+ // the selected prediction configuration.
+ int num_bits;
+ // Secondary metric: absolute difference of residuals for the given
+ // configuration.
+ int residual_error;
+
+ bool operator<(const Error &e) const {
+ if (num_bits < e.num_bits)
+ return true;
+ if (num_bits > e.num_bits)
+ return false;
+ return residual_error < e.residual_error;
+ }
+ };
+
+ // Computes error for predicting |predicted_val| instead of |actual_val|.
+ // Error is computed as the number of bits needed to encode the difference
+ // between the values.
+ Error ComputeError(const DataTypeT *predicted_val,
+ const DataTypeT *actual_val, int *out_residuals,
+ int num_components) {
+ Error error;
+
+ for (int i = 0; i < num_components; ++i) {
+ const int dif = (predicted_val[i] - actual_val[i]);
+ error.residual_error += std::abs(dif);
+ out_residuals[i] = dif;
+ // Entropy needs unsigned symbols, so convert the signed difference to an
+ // unsigned symbol.
+ entropy_symbols_[i] = ConvertSignedIntToSymbol(dif);
+ }
+
+ // Generate entropy data for case that this configuration was used.
+ // Note that the entropy stream is NOT updated in this case.
+ const auto entropy_data =
+ entropy_tracker_.Peek(entropy_symbols_.data(), num_components);
+
+ error.num_bits = entropy_tracker_.GetNumberOfDataBits(entropy_data) +
+ entropy_tracker_.GetNumberOfRAnsTableBits(entropy_data);
+ return error;
+ }
+
+ typedef constrained_multi_parallelogram::Mode Mode;
+ static constexpr int kMaxNumParallelograms =
+ constrained_multi_parallelogram::kMaxNumParallelograms;
+ // Crease edges are used to store whether any given edge should be used for
+ // parallelogram prediction or not. New values are added in the order in which
+ // the edges are processed. For better compression, the flags are stored in
+ // in separate contexts based on the number of available parallelograms at a
+ // given vertex.
+ // TODO(draco-eng) reconsider std::vector<bool> (performance/space).
+ std::vector<bool> is_crease_edge_[kMaxNumParallelograms];
+ Mode selected_mode_;
+
+ ShannonEntropyTracker entropy_tracker_;
+
+ // Temporary storage for symbols that are fed into the |entropy_stream|.
+ // Always contains only |num_components| entries.
+ std::vector<uint32_t> entropy_symbols_;
+};
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeConstrainedMultiParallelogramEncoder<
+ DataTypeT, TransformT, MeshDataT>::
+ ComputeCorrectionValues(const DataTypeT *in_data, CorrType *out_corr,
+ int size, int num_components,
+ const PointIndex * /* entry_to_point_id_map */) {
+ this->transform().Init(in_data, size, num_components);
+ const CornerTable *const table = this->mesh_data().corner_table();
+ const std::vector<int32_t> *const vertex_to_data_map =
+ this->mesh_data().vertex_to_data_map();
+
+ // Predicted values for all simple parallelograms encountered at any given
+ // vertex.
+ std::vector<DataTypeT> pred_vals[kMaxNumParallelograms];
+ for (int i = 0; i < kMaxNumParallelograms; ++i) {
+ pred_vals[i].resize(num_components);
+ }
+ // Used to store predicted value for various multi-parallelogram predictions
+ // (combinations of simple parallelogram predictions).
+ std::vector<DataTypeT> multi_pred_vals(num_components);
+ entropy_symbols_.resize(num_components);
+
+ // Struct for holding data about prediction configuration for different sets
+ // of used parallelograms.
+ struct PredictionConfiguration {
+ PredictionConfiguration()
+ : error(), configuration(0), num_used_parallelograms(0) {}
+ Error error;
+ uint8_t configuration; // Bitfield, 1 use parallelogram, 0 don't use it.
+ int num_used_parallelograms;
+ std::vector<DataTypeT> predicted_value;
+ std::vector<int32_t> residuals;
+ };
+
+ // Bit-field used for computing permutations of excluded edges
+ // (parallelograms).
+ bool exluded_parallelograms[kMaxNumParallelograms];
+
+ // Data about the number of used parallelogram and total number of available
+ // parallelogram for each context. Used to compute overhead needed for storing
+ // the parallelogram choices made by the encoder.
+ int64_t total_used_parallelograms[kMaxNumParallelograms] = {0};
+ int64_t total_parallelograms[kMaxNumParallelograms] = {0};
+
+ std::vector<int> current_residuals(num_components);
+
+ // We start processing the vertices from the end because this prediction uses
+ // data from previous entries that could be overwritten when an entry is
+ // processed.
+ for (int p =
+ static_cast<int>(this->mesh_data().data_to_corner_map()->size()) - 1;
+ p > 0; --p) {
+ const CornerIndex start_corner_id =
+ this->mesh_data().data_to_corner_map()->at(p);
+
+ // Go over all corners attached to the vertex and compute the predicted
+ // value from the parallelograms defined by their opposite faces.
+ CornerIndex corner_id(start_corner_id);
+ int num_parallelograms = 0;
+ bool first_pass = true;
+ while (corner_id != kInvalidCornerIndex) {
+ if (ComputeParallelogramPrediction(
+ p, corner_id, table, *vertex_to_data_map, in_data, num_components,
+ &(pred_vals[num_parallelograms][0]))) {
+ // Parallelogram prediction applied and stored in
+ // |pred_vals[num_parallelograms]|
+ ++num_parallelograms;
+ // Stop processing when we reach the maximum number of allowed
+ // parallelograms.
+ if (num_parallelograms == kMaxNumParallelograms)
+ break;
+ }
+
+ // Proceed to the next corner attached to the vertex. First swing left
+ // and if we reach a boundary, swing right from the start corner.
+ if (first_pass) {
+ corner_id = table->SwingLeft(corner_id);
+ } else {
+ corner_id = table->SwingRight(corner_id);
+ }
+ if (corner_id == start_corner_id) {
+ break;
+ }
+ if (corner_id == kInvalidCornerIndex && first_pass) {
+ first_pass = false;
+ corner_id = table->SwingRight(start_corner_id);
+ }
+ }
+
+ // Offset to the target (destination) vertex.
+ const int dst_offset = p * num_components;
+ Error error;
+
+ // Compute all prediction errors for all possible configurations of
+ // available parallelograms.
+
+ // Variable for holding the best configuration that has been found so far.
+ PredictionConfiguration best_prediction;
+
+ // Compute delta coding error (configuration when no parallelogram is
+ // selected).
+ const int src_offset = (p - 1) * num_components;
+ error = ComputeError(in_data + src_offset, in_data + dst_offset,
+ &current_residuals[0], num_components);
+
+ if (num_parallelograms > 0) {
+ total_parallelograms[num_parallelograms - 1] += num_parallelograms;
+ const int64_t new_overhead_bits =
+ ComputeOverheadBits(total_used_parallelograms[num_parallelograms - 1],
+ total_parallelograms[num_parallelograms - 1]);
+ error.num_bits += new_overhead_bits;
+ }
+
+ best_prediction.error = error;
+ best_prediction.configuration = 0;
+ best_prediction.num_used_parallelograms = 0;
+ best_prediction.predicted_value.assign(
+ in_data + src_offset, in_data + src_offset + num_components);
+ best_prediction.residuals.assign(current_residuals.begin(),
+ current_residuals.end());
+
+ // Compute prediction error for different cases of used parallelograms.
+ for (int num_used_parallelograms = 1;
+ num_used_parallelograms <= num_parallelograms;
+ ++num_used_parallelograms) {
+ // Mark all parallelograms as excluded.
+ std::fill(exluded_parallelograms,
+ exluded_parallelograms + num_parallelograms, true);
+ // TODO(draco-eng) maybe this should be another std::fill.
+ // Mark the first |num_used_parallelograms| as not excluded.
+ for (int j = 0; j < num_used_parallelograms; ++j) {
+ exluded_parallelograms[j] = false;
+ }
+ // Permute over the excluded edges and compute error for each
+ // configuration (permutation of excluded parallelograms).
+ do {
+ // Reset the multi-parallelogram predicted values.
+ for (int j = 0; j < num_components; ++j) {
+ multi_pred_vals[j] = 0;
+ }
+ uint8_t configuration = 0;
+ for (int j = 0; j < num_parallelograms; ++j) {
+ if (exluded_parallelograms[j])
+ continue;
+ for (int c = 0; c < num_components; ++c) {
+ multi_pred_vals[c] += pred_vals[j][c];
+ }
+ // Set jth bit of the configuration.
+ configuration |= (1 << j);
+ }
+
+ for (int j = 0; j < num_components; ++j) {
+ multi_pred_vals[j] /= num_used_parallelograms;
+ }
+ error = ComputeError(multi_pred_vals.data(), in_data + dst_offset,
+ &current_residuals[0], num_components);
+ if (num_parallelograms > 0) {
+ const int64_t new_overhead_bits = ComputeOverheadBits(
+ total_used_parallelograms[num_parallelograms - 1] +
+ num_used_parallelograms,
+ total_parallelograms[num_parallelograms - 1]);
+
+ // Add overhead bits to the total error.
+ error.num_bits += new_overhead_bits;
+ }
+ if (error < best_prediction.error) {
+ best_prediction.error = error;
+ best_prediction.configuration = configuration;
+ best_prediction.num_used_parallelograms = num_used_parallelograms;
+ best_prediction.predicted_value.assign(multi_pred_vals.begin(),
+ multi_pred_vals.end());
+ best_prediction.residuals.assign(current_residuals.begin(),
+ current_residuals.end());
+ }
+ } while (std::next_permutation(
+ exluded_parallelograms, exluded_parallelograms + num_parallelograms));
+ }
+ if (num_parallelograms > 0) {
+ total_used_parallelograms[num_parallelograms - 1] +=
+ best_prediction.num_used_parallelograms;
+ }
+
+ // Update the entropy stream by adding selected residuals as symbols to the
+ // stream.
+ for (int i = 0; i < num_components; ++i) {
+ entropy_symbols_[i] =
+ ConvertSignedIntToSymbol(best_prediction.residuals[i]);
+ }
+ entropy_tracker_.Push(entropy_symbols_.data(), num_components);
+
+ for (int i = 0; i < num_parallelograms; ++i) {
+ if ((best_prediction.configuration & (1 << i)) == 0) {
+ // Parallelogram not used, mark the edge as crease.
+ is_crease_edge_[num_parallelograms - 1].push_back(true);
+ } else {
+ // Parallelogram used. Add it to the predicted value and mark the
+ // edge as not a crease.
+ is_crease_edge_[num_parallelograms - 1].push_back(false);
+ }
+ }
+ this->transform().ComputeCorrection(in_data + dst_offset,
+ best_prediction.predicted_value.data(),
+ out_corr + dst_offset);
+ }
+ // First element is always fixed because it cannot be predicted.
+ for (int i = 0; i < num_components; ++i) {
+ pred_vals[0][i] = static_cast<DataTypeT>(0);
+ }
+ this->transform().ComputeCorrection(in_data, pred_vals[0].data(), out_corr);
+ return true;
+}
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeConstrainedMultiParallelogramEncoder<
+ DataTypeT, TransformT, MeshDataT>::EncodePredictionData(EncoderBuffer
+ *buffer) {
+ // Encode selected edges using separate rans bit coder for each context.
+ for (int i = 0; i < kMaxNumParallelograms; ++i) {
+ // |i| is the context based on the number of available parallelograms, which
+ // is always equal to |i + 1|.
+ const int num_used_parallelograms = i + 1;
+ EncodeVarint<uint32_t>(is_crease_edge_[i].size(), buffer);
+ if (is_crease_edge_[i].size()) {
+ RAnsBitEncoder encoder;
+ encoder.StartEncoding();
+ // Encode the crease edge flags in the reverse vertex order that is needed
+ // be the decoder. Note that for the currently supported mode, each vertex
+ // has exactly |num_used_parallelograms| edges that need to be encoded.
+ for (int j = static_cast<int>(is_crease_edge_[i].size()) -
+ num_used_parallelograms;
+ j >= 0; j -= num_used_parallelograms) {
+ // Go over all edges of the current vertex.
+ for (int k = 0; k < num_used_parallelograms; ++k) {
+ encoder.EncodeBit(is_crease_edge_[i][j + k]);
+ }
+ }
+ encoder.EndEncoding(buffer);
+ }
+ }
+ return MeshPredictionSchemeEncoder<DataTypeT, TransformT,
+ MeshDataT>::EncodePredictionData(buffer);
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_CONSTRAINED_MULTI_PARALLELOGRAM_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_shared.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_shared.h
new file mode 100644
index 00000000000..c7a4e351aeb
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_shared.h
@@ -0,0 +1,34 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_CONSTRAINED_MULTI_PARALLELOGRAM_SHARED_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_CONSTRAINED_MULTI_PARALLELOGRAM_SHARED_H_
+
+namespace draco {
+
+// Data shared between constrained multi-parallelogram encoder and decoder.
+namespace constrained_multi_parallelogram {
+
+enum Mode {
+ // Selects the optimal multi-parallelogram from up to 4 available
+ // parallelograms.
+ OPTIMAL_MULTI_PARALLELOGRAM = 0,
+};
+
+static constexpr int kMaxNumParallelograms = 4;
+
+} // namespace constrained_multi_parallelogram
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_CONSTRAINED_MULTI_PARALLELOGRAM_SHARED_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_data.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_data.h
new file mode 100644
index 00000000000..f712952556a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_data.h
@@ -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.
+//
+#ifndef DRACO_COMPRESSION_ATTRIBUTES_MESH_PREDICTION_SCHEMES_PREDICTION_SCHEME_DATA_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_MESH_PREDICTION_SCHEMES_PREDICTION_SCHEME_DATA_H_
+
+#include "draco/mesh/corner_table.h"
+#include "draco/mesh/mesh.h"
+
+namespace draco {
+
+// Class stores data about the connectivity data of the mesh and information
+// about how the connectivity was encoded/decoded.
+template <class CornerTableT>
+class MeshPredictionSchemeData {
+ public:
+ typedef CornerTableT CornerTable;
+ MeshPredictionSchemeData()
+ : mesh_(nullptr),
+ corner_table_(nullptr),
+ vertex_to_data_map_(nullptr),
+ data_to_corner_map_(nullptr) {}
+
+ void Set(const Mesh *mesh, const CornerTable *table,
+ const std::vector<CornerIndex> *data_to_corner_map,
+ const std::vector<int32_t> *vertex_to_data_map) {
+ mesh_ = mesh;
+ corner_table_ = table;
+ data_to_corner_map_ = data_to_corner_map;
+ vertex_to_data_map_ = vertex_to_data_map;
+ }
+
+ const Mesh *mesh() const { return mesh_; }
+ const CornerTable *corner_table() const { return corner_table_; }
+ const std::vector<int32_t> *vertex_to_data_map() const {
+ return vertex_to_data_map_;
+ }
+ const std::vector<CornerIndex> *data_to_corner_map() const {
+ return data_to_corner_map_;
+ }
+ bool IsInitialized() const {
+ return mesh_ != nullptr && corner_table_ != nullptr &&
+ vertex_to_data_map_ != nullptr && data_to_corner_map_ != nullptr;
+ }
+
+ private:
+ const Mesh *mesh_;
+ const CornerTable *corner_table_;
+
+ // Mapping between vertices and their encoding order. I.e. when an attribute
+ // entry on a given vertex was encoded.
+ const std::vector<int32_t> *vertex_to_data_map_;
+
+ // Array that stores which corner was processed when a given attribute entry
+ // was encoded or decoded.
+ const std::vector<CornerIndex> *data_to_corner_map_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_DATA_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h
new file mode 100644
index 00000000000..6694a981c10
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h
@@ -0,0 +1,46 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_DECODER_H_
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_data.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_decoder.h"
+
+namespace draco {
+
+// Base class for all mesh prediction scheme decoders that use the mesh
+// connectivity data. |MeshDataT| can be any class that provides the same
+// interface as the PredictionSchemeMeshData class.
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeDecoder
+ : public PredictionSchemeDecoder<DataTypeT, TransformT> {
+ public:
+ typedef MeshDataT MeshData;
+ MeshPredictionSchemeDecoder(const PointAttribute *attribute,
+ const TransformT &transform,
+ const MeshDataT &mesh_data)
+ : PredictionSchemeDecoder<DataTypeT, TransformT>(attribute, transform),
+ mesh_data_(mesh_data) {}
+
+ protected:
+ const MeshData &mesh_data() const { return mesh_data_; }
+
+ private:
+ MeshData mesh_data_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_encoder.h
new file mode 100644
index 00000000000..ab3c81a3901
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_encoder.h
@@ -0,0 +1,46 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_ENCODER_H_
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_data.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_encoder.h"
+
+namespace draco {
+
+// Base class for all mesh prediction scheme encoders that use the mesh
+// connectivity data. |MeshDataT| can be any class that provides the same
+// interface as the PredictionSchemeMeshData class.
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeEncoder
+ : public PredictionSchemeEncoder<DataTypeT, TransformT> {
+ public:
+ typedef MeshDataT MeshData;
+ MeshPredictionSchemeEncoder(const PointAttribute *attribute,
+ const TransformT &transform,
+ const MeshDataT &mesh_data)
+ : PredictionSchemeEncoder<DataTypeT, TransformT>(attribute, transform),
+ mesh_data_(mesh_data) {}
+
+ protected:
+ const MeshData &mesh_data() const { return mesh_data_; }
+
+ private:
+ MeshData mesh_data_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_decoder.h
new file mode 100644
index 00000000000..cd8299627b7
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_decoder.h
@@ -0,0 +1,163 @@
+// Copyright 2017 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_GEOMETRIC_NORMAL_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_GEOMETRIC_NORMAL_DECODER_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_area.h"
+#include "draco/compression/bit_coders/rans_bit_decoder.h"
+
+namespace draco {
+
+// See MeshPredictionSchemeGeometricNormalEncoder for documentation.
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeGeometricNormalDecoder
+ : public MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT> {
+ public:
+ using CorrType = typename MeshPredictionSchemeDecoder<DataTypeT, TransformT,
+ MeshDataT>::CorrType;
+ MeshPredictionSchemeGeometricNormalDecoder(const PointAttribute *attribute,
+ const TransformT &transform,
+ const MeshDataT &mesh_data)
+ : MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT>(
+ attribute, transform, mesh_data),
+ predictor_(mesh_data) {}
+
+ private:
+ MeshPredictionSchemeGeometricNormalDecoder() {}
+
+ public:
+ bool ComputeOriginalValues(const CorrType *in_corr, DataTypeT *out_data,
+ int size, int num_components,
+ const PointIndex *entry_to_point_id_map) override;
+
+ bool DecodePredictionData(DecoderBuffer *buffer) override;
+
+ PredictionSchemeMethod GetPredictionMethod() const override {
+ return MESH_PREDICTION_GEOMETRIC_NORMAL;
+ }
+
+ bool IsInitialized() const override {
+ if (!predictor_.IsInitialized())
+ return false;
+ if (!this->mesh_data().IsInitialized())
+ return false;
+ if (!octahedron_tool_box_.IsInitialized())
+ return false;
+ return true;
+ }
+
+ int GetNumParentAttributes() const override { return 1; }
+
+ GeometryAttribute::Type GetParentAttributeType(int i) const override {
+ DRACO_DCHECK_EQ(i, 0);
+ (void)i;
+ return GeometryAttribute::POSITION;
+ }
+
+ bool SetParentAttribute(const PointAttribute *att) override {
+ if (att->attribute_type() != GeometryAttribute::POSITION)
+ return false; // Invalid attribute type.
+ if (att->num_components() != 3)
+ return false; // Currently works only for 3 component positions.
+ predictor_.SetPositionAttribute(*att);
+ return true;
+ }
+ void SetQuantizationBits(int q) {
+ octahedron_tool_box_.SetQuantizationBits(q);
+ }
+
+ private:
+ MeshPredictionSchemeGeometricNormalPredictorArea<DataTypeT, TransformT,
+ MeshDataT>
+ predictor_;
+ OctahedronToolBox octahedron_tool_box_;
+ RAnsBitDecoder flip_normal_bit_decoder_;
+};
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeGeometricNormalDecoder<
+ DataTypeT, TransformT,
+ MeshDataT>::ComputeOriginalValues(const CorrType *in_corr,
+ DataTypeT *out_data, int /* size */,
+ int num_components,
+ const PointIndex *entry_to_point_id_map) {
+ this->SetQuantizationBits(this->transform().quantization_bits());
+ predictor_.SetEntryToPointIdMap(entry_to_point_id_map);
+ DRACO_DCHECK(this->IsInitialized());
+
+ // Expecting in_data in octahedral coordinates, i.e., portable attribute.
+ DRACO_DCHECK_EQ(num_components, 2);
+
+ const int corner_map_size =
+ static_cast<int>(this->mesh_data().data_to_corner_map()->size());
+
+ VectorD<int32_t, 3> pred_normal_3d;
+ int32_t pred_normal_oct[2];
+
+ for (int data_id = 0; data_id < corner_map_size; ++data_id) {
+ const CornerIndex corner_id =
+ this->mesh_data().data_to_corner_map()->at(data_id);
+ predictor_.ComputePredictedValue(corner_id, pred_normal_3d.data());
+
+ // Compute predicted octahedral coordinates.
+ octahedron_tool_box_.CanonicalizeIntegerVector(pred_normal_3d.data());
+ DRACO_DCHECK_EQ(pred_normal_3d.AbsSum(),
+ octahedron_tool_box_.center_value());
+ if (flip_normal_bit_decoder_.DecodeNextBit()) {
+ pred_normal_3d = -pred_normal_3d;
+ }
+ octahedron_tool_box_.IntegerVectorToQuantizedOctahedralCoords(
+ pred_normal_3d.data(), pred_normal_oct, pred_normal_oct + 1);
+
+ const int data_offset = data_id * 2;
+ this->transform().ComputeOriginalValue(
+ pred_normal_oct, in_corr + data_offset, out_data + data_offset);
+ }
+ flip_normal_bit_decoder_.EndDecoding();
+ return true;
+}
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeGeometricNormalDecoder<
+ DataTypeT, TransformT, MeshDataT>::DecodePredictionData(DecoderBuffer
+ *buffer) {
+ // Get data needed for transform
+ if (!this->transform().DecodeTransformData(buffer))
+ return false;
+
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (buffer->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ uint8_t prediction_mode;
+ buffer->Decode(&prediction_mode);
+
+ if (!predictor_.SetNormalPredictionMode(
+ NormalPredictionMode(prediction_mode)))
+ return false;
+ }
+#endif
+
+ // Init normal flips.
+ if (!flip_normal_bit_decoder_.StartDecoding(buffer))
+ return false;
+
+ return true;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_GEOMETRIC_NORMAL_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_encoder.h
new file mode 100644
index 00000000000..ffddf0170d6
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_encoder.h
@@ -0,0 +1,175 @@
+// Copyright 2017 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_GEOMETRIC_NORMAL_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_GEOMETRIC_NORMAL_ENCODER_H_
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_encoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_area.h"
+#include "draco/compression/bit_coders/rans_bit_encoder.h"
+#include "draco/compression/config/compression_shared.h"
+
+namespace draco {
+
+// Prediction scheme for normals based on the underlying geometry.
+// At a smooth vertices normals are computed by weighting the normals of
+// adjacent faces with the area of these faces. At seams, the same approach
+// applies for seam corners.
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeGeometricNormalEncoder
+ : public MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT> {
+ public:
+ using CorrType = typename MeshPredictionSchemeEncoder<DataTypeT, TransformT,
+ MeshDataT>::CorrType;
+ MeshPredictionSchemeGeometricNormalEncoder(const PointAttribute *attribute,
+ const TransformT &transform,
+ const MeshDataT &mesh_data)
+ : MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT>(
+ attribute, transform, mesh_data),
+ predictor_(mesh_data) {}
+
+ bool ComputeCorrectionValues(
+ const DataTypeT *in_data, CorrType *out_corr, int size,
+ int num_components, const PointIndex *entry_to_point_id_map) override;
+
+ bool EncodePredictionData(EncoderBuffer *buffer) override;
+
+ PredictionSchemeMethod GetPredictionMethod() const override {
+ return MESH_PREDICTION_GEOMETRIC_NORMAL;
+ }
+
+ bool IsInitialized() const override {
+ if (!predictor_.IsInitialized())
+ return false;
+ if (!this->mesh_data().IsInitialized())
+ return false;
+ return true;
+ }
+
+ int GetNumParentAttributes() const override { return 1; }
+
+ GeometryAttribute::Type GetParentAttributeType(int i) const override {
+ DRACO_DCHECK_EQ(i, 0);
+ (void)i;
+ return GeometryAttribute::POSITION;
+ }
+
+ bool SetParentAttribute(const PointAttribute *att) override {
+ if (att->attribute_type() != GeometryAttribute::POSITION)
+ return false; // Invalid attribute type.
+ if (att->num_components() != 3)
+ return false; // Currently works only for 3 component positions.
+ predictor_.SetPositionAttribute(*att);
+ return true;
+ }
+
+ private:
+ void SetQuantizationBits(int q) {
+ DRACO_DCHECK_GE(q, 2);
+ DRACO_DCHECK_LE(q, 30);
+ octahedron_tool_box_.SetQuantizationBits(q);
+ }
+ MeshPredictionSchemeGeometricNormalPredictorArea<DataTypeT, TransformT,
+ MeshDataT>
+ predictor_;
+
+ OctahedronToolBox octahedron_tool_box_;
+ RAnsBitEncoder flip_normal_bit_encoder_;
+};
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeGeometricNormalEncoder<DataTypeT, TransformT,
+ MeshDataT>::
+ ComputeCorrectionValues(const DataTypeT *in_data, CorrType *out_corr,
+ int size, int num_components,
+ const PointIndex *entry_to_point_id_map) {
+ this->SetQuantizationBits(this->transform().quantization_bits());
+ predictor_.SetEntryToPointIdMap(entry_to_point_id_map);
+ DRACO_DCHECK(this->IsInitialized());
+ // Expecting in_data in octahedral coordinates, i.e., portable attribute.
+ DRACO_DCHECK_EQ(num_components, 2);
+
+ flip_normal_bit_encoder_.StartEncoding();
+
+ const int corner_map_size =
+ static_cast<int>(this->mesh_data().data_to_corner_map()->size());
+
+ VectorD<int32_t, 3> pred_normal_3d;
+ VectorD<int32_t, 2> pos_pred_normal_oct;
+ VectorD<int32_t, 2> neg_pred_normal_oct;
+ VectorD<int32_t, 2> pos_correction;
+ VectorD<int32_t, 2> neg_correction;
+ for (int data_id = 0; data_id < corner_map_size; ++data_id) {
+ const CornerIndex corner_id =
+ this->mesh_data().data_to_corner_map()->at(data_id);
+ predictor_.ComputePredictedValue(corner_id, pred_normal_3d.data());
+
+ // Compute predicted octahedral coordinates.
+ octahedron_tool_box_.CanonicalizeIntegerVector(pred_normal_3d.data());
+ DRACO_DCHECK_EQ(pred_normal_3d.AbsSum(),
+ octahedron_tool_box_.center_value());
+
+ // Compute octahedral coordinates for both possible directions.
+ octahedron_tool_box_.IntegerVectorToQuantizedOctahedralCoords(
+ pred_normal_3d.data(), pos_pred_normal_oct.data(),
+ pos_pred_normal_oct.data() + 1);
+ pred_normal_3d = -pred_normal_3d;
+ octahedron_tool_box_.IntegerVectorToQuantizedOctahedralCoords(
+ pred_normal_3d.data(), neg_pred_normal_oct.data(),
+ neg_pred_normal_oct.data() + 1);
+
+ // Choose the one with the best correction value.
+ const int data_offset = data_id * 2;
+ this->transform().ComputeCorrection(in_data + data_offset,
+ pos_pred_normal_oct.data(),
+ pos_correction.data());
+ this->transform().ComputeCorrection(in_data + data_offset,
+ neg_pred_normal_oct.data(),
+ neg_correction.data());
+ pos_correction[0] = octahedron_tool_box_.ModMax(pos_correction[0]);
+ pos_correction[1] = octahedron_tool_box_.ModMax(pos_correction[1]);
+ neg_correction[0] = octahedron_tool_box_.ModMax(neg_correction[0]);
+ neg_correction[1] = octahedron_tool_box_.ModMax(neg_correction[1]);
+ if (pos_correction.AbsSum() < neg_correction.AbsSum()) {
+ flip_normal_bit_encoder_.EncodeBit(false);
+ (out_corr + data_offset)[0] =
+ octahedron_tool_box_.MakePositive(pos_correction[0]);
+ (out_corr + data_offset)[1] =
+ octahedron_tool_box_.MakePositive(pos_correction[1]);
+ } else {
+ flip_normal_bit_encoder_.EncodeBit(true);
+ (out_corr + data_offset)[0] =
+ octahedron_tool_box_.MakePositive(neg_correction[0]);
+ (out_corr + data_offset)[1] =
+ octahedron_tool_box_.MakePositive(neg_correction[1]);
+ }
+ }
+ return true;
+}
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeGeometricNormalEncoder<
+ DataTypeT, TransformT, MeshDataT>::EncodePredictionData(EncoderBuffer
+ *buffer) {
+ if (!this->transform().EncodeTransformData(buffer))
+ return false;
+
+ // Encode normal flips.
+ flip_normal_bit_encoder_.EndEncoding(buffer);
+ return true;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_GEOMETRIC_NORMAL_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_area.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_area.h
new file mode 100644
index 00000000000..bf1a6146111
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_area.h
@@ -0,0 +1,110 @@
+// Copyright 2017 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_GEOMETRIC_NORMAL_PREDICTOR_AREA_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_GEOMETRIC_NORMAL_PREDICTOR_AREA_H_
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_base.h"
+
+namespace draco {
+
+// This predictor estimates the normal via the surrounding triangles of the
+// given corner. Triangles are weighted according to their area.
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeGeometricNormalPredictorArea
+ : public MeshPredictionSchemeGeometricNormalPredictorBase<
+ DataTypeT, TransformT, MeshDataT> {
+ typedef MeshPredictionSchemeGeometricNormalPredictorBase<
+ DataTypeT, TransformT, MeshDataT>
+ Base;
+
+ public:
+ explicit MeshPredictionSchemeGeometricNormalPredictorArea(const MeshDataT &md)
+ : Base(md) {
+ this->SetNormalPredictionMode(TRIANGLE_AREA);
+ };
+ virtual ~MeshPredictionSchemeGeometricNormalPredictorArea() {}
+
+ // Computes predicted octahedral coordinates on a given corner.
+ void ComputePredictedValue(CornerIndex corner_id,
+ DataTypeT *prediction) override {
+ DRACO_DCHECK(this->IsInitialized());
+ typedef typename MeshDataT::CornerTable CornerTable;
+ const CornerTable *const corner_table = this->mesh_data_.corner_table();
+ // Going to compute the predicted normal from the surrounding triangles
+ // according to the connectivity of the given corner table.
+ VertexCornersIterator<CornerTable> cit(corner_table, corner_id);
+ // Position of central vertex does not change in loop.
+ const VectorD<int64_t, 3> pos_cent = this->GetPositionForCorner(corner_id);
+ // Computing normals for triangles and adding them up.
+
+ VectorD<int64_t, 3> normal;
+ CornerIndex c_next, c_prev;
+ while (!cit.End()) {
+ // Getting corners.
+ if (this->normal_prediction_mode_ == ONE_TRIANGLE) {
+ c_next = corner_table->Next(corner_id);
+ c_prev = corner_table->Previous(corner_id);
+ } else {
+ c_next = corner_table->Next(cit.Corner());
+ c_prev = corner_table->Previous(cit.Corner());
+ }
+ const VectorD<int64_t, 3> pos_next = this->GetPositionForCorner(c_next);
+ const VectorD<int64_t, 3> pos_prev = this->GetPositionForCorner(c_prev);
+
+ // Computing delta vectors to next and prev.
+ const VectorD<int64_t, 3> delta_next = pos_next - pos_cent;
+ const VectorD<int64_t, 3> delta_prev = pos_prev - pos_cent;
+
+ // Computing cross product.
+ const VectorD<int64_t, 3> cross = CrossProduct(delta_next, delta_prev);
+ normal = normal + cross;
+ cit.Next();
+ }
+
+ // Convert to int32_t, make sure entries are not too large.
+ constexpr int64_t upper_bound = 1 << 29;
+ if (this->normal_prediction_mode_ == ONE_TRIANGLE) {
+ const int32_t abs_sum = static_cast<int32_t>(normal.AbsSum());
+ if (abs_sum > upper_bound) {
+ const int64_t quotient = abs_sum / upper_bound;
+ normal = normal / quotient;
+ }
+ } else {
+ const int64_t abs_sum = normal.AbsSum();
+ if (abs_sum > upper_bound) {
+ const int64_t quotient = abs_sum / upper_bound;
+ normal = normal / quotient;
+ }
+ }
+ DRACO_DCHECK_LE(normal.AbsSum(), upper_bound);
+ prediction[0] = static_cast<int32_t>(normal[0]);
+ prediction[1] = static_cast<int32_t>(normal[1]);
+ prediction[2] = static_cast<int32_t>(normal[2]);
+ }
+ bool SetNormalPredictionMode(NormalPredictionMode mode) override {
+ if (mode == ONE_TRIANGLE) {
+ this->normal_prediction_mode_ = mode;
+ return true;
+ } else if (mode == TRIANGLE_AREA) {
+ this->normal_prediction_mode_ = mode;
+ return true;
+ }
+ return false;
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_GEOMETRIC_NORMAL_PREDICTOR_AREA_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_base.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_base.h
new file mode 100644
index 00000000000..9a26551decf
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_predictor_base.h
@@ -0,0 +1,94 @@
+// Copyright 2017 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_GEOMETRIC_NORMAL_PREDICTOR_BASE_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_GEOMETRIC_NORMAL_PREDICTOR_BASE_H_
+
+#include <math.h>
+
+#include "draco/attributes/point_attribute.h"
+#include "draco/compression/attributes/normal_compression_utils.h"
+#include "draco/compression/config/compression_shared.h"
+#include "draco/core/math_utils.h"
+#include "draco/core/vector_d.h"
+#include "draco/mesh/corner_table.h"
+#include "draco/mesh/corner_table_iterators.h"
+
+namespace draco {
+
+// Base class for geometric normal predictors using position attribute.
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeGeometricNormalPredictorBase {
+ protected:
+ explicit MeshPredictionSchemeGeometricNormalPredictorBase(const MeshDataT &md)
+ : pos_attribute_(nullptr),
+ entry_to_point_id_map_(nullptr),
+ mesh_data_(md) {}
+ virtual ~MeshPredictionSchemeGeometricNormalPredictorBase() {}
+
+ public:
+ void SetPositionAttribute(const PointAttribute &position_attribute) {
+ pos_attribute_ = &position_attribute;
+ }
+ void SetEntryToPointIdMap(const PointIndex *map) {
+ entry_to_point_id_map_ = map;
+ }
+ bool IsInitialized() const {
+ if (pos_attribute_ == nullptr)
+ return false;
+ if (entry_to_point_id_map_ == nullptr)
+ return false;
+ return true;
+ }
+
+ virtual bool SetNormalPredictionMode(NormalPredictionMode mode) = 0;
+ virtual NormalPredictionMode GetNormalPredictionMode() const {
+ return normal_prediction_mode_;
+ }
+
+ protected:
+ VectorD<int64_t, 3> GetPositionForDataId(int data_id) const {
+ DRACO_DCHECK(this->IsInitialized());
+ const auto point_id = entry_to_point_id_map_[data_id];
+ const auto pos_val_id = pos_attribute_->mapped_index(point_id);
+ VectorD<int64_t, 3> pos;
+ pos_attribute_->ConvertValue(pos_val_id, &pos[0]);
+ return pos;
+ }
+ VectorD<int64_t, 3> GetPositionForCorner(CornerIndex ci) const {
+ DRACO_DCHECK(this->IsInitialized());
+ const auto corner_table = mesh_data_.corner_table();
+ const auto vert_id = corner_table->Vertex(ci).value();
+ const auto data_id = mesh_data_.vertex_to_data_map()->at(vert_id);
+ return GetPositionForDataId(data_id);
+ }
+ VectorD<int32_t, 2> GetOctahedralCoordForDataId(int data_id,
+ const DataTypeT *data) const {
+ DRACO_DCHECK(this->IsInitialized());
+ const int data_offset = data_id * 2;
+ return VectorD<int32_t, 2>(data[data_offset], data[data_offset + 1]);
+ }
+ // Computes predicted octahedral coordinates on a given corner.
+ virtual void ComputePredictedValue(CornerIndex corner_id,
+ DataTypeT *prediction) = 0;
+
+ const PointAttribute *pos_attribute_;
+ const PointIndex *entry_to_point_id_map_;
+ MeshDataT mesh_data_;
+ NormalPredictionMode normal_prediction_mode_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_GEOMETRIC_NORMAL_PREDICTOR_BASE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_decoder.h
new file mode 100644
index 00000000000..a0cc802da03
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_decoder.h
@@ -0,0 +1,127 @@
+// 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.
+//
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+#ifndef DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_MULTI_PARALLELOGRAM_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_MULTI_PARALLELOGRAM_DECODER_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h"
+
+namespace draco {
+
+// Decoder for predictions encoded by multi-parallelogram encoding scheme.
+// See the corresponding encoder for method description.
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeMultiParallelogramDecoder
+ : public MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT> {
+ public:
+ using CorrType =
+ typename PredictionSchemeDecoder<DataTypeT, TransformT>::CorrType;
+ using CornerTable = typename MeshDataT::CornerTable;
+
+ explicit MeshPredictionSchemeMultiParallelogramDecoder(
+ const PointAttribute *attribute)
+ : MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT>(
+ attribute) {}
+ MeshPredictionSchemeMultiParallelogramDecoder(const PointAttribute *attribute,
+ const TransformT &transform,
+ const MeshDataT &mesh_data)
+ : MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT>(
+ attribute, transform, mesh_data) {}
+
+ bool ComputeOriginalValues(const CorrType *in_corr, DataTypeT *out_data,
+ int size, int num_components,
+ const PointIndex *entry_to_point_id_map) override;
+ PredictionSchemeMethod GetPredictionMethod() const override {
+ return MESH_PREDICTION_MULTI_PARALLELOGRAM;
+ }
+
+ bool IsInitialized() const override {
+ return this->mesh_data().IsInitialized();
+ }
+};
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeMultiParallelogramDecoder<DataTypeT, TransformT,
+ MeshDataT>::
+ ComputeOriginalValues(const CorrType *in_corr, DataTypeT *out_data,
+ int /* size */, int num_components,
+ const PointIndex * /* entry_to_point_id_map */) {
+ this->transform().Init(num_components);
+
+ // For storage of prediction values (already initialized to zero).
+ std::unique_ptr<DataTypeT[]> pred_vals(new DataTypeT[num_components]());
+ std::unique_ptr<DataTypeT[]> parallelogram_pred_vals(
+ new DataTypeT[num_components]());
+
+ this->transform().ComputeOriginalValue(pred_vals.get(), in_corr, out_data);
+
+ const CornerTable *const table = this->mesh_data().corner_table();
+ const std::vector<int32_t> *const vertex_to_data_map =
+ this->mesh_data().vertex_to_data_map();
+
+ const int corner_map_size =
+ static_cast<int>(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);
+
+ CornerIndex corner_id(start_corner_id);
+ int num_parallelograms = 0;
+ for (int i = 0; i < num_components; ++i) {
+ pred_vals[i] = static_cast<DataTypeT>(0);
+ }
+ while (corner_id != kInvalidCornerIndex) {
+ if (ComputeParallelogramPrediction(
+ p, corner_id, table, *vertex_to_data_map, out_data,
+ num_components, parallelogram_pred_vals.get())) {
+ for (int c = 0; c < num_components; ++c) {
+ pred_vals[c] += parallelogram_pred_vals[c];
+ }
+ ++num_parallelograms;
+ }
+
+ // Proceed to the next corner attached to the vertex.
+ corner_id = table->SwingRight(corner_id);
+ if (corner_id == start_corner_id) {
+ corner_id = kInvalidCornerIndex;
+ }
+ }
+
+ const int dst_offset = p * num_components;
+ if (num_parallelograms == 0) {
+ // No parallelogram was valid.
+ // We use the last decoded point as a reference.
+ const int src_offset = (p - 1) * num_components;
+ this->transform().ComputeOriginalValue(
+ out_data + src_offset, in_corr + dst_offset, out_data + dst_offset);
+ } else {
+ // Compute the correction from the predicted value.
+ for (int c = 0; c < num_components; ++c) {
+ pred_vals[c] /= num_parallelograms;
+ }
+ this->transform().ComputeOriginalValue(
+ pred_vals.get(), in_corr + dst_offset, out_data + dst_offset);
+ }
+ }
+ return true;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_MULTI_PARALLELOGRAM_DECODER_H_
+#endif
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_encoder.h
new file mode 100644
index 00000000000..301b357d411
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_encoder.h
@@ -0,0 +1,133 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_MULTI_PARALLELOGRAM_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_MULTI_PARALLELOGRAM_ENCODER_H_
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_encoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h"
+
+namespace draco {
+
+// Multi parallelogram prediction predicts attribute values using information
+// from all opposite faces to the predicted vertex, compared to the standard
+// prediction scheme, where only one opposite face is used (see
+// prediction_scheme_parallelogram.h). This approach is generally slower than
+// the standard parallelogram prediction, but it usually results in better
+// prediction (5 - 20% based on the quantization level. Better gains can be
+// achieved when more aggressive quantization is used).
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeMultiParallelogramEncoder
+ : public MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT> {
+ public:
+ using CorrType =
+ typename PredictionSchemeEncoder<DataTypeT, TransformT>::CorrType;
+ using CornerTable = typename MeshDataT::CornerTable;
+
+ explicit MeshPredictionSchemeMultiParallelogramEncoder(
+ const PointAttribute *attribute)
+ : MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT>(
+ attribute) {}
+ MeshPredictionSchemeMultiParallelogramEncoder(const PointAttribute *attribute,
+ const TransformT &transform,
+ const MeshDataT &mesh_data)
+ : MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT>(
+ attribute, transform, mesh_data) {}
+
+ bool ComputeCorrectionValues(
+ const DataTypeT *in_data, CorrType *out_corr, int size,
+ int num_components, const PointIndex *entry_to_point_id_map) override;
+ PredictionSchemeMethod GetPredictionMethod() const override {
+ return MESH_PREDICTION_MULTI_PARALLELOGRAM;
+ }
+
+ bool IsInitialized() const override {
+ return this->mesh_data().IsInitialized();
+ }
+};
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeMultiParallelogramEncoder<DataTypeT, TransformT,
+ MeshDataT>::
+ ComputeCorrectionValues(const DataTypeT *in_data, CorrType *out_corr,
+ int size, int num_components,
+ const PointIndex * /* entry_to_point_id_map */) {
+ this->transform().Init(in_data, size, num_components);
+ const CornerTable *const table = this->mesh_data().corner_table();
+ const std::vector<int32_t> *const vertex_to_data_map =
+ this->mesh_data().vertex_to_data_map();
+
+ // For storage of prediction values (already initialized to zero).
+ std::unique_ptr<DataTypeT[]> pred_vals(new DataTypeT[num_components]());
+ std::unique_ptr<DataTypeT[]> parallelogram_pred_vals(
+ new DataTypeT[num_components]());
+
+ // We start processing from the end because this prediction uses data from
+ // previous entries that could be overwritten when an entry is processed.
+ for (int p =
+ static_cast<int>(this->mesh_data().data_to_corner_map()->size() - 1);
+ p > 0; --p) {
+ const CornerIndex start_corner_id =
+ this->mesh_data().data_to_corner_map()->at(p);
+
+ // Go over all corners attached to the vertex and compute the predicted
+ // value from the parallelograms defined by their opposite faces.
+ CornerIndex corner_id(start_corner_id);
+ int num_parallelograms = 0;
+ for (int i = 0; i < num_components; ++i) {
+ pred_vals[i] = static_cast<DataTypeT>(0);
+ }
+ while (corner_id != kInvalidCornerIndex) {
+ if (ComputeParallelogramPrediction(
+ p, corner_id, table, *vertex_to_data_map, in_data, num_components,
+ parallelogram_pred_vals.get())) {
+ for (int c = 0; c < num_components; ++c) {
+ pred_vals[c] += parallelogram_pred_vals[c];
+ }
+ ++num_parallelograms;
+ }
+
+ // Proceed to the next corner attached to the vertex.
+ corner_id = table->SwingRight(corner_id);
+ if (corner_id == start_corner_id) {
+ corner_id = kInvalidCornerIndex;
+ }
+ }
+ const int dst_offset = p * num_components;
+ if (num_parallelograms == 0) {
+ // No parallelogram was valid.
+ // We use the last encoded point as a reference.
+ const int src_offset = (p - 1) * num_components;
+ this->transform().ComputeCorrection(
+ in_data + dst_offset, in_data + src_offset, out_corr + dst_offset);
+ } else {
+ // Compute the correction from the predicted value.
+ for (int c = 0; c < num_components; ++c) {
+ pred_vals[c] /= num_parallelograms;
+ }
+ this->transform().ComputeCorrection(in_data + dst_offset, pred_vals.get(),
+ out_corr + dst_offset);
+ }
+ }
+ // First element is always fixed because it cannot be predicted.
+ for (int i = 0; i < num_components; ++i) {
+ pred_vals[i] = static_cast<DataTypeT>(0);
+ }
+ this->transform().ComputeCorrection(in_data, pred_vals.get(), out_corr);
+ return true;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_MULTI_PARALLELOGRAM_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_decoder.h
new file mode 100644
index 00000000000..4d47ddf306e
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_decoder.h
@@ -0,0 +1,98 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_PARALLELOGRAM_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_PARALLELOGRAM_DECODER_H_
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h"
+
+namespace draco {
+
+// Decoder for attribute values encoded with the standard parallelogram
+// prediction. See the description of the corresponding encoder for more
+// details.
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeParallelogramDecoder
+ : public MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT> {
+ public:
+ using CorrType =
+ typename PredictionSchemeDecoder<DataTypeT, TransformT>::CorrType;
+ using CornerTable = typename MeshDataT::CornerTable;
+ explicit MeshPredictionSchemeParallelogramDecoder(
+ const PointAttribute *attribute)
+ : MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT>(
+ attribute) {}
+ MeshPredictionSchemeParallelogramDecoder(const PointAttribute *attribute,
+ const TransformT &transform,
+ const MeshDataT &mesh_data)
+ : MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT>(
+ attribute, transform, mesh_data) {}
+
+ bool ComputeOriginalValues(const CorrType *in_corr, DataTypeT *out_data,
+ int size, int num_components,
+ const PointIndex *entry_to_point_id_map) override;
+ PredictionSchemeMethod GetPredictionMethod() const override {
+ return MESH_PREDICTION_PARALLELOGRAM;
+ }
+
+ bool IsInitialized() const override {
+ return this->mesh_data().IsInitialized();
+ }
+};
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeParallelogramDecoder<DataTypeT, TransformT,
+ MeshDataT>::
+ ComputeOriginalValues(const CorrType *in_corr, DataTypeT *out_data,
+ int /* size */, int num_components,
+ const PointIndex * /* entry_to_point_id_map */) {
+ this->transform().Init(num_components);
+
+ const CornerTable *const table = this->mesh_data().corner_table();
+ const std::vector<int32_t> *const vertex_to_data_map =
+ this->mesh_data().vertex_to_data_map();
+
+ // For storage of prediction values (already initialized to zero).
+ std::unique_ptr<DataTypeT[]> pred_vals(new DataTypeT[num_components]());
+
+ // Restore the first value.
+ this->transform().ComputeOriginalValue(pred_vals.get(), in_corr, out_data);
+
+ const int corner_map_size =
+ static_cast<int>(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);
+ const int dst_offset = p * num_components;
+ if (!ComputeParallelogramPrediction(p, corner_id, table,
+ *vertex_to_data_map, out_data,
+ num_components, pred_vals.get())) {
+ // Parallelogram could not be computed, Possible because some of the
+ // vertices are not valid (not encoded yet).
+ // We use the last encoded point as a reference (delta coding).
+ const int src_offset = (p - 1) * num_components;
+ this->transform().ComputeOriginalValue(
+ out_data + src_offset, in_corr + dst_offset, out_data + dst_offset);
+ } else {
+ // Apply the parallelogram prediction.
+ this->transform().ComputeOriginalValue(
+ pred_vals.get(), in_corr + dst_offset, out_data + dst_offset);
+ }
+ }
+ return true;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_PARALLELOGRAM_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_encoder.h
new file mode 100644
index 00000000000..f00801932c2
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_encoder.h
@@ -0,0 +1,111 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_PARALLELOGRAM_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_PARALLELOGRAM_ENCODER_H_
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_encoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h"
+
+namespace draco {
+
+// Parallelogram prediction predicts an attribute value V from three vertices
+// on the opposite face to the predicted vertex. The values on the three
+// vertices are used to construct a parallelogram V' = O - A - B, where O is the
+// value on the opposite vertex, and A, B are values on the shared vertices:
+// V
+// / \
+// / \
+// / \
+// A-------B
+// \ /
+// \ /
+// \ /
+// O
+//
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeParallelogramEncoder
+ : public MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT> {
+ public:
+ using CorrType =
+ typename PredictionSchemeEncoder<DataTypeT, TransformT>::CorrType;
+ using CornerTable = typename MeshDataT::CornerTable;
+ explicit MeshPredictionSchemeParallelogramEncoder(
+ const PointAttribute *attribute)
+ : MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT>(
+ attribute) {}
+ MeshPredictionSchemeParallelogramEncoder(const PointAttribute *attribute,
+ const TransformT &transform,
+ const MeshDataT &mesh_data)
+ : MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT>(
+ attribute, transform, mesh_data) {}
+
+ bool ComputeCorrectionValues(
+ const DataTypeT *in_data, CorrType *out_corr, int size,
+ int num_components, const PointIndex *entry_to_point_id_map) override;
+ PredictionSchemeMethod GetPredictionMethod() const override {
+ return MESH_PREDICTION_PARALLELOGRAM;
+ }
+
+ bool IsInitialized() const override {
+ return this->mesh_data().IsInitialized();
+ }
+};
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeParallelogramEncoder<DataTypeT, TransformT,
+ MeshDataT>::
+ ComputeCorrectionValues(const DataTypeT *in_data, CorrType *out_corr,
+ int size, int num_components,
+ const PointIndex * /* entry_to_point_id_map */) {
+ this->transform().Init(in_data, size, num_components);
+ // For storage of prediction values (already initialized to zero).
+ std::unique_ptr<DataTypeT[]> pred_vals(new DataTypeT[num_components]());
+
+ // We start processing from the end because this prediction uses data from
+ // previous entries that could be overwritten when an entry is processed.
+ const CornerTable *const table = this->mesh_data().corner_table();
+ const std::vector<int32_t> *const vertex_to_data_map =
+ this->mesh_data().vertex_to_data_map();
+ for (int p =
+ static_cast<int>(this->mesh_data().data_to_corner_map()->size() - 1);
+ p > 0; --p) {
+ const CornerIndex corner_id = this->mesh_data().data_to_corner_map()->at(p);
+ const int dst_offset = p * num_components;
+ if (!ComputeParallelogramPrediction(p, corner_id, table,
+ *vertex_to_data_map, in_data,
+ num_components, pred_vals.get())) {
+ // Parallelogram could not be computed, Possible because some of the
+ // vertices are not valid (not encoded yet).
+ // We use the last encoded point as a reference (delta coding).
+ const int src_offset = (p - 1) * num_components;
+ this->transform().ComputeCorrection(
+ in_data + dst_offset, in_data + src_offset, out_corr + dst_offset);
+ } else {
+ // Apply the parallelogram prediction.
+ this->transform().ComputeCorrection(in_data + dst_offset, pred_vals.get(),
+ out_corr + dst_offset);
+ }
+ }
+ // First element is always fixed because it cannot be predicted.
+ for (int i = 0; i < num_components; ++i) {
+ pred_vals[i] = static_cast<DataTypeT>(0);
+ }
+ this->transform().ComputeCorrection(in_data, pred_vals.get(), out_corr);
+ return true;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_PARALLELOGRAM_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h
new file mode 100644
index 00000000000..c63c8d05ba6
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_shared.h
@@ -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.
+//
+// Shared functionality for different parallelogram prediction schemes.
+
+#ifndef DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_PARALLELOGRAM_SHARED_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_PARALLELOGRAM_SHARED_H_
+
+#include "draco/mesh/corner_table.h"
+#include "draco/mesh/mesh.h"
+
+namespace draco {
+
+// TODO(draco-eng) consolidate Vertex/next/previous queries to one call
+// (performance).
+template <class CornerTableT>
+inline void GetParallelogramEntries(
+ const CornerIndex ci, const CornerTableT *table,
+ const std::vector<int32_t> &vertex_to_data_map, int *opp_entry,
+ int *next_entry, int *prev_entry) {
+ // One vertex of the input |table| correspond to exactly one attribute value
+ // entry. The |table| can be either CornerTable for per-vertex attributes,
+ // or MeshAttributeCornerTable for attributes with interior seams.
+ *opp_entry = vertex_to_data_map[table->Vertex(ci).value()];
+ *next_entry = vertex_to_data_map[table->Vertex(table->Next(ci)).value()];
+ *prev_entry = vertex_to_data_map[table->Vertex(table->Previous(ci)).value()];
+}
+
+// Computes parallelogram prediction for a given corner and data entry id.
+// The prediction is stored in |out_prediction|.
+// Function returns false when the prediction couldn't be computed, e.g. because
+// not all entry points were available.
+template <class CornerTableT, typename DataTypeT>
+inline bool ComputeParallelogramPrediction(
+ int data_entry_id, const CornerIndex ci, const CornerTableT *table,
+ const std::vector<int32_t> &vertex_to_data_map, const DataTypeT *in_data,
+ int num_components, DataTypeT *out_prediction) {
+ const CornerIndex oci = table->Opposite(ci);
+ if (oci == kInvalidCornerIndex)
+ return false;
+ int vert_opp, vert_next, vert_prev;
+ GetParallelogramEntries<CornerTableT>(oci, table, vertex_to_data_map,
+ &vert_opp, &vert_next, &vert_prev);
+ if (vert_opp < data_entry_id && vert_next < data_entry_id &&
+ vert_prev < data_entry_id) {
+ // Apply the parallelogram prediction.
+ const int v_opp_off = vert_opp * num_components;
+ const int v_next_off = vert_next * num_components;
+ const int v_prev_off = vert_prev * num_components;
+ for (int c = 0; c < num_components; ++c) {
+ out_prediction[c] = (in_data[v_next_off + c] + in_data[v_prev_off + c]) -
+ in_data[v_opp_off + c];
+ }
+ return true;
+ }
+ return false; // Not all data is available for prediction
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_PARALLELOGRAM_SHARED_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_decoder.h
new file mode 100644
index 00000000000..2e389b2a2b4
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_decoder.h
@@ -0,0 +1,335 @@
+// 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.
+//
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+#ifndef DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_DECODER_H_
+
+#include <math.h>
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h"
+#include "draco/compression/bit_coders/rans_bit_decoder.h"
+#include "draco/core/varint_decoding.h"
+#include "draco/core/vector_d.h"
+#include "draco/mesh/corner_table.h"
+
+namespace draco {
+
+// Decoder for predictions of UV coordinates encoded by our specialized texture
+// coordinate predictor. See the corresponding encoder for more details. Note
+// that this predictor is not portable and should not be used anymore. See
+// MeshPredictionSchemeTexCoordsPortableEncoder/Decoder for a portable version
+// of this prediction scheme.
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeTexCoordsDecoder
+ : public MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT> {
+ public:
+ using CorrType = typename MeshPredictionSchemeDecoder<DataTypeT, TransformT,
+ MeshDataT>::CorrType;
+ MeshPredictionSchemeTexCoordsDecoder(const PointAttribute *attribute,
+ const TransformT &transform,
+ const MeshDataT &mesh_data, int version)
+ : MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT>(
+ attribute, transform, mesh_data),
+ pos_attribute_(nullptr),
+ entry_to_point_id_map_(nullptr),
+ num_components_(0),
+ version_(version) {}
+
+ bool ComputeOriginalValues(const CorrType *in_corr, DataTypeT *out_data,
+ int size, int num_components,
+ const PointIndex *entry_to_point_id_map) override;
+
+ bool DecodePredictionData(DecoderBuffer *buffer) override;
+
+ PredictionSchemeMethod GetPredictionMethod() const override {
+ return MESH_PREDICTION_TEX_COORDS_DEPRECATED;
+ }
+
+ bool IsInitialized() const override {
+ if (pos_attribute_ == nullptr)
+ return false;
+ if (!this->mesh_data().IsInitialized())
+ return false;
+ return true;
+ }
+
+ int GetNumParentAttributes() const override { return 1; }
+
+ GeometryAttribute::Type GetParentAttributeType(int i) const override {
+ DRACO_DCHECK_EQ(i, 0);
+ (void)i;
+ return GeometryAttribute::POSITION;
+ }
+
+ bool SetParentAttribute(const PointAttribute *att) override {
+ if (att == nullptr)
+ return false;
+ if (att->attribute_type() != GeometryAttribute::POSITION)
+ return false; // Invalid attribute type.
+ if (att->num_components() != 3)
+ return false; // Currently works only for 3 component positions.
+ pos_attribute_ = att;
+ return true;
+ }
+
+ protected:
+ Vector3f GetPositionForEntryId(int entry_id) const {
+ const PointIndex point_id = entry_to_point_id_map_[entry_id];
+ Vector3f pos;
+ pos_attribute_->ConvertValue(pos_attribute_->mapped_index(point_id),
+ &pos[0]);
+ return pos;
+ }
+
+ Vector2f GetTexCoordForEntryId(int entry_id, const DataTypeT *data) const {
+ const int data_offset = entry_id * num_components_;
+ return Vector2f(static_cast<float>(data[data_offset]),
+ static_cast<float>(data[data_offset + 1]));
+ }
+
+ void ComputePredictedValue(CornerIndex corner_id, const DataTypeT *data,
+ int data_id);
+
+ private:
+ const PointAttribute *pos_attribute_;
+ const PointIndex *entry_to_point_id_map_;
+ std::unique_ptr<DataTypeT[]> predicted_value_;
+ int num_components_;
+ // Encoded / decoded array of UV flips.
+ std::vector<bool> orientations_;
+ int version_;
+};
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeTexCoordsDecoder<DataTypeT, TransformT, MeshDataT>::
+ ComputeOriginalValues(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<DataTypeT[]>(new DataTypeT[num_components]);
+ this->transform().Init(num_components);
+
+ const int corner_map_size =
+ static_cast<int>(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);
+
+ const int dst_offset = p * num_components;
+ this->transform().ComputeOriginalValue(
+ predicted_value_.get(), in_corr + dst_offset, out_data + dst_offset);
+ }
+ return true;
+}
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeTexCoordsDecoder<DataTypeT, TransformT, MeshDataT>::
+ DecodePredictionData(DecoderBuffer *buffer) {
+ // Decode the delta coded orientations.
+ uint32_t num_orientations = 0;
+ if (buffer->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ if (!buffer->Decode(&num_orientations))
+ return false;
+ } else {
+ if (!DecodeVarint(&num_orientations, buffer))
+ return false;
+ }
+ if (num_orientations == 0)
+ return false;
+ orientations_.resize(num_orientations);
+ bool last_orientation = true;
+ RAnsBitDecoder decoder;
+ if (!decoder.StartDecoding(buffer))
+ return false;
+ for (uint32_t i = 0; i < num_orientations; ++i) {
+ if (!decoder.DecodeNextBit())
+ last_orientation = !last_orientation;
+ orientations_[i] = last_orientation;
+ }
+ decoder.EndDecoding();
+ return MeshPredictionSchemeDecoder<DataTypeT, TransformT,
+ MeshDataT>::DecodePredictionData(buffer);
+}
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+void MeshPredictionSchemeTexCoordsDecoder<DataTypeT, TransformT, MeshDataT>::
+ ComputePredictedValue(CornerIndex corner_id, const DataTypeT *data,
+ int data_id) {
+ // Compute the predicted UV coordinate from the positions on all corners
+ // of the processed triangle. For the best prediction, the UV coordinates
+ // on the next/previous corners need to be already encoded/decoded.
+ const CornerIndex next_corner_id =
+ this->mesh_data().corner_table()->Next(corner_id);
+ const CornerIndex prev_corner_id =
+ this->mesh_data().corner_table()->Previous(corner_id);
+ // Get the encoded data ids from the next and previous corners.
+ // The data id is the encoding order of the UV coordinates.
+ int next_data_id, prev_data_id;
+
+ int next_vert_id, prev_vert_id;
+ next_vert_id =
+ this->mesh_data().corner_table()->Vertex(next_corner_id).value();
+ prev_vert_id =
+ this->mesh_data().corner_table()->Vertex(prev_corner_id).value();
+
+ next_data_id = this->mesh_data().vertex_to_data_map()->at(next_vert_id);
+ prev_data_id = this->mesh_data().vertex_to_data_map()->at(prev_vert_id);
+
+ if (prev_data_id < data_id && next_data_id < data_id) {
+ // Both other corners have available UV coordinates for prediction.
+ const Vector2f n_uv = GetTexCoordForEntryId(next_data_id, data);
+ 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] = static_cast<int>(p_uv[0]);
+ predicted_value_[1] = static_cast<int>(p_uv[1]);
+ return;
+ }
+
+ // Get positions at all corners.
+ const Vector3f tip_pos = GetPositionForEntryId(data_id);
+ const Vector3f next_pos = GetPositionForEntryId(next_data_id);
+ const Vector3f prev_pos = GetPositionForEntryId(prev_data_id);
+ // Use the positions of the above triangle to predict the texture coordinate
+ // on the tip corner C.
+ // Convert the triangle into a new coordinate system defined by orthogonal
+ // bases vectors S, T, where S is vector prev_pos - next_pos and T is an
+ // perpendicular vector to S in the same plane as vector the
+ // tip_pos - next_pos.
+ // The transformed triangle in the new coordinate system is then going to
+ // be represented as:
+ //
+ // 1 ^
+ // |
+ // |
+ // | C
+ // | / \
+ // | / \
+ // |/ \
+ // N--------------P
+ // 0 1
+ //
+ // Where next_pos point (N) is at position (0, 0), prev_pos point (P) is
+ // at (1, 0). Our goal is to compute the position of the tip_pos point (C)
+ // in this new coordinate space (s, t).
+ //
+ const Vector3f pn = prev_pos - next_pos;
+ const Vector3f cn = tip_pos - next_pos;
+ const float pn_norm2_squared = pn.SquaredNorm();
+ // Coordinate s of the tip corner C is simply the dot product of the
+ // normalized vectors |pn| and |cn| (normalized by the length of |pn|).
+ // Since both of these vectors are normalized, we don't need to perform the
+ // normalization explicitly and instead we can just use the squared norm
+ // of |pn| as a denominator of the resulting dot product of non normalized
+ // vectors.
+ float s, t;
+ // |pn_norm2_squared| can be exactly 0 when the next_pos and prev_pos are
+ // the same positions (e.g. because they were quantized to the same
+ // location).
+ if (version_ < DRACO_BITSTREAM_VERSION(1, 2) || pn_norm2_squared > 0) {
+ s = pn.Dot(cn) / pn_norm2_squared;
+ // To get the coordinate t, we can use formula:
+ // t = |C-N - (P-N) * s| / |P-N|
+ // Do not use std::sqrt to avoid changes in the bitstream.
+ t = sqrt((cn - pn * s).SquaredNorm() / pn_norm2_squared);
+ } else {
+ s = 0;
+ t = 0;
+ }
+
+ // Now we need to transform the point (s, t) to the texture coordinate space
+ // UV. We know the UV coordinates on points N and P (N_UV and P_UV). Lets
+ // denote P_UV - N_UV = PN_UV. PN_UV is then 2 dimensional vector that can
+ // be used to define transformation from the normalized coordinate system
+ // to the texture coordinate system using a 3x3 affine matrix M:
+ //
+ // M = | PN_UV[0] -PN_UV[1] N_UV[0] |
+ // | PN_UV[1] PN_UV[0] N_UV[1] |
+ // | 0 0 1 |
+ //
+ // The predicted point C_UV in the texture space is then equal to
+ // C_UV = M * (s, t, 1). Because the triangle in UV space may be flipped
+ // around the PN_UV axis, we also need to consider point C_UV' = M * (s, -t)
+ // as the prediction.
+ const Vector2f pn_uv = p_uv - n_uv;
+ const float pnus = pn_uv[0] * s + n_uv[0];
+ const float pnut = pn_uv[0] * t;
+ const float pnvs = pn_uv[1] * s + n_uv[1];
+ const float pnvt = pn_uv[1] * t;
+ Vector2f predicted_uv;
+
+ // When decoding the data, we already know which orientation to use.
+ const bool orientation = orientations_.back();
+ orientations_.pop_back();
+ if (orientation)
+ predicted_uv = Vector2f(pnus - pnvt, pnvs + pnut);
+ else
+ predicted_uv = Vector2f(pnus + pnvt, pnvs - pnut);
+
+ if (std::is_integral<DataTypeT>::value) {
+ // Round the predicted value for integer types.
+ if (std::isnan(predicted_uv[0])) {
+ predicted_value_[0] = INT_MIN;
+ } else {
+ predicted_value_[0] = static_cast<int>(floor(predicted_uv[0] + 0.5));
+ }
+ if (std::isnan(predicted_uv[1])) {
+ predicted_value_[1] = INT_MIN;
+ } else {
+ predicted_value_[1] = static_cast<int>(floor(predicted_uv[1] + 0.5));
+ }
+ } else {
+ predicted_value_[0] = static_cast<int>(predicted_uv[0]);
+ predicted_value_[1] = static_cast<int>(predicted_uv[1]);
+ }
+ return;
+ }
+ // Else we don't have available textures on both corners. For such case we
+ // can't use positions for predicting the uv value and we resort to delta
+ // coding.
+ int data_offset = 0;
+ if (prev_data_id < data_id) {
+ // Use the value on the previous corner as the prediction.
+ data_offset = prev_data_id * num_components_;
+ }
+ if (next_data_id < data_id) {
+ // Use the value on the next corner as the prediction.
+ data_offset = next_data_id * num_components_;
+ } else {
+ // None of the other corners have a valid value. Use the last encoded value
+ // as the prediction if possible.
+ if (data_id > 0) {
+ data_offset = (data_id - 1) * num_components_;
+ } else {
+ // We are encoding the first value. Predict 0.
+ for (int i = 0; i < num_components_; ++i) {
+ predicted_value_[i] = 0;
+ }
+ return;
+ }
+ }
+ for (int i = 0; i < num_components_; ++i) {
+ predicted_value_[i] = data[data_offset + i];
+ }
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_DECODER_H_
+#endif
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_encoder.h
new file mode 100644
index 00000000000..0e938b9c388
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_encoder.h
@@ -0,0 +1,313 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_ENCODER_H_
+
+#include <math.h>
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_encoder.h"
+#include "draco/compression/bit_coders/rans_bit_encoder.h"
+#include "draco/core/varint_encoding.h"
+#include "draco/core/vector_d.h"
+#include "draco/mesh/corner_table.h"
+
+namespace draco {
+
+// Prediction scheme designed for predicting texture coordinates from known
+// spatial position of vertices. For good parametrization, the ratios between
+// triangle edge lengths should be about the same in both the spatial and UV
+// coordinate spaces, which makes the positions a good predictor for the UV
+// coordinates.
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeTexCoordsEncoder
+ : public MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT> {
+ public:
+ using CorrType = typename MeshPredictionSchemeEncoder<DataTypeT, TransformT,
+ MeshDataT>::CorrType;
+ MeshPredictionSchemeTexCoordsEncoder(const PointAttribute *attribute,
+ const TransformT &transform,
+ const MeshDataT &mesh_data)
+ : MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT>(
+ attribute, transform, mesh_data),
+ pos_attribute_(nullptr),
+ entry_to_point_id_map_(nullptr),
+ num_components_(0) {}
+
+ bool ComputeCorrectionValues(
+ const DataTypeT *in_data, CorrType *out_corr, int size,
+ int num_components, const PointIndex *entry_to_point_id_map) override;
+
+ bool EncodePredictionData(EncoderBuffer *buffer) override;
+
+ PredictionSchemeMethod GetPredictionMethod() const override {
+ return MESH_PREDICTION_TEX_COORDS_DEPRECATED;
+ }
+
+ bool IsInitialized() const override {
+ if (pos_attribute_ == nullptr)
+ return false;
+ if (!this->mesh_data().IsInitialized())
+ return false;
+ return true;
+ }
+
+ int GetNumParentAttributes() const override { return 1; }
+
+ GeometryAttribute::Type GetParentAttributeType(int i) const override {
+ DRACO_DCHECK_EQ(i, 0);
+ (void)i;
+ return GeometryAttribute::POSITION;
+ }
+
+ bool SetParentAttribute(const PointAttribute *att) override {
+ if (att->attribute_type() != GeometryAttribute::POSITION)
+ return false; // Invalid attribute type.
+ if (att->num_components() != 3)
+ return false; // Currently works only for 3 component positions.
+ pos_attribute_ = att;
+ return true;
+ }
+
+ protected:
+ Vector3f GetPositionForEntryId(int entry_id) const {
+ const PointIndex point_id = entry_to_point_id_map_[entry_id];
+ Vector3f pos;
+ pos_attribute_->ConvertValue(pos_attribute_->mapped_index(point_id),
+ &pos[0]);
+ return pos;
+ }
+
+ Vector2f GetTexCoordForEntryId(int entry_id, const DataTypeT *data) const {
+ const int data_offset = entry_id * num_components_;
+ return Vector2f(static_cast<float>(data[data_offset]),
+ static_cast<float>(data[data_offset + 1]));
+ }
+
+ void ComputePredictedValue(CornerIndex corner_id, const DataTypeT *data,
+ int data_id);
+
+ private:
+ const PointAttribute *pos_attribute_;
+ const PointIndex *entry_to_point_id_map_;
+ std::unique_ptr<DataTypeT[]> predicted_value_;
+ int num_components_;
+ // Encoded / decoded array of UV flips.
+ std::vector<bool> orientations_;
+};
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeTexCoordsEncoder<DataTypeT, TransformT, MeshDataT>::
+ ComputeCorrectionValues(const DataTypeT *in_data, CorrType *out_corr,
+ 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<DataTypeT[]>(new DataTypeT[num_components]);
+ this->transform().Init(in_data, size, num_components);
+ // We start processing from the end because this prediction uses data from
+ // previous entries that could be overwritten when an entry is processed.
+ for (int p =
+ static_cast<int>(this->mesh_data().data_to_corner_map()->size()) - 1;
+ p >= 0; --p) {
+ const CornerIndex corner_id = this->mesh_data().data_to_corner_map()->at(p);
+ ComputePredictedValue(corner_id, in_data, p);
+
+ const int dst_offset = p * num_components;
+ this->transform().ComputeCorrection(
+ in_data + dst_offset, predicted_value_.get(), out_corr + dst_offset);
+ }
+ return true;
+}
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeTexCoordsEncoder<DataTypeT, TransformT, MeshDataT>::
+ EncodePredictionData(EncoderBuffer *buffer) {
+ // Encode the delta-coded orientations using arithmetic coding.
+ const uint32_t num_orientations = static_cast<uint32_t>(orientations_.size());
+ EncodeVarint(num_orientations, buffer);
+ bool last_orientation = true;
+ RAnsBitEncoder encoder;
+ encoder.StartEncoding();
+ for (bool orientation : orientations_) {
+ encoder.EncodeBit(orientation == last_orientation);
+ last_orientation = orientation;
+ }
+ encoder.EndEncoding(buffer);
+ return MeshPredictionSchemeEncoder<DataTypeT, TransformT,
+ MeshDataT>::EncodePredictionData(buffer);
+}
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+void MeshPredictionSchemeTexCoordsEncoder<DataTypeT, TransformT, MeshDataT>::
+ ComputePredictedValue(CornerIndex corner_id, const DataTypeT *data,
+ int data_id) {
+ // Compute the predicted UV coordinate from the positions on all corners
+ // of the processed triangle. For the best prediction, the UV coordinates
+ // on the next/previous corners need to be already encoded/decoded.
+ const CornerIndex next_corner_id =
+ this->mesh_data().corner_table()->Next(corner_id);
+ const CornerIndex prev_corner_id =
+ this->mesh_data().corner_table()->Previous(corner_id);
+ // Get the encoded data ids from the next and previous corners.
+ // The data id is the encoding order of the UV coordinates.
+ int next_data_id, prev_data_id;
+
+ int next_vert_id, prev_vert_id;
+ next_vert_id =
+ this->mesh_data().corner_table()->Vertex(next_corner_id).value();
+ prev_vert_id =
+ this->mesh_data().corner_table()->Vertex(prev_corner_id).value();
+
+ next_data_id = this->mesh_data().vertex_to_data_map()->at(next_vert_id);
+ prev_data_id = this->mesh_data().vertex_to_data_map()->at(prev_vert_id);
+
+ if (prev_data_id < data_id && next_data_id < data_id) {
+ // Both other corners have available UV coordinates for prediction.
+ const Vector2f n_uv = GetTexCoordForEntryId(next_data_id, data);
+ 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] = static_cast<int>(p_uv[0]);
+ predicted_value_[1] = static_cast<int>(p_uv[1]);
+ return;
+ }
+
+ // Get positions at all corners.
+ const Vector3f tip_pos = GetPositionForEntryId(data_id);
+ const Vector3f next_pos = GetPositionForEntryId(next_data_id);
+ const Vector3f prev_pos = GetPositionForEntryId(prev_data_id);
+ // Use the positions of the above triangle to predict the texture coordinate
+ // on the tip corner C.
+ // Convert the triangle into a new coordinate system defined by orthogonal
+ // bases vectors S, T, where S is vector prev_pos - next_pos and T is an
+ // perpendicular vector to S in the same plane as vector the
+ // tip_pos - next_pos.
+ // The transformed triangle in the new coordinate system is then going to
+ // be represented as:
+ //
+ // 1 ^
+ // |
+ // |
+ // | C
+ // | / \
+ // | / \
+ // |/ \
+ // N--------------P
+ // 0 1
+ //
+ // Where next_pos point (N) is at position (0, 0), prev_pos point (P) is
+ // at (1, 0). Our goal is to compute the position of the tip_pos point (C)
+ // in this new coordinate space (s, t).
+ //
+ const Vector3f pn = prev_pos - next_pos;
+ const Vector3f cn = tip_pos - next_pos;
+ const float pn_norm2_squared = pn.SquaredNorm();
+ // Coordinate s of the tip corner C is simply the dot product of the
+ // normalized vectors |pn| and |cn| (normalized by the length of |pn|).
+ // Since both of these vectors are normalized, we don't need to perform the
+ // normalization explicitly and instead we can just use the squared norm
+ // of |pn| as a denominator of the resulting dot product of non normalized
+ // vectors.
+ float s, t;
+ // |pn_norm2_squared| can be exactly 0 when the next_pos and prev_pos are
+ // the same positions (e.g. because they were quantized to the same
+ // location).
+ if (pn_norm2_squared > 0) {
+ s = pn.Dot(cn) / pn_norm2_squared;
+ // To get the coordinate t, we can use formula:
+ // t = |C-N - (P-N) * s| / |P-N|
+ // Do not use std::sqrt to avoid changes in the bitstream.
+ t = sqrt((cn - pn * s).SquaredNorm() / pn_norm2_squared);
+ } else {
+ s = 0;
+ t = 0;
+ }
+
+ // Now we need to transform the point (s, t) to the texture coordinate space
+ // UV. We know the UV coordinates on points N and P (N_UV and P_UV). Lets
+ // denote P_UV - N_UV = PN_UV. PN_UV is then 2 dimensional vector that can
+ // be used to define transformation from the normalized coordinate system
+ // to the texture coordinate system using a 3x3 affine matrix M:
+ //
+ // M = | PN_UV[0] -PN_UV[1] N_UV[0] |
+ // | PN_UV[1] PN_UV[0] N_UV[1] |
+ // | 0 0 1 |
+ //
+ // The predicted point C_UV in the texture space is then equal to
+ // C_UV = M * (s, t, 1). Because the triangle in UV space may be flipped
+ // around the PN_UV axis, we also need to consider point C_UV' = M * (s, -t)
+ // as the prediction.
+ const Vector2f pn_uv = p_uv - n_uv;
+ const float pnus = pn_uv[0] * s + n_uv[0];
+ const float pnut = pn_uv[0] * t;
+ const float pnvs = pn_uv[1] * s + n_uv[1];
+ const float pnvt = pn_uv[1] * t;
+ Vector2f predicted_uv;
+
+ // When encoding compute both possible vectors and determine which one
+ // results in a better prediction.
+ const Vector2f predicted_uv_0(pnus - pnvt, pnvs + pnut);
+ const Vector2f predicted_uv_1(pnus + pnvt, pnvs - pnut);
+ const Vector2f c_uv = GetTexCoordForEntryId(data_id, data);
+ if ((c_uv - predicted_uv_0).SquaredNorm() <
+ (c_uv - predicted_uv_1).SquaredNorm()) {
+ predicted_uv = predicted_uv_0;
+ orientations_.push_back(true);
+ } else {
+ predicted_uv = predicted_uv_1;
+ orientations_.push_back(false);
+ }
+ if (std::is_integral<DataTypeT>::value) {
+ // Round the predicted value for integer types.
+ predicted_value_[0] = static_cast<int>(floor(predicted_uv[0] + 0.5));
+ predicted_value_[1] = static_cast<int>(floor(predicted_uv[1] + 0.5));
+ } else {
+ predicted_value_[0] = static_cast<int>(predicted_uv[0]);
+ predicted_value_[1] = static_cast<int>(predicted_uv[1]);
+ }
+ return;
+ }
+ // Else we don't have available textures on both corners. For such case we
+ // can't use positions for predicting the uv value and we resort to delta
+ // coding.
+ int data_offset = 0;
+ if (prev_data_id < data_id) {
+ // Use the value on the previous corner as the prediction.
+ data_offset = prev_data_id * num_components_;
+ }
+ if (next_data_id < data_id) {
+ // Use the value on the next corner as the prediction.
+ data_offset = next_data_id * num_components_;
+ } else {
+ // None of the other corners have a valid value. Use the last encoded value
+ // as the prediction if possible.
+ if (data_id > 0) {
+ data_offset = (data_id - 1) * num_components_;
+ } else {
+ // We are encoding the first value. Predict 0.
+ for (int i = 0; i < num_components_; ++i) {
+ predicted_value_[i] = 0;
+ }
+ return;
+ }
+ }
+ for (int i = 0; i < num_components_; ++i) {
+ predicted_value_[i] = data[data_offset + i];
+ }
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_decoder.h
new file mode 100644
index 00000000000..0fee0ceb6e2
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_decoder.h
@@ -0,0 +1,131 @@
+// Copyright 2017 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_PORTABLE_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_PORTABLE_DECODER_H_
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_decoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h"
+#include "draco/compression/bit_coders/rans_bit_decoder.h"
+
+namespace draco {
+
+// Decoder for predictions of UV coordinates encoded by our specialized and
+// portable texture coordinate predictor. See the corresponding encoder for more
+// details.
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeTexCoordsPortableDecoder
+ : public MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT> {
+ public:
+ using CorrType = typename MeshPredictionSchemeDecoder<DataTypeT, TransformT,
+ MeshDataT>::CorrType;
+ MeshPredictionSchemeTexCoordsPortableDecoder(const PointAttribute *attribute,
+ const TransformT &transform,
+ const MeshDataT &mesh_data)
+ : MeshPredictionSchemeDecoder<DataTypeT, TransformT, MeshDataT>(
+ attribute, transform, mesh_data),
+ predictor_(mesh_data) {}
+
+ bool ComputeOriginalValues(const CorrType *in_corr, DataTypeT *out_data,
+ int size, int num_components,
+ const PointIndex *entry_to_point_id_map) override;
+
+ bool DecodePredictionData(DecoderBuffer *buffer) override;
+
+ PredictionSchemeMethod GetPredictionMethod() const override {
+ return MESH_PREDICTION_TEX_COORDS_PORTABLE;
+ }
+
+ bool IsInitialized() const override {
+ if (!predictor_.IsInitialized())
+ return false;
+ if (!this->mesh_data().IsInitialized())
+ return false;
+ return true;
+ }
+
+ int GetNumParentAttributes() const override { return 1; }
+
+ GeometryAttribute::Type GetParentAttributeType(int i) const override {
+ DRACO_DCHECK_EQ(i, 0);
+ (void)i;
+ return GeometryAttribute::POSITION;
+ }
+
+ bool SetParentAttribute(const PointAttribute *att) override {
+ if (!att || att->attribute_type() != GeometryAttribute::POSITION)
+ return false; // Invalid attribute type.
+ if (att->num_components() != 3)
+ return false; // Currently works only for 3 component positions.
+ predictor_.SetPositionAttribute(*att);
+ return true;
+ }
+
+ private:
+ MeshPredictionSchemeTexCoordsPortablePredictor<DataTypeT, MeshDataT>
+ predictor_;
+};
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeTexCoordsPortableDecoder<
+ DataTypeT, TransformT,
+ MeshDataT>::ComputeOriginalValues(const CorrType *in_corr,
+ DataTypeT *out_data, int /* size */,
+ int num_components,
+ const PointIndex *entry_to_point_id_map) {
+ predictor_.SetEntryToPointIdMap(entry_to_point_id_map);
+ this->transform().Init(num_components);
+
+ const int corner_map_size =
+ static_cast<int>(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);
+ if (!predictor_.template ComputePredictedValue<false>(corner_id, out_data,
+ p))
+ return false;
+
+ const int dst_offset = p * num_components;
+ this->transform().ComputeOriginalValue(predictor_.predicted_value(),
+ in_corr + dst_offset,
+ out_data + dst_offset);
+ }
+ return true;
+}
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeTexCoordsPortableDecoder<
+ DataTypeT, TransformT, MeshDataT>::DecodePredictionData(DecoderBuffer
+ *buffer) {
+ // Decode the delta coded orientations.
+ int32_t num_orientations = 0;
+ if (!buffer->Decode(&num_orientations) || num_orientations < 0)
+ return false;
+ predictor_.ResizeOrientations(num_orientations);
+ bool last_orientation = true;
+ RAnsBitDecoder decoder;
+ if (!decoder.StartDecoding(buffer))
+ return false;
+ for (int i = 0; i < num_orientations; ++i) {
+ if (!decoder.DecodeNextBit())
+ last_orientation = !last_orientation;
+ predictor_.set_orientation(i, last_orientation);
+ }
+ decoder.EndDecoding();
+ return MeshPredictionSchemeDecoder<DataTypeT, TransformT,
+ MeshDataT>::DecodePredictionData(buffer);
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_PORTABLE_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_encoder.h
new file mode 100644
index 00000000000..04a66325183
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_encoder.h
@@ -0,0 +1,129 @@
+// Copyright 2017 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_PORTABLE_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_PORTABLE_ENCODER_H_
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_encoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h"
+#include "draco/compression/bit_coders/rans_bit_encoder.h"
+
+namespace draco {
+
+// Prediction scheme designed for predicting texture coordinates from known
+// spatial position of vertices. For isometric parametrizations, the ratios
+// between triangle edge lengths should be about the same in both the spatial
+// and UV coordinate spaces, which makes the positions a good predictor for the
+// UV coordinates. Note that this may not be the optimal approach for other
+// parametrizations such as projective ones.
+template <typename DataTypeT, class TransformT, class MeshDataT>
+class MeshPredictionSchemeTexCoordsPortableEncoder
+ : public MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT> {
+ public:
+ using CorrType = typename MeshPredictionSchemeEncoder<DataTypeT, TransformT,
+ MeshDataT>::CorrType;
+ MeshPredictionSchemeTexCoordsPortableEncoder(const PointAttribute *attribute,
+ const TransformT &transform,
+ const MeshDataT &mesh_data)
+ : MeshPredictionSchemeEncoder<DataTypeT, TransformT, MeshDataT>(
+ attribute, transform, mesh_data),
+ predictor_(mesh_data) {}
+
+ bool ComputeCorrectionValues(
+ const DataTypeT *in_data, CorrType *out_corr, int size,
+ int num_components, const PointIndex *entry_to_point_id_map) override;
+
+ bool EncodePredictionData(EncoderBuffer *buffer) override;
+
+ PredictionSchemeMethod GetPredictionMethod() const override {
+ return MESH_PREDICTION_TEX_COORDS_PORTABLE;
+ }
+
+ bool IsInitialized() const override {
+ if (!predictor_.IsInitialized())
+ return false;
+ if (!this->mesh_data().IsInitialized())
+ return false;
+ return true;
+ }
+
+ int GetNumParentAttributes() const override { return 1; }
+
+ GeometryAttribute::Type GetParentAttributeType(int i) const override {
+ DRACO_DCHECK_EQ(i, 0);
+ (void)i;
+ return GeometryAttribute::POSITION;
+ }
+
+ bool SetParentAttribute(const PointAttribute *att) override {
+ if (att->attribute_type() != GeometryAttribute::POSITION)
+ return false; // Invalid attribute type.
+ if (att->num_components() != 3)
+ return false; // Currently works only for 3 component positions.
+ predictor_.SetPositionAttribute(*att);
+ return true;
+ }
+
+ private:
+ MeshPredictionSchemeTexCoordsPortablePredictor<DataTypeT, MeshDataT>
+ predictor_;
+};
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeTexCoordsPortableEncoder<DataTypeT, TransformT,
+ MeshDataT>::
+ ComputeCorrectionValues(const DataTypeT *in_data, CorrType *out_corr,
+ int size, int num_components,
+ const PointIndex *entry_to_point_id_map) {
+ predictor_.SetEntryToPointIdMap(entry_to_point_id_map);
+ this->transform().Init(in_data, size, num_components);
+ // We start processing from the end because this prediction uses data from
+ // previous entries that could be overwritten when an entry is processed.
+ for (int p =
+ static_cast<int>(this->mesh_data().data_to_corner_map()->size() - 1);
+ p >= 0; --p) {
+ const CornerIndex corner_id = this->mesh_data().data_to_corner_map()->at(p);
+ predictor_.template ComputePredictedValue<true>(corner_id, in_data, p);
+
+ const int dst_offset = p * num_components;
+ this->transform().ComputeCorrection(in_data + dst_offset,
+ predictor_.predicted_value(),
+ out_corr + dst_offset);
+ }
+ return true;
+}
+
+template <typename DataTypeT, class TransformT, class MeshDataT>
+bool MeshPredictionSchemeTexCoordsPortableEncoder<
+ DataTypeT, TransformT, MeshDataT>::EncodePredictionData(EncoderBuffer
+ *buffer) {
+ // Encode the delta-coded orientations using arithmetic coding.
+ const int32_t num_orientations = predictor_.num_orientations();
+ buffer->Encode(num_orientations);
+ bool last_orientation = true;
+ RAnsBitEncoder encoder;
+ encoder.StartEncoding();
+ for (int i = 0; i < num_orientations; ++i) {
+ const bool orientation = predictor_.orientation(i);
+ encoder.EncodeBit(orientation == last_orientation);
+ last_orientation = orientation;
+ }
+ encoder.EndEncoding(buffer);
+ return MeshPredictionSchemeEncoder<DataTypeT, TransformT,
+ MeshDataT>::EncodePredictionData(buffer);
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_PORTABLE_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h
new file mode 100644
index 00000000000..f9357712638
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_predictor.h
@@ -0,0 +1,252 @@
+// Copyright 2017 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_PORTABLE_PREDICTOR_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_PORTABLE_PREDICTOR_H_
+
+#include <math.h>
+#include "draco/attributes/point_attribute.h"
+#include "draco/core/math_utils.h"
+#include "draco/core/vector_d.h"
+#include "draco/mesh/corner_table.h"
+
+namespace draco {
+
+// Predictor functionality used for portable UV prediction by both encoder and
+// decoder.
+template <typename DataTypeT, class MeshDataT>
+class MeshPredictionSchemeTexCoordsPortablePredictor {
+ public:
+ explicit MeshPredictionSchemeTexCoordsPortablePredictor(const MeshDataT &md)
+ : pos_attribute_(nullptr),
+ entry_to_point_id_map_(nullptr),
+ mesh_data_(md) {}
+ void SetPositionAttribute(const PointAttribute &position_attribute) {
+ pos_attribute_ = &position_attribute;
+ }
+ void SetEntryToPointIdMap(const PointIndex *map) {
+ entry_to_point_id_map_ = map;
+ }
+ bool IsInitialized() const { return pos_attribute_ != nullptr; }
+
+ VectorD<int64_t, 3> GetPositionForEntryId(int entry_id) const {
+ const PointIndex point_id = entry_to_point_id_map_[entry_id];
+ VectorD<int64_t, 3> pos;
+ pos_attribute_->ConvertValue(pos_attribute_->mapped_index(point_id),
+ &pos[0]);
+ return pos;
+ }
+
+ VectorD<int64_t, 2> GetTexCoordForEntryId(int entry_id,
+ const DataTypeT *data) const {
+ const int data_offset = entry_id * kNumComponents;
+ return VectorD<int64_t, 2>(data[data_offset], data[data_offset + 1]);
+ }
+
+ // Computes predicted UV coordinates on a given corner. The coordinates are
+ // stored in |predicted_value_| member.
+ template <bool is_encoder_t>
+ bool ComputePredictedValue(CornerIndex corner_id, const DataTypeT *data,
+ int data_id);
+
+ const DataTypeT *predicted_value() const { return predicted_value_; }
+ bool orientation(int i) const { return orientations_[i]; }
+ void set_orientation(int i, bool v) { orientations_[i] = v; }
+ size_t num_orientations() const { return orientations_.size(); }
+ void ResizeOrientations(int num_orientations) {
+ orientations_.resize(num_orientations);
+ }
+
+ private:
+ const PointAttribute *pos_attribute_;
+ const PointIndex *entry_to_point_id_map_;
+ static constexpr int kNumComponents = 2;
+ DataTypeT predicted_value_[kNumComponents];
+ // Encoded / decoded array of UV flips.
+ // TODO(ostava): We should remove this and replace this with in-place encoding
+ // and decoding to avoid unnecessary copy.
+ std::vector<bool> orientations_;
+ MeshDataT mesh_data_;
+};
+
+template <typename DataTypeT, class MeshDataT>
+template <bool is_encoder_t>
+bool MeshPredictionSchemeTexCoordsPortablePredictor<
+ DataTypeT, MeshDataT>::ComputePredictedValue(CornerIndex corner_id,
+ const DataTypeT *data,
+ int data_id) {
+ // Compute the predicted UV coordinate from the positions on all corners
+ // of the processed triangle. For the best prediction, the UV coordinates
+ // on the next/previous corners need to be already encoded/decoded.
+ const CornerIndex next_corner_id = mesh_data_.corner_table()->Next(corner_id);
+ const CornerIndex prev_corner_id =
+ mesh_data_.corner_table()->Previous(corner_id);
+ // Get the encoded data ids from the next and previous corners.
+ // The data id is the encoding order of the UV coordinates.
+ int next_data_id, prev_data_id;
+
+ int next_vert_id, prev_vert_id;
+ next_vert_id = mesh_data_.corner_table()->Vertex(next_corner_id).value();
+ prev_vert_id = mesh_data_.corner_table()->Vertex(prev_corner_id).value();
+
+ next_data_id = mesh_data_.vertex_to_data_map()->at(next_vert_id);
+ prev_data_id = mesh_data_.vertex_to_data_map()->at(prev_vert_id);
+
+ if (prev_data_id < data_id && next_data_id < data_id) {
+ // Both other corners have available UV coordinates for prediction.
+ const VectorD<int64_t, 2> n_uv = GetTexCoordForEntryId(next_data_id, data);
+ const VectorD<int64_t, 2> 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];
+ return true;
+ }
+
+ // Get positions at all corners.
+ const VectorD<int64_t, 3> tip_pos = GetPositionForEntryId(data_id);
+ const VectorD<int64_t, 3> next_pos = GetPositionForEntryId(next_data_id);
+ const VectorD<int64_t, 3> prev_pos = GetPositionForEntryId(prev_data_id);
+ // We use the positions of the above triangle to predict the texture
+ // coordinate on the tip corner C.
+ // To convert the triangle into the UV coordinate system we first compute
+ // position X on the vector |prev_pos - next_pos| that is the projection of
+ // point C onto vector |prev_pos - next_pos|:
+ //
+ // C
+ // /. \
+ // / . \
+ // / . \
+ // N---X----------P
+ //
+ // Where next_pos is point (N), prev_pos is point (P) and tip_pos is the
+ // position of predicted coordinate (C).
+ //
+ const VectorD<int64_t, 3> pn = prev_pos - next_pos;
+ const uint64_t pn_norm2_squared = pn.SquaredNorm();
+ if (pn_norm2_squared != 0) {
+ // Compute the projection of C onto PN by computing dot product of CN with
+ // PN and normalizing it by length of PN. This gives us a factor |s| where
+ // |s = PN.Dot(CN) / PN.SquaredNorm2()|. This factor can be used to
+ // compute X in UV space |X_UV| as |X_UV = N_UV + s * PN_UV|.
+ const VectorD<int64_t, 3> cn = tip_pos - next_pos;
+ const int64_t cn_dot_pn = pn.Dot(cn);
+
+ const VectorD<int64_t, 2> pn_uv = p_uv - n_uv;
+ // Because we perform all computations with integers, we don't explicitly
+ // compute the normalized factor |s|, but rather we perform all operations
+ // over UV vectors in a non-normalized coordinate system scaled with a
+ // scaling factor |pn_norm2_squared|:
+ //
+ // x_uv = X_UV * PN.Norm2Squared()
+ //
+ const VectorD<int64_t, 2> x_uv =
+ n_uv * pn_norm2_squared + (cn_dot_pn * pn_uv);
+
+ // Compute squared length of vector CX in position coordinate system:
+ const VectorD<int64_t, 3> x_pos =
+ next_pos + (cn_dot_pn * pn) / pn_norm2_squared;
+ const uint64_t cx_norm2_squared = (tip_pos - x_pos).SquaredNorm();
+
+ // Compute vector CX_UV in the uv space by rotating vector PN_UV by 90
+ // degrees and scaling it with factor CX.Norm2() / PN.Norm2():
+ //
+ // CX_UV = (CX.Norm2() / PN.Norm2()) * Rot(PN_UV)
+ //
+ // To preserve precision, we perform all operations in scaled space as
+ // explained above, so we want the final vector to be:
+ //
+ // cx_uv = CX_UV * PN.Norm2Squared()
+ //
+ // We can then rewrite the formula as:
+ //
+ // cx_uv = CX.Norm2() * PN.Norm2() * Rot(PN_UV)
+ //
+ VectorD<int64_t, 2> cx_uv(pn_uv[1], -pn_uv[0]); // Rotated PN_UV.
+ // Compute CX.Norm2() * PN.Norm2()
+ const uint64_t norm_squared =
+ IntSqrt(cx_norm2_squared * pn_norm2_squared);
+ // Final cx_uv in the scaled coordinate space.
+ cx_uv = cx_uv * norm_squared;
+
+ // Predicted uv coordinate is then computed by either adding or
+ // subtracting CX_UV to/from X_UV.
+ VectorD<int64_t, 2> predicted_uv;
+ if (is_encoder_t) {
+ // When encoding, compute both possible vectors and determine which one
+ // results in a better prediction.
+ // Both vectors need to be transformed back from the scaled space to
+ // the real UV coordinate space.
+ const VectorD<int64_t, 2> predicted_uv_0((x_uv + cx_uv) /
+ pn_norm2_squared);
+ const VectorD<int64_t, 2> predicted_uv_1((x_uv - cx_uv) /
+ pn_norm2_squared);
+ const VectorD<int64_t, 2> c_uv = GetTexCoordForEntryId(data_id, data);
+ if ((c_uv - predicted_uv_0).SquaredNorm() <
+ (c_uv - predicted_uv_1).SquaredNorm()) {
+ predicted_uv = predicted_uv_0;
+ orientations_.push_back(true);
+ } else {
+ predicted_uv = predicted_uv_1;
+ orientations_.push_back(false);
+ }
+ } else {
+ // When decoding the data, we already know which orientation to use.
+ if (orientations_.empty())
+ return false;
+ const bool orientation = orientations_.back();
+ orientations_.pop_back();
+ if (orientation)
+ predicted_uv = (x_uv + cx_uv) / pn_norm2_squared;
+ else
+ predicted_uv = (x_uv - cx_uv) / pn_norm2_squared;
+ }
+ predicted_value_[0] = static_cast<int>(predicted_uv[0]);
+ predicted_value_[1] = static_cast<int>(predicted_uv[1]);
+ return true;
+ }
+ }
+ // Else we don't have available textures on both corners or the position data
+ // is invalid. For such cases we can't use positions for predicting the uv
+ // value and we resort to delta coding.
+ int data_offset = 0;
+ if (prev_data_id < data_id) {
+ // Use the value on the previous corner as the prediction.
+ data_offset = prev_data_id * kNumComponents;
+ }
+ if (next_data_id < data_id) {
+ // Use the value on the next corner as the prediction.
+ data_offset = next_data_id * kNumComponents;
+ } else {
+ // None of the other corners have a valid value. Use the last encoded value
+ // as the prediction if possible.
+ if (data_id > 0) {
+ data_offset = (data_id - 1) * kNumComponents;
+ } else {
+ // We are encoding the first value. Predict 0.
+ for (int i = 0; i < kNumComponents; ++i) {
+ predicted_value_[i] = 0;
+ }
+ return true;
+ }
+ }
+ for (int i = 0; i < kNumComponents; ++i) {
+ predicted_value_[i] = data[data_offset + i];
+ }
+ return true;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_MESH_PREDICTION_SCHEME_TEX_COORDS_PORTABLE_PREDICTOR_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoder.h
new file mode 100644
index 00000000000..9a14ff8d7d2
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoder.h
@@ -0,0 +1,89 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DECODER_H_
+
+#include <type_traits>
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_decoder_interface.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_decoding_transform.h"
+
+// Prediction schemes can be used during encoding and decoding of vertex
+// attributes to predict attribute values based on the previously
+// encoded/decoded data. The differences between the original and predicted
+// attribute values are used to compute correction values that can be usually
+// encoded with fewer bits compared to the original data.
+namespace draco {
+
+// Abstract base class for typed prediction schemes. It provides basic access
+// to the encoded attribute and to the supplied prediction transform.
+template <typename DataTypeT,
+ class TransformT =
+ PredictionSchemeDecodingTransform<DataTypeT, DataTypeT>>
+class PredictionSchemeDecoder : public PredictionSchemeTypedDecoderInterface<
+ DataTypeT, typename TransformT::CorrType> {
+ public:
+ typedef DataTypeT DataType;
+ typedef TransformT Transform;
+ // Correction type needs to be defined in the prediction transform class.
+ typedef typename Transform::CorrType CorrType;
+ explicit PredictionSchemeDecoder(const PointAttribute *attribute)
+ : PredictionSchemeDecoder(attribute, Transform()) {}
+ PredictionSchemeDecoder(const PointAttribute *attribute,
+ const Transform &transform)
+ : attribute_(attribute), transform_(transform) {}
+
+ bool DecodePredictionData(DecoderBuffer *buffer) override {
+ if (!transform_.DecodeTransformData(buffer))
+ return false;
+ return true;
+ }
+
+ const PointAttribute *GetAttribute() const override { return attribute(); }
+
+ // Returns the number of parent attributes that are needed for the prediction.
+ int GetNumParentAttributes() const override { return 0; }
+
+ // Returns the type of each of the parent attribute.
+ GeometryAttribute::Type GetParentAttributeType(int /* i */) const override {
+ return GeometryAttribute::INVALID;
+ }
+
+ // Sets the required parent attribute.
+ bool SetParentAttribute(const PointAttribute * /* att */) override {
+ return false;
+ }
+
+ bool AreCorrectionsPositive() override {
+ return transform_.AreCorrectionsPositive();
+ }
+
+ PredictionSchemeTransformType GetTransformType() const override {
+ return transform_.GetType();
+ }
+
+ protected:
+ inline const PointAttribute *attribute() const { return attribute_; }
+ inline const Transform &transform() const { return transform_; }
+ inline Transform &transform() { return transform_; }
+
+ private:
+ const PointAttribute *attribute_;
+ Transform transform_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoder_factory.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoder_factory.h
new file mode 100644
index 00000000000..3a00bda943c
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoder_factory.h
@@ -0,0 +1,186 @@
+// 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.
+//
+// Functions for creating prediction schemes for decoders using the provided
+// prediction method id.
+
+#ifndef DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DECODER_FACTORY_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DECODER_FACTORY_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_decoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_decoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_decoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_decoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_decoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_decoder.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_decoder.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_delta_decoder.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_factory.h"
+#include "draco/compression/mesh/mesh_decoder.h"
+
+namespace draco {
+
+// Factory class for creating mesh prediction schemes. The factory implements
+// operator() that is used to create an appropriate mesh prediction scheme in
+// CreateMeshPredictionScheme() function in prediction_scheme_factory.h
+template <typename DataTypeT>
+struct MeshPredictionSchemeDecoderFactory {
+ // Operator () specialized for the wrap transform. Wrap transform can be used
+ // for all mesh prediction schemes. The specialization is done in compile time
+ // to prevent instantiations of unneeded combinations of prediction schemes +
+ // prediction transforms.
+ template <class TransformT, class MeshDataT,
+ PredictionSchemeTransformType Method>
+ struct DispatchFunctor {
+ std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>> operator()(
+ PredictionSchemeMethod method, const PointAttribute *attribute,
+ const TransformT &transform, const MeshDataT &mesh_data,
+ uint16_t bitstream_version) {
+ if (method == MESH_PREDICTION_PARALLELOGRAM) {
+ return std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>>(
+ new MeshPredictionSchemeParallelogramDecoder<DataTypeT, TransformT,
+ MeshDataT>(
+ attribute, transform, mesh_data));
+ }
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ else if (method == MESH_PREDICTION_MULTI_PARALLELOGRAM) {
+ return std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>>(
+ new MeshPredictionSchemeMultiParallelogramDecoder<
+ DataTypeT, TransformT, MeshDataT>(attribute, transform,
+ mesh_data));
+ }
+#endif
+ else if (method == MESH_PREDICTION_CONSTRAINED_MULTI_PARALLELOGRAM) {
+ return std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>>(
+ new MeshPredictionSchemeConstrainedMultiParallelogramDecoder<
+ DataTypeT, TransformT, MeshDataT>(attribute, transform,
+ mesh_data));
+ }
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ else if (method == MESH_PREDICTION_TEX_COORDS_DEPRECATED) {
+ return std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>>(
+ new MeshPredictionSchemeTexCoordsDecoder<DataTypeT, TransformT,
+ MeshDataT>(
+ attribute, transform, mesh_data, bitstream_version));
+ }
+#endif
+ else if (method == MESH_PREDICTION_TEX_COORDS_PORTABLE) {
+ return std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>>(
+ new MeshPredictionSchemeTexCoordsPortableDecoder<
+ DataTypeT, TransformT, MeshDataT>(attribute, transform,
+ mesh_data));
+ } else if (method == MESH_PREDICTION_GEOMETRIC_NORMAL) {
+ return std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>>(
+ new MeshPredictionSchemeGeometricNormalDecoder<
+ DataTypeT, TransformT, MeshDataT>(attribute, transform,
+ mesh_data));
+ }
+ return nullptr;
+ }
+ };
+
+ // Operator () specialized for normal octahedron transforms. These transforms
+ // are currently used only by the geometric normal prediction scheme (the
+ // transform is also used by delta coding, but delta predictor is not
+ // constructed in this function).
+ template <class TransformT, class MeshDataT>
+ struct DispatchFunctor<TransformT, MeshDataT,
+ PREDICTION_TRANSFORM_NORMAL_OCTAHEDRON_CANONICALIZED> {
+ std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>> operator()(
+ PredictionSchemeMethod method, const PointAttribute *attribute,
+ const TransformT &transform, const MeshDataT &mesh_data,
+ uint16_t bitstream_version) {
+ if (method == MESH_PREDICTION_GEOMETRIC_NORMAL) {
+ return std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>>(
+ new MeshPredictionSchemeGeometricNormalDecoder<
+ DataTypeT, TransformT, MeshDataT>(attribute, transform,
+ mesh_data));
+ }
+ return nullptr;
+ }
+ };
+ template <class TransformT, class MeshDataT>
+ struct DispatchFunctor<TransformT, MeshDataT,
+ PREDICTION_TRANSFORM_NORMAL_OCTAHEDRON> {
+ std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>> operator()(
+ PredictionSchemeMethod method, const PointAttribute *attribute,
+ const TransformT &transform, const MeshDataT &mesh_data,
+ uint16_t bitstream_version) {
+ if (method == MESH_PREDICTION_GEOMETRIC_NORMAL) {
+ return std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>>(
+ new MeshPredictionSchemeGeometricNormalDecoder<
+ DataTypeT, TransformT, MeshDataT>(attribute, transform,
+ mesh_data));
+ }
+ return nullptr;
+ }
+ };
+
+ template <class TransformT, class MeshDataT>
+ std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>> operator()(
+ PredictionSchemeMethod method, const PointAttribute *attribute,
+ const TransformT &transform, const MeshDataT &mesh_data,
+ uint16_t bitstream_version) {
+ return DispatchFunctor<TransformT, MeshDataT, TransformT::GetType()>()(
+ method, attribute, transform, mesh_data, bitstream_version);
+ }
+};
+
+// Creates a prediction scheme for a given decoder and given prediction method.
+// The prediction schemes are automatically initialized with decoder specific
+// data if needed.
+template <typename DataTypeT, class TransformT>
+std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>>
+CreatePredictionSchemeForDecoder(PredictionSchemeMethod method, int att_id,
+ const PointCloudDecoder *decoder,
+ const TransformT &transform) {
+ if (method == PREDICTION_NONE)
+ return nullptr;
+ const PointAttribute *const att = decoder->point_cloud()->attribute(att_id);
+ if (decoder->GetGeometryType() == TRIANGULAR_MESH) {
+ // Cast the decoder to mesh decoder. This is not necessarily safe if there
+ // is some other decoder decides to use TRIANGULAR_MESH as the return type,
+ // but unfortunately there is not nice work around for this without using
+ // RTTI (double dispatch and similar concepts will not work because of the
+ // template nature of the prediction schemes).
+ const MeshDecoder *const mesh_decoder =
+ static_cast<const MeshDecoder *>(decoder);
+
+ auto ret = CreateMeshPredictionScheme<
+ MeshDecoder, PredictionSchemeDecoder<DataTypeT, TransformT>,
+ MeshPredictionSchemeDecoderFactory<DataTypeT>>(
+ mesh_decoder, method, att_id, transform, decoder->bitstream_version());
+ if (ret)
+ return ret;
+ // Otherwise try to create another prediction scheme.
+ }
+ // Create delta decoder.
+ return std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>>(
+ new PredictionSchemeDeltaDecoder<DataTypeT, TransformT>(att, transform));
+}
+
+// Create a prediction scheme using a default transform constructor.
+template <typename DataTypeT, class TransformT>
+std::unique_ptr<PredictionSchemeDecoder<DataTypeT, TransformT>>
+CreatePredictionSchemeForDecoder(PredictionSchemeMethod method, int att_id,
+ const PointCloudDecoder *decoder) {
+ return CreatePredictionSchemeForDecoder<DataTypeT, TransformT>(
+ method, att_id, decoder, TransformT());
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DECODER_FACTORY_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoder_interface.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoder_interface.h
new file mode 100644
index 00000000000..6f19f7fdb99
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoder_interface.h
@@ -0,0 +1,53 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DECODER_INTERFACE_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DECODER_INTERFACE_H_
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_interface.h"
+#include "draco/core/decoder_buffer.h"
+
+// Prediction schemes can be used during encoding and decoding of attributes
+// to predict attribute values based on the previously encoded/decoded data.
+// See prediction_scheme.h for more details.
+namespace draco {
+
+// Abstract interface for all prediction schemes used during attribute encoding.
+class PredictionSchemeDecoderInterface : public PredictionSchemeInterface {
+ public:
+ // Method that can be used to decode any prediction scheme specific data
+ // from the input buffer.
+ virtual bool DecodePredictionData(DecoderBuffer *buffer) = 0;
+};
+
+// A specialized version of the prediction scheme interface for specific
+// input and output data types.
+// |entry_to_point_id_map| is the mapping between value entries to point ids
+// of the associated point cloud, where one entry is defined as |num_components|
+// values of the |in_data|.
+// DataTypeT is the data type of input and predicted values.
+// CorrTypeT is the data type used for storing corrected values.
+template <typename DataTypeT, typename CorrTypeT = DataTypeT>
+class PredictionSchemeTypedDecoderInterface
+ : public PredictionSchemeDecoderInterface {
+ public:
+ // Reverts changes made by the prediction scheme during encoding.
+ virtual bool ComputeOriginalValues(
+ const CorrTypeT *in_corr, DataTypeT *out_data, int size,
+ int num_components, const PointIndex *entry_to_point_id_map) = 0;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DECODER_INTERFACE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoding_transform.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoding_transform.h
new file mode 100644
index 00000000000..47c1532ad98
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_decoding_transform.h
@@ -0,0 +1,65 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DECODING_TRANSFORM_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DECODING_TRANSFORM_H_
+
+#include "draco/compression/config/compression_shared.h"
+#include "draco/core/decoder_buffer.h"
+
+namespace draco {
+
+// PredictionSchemeDecodingTransform is used to transform predicted values and
+// correction values into the final original attribute values.
+// DataTypeT is the data type of predicted values.
+// CorrTypeT is the data type used for storing corrected values. It allows
+// transforms to store corrections into a different type or format compared to
+// the predicted data.
+template <typename DataTypeT, typename CorrTypeT>
+class PredictionSchemeDecodingTransform {
+ public:
+ typedef CorrTypeT CorrType;
+ PredictionSchemeDecodingTransform() : num_components_(0) {}
+
+ void Init(int num_components) { num_components_ = num_components; }
+
+ // Computes the original value from the input predicted value and the decoded
+ // corrections. The default implementation is equal to std:plus.
+ inline void ComputeOriginalValue(const DataTypeT *predicted_vals,
+ const CorrTypeT *corr_vals,
+ DataTypeT *out_original_vals) const {
+ static_assert(std::is_same<DataTypeT, CorrTypeT>::value,
+ "For the default prediction transform, correction and input "
+ "data must be of the same type.");
+ for (int i = 0; i < num_components_; ++i) {
+ out_original_vals[i] = predicted_vals[i] + corr_vals[i];
+ }
+ }
+
+ // Decodes any transform specific data. Called before Init() method.
+ bool DecodeTransformData(DecoderBuffer * /* buffer */) { return true; }
+
+ // Should return true if all corrected values are guaranteed to be positive.
+ bool AreCorrectionsPositive() const { return false; }
+
+ protected:
+ int num_components() const { return num_components_; }
+
+ private:
+ int num_components_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DECODING_TRANSFORM_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_delta_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_delta_decoder.h
new file mode 100644
index 00000000000..ae72c71208a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_delta_decoder.h
@@ -0,0 +1,65 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DELTA_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DELTA_DECODER_H_
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_decoder.h"
+
+namespace draco {
+
+// Decoder for values encoded with delta coding. See the corresponding encoder
+// for more details.
+template <typename DataTypeT, class TransformT>
+class PredictionSchemeDeltaDecoder
+ : public PredictionSchemeDecoder<DataTypeT, TransformT> {
+ public:
+ using CorrType =
+ typename PredictionSchemeDecoder<DataTypeT, TransformT>::CorrType;
+ // Initialized the prediction scheme.
+ explicit PredictionSchemeDeltaDecoder(const PointAttribute *attribute)
+ : PredictionSchemeDecoder<DataTypeT, TransformT>(attribute) {}
+ PredictionSchemeDeltaDecoder(const PointAttribute *attribute,
+ const TransformT &transform)
+ : PredictionSchemeDecoder<DataTypeT, TransformT>(attribute, transform) {}
+
+ bool ComputeOriginalValues(const CorrType *in_corr, DataTypeT *out_data,
+ int size, int num_components,
+ const PointIndex *entry_to_point_id_map) override;
+ PredictionSchemeMethod GetPredictionMethod() const override {
+ return PREDICTION_DIFFERENCE;
+ }
+ bool IsInitialized() const override { return true; }
+};
+
+template <typename DataTypeT, class TransformT>
+bool PredictionSchemeDeltaDecoder<DataTypeT, TransformT>::ComputeOriginalValues(
+ const CorrType *in_corr, DataTypeT *out_data, int size, int num_components,
+ const PointIndex *) {
+ this->transform().Init(num_components);
+ // Decode the original value for the first element.
+ std::unique_ptr<DataTypeT[]> zero_vals(new DataTypeT[num_components]());
+ this->transform().ComputeOriginalValue(zero_vals.get(), in_corr, out_data);
+
+ // Decode data from the front using D(i) = D(i) + D(i - 1).
+ for (int i = num_components; i < size; i += num_components) {
+ this->transform().ComputeOriginalValue(out_data + i - num_components,
+ in_corr + i, out_data + i);
+ }
+ return true;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DELTA_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_delta_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_delta_encoder.h
new file mode 100644
index 00000000000..324afafa637
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_delta_encoder.h
@@ -0,0 +1,69 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DELTA_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DELTA_ENCODER_H_
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_encoder.h"
+
+namespace draco {
+
+// Basic prediction scheme based on computing backward differences between
+// stored attribute values (also known as delta-coding). Usually works better
+// than the reference point prediction scheme, because nearby values are often
+// encoded next to each other.
+template <typename DataTypeT, class TransformT>
+class PredictionSchemeDeltaEncoder
+ : public PredictionSchemeEncoder<DataTypeT, TransformT> {
+ public:
+ using CorrType =
+ typename PredictionSchemeEncoder<DataTypeT, TransformT>::CorrType;
+ // Initialized the prediction scheme.
+ explicit PredictionSchemeDeltaEncoder(const PointAttribute *attribute)
+ : PredictionSchemeEncoder<DataTypeT, TransformT>(attribute) {}
+ PredictionSchemeDeltaEncoder(const PointAttribute *attribute,
+ const TransformT &transform)
+ : PredictionSchemeEncoder<DataTypeT, TransformT>(attribute, transform) {}
+
+ bool ComputeCorrectionValues(
+ const DataTypeT *in_data, CorrType *out_corr, int size,
+ int num_components, const PointIndex *entry_to_point_id_map) override;
+ PredictionSchemeMethod GetPredictionMethod() const override {
+ return PREDICTION_DIFFERENCE;
+ }
+ bool IsInitialized() const override { return true; }
+};
+
+template <typename DataTypeT, class TransformT>
+bool PredictionSchemeDeltaEncoder<
+ DataTypeT, TransformT>::ComputeCorrectionValues(const DataTypeT *in_data,
+ CorrType *out_corr,
+ int size,
+ int num_components,
+ const PointIndex *) {
+ this->transform().Init(in_data, size, num_components);
+ // Encode data from the back using D(i) = D(i) - D(i - 1).
+ for (int i = size - num_components; i > 0; i -= num_components) {
+ this->transform().ComputeCorrection(
+ in_data + i, in_data + i - num_components, out_corr + i);
+ }
+ // Encode correction for the first element.
+ std::unique_ptr<DataTypeT[]> zero_vals(new DataTypeT[num_components]());
+ this->transform().ComputeCorrection(in_data, zero_vals.get(), out_corr);
+ return true;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_DELTA_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder.h
new file mode 100644
index 00000000000..548a54d4e02
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder.h
@@ -0,0 +1,89 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_ENCODER_H_
+
+#include <type_traits>
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_interface.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_encoding_transform.h"
+
+// Prediction schemes can be used during encoding and decoding of vertex
+// attributes to predict attribute values based on the previously
+// encoded/decoded data. The differences between the original and predicted
+// attribute values are used to compute correction values that can be usually
+// encoded with fewer bits compared to the original data.
+namespace draco {
+
+// Abstract base class for typed prediction schemes. It provides basic access
+// to the encoded attribute and to the supplied prediction transform.
+template <typename DataTypeT,
+ class TransformT =
+ PredictionSchemeEncodingTransform<DataTypeT, DataTypeT>>
+class PredictionSchemeEncoder : public PredictionSchemeTypedEncoderInterface<
+ DataTypeT, typename TransformT::CorrType> {
+ public:
+ typedef DataTypeT DataType;
+ typedef TransformT Transform;
+ // Correction type needs to be defined in the prediction transform class.
+ typedef typename Transform::CorrType CorrType;
+ explicit PredictionSchemeEncoder(const PointAttribute *attribute)
+ : PredictionSchemeEncoder(attribute, Transform()) {}
+ PredictionSchemeEncoder(const PointAttribute *attribute,
+ const Transform &transform)
+ : attribute_(attribute), transform_(transform) {}
+
+ bool EncodePredictionData(EncoderBuffer *buffer) override {
+ if (!transform_.EncodeTransformData(buffer))
+ return false;
+ return true;
+ }
+
+ const PointAttribute *GetAttribute() const override { return attribute(); }
+
+ // Returns the number of parent attributes that are needed for the prediction.
+ int GetNumParentAttributes() const override { return 0; }
+
+ // Returns the type of each of the parent attribute.
+ GeometryAttribute::Type GetParentAttributeType(int /* i */) const override {
+ return GeometryAttribute::INVALID;
+ }
+
+ // Sets the required parent attribute.
+ bool SetParentAttribute(const PointAttribute * /* att */) override {
+ return false;
+ }
+
+ bool AreCorrectionsPositive() override {
+ return transform_.AreCorrectionsPositive();
+ }
+
+ PredictionSchemeTransformType GetTransformType() const override {
+ return transform_.GetType();
+ }
+
+ protected:
+ inline const PointAttribute *attribute() const { return attribute_; }
+ inline const Transform &transform() const { return transform_; }
+ inline Transform &transform() { return transform_; }
+
+ private:
+ const PointAttribute *attribute_;
+ Transform transform_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc
new file mode 100644
index 00000000000..3144bdb9a6b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc
@@ -0,0 +1,70 @@
+// 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 "draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h"
+
+namespace draco {
+
+PredictionSchemeMethod SelectPredictionMethod(
+ int att_id, const PointCloudEncoder *encoder) {
+ if (encoder->options()->GetSpeed() >= 10) {
+ // Selected fastest, though still doing some compression.
+ return PREDICTION_DIFFERENCE;
+ }
+ if (encoder->GetGeometryType() == TRIANGULAR_MESH) {
+ // Use speed setting to select the best encoding method.
+ const PointAttribute *const att = encoder->point_cloud()->attribute(att_id);
+ if (att->attribute_type() == GeometryAttribute::TEX_COORD) {
+ if (encoder->options()->GetSpeed() < 4) {
+ // Use texture coordinate prediction for speeds 0, 1, 2, 3.
+ return MESH_PREDICTION_TEX_COORDS_PORTABLE;
+ }
+ }
+ if (att->attribute_type() == GeometryAttribute::NORMAL) {
+ if (encoder->options()->GetSpeed() < 4) {
+ // Use geometric normal prediction for speeds 0, 1, 2, 3.
+ return MESH_PREDICTION_GEOMETRIC_NORMAL;
+ }
+ return PREDICTION_DIFFERENCE; // default
+ }
+ // Handle other attribute types.
+ if (encoder->options()->GetSpeed() >= 8) {
+ return PREDICTION_DIFFERENCE;
+ }
+ if (encoder->options()->GetSpeed() >= 2 ||
+ encoder->point_cloud()->num_points() < 40) {
+ // Parallelogram prediction is used for speeds 2 - 7 or when the overhead
+ // of using constrained multi-parallelogram would be too high.
+ return MESH_PREDICTION_PARALLELOGRAM;
+ }
+ // Multi-parallelogram is used for speeds 0, 1.
+ return MESH_PREDICTION_CONSTRAINED_MULTI_PARALLELOGRAM;
+ }
+ // Default option is delta coding.
+ return PREDICTION_DIFFERENCE;
+}
+
+// Returns the preferred prediction scheme based on the encoder options.
+PredictionSchemeMethod GetPredictionMethodFromOptions(
+ int att_id, const EncoderOptions &options) {
+ const int pred_type =
+ options.GetAttributeInt(att_id, "prediction_scheme", -1);
+ if (pred_type == -1)
+ return PREDICTION_UNDEFINED;
+ if (pred_type < 0 || pred_type >= NUM_PREDICTION_SCHEMES)
+ return PREDICTION_NONE;
+ return static_cast<PredictionSchemeMethod>(pred_type);
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h
new file mode 100644
index 00000000000..cde5ba2435f
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h
@@ -0,0 +1,132 @@
+// 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.
+//
+// Functions for creating prediction schemes for encoders using the provided
+// prediction method id.
+
+#ifndef DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_ENCODER_FACTORY_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_ENCODER_FACTORY_H_
+
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_constrained_multi_parallelogram_encoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_geometric_normal_encoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_multi_parallelogram_encoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_parallelogram_encoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_encoder.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_tex_coords_portable_encoder.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_delta_encoder.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_encoder.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_factory.h"
+#include "draco/compression/mesh/mesh_encoder.h"
+
+namespace draco {
+
+// Selects a prediction method based on the input geometry type and based on the
+// encoder options.
+PredictionSchemeMethod SelectPredictionMethod(int att_id,
+ const PointCloudEncoder *encoder);
+
+// Factory class for creating mesh prediction schemes.
+template <typename DataTypeT>
+struct MeshPredictionSchemeEncoderFactory {
+ template <class TransformT, class MeshDataT>
+ std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>> operator()(
+ PredictionSchemeMethod method, const PointAttribute *attribute,
+ const TransformT &transform, const MeshDataT &mesh_data,
+ uint16_t bitstream_version) {
+ if (method == MESH_PREDICTION_PARALLELOGRAM) {
+ return std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>>(
+ new MeshPredictionSchemeParallelogramEncoder<DataTypeT, TransformT,
+ MeshDataT>(
+ attribute, transform, mesh_data));
+ } else if (method == MESH_PREDICTION_MULTI_PARALLELOGRAM) {
+ return std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>>(
+ new MeshPredictionSchemeMultiParallelogramEncoder<
+ DataTypeT, TransformT, MeshDataT>(attribute, transform,
+ mesh_data));
+ } else if (method == MESH_PREDICTION_CONSTRAINED_MULTI_PARALLELOGRAM) {
+ return std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>>(
+ new MeshPredictionSchemeConstrainedMultiParallelogramEncoder<
+ DataTypeT, TransformT, MeshDataT>(attribute, transform,
+ mesh_data));
+ } else if (method == MESH_PREDICTION_TEX_COORDS_DEPRECATED) {
+ return std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>>(
+ new MeshPredictionSchemeTexCoordsEncoder<DataTypeT, TransformT,
+ MeshDataT>(
+ attribute, transform, mesh_data));
+ } else if (method == MESH_PREDICTION_TEX_COORDS_PORTABLE) {
+ return std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>>(
+ new MeshPredictionSchemeTexCoordsPortableEncoder<
+ DataTypeT, TransformT, MeshDataT>(attribute, transform,
+ mesh_data));
+ } else if (method == MESH_PREDICTION_GEOMETRIC_NORMAL) {
+ return std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>>(
+ new MeshPredictionSchemeGeometricNormalEncoder<DataTypeT, TransformT,
+ MeshDataT>(
+ attribute, transform, mesh_data));
+ }
+ return nullptr;
+ }
+};
+
+// Creates a prediction scheme for a given encoder and given prediction method.
+// The prediction schemes are automatically initialized with encoder specific
+// data if needed.
+template <typename DataTypeT, class TransformT>
+std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>>
+CreatePredictionSchemeForEncoder(PredictionSchemeMethod method, int att_id,
+ const PointCloudEncoder *encoder,
+ const TransformT &transform) {
+ const PointAttribute *const att = encoder->point_cloud()->attribute(att_id);
+ if (method == PREDICTION_UNDEFINED) {
+ method = SelectPredictionMethod(att_id, encoder);
+ }
+ if (method == PREDICTION_NONE)
+ return nullptr; // No prediction is used.
+ if (encoder->GetGeometryType() == TRIANGULAR_MESH) {
+ // Cast the encoder to mesh encoder. This is not necessarily safe if there
+ // is some other encoder decides to use TRIANGULAR_MESH as the return type,
+ // but unfortunately there is not nice work around for this without using
+ // RTTI (double dispatch and similar concepts will not work because of the
+ // template nature of the prediction schemes).
+ const MeshEncoder *const mesh_encoder =
+ static_cast<const MeshEncoder *>(encoder);
+ auto ret = CreateMeshPredictionScheme<
+ MeshEncoder, PredictionSchemeEncoder<DataTypeT, TransformT>,
+ MeshPredictionSchemeEncoderFactory<DataTypeT>>(
+ mesh_encoder, method, att_id, transform, kDracoMeshBitstreamVersion);
+ if (ret)
+ return ret;
+ // Otherwise try to create another prediction scheme.
+ }
+ // Create delta encoder.
+ return std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>>(
+ new PredictionSchemeDeltaEncoder<DataTypeT, TransformT>(att, transform));
+}
+
+// Create a prediction scheme using a default transform constructor.
+template <typename DataTypeT, class TransformT>
+std::unique_ptr<PredictionSchemeEncoder<DataTypeT, TransformT>>
+CreatePredictionSchemeForEncoder(PredictionSchemeMethod method, int att_id,
+ const PointCloudEncoder *encoder) {
+ return CreatePredictionSchemeForEncoder<DataTypeT, TransformT>(
+ method, att_id, encoder, TransformT());
+}
+
+// Returns the preferred prediction scheme based on the encoder options.
+PredictionSchemeMethod GetPredictionMethodFromOptions(
+ int att_id, const EncoderOptions &options);
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_ENCODER_FACTORY_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_interface.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_interface.h
new file mode 100644
index 00000000000..ab64bce7114
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_interface.h
@@ -0,0 +1,55 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_ENCODING_INTERFACE_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_ENCODING_INTERFACE_H_
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_interface.h"
+#include "draco/core/encoder_buffer.h"
+
+// Prediction schemes can be used during encoding and decoding of attributes
+// to predict attribute values based on the previously encoded/decoded data.
+// See prediction_scheme.h for more details.
+namespace draco {
+
+// Abstract interface for all prediction schemes used during attribute encoding.
+class PredictionSchemeEncoderInterface : public PredictionSchemeInterface {
+ public:
+ // Method that can be used to encode any prediction scheme specific data
+ // into the output buffer.
+ virtual bool EncodePredictionData(EncoderBuffer *buffer) = 0;
+};
+
+// A specialized version of the prediction scheme interface for specific
+// input and output data types.
+// |entry_to_point_id_map| is the mapping between value entries to point ids
+// of the associated point cloud, where one entry is defined as |num_components|
+// values of the |in_data|.
+// DataTypeT is the data type of input and predicted values.
+// CorrTypeT is the data type used for storing corrected values.
+template <typename DataTypeT, typename CorrTypeT = DataTypeT>
+class PredictionSchemeTypedEncoderInterface
+ : public PredictionSchemeEncoderInterface {
+ public:
+ // Applies the prediction scheme when encoding the attribute.
+ // |in_data| contains value entries to be encoded.
+ // |out_corr| is an output array containing the to be encoded corrections.
+ virtual bool ComputeCorrectionValues(
+ const DataTypeT *in_data, CorrTypeT *out_corr, int size,
+ int num_components, const PointIndex *entry_to_point_id_map) = 0;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_ENCODING_INTERFACE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoding_transform.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoding_transform.h
new file mode 100644
index 00000000000..0929492aaec
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_encoding_transform.h
@@ -0,0 +1,77 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_ENCODING_TRANSFORM_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_ENCODING_TRANSFORM_H_
+
+#include "draco/compression/config/compression_shared.h"
+#include "draco/core/encoder_buffer.h"
+
+namespace draco {
+
+// PredictionSchemeEncodingTransform is used to transform predicted values into
+// correction values.
+// CorrTypeT is the data type used for storing corrected values. It allows
+// transforms to store corrections into a different type or format compared to
+// the predicted data.
+template <typename DataTypeT, typename CorrTypeT>
+class PredictionSchemeEncodingTransform {
+ public:
+ typedef CorrTypeT CorrType;
+ PredictionSchemeEncodingTransform() : num_components_(0) {}
+
+ PredictionSchemeTransformType GetType() const {
+ return PREDICTION_TRANSFORM_DELTA;
+ }
+
+ // Performs any custom initialization of the transform for the encoder.
+ // |size| = total number of values in |orig_data| (i.e., number of entries *
+ // number of components).
+ void Init(const DataTypeT * /* orig_data */, int /* size */,
+ int num_components) {
+ num_components_ = num_components;
+ }
+
+ // Computes the corrections based on the input original values and the
+ // predicted values. The correction is always computed for all components
+ // of the input element. |val_id| is the id of the input value
+ // (i.e., element_id * num_components). The default implementation is equal to
+ // std::minus.
+ inline void ComputeCorrection(const DataTypeT *original_vals,
+ const DataTypeT *predicted_vals,
+ CorrTypeT *out_corr_vals) {
+ static_assert(std::is_same<DataTypeT, CorrTypeT>::value,
+ "For the default prediction transform, correction and input "
+ "data must be of the same type.");
+ for (int i = 0; i < num_components_; ++i) {
+ out_corr_vals[i] = original_vals[i] - predicted_vals[i];
+ }
+ }
+
+ // Encode any transform specific data.
+ bool EncodeTransformData(EncoderBuffer * /* buffer */) { return true; }
+
+ // Should return true if all corrected values are guaranteed to be positive.
+ bool AreCorrectionsPositive() const { return false; }
+
+ protected:
+ int num_components() const { return num_components_; }
+
+ private:
+ int num_components_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_ENCODING_TRANSFORM_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_factory.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_factory.h
new file mode 100644
index 00000000000..9fce686ae41
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_factory.h
@@ -0,0 +1,83 @@
+// 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.
+//
+// Functions for creating prediction schemes from a provided prediction method
+// name. The functions in this file can create only basic prediction schemes
+// that don't require any encoder or decoder specific data. To create more
+// sophisticated prediction schemes, use functions from either
+// prediction_scheme_encoder_factory.h or,
+// prediction_scheme_decoder_factory.h.
+
+#ifndef DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_FACTORY_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_FACTORY_H_
+
+#include "draco/compression/attributes/mesh_attribute_indices_encoding_data.h"
+#include "draco/compression/attributes/prediction_schemes/mesh_prediction_scheme_data.h"
+#include "draco/compression/config/compression_shared.h"
+#include "draco/mesh/mesh_attribute_corner_table.h"
+
+namespace draco {
+
+template <class EncodingDataSourceT, class PredictionSchemeT,
+ class MeshPredictionSchemeFactoryT>
+std::unique_ptr<PredictionSchemeT> CreateMeshPredictionScheme(
+ const EncodingDataSourceT *source, PredictionSchemeMethod method,
+ int att_id, const typename PredictionSchemeT::Transform &transform,
+ uint16_t bitstream_version) {
+ const PointAttribute *const att = source->point_cloud()->attribute(att_id);
+ if (source->GetGeometryType() == TRIANGULAR_MESH &&
+ (method == MESH_PREDICTION_PARALLELOGRAM ||
+ method == MESH_PREDICTION_MULTI_PARALLELOGRAM ||
+ method == MESH_PREDICTION_CONSTRAINED_MULTI_PARALLELOGRAM ||
+ method == MESH_PREDICTION_TEX_COORDS_PORTABLE ||
+ method == MESH_PREDICTION_GEOMETRIC_NORMAL ||
+ method == MESH_PREDICTION_TEX_COORDS_DEPRECATED)) {
+ const CornerTable *const ct = source->GetCornerTable();
+ const MeshAttributeIndicesEncodingData *const encoding_data =
+ source->GetAttributeEncodingData(att_id);
+ if (ct == nullptr || encoding_data == nullptr) {
+ // No connectivity data found.
+ return nullptr;
+ }
+ // Connectivity data exists.
+ const MeshAttributeCornerTable *const att_ct =
+ source->GetAttributeCornerTable(att_id);
+ if (att_ct != nullptr) {
+ typedef MeshPredictionSchemeData<MeshAttributeCornerTable> MeshData;
+ MeshData md;
+ md.Set(source->mesh(), att_ct,
+ &encoding_data->encoded_attribute_value_index_to_corner_map,
+ &encoding_data->vertex_to_encoded_attribute_value_index_map);
+ MeshPredictionSchemeFactoryT factory;
+ auto ret = factory(method, att, transform, md, bitstream_version);
+ if (ret)
+ return ret;
+ } else {
+ typedef MeshPredictionSchemeData<CornerTable> MeshData;
+ MeshData md;
+ md.Set(source->mesh(), ct,
+ &encoding_data->encoded_attribute_value_index_to_corner_map,
+ &encoding_data->vertex_to_encoded_attribute_value_index_map);
+ MeshPredictionSchemeFactoryT factory;
+ auto ret = factory(method, att, transform, md, bitstream_version);
+ if (ret)
+ return ret;
+ }
+ }
+ return nullptr;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_FACTORY_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_interface.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_interface.h
new file mode 100644
index 00000000000..c9b3706930f
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_interface.h
@@ -0,0 +1,60 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_INTERFACE_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_INTERFACE_H_
+
+#include "draco/attributes/point_attribute.h"
+#include "draco/compression/config/compression_shared.h"
+
+// Prediction schemes can be used during encoding and decoding of attributes
+// to predict attribute values based on the previously encoded/decoded data.
+// See prediction_scheme.h for more details.
+namespace draco {
+
+// Abstract interface for all prediction schemes used during attribute encoding.
+class PredictionSchemeInterface {
+ public:
+ virtual ~PredictionSchemeInterface() = default;
+ virtual PredictionSchemeMethod GetPredictionMethod() const = 0;
+
+ // Returns the encoded attribute.
+ virtual const PointAttribute *GetAttribute() const = 0;
+
+ // Returns true when the prediction scheme is initialized with all data it
+ // needs.
+ virtual bool IsInitialized() const = 0;
+
+ // Returns the number of parent attributes that are needed for the prediction.
+ virtual int GetNumParentAttributes() const = 0;
+
+ // Returns the type of each of the parent attribute.
+ virtual GeometryAttribute::Type GetParentAttributeType(int i) const = 0;
+
+ // Sets the required parent attribute.
+ // Returns false if the attribute doesn't meet the requirements of the
+ // prediction scheme.
+ virtual bool SetParentAttribute(const PointAttribute *att) = 0;
+
+ // Method should return true if the prediction scheme guarantees that all
+ // correction values are always positive (or at least non-negative).
+ virtual bool AreCorrectionsPositive() = 0;
+
+ // Returns the transform type used by the prediction scheme.
+ virtual PredictionSchemeTransformType GetTransformType() const = 0;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_INTERFACE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_decoding_transform.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_decoding_transform.h
new file mode 100644
index 00000000000..14ecb84654b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_decoding_transform.h
@@ -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.
+//
+#ifndef DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_CANONICALIZED_DECODING_TRANSFORM_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_CANONICALIZED_DECODING_TRANSFORM_H_
+
+#include <cmath>
+
+#include "draco/compression/attributes/normal_compression_utils.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h"
+#include "draco/core/decoder_buffer.h"
+#include "draco/core/macros.h"
+#include "draco/core/vector_d.h"
+
+namespace draco {
+
+// Class for converting correction values transformed by the canonicalized
+// normal octahedron transform back to the original values. See the
+// corresponding encoder for more details.
+template <typename DataTypeT>
+class PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform
+ : public PredictionSchemeNormalOctahedronCanonicalizedTransformBase<
+ DataTypeT> {
+ public:
+ typedef VectorD<DataTypeT, 2> Point2;
+ typedef DataTypeT CorrType;
+ typedef DataTypeT DataType;
+
+ PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform() {}
+
+ // Dummy to fulfill concept.
+ void Init(int num_components) {}
+
+ bool DecodeTransformData(DecoderBuffer *buffer) {
+ DataTypeT max_quantized_value, center_value;
+ if (!buffer->Decode(&max_quantized_value))
+ return false;
+ if (!buffer->Decode(&center_value))
+ return false;
+ (void)center_value;
+ if (!this->set_max_quantized_value(max_quantized_value))
+ return false;
+ // Account for reading wrong values, e.g., due to fuzzing.
+ if (this->quantization_bits() < 2)
+ return false;
+ if (this->quantization_bits() > 30)
+ return false;
+ return true;
+ }
+
+ inline void ComputeOriginalValue(const DataType *pred_vals,
+ const CorrType *corr_vals,
+ DataType *out_orig_vals) const {
+ DRACO_DCHECK_LE(pred_vals[0], 2 * this->center_value());
+ DRACO_DCHECK_LE(pred_vals[1], 2 * this->center_value());
+ DRACO_DCHECK_LE(corr_vals[0], 2 * this->center_value());
+ DRACO_DCHECK_LE(corr_vals[1], 2 * this->center_value());
+
+ DRACO_DCHECK_LE(0, pred_vals[0]);
+ DRACO_DCHECK_LE(0, pred_vals[1]);
+ DRACO_DCHECK_LE(0, corr_vals[0]);
+ DRACO_DCHECK_LE(0, corr_vals[1]);
+
+ const Point2 pred = Point2(pred_vals[0], pred_vals[1]);
+ const Point2 corr = Point2(corr_vals[0], corr_vals[1]);
+ const Point2 orig = ComputeOriginalValue(pred, corr);
+
+ out_orig_vals[0] = orig[0];
+ out_orig_vals[1] = orig[1];
+ }
+
+ private:
+ Point2 ComputeOriginalValue(Point2 pred, Point2 corr) const {
+ const Point2 t(this->center_value(), this->center_value());
+ pred = pred - t;
+ const bool pred_is_in_diamond = this->IsInDiamond(pred[0], pred[1]);
+ if (!pred_is_in_diamond) {
+ this->InvertDiamond(&pred[0], &pred[1]);
+ }
+ const bool pred_is_in_bottom_left = this->IsInBottomLeft(pred);
+ const int32_t rotation_count = this->GetRotationCount(pred);
+ if (!pred_is_in_bottom_left) {
+ pred = this->RotatePoint(pred, rotation_count);
+ }
+ Point2 orig = pred + corr;
+ orig[0] = this->ModMax(orig[0]);
+ orig[1] = this->ModMax(orig[1]);
+ if (!pred_is_in_bottom_left) {
+ const int32_t reverse_rotation_count = (4 - rotation_count) % 4;
+ orig = this->RotatePoint(orig, reverse_rotation_count);
+ }
+ if (!pred_is_in_diamond) {
+ this->InvertDiamond(&orig[0], &orig[1]);
+ }
+ orig = orig + t;
+ return orig;
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_CANONICALIZED_DECODING_TRANSFORM_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_encoding_transform.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_encoding_transform.h
new file mode 100644
index 00000000000..0dc96967b10
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_encoding_transform.h
@@ -0,0 +1,116 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_CANONICALIZED_ENCODING_TRANSFORM_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_CANONICALIZED_ENCODING_TRANSFORM_H_
+
+#include <cmath>
+
+#include "draco/compression/attributes/normal_compression_utils.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h"
+#include "draco/core/encoder_buffer.h"
+#include "draco/core/macros.h"
+#include "draco/core/vector_d.h"
+
+namespace draco {
+
+// The transform works on octahedral coordinates for normals. The square is
+// subdivided into four inner triangles (diamond) and four outer triangles. The
+// inner triangles are associated with the upper part of the octahedron and the
+// outer triangles are associated with the lower part.
+// Given a prediction value P and the actual value Q that should be encoded,
+// this transform first checks if P is outside the diamond. If so, the outer
+// triangles are flipped towards the inside and vice versa. Then it checks if p
+// is in the bottom left quadrant. If it is not, it rotates p and q accordingly.
+// The actual correction value is then based on the mapped and rotated P and Q
+// values. The inversion tends to result in shorter correction vectors and the
+// rotation makes it so that all long correction values are positive, reducing
+// the possible value range of the correction values and increasing the
+// occurrences of positive large correction values, which helps the entropy
+// encoder. This is possible since P is also known by the decoder, see also
+// ComputeCorrection and ComputeOriginalValue functions.
+// Note that the tile is not periodic, which implies that the outer edges can
+// not be identified, which requires us to use an odd number of values on each
+// axis.
+// DataTypeT is expected to be some integral type.
+//
+template <typename DataTypeT>
+class PredictionSchemeNormalOctahedronCanonicalizedEncodingTransform
+ : public PredictionSchemeNormalOctahedronCanonicalizedTransformBase<
+ DataTypeT> {
+ public:
+ typedef PredictionSchemeNormalOctahedronCanonicalizedTransformBase<DataTypeT>
+ Base;
+ typedef VectorD<DataTypeT, 2> Point2;
+ typedef DataTypeT CorrType;
+ typedef DataTypeT DataType;
+
+ // We expect the mod value to be of the form 2^b-1.
+ explicit PredictionSchemeNormalOctahedronCanonicalizedEncodingTransform(
+ DataType max_quantized_value)
+ : Base(max_quantized_value) {}
+
+ // Dummy function to fulfill concept.
+ void Init(const DataTypeT *orig_data, int size, int num_components) {}
+
+ bool EncodeTransformData(EncoderBuffer *buffer) {
+ buffer->Encode(this->max_quantized_value());
+ buffer->Encode(this->center_value());
+ return true;
+ }
+
+ inline void ComputeCorrection(const DataType *orig_vals,
+ const DataType *pred_vals,
+ CorrType *out_corr_vals) const {
+ DRACO_DCHECK_LE(pred_vals[0], this->center_value() * 2);
+ DRACO_DCHECK_LE(pred_vals[1], this->center_value() * 2);
+ DRACO_DCHECK_LE(orig_vals[0], this->center_value() * 2);
+ DRACO_DCHECK_LE(orig_vals[1], this->center_value() * 2);
+ DRACO_DCHECK_LE(0, pred_vals[0]);
+ DRACO_DCHECK_LE(0, pred_vals[1]);
+ DRACO_DCHECK_LE(0, orig_vals[0]);
+ DRACO_DCHECK_LE(0, orig_vals[1]);
+
+ const Point2 orig = Point2(orig_vals[0], orig_vals[1]);
+ const Point2 pred = Point2(pred_vals[0], pred_vals[1]);
+ const Point2 corr = ComputeCorrection(orig, pred);
+
+ out_corr_vals[0] = corr[0];
+ out_corr_vals[1] = corr[1];
+ }
+
+ private:
+ Point2 ComputeCorrection(Point2 orig, Point2 pred) const {
+ const Point2 t(this->center_value(), this->center_value());
+ orig = orig - t;
+ pred = pred - t;
+ if (!this->IsInDiamond(pred[0], pred[1])) {
+ this->InvertDiamond(&orig[0], &orig[1]);
+ this->InvertDiamond(&pred[0], &pred[1]);
+ }
+ if (!this->IsInBottomLeft(pred)) {
+ const int32_t rotation_count = this->GetRotationCount(pred);
+ orig = this->RotatePoint(orig, rotation_count);
+ pred = this->RotatePoint(pred, rotation_count);
+ }
+ Point2 corr = orig - pred;
+ corr[0] = this->MakePositive(corr[0]);
+ corr[1] = this->MakePositive(corr[1]);
+ return corr;
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_CANONICALIZED_ENCODING_TRANSFORM_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h
new file mode 100644
index 00000000000..981077294f8
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_base.h
@@ -0,0 +1,101 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_CANONICALIZED_TRANSFORM_BASE_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_CANONICALIZED_TRANSFORM_BASE_H_
+
+#include <cmath>
+
+#include "draco/compression/attributes/normal_compression_utils.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_base.h"
+#include "draco/compression/config/compression_shared.h"
+#include "draco/core/bit_utils.h"
+#include "draco/core/macros.h"
+#include "draco/core/vector_d.h"
+
+namespace draco {
+
+// Base class containing shared functionality used by both encoding and decoding
+// canonicalized normal octahedron prediction scheme transforms. See the
+// encoding transform for more details about the method.
+template <typename DataTypeT>
+class PredictionSchemeNormalOctahedronCanonicalizedTransformBase
+ : public PredictionSchemeNormalOctahedronTransformBase<DataTypeT> {
+ public:
+ typedef PredictionSchemeNormalOctahedronTransformBase<DataTypeT> Base;
+ typedef VectorD<DataTypeT, 2> Point2;
+ typedef DataTypeT DataType;
+
+ PredictionSchemeNormalOctahedronCanonicalizedTransformBase() : Base() {}
+ // We expect the mod value to be of the form 2^b-1.
+ explicit PredictionSchemeNormalOctahedronCanonicalizedTransformBase(
+ DataType mod_value)
+ : Base(mod_value) {}
+
+ static constexpr PredictionSchemeTransformType GetType() {
+ return PREDICTION_TRANSFORM_NORMAL_OCTAHEDRON_CANONICALIZED;
+ }
+
+ int32_t GetRotationCount(Point2 pred) const {
+ const DataType sign_x = pred[0];
+ const DataType sign_y = pred[1];
+
+ int32_t rotation_count = 0;
+ if (sign_x == 0) {
+ if (sign_y == 0) {
+ rotation_count = 0;
+ } else if (sign_y > 0) {
+ rotation_count = 3;
+ } else {
+ rotation_count = 1;
+ }
+ } else if (sign_x > 0) {
+ if (sign_y >= 0) {
+ rotation_count = 2;
+ } else {
+ rotation_count = 1;
+ }
+ } else {
+ if (sign_y <= 0) {
+ rotation_count = 0;
+ } else {
+ rotation_count = 3;
+ }
+ }
+ return rotation_count;
+ }
+
+ Point2 RotatePoint(Point2 p, int32_t rotation_count) const {
+ switch (rotation_count) {
+ case 1:
+ return Point2(p[1], -p[0]);
+ case 2:
+ return Point2(-p[0], -p[1]);
+ case 3:
+ return Point2(-p[1], p[0]);
+ default:
+ return p;
+ }
+ }
+
+ bool IsInBottomLeft(const Point2 &p) const {
+ if (p[0] == 0 && p[1] == 0)
+ return true;
+ return (p[0] < 0 && p[1] <= 0);
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_CANONICALIZED_TRANSFORM_BASE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_test.cc b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_test.cc
new file mode 100644
index 00000000000..8c8932f77c3
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_transform_test.cc
@@ -0,0 +1,192 @@
+// 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 "draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_encoding_transform.h"
+#include "draco/core/draco_test_base.h"
+
+namespace {
+
+class PredictionSchemeNormalOctahedronCanonicalizedTransformTest
+ : public ::testing::Test {
+ protected:
+ typedef draco::PredictionSchemeNormalOctahedronCanonicalizedEncodingTransform<
+ int32_t>
+ 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);
+ ASSERT_EQ(corr[0], (cx + 15) % 15);
+ ASSERT_EQ(corr[1], (cy + 15) % 15);
+ }
+
+ void TestGetRotationCount(const Transform &transform, const Point2 &pred,
+ const int32_t &rot_dir) {
+ const int32_t rotation_count = transform.GetRotationCount(pred);
+ ASSERT_EQ(rot_dir, rotation_count);
+ }
+
+ void TestRotateRepresentation(const Transform &transform, const Point2 &org,
+ const Point2 &pred, const Point2 &rot_org,
+ const Point2 &rot_pred) {
+ const int32_t rotation_count = transform.GetRotationCount(pred);
+ const Point2 res_org = transform.RotatePoint(org, rotation_count);
+ const Point2 res_pred = transform.RotatePoint(pred, rotation_count);
+ ASSERT_EQ(rot_org[0], res_org[0]);
+ ASSERT_EQ(rot_org[1], res_org[1]);
+ ASSERT_EQ(rot_pred[0], res_pred[0]);
+ ASSERT_EQ(rot_pred[1], res_pred[1]);
+ }
+};
+
+TEST_F(PredictionSchemeNormalOctahedronCanonicalizedTransformTest, Init) {
+ const Transform transform(15);
+ ASSERT_TRUE(transform.AreCorrectionsPositive());
+}
+
+TEST_F(PredictionSchemeNormalOctahedronCanonicalizedTransformTest,
+ IsInBottomLeft) {
+ const Transform transform(15);
+ ASSERT_TRUE(transform.IsInBottomLeft(Point2(0, 0)));
+ ASSERT_TRUE(transform.IsInBottomLeft(Point2(-1, -1)));
+ ASSERT_TRUE(transform.IsInBottomLeft(Point2(-7, -7)));
+
+ ASSERT_FALSE(transform.IsInBottomLeft(Point2(1, 1)));
+ ASSERT_FALSE(transform.IsInBottomLeft(Point2(7, 7)));
+ ASSERT_FALSE(transform.IsInBottomLeft(Point2(-1, 1)));
+ ASSERT_FALSE(transform.IsInBottomLeft(Point2(-7, 7)));
+ ASSERT_FALSE(transform.IsInBottomLeft(Point2(1, -1)));
+ ASSERT_FALSE(transform.IsInBottomLeft(Point2(7, -7)));
+}
+
+TEST_F(PredictionSchemeNormalOctahedronCanonicalizedTransformTest,
+ GetRotationCount) {
+ const Transform transform(15);
+ TestGetRotationCount(transform, Point2(1, 2), 2); // top right
+ TestGetRotationCount(transform, Point2(-1, 2), 3); // top left
+ TestGetRotationCount(transform, Point2(1, -2), 1); // bottom right
+ TestGetRotationCount(transform, Point2(-1, -2), 0); // bottom left
+ TestGetRotationCount(transform, Point2(0, 2), 3); // top left
+ TestGetRotationCount(transform, Point2(0, -2), 1); // bottom right
+ TestGetRotationCount(transform, Point2(2, 0), 2); // top right
+ TestGetRotationCount(transform, Point2(-2, 0), 0); // bottom left
+ TestGetRotationCount(transform, Point2(0, 0), 0); // bottom left
+}
+
+TEST_F(PredictionSchemeNormalOctahedronCanonicalizedTransformTest,
+ RotateRepresentation) {
+ const Transform transform(15);
+ // p top left; shift clockwise by 3
+ TestRotateRepresentation(transform, Point2(1, 2), Point2(-3, 1),
+ Point2(-2, 1), Point2(-1, -3)); // q top right
+ TestRotateRepresentation(transform, Point2(-1, -2), Point2(-3, 1),
+ Point2(2, -1), Point2(-1, -3)); // q bottom left
+ TestRotateRepresentation(transform, Point2(1, -2), Point2(-3, 1),
+ Point2(2, 1), Point2(-1, -3)); // q bottom right
+ TestRotateRepresentation(transform, Point2(-1, 2), Point2(-3, 1),
+ Point2(-2, -1), Point2(-1, -3)); // q top left
+ // p top right; shift clockwise by 2 (flip)
+ TestRotateRepresentation(transform, Point2(1, 1), Point2(1, 3),
+ Point2(-1, -1), Point2(-1, -3)); // q top right
+ TestRotateRepresentation(transform, Point2(-1, -2), Point2(1, 3),
+ Point2(1, 2), Point2(-1, -3)); // q bottom left
+ TestRotateRepresentation(transform, Point2(-1, 2), Point2(1, 3),
+ Point2(1, -2), Point2(-1, -3)); // q top left
+ TestRotateRepresentation(transform, Point2(1, -2), Point2(1, 3),
+ Point2(-1, 2), Point2(-1, -3)); // q bottom right
+ // p bottom right; shift clockwise by 1
+ TestRotateRepresentation(transform, Point2(1, 2), Point2(3, -1),
+ Point2(2, -1), Point2(-1, -3)); // q top right
+ TestRotateRepresentation(transform, Point2(1, -2), Point2(3, -1),
+ Point2(-2, -1), Point2(-1, -3)); // q bottom right
+ TestRotateRepresentation(transform, Point2(-1, -2), Point2(3, -1),
+ Point2(-2, 1), Point2(-1, -3)); // q bottom left
+ TestRotateRepresentation(transform, Point2(-1, 2), Point2(3, -1),
+ Point2(2, 1), Point2(-1, -3)); // q top left
+ // p bottom left; no change
+ TestRotateRepresentation(transform, Point2(1, 2), Point2(-1, -3),
+ Point2(1, 2), Point2(-1, -3)); // q top right
+ TestRotateRepresentation(transform, Point2(-1, 2), Point2(-1, -3),
+ Point2(-1, 2), Point2(-1, -3)); // q top left
+ TestRotateRepresentation(transform, Point2(1, -2), Point2(-1, -3),
+ Point2(1, -2), Point2(-1, -3)); // q bottom right
+ TestRotateRepresentation(transform, Point2(-1, -2), Point2(-1, -3),
+ Point2(-1, -2), Point2(-1, -3)); // q bottom left
+}
+
+TEST_F(PredictionSchemeNormalOctahedronCanonicalizedTransformTest,
+ ComputeCorrection) {
+ const Transform transform(15);
+ TestComputeCorrection(transform, 0, 0, 0, 0, 0, 0);
+ TestComputeCorrection(transform, 1, 1, 1, 1, 0, 0);
+ // inside diamond; p top right
+ TestComputeCorrection(transform, 3, 4, 1, 2, -2, -2); // q top right
+ TestComputeCorrection(transform, -3, 4, 1, 2, 4, -2); // q top left
+ TestComputeCorrection(transform, 3, -4, 1, 2, -2, 6); // q bottom right
+ TestComputeCorrection(transform, -3, -4, 1, 2, 4, 6); // q bottom left
+ // inside diamond; p top left
+ TestComputeCorrection(transform, 3, 4, -1, 2, -2, 4); // q top right
+ TestComputeCorrection(transform, -3, 4, -1, 2, -2, -2); // q top left
+ TestComputeCorrection(transform, 3, -4, -1, 2, 6, 4); // q bottom right
+ TestComputeCorrection(transform, -3, -4, -1, 2, 6, -2); // q bottom left
+ // inside diamond; p bottom right
+ TestComputeCorrection(transform, 3, 4, 1, -2, 6, -2); // q top right
+ TestComputeCorrection(transform, -3, 4, 1, -2, 6, 4); // q top left
+ TestComputeCorrection(transform, 3, -4, 1, -2, -2, -2); // q bottom right
+ TestComputeCorrection(transform, -3, -4, 1, -2, -2, 4); // q bottom left
+ // inside diamond; p bottom left
+ TestComputeCorrection(transform, 3, 4, -1, -2, 4, 6); // q top right
+ TestComputeCorrection(transform, -3, 4, -1, -2, -2, 6); // q top left
+ TestComputeCorrection(transform, 3, -4, -1, -2, 4, -2); // q bottom right
+ TestComputeCorrection(transform, -3, -4, -1, -2, -2, -2); // q bottom left
+ // outside diamond; p top right
+ TestComputeCorrection(transform, 1, 2, 5, 4, -2, -4); // q top right
+ TestComputeCorrection(transform, -1, 2, 5, 4, -7, -4); // q top left
+ TestComputeCorrection(transform, 1, -2, 5, 4, -2, -7); // q bottom right
+ TestComputeCorrection(transform, -1, -2, 5, 4, -7, -7); // q bottom left
+ // outside diamond; p top left
+ TestComputeCorrection(transform, 1, 2, -5, 4, -4, -7); // q top right
+ TestComputeCorrection(transform, -1, 2, -5, 4, -4, -2); // q top left
+ TestComputeCorrection(transform, 1, -2, -5, 4, -7, -7); // q bottom right
+ TestComputeCorrection(transform, -1, -2, -5, 4, -7, -2); // q bottom left
+ // outside diamond; p bottom right
+ TestComputeCorrection(transform, 1, 2, 5, -4, -7, -2); // q top right
+ TestComputeCorrection(transform, -1, 2, 5, -4, -7, -7); // q top left
+ TestComputeCorrection(transform, 1, -2, 5, -4, -4, -2); // q bottom right
+ TestComputeCorrection(transform, -1, -2, 5, -4, -4, -7); // q bottom left
+ // outside diamond; p bottom left
+ TestComputeCorrection(transform, 1, 2, -5, -4, -7, -7); // q top right
+ TestComputeCorrection(transform, -1, 2, -5, -4, -2, -7); // q top left
+ TestComputeCorrection(transform, 1, -2, -5, -4, -7, -4); // q bottom right
+ TestComputeCorrection(transform, -1, -2, -5, -4, -2, -4); // q bottom left
+
+ TestComputeCorrection(transform, -1, -2, 7, 7, -5, -6);
+ TestComputeCorrection(transform, 0, 0, 7, 7, 7, 7);
+ TestComputeCorrection(transform, -1, -2, 0, -2, 0, 1);
+}
+
+TEST_F(PredictionSchemeNormalOctahedronCanonicalizedTransformTest, Interface) {
+ const Transform transform(15);
+ ASSERT_EQ(transform.max_quantized_value(), 15);
+ ASSERT_EQ(transform.center_value(), 7);
+ ASSERT_EQ(transform.quantization_bits(), 4);
+}
+
+} // namespace
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_decoding_transform.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_decoding_transform.h
new file mode 100644
index 00000000000..dbb788a33e0
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_decoding_transform.h
@@ -0,0 +1,102 @@
+// 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.
+//
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+#ifndef DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_DECODING_TRANSFORM_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_DECODING_TRANSFORM_H_
+
+#include <cmath>
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/attributes/normal_compression_utils.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_base.h"
+#include "draco/core/decoder_buffer.h"
+#include "draco/core/macros.h"
+#include "draco/core/vector_d.h"
+
+namespace draco {
+
+// Class for converting correction values transformed by the octahedral normal
+// transform back to the original values. See the corresponding encoder for more
+// details.
+template <typename DataTypeT>
+class PredictionSchemeNormalOctahedronDecodingTransform
+ : public PredictionSchemeNormalOctahedronTransformBase<DataTypeT> {
+ public:
+ typedef VectorD<DataTypeT, 2> Point2;
+ typedef DataTypeT CorrType;
+ typedef DataTypeT DataType;
+
+ PredictionSchemeNormalOctahedronDecodingTransform() {}
+
+ // Dummy function to fulfill concept.
+ void Init(int num_components) {}
+ bool DecodeTransformData(DecoderBuffer *buffer) {
+ DataTypeT max_quantized_value, center_value;
+ if (!buffer->Decode(&max_quantized_value))
+ return false;
+ if (buffer->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ if (!buffer->Decode(&center_value))
+ return false;
+ }
+ (void)center_value;
+ return this->set_max_quantized_value(max_quantized_value);
+ }
+
+ inline void ComputeOriginalValue(const DataType *pred_vals,
+ const CorrType *corr_vals,
+ DataType *out_orig_vals) const {
+ DRACO_DCHECK_LE(pred_vals[0], 2 * this->center_value());
+ DRACO_DCHECK_LE(pred_vals[1], 2 * this->center_value());
+ DRACO_DCHECK_LE(corr_vals[0], 2 * this->center_value());
+ DRACO_DCHECK_LE(corr_vals[1], 2 * this->center_value());
+
+ DRACO_DCHECK_LE(0, pred_vals[0]);
+ DRACO_DCHECK_LE(0, pred_vals[1]);
+ DRACO_DCHECK_LE(0, corr_vals[0]);
+ DRACO_DCHECK_LE(0, corr_vals[1]);
+
+ const Point2 pred = Point2(pred_vals[0], pred_vals[1]);
+ const Point2 corr = Point2(corr_vals[0], corr_vals[1]);
+ const Point2 orig = ComputeOriginalValue(pred, corr);
+
+ out_orig_vals[0] = orig[0];
+ out_orig_vals[1] = orig[1];
+ }
+
+ private:
+ Point2 ComputeOriginalValue(Point2 pred, const Point2 &corr) const {
+ const Point2 t(this->center_value(), this->center_value());
+ pred = pred - t;
+
+ const bool pred_is_in_diamond = this->IsInDiamond(pred[0], pred[1]);
+ if (!pred_is_in_diamond) {
+ this->InvertDiamond(&pred[0], &pred[1]);
+ }
+ Point2 orig = pred + corr;
+ orig[0] = this->ModMax(orig[0]);
+ orig[1] = this->ModMax(orig[1]);
+ if (!pred_is_in_diamond) {
+ this->InvertDiamond(&orig[0], &orig[1]);
+ }
+ orig = orig + t;
+ return orig;
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_DECODING_TRANSFORM_H_
+#endif
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_encoding_transform.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_encoding_transform.h
new file mode 100644
index 00000000000..4abfef66903
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_encoding_transform.h
@@ -0,0 +1,105 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_ENCODING_TRANSFORM_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_ENCODING_TRANSFORM_H_
+
+#include <cmath>
+
+#include "draco/compression/attributes/normal_compression_utils.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_base.h"
+#include "draco/core/encoder_buffer.h"
+#include "draco/core/macros.h"
+#include "draco/core/vector_d.h"
+
+namespace draco {
+
+// The transform works on octahedral coordinates for normals. The square is
+// subdivided into four inner triangles (diamond) and four outer triangles. The
+// inner triangles are associated with the upper part of the octahedron and the
+// outer triangles are associated with the lower part.
+// Given a prediction value P and the actual value Q that should be encoded,
+// this transform first checks if P is outside the diamond. If so, the outer
+// triangles are flipped towards the inside and vice versa. The actual
+// correction value is then based on the mapped P and Q values. This tends to
+// result in shorter correction vectors.
+// This is possible since the P value is also known by the decoder, see also
+// ComputeCorrection and ComputeOriginalValue functions.
+// Note that the tile is not periodic, which implies that the outer edges can
+// not be identified, which requires us to use an odd number of values on each
+// axis.
+// DataTypeT is expected to be some integral type.
+//
+template <typename DataTypeT>
+class PredictionSchemeNormalOctahedronEncodingTransform
+ : public PredictionSchemeNormalOctahedronTransformBase<DataTypeT> {
+ public:
+ typedef PredictionSchemeNormalOctahedronTransformBase<DataTypeT> Base;
+ typedef VectorD<DataTypeT, 2> Point2;
+ typedef DataTypeT CorrType;
+ typedef DataTypeT DataType;
+
+ // We expect the mod value to be of the form 2^b-1.
+ explicit PredictionSchemeNormalOctahedronEncodingTransform(
+ DataType max_quantized_value)
+ : Base(max_quantized_value) {}
+
+ void Init(const DataTypeT *orig_data, int size, int num_components) {}
+
+ bool EncodeTransformData(EncoderBuffer *buffer) {
+ buffer->Encode(this->max_quantized_value());
+ return true;
+ }
+
+ inline void ComputeCorrection(const DataType *orig_vals,
+ const DataType *pred_vals,
+ CorrType *out_corr_vals) const {
+ DRACO_DCHECK_LE(pred_vals[0], this->center_value() * 2);
+ DRACO_DCHECK_LE(pred_vals[1], this->center_value() * 2);
+ DRACO_DCHECK_LE(orig_vals[0], this->center_value() * 2);
+ DRACO_DCHECK_LE(orig_vals[1], this->center_value() * 2);
+ DRACO_DCHECK_LE(0, pred_vals[0]);
+ DRACO_DCHECK_LE(0, pred_vals[1]);
+ DRACO_DCHECK_LE(0, orig_vals[0]);
+ DRACO_DCHECK_LE(0, orig_vals[1]);
+
+ const Point2 orig = Point2(orig_vals[0], orig_vals[1]);
+ const Point2 pred = Point2(pred_vals[0], pred_vals[1]);
+ const Point2 corr = ComputeCorrection(orig, pred);
+
+ out_corr_vals[0] = corr[0];
+ out_corr_vals[1] = corr[1];
+ }
+
+ private:
+ Point2 ComputeCorrection(Point2 orig, Point2 pred) const {
+ const Point2 t(this->center_value(), this->center_value());
+ orig = orig - t;
+ pred = pred - t;
+
+ if (!this->IsInDiamond(pred[0], pred[1])) {
+ this->InvertDiamond(&orig[0], &orig[1]);
+ this->InvertDiamond(&pred[0], &pred[1]);
+ }
+
+ Point2 corr = orig - pred;
+ corr[0] = this->MakePositive(corr[0]);
+ corr[1] = this->MakePositive(corr[1]);
+ return corr;
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_ENCODING_TRANSFORM_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_base.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_base.h
new file mode 100644
index 00000000000..ff71fe86fdf
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_base.h
@@ -0,0 +1,89 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_TRANSFORM_BASE_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_TRANSFORM_BASE_H_
+
+#include <cmath>
+
+#include "draco/compression/attributes/normal_compression_utils.h"
+#include "draco/compression/config/compression_shared.h"
+#include "draco/core/bit_utils.h"
+#include "draco/core/macros.h"
+#include "draco/core/vector_d.h"
+
+namespace draco {
+
+// Base class containing shared functionality used by both encoding and decoding
+// octahedral normal prediction scheme transforms. See the encoding transform
+// for more details about the method.
+template <typename DataTypeT>
+class PredictionSchemeNormalOctahedronTransformBase {
+ public:
+ typedef VectorD<DataTypeT, 2> Point2;
+ typedef DataTypeT DataType;
+
+ PredictionSchemeNormalOctahedronTransformBase() {}
+ // We expect the mod value to be of the form 2^b-1.
+ explicit PredictionSchemeNormalOctahedronTransformBase(
+ DataType max_quantized_value) {
+ this->set_max_quantized_value(max_quantized_value);
+ }
+
+ static constexpr PredictionSchemeTransformType GetType() {
+ return PREDICTION_TRANSFORM_NORMAL_OCTAHEDRON;
+ }
+
+ // We can return true as we keep correction values positive.
+ bool AreCorrectionsPositive() const { return true; }
+
+ inline DataTypeT max_quantized_value() const {
+ return octahedron_tool_box_.max_quantized_value();
+ }
+ inline DataTypeT center_value() const {
+ return octahedron_tool_box_.center_value();
+ }
+ inline int32_t quantization_bits() const {
+ return octahedron_tool_box_.quantization_bits();
+ }
+
+ protected:
+ inline bool set_max_quantized_value(DataTypeT max_quantized_value) {
+ if (max_quantized_value % 2 == 0)
+ return false;
+ int q = MostSignificantBit(max_quantized_value) + 1;
+ return octahedron_tool_box_.SetQuantizationBits(q);
+ }
+
+ bool IsInDiamond(DataTypeT s, DataTypeT t) const {
+ return octahedron_tool_box_.IsInDiamond(s, t);
+ }
+ void InvertDiamond(DataTypeT *s, DataTypeT *t) const {
+ return octahedron_tool_box_.InvertDiamond(s, t);
+ }
+
+ int32_t ModMax(int32_t x) const { return octahedron_tool_box_.ModMax(x); }
+
+ // For correction values.
+ int32_t MakePositive(int32_t x) const {
+ return octahedron_tool_box_.MakePositive(x);
+ }
+
+ private:
+ OctahedronToolBox octahedron_tool_box_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_NORMAL_OCTAHEDRON_TRANSFORM_BASE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_test.cc b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_test.cc
new file mode 100644
index 00000000000..1001b19fa50
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_transform_test.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 "draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_encoding_transform.h"
+#include "draco/core/draco_test_base.h"
+
+namespace {
+
+class PredictionSchemeNormalOctahedronTransformTest : public ::testing::Test {
+ protected:
+ typedef draco::PredictionSchemeNormalOctahedronEncodingTransform<int32_t>
+ 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);
+ 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);
+}
+
+TEST_F(PredictionSchemeNormalOctahedronTransformTest, Interface) {
+ const Transform transform(15);
+ ASSERT_EQ(transform.max_quantized_value(), 15);
+ ASSERT_EQ(transform.center_value(), 7);
+ ASSERT_EQ(transform.quantization_bits(), 4);
+}
+
+} // namespace
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_decoding_transform.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_decoding_transform.h
new file mode 100644
index 00000000000..bd40df8d2f9
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_decoding_transform.h
@@ -0,0 +1,67 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_WRAP_DECODING_TRANSFORM_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_WRAP_DECODING_TRANSFORM_H_
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_transform_base.h"
+#include "draco/core/decoder_buffer.h"
+
+namespace draco {
+
+// PredictionSchemeWrapDecodingTransform unwraps values encoded with the
+// PredictionSchemeWrapEncodingTransform.
+// See prediction_scheme_wrap_transform_base.h for more details about the
+// method.
+template <typename DataTypeT, typename CorrTypeT = DataTypeT>
+class PredictionSchemeWrapDecodingTransform
+ : public PredictionSchemeWrapTransformBase<DataTypeT> {
+ public:
+ typedef CorrTypeT CorrType;
+ PredictionSchemeWrapDecodingTransform() {}
+
+ // Computes the original value from the input predicted value and the decoded
+ // corrections. Values out of the bounds of the input values are unwrapped.
+ inline void ComputeOriginalValue(const DataTypeT *predicted_vals,
+ const CorrTypeT *corr_vals,
+ DataTypeT *out_original_vals) const {
+ predicted_vals = this->ClampPredictedValue(predicted_vals);
+ for (int i = 0; i < this->num_components(); ++i) {
+ out_original_vals[i] = predicted_vals[i] + corr_vals[i];
+ if (out_original_vals[i] > this->max_value())
+ out_original_vals[i] -= this->max_dif();
+ else if (out_original_vals[i] < this->min_value())
+ out_original_vals[i] += this->max_dif();
+ }
+ }
+
+ bool DecodeTransformData(DecoderBuffer *buffer) {
+ DataTypeT min_value, max_value;
+ if (!buffer->Decode(&min_value))
+ return false;
+ if (!buffer->Decode(&max_value))
+ return false;
+ if (min_value > max_value)
+ return false;
+ this->set_min_value(min_value);
+ this->set_max_value(max_value);
+ if (!this->InitCorrectionBounds())
+ return false;
+ return true;
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_WRAP_DECODING_TRANSFORM_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_encoding_transform.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_encoding_transform.h
new file mode 100644
index 00000000000..8cd241f2e0a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_encoding_transform.h
@@ -0,0 +1,78 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_WRAP_ENCODING_TRANSFORM_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_WRAP_ENCODING_TRANSFORM_H_
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_transform_base.h"
+#include "draco/core/encoder_buffer.h"
+
+namespace draco {
+
+// PredictionSchemeWrapEncodingTransform wraps input values using the wrapping
+// scheme described in: prediction_scheme_wrap_transform_base.h .
+template <typename DataTypeT, typename CorrTypeT = DataTypeT>
+class PredictionSchemeWrapEncodingTransform
+ : public PredictionSchemeWrapTransformBase<DataTypeT> {
+ public:
+ typedef CorrTypeT CorrType;
+ PredictionSchemeWrapEncodingTransform() {}
+
+ void Init(const DataTypeT *orig_data, int size, int num_components) {
+ PredictionSchemeWrapTransformBase<DataTypeT>::Init(num_components);
+ // Go over the original values and compute the bounds.
+ if (size == 0)
+ return;
+ DataTypeT min_value = orig_data[0];
+ DataTypeT max_value = min_value;
+ for (int i = 1; i < size; ++i) {
+ if (orig_data[i] < min_value)
+ min_value = orig_data[i];
+ else if (orig_data[i] > max_value)
+ max_value = orig_data[i];
+ }
+ this->set_min_value(min_value);
+ this->set_max_value(max_value);
+ this->InitCorrectionBounds();
+ }
+
+ // Computes the corrections based on the input original value and the
+ // predicted value. Out of bound correction values are wrapped around the max
+ // range of input values.
+ inline void ComputeCorrection(const DataTypeT *original_vals,
+ const DataTypeT *predicted_vals,
+ CorrTypeT *out_corr_vals) const {
+ for (int i = 0; i < this->num_components(); ++i) {
+ predicted_vals = this->ClampPredictedValue(predicted_vals);
+ out_corr_vals[i] = original_vals[i] - predicted_vals[i];
+ // Wrap around if needed.
+ DataTypeT &corr_val = out_corr_vals[i];
+ if (corr_val < this->min_correction())
+ corr_val += this->max_dif();
+ else if (corr_val > this->max_correction())
+ corr_val -= this->max_dif();
+ }
+ }
+
+ bool EncodeTransformData(EncoderBuffer *buffer) {
+ // Store the input value range as it is needed by the decoder.
+ buffer->Encode(this->min_value());
+ buffer->Encode(this->max_value());
+ return true;
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_WRAP_ENCODING_TRANSFORM_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_transform_base.h b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_transform_base.h
new file mode 100644
index 00000000000..cf522269f1f
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_transform_base.h
@@ -0,0 +1,116 @@
+// 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_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_WRAP_TRANSFORM_BASE_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_WRAP_TRANSFORM_BASE_H_
+
+#include <vector>
+
+#include "draco/compression/config/compression_shared.h"
+#include "draco/core/macros.h"
+
+namespace draco {
+
+// PredictionSchemeWrapTransform uses the min and max bounds of the original
+// data to wrap stored correction values around these bounds centered at 0,
+// i.e., when the range of the original values O is between <MIN, MAX> and
+// N = MAX-MIN, we can then store any correction X = O - P, as:
+// X + N, if X < -N / 2
+// X - N, if X > N / 2
+// X otherwise
+// To unwrap this value, the decoder then simply checks whether the final
+// corrected value F = P + X is out of the bounds of the input values.
+// All out of bounds values are unwrapped using
+// F + N, if F < MIN
+// F - N, if F > MAX
+// This wrapping can reduce the number of unique values, which translates to a
+// better entropy of the stored values and better compression rates.
+template <typename DataTypeT>
+class PredictionSchemeWrapTransformBase {
+ public:
+ PredictionSchemeWrapTransformBase()
+ : num_components_(0),
+ min_value_(0),
+ max_value_(0),
+ max_dif_(0),
+ max_correction_(0),
+ min_correction_(0) {}
+
+ static constexpr PredictionSchemeTransformType GetType() {
+ return PREDICTION_TRANSFORM_WRAP;
+ }
+
+ void Init(int num_components) {
+ num_components_ = num_components;
+ clamped_value_.resize(num_components);
+ }
+
+ bool AreCorrectionsPositive() const { return false; }
+
+ inline const DataTypeT *ClampPredictedValue(
+ const DataTypeT *predicted_val) const {
+ for (int i = 0; i < this->num_components(); ++i) {
+ if (predicted_val[i] > max_value_)
+ clamped_value_[i] = max_value_;
+ else if (predicted_val[i] < min_value_)
+ clamped_value_[i] = min_value_;
+ else
+ clamped_value_[i] = predicted_val[i];
+ }
+ return &clamped_value_[0];
+ }
+
+ // TODO(hemmer): Consider refactoring to avoid this dummy.
+ int quantization_bits() const {
+ DRACO_DCHECK(false);
+ return -1;
+ }
+
+ protected:
+ bool InitCorrectionBounds() {
+ const int64_t dif =
+ static_cast<int64_t>(max_value_) - static_cast<int64_t>(min_value_);
+ if (dif < 0 || dif >= std::numeric_limits<DataTypeT>::max())
+ return false;
+ max_dif_ = 1 + static_cast<DataTypeT>(dif);
+ max_correction_ = max_dif_ / 2;
+ min_correction_ = -max_correction_;
+ if ((max_dif_ & 1) == 0)
+ max_correction_ -= 1;
+ return true;
+ }
+
+ inline int num_components() const { return num_components_; }
+ inline DataTypeT min_value() const { return min_value_; }
+ inline void set_min_value(const DataTypeT &v) { min_value_ = v; }
+ inline DataTypeT max_value() const { return max_value_; }
+ inline void set_max_value(const DataTypeT &v) { max_value_ = v; }
+ inline DataTypeT max_dif() const { return max_dif_; }
+ inline DataTypeT min_correction() const { return min_correction_; }
+ inline DataTypeT max_correction() const { return max_correction_; }
+
+ private:
+ int num_components_;
+ DataTypeT min_value_;
+ DataTypeT max_value_;
+ DataTypeT max_dif_;
+ DataTypeT max_correction_;
+ DataTypeT min_correction_;
+ // This is in fact just a tmp variable to avoid reallocation.
+ mutable std::vector<DataTypeT> clamped_value_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_PREDICTION_SCHEMES_PREDICTION_SCHEME_WRAP_TRANSFORM_BASE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoder.cc b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoder.cc
new file mode 100644
index 00000000000..9e33cef3428
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoder.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 "draco/compression/attributes/sequential_attribute_decoder.h"
+
+namespace draco {
+
+SequentialAttributeDecoder::SequentialAttributeDecoder()
+ : decoder_(nullptr), attribute_(nullptr), attribute_id_(-1) {}
+
+bool SequentialAttributeDecoder::Init(PointCloudDecoder *decoder,
+ int attribute_id) {
+ decoder_ = decoder;
+ attribute_ = decoder->point_cloud()->attribute(attribute_id);
+ attribute_id_ = attribute_id;
+ return true;
+}
+
+bool SequentialAttributeDecoder::InitializeStandalone(
+ PointAttribute *attribute) {
+ attribute_ = attribute;
+ attribute_id_ = -1;
+ return true;
+}
+
+bool SequentialAttributeDecoder::DecodePortableAttribute(
+ const std::vector<PointIndex> &point_ids, DecoderBuffer *in_buffer) {
+ if (attribute_->num_components() <= 0 || !attribute_->Reset(point_ids.size()))
+ return false;
+ if (!DecodeValues(point_ids, in_buffer))
+ return false;
+ return true;
+}
+
+bool SequentialAttributeDecoder::DecodeDataNeededByPortableTransform(
+ const std::vector<PointIndex> &point_ids, DecoderBuffer *in_buffer) {
+ // Default implementation does not apply any transform.
+ return true;
+}
+
+bool SequentialAttributeDecoder::TransformAttributeToOriginalFormat(
+ const std::vector<PointIndex> &point_ids) {
+ // Default implementation does not apply any transform.
+ return true;
+}
+
+const PointAttribute *SequentialAttributeDecoder::GetPortableAttribute() {
+ // If needed, copy point to attribute value index mapping from the final
+ // attribute to the portable attribute.
+ if (!attribute_->is_mapping_identity() && portable_attribute_ &&
+ portable_attribute_->is_mapping_identity()) {
+ portable_attribute_->SetExplicitMapping(attribute_->indices_map_size());
+ for (PointIndex i(0);
+ i < static_cast<uint32_t>(attribute_->indices_map_size()); ++i) {
+ portable_attribute_->SetPointMapEntry(i, attribute_->mapped_index(i));
+ }
+ }
+ return portable_attribute_.get();
+}
+
+bool SequentialAttributeDecoder::InitPredictionScheme(
+ PredictionSchemeInterface *ps) {
+ for (int i = 0; i < ps->GetNumParentAttributes(); ++i) {
+ const int att_id = decoder_->point_cloud()->GetNamedAttributeId(
+ ps->GetParentAttributeType(i));
+ if (att_id == -1)
+ return false; // Requested attribute does not exist.
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0)) {
+ if (!ps->SetParentAttribute(decoder_->point_cloud()->attribute(att_id))) {
+ return false;
+ }
+ } else
+#endif
+ {
+ const PointAttribute *const pa = decoder_->GetPortableAttribute(att_id);
+ if (pa == nullptr || !ps->SetParentAttribute(pa)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool SequentialAttributeDecoder::DecodeValues(
+ const std::vector<PointIndex> &point_ids, DecoderBuffer *in_buffer) {
+ const int32_t num_values = static_cast<uint32_t>(point_ids.size());
+ const int entry_size = static_cast<int>(attribute_->byte_stride());
+ std::unique_ptr<uint8_t[]> value_data_ptr(new uint8_t[entry_size]);
+ uint8_t *const value_data = value_data_ptr.get();
+ int out_byte_pos = 0;
+ // Decode raw attribute values in their original format.
+ for (int i = 0; i < num_values; ++i) {
+ if (!in_buffer->Decode(value_data, entry_size))
+ return false;
+ attribute_->buffer()->Write(out_byte_pos, value_data, entry_size);
+ out_byte_pos += entry_size;
+ }
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoder.h
new file mode 100644
index 00000000000..721a587fc2a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoder.h
@@ -0,0 +1,87 @@
+// 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_COMPRESSION_ATTRIBUTES_SEQUENTIAL_ATTRIBUTE_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_ATTRIBUTE_DECODER_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_interface.h"
+#include "draco/compression/point_cloud/point_cloud_decoder.h"
+
+namespace draco {
+
+// A base class for decoding attribute values encoded by the
+// SequentialAttributeEncoder.
+class SequentialAttributeDecoder {
+ public:
+ SequentialAttributeDecoder();
+ virtual ~SequentialAttributeDecoder() = default;
+
+ virtual bool Init(PointCloudDecoder *decoder, int attribute_id);
+
+ // Initialization for a specific attribute. This can be used mostly for
+ // standalone decoding of an attribute without an PointCloudDecoder.
+ virtual bool InitializeStandalone(PointAttribute *attribute);
+
+ // Performs lossless decoding of the portable attribute data.
+ virtual bool DecodePortableAttribute(const std::vector<PointIndex> &point_ids,
+ DecoderBuffer *in_buffer);
+
+ // Decodes any data needed to revert portable transform of the decoded
+ // attribute.
+ virtual bool DecodeDataNeededByPortableTransform(
+ const std::vector<PointIndex> &point_ids, DecoderBuffer *in_buffer);
+
+ // Reverts transformation performed by encoder in
+ // SequentialAttributeEncoder::TransformAttributeToPortableFormat() method.
+ virtual bool TransformAttributeToOriginalFormat(
+ const std::vector<PointIndex> &point_ids);
+
+ const PointAttribute *GetPortableAttribute();
+
+ const PointAttribute *attribute() const { return attribute_; }
+ PointAttribute *attribute() { return attribute_; }
+ int attribute_id() const { return attribute_id_; }
+ PointCloudDecoder *decoder() const { return decoder_; }
+
+ protected:
+ // Should be used to initialize newly created prediction scheme.
+ // Returns false when the initialization failed (in which case the scheme
+ // cannot be used).
+ virtual bool InitPredictionScheme(PredictionSchemeInterface *ps);
+
+ // The actual implementation of the attribute decoding. Should be overridden
+ // for specialized decoders.
+ virtual bool DecodeValues(const std::vector<PointIndex> &point_ids,
+ DecoderBuffer *in_buffer);
+
+ void SetPortableAttribute(std::unique_ptr<PointAttribute> att) {
+ portable_attribute_ = std::move(att);
+ }
+
+ PointAttribute *portable_attribute() { return portable_attribute_.get(); }
+
+ private:
+ PointCloudDecoder *decoder_;
+ PointAttribute *attribute_;
+ int attribute_id_;
+
+ // Storage for decoded portable attribute (after lossless decoding).
+ std::unique_ptr<PointAttribute> portable_attribute_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_ATTRIBUTE_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoders_controller.cc b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoders_controller.cc
new file mode 100644
index 00000000000..fcd295039f6
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoders_controller.cc
@@ -0,0 +1,136 @@
+// 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 "draco/compression/attributes/sequential_attribute_decoders_controller.h"
+#include "draco/compression/attributes/sequential_normal_attribute_decoder.h"
+#include "draco/compression/attributes/sequential_quantization_attribute_decoder.h"
+#include "draco/compression/config/compression_shared.h"
+
+namespace draco {
+
+SequentialAttributeDecodersController::SequentialAttributeDecodersController(
+ std::unique_ptr<PointsSequencer> sequencer)
+ : sequencer_(std::move(sequencer)) {}
+
+bool SequentialAttributeDecodersController::DecodeAttributesDecoderData(
+ DecoderBuffer *buffer) {
+ if (!AttributesDecoder::DecodeAttributesDecoderData(buffer))
+ return false;
+ // Decode unique ids of all sequential encoders and create them.
+ const int32_t num_attributes = GetNumAttributes();
+ sequential_decoders_.resize(num_attributes);
+ for (int i = 0; i < num_attributes; ++i) {
+ uint8_t decoder_type;
+ if (!buffer->Decode(&decoder_type))
+ return false;
+ // Create the decoder from the id.
+ sequential_decoders_[i] = CreateSequentialDecoder(decoder_type);
+ if (!sequential_decoders_[i])
+ return false;
+ if (!sequential_decoders_[i]->Init(GetDecoder(), GetAttributeId(i)))
+ return false;
+ }
+ return true;
+}
+
+bool SequentialAttributeDecodersController::DecodeAttributes(
+ DecoderBuffer *buffer) {
+ if (!sequencer_ || !sequencer_->GenerateSequence(&point_ids_))
+ return false;
+ // Initialize point to attribute value mapping for all decoded attributes.
+ const int32_t num_attributes = GetNumAttributes();
+ for (int i = 0; i < num_attributes; ++i) {
+ PointAttribute *const pa =
+ GetDecoder()->point_cloud()->attribute(GetAttributeId(i));
+ if (!sequencer_->UpdatePointToAttributeIndexMapping(pa))
+ return false;
+ }
+ return AttributesDecoder::DecodeAttributes(buffer);
+}
+
+bool SequentialAttributeDecodersController::DecodePortableAttributes(
+ DecoderBuffer *in_buffer) {
+ const int32_t num_attributes = GetNumAttributes();
+ for (int i = 0; i < num_attributes; ++i) {
+ if (!sequential_decoders_[i]->DecodePortableAttribute(point_ids_,
+ in_buffer))
+ return false;
+ }
+ return true;
+}
+
+bool SequentialAttributeDecodersController::
+ DecodeDataNeededByPortableTransforms(DecoderBuffer *in_buffer) {
+ const int32_t num_attributes = GetNumAttributes();
+ for (int i = 0; i < num_attributes; ++i) {
+ if (!sequential_decoders_[i]->DecodeDataNeededByPortableTransform(
+ point_ids_, in_buffer))
+ return false;
+ }
+ return true;
+}
+
+bool SequentialAttributeDecodersController::
+ TransformAttributesToOriginalFormat() {
+ const int32_t num_attributes = GetNumAttributes();
+ for (int i = 0; i < num_attributes; ++i) {
+ // Check whether the attribute transform should be skipped.
+ if (GetDecoder()->options()) {
+ const PointAttribute *const attribute =
+ sequential_decoders_[i]->attribute();
+ const PointAttribute *const portable_attribute =
+ sequential_decoders_[i]->GetPortableAttribute();
+ if (portable_attribute &&
+ GetDecoder()->options()->GetAttributeBool(
+ attribute->attribute_type(), "skip_attribute_transform", false)) {
+ // Attribute transform should not be performed. In this case, we replace
+ // the output geometry attribute with the portable attribute.
+ // TODO(ostava): We can potentially avoid this copy by introducing a new
+ // mechanism that would allow to use the final attributes as portable
+ // attributes for predictors that may need them.
+ sequential_decoders_[i]->attribute()->CopyFrom(*portable_attribute);
+ continue;
+ }
+ }
+ if (!sequential_decoders_[i]->TransformAttributeToOriginalFormat(
+ point_ids_))
+ return false;
+ }
+ return true;
+}
+
+std::unique_ptr<SequentialAttributeDecoder>
+SequentialAttributeDecodersController::CreateSequentialDecoder(
+ uint8_t decoder_type) {
+ switch (decoder_type) {
+ case SEQUENTIAL_ATTRIBUTE_ENCODER_GENERIC:
+ return std::unique_ptr<SequentialAttributeDecoder>(
+ new SequentialAttributeDecoder());
+ case SEQUENTIAL_ATTRIBUTE_ENCODER_INTEGER:
+ return std::unique_ptr<SequentialAttributeDecoder>(
+ new SequentialIntegerAttributeDecoder());
+ case SEQUENTIAL_ATTRIBUTE_ENCODER_QUANTIZATION:
+ return std::unique_ptr<SequentialAttributeDecoder>(
+ new SequentialQuantizationAttributeDecoder());
+ case SEQUENTIAL_ATTRIBUTE_ENCODER_NORMALS:
+ return std::unique_ptr<SequentialNormalAttributeDecoder>(
+ new SequentialNormalAttributeDecoder());
+ default:
+ break;
+ }
+ // Unknown or unsupported decoder type.
+ return nullptr;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoders_controller.h b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoders_controller.h
new file mode 100644
index 00000000000..9fd5e490741
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_decoders_controller.h
@@ -0,0 +1,60 @@
+// 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_COMPRESSION_ATTRIBUTES_SEQUENTIAL_ATTRIBUTE_DECODERS_CONTROLLER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_ATTRIBUTE_DECODERS_CONTROLLER_H_
+
+#include "draco/compression/attributes/attributes_decoder.h"
+#include "draco/compression/attributes/points_sequencer.h"
+#include "draco/compression/attributes/sequential_attribute_decoder.h"
+
+namespace draco {
+
+// A basic implementation of an attribute decoder that decodes data encoded by
+// the SequentialAttributeEncodersController class. The
+// SequentialAttributeDecodersController creates a single
+// AttributeIndexedValuesDecoder for each of the decoded attribute, where the
+// type of the values decoder is determined by the unique identifier that was
+// encoded by the encoder.
+class SequentialAttributeDecodersController : public AttributesDecoder {
+ public:
+ explicit SequentialAttributeDecodersController(
+ std::unique_ptr<PointsSequencer> sequencer);
+
+ bool DecodeAttributesDecoderData(DecoderBuffer *buffer) override;
+ bool DecodeAttributes(DecoderBuffer *buffer) override;
+ const PointAttribute *GetPortableAttribute(
+ int32_t point_attribute_id) override {
+ const int32_t loc_id = GetLocalIdForPointAttribute(point_attribute_id);
+ if (loc_id < 0)
+ return nullptr;
+ return sequential_decoders_[loc_id]->GetPortableAttribute();
+ }
+
+ protected:
+ bool DecodePortableAttributes(DecoderBuffer *in_buffer) override;
+ bool DecodeDataNeededByPortableTransforms(DecoderBuffer *in_buffer) override;
+ bool TransformAttributesToOriginalFormat() override;
+ virtual std::unique_ptr<SequentialAttributeDecoder> CreateSequentialDecoder(
+ uint8_t decoder_type);
+
+ private:
+ std::vector<std::unique_ptr<SequentialAttributeDecoder>> sequential_decoders_;
+ std::vector<PointIndex> point_ids_;
+ std::unique_ptr<PointsSequencer> sequencer_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_ATTRIBUTE_DECODERS_CONTROLLER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoder.cc b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoder.cc
new file mode 100644
index 00000000000..95547c005e7
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoder.cc
@@ -0,0 +1,104 @@
+// 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 "draco/compression/attributes/sequential_attribute_encoder.h"
+
+namespace draco {
+
+SequentialAttributeEncoder::SequentialAttributeEncoder()
+ : encoder_(nullptr),
+ attribute_(nullptr),
+ attribute_id_(-1),
+ is_parent_encoder_(false) {}
+
+bool SequentialAttributeEncoder::Init(PointCloudEncoder *encoder,
+ int attribute_id) {
+ encoder_ = encoder;
+ attribute_ = encoder_->point_cloud()->attribute(attribute_id);
+ attribute_id_ = attribute_id;
+ return true;
+}
+
+bool SequentialAttributeEncoder::InitializeStandalone(
+ PointAttribute *attribute) {
+ attribute_ = attribute;
+ attribute_id_ = -1;
+ return true;
+}
+
+bool SequentialAttributeEncoder::TransformAttributeToPortableFormat(
+ const std::vector<PointIndex> &point_ids) {
+ // Default implementation doesn't transform the input data.
+ return true;
+}
+
+bool SequentialAttributeEncoder::EncodePortableAttribute(
+ const std::vector<PointIndex> &point_ids, EncoderBuffer *out_buffer) {
+ // Lossless encoding of the input values.
+ if (!EncodeValues(point_ids, out_buffer))
+ return false;
+ return true;
+}
+
+bool SequentialAttributeEncoder::EncodeDataNeededByPortableTransform(
+ EncoderBuffer *out_buffer) {
+ // Default implementation doesn't transform the input data.
+ return true;
+}
+
+bool SequentialAttributeEncoder::EncodeValues(
+ const std::vector<PointIndex> &point_ids, EncoderBuffer *out_buffer) {
+ const int entry_size = static_cast<int>(attribute_->byte_stride());
+ const std::unique_ptr<uint8_t[]> 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 (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);
+ }
+ return true;
+}
+
+void SequentialAttributeEncoder::MarkParentAttribute() {
+ is_parent_encoder_ = true;
+}
+
+bool SequentialAttributeEncoder::InitPredictionScheme(
+ PredictionSchemeInterface *ps) {
+ for (int i = 0; i < ps->GetNumParentAttributes(); ++i) {
+ const int att_id = encoder_->point_cloud()->GetNamedAttributeId(
+ ps->GetParentAttributeType(i));
+ if (att_id == -1)
+ return false; // Requested attribute does not exist.
+ parent_attributes_.push_back(att_id);
+ encoder_->MarkParentAttribute(att_id);
+ }
+ return true;
+}
+
+bool SequentialAttributeEncoder::SetPredictionSchemeParentAttributes(
+ PredictionSchemeInterface *ps) {
+ for (int i = 0; i < ps->GetNumParentAttributes(); ++i) {
+ const int att_id = encoder_->point_cloud()->GetNamedAttributeId(
+ ps->GetParentAttributeType(i));
+ if (att_id == -1)
+ return false; // Requested attribute does not exist.
+ if (!ps->SetParentAttribute(encoder_->GetPortableAttribute(att_id)))
+ return false;
+ }
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoder.h
new file mode 100644
index 00000000000..540ba947077
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoder.h
@@ -0,0 +1,133 @@
+// 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_COMPRESSION_ATTRIBUTES_SEQUENTIAL_ATTRIBUTE_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_ATTRIBUTE_ENCODER_H_
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_interface.h"
+#include "draco/compression/point_cloud/point_cloud_encoder.h"
+
+namespace draco {
+
+// A base class for encoding attribute values of a single attribute using a
+// given sequence of point ids. The default implementation encodes all attribute
+// values directly to the buffer but derived classes can perform any custom
+// encoding (such as quantization) by overriding the EncodeValues() method.
+class SequentialAttributeEncoder {
+ public:
+ SequentialAttributeEncoder();
+ virtual ~SequentialAttributeEncoder() = default;
+
+ // Method that can be used for custom initialization of an attribute encoder,
+ // such as creation of prediction schemes and initialization of attribute
+ // encoder dependencies.
+ // |encoder| is the parent PointCloudEncoder,
+ // |attribute_id| is the id of the attribute that is being encoded by this
+ // encoder.
+ // This method is automatically called by the PointCloudEncoder after all
+ // attribute encoders are created and it should not be called explicitly from
+ // other places.
+ virtual bool Init(PointCloudEncoder *encoder, int attribute_id);
+
+ // Initialization for a specific attribute. This can be used mostly for
+ // standalone encoding of an attribute without an PointCloudEncoder.
+ virtual bool InitializeStandalone(PointAttribute *attribute);
+
+ // Transforms attribute data into format that is going to be encoded
+ // losslessly. The transform itself can be lossy.
+ virtual bool TransformAttributeToPortableFormat(
+ const std::vector<PointIndex> &point_ids);
+
+ // Performs lossless encoding of the transformed attribute data.
+ virtual bool EncodePortableAttribute(const std::vector<PointIndex> &point_ids,
+ EncoderBuffer *out_buffer);
+
+ // Encodes any data related to the portable attribute transform.
+ virtual bool EncodeDataNeededByPortableTransform(EncoderBuffer *out_buffer);
+
+ virtual bool IsLossyEncoder() const { return false; }
+
+ int NumParentAttributes() const {
+ return static_cast<int>(parent_attributes_.size());
+ }
+ int GetParentAttributeId(int i) const { return parent_attributes_[i]; }
+
+ const PointAttribute *GetPortableAttribute() const {
+ if (portable_attribute_ != nullptr)
+ return portable_attribute_.get();
+ return attribute();
+ }
+
+ // Called when this attribute encoder becomes a parent encoder of another
+ // encoder.
+ void MarkParentAttribute();
+
+ virtual uint8_t GetUniqueId() const {
+ return SEQUENTIAL_ATTRIBUTE_ENCODER_GENERIC;
+ }
+
+ const PointAttribute *attribute() const { return attribute_; }
+ int attribute_id() const { return attribute_id_; }
+ PointCloudEncoder *encoder() const { return encoder_; }
+
+ protected:
+ // Should be used to initialize newly created prediction scheme.
+ // Returns false when the initialization failed (in which case the scheme
+ // cannot be used).
+ virtual bool InitPredictionScheme(PredictionSchemeInterface *ps);
+
+ // Sets parent attributes for a given prediction scheme. Must be called
+ // after all prediction schemes are initialized, but before the prediction
+ // scheme is used.
+ virtual bool SetPredictionSchemeParentAttributes(
+ PredictionSchemeInterface *ps);
+
+ // Encodes all attribute values in the specified order. Should be overridden
+ // for specialized encoders.
+ virtual bool EncodeValues(const std::vector<PointIndex> &point_ids,
+ EncoderBuffer *out_buffer);
+
+ bool is_parent_encoder() const { return is_parent_encoder_; }
+
+ void SetPortableAttribute(std::unique_ptr<PointAttribute> att) {
+ portable_attribute_ = std::move(att);
+ }
+
+ // Returns a mutable attribute that should be filled by derived encoders with
+ // the transformed version of the attribute data. To get a public const
+ // version, use the GetPortableAttribute() method.
+ PointAttribute *portable_attribute() { return portable_attribute_.get(); }
+
+ private:
+ PointCloudEncoder *encoder_;
+ const PointAttribute *attribute_;
+ int attribute_id_;
+
+ // List of attribute encoders that need to be encoded before this attribute.
+ // E.g. The parent attributes may be used to predict values used by this
+ // attribute encoder.
+ std::vector<int32_t> parent_attributes_;
+
+ bool is_parent_encoder_;
+
+ // Attribute that stores transformed data from the source attribute after it
+ // is processed through the ApplyTransform() method. Attribute data stored
+ // within this attribute is guaranteed to be encoded losslessly and it can be
+ // safely used for prediction of other attributes.
+ std::unique_ptr<PointAttribute> portable_attribute_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_ATTRIBUTE_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoders_controller.cc b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoders_controller.cc
new file mode 100644
index 00000000000..467508b226f
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoders_controller.cc
@@ -0,0 +1,143 @@
+// 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 "draco/compression/attributes/sequential_attribute_encoders_controller.h"
+#include "draco/compression/attributes/sequential_normal_attribute_encoder.h"
+#include "draco/compression/attributes/sequential_quantization_attribute_encoder.h"
+#include "draco/compression/point_cloud/point_cloud_encoder.h"
+
+namespace draco {
+
+SequentialAttributeEncodersController::SequentialAttributeEncodersController(
+ std::unique_ptr<PointsSequencer> sequencer)
+ : sequencer_(std::move(sequencer)) {}
+
+SequentialAttributeEncodersController::SequentialAttributeEncodersController(
+ std::unique_ptr<PointsSequencer> sequencer, int att_id)
+ : AttributesEncoder(att_id), sequencer_(std::move(sequencer)) {}
+
+bool SequentialAttributeEncodersController::Init(PointCloudEncoder *encoder,
+ const PointCloud *pc) {
+ if (!AttributesEncoder::Init(encoder, pc))
+ return false;
+ if (!CreateSequentialEncoders())
+ return false;
+ // Initialize all value encoders.
+ for (uint32_t i = 0; i < num_attributes(); ++i) {
+ const int32_t att_id = GetAttributeId(i);
+ if (!sequential_encoders_[i]->Init(encoder, att_id))
+ return false;
+ }
+ return true;
+}
+
+bool SequentialAttributeEncodersController::EncodeAttributesEncoderData(
+ EncoderBuffer *out_buffer) {
+ if (!AttributesEncoder::EncodeAttributesEncoderData(out_buffer))
+ return false;
+ // Encode a unique id of every sequential encoder.
+ for (uint32_t i = 0; i < sequential_encoders_.size(); ++i) {
+ out_buffer->Encode(sequential_encoders_[i]->GetUniqueId());
+ }
+ return true;
+}
+
+bool SequentialAttributeEncodersController::EncodeAttributes(
+ EncoderBuffer *buffer) {
+ if (!sequencer_ || !sequencer_->GenerateSequence(&point_ids_))
+ return false;
+ return AttributesEncoder::EncodeAttributes(buffer);
+}
+
+bool SequentialAttributeEncodersController::
+ TransformAttributesToPortableFormat() {
+ for (uint32_t i = 0; i < sequential_encoders_.size(); ++i) {
+ if (!sequential_encoders_[i]->TransformAttributeToPortableFormat(
+ point_ids_))
+ return false;
+ }
+ return true;
+}
+
+bool SequentialAttributeEncodersController::EncodePortableAttributes(
+ EncoderBuffer *out_buffer) {
+ for (uint32_t i = 0; i < sequential_encoders_.size(); ++i) {
+ if (!sequential_encoders_[i]->EncodePortableAttribute(point_ids_,
+ out_buffer))
+ return false;
+ }
+ return true;
+}
+
+bool SequentialAttributeEncodersController::
+ EncodeDataNeededByPortableTransforms(EncoderBuffer *out_buffer) {
+ for (uint32_t i = 0; i < sequential_encoders_.size(); ++i) {
+ if (!sequential_encoders_[i]->EncodeDataNeededByPortableTransform(
+ out_buffer))
+ return false;
+ }
+ return true;
+}
+
+bool SequentialAttributeEncodersController::CreateSequentialEncoders() {
+ sequential_encoders_.resize(num_attributes());
+ for (uint32_t i = 0; i < num_attributes(); ++i) {
+ sequential_encoders_[i] = CreateSequentialEncoder(i);
+ if (sequential_encoders_[i] == nullptr)
+ return false;
+ if (i < sequential_encoder_marked_as_parent_.size()) {
+ if (sequential_encoder_marked_as_parent_[i])
+ sequential_encoders_[i]->MarkParentAttribute();
+ }
+ }
+ return true;
+}
+
+std::unique_ptr<SequentialAttributeEncoder>
+SequentialAttributeEncodersController::CreateSequentialEncoder(int i) {
+ const int32_t att_id = GetAttributeId(i);
+ const PointAttribute *const att = encoder()->point_cloud()->attribute(att_id);
+
+ switch (att->data_type()) {
+ case DT_UINT8:
+ case DT_INT8:
+ case DT_UINT16:
+ case DT_INT16:
+ case DT_UINT32:
+ case DT_INT32:
+ return std::unique_ptr<SequentialAttributeEncoder>(
+ new SequentialIntegerAttributeEncoder());
+ case DT_FLOAT32:
+ if (encoder()->options()->GetAttributeInt(att_id, "quantization_bits",
+ -1) > 0) {
+ if (att->attribute_type() == GeometryAttribute::NORMAL) {
+ // We currently only support normals with float coordinates
+ // and must be quantized.
+ return std::unique_ptr<SequentialAttributeEncoder>(
+ new SequentialNormalAttributeEncoder());
+ } else {
+ return std::unique_ptr<SequentialAttributeEncoder>(
+ new SequentialQuantizationAttributeEncoder());
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ // Return the default attribute encoder.
+ return std::unique_ptr<SequentialAttributeEncoder>(
+ new SequentialAttributeEncoder());
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoders_controller.h b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoders_controller.h
new file mode 100644
index 00000000000..d5574abef8d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_attribute_encoders_controller.h
@@ -0,0 +1,110 @@
+// 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_COMPRESSION_ATTRIBUTES_SEQUENTIAL_ATTRIBUTE_ENCODERS_CONTROLLER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_ATTRIBUTE_ENCODERS_CONTROLLER_H_
+
+#include "draco/compression/attributes/attributes_encoder.h"
+#include "draco/compression/attributes/points_sequencer.h"
+#include "draco/compression/attributes/sequential_attribute_encoder.h"
+
+namespace draco {
+
+// A basic implementation of an attribute encoder that can be used to encode
+// an arbitrary set of attributes. The encoder creates a sequential attribute
+// encoder for each encoded attribute (see sequential_attribute_encoder.h) and
+// then it encodes all attribute values in an order defined by a point sequence
+// generated in the GeneratePointSequence() method. The default implementation
+// generates a linear sequence of all points, but derived classes can generate
+// any custom sequence.
+class SequentialAttributeEncodersController : public AttributesEncoder {
+ public:
+ explicit SequentialAttributeEncodersController(
+ std::unique_ptr<PointsSequencer> sequencer);
+ SequentialAttributeEncodersController(
+ std::unique_ptr<PointsSequencer> sequencer, int point_attrib_id);
+
+ bool Init(PointCloudEncoder *encoder, const PointCloud *pc) override;
+ bool EncodeAttributesEncoderData(EncoderBuffer *out_buffer) override;
+ bool EncodeAttributes(EncoderBuffer *buffer) override;
+ uint8_t GetUniqueId() const override { return BASIC_ATTRIBUTE_ENCODER; }
+
+ int NumParentAttributes(int32_t point_attribute_id) const override {
+ const int32_t loc_id = GetLocalIdForPointAttribute(point_attribute_id);
+ if (loc_id < 0)
+ return 0;
+ return sequential_encoders_[loc_id]->NumParentAttributes();
+ }
+
+ int GetParentAttributeId(int32_t point_attribute_id,
+ int32_t parent_i) const override {
+ const int32_t loc_id = GetLocalIdForPointAttribute(point_attribute_id);
+ if (loc_id < 0)
+ return -1;
+ return sequential_encoders_[loc_id]->GetParentAttributeId(parent_i);
+ }
+
+ bool MarkParentAttribute(int32_t point_attribute_id) override {
+ const int32_t loc_id = GetLocalIdForPointAttribute(point_attribute_id);
+ if (loc_id < 0)
+ return false;
+ // Mark the attribute encoder as parent (even when if it is not created
+ // yet).
+ if (sequential_encoder_marked_as_parent_.size() <= loc_id) {
+ sequential_encoder_marked_as_parent_.resize(loc_id + 1, false);
+ }
+ sequential_encoder_marked_as_parent_[loc_id] = true;
+
+ if (sequential_encoders_.size() <= loc_id)
+ return true; // Sequential encoders not generated yet.
+ sequential_encoders_[loc_id]->MarkParentAttribute();
+ return true;
+ }
+
+ const PointAttribute *GetPortableAttribute(
+ int32_t point_attribute_id) override {
+ const int32_t loc_id = GetLocalIdForPointAttribute(point_attribute_id);
+ if (loc_id < 0)
+ return nullptr;
+ return sequential_encoders_[loc_id]->GetPortableAttribute();
+ }
+
+ protected:
+ bool TransformAttributesToPortableFormat() override;
+ bool EncodePortableAttributes(EncoderBuffer *out_buffer) override;
+ bool EncodeDataNeededByPortableTransforms(EncoderBuffer *out_buffer) override;
+
+ // Creates all sequential encoders (one for each attribute associated with the
+ // encoder).
+ virtual bool CreateSequentialEncoders();
+
+ // Create a sequential encoder for a given attribute based on the attribute
+ // type
+ // and the provided encoder options.
+ virtual std::unique_ptr<SequentialAttributeEncoder> CreateSequentialEncoder(
+ int i);
+
+ private:
+ std::vector<std::unique_ptr<SequentialAttributeEncoder>> sequential_encoders_;
+
+ // Flag for each sequential attribute encoder indicating whether it was marked
+ // as parent attribute or not.
+ std::vector<bool> sequential_encoder_marked_as_parent_;
+ std::vector<PointIndex> point_ids_;
+ std::unique_ptr<PointsSequencer> sequencer_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_ATTRIBUTE_ENCODERS_CONTROLLER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_decoder.cc b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_decoder.cc
new file mode 100644
index 00000000000..8d9210af912
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_decoder.cc
@@ -0,0 +1,213 @@
+// 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 "draco/compression/attributes/sequential_integer_attribute_decoder.h"
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_decoder_factory.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_decoding_transform.h"
+#include "draco/compression/entropy/symbol_decoding.h"
+
+namespace draco {
+
+SequentialIntegerAttributeDecoder::SequentialIntegerAttributeDecoder() {}
+
+bool SequentialIntegerAttributeDecoder::Init(PointCloudDecoder *decoder,
+ int attribute_id) {
+ if (!SequentialAttributeDecoder::Init(decoder, attribute_id))
+ return false;
+ return true;
+}
+
+bool SequentialIntegerAttributeDecoder::TransformAttributeToOriginalFormat(
+ const std::vector<PointIndex> &point_ids) {
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder() &&
+ decoder()->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0))
+ return true; // Don't revert the transform here for older files.
+#endif
+ return StoreValues(static_cast<uint32_t>(point_ids.size()));
+}
+
+bool SequentialIntegerAttributeDecoder::DecodeValues(
+ const std::vector<PointIndex> &point_ids, DecoderBuffer *in_buffer) {
+ // Decode prediction scheme.
+ int8_t prediction_scheme_method;
+ in_buffer->Decode(&prediction_scheme_method);
+ if (prediction_scheme_method != PREDICTION_NONE) {
+ int8_t prediction_transform_type;
+ in_buffer->Decode(&prediction_transform_type);
+ prediction_scheme_ = CreateIntPredictionScheme(
+ static_cast<PredictionSchemeMethod>(prediction_scheme_method),
+ static_cast<PredictionSchemeTransformType>(prediction_transform_type));
+ }
+
+ if (prediction_scheme_) {
+ if (!InitPredictionScheme(prediction_scheme_.get()))
+ return false;
+ }
+
+ if (!DecodeIntegerValues(point_ids, in_buffer))
+ return false;
+
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ const int32_t num_values = static_cast<uint32_t>(point_ids.size());
+ if (decoder() &&
+ decoder()->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0)) {
+ // For older files, revert the transform right after we decode the data.
+ if (!StoreValues(num_values))
+ return false;
+ }
+#endif
+ return true;
+}
+
+std::unique_ptr<PredictionSchemeTypedDecoderInterface<int32_t>>
+SequentialIntegerAttributeDecoder::CreateIntPredictionScheme(
+ PredictionSchemeMethod method,
+ PredictionSchemeTransformType transform_type) {
+ if (transform_type != PREDICTION_TRANSFORM_WRAP)
+ return nullptr; // For now we support only wrap transform.
+ return CreatePredictionSchemeForDecoder<
+ int32_t, PredictionSchemeWrapDecodingTransform<int32_t>>(
+ method, attribute_id(), decoder());
+}
+
+bool SequentialIntegerAttributeDecoder::DecodeIntegerValues(
+ const std::vector<PointIndex> &point_ids, DecoderBuffer *in_buffer) {
+ const int num_components = GetNumValueComponents();
+ if (num_components <= 0)
+ return false;
+ const size_t num_entries = point_ids.size();
+ const size_t num_values = num_entries * num_components;
+ PreparePortableAttribute(static_cast<int>(num_entries), num_components);
+ int32_t *const portable_attribute_data = GetPortableAttributeData();
+ if (portable_attribute_data == nullptr)
+ return false;
+ uint8_t compressed;
+ if (!in_buffer->Decode(&compressed))
+ return false;
+ if (compressed > 0) {
+ // Decode compressed values.
+ if (!DecodeSymbols(static_cast<uint32_t>(num_values), num_components,
+ in_buffer,
+ reinterpret_cast<uint32_t *>(portable_attribute_data)))
+ return false;
+ } else {
+ // Decode the integer data directly.
+ // Get the number of bytes for a given entry.
+ uint8_t num_bytes;
+ if (!in_buffer->Decode(&num_bytes))
+ return false;
+ if (num_bytes == DataTypeLength(DT_INT32)) {
+ if (portable_attribute()->buffer()->data_size() <
+ sizeof(int32_t) * num_values)
+ return false;
+ if (!in_buffer->Decode(portable_attribute_data,
+ sizeof(int32_t) * num_values))
+ return false;
+ } else {
+ if (portable_attribute()->buffer()->data_size() < num_bytes * num_values)
+ return false;
+ if (in_buffer->remaining_size() <
+ static_cast<int64_t>(num_bytes) * static_cast<int64_t>(num_values))
+ return false;
+ for (size_t i = 0; i < num_values; ++i) {
+ in_buffer->Decode(portable_attribute_data + i, num_bytes);
+ }
+ }
+ }
+
+ if (num_values > 0 && (prediction_scheme_ == nullptr ||
+ !prediction_scheme_->AreCorrectionsPositive())) {
+ // Convert the values back to the original signed format.
+ ConvertSymbolsToSignedInts(
+ reinterpret_cast<const uint32_t *>(portable_attribute_data),
+ static_cast<int>(num_values), portable_attribute_data);
+ }
+
+ // If the data was encoded with a prediction scheme, we must revert it.
+ if (prediction_scheme_) {
+ if (!prediction_scheme_->DecodePredictionData(in_buffer))
+ return false;
+
+ if (num_values > 0) {
+ if (!prediction_scheme_->ComputeOriginalValues(
+ portable_attribute_data, portable_attribute_data,
+ static_cast<int>(num_values), num_components, point_ids.data())) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool SequentialIntegerAttributeDecoder::StoreValues(uint32_t num_values) {
+ switch (attribute()->data_type()) {
+ case DT_UINT8:
+ StoreTypedValues<uint8_t>(num_values);
+ break;
+ case DT_INT8:
+ StoreTypedValues<int8_t>(num_values);
+ break;
+ case DT_UINT16:
+ StoreTypedValues<uint16_t>(num_values);
+ break;
+ case DT_INT16:
+ StoreTypedValues<int16_t>(num_values);
+ break;
+ case DT_UINT32:
+ StoreTypedValues<uint32_t>(num_values);
+ break;
+ case DT_INT32:
+ StoreTypedValues<int32_t>(num_values);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+template <typename AttributeTypeT>
+void SequentialIntegerAttributeDecoder::StoreTypedValues(uint32_t num_values) {
+ const int num_components = attribute()->num_components();
+ const int entry_size = sizeof(AttributeTypeT) * num_components;
+ const std::unique_ptr<AttributeTypeT[]> att_val(
+ new AttributeTypeT[num_components]);
+ const int32_t *const portable_attribute_data = GetPortableAttributeData();
+ int val_id = 0;
+ int out_byte_pos = 0;
+ for (uint32_t i = 0; i < num_values; ++i) {
+ for (int c = 0; c < num_components; ++c) {
+ const AttributeTypeT value =
+ static_cast<AttributeTypeT>(portable_attribute_data[val_id++]);
+ att_val[c] = value;
+ }
+ // Store the integer value into the attribute buffer.
+ attribute()->buffer()->Write(out_byte_pos, att_val.get(), entry_size);
+ out_byte_pos += entry_size;
+ }
+}
+
+void SequentialIntegerAttributeDecoder::PreparePortableAttribute(
+ int num_entries, int num_components) {
+ GeometryAttribute va;
+ va.Init(attribute()->attribute_type(), nullptr, num_components, DT_INT32,
+ false, num_components * DataTypeLength(DT_INT32), 0);
+ std::unique_ptr<PointAttribute> port_att(new PointAttribute(va));
+ port_att->SetIdentityMapping();
+ port_att->Reset(num_entries);
+ SetPortableAttribute(std::move(port_att));
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_decoder.h
new file mode 100644
index 00000000000..150ae1f831f
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_decoder.h
@@ -0,0 +1,76 @@
+// 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_COMPRESSION_ATTRIBUTES_SEQUENTIAL_INTEGER_ATTRIBUTE_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_INTEGER_ATTRIBUTE_DECODER_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_decoder.h"
+#include "draco/compression/attributes/sequential_attribute_decoder.h"
+
+namespace draco {
+
+// Decoder for attributes encoded with the SequentialIntegerAttributeEncoder.
+class SequentialIntegerAttributeDecoder : public SequentialAttributeDecoder {
+ public:
+ SequentialIntegerAttributeDecoder();
+ bool Init(PointCloudDecoder *decoder, int attribute_id) override;
+
+ bool TransformAttributeToOriginalFormat(
+ const std::vector<PointIndex> &point_ids) override;
+
+ protected:
+ bool DecodeValues(const std::vector<PointIndex> &point_ids,
+ DecoderBuffer *in_buffer) override;
+ virtual bool DecodeIntegerValues(const std::vector<PointIndex> &point_ids,
+ DecoderBuffer *in_buffer);
+
+ // Returns a prediction scheme that should be used for decoding of the
+ // integer values.
+ virtual std::unique_ptr<PredictionSchemeTypedDecoderInterface<int32_t>>
+ CreateIntPredictionScheme(PredictionSchemeMethod method,
+ PredictionSchemeTransformType transform_type);
+
+ // Returns the number of integer attribute components. In general, this
+ // can be different from the number of components of the input attribute.
+ virtual int32_t GetNumValueComponents() const {
+ return attribute()->num_components();
+ }
+
+ // Called after all integer values are decoded. The implementation should
+ // use this method to store the values into the attribute.
+ virtual bool StoreValues(uint32_t num_values);
+
+ void PreparePortableAttribute(int num_entries, int num_components);
+
+ int32_t *GetPortableAttributeData() {
+ if (portable_attribute()->size() == 0)
+ return nullptr;
+ return reinterpret_cast<int32_t *>(
+ portable_attribute()->GetAddress(AttributeValueIndex(0)));
+ }
+
+ private:
+ // Stores decoded values into the attribute with a data type AttributeTypeT.
+ template <typename AttributeTypeT>
+ void StoreTypedValues(uint32_t num_values);
+
+ std::unique_ptr<PredictionSchemeTypedDecoderInterface<int32_t>>
+ prediction_scheme_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_INTEGER_ATTRIBUTE_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_encoder.cc b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_encoder.cc
new file mode 100644
index 00000000000..0cae81d9f95
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_encoder.cc
@@ -0,0 +1,225 @@
+// 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 "draco/compression/attributes/sequential_integer_attribute_encoder.h"
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_encoding_transform.h"
+#include "draco/compression/entropy/symbol_encoding.h"
+#include "draco/core/bit_utils.h"
+
+namespace draco {
+
+SequentialIntegerAttributeEncoder::SequentialIntegerAttributeEncoder() {}
+
+bool SequentialIntegerAttributeEncoder::Init(PointCloudEncoder *encoder,
+ int attribute_id) {
+ if (!SequentialAttributeEncoder::Init(encoder, attribute_id))
+ return false;
+ if (GetUniqueId() == SEQUENTIAL_ATTRIBUTE_ENCODER_INTEGER) {
+ // When encoding integers, this encoder currently works only for integer
+ // attributes up to 32 bits.
+ switch (attribute()->data_type()) {
+ case DT_INT8:
+ case DT_UINT8:
+ case DT_INT16:
+ case DT_UINT16:
+ case DT_INT32:
+ case DT_UINT32:
+ break;
+ default:
+ return false;
+ }
+ }
+ // Init prediction scheme.
+ const PredictionSchemeMethod prediction_scheme_method =
+ GetPredictionMethodFromOptions(attribute_id, *encoder->options());
+
+ prediction_scheme_ = CreateIntPredictionScheme(prediction_scheme_method);
+
+ if (prediction_scheme_ && !InitPredictionScheme(prediction_scheme_.get())) {
+ prediction_scheme_ = nullptr;
+ }
+
+ return true;
+}
+
+bool SequentialIntegerAttributeEncoder::TransformAttributeToPortableFormat(
+ const std::vector<PointIndex> &point_ids) {
+ if (encoder()) {
+ if (!PrepareValues(point_ids, encoder()->point_cloud()->num_points()))
+ return false;
+ } else {
+ if (!PrepareValues(point_ids, 0))
+ return false;
+ }
+
+ // Update point to attribute mapping with the portable attribute if the
+ // attribute is a parent attribute (for now, we can skip it otherwise).
+ if (is_parent_encoder()) {
+ // First create map between original attribute value indices and new ones
+ // (determined by the encoding order).
+ const PointAttribute *const orig_att = attribute();
+ PointAttribute *const portable_att = portable_attribute();
+ IndexTypeVector<AttributeValueIndex, AttributeValueIndex>
+ value_to_value_map(orig_att->size());
+ for (int i = 0; i < point_ids.size(); ++i) {
+ value_to_value_map[orig_att->mapped_index(point_ids[i])] =
+ AttributeValueIndex(i);
+ }
+ // Go over all points of the original attribute and update the mapping in
+ // the portable attribute.
+ for (PointIndex i(0); i < encoder()->point_cloud()->num_points(); ++i) {
+ portable_att->SetPointMapEntry(
+ i, value_to_value_map[orig_att->mapped_index(i)]);
+ }
+ }
+ return true;
+}
+
+std::unique_ptr<PredictionSchemeTypedEncoderInterface<int32_t>>
+SequentialIntegerAttributeEncoder::CreateIntPredictionScheme(
+ PredictionSchemeMethod method) {
+ return CreatePredictionSchemeForEncoder<
+ int32_t, PredictionSchemeWrapEncodingTransform<int32_t>>(
+ method, attribute_id(), encoder());
+}
+
+bool SequentialIntegerAttributeEncoder::EncodeValues(
+ const std::vector<PointIndex> &point_ids, EncoderBuffer *out_buffer) {
+ // Initialize general quantization data.
+ const PointAttribute *const attrib = attribute();
+ if (attrib->size() == 0)
+ return true;
+
+ int8_t prediction_scheme_method = PREDICTION_NONE;
+ if (prediction_scheme_) {
+ if (!SetPredictionSchemeParentAttributes(prediction_scheme_.get())) {
+ return false;
+ }
+ prediction_scheme_method =
+ static_cast<int8_t>(prediction_scheme_->GetPredictionMethod());
+ }
+ out_buffer->Encode(prediction_scheme_method);
+ if (prediction_scheme_) {
+ out_buffer->Encode(
+ static_cast<int8_t>(prediction_scheme_->GetTransformType()));
+ }
+
+ const int num_components = portable_attribute()->num_components();
+ const int num_values =
+ static_cast<int>(num_components * portable_attribute()->size());
+ const int32_t *const portable_attribute_data = GetPortableAttributeData();
+
+ // We need to keep the portable data intact, but several encoding steps can
+ // result in changes of this data, e.g., by applying prediction schemes that
+ // change the data in place. To preserve the portable data we store and
+ // process all encoded data in a separate array.
+ std::vector<int32_t> encoded_data(num_values);
+
+ // All integer values are initialized. Process them using the prediction
+ // scheme if we have one.
+ if (prediction_scheme_) {
+ prediction_scheme_->ComputeCorrectionValues(
+ portable_attribute_data, &encoded_data[0], num_values, num_components,
+ point_ids.data());
+ }
+
+ if (prediction_scheme_ == nullptr ||
+ !prediction_scheme_->AreCorrectionsPositive()) {
+ const int32_t *const input =
+ prediction_scheme_ ? encoded_data.data() : portable_attribute_data;
+ ConvertSignedIntsToSymbols(input, num_values,
+ reinterpret_cast<uint32_t *>(&encoded_data[0]));
+ }
+
+ if (encoder() == nullptr || encoder()->options()->GetGlobalBool(
+ "use_built_in_attribute_compression", true)) {
+ out_buffer->Encode(static_cast<uint8_t>(1));
+ Options symbol_encoding_options;
+ if (encoder() != nullptr) {
+ SetSymbolEncodingCompressionLevel(&symbol_encoding_options,
+ 10 - encoder()->options()->GetSpeed());
+ }
+ if (!EncodeSymbols(reinterpret_cast<uint32_t *>(encoded_data.data()),
+ static_cast<int>(point_ids.size()) * num_components,
+ num_components, &symbol_encoding_options, out_buffer)) {
+ return false;
+ }
+ } else {
+ // No compression. Just store the raw integer values, using the number of
+ // bytes as needed.
+
+ // To compute the maximum bit-length, first OR all values.
+ uint32_t masked_value = 0;
+ for (uint32_t i = 0; i < static_cast<uint32_t>(num_values); ++i) {
+ masked_value |= encoded_data[i];
+ }
+ // Compute the msb of the ORed value.
+ int value_msb_pos = 0;
+ if (masked_value != 0) {
+ value_msb_pos = MostSignificantBit(masked_value);
+ }
+ const int num_bytes = 1 + value_msb_pos / 8;
+
+ out_buffer->Encode(static_cast<uint8_t>(0));
+ out_buffer->Encode(static_cast<uint8_t>(num_bytes));
+
+ if (num_bytes == DataTypeLength(DT_INT32)) {
+ out_buffer->Encode(encoded_data.data(), sizeof(int32_t) * num_values);
+ } else {
+ for (uint32_t i = 0; i < static_cast<uint32_t>(num_values); ++i) {
+ out_buffer->Encode(encoded_data.data() + i, num_bytes);
+ }
+ }
+ }
+ if (prediction_scheme_) {
+ prediction_scheme_->EncodePredictionData(out_buffer);
+ }
+ return true;
+}
+
+bool SequentialIntegerAttributeEncoder::PrepareValues(
+ const std::vector<PointIndex> &point_ids, int num_points) {
+ // Convert all values to int32_t format.
+ const PointAttribute *const attrib = attribute();
+ const int num_components = attrib->num_components();
+ const int num_entries = static_cast<int>(point_ids.size());
+ PreparePortableAttribute(num_entries, num_components, num_points);
+ int32_t dst_index = 0;
+ int32_t *const portable_attribute_data = GetPortableAttributeData();
+ for (PointIndex pi : point_ids) {
+ const AttributeValueIndex att_id = attrib->mapped_index(pi);
+ if (!attrib->ConvertValue<int32_t>(att_id,
+ portable_attribute_data + dst_index))
+ return false;
+ dst_index += num_components;
+ }
+ return true;
+}
+
+void SequentialIntegerAttributeEncoder::PreparePortableAttribute(
+ int num_entries, int num_components, int num_points) {
+ GeometryAttribute va;
+ va.Init(attribute()->attribute_type(), nullptr, num_components, DT_INT32,
+ false, num_components * DataTypeLength(DT_INT32), 0);
+ std::unique_ptr<PointAttribute> port_att(new PointAttribute(va));
+ port_att->Reset(num_entries);
+ SetPortableAttribute(std::move(port_att));
+ if (num_points) {
+ portable_attribute()->SetExplicitMapping(num_points);
+ }
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_encoder.h
new file mode 100644
index 00000000000..c1d6222ef40
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_encoder.h
@@ -0,0 +1,67 @@
+// 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_COMPRESSION_ATTRIBUTES_SEQUENTIAL_INTEGER_ATTRIBUTE_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_INTEGER_ATTRIBUTE_ENCODER_H_
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_encoder.h"
+#include "draco/compression/attributes/sequential_attribute_encoder.h"
+
+namespace draco {
+
+// Attribute encoder designed for lossless encoding of integer attributes. The
+// attribute values can be pre-processed by a prediction scheme and compressed
+// with a built-in entropy coder.
+class SequentialIntegerAttributeEncoder : public SequentialAttributeEncoder {
+ public:
+ SequentialIntegerAttributeEncoder();
+ uint8_t GetUniqueId() const override {
+ return SEQUENTIAL_ATTRIBUTE_ENCODER_INTEGER;
+ }
+
+ bool Init(PointCloudEncoder *encoder, int attribute_id) override;
+ bool TransformAttributeToPortableFormat(
+ const std::vector<PointIndex> &point_ids) override;
+
+ protected:
+ bool EncodeValues(const std::vector<PointIndex> &point_ids,
+ EncoderBuffer *out_buffer) override;
+
+ // Returns a prediction scheme that should be used for encoding of the
+ // integer values.
+ virtual std::unique_ptr<PredictionSchemeTypedEncoderInterface<int32_t>>
+ CreateIntPredictionScheme(PredictionSchemeMethod method);
+
+ // Prepares the integer values that are going to be encoded.
+ virtual bool PrepareValues(const std::vector<PointIndex> &point_ids,
+ int num_points);
+
+ void PreparePortableAttribute(int num_entries, int num_components,
+ int num_points);
+
+ int32_t *GetPortableAttributeData() {
+ return reinterpret_cast<int32_t *>(
+ portable_attribute()->GetAddress(AttributeValueIndex(0)));
+ }
+
+ private:
+ // Optional prediction scheme can be used to modify the integer values in
+ // order to make them easier to compress.
+ std::unique_ptr<PredictionSchemeTypedEncoderInterface<int32_t>>
+ prediction_scheme_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_INTEGER_ATTRIBUTE_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_encoding_test.cc b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_encoding_test.cc
new file mode 100644
index 00000000000..d7b0cd25df6
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_integer_attribute_encoding_test.cc
@@ -0,0 +1,67 @@
+// 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 <numeric>
+
+#include "draco/compression/attributes/sequential_integer_attribute_decoder.h"
+#include "draco/compression/attributes/sequential_integer_attribute_encoder.h"
+#include "draco/compression/config/compression_shared.h"
+#include "draco/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<int32_t> 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<PointIndex> 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.TransformAttributeToPortableFormat(point_ids));
+ ASSERT_TRUE(ie.EncodePortableAttribute(point_ids, &out_buf));
+ ASSERT_TRUE(ie.EncodeDataNeededByPortableTransform(&out_buf));
+
+ DecoderBuffer in_buf;
+ in_buf.Init(out_buf.data(), out_buf.size());
+ in_buf.set_bitstream_version(kDracoMeshBitstreamVersion);
+ SequentialIntegerAttributeDecoder id;
+ ASSERT_TRUE(id.InitializeStandalone(&pa));
+ ASSERT_TRUE(id.DecodePortableAttribute(point_ids, &in_buf));
+ ASSERT_TRUE(id.DecodeDataNeededByPortableTransform(point_ids, &in_buf));
+ ASSERT_TRUE(id.TransformAttributeToOriginalFormat(point_ids));
+
+ 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/extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_decoder.cc b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_decoder.cc
new file mode 100644
index 00000000000..2f208d50dbe
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_decoder.cc
@@ -0,0 +1,89 @@
+// 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 "draco/compression/attributes/sequential_normal_attribute_decoder.h"
+#include "draco/attributes/attribute_octahedron_transform.h"
+#include "draco/compression/attributes/normal_compression_utils.h"
+
+namespace draco {
+
+SequentialNormalAttributeDecoder::SequentialNormalAttributeDecoder()
+ : quantization_bits_(-1) {}
+
+bool SequentialNormalAttributeDecoder::Init(PointCloudDecoder *decoder,
+ int attribute_id) {
+ if (!SequentialIntegerAttributeDecoder::Init(decoder, attribute_id))
+ return false;
+ // Currently, this encoder works only for 3-component normal vectors.
+ if (attribute()->num_components() != 3)
+ return false;
+ // Also the data type must be DT_FLOAT32.
+ if (attribute()->data_type() != DT_FLOAT32)
+ return false;
+ return true;
+}
+
+bool SequentialNormalAttributeDecoder::DecodeIntegerValues(
+ const std::vector<PointIndex> &point_ids, DecoderBuffer *in_buffer) {
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder()->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0)) {
+ uint8_t quantization_bits;
+ if (!in_buffer->Decode(&quantization_bits))
+ return false;
+ quantization_bits_ = quantization_bits;
+ }
+#endif
+ return SequentialIntegerAttributeDecoder::DecodeIntegerValues(point_ids,
+ in_buffer);
+}
+
+bool SequentialNormalAttributeDecoder::DecodeDataNeededByPortableTransform(
+ const std::vector<PointIndex> &point_ids, DecoderBuffer *in_buffer) {
+ if (decoder()->bitstream_version() >= DRACO_BITSTREAM_VERSION(2, 0)) {
+ // For newer file version, decode attribute transform data here.
+ uint8_t quantization_bits;
+ if (!in_buffer->Decode(&quantization_bits))
+ return false;
+ quantization_bits_ = quantization_bits;
+ }
+
+ // Store the decoded transform data in portable attribute.
+ AttributeOctahedronTransform octahedral_transform;
+ octahedral_transform.SetParameters(quantization_bits_);
+ return octahedral_transform.TransferToAttribute(portable_attribute());
+}
+
+bool SequentialNormalAttributeDecoder::StoreValues(uint32_t num_points) {
+ // Convert all quantized values back to floats.
+ const int num_components = attribute()->num_components();
+ const int entry_size = sizeof(float) * num_components;
+ float att_val[3];
+ int quant_val_id = 0;
+ int out_byte_pos = 0;
+ const int32_t *const portable_attribute_data = GetPortableAttributeData();
+ OctahedronToolBox octahedron_tool_box;
+ if (!octahedron_tool_box.SetQuantizationBits(quantization_bits_))
+ return false;
+ for (uint32_t i = 0; i < num_points; ++i) {
+ const int32_t s = portable_attribute_data[quant_val_id++];
+ const int32_t t = portable_attribute_data[quant_val_id++];
+ octahedron_tool_box.QuantizedOctaherdalCoordsToUnitVector(s, t, att_val);
+ // Store the decoded floating point value into the attribute buffer.
+ attribute()->buffer()->Write(out_byte_pos, att_val, entry_size);
+ out_byte_pos += entry_size;
+ }
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_decoder.h
new file mode 100644
index 00000000000..d6439fcd6cf
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_decoder.h
@@ -0,0 +1,83 @@
+// 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_COMPRESSION_ATTRIBUTES_SEQUENTIAL_NORMAL_ATTRIBUTE_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_NORMAL_ATTRIBUTE_DECODER_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_decoder_factory.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_decoding_transform.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_decoding_transform.h"
+#include "draco/compression/attributes/sequential_integer_attribute_decoder.h"
+
+namespace draco {
+
+// Decoder for attributes encoded with SequentialNormalAttributeEncoder.
+class SequentialNormalAttributeDecoder
+ : public SequentialIntegerAttributeDecoder {
+ public:
+ SequentialNormalAttributeDecoder();
+ bool Init(PointCloudDecoder *decoder, int attribute_id) override;
+
+ protected:
+ int32_t GetNumValueComponents() const override {
+ return 2; // We quantize everything into two components.
+ }
+ bool DecodeIntegerValues(const std::vector<PointIndex> &point_ids,
+ DecoderBuffer *in_buffer) override;
+ bool DecodeDataNeededByPortableTransform(
+ const std::vector<PointIndex> &point_ids,
+ DecoderBuffer *in_buffer) override;
+ bool StoreValues(uint32_t num_points) override;
+
+ private:
+ int32_t quantization_bits_;
+
+ std::unique_ptr<PredictionSchemeTypedDecoderInterface<int32_t>>
+ CreateIntPredictionScheme(
+ PredictionSchemeMethod method,
+ PredictionSchemeTransformType transform_type) override {
+ switch (transform_type) {
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ case PREDICTION_TRANSFORM_NORMAL_OCTAHEDRON: {
+ typedef PredictionSchemeNormalOctahedronDecodingTransform<int32_t>
+ Transform;
+ // At this point the decoder has not read the quantization bits,
+ // which is why we must construct the transform by default.
+ // See Transform.DecodeTransformData for more details.
+ return CreatePredictionSchemeForDecoder<int32_t, Transform>(
+ method, attribute_id(), decoder());
+ }
+#endif
+ case PREDICTION_TRANSFORM_NORMAL_OCTAHEDRON_CANONICALIZED: {
+ typedef PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform<
+ int32_t>
+ Transform;
+ // At this point the decoder has not read the quantization bits,
+ // which is why we must construct the transform by default.
+ // See Transform.DecodeTransformData for more details.
+ return CreatePredictionSchemeForDecoder<int32_t, Transform>(
+ method, attribute_id(), decoder());
+ }
+ default:
+ return nullptr; // Currently, we support only octahedron transform and
+ // octahedron transform canonicalized.
+ }
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_NORMAL_ATTRIBUTE_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_encoder.cc b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_encoder.cc
new file mode 100644
index 00000000000..af207a87111
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_encoder.cc
@@ -0,0 +1,50 @@
+// 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 "draco/compression/attributes/sequential_normal_attribute_encoder.h"
+#include "draco/compression/attributes/normal_compression_utils.h"
+
+namespace draco {
+
+bool SequentialNormalAttributeEncoder::Init(PointCloudEncoder *encoder,
+ int attribute_id) {
+ if (!SequentialIntegerAttributeEncoder::Init(encoder, attribute_id))
+ return false;
+ // Currently this encoder works only for 3-component normal vectors.
+ if (attribute()->num_components() != 3)
+ return false;
+
+ // Initialize AttributeOctahedronTransform.
+ const int quantization_bits = encoder->options()->GetAttributeInt(
+ attribute_id, "quantization_bits", -1);
+ if (quantization_bits < 1)
+ return false;
+ attribute_octahedron_transform_.SetParameters(quantization_bits);
+ return true;
+}
+
+bool SequentialNormalAttributeEncoder::EncodeDataNeededByPortableTransform(
+ EncoderBuffer *out_buffer) {
+ return attribute_octahedron_transform_.EncodeParameters(out_buffer);
+}
+
+bool SequentialNormalAttributeEncoder::PrepareValues(
+ const std::vector<PointIndex> &point_ids, int num_points) {
+ SetPortableAttribute(
+ attribute_octahedron_transform_.GeneratePortableAttribute(
+ *(attribute()), point_ids, num_points));
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_encoder.h
new file mode 100644
index 00000000000..53705c5982b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_normal_attribute_encoder.h
@@ -0,0 +1,82 @@
+// 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_COMPRESSION_ATTRIBUTES_SEQUENTIAL_NORMAL_ATTRIBUTE_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_NORMAL_ATTRIBUTE_ENCODER_H_
+
+#include "draco/attributes/attribute_octahedron_transform.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h"
+#include "draco/compression/attributes/prediction_schemes/prediction_scheme_normal_octahedron_canonicalized_encoding_transform.h"
+#include "draco/compression/attributes/sequential_integer_attribute_encoder.h"
+#include "draco/compression/config/compression_shared.h"
+
+namespace draco {
+
+// Class for encoding normal vectors using an octahedral encoding, see Cigolle
+// et al.'14 “A Survey of Efficient Representations for Independent Unit
+// Vectors”. Compared to the basic quantization encoder, this encoder results
+// 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.
+class SequentialNormalAttributeEncoder
+ : public SequentialIntegerAttributeEncoder {
+ public:
+ uint8_t GetUniqueId() const override {
+ return SEQUENTIAL_ATTRIBUTE_ENCODER_NORMALS;
+ }
+ bool IsLossyEncoder() const override { return true; }
+
+ bool EncodeDataNeededByPortableTransform(EncoderBuffer *out_buffer) override;
+
+ protected:
+ bool Init(PointCloudEncoder *encoder, int attribute_id) override;
+
+ // Put quantized values in portable attribute for sequential encoding.
+ bool PrepareValues(const std::vector<PointIndex> &point_ids,
+ int num_points) override;
+
+ std::unique_ptr<PredictionSchemeTypedEncoderInterface<int32_t>>
+ CreateIntPredictionScheme(PredictionSchemeMethod /* method */) override {
+ typedef PredictionSchemeNormalOctahedronCanonicalizedEncodingTransform<
+ int32_t>
+ Transform;
+ const int32_t quantization_bits = encoder()->options()->GetAttributeInt(
+ attribute_id(), "quantization_bits", -1);
+ const int32_t max_value = (1 << quantization_bits) - 1;
+ const Transform transform(max_value);
+ const PredictionSchemeMethod default_prediction_method =
+ SelectPredictionMethod(attribute_id(), encoder());
+ const int32_t prediction_method = encoder()->options()->GetAttributeInt(
+ attribute_id(), "prediction_scheme", default_prediction_method);
+
+ if (prediction_method == MESH_PREDICTION_GEOMETRIC_NORMAL) {
+ return CreatePredictionSchemeForEncoder<int32_t, Transform>(
+ MESH_PREDICTION_GEOMETRIC_NORMAL, attribute_id(), encoder(),
+ transform);
+ }
+ if (prediction_method == PREDICTION_DIFFERENCE) {
+ return CreatePredictionSchemeForEncoder<int32_t, Transform>(
+ PREDICTION_DIFFERENCE, attribute_id(), encoder(), transform);
+ }
+ DRACO_DCHECK(false); // Should never be reached.
+ return nullptr;
+ }
+
+ // Used for the conversion to quantized normals in octahedral format.
+ AttributeOctahedronTransform attribute_octahedron_transform_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_NORMAL_ATTRIBUTE_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_decoder.cc b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_decoder.cc
new file mode 100644
index 00000000000..dfaf2edbe8e
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_decoder.cc
@@ -0,0 +1,112 @@
+// 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 "draco/compression/attributes/sequential_quantization_attribute_decoder.h"
+
+#include "draco/attributes/attribute_quantization_transform.h"
+#include "draco/core/quantization_utils.h"
+
+namespace draco {
+
+SequentialQuantizationAttributeDecoder::SequentialQuantizationAttributeDecoder()
+ : quantization_bits_(-1), max_value_dif_(0.f) {}
+
+bool SequentialQuantizationAttributeDecoder::Init(PointCloudDecoder *decoder,
+ int attribute_id) {
+ if (!SequentialIntegerAttributeDecoder::Init(decoder, attribute_id))
+ return false;
+ const PointAttribute *const attribute =
+ decoder->point_cloud()->attribute(attribute_id);
+ // Currently we can quantize only floating point arguments.
+ if (attribute->data_type() != DT_FLOAT32)
+ return false;
+ return true;
+}
+
+bool SequentialQuantizationAttributeDecoder::DecodeIntegerValues(
+ const std::vector<PointIndex> &point_ids, DecoderBuffer *in_buffer) {
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder()->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0) &&
+ !DecodeQuantizedDataInfo())
+ return false;
+#endif
+ return SequentialIntegerAttributeDecoder::DecodeIntegerValues(point_ids,
+ in_buffer);
+}
+
+bool SequentialQuantizationAttributeDecoder::
+ DecodeDataNeededByPortableTransform(
+ const std::vector<PointIndex> &point_ids, DecoderBuffer *in_buffer) {
+ if (decoder()->bitstream_version() >= DRACO_BITSTREAM_VERSION(2, 0)) {
+ // Decode quantization data here only for files with bitstream version 2.0+
+ if (!DecodeQuantizedDataInfo())
+ return false;
+ }
+
+ // Store the decoded transform data in portable attribute;
+ AttributeQuantizationTransform transform;
+ transform.SetParameters(quantization_bits_, min_value_.get(),
+ attribute()->num_components(), max_value_dif_);
+ return transform.TransferToAttribute(portable_attribute());
+}
+
+bool SequentialQuantizationAttributeDecoder::StoreValues(uint32_t num_values) {
+ return DequantizeValues(num_values);
+}
+
+bool SequentialQuantizationAttributeDecoder::DecodeQuantizedDataInfo() {
+ const int num_components = attribute()->num_components();
+ min_value_ = std::unique_ptr<float[]>(new float[num_components]);
+ if (!decoder()->buffer()->Decode(min_value_.get(),
+ sizeof(float) * num_components))
+ return false;
+ if (!decoder()->buffer()->Decode(&max_value_dif_))
+ return false;
+ uint8_t quantization_bits;
+ if (!decoder()->buffer()->Decode(&quantization_bits) ||
+ quantization_bits > 31)
+ return false;
+ quantization_bits_ = quantization_bits;
+ return true;
+}
+
+bool SequentialQuantizationAttributeDecoder::DequantizeValues(
+ uint32_t num_values) {
+ // Convert all quantized values back to floats.
+ const int32_t max_quantized_value =
+ (1u << static_cast<uint32_t>(quantization_bits_)) - 1;
+ const int num_components = attribute()->num_components();
+ const int entry_size = sizeof(float) * num_components;
+ const std::unique_ptr<float[]> att_val(new float[num_components]);
+ int quant_val_id = 0;
+ int out_byte_pos = 0;
+ Dequantizer dequantizer;
+ if (!dequantizer.Init(max_value_dif_, max_quantized_value))
+ return false;
+ const int32_t *const portable_attribute_data = GetPortableAttributeData();
+ for (uint32_t i = 0; i < num_values; ++i) {
+ for (int c = 0; c < num_components; ++c) {
+ float value =
+ dequantizer.DequantizeFloat(portable_attribute_data[quant_val_id++]);
+ value = value + min_value_[c];
+ att_val[c] = value;
+ }
+ // Store the floating point value into the attribute buffer.
+ attribute()->buffer()->Write(out_byte_pos, att_val.get(), entry_size);
+ out_byte_pos += entry_size;
+ }
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_decoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_decoder.h
new file mode 100644
index 00000000000..27d88be72bd
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_decoder.h
@@ -0,0 +1,57 @@
+// 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_COMPRESSION_ATTRIBUTES_SEQUENTIAL_QUANTIZATION_ATTRIBUTE_DECODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_QUANTIZATION_ATTRIBUTE_DECODER_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/attributes/sequential_integer_attribute_decoder.h"
+
+namespace draco {
+
+// Decoder for attribute values encoded with the
+// SequentialQuantizationAttributeEncoder.
+class SequentialQuantizationAttributeDecoder
+ : public SequentialIntegerAttributeDecoder {
+ public:
+ SequentialQuantizationAttributeDecoder();
+ bool Init(PointCloudDecoder *decoder, int attribute_id) override;
+
+ protected:
+ bool DecodeIntegerValues(const std::vector<PointIndex> &point_ids,
+ DecoderBuffer *in_buffer) override;
+ bool DecodeDataNeededByPortableTransform(
+ const std::vector<PointIndex> &point_ids,
+ DecoderBuffer *in_buffer) override;
+ bool StoreValues(uint32_t num_points) override;
+
+ // Decodes data necessary for dequantizing the encoded values.
+ virtual bool DecodeQuantizedDataInfo();
+
+ // Dequantizes all values and stores them into the output attribute.
+ virtual bool DequantizeValues(uint32_t num_values);
+
+ private:
+ // Max number of quantization bits used to encode each component of the
+ // attribute.
+ int32_t quantization_bits_;
+
+ std::unique_ptr<float[]> min_value_;
+ float max_value_dif_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_QUANTIZATION_ATTRIBUTE_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_encoder.cc b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_encoder.cc
new file mode 100644
index 00000000000..9110450061c
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_encoder.cc
@@ -0,0 +1,74 @@
+// 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 "draco/compression/attributes/sequential_quantization_attribute_encoder.h"
+
+#include "draco/core/quantization_utils.h"
+
+namespace draco {
+
+SequentialQuantizationAttributeEncoder::
+ SequentialQuantizationAttributeEncoder() {}
+
+bool SequentialQuantizationAttributeEncoder::Init(PointCloudEncoder *encoder,
+ int attribute_id) {
+ if (!SequentialIntegerAttributeEncoder::Init(encoder, attribute_id))
+ return false;
+ // This encoder currently works only for floating point attributes.
+ const PointAttribute *const attribute =
+ encoder->point_cloud()->attribute(attribute_id);
+ if (attribute->data_type() != DT_FLOAT32)
+ return false;
+
+ // Initialize AttributeQuantizationTransform.
+ const int quantization_bits = encoder->options()->GetAttributeInt(
+ attribute_id, "quantization_bits", -1);
+ if (quantization_bits < 1)
+ return false;
+ if (encoder->options()->IsAttributeOptionSet(attribute_id,
+ "quantization_origin") &&
+ encoder->options()->IsAttributeOptionSet(attribute_id,
+ "quantization_range")) {
+ // Quantization settings are explicitly specified in the provided options.
+ std::vector<float> quantization_origin(attribute->num_components());
+ encoder->options()->GetAttributeVector(attribute_id, "quantization_origin",
+ attribute->num_components(),
+ &quantization_origin[0]);
+ const float range = encoder->options()->GetAttributeFloat(
+ attribute_id, "quantization_range", 1.f);
+ attribute_quantization_transform_.SetParameters(
+ quantization_bits, quantization_origin.data(),
+ attribute->num_components(), range);
+ } else {
+ // Compute quantization settings from the attribute values.
+ attribute_quantization_transform_.ComputeParameters(*attribute,
+ quantization_bits);
+ }
+ return true;
+}
+
+bool SequentialQuantizationAttributeEncoder::
+ EncodeDataNeededByPortableTransform(EncoderBuffer *out_buffer) {
+ return attribute_quantization_transform_.EncodeParameters(out_buffer);
+}
+
+bool SequentialQuantizationAttributeEncoder::PrepareValues(
+ const std::vector<PointIndex> &point_ids, int num_points) {
+ SetPortableAttribute(
+ attribute_quantization_transform_.GeneratePortableAttribute(
+ *(attribute()), point_ids, num_points));
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_encoder.h b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_encoder.h
new file mode 100644
index 00000000000..e9762bdd6d0
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/attributes/sequential_quantization_attribute_encoder.h
@@ -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.
+//
+#ifndef DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_QUANTIZATION_ATTRIBUTE_ENCODER_H_
+#define DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_QUANTIZATION_ATTRIBUTE_ENCODER_H_
+
+#include "draco/attributes/attribute_quantization_transform.h"
+#include "draco/compression/attributes/sequential_integer_attribute_encoder.h"
+
+namespace draco {
+
+class MeshEncoder;
+
+// Attribute encoder that quantizes floating point attribute values. The
+// quantized values can be optionally compressed using an entropy coding.
+class SequentialQuantizationAttributeEncoder
+ : public SequentialIntegerAttributeEncoder {
+ public:
+ SequentialQuantizationAttributeEncoder();
+ uint8_t GetUniqueId() const override {
+ return SEQUENTIAL_ATTRIBUTE_ENCODER_QUANTIZATION;
+ }
+ bool Init(PointCloudEncoder *encoder, int attribute_id) override;
+
+ bool IsLossyEncoder() const override { return true; }
+
+ bool EncodeDataNeededByPortableTransform(EncoderBuffer *out_buffer) override;
+
+ protected:
+ // Put quantized values in portable attribute for sequential encoding.
+ bool PrepareValues(const std::vector<PointIndex> &point_ids,
+ int num_points) override;
+
+ private:
+ // Used for the quantization.
+ AttributeQuantizationTransform attribute_quantization_transform_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ATTRIBUTES_SEQUENTIAL_QUANTIZATION_ATTRIBUTE_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_coding_shared.h b/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_coding_shared.h
new file mode 100644
index 00000000000..faacbd5b940
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_coding_shared.h
@@ -0,0 +1,43 @@
+// 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.
+//
+// File provides shared functions for adaptive rANS bit coding.
+#ifndef DRACO_COMPRESSION_BIT_CODERS_ADAPTIVE_RANS_BIT_CODING_SHARED_H_
+#define DRACO_COMPRESSION_BIT_CODERS_ADAPTIVE_RANS_BIT_CODING_SHARED_H_
+
+#include "draco/core/macros.h"
+
+namespace draco {
+
+// Clamp the probability p to a uint8_t in the range [1,255].
+inline uint8_t clamp_probability(double p) {
+ DRACO_DCHECK_LE(p, 1.0);
+ DRACO_DCHECK_LE(0.0, p);
+ uint32_t p_int = static_cast<uint32_t>((p * 256) + 0.5);
+ p_int -= (p_int == 256);
+ p_int += (p_int == 0);
+ return static_cast<uint8_t>(p_int);
+}
+
+// Update the probability according to new incoming bit.
+inline double update_probability(double old_p, bool bit) {
+ static constexpr double w = 128.0;
+ static constexpr double w0 = (w - 1.0) / w;
+ static constexpr double w1 = 1.0 / w;
+ return old_p * w0 + (!bit) * w1;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_BIT_CODERS_ADAPTIVE_RANS_BIT_CODING_SHARED_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_decoder.cc b/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_decoder.cc
new file mode 100644
index 00000000000..87ff7007a3d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_decoder.cc
@@ -0,0 +1,67 @@
+// 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 "draco/compression/bit_coders/adaptive_rans_bit_decoder.h"
+
+#include "draco/compression/bit_coders/adaptive_rans_bit_coding_shared.h"
+
+namespace draco {
+
+AdaptiveRAnsBitDecoder::AdaptiveRAnsBitDecoder() : p0_f_(0.5) {}
+
+AdaptiveRAnsBitDecoder::~AdaptiveRAnsBitDecoder() { Clear(); }
+
+bool AdaptiveRAnsBitDecoder::StartDecoding(DecoderBuffer *source_buffer) {
+ Clear();
+
+ uint32_t size_in_bytes;
+ if (!source_buffer->Decode(&size_in_bytes))
+ return false;
+ if (size_in_bytes > source_buffer->remaining_size())
+ return false;
+ if (ans_read_init(&ans_decoder_,
+ reinterpret_cast<uint8_t *>(
+ const_cast<char *>(source_buffer->data_head())),
+ size_in_bytes) != 0)
+ return false;
+ source_buffer->Advance(size_in_bytes);
+ return true;
+}
+
+bool AdaptiveRAnsBitDecoder::DecodeNextBit() {
+ const uint8_t p0 = clamp_probability(p0_f_);
+ const bool bit = static_cast<bool>(rabs_read(&ans_decoder_, p0));
+ p0_f_ = update_probability(p0_f_, bit);
+ return bit;
+}
+
+void AdaptiveRAnsBitDecoder::DecodeLeastSignificantBits32(int nbits,
+ uint32_t *value) {
+ DRACO_DCHECK_EQ(true, nbits <= 32);
+ DRACO_DCHECK_EQ(true, nbits > 0);
+
+ uint32_t result = 0;
+ while (nbits) {
+ result = (result << 1) + DecodeNextBit();
+ --nbits;
+ }
+ *value = result;
+}
+
+void AdaptiveRAnsBitDecoder::Clear() {
+ ans_read_end(&ans_decoder_);
+ p0_f_ = 0.5;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_decoder.h b/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_decoder.h
new file mode 100644
index 00000000000..a1ea011dd6a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_decoder.h
@@ -0,0 +1,54 @@
+// 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.
+//
+// File provides basic classes and functions for rANS bit decoding.
+#ifndef DRACO_COMPRESSION_BIT_CODERS_ADAPTIVE_RANS_BIT_DECODER_H_
+#define DRACO_COMPRESSION_BIT_CODERS_ADAPTIVE_RANS_BIT_DECODER_H_
+
+#include <vector>
+
+#include "draco/compression/entropy/ans.h"
+#include "draco/core/decoder_buffer.h"
+
+namespace draco {
+
+// Class for decoding a sequence of bits that were encoded with
+// AdaptiveRAnsBitEncoder.
+class AdaptiveRAnsBitDecoder {
+ public:
+ AdaptiveRAnsBitDecoder();
+ ~AdaptiveRAnsBitDecoder();
+
+ // Sets |source_buffer| as the buffer to decode bits from.
+ bool StartDecoding(DecoderBuffer *source_buffer);
+
+ // Decode one bit. Returns true if the bit is a 1, otherwise false.
+ bool DecodeNextBit();
+
+ // Decode the next |nbits| and return the sequence in |value|. |nbits| must be
+ // > 0 and <= 32.
+ void DecodeLeastSignificantBits32(int nbits, uint32_t *value);
+
+ void EndDecoding() {}
+
+ private:
+ void Clear();
+
+ AnsDecoder ans_decoder_;
+ double p0_f_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_BIT_CODERS_ADAPTIVE_RANS_BIT_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_encoder.cc b/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_encoder.cc
new file mode 100644
index 00000000000..5ce9dc388b8
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_encoder.cc
@@ -0,0 +1,59 @@
+// 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 "draco/compression/bit_coders/adaptive_rans_bit_encoder.h"
+
+#include "draco/compression/bit_coders/adaptive_rans_bit_coding_shared.h"
+
+namespace draco {
+
+AdaptiveRAnsBitEncoder::AdaptiveRAnsBitEncoder() {}
+
+AdaptiveRAnsBitEncoder::~AdaptiveRAnsBitEncoder() { Clear(); }
+
+void AdaptiveRAnsBitEncoder::StartEncoding() { Clear(); }
+
+void AdaptiveRAnsBitEncoder::EndEncoding(EncoderBuffer *target_buffer) {
+ // Buffer for ans to write.
+ std::vector<uint8_t> buffer(bits_.size() + 16);
+ AnsCoder ans_coder;
+ ans_write_init(&ans_coder, buffer.data());
+
+ // Unfortunately we have to encode the bits in reversed order, while the
+ // probabilities that should be given are those of the forward sequence.
+ double p0_f = 0.5;
+ std::vector<uint8_t> p0s;
+ p0s.reserve(bits_.size());
+ for (bool b : bits_) {
+ p0s.push_back(clamp_probability(p0_f));
+ p0_f = update_probability(p0_f, b);
+ }
+ auto bit = bits_.rbegin();
+ auto pit = p0s.rbegin();
+ while (bit != bits_.rend()) {
+ rabs_write(&ans_coder, *bit, *pit);
+ ++bit;
+ ++pit;
+ }
+
+ const uint32_t size_in_bytes = ans_write_end(&ans_coder);
+ target_buffer->Encode(size_in_bytes);
+ target_buffer->Encode(buffer.data(), size_in_bytes);
+
+ Clear();
+}
+
+void AdaptiveRAnsBitEncoder::Clear() { bits_.clear(); }
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_encoder.h b/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_encoder.h
new file mode 100644
index 00000000000..9b1832844ad
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/adaptive_rans_bit_encoder.h
@@ -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.
+//
+// File provides basic classes and functions for rANS bit encoding.
+#ifndef DRACO_COMPRESSION_BIT_CODERS_ADAPTIVE_RANS_BIT_ENCODER_H_
+#define DRACO_COMPRESSION_BIT_CODERS_ADAPTIVE_RANS_BIT_ENCODER_H_
+
+#include <vector>
+
+#include "draco/compression/entropy/ans.h"
+#include "draco/core/encoder_buffer.h"
+
+namespace draco {
+
+// Class for adaptive encoding a sequence of bits using rANS.
+class AdaptiveRAnsBitEncoder {
+ public:
+ AdaptiveRAnsBitEncoder();
+ ~AdaptiveRAnsBitEncoder();
+
+ // Must be called before any Encode* function is called.
+ void StartEncoding();
+
+ // Encode one bit. If |bit| is true encode a 1, otherwise encode a 0.
+ void EncodeBit(bool bit) { bits_.push_back(bit); }
+
+ // Encode |nbits| of |value|, starting from the least significant bit.
+ // |nbits| must be > 0 and <= 32.
+ void EncodeLeastSignificantBits32(int nbits, uint32_t value) {
+ DRACO_DCHECK_EQ(true, nbits <= 32);
+ DRACO_DCHECK_EQ(true, nbits > 0);
+ uint32_t selector = (1 << (nbits - 1));
+ while (selector) {
+ EncodeBit(value & selector);
+ selector = selector >> 1;
+ }
+ }
+
+ // Ends the bit encoding and stores the result into the target_buffer.
+ void EndEncoding(EncoderBuffer *target_buffer);
+
+ private:
+ void Clear();
+
+ std::vector<bool> bits_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_BIT_CODERS_ADAPTIVE_RANS_BIT_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_decoder.cc b/extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_decoder.cc
new file mode 100644
index 00000000000..515ea9b52ee
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_decoder.cc
@@ -0,0 +1,50 @@
+// 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 "draco/compression/bit_coders/direct_bit_decoder.h"
+
+namespace draco {
+
+DirectBitDecoder::DirectBitDecoder() : pos_(bits_.end()), num_used_bits_(0) {}
+
+DirectBitDecoder::~DirectBitDecoder() { Clear(); }
+
+bool DirectBitDecoder::StartDecoding(DecoderBuffer *source_buffer) {
+ Clear();
+ uint32_t size_in_bytes;
+ if (!source_buffer->Decode(&size_in_bytes))
+ return false;
+
+ // Check that size_in_bytes is > 0 and a multiple of 4 as the encoder always
+ // encodes 32 bit elements.
+ if (size_in_bytes == 0 || size_in_bytes & 0x3)
+ return false;
+ if (size_in_bytes > source_buffer->remaining_size())
+ return false;
+ const uint32_t num_32bit_elements = size_in_bytes / 4;
+ bits_.resize(num_32bit_elements);
+ if (!source_buffer->Decode(bits_.data(), size_in_bytes))
+ return false;
+ pos_ = bits_.begin();
+ num_used_bits_ = 0;
+ return true;
+}
+
+void DirectBitDecoder::Clear() {
+ bits_.clear();
+ num_used_bits_ = 0;
+ pos_ = bits_.end();
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_decoder.h b/extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_decoder.h
new file mode 100644
index 00000000000..b9fbc2d6fed
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_decoder.h
@@ -0,0 +1,90 @@
+// 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.
+//
+// File provides direct encoding of bits with arithmetic encoder interface.
+#ifndef DRACO_COMPRESSION_BIT_CODERS_DIRECT_BIT_DECODER_H_
+#define DRACO_COMPRESSION_BIT_CODERS_DIRECT_BIT_DECODER_H_
+
+#include <vector>
+
+#include "draco/core/decoder_buffer.h"
+
+namespace draco {
+
+class DirectBitDecoder {
+ public:
+ DirectBitDecoder();
+ ~DirectBitDecoder();
+
+ // Sets |source_buffer| as the buffer to decode bits from.
+ bool StartDecoding(DecoderBuffer *source_buffer);
+
+ // Decode one bit. Returns true if the bit is a 1, otherwise false.
+ bool DecodeNextBit() {
+ const uint32_t selector = 1 << (31 - num_used_bits_);
+ if (pos_ == bits_.end()) {
+ return false;
+ }
+ const bool bit = *pos_ & selector;
+ ++num_used_bits_;
+ if (num_used_bits_ == 32) {
+ ++pos_;
+ num_used_bits_ = 0;
+ }
+ return bit;
+ }
+
+ // Decode the next |nbits| and return the sequence in |value|. |nbits| must be
+ // > 0 and <= 32.
+ void DecodeLeastSignificantBits32(int nbits, uint32_t *value) {
+ DRACO_DCHECK_EQ(true, nbits <= 32);
+ DRACO_DCHECK_EQ(true, nbits > 0);
+ const int remaining = 32 - num_used_bits_;
+ if (nbits <= remaining) {
+ if (pos_ == bits_.end()) {
+ *value = 0;
+ return;
+ }
+ *value = (*pos_ << num_used_bits_) >> (32 - nbits);
+ num_used_bits_ += nbits;
+ if (num_used_bits_ == 32) {
+ ++pos_;
+ num_used_bits_ = 0;
+ }
+ } else {
+ if (pos_ + 1 == bits_.end()) {
+ *value = 0;
+ return;
+ }
+ const uint32_t value_l = ((*pos_) << num_used_bits_);
+ num_used_bits_ = nbits - remaining;
+ ++pos_;
+ const uint32_t value_r = (*pos_) >> (32 - num_used_bits_);
+ *value = (value_l >> (32 - num_used_bits_ - remaining)) | value_r;
+ }
+ }
+
+ void EndDecoding() {}
+
+ private:
+ void Clear();
+
+ std::vector<uint32_t> bits_;
+ std::vector<uint32_t>::const_iterator pos_;
+ uint32_t num_used_bits_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_BIT_CODERS_DIRECT_BIT_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_encoder.cc b/extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_encoder.cc
new file mode 100644
index 00000000000..d39143cf56a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_encoder.cc
@@ -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.
+//
+#include "draco/compression/bit_coders/direct_bit_encoder.h"
+
+namespace draco {
+
+DirectBitEncoder::DirectBitEncoder() : local_bits_(0), num_local_bits_(0) {}
+
+DirectBitEncoder::~DirectBitEncoder() { Clear(); }
+
+void DirectBitEncoder::StartEncoding() { Clear(); }
+
+void DirectBitEncoder::EndEncoding(EncoderBuffer *target_buffer) {
+ bits_.push_back(local_bits_);
+ const uint32_t size_in_byte = static_cast<uint32_t>(bits_.size()) * 4;
+ target_buffer->Encode(size_in_byte);
+ target_buffer->Encode(bits_.data(), size_in_byte);
+ Clear();
+}
+
+void DirectBitEncoder::Clear() {
+ bits_.clear();
+ local_bits_ = 0;
+ num_local_bits_ = 0;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_encoder.h b/extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_encoder.h
new file mode 100644
index 00000000000..705b2ca93c2
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/direct_bit_encoder.h
@@ -0,0 +1,89 @@
+// 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.
+//
+// File provides direct encoding of bits with arithmetic encoder interface.
+#ifndef DRACO_COMPRESSION_BIT_CODERS_DIRECT_BIT_ENCODER_H_
+#define DRACO_COMPRESSION_BIT_CODERS_DIRECT_BIT_ENCODER_H_
+
+#include <vector>
+
+#include "draco/core/encoder_buffer.h"
+
+namespace draco {
+
+class DirectBitEncoder {
+ public:
+ DirectBitEncoder();
+ ~DirectBitEncoder();
+
+ // Must be called before any Encode* function is called.
+ void StartEncoding();
+
+ // Encode one bit. If |bit| is true encode a 1, otherwise encode a 0.
+ void EncodeBit(bool bit) {
+ if (bit) {
+ local_bits_ |= 1 << (31 - num_local_bits_);
+ }
+ num_local_bits_++;
+ if (num_local_bits_ == 32) {
+ bits_.push_back(local_bits_);
+ num_local_bits_ = 0;
+ local_bits_ = 0;
+ }
+ }
+
+ // Encode |nbits| of |value|, starting from the least significant bit.
+ // |nbits| must be > 0 and <= 32.
+ void EncodeLeastSignificantBits32(int nbits, uint32_t value) {
+ DRACO_DCHECK_EQ(true, nbits <= 32);
+ DRACO_DCHECK_EQ(true, nbits > 0);
+
+ const int remaining = 32 - num_local_bits_;
+
+ // Make sure there are no leading bits that should not be encoded and
+ // start from here.
+ value = value << (32 - nbits);
+ if (nbits <= remaining) {
+ value = value >> num_local_bits_;
+ local_bits_ = local_bits_ | value;
+ num_local_bits_ += nbits;
+ if (num_local_bits_ == 32) {
+ bits_.push_back(local_bits_);
+ local_bits_ = 0;
+ num_local_bits_ = 0;
+ }
+ } else {
+ value = value >> (32 - nbits);
+ num_local_bits_ = nbits - remaining;
+ const uint32_t value_l = value >> num_local_bits_;
+ local_bits_ = local_bits_ | value_l;
+ bits_.push_back(local_bits_);
+ local_bits_ = value << (32 - num_local_bits_);
+ }
+ }
+
+ // Ends the bit encoding and stores the result into the target_buffer.
+ void EndEncoding(EncoderBuffer *target_buffer);
+
+ private:
+ void Clear();
+
+ std::vector<uint32_t> bits_;
+ uint32_t local_bits_;
+ uint32_t num_local_bits_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_BIT_CODERS_DIRECT_BIT_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/folded_integer_bit_decoder.h b/extern/draco/dracoenc/src/draco/compression/bit_coders/folded_integer_bit_decoder.h
new file mode 100644
index 00000000000..9f8bedd1eb0
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/folded_integer_bit_decoder.h
@@ -0,0 +1,76 @@
+// 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.
+//
+// File provides direct encoding of bits with arithmetic encoder interface.
+#ifndef DRACO_COMPRESSION_BIT_CODERS_FOLDED_INTEGER_BIT_DECODER_H_
+#define DRACO_COMPRESSION_BIT_CODERS_FOLDED_INTEGER_BIT_DECODER_H_
+
+#include <vector>
+
+#include "draco/core/decoder_buffer.h"
+
+namespace draco {
+
+// See FoldedBit32Encoder for more details.
+template <class BitDecoderT>
+class FoldedBit32Decoder {
+ public:
+ FoldedBit32Decoder() {}
+ ~FoldedBit32Decoder() {}
+
+ // Sets |source_buffer| as the buffer to decode bits from.
+ bool StartDecoding(DecoderBuffer *source_buffer) {
+ for (int i = 0; i < 32; i++) {
+ if (!folded_number_decoders_[i].StartDecoding(source_buffer))
+ return false;
+ }
+ return bit_decoder_.StartDecoding(source_buffer);
+ }
+
+ // Decode one bit. Returns true if the bit is a 1, otherwise false.
+ bool DecodeNextBit() { return bit_decoder_.DecodeNextBit(); }
+
+ // Decode the next |nbits| and return the sequence in |value|. |nbits| must be
+ // > 0 and <= 32.
+ 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();
+ result = (result << 1) + bit;
+ }
+ *value = result;
+ }
+
+ void EndDecoding() {
+ for (int i = 0; i < 32; i++) {
+ folded_number_decoders_[i].EndDecoding();
+ }
+ bit_decoder_.EndDecoding();
+ }
+
+ private:
+ void Clear() {
+ for (int i = 0; i < 32; i++) {
+ folded_number_decoders_[i].Clear();
+ }
+ bit_decoder_.Clear();
+ }
+
+ std::array<BitDecoderT, 32> folded_number_decoders_;
+ BitDecoderT bit_decoder_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_BIT_CODERS_FOLDED_INTEGER_BIT_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/folded_integer_bit_encoder.h b/extern/draco/dracoenc/src/draco/compression/bit_coders/folded_integer_bit_encoder.h
new file mode 100644
index 00000000000..375b38a61fb
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/folded_integer_bit_encoder.h
@@ -0,0 +1,82 @@
+// 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.
+//
+// File provides direct encoding of bits with arithmetic encoder interface.
+#ifndef DRACO_COMPRESSION_BIT_CODERS_FOLDED_INTEGER_BIT_ENCODER_H_
+#define DRACO_COMPRESSION_BIT_CODERS_FOLDED_INTEGER_BIT_ENCODER_H_
+
+#include <vector>
+
+#include "draco/core/encoder_buffer.h"
+
+namespace draco {
+
+// This coding scheme considers every bit of an (up to) 32bit integer as a
+// separate context. This can be a significant advantage when encoding numbers
+// where it is more likely that the front bits are zero.
+// The behavior is essentially the same as other arithmetic encoding schemes,
+// the only difference is that encoding and decoding of bits must be absolutely
+// symmetric, bits handed in by EncodeBit32 must be also decoded in this way.
+// This is the FoldedBit32Encoder, see also FoldedBit32Decoder.
+template <class BitEncoderT>
+class FoldedBit32Encoder {
+ public:
+ FoldedBit32Encoder() {}
+ ~FoldedBit32Encoder() {}
+
+ // Must be called before any Encode* function is called.
+ void StartEncoding() {
+ for (int i = 0; i < 32; i++) {
+ folded_number_encoders_[i].StartEncoding();
+ }
+ bit_encoder_.StartEncoding();
+ }
+
+ // Encode one bit. If |bit| is true encode a 1, otherwise encode a 0.
+ void EncodeBit(bool bit) { bit_encoder_.EncodeBit(bit); }
+
+ // Encode |nbits| of |value|, starting from the least significant bit.
+ // |nbits| must be > 0 and <= 32.
+ 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);
+ folded_number_encoders_[i].EncodeBit(bit);
+ selector = selector >> 1;
+ }
+ }
+
+ // Ends the bit encoding and stores the result into the target_buffer.
+ void EndEncoding(EncoderBuffer *target_buffer) {
+ for (int i = 0; i < 32; i++) {
+ folded_number_encoders_[i].EndEncoding(target_buffer);
+ }
+ bit_encoder_.EndEncoding(target_buffer);
+ }
+
+ private:
+ void Clear() {
+ for (int i = 0; i < 32; i++) {
+ folded_number_encoders_[i].Clear();
+ }
+ bit_encoder_.Clear();
+ }
+
+ std::array<BitEncoderT, 32> folded_number_encoders_;
+ BitEncoderT bit_encoder_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_BIT_CODERS_FOLDED_INTEGER_BIT_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_decoder.cc b/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_decoder.cc
new file mode 100644
index 00000000000..023e0dbc192
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_decoder.cc
@@ -0,0 +1,77 @@
+// 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 "draco/compression/bit_coders/rans_bit_decoder.h"
+
+#include "draco/compression/config/compression_shared.h"
+#include "draco/core/bit_utils.h"
+#include "draco/core/varint_decoding.h"
+
+namespace draco {
+
+RAnsBitDecoder::RAnsBitDecoder() : prob_zero_(0) {}
+
+RAnsBitDecoder::~RAnsBitDecoder() { Clear(); }
+
+bool RAnsBitDecoder::StartDecoding(DecoderBuffer *source_buffer) {
+ Clear();
+
+ if (!source_buffer->Decode(&prob_zero_))
+ return false;
+
+ uint32_t size_in_bytes;
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (source_buffer->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ if (!source_buffer->Decode(&size_in_bytes))
+ return false;
+
+ } else
+#endif
+ {
+ if (!DecodeVarint(&size_in_bytes, source_buffer))
+ return false;
+ }
+
+ if (size_in_bytes > source_buffer->remaining_size())
+ return false;
+
+ if (ans_read_init(&ans_decoder_,
+ reinterpret_cast<uint8_t *>(
+ const_cast<char *>(source_buffer->data_head())),
+ size_in_bytes) != 0)
+ return false;
+ source_buffer->Advance(size_in_bytes);
+ return true;
+}
+
+bool RAnsBitDecoder::DecodeNextBit() {
+ const uint8_t bit = rabs_read(&ans_decoder_, prob_zero_);
+ return bit > 0;
+}
+
+void RAnsBitDecoder::DecodeLeastSignificantBits32(int nbits, uint32_t *value) {
+ DRACO_DCHECK_EQ(true, nbits <= 32);
+ DRACO_DCHECK_EQ(true, nbits > 0);
+
+ uint32_t result = 0;
+ while (nbits) {
+ result = (result << 1) + DecodeNextBit();
+ --nbits;
+ }
+ *value = result;
+}
+
+void RAnsBitDecoder::Clear() { ans_read_end(&ans_decoder_); }
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_decoder.h b/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_decoder.h
new file mode 100644
index 00000000000..1fbf6a1324a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_decoder.h
@@ -0,0 +1,56 @@
+// 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.
+//
+// File provides basic classes and functions for rANS coding.
+#ifndef DRACO_COMPRESSION_BIT_CODERS_RANS_BIT_DECODER_H_
+#define DRACO_COMPRESSION_BIT_CODERS_RANS_BIT_DECODER_H_
+
+#include <vector>
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/entropy/ans.h"
+#include "draco/core/decoder_buffer.h"
+
+namespace draco {
+
+// Class for decoding a sequence of bits that were encoded with RAnsBitEncoder.
+class RAnsBitDecoder {
+ public:
+ RAnsBitDecoder();
+ ~RAnsBitDecoder();
+
+ // Sets |source_buffer| as the buffer to decode bits from.
+ // Returns false when the data is invalid.
+ bool StartDecoding(DecoderBuffer *source_buffer);
+
+ // Decode one bit. Returns true if the bit is a 1, otherwise false.
+ bool DecodeNextBit();
+
+ // Decode the next |nbits| and return the sequence in |value|. |nbits| must be
+ // > 0 and <= 32.
+ void DecodeLeastSignificantBits32(int nbits, uint32_t *value);
+
+ void EndDecoding() {}
+
+ private:
+ void Clear();
+
+ AnsDecoder ans_decoder_;
+ uint8_t prob_zero_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_BIT_CODERS_RANS_BIT_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_encoder.cc b/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_encoder.cc
new file mode 100644
index 00000000000..ea2972fe262
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_encoder.cc
@@ -0,0 +1,123 @@
+// 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 "draco/compression/bit_coders/rans_bit_encoder.h"
+
+#include "draco/compression/entropy/ans.h"
+#include "draco/core/bit_utils.h"
+#include "draco/core/varint_encoding.h"
+
+namespace draco {
+
+RAnsBitEncoder::RAnsBitEncoder() : local_bits_(0), num_local_bits_(0) {}
+
+RAnsBitEncoder::~RAnsBitEncoder() { Clear(); }
+
+void RAnsBitEncoder::StartEncoding() { Clear(); }
+
+void RAnsBitEncoder::EncodeBit(bool bit) {
+ if (bit) {
+ bit_counts_[1]++;
+ local_bits_ |= 1 << num_local_bits_;
+ } else {
+ bit_counts_[0]++;
+ }
+ num_local_bits_++;
+
+ if (num_local_bits_ == 32) {
+ bits_.push_back(local_bits_);
+ num_local_bits_ = 0;
+ local_bits_ = 0;
+ }
+}
+
+void RAnsBitEncoder::EncodeLeastSignificantBits32(int nbits, uint32_t value) {
+ DRACO_DCHECK_EQ(true, nbits <= 32);
+ DRACO_DCHECK_EQ(true, nbits > 0);
+
+ const uint32_t reversed = ReverseBits32(value) >> (32 - nbits);
+ const int ones = CountOneBits32(reversed);
+ bit_counts_[0] += (nbits - ones);
+ bit_counts_[1] += ones;
+
+ const int remaining = 32 - num_local_bits_;
+
+ if (nbits <= remaining) {
+ CopyBits32(&local_bits_, num_local_bits_, reversed, 0, nbits);
+ num_local_bits_ += nbits;
+ if (num_local_bits_ == 32) {
+ bits_.push_back(local_bits_);
+ local_bits_ = 0;
+ num_local_bits_ = 0;
+ }
+ } else {
+ CopyBits32(&local_bits_, num_local_bits_, reversed, 0, remaining);
+ bits_.push_back(local_bits_);
+ local_bits_ = 0;
+ CopyBits32(&local_bits_, 0, reversed, remaining, nbits - remaining);
+ num_local_bits_ = nbits - remaining;
+ }
+}
+
+void RAnsBitEncoder::EndEncoding(EncoderBuffer *target_buffer) {
+ uint64_t total = bit_counts_[1] + bit_counts_[0];
+ if (total == 0)
+ total++;
+
+ // The probability interval [0,1] is mapped to values of [0, 256]. However,
+ // the coding scheme can not deal with probabilities of 0 or 1, which is why
+ // we must clamp the values to interval [1, 255]. Specifically 128
+ // corresponds to 0.5 exactly. And the value can be given as uint8_t.
+ const uint32_t zero_prob_raw = static_cast<uint32_t>(
+ ((bit_counts_[0] / static_cast<double>(total)) * 256.0) + 0.5);
+
+ uint8_t zero_prob = 255;
+ if (zero_prob_raw < 255)
+ zero_prob = static_cast<uint8_t>(zero_prob_raw);
+
+ zero_prob += (zero_prob == 0);
+
+ // Space for 32 bit integer and some extra space.
+ std::vector<uint8_t> buffer((bits_.size() + 8) * 8);
+ AnsCoder ans_coder;
+ ans_write_init(&ans_coder, buffer.data());
+
+ for (int i = num_local_bits_ - 1; i >= 0; --i) {
+ const uint8_t bit = (local_bits_ >> i) & 1;
+ rabs_write(&ans_coder, bit, zero_prob);
+ }
+ for (auto it = bits_.rbegin(); it != bits_.rend(); ++it) {
+ const uint32_t bits = *it;
+ for (int i = 31; i >= 0; --i) {
+ const uint8_t bit = (bits >> i) & 1;
+ rabs_write(&ans_coder, bit, zero_prob);
+ }
+ }
+
+ const int size_in_bytes = ans_write_end(&ans_coder);
+ target_buffer->Encode(zero_prob);
+ EncodeVarint(static_cast<uint32_t>(size_in_bytes), target_buffer);
+ target_buffer->Encode(buffer.data(), size_in_bytes);
+
+ Clear();
+}
+
+void RAnsBitEncoder::Clear() {
+ bit_counts_.assign(2, 0);
+ bits_.clear();
+ local_bits_ = 0;
+ num_local_bits_ = 0;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_encoder.h b/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_encoder.h
new file mode 100644
index 00000000000..1993dd3d3c2
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_bit_encoder.h
@@ -0,0 +1,57 @@
+// 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.
+//
+// File provides basic classes and functions for rANS coding.
+#ifndef DRACO_COMPRESSION_BIT_CODERS_RANS_BIT_ENCODER_H_
+#define DRACO_COMPRESSION_BIT_CODERS_RANS_BIT_ENCODER_H_
+
+#include <vector>
+
+#include "draco/core/encoder_buffer.h"
+
+namespace draco {
+
+// Class for encoding a sequence of bits using rANS. The probability table used
+// to encode the bits is based off the total counts of bits.
+// TODO(fgalligan): Investigate using an adaptive table for more compression.
+class RAnsBitEncoder {
+ public:
+ RAnsBitEncoder();
+ ~RAnsBitEncoder();
+
+ // Must be called before any Encode* function is called.
+ void StartEncoding();
+
+ // Encode one bit. If |bit| is true encode a 1, otherwise encode a 0.
+ void EncodeBit(bool bit);
+
+ // Encode |nbits| of |value|, starting from the least significant bit.
+ // |nbits| must be > 0 and <= 32.
+ void EncodeLeastSignificantBits32(int nbits, uint32_t value);
+
+ // Ends the bit encoding and stores the result into the target_buffer.
+ void EndEncoding(EncoderBuffer *target_buffer);
+
+ private:
+ void Clear();
+
+ std::vector<uint64_t> bit_counts_;
+ std::vector<uint32_t> bits_;
+ uint32_t local_bits_;
+ uint32_t num_local_bits_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_BIT_CODERS_RANS_BIT_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_coding_test.cc b/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_coding_test.cc
new file mode 100644
index 00000000000..9509ad9f354
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/rans_coding_test.cc
@@ -0,0 +1,9 @@
+#include "draco/compression/bit_coders/adaptive_rans_bit_decoder.h"
+#include "draco/compression/bit_coders/adaptive_rans_bit_encoder.h"
+#include "draco/compression/bit_coders/rans_bit_decoder.h"
+#include "draco/compression/bit_coders/rans_bit_encoder.h"
+#include "draco/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/extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_decoder.cc b/extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_decoder.cc
new file mode 100644
index 00000000000..6b35ae3f87f
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_decoder.cc
@@ -0,0 +1,47 @@
+#include "draco/compression/bit_coders/symbol_bit_decoder.h"
+
+#include "draco/compression/entropy/symbol_decoding.h"
+
+namespace draco {
+
+bool SymbolBitDecoder::StartDecoding(DecoderBuffer *source_buffer) {
+ uint32_t size;
+ if (!source_buffer->Decode(&size))
+ return false;
+
+ symbols_.resize(size);
+ if (!DecodeSymbols(size, 1, source_buffer, symbols_.data()))
+ return false;
+ std::reverse(symbols_.begin(), symbols_.end());
+ return true;
+}
+
+bool SymbolBitDecoder::DecodeNextBit() {
+ uint32_t symbol;
+ DecodeLeastSignificantBits32(1, &symbol);
+ DRACO_DCHECK(symbol == 0 || symbol == 1);
+ return symbol == 1;
+}
+
+void SymbolBitDecoder::DecodeLeastSignificantBits32(int nbits,
+ uint32_t *value) {
+ DRACO_DCHECK_LE(1, nbits);
+ DRACO_DCHECK_LE(nbits, 32);
+ DRACO_DCHECK_NE(value, nullptr);
+ // Testing: check to make sure there is something to decode.
+ DRACO_DCHECK_GT(symbols_.size(), 0);
+
+ (*value) = symbols_.back();
+ symbols_.pop_back();
+
+ const int discarded_bits = 32 - nbits;
+ (*value) <<= discarded_bits;
+ (*value) >>= discarded_bits;
+}
+
+void SymbolBitDecoder::Clear() {
+ symbols_.clear();
+ symbols_.shrink_to_fit();
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_decoder.h b/extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_decoder.h
new file mode 100644
index 00000000000..909d7174fb5
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_decoder.h
@@ -0,0 +1,36 @@
+#ifndef DRACO_COMPRESSION_BIT_CODERS_SYMBOL_BIT_DECODER_H_
+#define DRACO_COMPRESSION_BIT_CODERS_SYMBOL_BIT_DECODER_H_
+
+#include <algorithm>
+#include <vector>
+
+#include "draco/core/decoder_buffer.h"
+
+namespace draco {
+
+// Class for decoding bits using the symbol entropy encoding. Wraps
+// |DecodeSymbols|. Note that this uses a symbol-based encoding scheme for
+// encoding bits.
+class SymbolBitDecoder {
+ public:
+ // Sets |source_buffer| as the buffer to decode bits from.
+ bool StartDecoding(DecoderBuffer *source_buffer);
+
+ // Decode one bit. Returns true if the bit is a 1, otherwise false.
+ bool DecodeNextBit();
+
+ // Decode the next |nbits| and return the sequence in |value|. |nbits| must be
+ // > 0 and <= 32.
+ void DecodeLeastSignificantBits32(int nbits, uint32_t *value);
+
+ void EndDecoding() { Clear(); }
+
+ private:
+ void Clear();
+
+ std::vector<uint32_t> symbols_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_BIT_CODERS_SYMBOL_BIT_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_encoder.cc b/extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_encoder.cc
new file mode 100644
index 00000000000..83834236fa5
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_encoder.cc
@@ -0,0 +1,30 @@
+#include "draco/compression/bit_coders/symbol_bit_encoder.h"
+
+#include "draco/compression/entropy/symbol_encoding.h"
+
+namespace draco {
+
+void SymbolBitEncoder::EncodeLeastSignificantBits32(int nbits, uint32_t value) {
+ DRACO_DCHECK_LE(1, nbits);
+ DRACO_DCHECK_LE(nbits, 32);
+
+ const int discarded_bits = 32 - nbits;
+ value <<= discarded_bits;
+ value >>= discarded_bits;
+
+ symbols_.push_back(value);
+}
+
+void SymbolBitEncoder::EndEncoding(EncoderBuffer *target_buffer) {
+ target_buffer->Encode(static_cast<uint32_t>(symbols_.size()));
+ EncodeSymbols(symbols_.data(), static_cast<int>(symbols_.size()), 1, nullptr,
+ target_buffer);
+ Clear();
+}
+
+void SymbolBitEncoder::Clear() {
+ symbols_.clear();
+ symbols_.shrink_to_fit();
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_encoder.h b/extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_encoder.h
new file mode 100644
index 00000000000..7f1570c1a7c
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/bit_coders/symbol_bit_encoder.h
@@ -0,0 +1,36 @@
+#ifndef DRACO_COMPRESSION_BIT_CODERS_SYMBOL_BIT_ENCODER_H_
+#define DRACO_COMPRESSION_BIT_CODERS_SYMBOL_BIT_ENCODER_H_
+
+#include <algorithm>
+#include <vector>
+
+#include "draco/core/encoder_buffer.h"
+
+namespace draco {
+
+// Class for encoding bits using the symbol entropy encoding. Wraps
+// |EncodeSymbols|. Note that this uses a symbol-based encoding scheme for
+// encoding bits.
+class SymbolBitEncoder {
+ public:
+ // Must be called before any Encode* function is called.
+ void StartEncoding() { Clear(); }
+
+ // Encode one bit. If |bit| is true encode a 1, otherwise encode a 0.
+ void EncodeBit(bool bit) { EncodeLeastSignificantBits32(1, bit ? 1 : 0); }
+
+ // Encode |nbits| LSBs of |value| as a symbol. |nbits| must be > 0 and <= 32.
+ void EncodeLeastSignificantBits32(int nbits, uint32_t value);
+
+ // Ends the bit encoding and stores the result into the target_buffer.
+ void EndEncoding(EncoderBuffer *target_buffer);
+
+ private:
+ void Clear();
+
+ std::vector<uint32_t> symbols_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_BIT_CODERS_SYMBOL_BIT_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/config/compression_shared.h b/extern/draco/dracoenc/src/draco/compression/config/compression_shared.h
new file mode 100644
index 00000000000..5204fbc1f3e
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/config/compression_shared.h
@@ -0,0 +1,153 @@
+// 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_COMPRESSION_CONFIG_COMPRESSION_SHARED_H_
+#define DRACO_COMPRESSION_CONFIG_COMPRESSION_SHARED_H_
+
+#include <stdint.h>
+
+#include "draco/core/macros.h"
+
+#include "draco/draco_features.h"
+
+namespace draco {
+
+// Latest Draco bit-stream version.
+static constexpr uint8_t kDracoPointCloudBitstreamVersionMajor = 2;
+static constexpr uint8_t kDracoPointCloudBitstreamVersionMinor = 3;
+static constexpr uint8_t kDracoMeshBitstreamVersionMajor = 2;
+static constexpr uint8_t kDracoMeshBitstreamVersionMinor = 2;
+
+// Concatenated latest bit-stream version.
+static constexpr uint16_t kDracoPointCloudBitstreamVersion =
+ DRACO_BITSTREAM_VERSION(kDracoPointCloudBitstreamVersionMajor,
+ kDracoPointCloudBitstreamVersionMinor);
+
+static constexpr uint16_t kDracoMeshBitstreamVersion = DRACO_BITSTREAM_VERSION(
+ kDracoMeshBitstreamVersionMajor, kDracoMeshBitstreamVersionMinor);
+
+// Currently, we support point cloud and triangular mesh encoding.
+// TODO(draco-eng) Convert enum to enum class (safety, not performance).
+enum EncodedGeometryType {
+ INVALID_GEOMETRY_TYPE = -1,
+ POINT_CLOUD = 0,
+ TRIANGULAR_MESH,
+};
+
+// List of encoding methods for point clouds.
+enum PointCloudEncodingMethod {
+ POINT_CLOUD_SEQUENTIAL_ENCODING = 0,
+ POINT_CLOUD_KD_TREE_ENCODING
+};
+
+// List of encoding methods for meshes.
+enum MeshEncoderMethod {
+ MESH_SEQUENTIAL_ENCODING = 0,
+ MESH_EDGEBREAKER_ENCODING,
+};
+
+// List of various attribute encoders supported by our framework. The entries
+// are used as unique identifiers of the encoders and their values should not
+// be changed!
+enum AttributeEncoderType {
+ BASIC_ATTRIBUTE_ENCODER = 0,
+ MESH_TRAVERSAL_ATTRIBUTE_ENCODER,
+ KD_TREE_ATTRIBUTE_ENCODER,
+};
+
+// List of various sequential attribute encoder/decoders that can be used in our
+// pipeline. The values represent unique identifiers used by the decoder and
+// they should not be changed.
+enum SequentialAttributeEncoderType {
+ SEQUENTIAL_ATTRIBUTE_ENCODER_GENERIC = 0,
+ SEQUENTIAL_ATTRIBUTE_ENCODER_INTEGER,
+ SEQUENTIAL_ATTRIBUTE_ENCODER_QUANTIZATION,
+ SEQUENTIAL_ATTRIBUTE_ENCODER_NORMALS,
+};
+
+// List of all prediction methods currently supported by our framework.
+enum PredictionSchemeMethod {
+ // Special value indicating that no prediction scheme was used.
+ PREDICTION_NONE = -2,
+ // Used when no specific prediction scheme is required.
+ PREDICTION_UNDEFINED = -1,
+ PREDICTION_DIFFERENCE = 0,
+ MESH_PREDICTION_PARALLELOGRAM = 1,
+ MESH_PREDICTION_MULTI_PARALLELOGRAM = 2,
+ MESH_PREDICTION_TEX_COORDS_DEPRECATED = 3,
+ MESH_PREDICTION_CONSTRAINED_MULTI_PARALLELOGRAM = 4,
+ MESH_PREDICTION_TEX_COORDS_PORTABLE = 5,
+ MESH_PREDICTION_GEOMETRIC_NORMAL = 6,
+ NUM_PREDICTION_SCHEMES
+};
+
+// List of all prediction scheme transforms used by our framework.
+enum PredictionSchemeTransformType {
+ PREDICTION_TRANSFORM_NONE = -1,
+ // Basic delta transform where the prediction is computed as difference the
+ // predicted and original value.
+ PREDICTION_TRANSFORM_DELTA = 0,
+ // An improved delta transform where all computed delta values are wrapped
+ // around a fixed interval which lowers the entropy.
+ PREDICTION_TRANSFORM_WRAP = 1,
+ // Specialized transform for normal coordinates using inverted tiles.
+ PREDICTION_TRANSFORM_NORMAL_OCTAHEDRON = 2,
+ // Specialized transform for normal coordinates using canonicalized inverted
+ // tiles.
+ PREDICTION_TRANSFORM_NORMAL_OCTAHEDRON_CANONICALIZED = 3,
+};
+
+// List of all mesh traversal methods supported by Draco framework.
+enum MeshTraversalMethod {
+ MESH_TRAVERSAL_DEPTH_FIRST = 0,
+ MESH_TRAVERSAL_PREDICTION_DEGREE = 1,
+ NUM_TRAVERSAL_METHODS
+};
+
+// List of all variant of the edgebreaker method that is used for compression
+// of mesh connectivity.
+enum MeshEdgebreakerConnectivityEncodingMethod {
+ MESH_EDGEBREAKER_STANDARD_ENCODING = 0,
+ MESH_EDGEBREAKER_PREDICTIVE_ENCODING = 1, // Deprecated.
+ MESH_EDGEBREAKER_VALENCE_ENCODING = 2,
+};
+
+// Draco header V1
+struct DracoHeader {
+ int8_t draco_string[5];
+ uint8_t version_major;
+ uint8_t version_minor;
+ uint8_t encoder_type;
+ uint8_t encoder_method;
+ uint16_t flags;
+};
+
+enum NormalPredictionMode {
+ ONE_TRIANGLE = 0, // To be deprecated.
+ TRIANGLE_AREA = 1,
+};
+
+// Different methods used for symbol entropy encoding.
+enum SymbolCodingMethod {
+ SYMBOL_CODING_TAGGED = 0,
+ SYMBOL_CODING_RAW = 1,
+ NUM_SYMBOL_CODING_METHODS,
+};
+
+// Mask for setting and getting the bit for metadata in |flags| of header.
+#define METADATA_FLAG_MASK 0x8000
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_CONFIG_COMPRESSION_SHARED_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/config/decoder_options.h b/extern/draco/dracoenc/src/draco/compression/config/decoder_options.h
new file mode 100644
index 00000000000..3b3889993e2
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/config/decoder_options.h
@@ -0,0 +1,34 @@
+// Copyright 2017 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_COMPRESSION_CONFIG_DECODER_OPTIONS_H_
+#define DRACO_COMPRESSION_CONFIG_DECODER_OPTIONS_H_
+
+#include <map>
+#include <memory>
+
+#include "draco/attributes/geometry_attribute.h"
+#include "draco/compression/config/draco_options.h"
+
+namespace draco {
+
+// Class containing options that can be passed to PointCloudDecoder to control
+// decoding of the input geometry. The options can be specified either for the
+// whole geometry or for a specific attribute type. Each option is identified
+// by a unique name stored as an std::string.
+typedef DracoOptions<GeometryAttribute::Type> DecoderOptions;
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_CONFIG_DECODER_OPTIONS_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/config/decoder_options_test.cc b/extern/draco/dracoenc/src/draco/compression/config/decoder_options_test.cc
new file mode 100644
index 00000000000..a5cd7f10640
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/config/decoder_options_test.cc
@@ -0,0 +1,67 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/compression/config/decoder_options.h"
+
+#include "draco/core/draco_test_base.h"
+
+namespace {
+
+class DecoderOptionsTest : public ::testing::Test {
+ protected:
+ DecoderOptionsTest() {}
+};
+
+TEST_F(DecoderOptionsTest, TestOptions) {
+ // This test verifies that we can update global and attribute options of the
+ // DecoderOptions class instance.
+ draco::DecoderOptions options;
+ options.SetGlobalInt("test", 3);
+ ASSERT_EQ(options.GetGlobalInt("test", -1), 3);
+
+ options.SetAttributeInt(draco::GeometryAttribute::POSITION, "test", 1);
+ options.SetAttributeInt(draco::GeometryAttribute::GENERIC, "test", 2);
+ ASSERT_EQ(
+ options.GetAttributeInt(draco::GeometryAttribute::TEX_COORD, "test", -1),
+ 3);
+ ASSERT_EQ(
+ options.GetAttributeInt(draco::GeometryAttribute::POSITION, "test", -1),
+ 1);
+ ASSERT_EQ(
+ options.GetAttributeInt(draco::GeometryAttribute::GENERIC, "test", -1),
+ 2);
+}
+
+TEST_F(DecoderOptionsTest, TestAttributeOptionsAccessors) {
+ // This test verifies that we can query options stored in DecoderOptions
+ // class instance.
+ draco::DecoderOptions options;
+ options.SetGlobalInt("test", 1);
+ options.SetAttributeInt(draco::GeometryAttribute::POSITION, "test", 2);
+ options.SetAttributeInt(draco::GeometryAttribute::TEX_COORD, "test", 3);
+
+ ASSERT_EQ(
+ options.GetAttributeInt(draco::GeometryAttribute::POSITION, "test", -1),
+ 2);
+ ASSERT_EQ(
+ options.GetAttributeInt(draco::GeometryAttribute::POSITION, "test2", -1),
+ -1);
+ ASSERT_EQ(
+ options.GetAttributeInt(draco::GeometryAttribute::TEX_COORD, "test", -1),
+ 3);
+ ASSERT_EQ(
+ options.GetAttributeInt(draco::GeometryAttribute::NORMAL, "test", -1), 1);
+}
+
+} // namespace
diff --git a/extern/draco/dracoenc/src/draco/compression/config/draco_options.h b/extern/draco/dracoenc/src/draco/compression/config/draco_options.h
new file mode 100644
index 00000000000..c77f5df4014
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/config/draco_options.h
@@ -0,0 +1,244 @@
+// Copyright 2017 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_SRC_DRACO_COMPRESSION_CONFIG_DRACO_OPTIONS_H_
+#define DRACO_SRC_DRACO_COMPRESSION_CONFIG_DRACO_OPTIONS_H_
+
+#include <map>
+#include <memory>
+
+#include "draco/core/options.h"
+
+namespace draco {
+
+// Base option class used to control encoding and decoding. The geometry coding
+// can be controlled through the following options:
+// 1. Global options - Options specific to overall geometry or options common
+// for all attributes
+// 2. Per attribute options - Options specific to a given attribute.
+// Each attribute is identified by the template
+// argument AttributeKeyT that can be for example
+// the attribute type or the attribute id.
+//
+// Example:
+//
+// DracoOptions<AttributeKey> options;
+//
+// // Set an option common for all attributes.
+// options.SetGlobalInt("some_option_name", 2);
+//
+// // Geometry with two attributes.
+// AttributeKey att_key0 = in_key0;
+// AttributeKey att_key1 = in_key1;
+//
+// options.SetAttributeInt(att_key0, "some_option_name", 3);
+//
+// options.GetAttributeInt(att_key0, "some_option_name"); // Returns 3
+// options.GetAttributeInt(att_key1, "some_option_name"); // Returns 2
+// options.GetGlobalInt("some_option_name"); // Returns 2
+//
+template <typename AttributeKeyT>
+class DracoOptions {
+ public:
+ typedef AttributeKeyT AttributeKey;
+
+ // Get an option for a specific attribute key. If the option is not found in
+ // an attribute specific storage, the implementation will return a global
+ // option of the given name (if available). If the option is not found, the
+ // provided default value |default_val| is returned instead.
+ int GetAttributeInt(const AttributeKey &att_key, const std::string &name,
+ int default_val) const;
+
+ // Sets an option for a specific attribute key.
+ void SetAttributeInt(const AttributeKey &att_key, const std::string &name,
+ int val);
+
+ float GetAttributeFloat(const AttributeKey &att_key, const std::string &name,
+ float default_val) const;
+ void SetAttributeFloat(const AttributeKey &att_key, const std::string &name,
+ float val);
+ bool GetAttributeBool(const AttributeKey &att_key, const std::string &name,
+ bool default_val) const;
+ void SetAttributeBool(const AttributeKey &att_key, const std::string &name,
+ bool val);
+ template <typename DataTypeT>
+ bool GetAttributeVector(const AttributeKey &att_key, const std::string &name,
+ int num_dims, DataTypeT *val) const;
+ template <typename DataTypeT>
+ void SetAttributeVector(const AttributeKey &att_key, const std::string &name,
+ int num_dims, const DataTypeT *val);
+
+ bool IsAttributeOptionSet(const AttributeKey &att_key,
+ const std::string &name) const;
+
+ // Gets/sets a global option that is not specific to any attribute.
+ int GetGlobalInt(const std::string &name, int default_val) const {
+ return global_options_.GetInt(name, default_val);
+ }
+ void SetGlobalInt(const std::string &name, int val) {
+ global_options_.SetInt(name, val);
+ }
+ float GetGlobalFloat(const std::string &name, float default_val) const {
+ return global_options_.GetFloat(name, default_val);
+ }
+ void SetGlobalFloat(const std::string &name, float val) {
+ global_options_.SetFloat(name, val);
+ }
+ bool GetGlobalBool(const std::string &name, bool default_val) const {
+ return global_options_.GetBool(name, default_val);
+ }
+ void SetGlobalBool(const std::string &name, bool val) {
+ global_options_.SetBool(name, val);
+ }
+ template <typename DataTypeT>
+ bool GetGlobalVector(const std::string &name, int num_dims,
+ DataTypeT *val) const {
+ return global_options_.GetVector(name, num_dims, val);
+ }
+ template <typename DataTypeT>
+ void SetGlobalVector(const std::string &name, int num_dims,
+ const DataTypeT *val) {
+ global_options_.SetVector(name, val, num_dims);
+ }
+ bool IsGlobalOptionSet(const std::string &name) const {
+ return global_options_.IsOptionSet(name);
+ }
+
+ // Sets or replaces attribute options with the provided |options|.
+ void SetAttributeOptions(const AttributeKey &att_key, const Options &options);
+ void SetGlobalOptions(const Options &options) { global_options_ = options; }
+
+ // Returns |Options| instance for the specified options class if it exists.
+ const Options *FindAttributeOptions(const AttributeKeyT &att_key) const;
+ const Options &GetGlobalOptions() const { return global_options_; }
+
+ private:
+ Options *GetAttributeOptions(const AttributeKeyT &att_key);
+
+ Options global_options_;
+
+ // Storage for options related to geometry attributes.
+ std::map<AttributeKey, Options> attribute_options_;
+};
+
+template <typename AttributeKeyT>
+const Options *DracoOptions<AttributeKeyT>::FindAttributeOptions(
+ const AttributeKeyT &att_key) const {
+ auto it = attribute_options_.find(att_key);
+ if (it == attribute_options_.end()) {
+ return nullptr;
+ }
+ return &it->second;
+}
+
+template <typename AttributeKeyT>
+Options *DracoOptions<AttributeKeyT>::GetAttributeOptions(
+ const AttributeKeyT &att_key) {
+ auto it = attribute_options_.find(att_key);
+ if (it != attribute_options_.end()) {
+ return &it->second;
+ }
+ Options new_options;
+ it = attribute_options_.insert(std::make_pair(att_key, new_options)).first;
+ return &it->second;
+}
+
+template <typename AttributeKeyT>
+int DracoOptions<AttributeKeyT>::GetAttributeInt(const AttributeKeyT &att_key,
+ const std::string &name,
+ int default_val) const {
+ const Options *const att_options = FindAttributeOptions(att_key);
+ if (att_options && att_options->IsOptionSet(name))
+ return att_options->GetInt(name, default_val);
+ return global_options_.GetInt(name, default_val);
+}
+
+template <typename AttributeKeyT>
+void DracoOptions<AttributeKeyT>::SetAttributeInt(const AttributeKeyT &att_key,
+ const std::string &name,
+ int val) {
+ GetAttributeOptions(att_key)->SetInt(name, val);
+}
+
+template <typename AttributeKeyT>
+float DracoOptions<AttributeKeyT>::GetAttributeFloat(
+ const AttributeKeyT &att_key, const std::string &name,
+ float default_val) const {
+ const Options *const att_options = FindAttributeOptions(att_key);
+ if (att_options && att_options->IsOptionSet(name))
+ return att_options->GetFloat(name, default_val);
+ return global_options_.GetFloat(name, default_val);
+}
+
+template <typename AttributeKeyT>
+void DracoOptions<AttributeKeyT>::SetAttributeFloat(
+ const AttributeKeyT &att_key, const std::string &name, float val) {
+ GetAttributeOptions(att_key)->SetFloat(name, val);
+}
+
+template <typename AttributeKeyT>
+bool DracoOptions<AttributeKeyT>::GetAttributeBool(const AttributeKeyT &att_key,
+ const std::string &name,
+ bool default_val) const {
+ const Options *const att_options = FindAttributeOptions(att_key);
+ if (att_options && att_options->IsOptionSet(name))
+ return att_options->GetBool(name, default_val);
+ return global_options_.GetBool(name, default_val);
+}
+
+template <typename AttributeKeyT>
+void DracoOptions<AttributeKeyT>::SetAttributeBool(const AttributeKeyT &att_key,
+ const std::string &name,
+ bool val) {
+ GetAttributeOptions(att_key)->SetBool(name, val);
+}
+
+template <typename AttributeKeyT>
+template <typename DataTypeT>
+bool DracoOptions<AttributeKeyT>::GetAttributeVector(
+ const AttributeKey &att_key, const std::string &name, int num_dims,
+ DataTypeT *val) const {
+ const Options *const att_options = FindAttributeOptions(att_key);
+ if (att_options && att_options->IsOptionSet(name))
+ return att_options->GetVector(name, num_dims, val);
+ return global_options_.GetVector(name, num_dims, val);
+}
+
+template <typename AttributeKeyT>
+template <typename DataTypeT>
+void DracoOptions<AttributeKeyT>::SetAttributeVector(
+ const AttributeKey &att_key, const std::string &name, int num_dims,
+ const DataTypeT *val) {
+ GetAttributeOptions(att_key)->SetVector(name, val, num_dims);
+}
+
+template <typename AttributeKeyT>
+bool DracoOptions<AttributeKeyT>::IsAttributeOptionSet(
+ const AttributeKey &att_key, const std::string &name) const {
+ const Options *const att_options = FindAttributeOptions(att_key);
+ if (att_options)
+ return att_options->IsOptionSet(name);
+ return global_options_.IsOptionSet(name);
+}
+
+template <typename AttributeKeyT>
+void DracoOptions<AttributeKeyT>::SetAttributeOptions(
+ const AttributeKey &att_key, const Options &options) {
+ Options *att_options = GetAttributeOptions(att_key);
+ *att_options = options;
+}
+
+} // namespace draco
+
+#endif // DRACO_SRC_DRACO_COMPRESSION_CONFIG_DRACO_OPTIONS_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/config/encoder_options.h b/extern/draco/dracoenc/src/draco/compression/config/encoder_options.h
new file mode 100644
index 00000000000..aacd58e0187
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/config/encoder_options.h
@@ -0,0 +1,97 @@
+// 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_COMPRESSION_CONFIG_ENCODER_OPTIONS_H_
+#define DRACO_COMPRESSION_CONFIG_ENCODER_OPTIONS_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/attributes/geometry_attribute.h"
+#include "draco/compression/config/draco_options.h"
+#include "draco/compression/config/encoding_features.h"
+
+namespace draco {
+
+// EncoderOptions allow users to specify so called feature options that are used
+// to inform the encoder which encoding features can be used (i.e. which
+// features are going to be available to the decoder).
+template <typename AttributeKeyT>
+class EncoderOptionsBase : public DracoOptions<AttributeKeyT> {
+ public:
+ static EncoderOptionsBase CreateDefaultOptions() {
+ EncoderOptionsBase options;
+#ifdef DRACO_STANDARD_EDGEBREAKER_SUPPORTED
+ options.SetSupportedFeature(features::kEdgebreaker, true);
+#endif
+#ifdef DRACO_PREDICTIVE_EDGEBREAKER_SUPPORTED
+ options.SetSupportedFeature(features::kPredictiveEdgebreaker, true);
+#endif
+ return options;
+ }
+ static EncoderOptionsBase CreateEmptyOptions() {
+ return EncoderOptionsBase();
+ }
+
+ // Returns speed options with default value of 5.
+ int GetEncodingSpeed() const {
+ return this->GetGlobalInt("encoding_speed", 5);
+ }
+ int GetDecodingSpeed() const {
+ return this->GetGlobalInt("decoding_speed", 5);
+ }
+
+ // Returns the maximum speed for both encoding/decoding.
+ int GetSpeed() const {
+ const int encoding_speed = this->GetGlobalInt("encoding_speed", -1);
+ const int decoding_speed = this->GetGlobalInt("decoding_speed", -1);
+ const int max_speed = std::max(encoding_speed, decoding_speed);
+ if (max_speed == -1)
+ return 5; // Default value.
+ return max_speed;
+ }
+
+ void SetSpeed(int encoding_speed, int decoding_speed) {
+ this->SetGlobalInt("encoding_speed", encoding_speed);
+ this->SetGlobalInt("decoding_speed", decoding_speed);
+ }
+
+ // Sets a given feature as supported or unsupported by the target decoder.
+ // Encoder will always use only supported features when encoding the input
+ // geometry.
+ void SetSupportedFeature(const std::string &name, bool supported) {
+ feature_options_.SetBool(name, supported);
+ }
+ bool IsFeatureSupported(const std::string &name) const {
+ return feature_options_.GetBool(name);
+ }
+
+ void SetFeatureOptions(const Options &options) { feature_options_ = options; }
+ const Options &GetFeaturelOptions() const { return feature_options_; }
+
+ private:
+ // Use helper methods to construct the encoder options.
+ // See CreateDefaultOptions();
+ EncoderOptionsBase() {}
+
+ // List of supported/unsupported features that can be used by the encoder.
+ Options feature_options_;
+};
+
+// Encoder options where attributes are identified by their attribute id.
+// Used to set options that are specific to a given geometry.
+typedef EncoderOptionsBase<int32_t> EncoderOptions;
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_CONFIG_ENCODER_OPTIONS_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/config/encoding_features.h b/extern/draco/dracoenc/src/draco/compression/config/encoding_features.h
new file mode 100644
index 00000000000..d6a8b7128a8
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/config/encoding_features.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.
+//
+// File provides helpful macros that define features available for encoding
+// the input of the input geometry. These macros can be used as an input in
+// the EncoderOptions::SetSupportedFeature() method instead of the text.
+// The most recent set of features supported
+// by the default implementation is:
+//
+// kEdgebreaker
+// - edgebreaker method for encoding meshes.
+// kPredictiveEdgebreaker
+// - advanced version of the edgebreaker method (slower but better
+// compression).
+//
+#ifndef DRACO_COMPRESSION_CONFIG_ENCODING_FEATURES_H_
+#define DRACO_COMPRESSION_CONFIG_ENCODING_FEATURES_H_
+
+namespace draco {
+namespace features {
+
+constexpr const char *kEdgebreaker = "standard_edgebreaker";
+constexpr const char *kPredictiveEdgebreaker = "predictive_edgebreaker";
+
+} // namespace features
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_CONFIG_ENCODING_FEATURES_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/decode.cc b/extern/draco/dracoenc/src/draco/compression/decode.cc
new file mode 100644
index 00000000000..a9ac2d5025f
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/decode.cc
@@ -0,0 +1,132 @@
+// 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 "draco/compression/decode.h"
+
+#include "draco/compression/config/compression_shared.h"
+
+#ifdef DRACO_MESH_COMPRESSION_SUPPORTED
+#include "draco/compression/mesh/mesh_edgebreaker_decoder.h"
+#include "draco/compression/mesh/mesh_sequential_decoder.h"
+#endif
+
+#ifdef DRACO_POINT_CLOUD_COMPRESSION_SUPPORTED
+#include "draco/compression/point_cloud/point_cloud_kd_tree_decoder.h"
+#include "draco/compression/point_cloud/point_cloud_sequential_decoder.h"
+#endif
+
+namespace draco {
+
+#ifdef DRACO_POINT_CLOUD_COMPRESSION_SUPPORTED
+StatusOr<std::unique_ptr<PointCloudDecoder>> CreatePointCloudDecoder(
+ int8_t method) {
+ if (method == POINT_CLOUD_SEQUENTIAL_ENCODING) {
+ return std::unique_ptr<PointCloudDecoder>(
+ new PointCloudSequentialDecoder());
+ } else if (method == POINT_CLOUD_KD_TREE_ENCODING) {
+ return std::unique_ptr<PointCloudDecoder>(new PointCloudKdTreeDecoder());
+ }
+ return Status(Status::ERROR, "Unsupported encoding method.");
+}
+#endif
+
+#ifdef DRACO_MESH_COMPRESSION_SUPPORTED
+StatusOr<std::unique_ptr<MeshDecoder>> CreateMeshDecoder(uint8_t method) {
+ if (method == MESH_SEQUENTIAL_ENCODING) {
+ return std::unique_ptr<MeshDecoder>(new MeshSequentialDecoder());
+ } else if (method == MESH_EDGEBREAKER_ENCODING) {
+ return std::unique_ptr<MeshDecoder>(new MeshEdgebreakerDecoder());
+ }
+ return Status(Status::ERROR, "Unsupported encoding method.");
+}
+#endif
+
+StatusOr<EncodedGeometryType> Decoder::GetEncodedGeometryType(
+ DecoderBuffer *in_buffer) {
+ DecoderBuffer temp_buffer(*in_buffer);
+ DracoHeader header;
+ DRACO_RETURN_IF_ERROR(PointCloudDecoder::DecodeHeader(&temp_buffer, &header))
+ return static_cast<EncodedGeometryType>(header.encoder_type);
+}
+
+StatusOr<std::unique_ptr<PointCloud>> Decoder::DecodePointCloudFromBuffer(
+ DecoderBuffer *in_buffer) {
+ DRACO_ASSIGN_OR_RETURN(EncodedGeometryType type,
+ GetEncodedGeometryType(in_buffer))
+ if (type == POINT_CLOUD) {
+#ifdef DRACO_POINT_CLOUD_COMPRESSION_SUPPORTED
+ std::unique_ptr<PointCloud> point_cloud(new PointCloud());
+ DRACO_RETURN_IF_ERROR(DecodeBufferToGeometry(in_buffer, point_cloud.get()))
+ return std::move(point_cloud);
+#endif
+ } else if (type == TRIANGULAR_MESH) {
+#ifdef DRACO_MESH_COMPRESSION_SUPPORTED
+ std::unique_ptr<Mesh> mesh(new Mesh());
+ DRACO_RETURN_IF_ERROR(DecodeBufferToGeometry(in_buffer, mesh.get()))
+ return static_cast<std::unique_ptr<PointCloud>>(std::move(mesh));
+#endif
+ }
+ return Status(Status::ERROR, "Unsupported geometry type.");
+}
+
+StatusOr<std::unique_ptr<Mesh>> Decoder::DecodeMeshFromBuffer(
+ DecoderBuffer *in_buffer) {
+ std::unique_ptr<Mesh> mesh(new Mesh());
+ DRACO_RETURN_IF_ERROR(DecodeBufferToGeometry(in_buffer, mesh.get()))
+ return std::move(mesh);
+}
+
+Status Decoder::DecodeBufferToGeometry(DecoderBuffer *in_buffer,
+ PointCloud *out_geometry) {
+#ifdef DRACO_POINT_CLOUD_COMPRESSION_SUPPORTED
+ DecoderBuffer temp_buffer(*in_buffer);
+ DracoHeader header;
+ DRACO_RETURN_IF_ERROR(PointCloudDecoder::DecodeHeader(&temp_buffer, &header))
+ if (header.encoder_type != POINT_CLOUD) {
+ return Status(Status::ERROR, "Input is not a point cloud.");
+ }
+ DRACO_ASSIGN_OR_RETURN(std::unique_ptr<PointCloudDecoder> decoder,
+ CreatePointCloudDecoder(header.encoder_method))
+
+ DRACO_RETURN_IF_ERROR(decoder->Decode(options_, in_buffer, out_geometry))
+ return OkStatus();
+#else
+ return Status(Status::ERROR, "Unsupported geometry type.");
+#endif
+}
+
+Status Decoder::DecodeBufferToGeometry(DecoderBuffer *in_buffer,
+ Mesh *out_geometry) {
+#ifdef DRACO_MESH_COMPRESSION_SUPPORTED
+ DecoderBuffer temp_buffer(*in_buffer);
+ DracoHeader header;
+ DRACO_RETURN_IF_ERROR(PointCloudDecoder::DecodeHeader(&temp_buffer, &header))
+ if (header.encoder_type != TRIANGULAR_MESH) {
+ return Status(Status::ERROR, "Input is not a mesh.");
+ }
+ DRACO_ASSIGN_OR_RETURN(std::unique_ptr<MeshDecoder> decoder,
+ CreateMeshDecoder(header.encoder_method))
+
+ DRACO_RETURN_IF_ERROR(decoder->Decode(options_, in_buffer, out_geometry))
+ return OkStatus();
+#else
+ return Status(Status::ERROR, "Unsupported geometry type.");
+#endif
+}
+
+void Decoder::SetSkipAttributeTransform(GeometryAttribute::Type att_type) {
+ options_.SetAttributeBool(att_type, "skip_attribute_transform", true);
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/decode.h b/extern/draco/dracoenc/src/draco/compression/decode.h
new file mode 100644
index 00000000000..9e24ce8d5d6
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/decode.h
@@ -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.
+//
+#ifndef DRACO_COMPRESSION_DECODE_H_
+#define DRACO_COMPRESSION_DECODE_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/config/compression_shared.h"
+#include "draco/compression/config/decoder_options.h"
+#include "draco/core/decoder_buffer.h"
+#include "draco/core/statusor.h"
+#include "draco/mesh/mesh.h"
+
+namespace draco {
+
+// Class responsible for decoding of meshes and point clouds that were
+// compressed by a Draco encoder.
+class Decoder {
+ public:
+ // Returns the geometry type encoded in the input |in_buffer|.
+ // The return value is one of POINT_CLOUD, MESH or INVALID_GEOMETRY in case
+ // the input data is invalid.
+ // The decoded geometry type can be used to choose an appropriate decoding
+ // function for a given geometry type (see below).
+ static StatusOr<EncodedGeometryType> GetEncodedGeometryType(
+ DecoderBuffer *in_buffer);
+
+ // Decodes point cloud from the provided buffer. The buffer must be filled
+ // with data that was encoded with either the EncodePointCloudToBuffer or
+ // EncodeMeshToBuffer methods in encode.h. In case the input buffer contains
+ // mesh, the returned instance can be down-casted to Mesh.
+ StatusOr<std::unique_ptr<PointCloud>> DecodePointCloudFromBuffer(
+ DecoderBuffer *in_buffer);
+
+ // Decodes a triangular mesh from the provided buffer. The mesh must be filled
+ // with data that was encoded using the EncodeMeshToBuffer method in encode.h.
+ // The function will return nullptr in case the input is invalid or if it was
+ // encoded with the EncodePointCloudToBuffer method.
+ StatusOr<std::unique_ptr<Mesh>> DecodeMeshFromBuffer(
+ DecoderBuffer *in_buffer);
+
+ // Decodes the buffer into a provided geometry. If the geometry is
+ // incompatible with the encoded data. For example, when |out_geometry| is
+ // draco::Mesh while the data contains a point cloud, the function will return
+ // an error status.
+ Status DecodeBufferToGeometry(DecoderBuffer *in_buffer,
+ PointCloud *out_geometry);
+ Status DecodeBufferToGeometry(DecoderBuffer *in_buffer, Mesh *out_geometry);
+
+ // When set, the decoder is going to skip attribute transform for a given
+ // attribute type. For example for quantized attributes, the decoder would
+ // skip the dequantization step and the returned geometry would contain an
+ // attribute with quantized values. The attribute would also contain an
+ // instance of AttributeTransform class that is used to describe the skipped
+ // transform, including all parameters that are needed to perform the
+ // transform manually.
+ void SetSkipAttributeTransform(GeometryAttribute::Type att_type);
+
+ // Returns the options instance used by the decoder that can be used by users
+ // to control the decoding process.
+ DecoderOptions *options() { return &options_; }
+
+ private:
+ DecoderOptions options_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_DECODE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/decode_test.cc b/extern/draco/dracoenc/src/draco/compression/decode_test.cc
new file mode 100644
index 00000000000..c57542069dd
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/decode_test.cc
@@ -0,0 +1,196 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/compression/decode.h"
+
+#include <cinttypes>
+#include <fstream>
+#include <sstream>
+
+#include "draco/core/draco_test_base.h"
+#include "draco/core/draco_test_utils.h"
+
+namespace {
+
+class DecodeTest : public ::testing::Test {
+ protected:
+ DecodeTest() {}
+};
+
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+TEST_F(DecodeTest, TestSkipAttributeTransform) {
+ const std::string file_name = "test_nm_quant.0.9.0.drc";
+ // Tests that decoders can successfully skip attribute transform.
+ std::ifstream input_file(draco::GetTestFileFullPath(file_name),
+ std::ios::binary);
+ ASSERT_TRUE(input_file);
+
+ // Read the file stream into a buffer.
+ std::streampos file_size = 0;
+ input_file.seekg(0, std::ios::end);
+ file_size = input_file.tellg() - file_size;
+ input_file.seekg(0, std::ios::beg);
+ std::vector<char> data(file_size);
+ input_file.read(data.data(), file_size);
+
+ ASSERT_FALSE(data.empty());
+
+ // Create a draco decoding buffer. Note that no data is copied in this step.
+ draco::DecoderBuffer buffer;
+ buffer.Init(data.data(), data.size());
+
+ draco::Decoder decoder;
+ // Make sure we skip dequantization for the position attribute.
+ decoder.SetSkipAttributeTransform(draco::GeometryAttribute::POSITION);
+
+ // Decode the input data into a geometry.
+ std::unique_ptr<draco::PointCloud> pc =
+ decoder.DecodePointCloudFromBuffer(&buffer).value();
+ ASSERT_NE(pc, nullptr);
+
+ const draco::PointAttribute *const pos_att =
+ pc->GetNamedAttribute(draco::GeometryAttribute::POSITION);
+ ASSERT_NE(pos_att, nullptr);
+
+ // Ensure the position attribute is of type int32_t and that it has a valid
+ // attribute transform.
+ ASSERT_EQ(pos_att->data_type(), draco::DT_INT32);
+ ASSERT_NE(pos_att->GetAttributeTransformData(), nullptr);
+
+ // Normal attribute should be left transformed.
+ const draco::PointAttribute *const norm_att =
+ pc->GetNamedAttribute(draco::GeometryAttribute::NORMAL);
+ ASSERT_EQ(norm_att->data_type(), draco::DT_FLOAT32);
+ ASSERT_EQ(norm_att->GetAttributeTransformData(), nullptr);
+}
+#endif
+
+void TestSkipAttributeTransformOnPointCloudWithColor(const std::string &file) {
+ std::ifstream input_file(draco::GetTestFileFullPath(file), std::ios::binary);
+ ASSERT_TRUE(input_file);
+
+ // Read the file stream into a buffer.
+ std::streampos file_size = 0;
+ input_file.seekg(0, std::ios::end);
+ file_size = input_file.tellg() - file_size;
+ input_file.seekg(0, std::ios::beg);
+ std::vector<char> data(file_size);
+ input_file.read(data.data(), file_size);
+
+ ASSERT_FALSE(data.empty());
+
+ // Create a draco decoding buffer. Note that no data is copied in this step.
+ draco::DecoderBuffer buffer;
+ buffer.Init(data.data(), data.size());
+
+ draco::Decoder decoder;
+ // Make sure we skip dequantization for the position attribute.
+ decoder.SetSkipAttributeTransform(draco::GeometryAttribute::POSITION);
+
+ // Decode the input data into a geometry.
+ std::unique_ptr<draco::PointCloud> pc =
+ decoder.DecodePointCloudFromBuffer(&buffer).value();
+ ASSERT_NE(pc, nullptr);
+
+ const draco::PointAttribute *const pos_att =
+ pc->GetNamedAttribute(draco::GeometryAttribute::POSITION);
+ ASSERT_NE(pos_att, nullptr);
+
+ // Ensure the position attribute is of type int32_t or uint32_t and that it
+ // has a valid attribute transform.
+ ASSERT_TRUE(pos_att->data_type() == draco::DT_INT32 ||
+ pos_att->data_type() == draco::DT_UINT32);
+ ASSERT_NE(pos_att->GetAttributeTransformData(), nullptr);
+
+ const draco::PointAttribute *const clr_att =
+ pc->GetNamedAttribute(draco::GeometryAttribute::COLOR);
+ ASSERT_EQ(clr_att->data_type(), draco::DT_UINT8);
+
+ // Ensure the color attribute was decoded correctly. Perform the decoding
+ // again without skipping the position dequantization and compare the
+ // attribute values.
+
+ draco::DecoderBuffer buffer_2;
+ buffer_2.Init(data.data(), data.size());
+
+ draco::Decoder decoder_2;
+
+ // Decode the input data into a geometry.
+ std::unique_ptr<draco::PointCloud> pc_2 =
+ decoder_2.DecodePointCloudFromBuffer(&buffer_2).value();
+ ASSERT_NE(pc_2, nullptr);
+
+ const draco::PointAttribute *const clr_att_2 =
+ pc_2->GetNamedAttribute(draco::GeometryAttribute::COLOR);
+ ASSERT_NE(clr_att_2, nullptr);
+ for (draco::PointIndex pi(0); pi < pc_2->num_points(); ++pi) {
+ // Colors should be exactly the same for both cases.
+ ASSERT_EQ(std::memcmp(clr_att->GetAddress(clr_att->mapped_index(pi)),
+ clr_att_2->GetAddress(clr_att_2->mapped_index(pi)),
+ clr_att->byte_stride()),
+ 0);
+ }
+}
+
+TEST_F(DecodeTest, TestSkipAttributeTransformOnPointCloud) {
+ // Tests that decoders can successfully skip attribute transform on a point
+ // cloud with multiple attributes encoded with one attributes encoder.
+ TestSkipAttributeTransformOnPointCloudWithColor("pc_color.drc");
+ TestSkipAttributeTransformOnPointCloudWithColor("pc_kd_color.drc");
+}
+
+TEST_F(DecodeTest, TestSkipAttributeTransformWithNoQuantization) {
+ // Tests that decoders can successfully skip attribute transform even though
+ // the input model was not quantized (it has no attribute transform).
+ const std::string file_name = "point_cloud_no_qp.drc";
+ std::ifstream input_file(draco::GetTestFileFullPath(file_name),
+ std::ios::binary);
+ ASSERT_TRUE(input_file);
+
+ // Read the file stream into a buffer.
+ std::streampos file_size = 0;
+ input_file.seekg(0, std::ios::end);
+ file_size = input_file.tellg() - file_size;
+ input_file.seekg(0, std::ios::beg);
+ std::vector<char> data(file_size);
+ input_file.read(data.data(), file_size);
+
+ ASSERT_FALSE(data.empty());
+
+ // Create a draco decoding buffer. Note that no data is copied in this step.
+ draco::DecoderBuffer buffer;
+ buffer.Init(data.data(), data.size());
+
+ draco::Decoder decoder;
+ // Make sure we skip dequantization for the position attribute.
+ decoder.SetSkipAttributeTransform(draco::GeometryAttribute::POSITION);
+
+ // Decode the input data into a geometry.
+ std::unique_ptr<draco::PointCloud> pc =
+ decoder.DecodePointCloudFromBuffer(&buffer).value();
+ ASSERT_NE(pc, nullptr);
+
+ const draco::PointAttribute *const pos_att =
+ pc->GetNamedAttribute(draco::GeometryAttribute::POSITION);
+ ASSERT_NE(pos_att, nullptr);
+
+ // Ensure the position attribute is of type float32 since the attribute was
+ // not quantized.
+ ASSERT_EQ(pos_att->data_type(), draco::DT_FLOAT32);
+
+ // Make sure there is no attribute transform available for the attribute.
+ ASSERT_EQ(pos_att->GetAttributeTransformData(), nullptr);
+}
+
+} // namespace
diff --git a/extern/draco/dracoenc/src/draco/compression/encode.cc b/extern/draco/dracoenc/src/draco/compression/encode.cc
new file mode 100644
index 00000000000..5de05ace81f
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/encode.cc
@@ -0,0 +1,95 @@
+// 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 "draco/compression/encode.h"
+
+#include "draco/compression/expert_encode.h"
+
+namespace draco {
+
+Encoder::Encoder() {}
+
+Status Encoder::EncodePointCloudToBuffer(const PointCloud &pc,
+ EncoderBuffer *out_buffer) {
+ ExpertEncoder encoder(pc);
+ encoder.Reset(CreateExpertEncoderOptions(pc));
+ return encoder.EncodeToBuffer(out_buffer);
+}
+
+Status Encoder::EncodeMeshToBuffer(const Mesh &m, EncoderBuffer *out_buffer) {
+ ExpertEncoder encoder(m);
+ encoder.Reset(CreateExpertEncoderOptions(m));
+ DRACO_RETURN_IF_ERROR(encoder.EncodeToBuffer(out_buffer));
+ set_num_encoded_points(encoder.num_encoded_points());
+ set_num_encoded_faces(encoder.num_encoded_faces());
+ return OkStatus();
+}
+
+EncoderOptions Encoder::CreateExpertEncoderOptions(const PointCloud &pc) const {
+ EncoderOptions ret_options = EncoderOptions::CreateEmptyOptions();
+ ret_options.SetGlobalOptions(options().GetGlobalOptions());
+ ret_options.SetFeatureOptions(options().GetFeaturelOptions());
+ // Convert type-based attribute options to specific attributes in the provided
+ // point cloud.
+ for (int i = 0; i < pc.num_attributes(); ++i) {
+ const Options *att_options =
+ options().FindAttributeOptions(pc.attribute(i)->attribute_type());
+ if (att_options) {
+ ret_options.SetAttributeOptions(i, *att_options);
+ }
+ }
+ return ret_options;
+}
+
+void Encoder::Reset(
+ const EncoderOptionsBase<GeometryAttribute::Type> &options) {
+ Base::Reset(options);
+}
+
+void Encoder::Reset() { Base::Reset(); }
+
+void Encoder::SetSpeedOptions(int encoding_speed, int decoding_speed) {
+ Base::SetSpeedOptions(encoding_speed, decoding_speed);
+}
+
+void Encoder::SetAttributeQuantization(GeometryAttribute::Type type,
+ int quantization_bits) {
+ options().SetAttributeInt(type, "quantization_bits", quantization_bits);
+}
+
+void Encoder::SetAttributeExplicitQuantization(GeometryAttribute::Type type,
+ int quantization_bits,
+ int num_dims,
+ const float *origin,
+ float range) {
+ options().SetAttributeInt(type, "quantization_bits", quantization_bits);
+ options().SetAttributeVector(type, "quantization_origin", num_dims, origin);
+ options().SetAttributeFloat(type, "quantization_range", range);
+}
+
+void Encoder::SetEncodingMethod(int encoding_method) {
+ Base::SetEncodingMethod(encoding_method);
+}
+
+Status Encoder::SetAttributePredictionScheme(GeometryAttribute::Type type,
+ int prediction_scheme_method) {
+ Status status = CheckPredictionScheme(type, prediction_scheme_method);
+ if (!status.ok())
+ return status;
+ options().SetAttributeInt(type, "prediction_scheme",
+ prediction_scheme_method);
+ return status;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/encode.h b/extern/draco/dracoenc/src/draco/compression/encode.h
new file mode 100644
index 00000000000..bce8b34c238
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/encode.h
@@ -0,0 +1,140 @@
+// 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_COMPRESSION_ENCODE_H_
+#define DRACO_COMPRESSION_ENCODE_H_
+
+#include "draco/compression/config/compression_shared.h"
+#include "draco/compression/config/encoder_options.h"
+#include "draco/compression/encode_base.h"
+#include "draco/core/encoder_buffer.h"
+#include "draco/core/status.h"
+#include "draco/mesh/mesh.h"
+
+namespace draco {
+
+// Basic helper class for encoding geometry using the Draco compression library.
+// The class provides various methods that can be used to control several common
+// options used during the encoding, such as the number of quantization bits for
+// a given attribute. All these options are defined per attribute type, i.e.,
+// if there are more attributes of the same type (such as multiple texture
+// coordinate attributes), the same options are going to be used for all of the
+// attributes of this type. If different attributes of the same type need to
+// use different options, use ExpertEncoder in expert_encode.h.
+class Encoder
+ : public EncoderBase<EncoderOptionsBase<GeometryAttribute::Type>> {
+ public:
+ typedef EncoderBase<EncoderOptionsBase<GeometryAttribute::Type>> Base;
+
+ Encoder();
+ virtual ~Encoder() {}
+
+ // Encodes a point cloud to the provided buffer.
+ virtual Status EncodePointCloudToBuffer(const PointCloud &pc,
+ EncoderBuffer *out_buffer);
+
+ // Encodes a mesh to the provided buffer.
+ virtual Status EncodeMeshToBuffer(const Mesh &m, EncoderBuffer *out_buffer);
+
+ // Set encoder options used during the geometry encoding. Note that this call
+ // overwrites any modifications to the options done with the functions below,
+ // i.e., it resets the encoder.
+ void Reset(const EncoderOptionsBase<GeometryAttribute::Type> &options);
+ void Reset();
+
+ // Sets the desired encoding and decoding speed for the given options.
+ //
+ // 0 = slowest speed, but the best compression.
+ // 10 = fastest, but the worst compression.
+ // -1 = undefined.
+ //
+ // Note that both speed options affect the encoder choice of used methods and
+ // algorithms. For example, a requirement for fast decoding may prevent the
+ // encoder from using the best compression methods even if the encoding speed
+ // is set to 0. In general, the faster of the two options limits the choice of
+ // features that can be used by the encoder. Additionally, setting
+ // |decoding_speed| to be faster than the |encoding_speed| may allow the
+ // encoder to choose the optimal method out of the available features for the
+ // given |decoding_speed|.
+ void SetSpeedOptions(int encoding_speed, int decoding_speed);
+
+ // Sets the quantization compression options for a named attribute. The
+ // attribute values will be quantized in a box defined by the maximum extent
+ // of the attribute values. I.e., the actual precision of this option depends
+ // on the scale of the attribute values.
+ void SetAttributeQuantization(GeometryAttribute::Type type,
+ int quantization_bits);
+
+ // Sets the explicit quantization compression for a named attribute. The
+ // attribute values will be quantized in a coordinate system defined by the
+ // provided origin and range (the input values should be within interval:
+ // <origin, origin + range>).
+ void SetAttributeExplicitQuantization(GeometryAttribute::Type type,
+ int quantization_bits, int num_dims,
+ const float *origin, float range);
+
+ // 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). This function should be called only when a specific prediction is
+ // preferred (e.g., when it is known that the encoder would select a less
+ // optimal prediction for the given input data).
+ //
+ // |prediction_scheme_method| should be one of the entries defined in
+ // compression/config/compression_shared.h :
+ //
+ // PREDICTION_NONE - use no prediction.
+ // PREDICTION_DIFFERENCE - delta coding
+ // MESH_PREDICTION_PARALLELOGRAM - parallelogram prediction for meshes.
+ // MESH_PREDICTION_CONSTRAINED_PARALLELOGRAM
+ // - better and more costly version of the parallelogram prediction.
+ // MESH_PREDICTION_TEX_COORDS_PORTABLE
+ // - specialized predictor for tex coordinates.
+ // MESH_PREDICTION_GEOMETRIC_NORMAL
+ // - specialized predictor for normal coordinates.
+ //
+ // Note that in case the desired prediction cannot be used, the default
+ // prediction will be automatically used instead.
+ Status SetAttributePredictionScheme(GeometryAttribute::Type type,
+ int prediction_scheme_method);
+
+ // 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 values defined in
+ // compression/config/compression_shared.h based on the type of the input
+ // geometry that is going to be encoded. For point clouds, allowed entries are
+ // POINT_CLOUD_SEQUENTIAL_ENCODING
+ // POINT_CLOUD_KD_TREE_ENCODING
+ //
+ // For meshes the input can be
+ // 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(int encoding_method);
+
+ protected:
+ // Creates encoder options for the expert encoder used during the actual
+ // encoding.
+ EncoderOptions CreateExpertEncoderOptions(const PointCloud &pc) const;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ENCODE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/encode_base.h b/extern/draco/dracoenc/src/draco/compression/encode_base.h
new file mode 100644
index 00000000000..fa263f8fa3d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/encode_base.h
@@ -0,0 +1,121 @@
+// Copyright 2017 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_SRC_DRACO_COMPRESSION_ENCODE_BASE_H_
+#define DRACO_SRC_DRACO_COMPRESSION_ENCODE_BASE_H_
+
+#include "draco/attributes/geometry_attribute.h"
+#include "draco/compression/config/compression_shared.h"
+#include "draco/core/status.h"
+
+namespace draco {
+
+// Base class for our geometry encoder classes. |EncoderOptionsT| specifies
+// options class used by the encoder. Please, see encode.h and expert_encode.h
+// for more details and method descriptions.
+template <class EncoderOptionsT>
+class EncoderBase {
+ public:
+ typedef EncoderOptionsT OptionsType;
+
+ EncoderBase()
+ : options_(EncoderOptionsT::CreateDefaultOptions()),
+ num_encoded_points_(0),
+ num_encoded_faces_(0) {}
+ virtual ~EncoderBase() {}
+
+ const EncoderOptionsT &options() const { return options_; }
+ EncoderOptionsT &options() { return options_; }
+
+ // If enabled, it tells the encoder to keep track of the number of encoded
+ // points and faces (default = false).
+ // Note that this can slow down encoding for certain encoders.
+ void SetTrackEncodedProperties(bool flag);
+
+ // Returns the number of encoded points and faces during the last encoding
+ // operation. Returns 0 if SetTrackEncodedProperties() was not set.
+ size_t num_encoded_points() const { return num_encoded_points_; }
+ size_t num_encoded_faces() const { return num_encoded_faces_; }
+
+ protected:
+ void Reset(const EncoderOptionsT &options) { options_ = options; }
+
+ void Reset() { options_ = EncoderOptionsT::CreateDefaultOptions(); }
+
+ void SetSpeedOptions(int encoding_speed, int decoding_speed) {
+ options_.SetSpeed(encoding_speed, decoding_speed);
+ }
+
+ void SetEncodingMethod(int encoding_method) {
+ options_.SetGlobalInt("encoding_method", encoding_method);
+ }
+
+ void SetEncodingSubmethod(int encoding_submethod) {
+ options_.SetGlobalInt("encoding_submethod", encoding_submethod);
+ }
+
+ Status CheckPredictionScheme(GeometryAttribute::Type att_type,
+ int prediction_scheme) const {
+ // Out of bound checks:
+ if (prediction_scheme < 0)
+ return Status(Status::ERROR, "Invalid prediction scheme requested.");
+ if (prediction_scheme >= NUM_PREDICTION_SCHEMES)
+ return Status(Status::ERROR, "Invalid prediction scheme requested.");
+ // Deprecated prediction schemes:
+ if (prediction_scheme == MESH_PREDICTION_TEX_COORDS_DEPRECATED)
+ return Status(Status::ERROR,
+ "MESH_PREDICTION_TEX_COORDS_DEPRECATED is deprecated.");
+ // Attribute specific checks:
+ if (prediction_scheme == MESH_PREDICTION_TEX_COORDS_PORTABLE) {
+ if (att_type != GeometryAttribute::TEX_COORD)
+ return Status(Status::ERROR,
+ "Invalid prediction scheme for attribute type.");
+ }
+ if (prediction_scheme == MESH_PREDICTION_GEOMETRIC_NORMAL) {
+ if (att_type != GeometryAttribute::NORMAL) {
+ return Status(Status::ERROR,
+ "Invalid prediction scheme for attribute type.");
+ }
+ }
+ // TODO(hemmer): Try to enable more prediction schemes for normals.
+ if (att_type == GeometryAttribute::NORMAL) {
+ if (!(prediction_scheme == PREDICTION_DIFFERENCE ||
+ prediction_scheme == MESH_PREDICTION_GEOMETRIC_NORMAL)) {
+ return Status(Status::ERROR,
+ "Invalid prediction scheme for attribute type.");
+ }
+ }
+ return OkStatus();
+ }
+
+ protected:
+ void set_num_encoded_points(size_t num) { num_encoded_points_ = num; }
+ void set_num_encoded_faces(size_t num) { num_encoded_faces_ = num; }
+
+ private:
+ EncoderOptionsT options_;
+
+ size_t num_encoded_points_;
+ size_t num_encoded_faces_;
+};
+
+template <class EncoderOptionsT>
+void EncoderBase<EncoderOptionsT>::SetTrackEncodedProperties(bool flag) {
+ options_.SetGlobalBool("store_number_of_encoded_points", flag);
+ options_.SetGlobalBool("store_number_of_encoded_faces", flag);
+}
+
+} // namespace draco
+
+#endif // DRACO_SRC_DRACO_COMPRESSION_ENCODE_BASE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/encode_test.cc b/extern/draco/dracoenc/src/draco/compression/encode_test.cc
new file mode 100644
index 00000000000..2eadb73b343
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/encode_test.cc
@@ -0,0 +1,293 @@
+// Copyright 2017 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 <cinttypes>
+#include <fstream>
+#include <sstream>
+
+#include "draco/attributes/attribute_quantization_transform.h"
+#include "draco/compression/decode.h"
+#include "draco/compression/encode.h"
+#include "draco/compression/expert_encode.h"
+#include "draco/core/draco_test_base.h"
+#include "draco/core/draco_test_utils.h"
+#include "draco/core/vector_d.h"
+#include "draco/io/obj_decoder.h"
+#include "draco/mesh/triangle_soup_mesh_builder.h"
+#include "draco/point_cloud/point_cloud_builder.h"
+
+namespace {
+
+class EncodeTest : public ::testing::Test {
+ protected:
+ EncodeTest() {}
+ std::unique_ptr<draco::Mesh> CreateTestMesh() const {
+ draco::TriangleSoupMeshBuilder mesh_builder;
+
+ // Create a simple mesh with one face.
+ mesh_builder.Start(1);
+
+ // Add one position attribute and two texture coordinate attributes.
+ const int32_t pos_att_id = mesh_builder.AddAttribute(
+ draco::GeometryAttribute::POSITION, 3, draco::DT_FLOAT32);
+ const int32_t tex_att_id_0 = mesh_builder.AddAttribute(
+ draco::GeometryAttribute::TEX_COORD, 2, draco::DT_FLOAT32);
+ const int32_t tex_att_id_1 = mesh_builder.AddAttribute(
+ draco::GeometryAttribute::TEX_COORD, 2, draco::DT_FLOAT32);
+
+ // Initialize the attribute values.
+ mesh_builder.SetAttributeValuesForFace(
+ pos_att_id, draco::FaceIndex(0), draco::Vector3f(0.f, 0.f, 0.f).data(),
+ draco::Vector3f(1.f, 0.f, 0.f).data(),
+ draco::Vector3f(1.f, 1.f, 0.f).data());
+ mesh_builder.SetAttributeValuesForFace(
+ tex_att_id_0, draco::FaceIndex(0), draco::Vector2f(0.f, 0.f).data(),
+ draco::Vector2f(1.f, 0.f).data(), draco::Vector2f(1.f, 1.f).data());
+ mesh_builder.SetAttributeValuesForFace(
+ tex_att_id_1, draco::FaceIndex(0), draco::Vector2f(0.f, 0.f).data(),
+ draco::Vector2f(1.f, 0.f).data(), draco::Vector2f(1.f, 1.f).data());
+
+ return mesh_builder.Finalize();
+ }
+
+ std::unique_ptr<draco::PointCloud> CreateTestPointCloud() const {
+ draco::PointCloudBuilder pc_builder;
+
+ constexpr int kNumPoints = 100;
+ constexpr int kNumGenAttCoords0 = 4;
+ constexpr int kNumGenAttCoords1 = 6;
+ pc_builder.Start(kNumPoints);
+
+ // Add one position attribute and two generic attributes.
+ const int32_t pos_att_id = pc_builder.AddAttribute(
+ draco::GeometryAttribute::POSITION, 3, draco::DT_FLOAT32);
+ const int32_t gen_att_id_0 = pc_builder.AddAttribute(
+ draco::GeometryAttribute::GENERIC, kNumGenAttCoords0, draco::DT_UINT32);
+ const int32_t gen_att_id_1 = pc_builder.AddAttribute(
+ draco::GeometryAttribute::GENERIC, kNumGenAttCoords1, draco::DT_UINT8);
+
+ std::vector<uint32_t> gen_att_data_0(kNumGenAttCoords0);
+ std::vector<uint32_t> gen_att_data_1(kNumGenAttCoords1);
+
+ // Initialize the attribute values.
+ for (draco::PointIndex i(0); i < kNumPoints; ++i) {
+ const float pos_coord = static_cast<float>(i.value());
+ pc_builder.SetAttributeValueForPoint(
+ pos_att_id, i,
+ draco::Vector3f(pos_coord, -pos_coord, pos_coord).data());
+
+ for (int j = 0; j < kNumGenAttCoords0; ++j) {
+ gen_att_data_0[j] = i.value();
+ }
+ pc_builder.SetAttributeValueForPoint(gen_att_id_0, i,
+ gen_att_data_0.data());
+
+ for (int j = 0; j < kNumGenAttCoords1; ++j) {
+ gen_att_data_1[j] = -i.value();
+ }
+ pc_builder.SetAttributeValueForPoint(gen_att_id_1, i,
+ gen_att_data_1.data());
+ }
+
+ return pc_builder.Finalize(false);
+ }
+
+ int GetQuantizationBitsFromAttribute(const draco::PointAttribute *att) const {
+ if (att == nullptr)
+ return -1;
+ draco::AttributeQuantizationTransform transform;
+ if (!transform.InitFromAttribute(*att))
+ return -1;
+ return transform.quantization_bits();
+ }
+
+ void VerifyNumQuantizationBits(const draco::EncoderBuffer &buffer,
+ int pos_quantization,
+ int tex_coord_0_quantization,
+ int tex_coord_1_quantization) const {
+ draco::Decoder decoder;
+
+ // Skip the dequantization for the attributes which will allow us to get
+ // the number of quantization bits used during encoding.
+ decoder.SetSkipAttributeTransform(draco::GeometryAttribute::POSITION);
+ decoder.SetSkipAttributeTransform(draco::GeometryAttribute::TEX_COORD);
+
+ draco::DecoderBuffer in_buffer;
+ in_buffer.Init(buffer.data(), buffer.size());
+ auto mesh = decoder.DecodeMeshFromBuffer(&in_buffer).value();
+ ASSERT_NE(mesh, nullptr);
+ ASSERT_EQ(GetQuantizationBitsFromAttribute(mesh->attribute(0)),
+ pos_quantization);
+ ASSERT_EQ(GetQuantizationBitsFromAttribute(mesh->attribute(1)),
+ tex_coord_0_quantization);
+ ASSERT_EQ(GetQuantizationBitsFromAttribute(mesh->attribute(2)),
+ tex_coord_1_quantization);
+ }
+
+ // Tests that the encoder returns the correct number of encoded points and
+ // faces for a given mesh or point cloud.
+ void TestNumberOfEncodedEntries(const std::string &file_name,
+ int32_t encoding_method) {
+ std::unique_ptr<draco::PointCloud> geometry;
+ draco::Mesh *mesh = nullptr;
+
+ if (encoding_method == draco::MESH_EDGEBREAKER_ENCODING ||
+ encoding_method == draco::MESH_SEQUENTIAL_ENCODING) {
+ std::unique_ptr<draco::Mesh> mesh_tmp =
+ draco::ReadMeshFromTestFile(file_name);
+ mesh = mesh_tmp.get();
+ if (!mesh->DeduplicateAttributeValues())
+ return;
+ mesh->DeduplicatePointIds();
+ geometry = std::move(mesh_tmp);
+ } else {
+ geometry = draco::ReadPointCloudFromTestFile(file_name);
+ }
+ ASSERT_NE(mesh, nullptr);
+
+ draco::Encoder encoder;
+ encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 14);
+ encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, 12);
+ encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, 10);
+
+ encoder.SetEncodingMethod(encoding_method);
+
+ encoder.SetTrackEncodedProperties(true);
+
+ draco::EncoderBuffer buffer;
+ if (mesh) {
+ encoder.EncodeMeshToBuffer(*mesh, &buffer);
+ } else {
+ encoder.EncodePointCloudToBuffer(*geometry, &buffer);
+ }
+
+ // Ensure the logged number of encoded points and faces matches the number
+ // we get from the decoder.
+
+ draco::DecoderBuffer decoder_buffer;
+ decoder_buffer.Init(buffer.data(), buffer.size());
+ draco::Decoder decoder;
+
+ if (mesh) {
+ auto maybe_mesh = decoder.DecodeMeshFromBuffer(&decoder_buffer);
+ ASSERT_TRUE(maybe_mesh.ok());
+ auto decoded_mesh = std::move(maybe_mesh).value();
+ ASSERT_NE(decoded_mesh, nullptr);
+ ASSERT_EQ(decoded_mesh->num_points(), encoder.num_encoded_points());
+ ASSERT_EQ(decoded_mesh->num_faces(), encoder.num_encoded_faces());
+ } else {
+ auto maybe_pc = decoder.DecodePointCloudFromBuffer(&decoder_buffer);
+ ASSERT_TRUE(maybe_pc.ok());
+ auto decoded_pc = std::move(maybe_pc).value();
+ ASSERT_EQ(decoded_pc->num_points(), encoder.num_encoded_points());
+ }
+ }
+};
+
+TEST_F(EncodeTest, TestExpertEncoderQuantization) {
+ // This test verifies that the expert encoder can quantize individual
+ // attributes even if they have the same type.
+ auto mesh = CreateTestMesh();
+ ASSERT_NE(mesh, nullptr);
+
+ draco::ExpertEncoder encoder(*mesh.get());
+ encoder.SetAttributeQuantization(0, 16); // Position quantization.
+ encoder.SetAttributeQuantization(1, 15); // Tex-coord 0 quantization.
+ encoder.SetAttributeQuantization(2, 14); // Tex-coord 1 quantization.
+
+ draco::EncoderBuffer buffer;
+ encoder.EncodeToBuffer(&buffer);
+ VerifyNumQuantizationBits(buffer, 16, 15, 14);
+}
+
+TEST_F(EncodeTest, TestEncoderQuantization) {
+ // This test verifies that Encoder applies the same quantization to all
+ // attributes of the same type.
+ auto mesh = CreateTestMesh();
+ ASSERT_NE(mesh, nullptr);
+
+ draco::Encoder encoder;
+ encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 16);
+ encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, 15);
+
+ draco::EncoderBuffer buffer;
+ encoder.EncodeMeshToBuffer(*mesh.get(), &buffer);
+ VerifyNumQuantizationBits(buffer, 16, 15, 15);
+}
+
+TEST_F(EncodeTest, TestLinesObj) {
+ // This test verifies that Encoder can encode file that contains only line
+ // segments (that are ignored).
+ std::unique_ptr<draco::Mesh> mesh(
+ draco::ReadMeshFromTestFile("test_lines.obj"));
+ ASSERT_NE(mesh, nullptr);
+ ASSERT_EQ(mesh->num_faces(), 0);
+ std::unique_ptr<draco::PointCloud> pc(
+ draco::ReadPointCloudFromTestFile("test_lines.obj"));
+ ASSERT_NE(pc, nullptr);
+
+ draco::Encoder encoder;
+ encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 16);
+
+ draco::EncoderBuffer buffer;
+ ASSERT_TRUE(encoder.EncodePointCloudToBuffer(*pc, &buffer).ok());
+}
+
+TEST_F(EncodeTest, TestKdTreeEncoding) {
+ // This test verifies that the API can successfully encode a point cloud
+ // defined by several attributes using the kd tree method.
+ std::unique_ptr<draco::PointCloud> pc = CreateTestPointCloud();
+ ASSERT_NE(pc, nullptr);
+
+ draco::EncoderBuffer buffer;
+ draco::Encoder encoder;
+ encoder.SetEncodingMethod(draco::POINT_CLOUD_KD_TREE_ENCODING);
+ // First try it without quantizing positions which should fail.
+ ASSERT_FALSE(encoder.EncodePointCloudToBuffer(*pc, &buffer).ok());
+
+ // Now set quantization for the position attribute which should make
+ // the encoder happy.
+ encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 16);
+ ASSERT_TRUE(encoder.EncodePointCloudToBuffer(*pc, &buffer).ok());
+}
+
+TEST_F(EncodeTest, TestTrackingOfNumberOfEncodedEntries) {
+ TestNumberOfEncodedEntries("deg_faces.obj", draco::MESH_EDGEBREAKER_ENCODING);
+ TestNumberOfEncodedEntries("deg_faces.obj", draco::MESH_SEQUENTIAL_ENCODING);
+ TestNumberOfEncodedEntries("cube_att.obj", draco::MESH_EDGEBREAKER_ENCODING);
+ TestNumberOfEncodedEntries("test_nm.obj", draco::MESH_EDGEBREAKER_ENCODING);
+ TestNumberOfEncodedEntries("test_nm.obj", draco::MESH_SEQUENTIAL_ENCODING);
+ TestNumberOfEncodedEntries("cube_subd.obj",
+ draco::POINT_CLOUD_KD_TREE_ENCODING);
+ TestNumberOfEncodedEntries("cube_subd.obj",
+ draco::POINT_CLOUD_SEQUENTIAL_ENCODING);
+}
+
+TEST_F(EncodeTest, TestTrackingOfNumberOfEncodedEntriesNotSet) {
+ // Tests that when tracing of encoded properties is disabled, the returned
+ // number of encoded faces and points is 0.
+ std::unique_ptr<draco::Mesh> mesh(
+ draco::ReadMeshFromTestFile("cube_att.obj"));
+ ASSERT_NE(mesh, nullptr);
+
+ draco::EncoderBuffer buffer;
+ draco::Encoder encoder;
+
+ ASSERT_TRUE(encoder.EncodeMeshToBuffer(*mesh, &buffer).ok());
+ ASSERT_EQ(encoder.num_encoded_points(), 0);
+ ASSERT_EQ(encoder.num_encoded_faces(), 0);
+}
+
+} // namespace
diff --git a/extern/draco/dracoenc/src/draco/compression/entropy/ans.h b/extern/draco/dracoenc/src/draco/compression/entropy/ans.h
new file mode 100644
index 00000000000..40b08ed33c6
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/entropy/ans.h
@@ -0,0 +1,514 @@
+// 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_ANS_H_
+#define DRACO_CORE_ANS_H_
+// An implementation of Asymmetric Numeral Systems (rANS).
+// See http://arxiv.org/abs/1311.2540v2 for more information on rANS.
+// This file is based off libvpx's ans.h.
+
+#include <vector>
+
+#define ANS_DIVIDE_BY_MULTIPLY 1
+#if ANS_DIVIDE_BY_MULTIPLY
+#include "draco/core/divide.h"
+#endif
+#include "draco/core/macros.h"
+
+namespace draco {
+
+#if ANS_DIVIDE_BY_MULTIPLY
+
+#define ANS_DIVREM(quotient, remainder, dividend, divisor) \
+ do { \
+ quotient = fastdiv(dividend, divisor); \
+ remainder = dividend - quotient * divisor; \
+ } while (0)
+#define ANS_DIV(dividend, divisor) fastdiv(dividend, divisor)
+#else
+#define ANS_DIVREM(quotient, remainder, dividend, divisor) \
+ do { \
+ quotient = dividend / divisor; \
+ remainder = dividend % divisor; \
+ } while (0)
+#define ANS_DIV(dividend, divisor) ((dividend) / (divisor))
+#endif
+
+struct AnsCoder {
+ AnsCoder() : buf(nullptr), buf_offset(0), state(0) {}
+ uint8_t *buf;
+ int buf_offset;
+ uint32_t state;
+};
+
+struct AnsDecoder {
+ AnsDecoder() : buf(nullptr), buf_offset(0), state(0) {}
+ const uint8_t *buf;
+ int buf_offset;
+ uint32_t state;
+};
+
+typedef uint8_t AnsP8;
+#define ans_p8_precision 256u
+#define ans_p8_shift 8
+#define ans_p10_precision 1024u
+
+#define l_base (ans_p10_precision * 4) // l_base % precision must be 0
+#define io_base 256
+// Range I = { l_base, l_base + 1, ..., l_base * io_base - 1 }
+
+static uint32_t mem_get_le16(const void *vmem) {
+ uint32_t val;
+ const uint8_t *mem = (const uint8_t *)vmem;
+
+ val = mem[1] << 8;
+ val |= mem[0];
+ return val;
+}
+
+static uint32_t mem_get_le24(const void *vmem) {
+ uint32_t val;
+ const uint8_t *mem = (const uint8_t *)vmem;
+
+ val = mem[2] << 16;
+ val |= mem[1] << 8;
+ val |= mem[0];
+ return val;
+}
+
+static inline uint32_t mem_get_le32(const void *vmem) {
+ uint32_t val;
+ const uint8_t *mem = (const uint8_t *)vmem;
+
+ val = mem[3] << 24;
+ val |= mem[2] << 16;
+ val |= mem[1] << 8;
+ val |= mem[0];
+ return val;
+}
+
+static inline void mem_put_le16(void *vmem, uint32_t val) {
+ uint8_t *mem = reinterpret_cast<uint8_t *>(vmem);
+
+ mem[0] = (val >> 0) & 0xff;
+ mem[1] = (val >> 8) & 0xff;
+}
+
+static inline void mem_put_le24(void *vmem, uint32_t val) {
+ uint8_t *mem = reinterpret_cast<uint8_t *>(vmem);
+
+ mem[0] = (val >> 0) & 0xff;
+ mem[1] = (val >> 8) & 0xff;
+ mem[2] = (val >> 16) & 0xff;
+}
+
+static inline void mem_put_le32(void *vmem, uint32_t val) {
+ uint8_t *mem = reinterpret_cast<uint8_t *>(vmem);
+
+ mem[0] = (val >> 0) & 0xff;
+ mem[1] = (val >> 8) & 0xff;
+ mem[2] = (val >> 16) & 0xff;
+ mem[3] = (val >> 24) & 0xff;
+}
+
+static inline void ans_write_init(struct AnsCoder *const ans,
+ uint8_t *const buf) {
+ ans->buf = buf;
+ ans->buf_offset = 0;
+ ans->state = l_base;
+}
+
+static inline int ans_write_end(struct AnsCoder *const ans) {
+ uint32_t state;
+ DRACO_DCHECK_GE(ans->state, l_base);
+ DRACO_DCHECK_LT(ans->state, l_base * io_base);
+ state = ans->state - l_base;
+ if (state < (1 << 6)) {
+ ans->buf[ans->buf_offset] = (0x00 << 6) + state;
+ return ans->buf_offset + 1;
+ } else if (state < (1 << 14)) {
+ mem_put_le16(ans->buf + ans->buf_offset, (0x01 << 14) + state);
+ return ans->buf_offset + 2;
+ } else if (state < (1 << 22)) {
+ mem_put_le24(ans->buf + ans->buf_offset, (0x02 << 22) + state);
+ return ans->buf_offset + 3;
+ } else {
+ DRACO_DCHECK(0 && "State is too large to be serialized");
+ return ans->buf_offset;
+ }
+}
+
+// rABS with descending spread
+// p or p0 takes the place of l_s from the paper
+// ans_p8_precision is m
+static inline void rabs_desc_write(struct AnsCoder *ans, int val, AnsP8 p0) {
+ const AnsP8 p = ans_p8_precision - p0;
+ const unsigned l_s = val ? p : p0;
+ unsigned quot, rem;
+ if (ans->state >= l_base / ans_p8_precision * io_base * l_s) {
+ ans->buf[ans->buf_offset++] = ans->state % io_base;
+ ans->state /= io_base;
+ }
+ ANS_DIVREM(quot, rem, ans->state, l_s);
+ ans->state = quot * ans_p8_precision + rem + (val ? 0 : p);
+}
+
+#define ANS_IMPL1 0
+#define UNPREDICTABLE(x) x
+static inline int rabs_desc_read(struct AnsDecoder *ans, AnsP8 p0) {
+ int val;
+#if ANS_IMPL1
+ unsigned l_s;
+#else
+ unsigned quot, rem, x, xn;
+#endif
+ const AnsP8 p = ans_p8_precision - p0;
+ if (ans->state < l_base && ans->buf_offset > 0) {
+ ans->state = ans->state * io_base + ans->buf[--ans->buf_offset];
+ }
+#if ANS_IMPL1
+ val = ans->state % ans_p8_precision < p;
+ l_s = val ? p : p0;
+ ans->state = (ans->state / ans_p8_precision) * l_s +
+ ans->state % ans_p8_precision - (!val * p);
+#else
+ x = ans->state;
+ quot = x / ans_p8_precision;
+ rem = x % ans_p8_precision;
+ xn = quot * p;
+ val = rem < p;
+ if (UNPREDICTABLE(val)) {
+ ans->state = xn + rem;
+ } else {
+ // ans->state = quot * p0 + rem - p;
+ ans->state = x - xn - p;
+ }
+#endif
+ return val;
+}
+
+// rABS with ascending spread
+// p or p0 takes the place of l_s from the paper
+// ans_p8_precision is m
+static inline void rabs_asc_write(struct AnsCoder *ans, int val, AnsP8 p0) {
+ const AnsP8 p = ans_p8_precision - p0;
+ const unsigned l_s = val ? p : p0;
+ unsigned quot, rem;
+ if (ans->state >= l_base / ans_p8_precision * io_base * l_s) {
+ ans->buf[ans->buf_offset++] = ans->state % io_base;
+ ans->state /= io_base;
+ }
+ ANS_DIVREM(quot, rem, ans->state, l_s);
+ ans->state = quot * ans_p8_precision + rem + (val ? p0 : 0);
+}
+
+static inline int rabs_asc_read(struct AnsDecoder *ans, AnsP8 p0) {
+ int val;
+#if ANS_IMPL1
+ unsigned l_s;
+#else
+ unsigned quot, rem, x, xn;
+#endif
+ const AnsP8 p = ans_p8_precision - p0;
+ if (ans->state < l_base) {
+ ans->state = ans->state * io_base + ans->buf[--ans->buf_offset];
+ }
+#if ANS_IMPL1
+ val = ans->state % ans_p8_precision < p;
+ l_s = val ? p : p0;
+ ans->state = (ans->state / ans_p8_precision) * l_s +
+ ans->state % ans_p8_precision - (!val * p);
+#else
+ x = ans->state;
+ quot = x / ans_p8_precision;
+ rem = x % ans_p8_precision;
+ xn = quot * p;
+ val = rem >= p0;
+ if (UNPREDICTABLE(val)) {
+ ans->state = xn + rem - p0;
+ } else {
+ // ans->state = quot * p0 + rem - p0;
+ ans->state = x - xn;
+ }
+#endif
+ return val;
+}
+
+#define rabs_read rabs_desc_read
+#define rabs_write rabs_desc_write
+
+// uABS with normalization
+static inline void uabs_write(struct AnsCoder *ans, int val, AnsP8 p0) {
+ AnsP8 p = ans_p8_precision - p0;
+ const unsigned l_s = val ? p : p0;
+ while (ans->state >= l_base / ans_p8_precision * io_base * l_s) {
+ ans->buf[ans->buf_offset++] = ans->state % io_base;
+ ans->state /= io_base;
+ }
+ if (!val)
+ ans->state = ANS_DIV(ans->state * ans_p8_precision, p0);
+ else
+ ans->state = ANS_DIV((ans->state + 1) * ans_p8_precision + p - 1, p) - 1;
+}
+
+static inline int uabs_read(struct AnsDecoder *ans, AnsP8 p0) {
+ AnsP8 p = ans_p8_precision - p0;
+ int s;
+ // unsigned int xp1;
+ unsigned xp, sp;
+ unsigned state = ans->state;
+ while (state < l_base && ans->buf_offset > 0) {
+ state = state * io_base + ans->buf[--ans->buf_offset];
+ }
+ sp = state * p;
+ // xp1 = (sp + p) / ans_p8_precision;
+ xp = sp / ans_p8_precision;
+ // s = xp1 - xp;
+ s = (sp & 0xFF) >= p0;
+ if (UNPREDICTABLE(s))
+ ans->state = xp;
+ else
+ ans->state = state - xp;
+ return s;
+}
+
+static inline int uabs_read_bit(struct AnsDecoder *ans) {
+ int s;
+ unsigned state = ans->state;
+ while (state < l_base && ans->buf_offset > 0) {
+ state = state * io_base + ans->buf[--ans->buf_offset];
+ }
+ s = static_cast<int>(state & 1);
+ ans->state = state >> 1;
+ return s;
+}
+
+static inline int ans_read_init(struct AnsDecoder *const ans,
+ const uint8_t *const buf, int offset) {
+ unsigned x;
+ if (offset < 1)
+ return 1;
+ ans->buf = buf;
+ x = buf[offset - 1] >> 6;
+ if (x == 0) {
+ ans->buf_offset = offset - 1;
+ ans->state = buf[offset - 1] & 0x3F;
+ } else if (x == 1) {
+ if (offset < 2)
+ return 1;
+ ans->buf_offset = offset - 2;
+ ans->state = mem_get_le16(buf + offset - 2) & 0x3FFF;
+ } else if (x == 2) {
+ if (offset < 3)
+ return 1;
+ ans->buf_offset = offset - 3;
+ ans->state = mem_get_le24(buf + offset - 3) & 0x3FFFFF;
+ } else {
+ return 1;
+ }
+ ans->state += l_base;
+ if (ans->state >= l_base * io_base)
+ return 1;
+ return 0;
+}
+
+static inline int ans_read_end(struct AnsDecoder *const ans) {
+ return ans->state == l_base;
+}
+
+static inline int ans_reader_has_error(const struct AnsDecoder *const ans) {
+ return ans->state < l_base && ans->buf_offset == 0;
+}
+
+struct rans_sym {
+ uint32_t prob;
+ uint32_t cum_prob; // not-inclusive
+};
+
+// Class for performing rANS encoding using a desired number of precision bits.
+// The max number of precision bits is currently 19. The actual number of
+// symbols in the input alphabet should be (much) smaller than that, otherwise
+// the compression rate may suffer.
+template <int rans_precision_bits_t>
+class RAnsEncoder {
+ public:
+ RAnsEncoder() {}
+
+ // Provides the input buffer where the data is going to be stored.
+ inline void write_init(uint8_t *const buf) {
+ ans_.buf = buf;
+ ans_.buf_offset = 0;
+ ans_.state = l_rans_base;
+ }
+
+ // Needs to be called after all symbols are encoded.
+ inline int write_end() {
+ uint32_t state;
+ DRACO_DCHECK_GE(ans_.state, l_rans_base);
+ DRACO_DCHECK_LT(ans_.state, l_rans_base * io_base);
+ state = ans_.state - l_rans_base;
+ if (state < (1 << 6)) {
+ ans_.buf[ans_.buf_offset] = (0x00 << 6) + state;
+ return ans_.buf_offset + 1;
+ } else if (state < (1 << 14)) {
+ mem_put_le16(ans_.buf + ans_.buf_offset, (0x01 << 14) + state);
+ return ans_.buf_offset + 2;
+ } else if (state < (1 << 22)) {
+ mem_put_le24(ans_.buf + ans_.buf_offset, (0x02 << 22) + state);
+ return ans_.buf_offset + 3;
+ } else if (state < (1 << 30)) {
+ mem_put_le32(ans_.buf + ans_.buf_offset, (0x03u << 30u) + state);
+ return ans_.buf_offset + 4;
+ } else {
+ DRACO_DCHECK(0 && "State is too large to be serialized");
+ return ans_.buf_offset;
+ }
+ }
+
+ // rANS with normalization
+ // sym->prob takes the place of l_s from the paper
+ // rans_precision is m
+ inline void rans_write(const struct rans_sym *const sym) {
+ const uint32_t p = sym->prob;
+ while (ans_.state >= l_rans_base / rans_precision * io_base * p) {
+ ans_.buf[ans_.buf_offset++] = ans_.state % io_base;
+ ans_.state /= io_base;
+ }
+ // TODO(ostava): The division and multiplication should be optimized.
+ ans_.state =
+ (ans_.state / p) * rans_precision + ans_.state % p + sym->cum_prob;
+ }
+
+ private:
+ static constexpr int rans_precision = 1 << rans_precision_bits_t;
+ static constexpr int l_rans_base = rans_precision * 4;
+ AnsCoder ans_;
+};
+
+struct rans_dec_sym {
+ uint32_t val;
+ uint32_t prob;
+ uint32_t cum_prob; // not-inclusive
+};
+
+// Class for performing rANS decoding using a desired number of precision bits.
+// The number of precision bits needs to be the same as with the RAnsEncoder
+// that was used to encode the input data.
+template <int rans_precision_bits_t>
+class RAnsDecoder {
+ public:
+ RAnsDecoder() {}
+
+ // Initializes the decoder from the input buffer. The |offset| specifies the
+ // number of bytes encoded by the encoder. A non zero return value is an
+ // error.
+ inline int read_init(const uint8_t *const buf, int offset) {
+ unsigned x;
+ if (offset < 1)
+ return 1;
+ ans_.buf = buf;
+ x = buf[offset - 1] >> 6;
+ if (x == 0) {
+ ans_.buf_offset = offset - 1;
+ ans_.state = buf[offset - 1] & 0x3F;
+ } else if (x == 1) {
+ if (offset < 2)
+ return 1;
+ ans_.buf_offset = offset - 2;
+ ans_.state = mem_get_le16(buf + offset - 2) & 0x3FFF;
+ } else if (x == 2) {
+ if (offset < 3)
+ return 1;
+ ans_.buf_offset = offset - 3;
+ ans_.state = mem_get_le24(buf + offset - 3) & 0x3FFFFF;
+ } else if (x == 3) {
+ ans_.buf_offset = offset - 4;
+ ans_.state = mem_get_le32(buf + offset - 4) & 0x3FFFFFFF;
+ } else {
+ return 1;
+ }
+ ans_.state += l_rans_base;
+ if (ans_.state >= l_rans_base * io_base)
+ return 1;
+ return 0;
+ }
+
+ inline int read_end() { return ans_.state == l_rans_base; }
+
+ inline int reader_has_error() {
+ return ans_.state < l_rans_base && ans_.buf_offset == 0;
+ }
+
+ inline int rans_read() {
+ unsigned rem;
+ unsigned quo;
+ struct rans_dec_sym sym;
+ while (ans_.state < l_rans_base && ans_.buf_offset > 0) {
+ ans_.state = ans_.state * io_base + ans_.buf[--ans_.buf_offset];
+ }
+ // |rans_precision| is a power of two compile time constant, and the below
+ // division and modulo are going to be optimized by the compiler.
+ quo = ans_.state / rans_precision;
+ rem = ans_.state % rans_precision;
+ fetch_sym(&sym, rem);
+ ans_.state = quo * sym.prob + rem - sym.cum_prob;
+ return sym.val;
+ }
+
+ // Construct a lookup table with |rans_precision| number of entries.
+ // Returns false if the table couldn't be built (because of wrong input data).
+ inline bool rans_build_look_up_table(const uint32_t token_probs[],
+ uint32_t num_symbols) {
+ lut_table_.resize(rans_precision);
+ probability_table_.resize(num_symbols);
+ uint32_t cum_prob = 0;
+ uint32_t act_prob = 0;
+ 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];
+ if (cum_prob > rans_precision) {
+ return false;
+ }
+ for (uint32_t j = act_prob; j < cum_prob; ++j) {
+ lut_table_[j] = i;
+ }
+ act_prob = cum_prob;
+ }
+ if (cum_prob != rans_precision) {
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ inline void fetch_sym(struct rans_dec_sym *out, uint32_t rem) {
+ uint32_t symbol = lut_table_[rem];
+ out->val = symbol;
+ out->prob = probability_table_[symbol].prob;
+ out->cum_prob = probability_table_[symbol].cum_prob;
+ }
+
+ static constexpr int rans_precision = 1 << rans_precision_bits_t;
+ static constexpr int l_rans_base = rans_precision * 4;
+ std::vector<uint32_t> lut_table_;
+ std::vector<rans_sym> probability_table_;
+ AnsDecoder ans_;
+};
+
+#undef ANS_DIVREM
+
+} // namespace draco
+
+#endif // DRACO_CORE_ANS_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/entropy/rans_symbol_coding.h b/extern/draco/dracoenc/src/draco/compression/entropy/rans_symbol_coding.h
new file mode 100644
index 00000000000..8d06644a9b9
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/entropy/rans_symbol_coding.h
@@ -0,0 +1,54 @@
+// 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.
+//
+// File providing shared functionality for RAnsSymbolEncoder and
+// RAnsSymbolDecoder (see rans_symbol_encoder.h / rans_symbol_decoder.h).
+#ifndef DRACO_COMPRESSION_ENTROPY_RANS_SYMBOL_CODING_H_
+#define DRACO_COMPRESSION_ENTROPY_RANS_SYMBOL_CODING_H_
+
+#include "draco/compression/entropy/ans.h"
+
+namespace draco {
+
+// Computes the desired precision of the rANS method for the specified number of
+// unique symbols the input data (defined by their bit_length).
+constexpr int ComputeRAnsUnclampedPrecision(int symbols_bit_length) {
+ return (3 * symbols_bit_length) / 2;
+}
+
+// Computes the desired precision clamped to guarantee a valid functionality of
+// our rANS library (which is between 12 to 20 bits).
+constexpr int ComputeRAnsPrecisionFromUniqueSymbolsBitLength(
+ int symbols_bit_length) {
+ return ComputeRAnsUnclampedPrecision(symbols_bit_length) < 12
+ ? 12
+ : ComputeRAnsUnclampedPrecision(symbols_bit_length) > 20
+ ? 20
+ : ComputeRAnsUnclampedPrecision(symbols_bit_length);
+}
+
+// Compute approximate frequency table size needed for storing the provided
+// symbols.
+static int64_t ApproximateRAnsFrequencyTableBits(int32_t max_value,
+ int num_unique_symbols) {
+ // Approximate number of bits for storing zero frequency entries using the
+ // run length encoding (with max length of 64).
+ const int64_t table_zero_frequency_bits =
+ 8 * (num_unique_symbols + (max_value - num_unique_symbols) / 64);
+ return 8 * num_unique_symbols + table_zero_frequency_bits;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ENTROPY_RANS_SYMBOL_CODING_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/entropy/rans_symbol_decoder.h b/extern/draco/dracoenc/src/draco/compression/entropy/rans_symbol_decoder.h
new file mode 100644
index 00000000000..1b5f1c8b715
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/entropy/rans_symbol_decoder.h
@@ -0,0 +1,153 @@
+// 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_COMPRESSION_ENTROPY_RANS_SYMBOL_DECODER_H_
+#define DRACO_COMPRESSION_ENTROPY_RANS_SYMBOL_DECODER_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/config/compression_shared.h"
+#include "draco/compression/entropy/rans_symbol_coding.h"
+#include "draco/core/decoder_buffer.h"
+#include "draco/core/varint_decoding.h"
+
+namespace draco {
+
+// A helper class for decoding symbols using the rANS algorithm (see ans.h).
+// The class can be used to decode the probability table and the data encoded
+// by the RAnsSymbolEncoder. |unique_symbols_bit_length_t| must be the same as
+// the one used for the corresponding RAnsSymbolEncoder.
+template <int unique_symbols_bit_length_t>
+class RAnsSymbolDecoder {
+ public:
+ RAnsSymbolDecoder() : num_symbols_(0) {}
+
+ // Initialize the decoder and decode the probability table.
+ bool Create(DecoderBuffer *buffer);
+
+ uint32_t num_symbols() const { return num_symbols_; }
+
+ // Starts decoding from the buffer. The buffer will be advanced past the
+ // encoded data after this call.
+ bool StartDecoding(DecoderBuffer *buffer);
+ uint32_t DecodeSymbol() { return ans_.rans_read(); }
+ void EndDecoding();
+
+ private:
+ static constexpr int rans_precision_bits_ =
+ ComputeRAnsPrecisionFromUniqueSymbolsBitLength(
+ unique_symbols_bit_length_t);
+ static constexpr int rans_precision_ = 1 << rans_precision_bits_;
+
+ std::vector<uint32_t> probability_table_;
+ uint32_t num_symbols_;
+ RAnsDecoder<rans_precision_bits_> ans_;
+};
+
+template <int unique_symbols_bit_length_t>
+bool RAnsSymbolDecoder<unique_symbols_bit_length_t>::Create(
+ DecoderBuffer *buffer) {
+ // Check that the DecoderBuffer version is set.
+ if (buffer->bitstream_version() == 0)
+ return false;
+ // Decode the number of alphabet symbols.
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (buffer->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0)) {
+ if (!buffer->Decode(&num_symbols_))
+ return false;
+
+ } else
+#endif
+ {
+ if (!DecodeVarint(&num_symbols_, buffer))
+ return false;
+ }
+ probability_table_.resize(num_symbols_);
+ if (num_symbols_ == 0)
+ return true;
+ // Decode the table.
+ for (uint32_t i = 0; i < num_symbols_; ++i) {
+ uint8_t prob_data = 0;
+ // Decode the first byte and extract the number of extra bytes we need to
+ // get, or the offset to the next symbol with non-zero probability.
+ if (!buffer->Decode(&prob_data))
+ return false;
+ // Token is stored in the first two bits of the first byte. Values 0-2 are
+ // used to indicate the number of extra bytes, and value 3 is a special
+ // symbol used to denote run-length coding of zero probability entries.
+ // See rans_symbol_encoder.h for more details.
+ const int token = prob_data & 3;
+ if (token == 3) {
+ const uint32_t offset = prob_data >> 2;
+ if (i + offset >= num_symbols_)
+ return false;
+ // Set zero probability for all symbols in the specified range.
+ for (uint32_t j = 0; j < offset + 1; ++j) {
+ probability_table_[i + j] = 0;
+ }
+ i += offset;
+ } else {
+ const int extra_bytes = token;
+ uint32_t prob = prob_data >> 2;
+ for (int b = 0; b < extra_bytes; ++b) {
+ uint8_t eb;
+ if (!buffer->Decode(&eb))
+ return false;
+ // Shift 8 bits for each extra byte and subtract 2 for the two first
+ // bits.
+ prob |= static_cast<uint32_t>(eb) << (8 * (b + 1) - 2);
+ }
+ probability_table_[i] = prob;
+ }
+ }
+ if (!ans_.rans_build_look_up_table(&probability_table_[0], num_symbols_))
+ return false;
+ return true;
+}
+
+template <int unique_symbols_bit_length_t>
+bool RAnsSymbolDecoder<unique_symbols_bit_length_t>::StartDecoding(
+ DecoderBuffer *buffer) {
+ uint64_t bytes_encoded;
+ // Decode the number of bytes encoded by the encoder.
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (buffer->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0)) {
+ if (!buffer->Decode(&bytes_encoded))
+ return false;
+
+ } else
+#endif
+ {
+ if (!DecodeVarint<uint64_t>(&bytes_encoded, buffer))
+ return false;
+ }
+ if (bytes_encoded > static_cast<uint64_t>(buffer->remaining_size()))
+ return false;
+ const uint8_t *const data_head =
+ reinterpret_cast<const uint8_t *>(buffer->data_head());
+ // Advance the buffer past the rANS data.
+ buffer->Advance(bytes_encoded);
+ if (ans_.read_init(data_head, static_cast<int>(bytes_encoded)) != 0)
+ return false;
+ return true;
+}
+
+template <int unique_symbols_bit_length_t>
+void RAnsSymbolDecoder<unique_symbols_bit_length_t>::EndDecoding() {
+ ans_.read_end();
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ENTROPY_RANS_SYMBOL_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/entropy/rans_symbol_encoder.h b/extern/draco/dracoenc/src/draco/compression/entropy/rans_symbol_encoder.h
new file mode 100644
index 00000000000..4b0ed091af0
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/entropy/rans_symbol_encoder.h
@@ -0,0 +1,280 @@
+// 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_COMPRESSION_ENTROPY_RANS_SYMBOL_ENCODER_H_
+#define DRACO_COMPRESSION_ENTROPY_RANS_SYMBOL_ENCODER_H_
+
+#include <algorithm>
+#include <cmath>
+#include <cstring>
+
+#include "draco/compression/entropy/ans.h"
+#include "draco/compression/entropy/rans_symbol_coding.h"
+#include "draco/core/encoder_buffer.h"
+#include "draco/core/varint_encoding.h"
+
+namespace draco {
+
+// A helper class for encoding symbols using the rANS algorithm (see ans.h).
+// The class can be used to initialize and encode probability table needed by
+// rANS, and to perform encoding of symbols into the provided EncoderBuffer.
+template <int unique_symbols_bit_length_t>
+class RAnsSymbolEncoder {
+ public:
+ RAnsSymbolEncoder()
+ : num_symbols_(0), num_expected_bits_(0), buffer_offset_(0) {}
+
+ // Creates a probability table needed by the rANS library and encode it into
+ // the provided buffer.
+ bool Create(const uint64_t *frequencies, int num_symbols,
+ EncoderBuffer *buffer);
+
+ void StartEncoding(EncoderBuffer *buffer);
+ void EncodeSymbol(uint32_t symbol) {
+ ans_.rans_write(&probability_table_[symbol]);
+ }
+ void EndEncoding(EncoderBuffer *buffer);
+
+ // rANS requires to encode the input symbols in the reverse order.
+ static constexpr bool needs_reverse_encoding() { return true; }
+
+ private:
+ // Functor used for sorting symbol ids according to their probabilities.
+ // The functor sorts symbol indices that index an underlying map between
+ // symbol ids and their probabilities. We don't sort the probability table
+ // directly, because that would require an additional indirection during the
+ // EncodeSymbol() function.
+ struct ProbabilityLess {
+ explicit ProbabilityLess(const std::vector<rans_sym> *probs)
+ : probabilities(probs) {}
+ bool operator()(int i, int j) const {
+ return probabilities->at(i).prob < probabilities->at(j).prob;
+ }
+ const std::vector<rans_sym> *probabilities;
+ };
+
+ // Encodes the probability table into the output buffer.
+ bool EncodeTable(EncoderBuffer *buffer);
+
+ static constexpr int rans_precision_bits_ =
+ ComputeRAnsPrecisionFromUniqueSymbolsBitLength(
+ unique_symbols_bit_length_t);
+ static constexpr int rans_precision_ = 1 << rans_precision_bits_;
+
+ std::vector<rans_sym> probability_table_;
+ // The number of symbols in the input alphabet.
+ uint32_t num_symbols_;
+ // Expected number of bits that is needed to encode the input.
+ uint64_t num_expected_bits_;
+
+ RAnsEncoder<rans_precision_bits_> ans_;
+ // Initial offset of the encoder buffer before any ans data was encoded.
+ uint64_t buffer_offset_;
+};
+
+template <int unique_symbols_bit_length_t>
+bool RAnsSymbolEncoder<unique_symbols_bit_length_t>::Create(
+ const uint64_t *frequencies, int num_symbols, EncoderBuffer *buffer) {
+ // Compute the total of the input frequencies.
+ uint64_t total_freq = 0;
+ int max_valid_symbol = 0;
+ for (int i = 0; i < num_symbols; ++i) {
+ total_freq += frequencies[i];
+ if (frequencies[i] > 0)
+ max_valid_symbol = i;
+ }
+ num_symbols = max_valid_symbol + 1;
+ num_symbols_ = num_symbols;
+ probability_table_.resize(num_symbols);
+ const double total_freq_d = static_cast<double>(total_freq);
+ const double rans_precision_d = static_cast<double>(rans_precision_);
+ // Compute probabilities by rescaling the normalized frequencies into interval
+ // [1, rans_precision - 1]. The total probability needs to be equal to
+ // rans_precision.
+ int total_rans_prob = 0;
+ for (int i = 0; i < num_symbols; ++i) {
+ const uint64_t freq = frequencies[i];
+
+ // Normalized probability.
+ const double prob = static_cast<double>(freq) / total_freq_d;
+
+ // RAns probability in range of [1, rans_precision - 1].
+ uint32_t rans_prob = static_cast<uint32_t>(prob * rans_precision_d + 0.5f);
+ if (rans_prob == 0 && freq > 0)
+ rans_prob = 1;
+ probability_table_[i].prob = rans_prob;
+ total_rans_prob += rans_prob;
+ }
+ // Because of rounding errors, the total precision may not be exactly accurate
+ // and we may need to adjust the entries a little bit.
+ if (total_rans_prob != rans_precision_) {
+ std::vector<int> sorted_probabilities(num_symbols);
+ for (int i = 0; i < num_symbols; ++i) {
+ sorted_probabilities[i] = i;
+ }
+ std::sort(sorted_probabilities.begin(), sorted_probabilities.end(),
+ ProbabilityLess(&probability_table_));
+ if (total_rans_prob < rans_precision_) {
+ // This happens rather infrequently, just add the extra needed precision
+ // to the most frequent symbol.
+ probability_table_[sorted_probabilities.back()].prob +=
+ rans_precision_ - total_rans_prob;
+ } else {
+ // We have over-allocated the precision, which is quite common.
+ // Rescale the probabilities of all symbols.
+ int32_t error = total_rans_prob - rans_precision_;
+ while (error > 0) {
+ const double act_total_prob_d = static_cast<double>(total_rans_prob);
+ const double act_rel_error_d = rans_precision_d / act_total_prob_d;
+ for (int j = num_symbols - 1; j > 0; --j) {
+ int symbol_id = sorted_probabilities[j];
+ if (probability_table_[symbol_id].prob <= 1) {
+ if (j == num_symbols - 1)
+ return false; // Most frequent symbol would be empty.
+ break;
+ }
+ const int32_t new_prob = static_cast<int32_t>(
+ floor(act_rel_error_d *
+ static_cast<double>(probability_table_[symbol_id].prob)));
+ int32_t fix = probability_table_[symbol_id].prob - new_prob;
+ if (fix == 0u)
+ fix = 1;
+ if (fix >= static_cast<int32_t>(probability_table_[symbol_id].prob))
+ fix = probability_table_[symbol_id].prob - 1;
+ if (fix > error)
+ fix = error;
+ probability_table_[symbol_id].prob -= fix;
+ total_rans_prob -= fix;
+ error -= fix;
+ if (total_rans_prob == rans_precision_)
+ break;
+ }
+ }
+ }
+ }
+
+ // Compute the cumulative probability (cdf).
+ uint32_t total_prob = 0;
+ for (int i = 0; i < num_symbols; ++i) {
+ probability_table_[i].cum_prob = total_prob;
+ total_prob += probability_table_[i].prob;
+ }
+ if (total_prob != rans_precision_)
+ return false;
+
+ // Estimate the number of bits needed to encode the input.
+ // From Shannon entropy the total number of bits N is:
+ // N = -sum{i : all_symbols}(F(i) * log2(P(i)))
+ // where P(i) is the normalized probability of symbol i and F(i) is the
+ // symbol's frequency in the input data.
+ double num_bits = 0;
+ for (int i = 0; i < num_symbols; ++i) {
+ if (probability_table_[i].prob == 0)
+ continue;
+ const double norm_prob =
+ static_cast<double>(probability_table_[i].prob) / rans_precision_d;
+ num_bits += static_cast<double>(frequencies[i]) * log2(norm_prob);
+ }
+ num_expected_bits_ = static_cast<uint64_t>(ceil(-num_bits));
+ if (!EncodeTable(buffer))
+ return false;
+ return true;
+}
+
+template <int unique_symbols_bit_length_t>
+bool RAnsSymbolEncoder<unique_symbols_bit_length_t>::EncodeTable(
+ EncoderBuffer *buffer) {
+ EncodeVarint(num_symbols_, buffer);
+ // Use varint encoding for the probabilities (first two bits represent the
+ // number of bytes used - 1).
+ 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)) {
+ num_extra_bytes++;
+ if (prob >= (1 << 14)) {
+ num_extra_bytes++;
+ if (prob >= (1 << 22)) {
+ // The maximum number of precision bits is 20 so we should not really
+ // get to this point.
+ return false;
+ }
+ }
+ }
+ if (prob == 0) {
+ // When the probability of the symbol is 0, set the first two bits to 1
+ // (unique identifier) and use the remaining 6 bits to store the offset
+ // to the next symbol with non-zero probability.
+ uint32_t offset = 0;
+ for (; offset < (1 << 6) - 1; ++offset) {
+ // Note: we don't have to check whether the next symbol id is larger
+ // than num_symbols_ because we know that the last symbol always has
+ // non-zero probability.
+ const uint32_t next_prob = probability_table_[i + offset + 1].prob;
+ if (next_prob > 0) {
+ break;
+ }
+ }
+ buffer->Encode(static_cast<uint8_t>((offset << 2) | 3));
+ i += offset;
+ } else {
+ // Encode the first byte (including the number of extra bytes).
+ buffer->Encode(static_cast<uint8_t>((prob << 2) | (num_extra_bytes & 3)));
+ // Encode the extra bytes.
+ for (int b = 0; b < num_extra_bytes; ++b) {
+ buffer->Encode(static_cast<uint8_t>(prob >> (8 * (b + 1) - 2)));
+ }
+ }
+ }
+ return true;
+}
+
+template <int unique_symbols_bit_length_t>
+void RAnsSymbolEncoder<unique_symbols_bit_length_t>::StartEncoding(
+ EncoderBuffer *buffer) {
+ // Allocate extra storage just in case.
+ const uint64_t required_bits = 2 * num_expected_bits_ + 32;
+
+ buffer_offset_ = buffer->size();
+ const int64_t required_bytes = (required_bits + 7) / 8;
+ buffer->Resize(buffer_offset_ + required_bytes + sizeof(buffer_offset_));
+ uint8_t *const data =
+ reinterpret_cast<uint8_t *>(const_cast<char *>(buffer->data()));
+ ans_.write_init(data + buffer_offset_);
+}
+
+template <int unique_symbols_bit_length_t>
+void RAnsSymbolEncoder<unique_symbols_bit_length_t>::EndEncoding(
+ EncoderBuffer *buffer) {
+ char *const src = const_cast<char *>(buffer->data()) + buffer_offset_;
+
+ // TODO(fgalligan): Look into changing this to uint32_t as write_end()
+ // returns an int.
+ const uint64_t bytes_written = static_cast<uint64_t>(ans_.write_end());
+ EncoderBuffer var_size_buffer;
+ EncodeVarint(bytes_written, &var_size_buffer);
+ const uint32_t size_len = static_cast<uint32_t>(var_size_buffer.size());
+ char *const dst = src + size_len;
+ memmove(dst, src, bytes_written);
+
+ // Store the size of the encoded data.
+ memcpy(src, var_size_buffer.data(), size_len);
+
+ // Resize the buffer to match the number of encoded bytes.
+ buffer->Resize(buffer_offset_ + bytes_written + size_len);
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ENTROPY_RANS_SYMBOL_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/entropy/shannon_entropy.cc b/extern/draco/dracoenc/src/draco/compression/entropy/shannon_entropy.cc
new file mode 100644
index 00000000000..5050c11188c
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/entropy/shannon_entropy.cc
@@ -0,0 +1,143 @@
+#include "draco/compression/entropy/shannon_entropy.h"
+
+#include <cmath>
+#include <vector>
+
+#include "draco/compression/entropy/rans_symbol_coding.h"
+
+namespace draco {
+
+int64_t ComputeShannonEntropy(const uint32_t *symbols, int num_symbols,
+ int max_value, int *out_num_unique_symbols) {
+ // First find frequency of all unique symbols in the input array.
+ int num_unique_symbols = 0;
+ std::vector<int> symbol_frequencies(max_value + 1, 0);
+ for (int i = 0; i < num_symbols; ++i) {
+ ++symbol_frequencies[symbols[i]];
+ }
+ double total_bits = 0;
+ double num_symbols_d = num_symbols;
+ for (int i = 0; i < max_value + 1; ++i) {
+ if (symbol_frequencies[i] > 0) {
+ ++num_unique_symbols;
+ // Compute Shannon entropy for the symbol.
+ // We don't want to use std::log2 here for Android build.
+ total_bits +=
+ symbol_frequencies[i] *
+ log2(static_cast<double>(symbol_frequencies[i]) / num_symbols_d);
+ }
+ }
+ if (out_num_unique_symbols)
+ *out_num_unique_symbols = num_unique_symbols;
+ // Entropy is always negative.
+ return static_cast<int64_t>(-total_bits);
+}
+
+double ComputeBinaryShannonEntropy(uint32_t num_values,
+ uint32_t num_true_values) {
+ if (num_values == 0)
+ return 0;
+
+ // We can exit early if the data set has 0 entropy.
+ if (num_true_values == 0 || num_values == num_true_values)
+ return 0;
+ const double true_freq =
+ static_cast<double>(num_true_values) / static_cast<double>(num_values);
+ const double false_freq = 1.0 - true_freq;
+ return -(true_freq * std::log2(true_freq) +
+ false_freq * std::log2(false_freq));
+}
+
+ShannonEntropyTracker::ShannonEntropyTracker() {}
+
+ShannonEntropyTracker::EntropyData ShannonEntropyTracker::Peek(
+ const uint32_t *symbols, int num_symbols) {
+ return UpdateSymbols(symbols, num_symbols, false);
+}
+
+ShannonEntropyTracker::EntropyData ShannonEntropyTracker::Push(
+ const uint32_t *symbols, int num_symbols) {
+ return UpdateSymbols(symbols, num_symbols, true);
+}
+
+ShannonEntropyTracker::EntropyData ShannonEntropyTracker::UpdateSymbols(
+ const uint32_t *symbols, int num_symbols, bool push_changes) {
+ EntropyData ret_data = entropy_data_;
+ ret_data.num_values += num_symbols;
+ for (int i = 0; i < num_symbols; ++i) {
+ const uint32_t symbol = symbols[i];
+ if (frequencies_.size() <= symbol) {
+ frequencies_.resize(symbol + 1, 0);
+ }
+
+ // Update the entropy of the stream. Note that entropy of |N| values
+ // represented by |S| unique symbols is defined as:
+ //
+ // entropy = -sum_over_S(symbol_frequency / N * log2(symbol_frequency / N))
+ //
+ // To avoid the need to recompute the entire sum when new values are added,
+ // we can instead update a so called entropy norm that is defined as:
+ //
+ // entropy_norm = sum_over_S(symbol_frequency * log2(symbol_frequency))
+ //
+ // In this case, all we need to do is update entries on the symbols where
+ // the frequency actually changed.
+ //
+ // Note that entropy_norm and entropy can be easily transformed to the
+ // actual entropy as:
+ //
+ // entropy = log2(N) - entropy_norm / N
+ //
+ double old_symbol_entropy_norm = 0;
+ int &frequency = frequencies_[symbol];
+ if (frequency > 1) {
+ old_symbol_entropy_norm = frequency * std::log2(frequency);
+ } else if (frequency == 0) {
+ ret_data.num_unique_symbols++;
+ if (symbol > static_cast<uint32_t>(ret_data.max_symbol)) {
+ ret_data.max_symbol = symbol;
+ }
+ }
+ frequency++;
+ const double new_symbol_entropy_norm = frequency * std::log2(frequency);
+
+ // Update the final entropy.
+ ret_data.entropy_norm += new_symbol_entropy_norm - old_symbol_entropy_norm;
+ }
+ if (push_changes) {
+ // Update entropy data of the stream.
+ entropy_data_ = ret_data;
+ } else {
+ // We are only peeking so do not update the stream.
+ // Revert changes in the frequency table.
+ for (int i = 0; i < num_symbols; ++i) {
+ const uint32_t symbol = symbols[i];
+ frequencies_[symbol]--;
+ }
+ }
+ return ret_data;
+}
+
+int64_t ShannonEntropyTracker::GetNumberOfDataBits(
+ const EntropyData &entropy_data) {
+ if (entropy_data.num_values < 2)
+ return 0;
+ // We need to compute the number of bits required to represent the stream
+ // using the entropy norm. Note that:
+ //
+ // entropy = log2(num_values) - entropy_norm / num_values
+ //
+ // and number of bits required for the entropy is: num_values * entropy
+ //
+ return static_cast<int64_t>(
+ ceil(entropy_data.num_values * std::log2(entropy_data.num_values) -
+ entropy_data.entropy_norm));
+}
+
+int64_t ShannonEntropyTracker::GetNumberOfRAnsTableBits(
+ const EntropyData &entropy_data) {
+ return ApproximateRAnsFrequencyTableBits(entropy_data.max_symbol + 1,
+ entropy_data.num_unique_symbols);
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/entropy/shannon_entropy.h b/extern/draco/dracoenc/src/draco/compression/entropy/shannon_entropy.h
new file mode 100644
index 00000000000..85165f4cb8d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/entropy/shannon_entropy.h
@@ -0,0 +1,110 @@
+// 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_COMPRESSION_ENTROPY_SHANNON_ENTROPY_H_
+#define DRACO_COMPRESSION_ENTROPY_SHANNON_ENTROPY_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+namespace draco {
+
+// Computes an approximate Shannon entropy of symbols stored in the provided
+// input array |symbols|. The entropy corresponds to the number of bits that is
+// required to represent/store all the symbols using an optimal entropy coding
+// algorithm. See for example "A mathematical theory of communication" by
+// Shannon'48 (http://ieeexplore.ieee.org/document/6773024/).
+//
+// |max_value| is a required input that define the maximum value in the input
+// |symbols| array.
+//
+// |out_num_unique_symbols| is an optional output argument that stores the
+// number of unique symbols contained within the |symbols| array.
+// TODO(ostava): This should be renamed or the return value should be changed to
+// return the actual entropy and not the number of bits needed to represent the
+// input symbols.
+int64_t ComputeShannonEntropy(const uint32_t *symbols, int num_symbols,
+ int max_value, int *out_num_unique_symbols);
+
+// Computes the Shannon entropy of |num_values| Boolean entries, where
+// |num_true_values| are set to true.
+// Returns entropy between 0-1.
+double ComputeBinaryShannonEntropy(uint32_t num_values,
+ uint32_t num_true_values);
+
+// Class that can be used to keep track of the Shannon entropy on streamed data.
+// As new symbols are pushed to the tracker, the entropy is automatically
+// recomputed. The class also support recomputing the entropy without actually
+// pushing the symbols to the tracker through the Peek() method.
+class ShannonEntropyTracker {
+ public:
+ ShannonEntropyTracker();
+
+ // Struct for holding entropy data about the symbols added to the tracker.
+ // It can be used to compute the number of bits needed to store the data using
+ // the method:
+ // ShannonEntropyTracker::GetNumberOfDataBits(entropy_data);
+ // or to compute the approximate size of the frequency table needed by the
+ // rans coding using method:
+ // ShannonEntropyTracker::GetNumberOfRAnsTableBits(entropy_data);
+ struct EntropyData {
+ double entropy_norm;
+ int num_values;
+ int max_symbol;
+ int num_unique_symbols;
+ EntropyData()
+ : entropy_norm(0.0),
+ num_values(0),
+ max_symbol(0),
+ num_unique_symbols(0) {}
+ };
+
+ // Adds new symbols to the tracker and recomputes the entropy accordingly.
+ EntropyData Push(const uint32_t *symbols, int num_symbols);
+
+ // Returns new entropy data for the tracker as if |symbols| were added to the
+ // tracker without actually changing the status of the tracker.
+ EntropyData Peek(const uint32_t *symbols, int num_symbols);
+
+ // Gets the number of bits needed for encoding symbols added to the tracker.
+ int64_t GetNumberOfDataBits() const {
+ return GetNumberOfDataBits(entropy_data_);
+ }
+
+ // Gets the number of bits needed for encoding frequency table using the rans
+ // encoder.
+ int64_t GetNumberOfRAnsTableBits() const {
+ return GetNumberOfRAnsTableBits(entropy_data_);
+ }
+
+ // Gets the number of bits needed for encoding given |entropy_data|.
+ static int64_t GetNumberOfDataBits(const EntropyData &entropy_data);
+
+ // Gets the number of bits needed for encoding frequency table using the rans
+ // encoder for the given |entropy_data|.
+ static int64_t GetNumberOfRAnsTableBits(const EntropyData &entropy_data);
+
+ private:
+ EntropyData UpdateSymbols(const uint32_t *symbols, int num_symbols,
+ bool push_changes);
+
+ std::vector<int32_t> frequencies_;
+
+ EntropyData entropy_data_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ENTROPY_SHANNON_ENTROPY_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/entropy/shannon_entropy_test.cc b/extern/draco/dracoenc/src/draco/compression/entropy/shannon_entropy_test.cc
new file mode 100644
index 00000000000..c2fe64440be
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/entropy/shannon_entropy_test.cc
@@ -0,0 +1,56 @@
+#include "draco/compression/entropy/shannon_entropy.h"
+#include "draco/core/draco_test_base.h"
+
+namespace {
+
+TEST(ShannonEntropyTest, TestBinaryEntropy) {
+ // Test verifies that computing binary entropy works as expected.
+ ASSERT_EQ(draco::ComputeBinaryShannonEntropy(0, 0), 0);
+ ASSERT_EQ(draco::ComputeBinaryShannonEntropy(10, 0), 0);
+ ASSERT_EQ(draco::ComputeBinaryShannonEntropy(10, 10), 0);
+ ASSERT_NEAR(draco::ComputeBinaryShannonEntropy(10, 5), 1.0, 1e-4);
+}
+
+TEST(ShannonEntropyTest, TestStreamEntropy) {
+ // Test verifies that the entropy of streamed data is computed correctly.
+ const std::vector<uint32_t> symbols = {1, 5, 1, 100, 2, 1};
+
+ draco::ShannonEntropyTracker entropy_tracker;
+
+ // Nothing added, 0 entropy.
+ ASSERT_EQ(entropy_tracker.GetNumberOfDataBits(), 0);
+
+ // Try to push symbols one by one.
+ uint32_t max_symbol = 0;
+ for (int i = 0; i < symbols.size(); ++i) {
+ if (symbols[i] > max_symbol)
+ max_symbol = symbols[i];
+ const auto entropy_data = entropy_tracker.Push(&symbols[i], 1);
+
+ const int64_t stream_entropy_bits = entropy_tracker.GetNumberOfDataBits();
+ // Ensure the returned entropy_data is in sync with the stream.
+ ASSERT_EQ(draco::ShannonEntropyTracker::GetNumberOfDataBits(entropy_data),
+ stream_entropy_bits);
+
+ // Make sure the entropy is approximately the same as the one we compute
+ // directly from all symbols.
+ const int64_t expected_entropy_bits = draco::ComputeShannonEntropy(
+ symbols.data(), i + 1, max_symbol, nullptr);
+
+ // For now hardcoded tolerance of 2 bits.
+ ASSERT_NEAR(expected_entropy_bits, stream_entropy_bits, 2);
+ }
+
+ // Compare it also to the case when we add all symbols in one call.
+ draco::ShannonEntropyTracker entropy_tracker_2;
+ entropy_tracker_2.Push(symbols.data(), symbols.size());
+ const int64_t stream_2_entropy_bits = entropy_tracker_2.GetNumberOfDataBits();
+ ASSERT_EQ(entropy_tracker.GetNumberOfDataBits(), stream_2_entropy_bits);
+
+ // Ensure that peeking does not change the entropy.
+ entropy_tracker_2.Peek(symbols.data(), 1);
+
+ ASSERT_EQ(stream_2_entropy_bits, entropy_tracker_2.GetNumberOfDataBits());
+}
+
+} // namespace
diff --git a/extern/draco/dracoenc/src/draco/compression/entropy/symbol_coding_test.cc b/extern/draco/dracoenc/src/draco/compression/entropy/symbol_coding_test.cc
new file mode 100644
index 00000000000..ba7166bbe75
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/entropy/symbol_coding_test.cc
@@ -0,0 +1,170 @@
+// 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 "draco/compression/config/compression_shared.h"
+#include "draco/compression/entropy/symbol_decoding.h"
+#include "draco/compression/entropy/symbol_encoding.h"
+#include "draco/core/bit_utils.h"
+#include "draco/core/decoder_buffer.h"
+#include "draco/core/draco_test_base.h"
+#include "draco/core/encoder_buffer.h"
+
+namespace draco {
+
+class SymbolCodingTest : public ::testing::Test {
+ protected:
+ SymbolCodingTest() : bitstream_version_(kDracoMeshBitstreamVersion) {}
+
+ template <class SignedIntTypeT>
+ void TestConvertToSymbolAndBack(SignedIntTypeT x) {
+ typedef typename std::make_unsigned<SignedIntTypeT>::type Symbol;
+ Symbol symbol = ConvertSignedIntToSymbol(x);
+ SignedIntTypeT y = ConvertSymbolToSignedInt(symbol);
+ ASSERT_EQ(x, y);
+ }
+
+ uint16_t bitstream_version_;
+};
+
+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, nullptr, &eb));
+
+ std::vector<uint32_t> out;
+ out.resize(num_values);
+ DecoderBuffer db;
+ db.Init(eb.data(), eb.size());
+ db.set_bitstream_version(bitstream_version_);
+ 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<uint32_t, uint32_t> in[] = {
+ {12, 1500}, {1025, 31000}, {7, 1}, {9, 5}, {0, 6432}};
+
+ const int num_pairs = sizeof(in) / sizeof(std::pair<uint32_t, uint32_t>);
+
+ std::vector<uint32_t> in_values;
+ for (int i = 0; i < num_pairs; ++i) {
+ in_values.insert(in_values.end(), in[i].second, in[i].first);
+ }
+ for (int method = 0; method < NUM_SYMBOL_CODING_METHODS; ++method) {
+ // Test the encoding using all available symbol coding methods.
+ Options options;
+ SetSymbolEncodingMethod(&options, static_cast<SymbolCodingMethod>(method));
+
+ EncoderBuffer eb;
+ ASSERT_TRUE(
+ EncodeSymbols(in_values.data(), in_values.size(), 1, &options, &eb));
+ std::vector<uint32_t> out_values;
+ out_values.resize(in_values.size());
+ DecoderBuffer db;
+ db.Init(eb.data(), eb.size());
+ db.set_bitstream_version(bitstream_version_);
+ 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, nullptr, &eb));
+ DecoderBuffer db;
+ db.Init(eb.data(), eb.size());
+ db.set_bitstream_version(bitstream_version_);
+ 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<uint32_t> in(1200, 0);
+ ASSERT_TRUE(EncodeSymbols(in.data(), in.size(), 1, nullptr, &eb));
+
+ std::vector<uint32_t> out(in.size());
+ DecoderBuffer db;
+ db.Init(eb.data(), eb.size());
+ db.set_bitstream_version(bitstream_version_);
+ 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, TestBitLengths) {
+ // This test verifies that SymbolCoding successfully encodes symbols of
+ // various bit lengths
+ EncoderBuffer eb;
+ std::vector<uint32_t> in;
+ constexpr int bit_lengths = 18;
+ for (int i = 0; i < bit_lengths; ++i) {
+ in.push_back(1 << i);
+ }
+ std::vector<uint32_t> out(in.size());
+ for (int i = 0; i < bit_lengths; ++i) {
+ eb.Clear();
+ ASSERT_TRUE(EncodeSymbols(in.data(), i + 1, 1, nullptr, &eb));
+ DecoderBuffer db;
+ db.Init(eb.data(), eb.size());
+ db.set_bitstream_version(bitstream_version_);
+ ASSERT_TRUE(DecodeSymbols(i + 1, 1, &db, &out[0]));
+ for (int j = 0; j < i + 1; ++j) {
+ ASSERT_EQ(in[j], out[j]);
+ }
+ }
+}
+
+TEST_F(SymbolCodingTest, TestLargeNumberCondition) {
+ // This test verifies that SymbolCoding successfully encodes large symbols
+ // that are on the boundary between raw scheme and tagged scheme (18 bits).
+ EncoderBuffer eb;
+ constexpr int num_symbols = 1000000;
+ const std::vector<uint32_t> in(num_symbols, 1 << 18);
+ ASSERT_TRUE(EncodeSymbols(in.data(), in.size(), 1, nullptr, &eb));
+
+ std::vector<uint32_t> out(in.size());
+ DecoderBuffer db;
+ db.Init(eb.data(), eb.size());
+ db.set_bitstream_version(bitstream_version_);
+ 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, TestConversionFullRange) {
+ TestConvertToSymbolAndBack(static_cast<int8_t>(-128));
+ TestConvertToSymbolAndBack(static_cast<int8_t>(-127));
+ TestConvertToSymbolAndBack(static_cast<int8_t>(-1));
+ TestConvertToSymbolAndBack(static_cast<int8_t>(0));
+ TestConvertToSymbolAndBack(static_cast<int8_t>(1));
+ TestConvertToSymbolAndBack(static_cast<int8_t>(127));
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/entropy/symbol_decoding.cc b/extern/draco/dracoenc/src/draco/compression/entropy/symbol_decoding.cc
new file mode 100644
index 00000000000..39a1eb0ff24
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/entropy/symbol_decoding.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 "draco/compression/entropy/symbol_decoding.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "draco/compression/entropy/rans_symbol_decoder.h"
+
+namespace draco {
+
+template <template <int> class SymbolDecoderT>
+bool DecodeTaggedSymbols(uint32_t num_values, int num_components,
+ DecoderBuffer *src_buffer, uint32_t *out_values);
+
+template <template <int> class SymbolDecoderT>
+bool DecodeRawSymbols(uint32_t num_values, DecoderBuffer *src_buffer,
+ uint32_t *out_values);
+
+bool DecodeSymbols(uint32_t num_values, int num_components,
+ DecoderBuffer *src_buffer, uint32_t *out_values) {
+ if (num_values == 0)
+ return true;
+ // Decode which scheme to use.
+ uint8_t scheme;
+ if (!src_buffer->Decode(&scheme))
+ return false;
+ if (scheme == SYMBOL_CODING_TAGGED) {
+ return DecodeTaggedSymbols<RAnsSymbolDecoder>(num_values, num_components,
+ src_buffer, out_values);
+ } else if (scheme == SYMBOL_CODING_RAW) {
+ return DecodeRawSymbols<RAnsSymbolDecoder>(num_values, src_buffer,
+ out_values);
+ }
+ return false;
+}
+
+template <template <int> class SymbolDecoderT>
+bool DecodeTaggedSymbols(uint32_t num_values, int num_components,
+ DecoderBuffer *src_buffer, uint32_t *out_values) {
+ // Decode the encoded data.
+ SymbolDecoderT<5> tag_decoder;
+ if (!tag_decoder.Create(src_buffer))
+ return false;
+
+ if (!tag_decoder.StartDecoding(src_buffer))
+ return false;
+
+ if (num_values > 0 && tag_decoder.num_symbols() == 0)
+ return false; // Wrong number of symbols.
+
+ // src_buffer now points behind the encoded tag data (to the place where the
+ // values are encoded).
+ src_buffer->StartBitDecoding(false, nullptr);
+ int value_id = 0;
+ for (uint32_t i = 0; i < num_values; i += num_components) {
+ // Decode the tag.
+ const int bit_length = tag_decoder.DecodeSymbol();
+ // Decode the actual value.
+ for (int j = 0; j < num_components; ++j) {
+ uint32_t val;
+ if (!src_buffer->DecodeLeastSignificantBits32(bit_length, &val))
+ return false;
+ out_values[value_id++] = val;
+ }
+ }
+ tag_decoder.EndDecoding();
+ src_buffer->EndBitDecoding();
+ return true;
+}
+
+template <class SymbolDecoderT>
+bool DecodeRawSymbolsInternal(uint32_t num_values, DecoderBuffer *src_buffer,
+ uint32_t *out_values) {
+ SymbolDecoderT decoder;
+ if (!decoder.Create(src_buffer))
+ return false;
+
+ if (num_values > 0 && decoder.num_symbols() == 0)
+ return false; // Wrong number of symbols.
+
+ if (!decoder.StartDecoding(src_buffer))
+ return false;
+ for (uint32_t i = 0; i < num_values; ++i) {
+ // Decode a symbol into the value.
+ const uint32_t value = decoder.DecodeSymbol();
+ out_values[i] = value;
+ }
+ decoder.EndDecoding();
+ return true;
+}
+
+template <template <int> class SymbolDecoderT>
+bool DecodeRawSymbols(uint32_t num_values, DecoderBuffer *src_buffer,
+ uint32_t *out_values) {
+ uint8_t max_bit_length;
+ if (!src_buffer->Decode(&max_bit_length))
+ return false;
+ switch (max_bit_length) {
+ case 1:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<1>>(num_values, src_buffer,
+ out_values);
+ case 2:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<2>>(num_values, src_buffer,
+ out_values);
+ case 3:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<3>>(num_values, src_buffer,
+ out_values);
+ case 4:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<4>>(num_values, src_buffer,
+ out_values);
+ case 5:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<5>>(num_values, src_buffer,
+ out_values);
+ case 6:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<6>>(num_values, src_buffer,
+ out_values);
+ case 7:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<7>>(num_values, src_buffer,
+ out_values);
+ case 8:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<8>>(num_values, src_buffer,
+ out_values);
+ case 9:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<9>>(num_values, src_buffer,
+ out_values);
+ case 10:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<10>>(
+ num_values, src_buffer, out_values);
+ case 11:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<11>>(
+ num_values, src_buffer, out_values);
+ case 12:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<12>>(
+ num_values, src_buffer, out_values);
+ case 13:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<13>>(
+ num_values, src_buffer, out_values);
+ case 14:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<14>>(
+ num_values, src_buffer, out_values);
+ case 15:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<15>>(
+ num_values, src_buffer, out_values);
+ case 16:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<16>>(
+ num_values, src_buffer, out_values);
+ case 17:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<17>>(
+ num_values, src_buffer, out_values);
+ case 18:
+ return DecodeRawSymbolsInternal<SymbolDecoderT<18>>(
+ num_values, src_buffer, out_values);
+ default:
+ return false;
+ }
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/entropy/symbol_decoding.h b/extern/draco/dracoenc/src/draco/compression/entropy/symbol_decoding.h
new file mode 100644
index 00000000000..ea11165c347
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/entropy/symbol_decoding.h
@@ -0,0 +1,29 @@
+// 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_COMPRESSION_ENTROPY_SYMBOL_DECODING_H_
+#define DRACO_COMPRESSION_ENTROPY_SYMBOL_DECODING_H_
+
+#include "draco/core/decoder_buffer.h"
+
+namespace draco {
+
+// Decodes an array of symbols that was previously encoded with an entropy code.
+// Returns false on error.
+bool DecodeSymbols(uint32_t num_values, int num_components,
+ DecoderBuffer *src_buffer, uint32_t *out_values);
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ENTROPY_SYMBOL_DECODING_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/entropy/symbol_encoding.cc b/extern/draco/dracoenc/src/draco/compression/entropy/symbol_encoding.cc
new file mode 100644
index 00000000000..9a5868c40a5
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/entropy/symbol_encoding.cc
@@ -0,0 +1,370 @@
+// 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 "draco/compression/entropy/symbol_encoding.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "draco/compression/entropy/rans_symbol_encoder.h"
+#include "draco/compression/entropy/shannon_entropy.h"
+#include "draco/core/bit_utils.h"
+#include "draco/core/macros.h"
+
+namespace draco {
+
+constexpr int32_t kMaxTagSymbolBitLength = 32;
+constexpr int kMaxRawEncodingBitLength = 18;
+constexpr int kDefaultSymbolCodingCompressionLevel = 7;
+
+typedef uint64_t TaggedBitLengthFrequencies[kMaxTagSymbolBitLength];
+
+void SetSymbolEncodingMethod(Options *options, SymbolCodingMethod method) {
+ options->SetInt("symbol_encoding_method", method);
+}
+
+bool SetSymbolEncodingCompressionLevel(Options *options,
+ int compression_level) {
+ if (compression_level < 0 || compression_level > 10)
+ return false;
+ options->SetInt("symbol_encoding_compression_level", compression_level);
+ return true;
+}
+
+// Computes bit lengths of the input values. If num_components > 1, the values
+// are processed in "num_components" sized chunks and the bit length is always
+// computed for the largest value from the chunk.
+static void ComputeBitLengths(const uint32_t *symbols, int num_values,
+ int num_components,
+ std::vector<uint32_t> *out_bit_lengths,
+ uint32_t *out_max_value) {
+ out_bit_lengths->reserve(num_values);
+ *out_max_value = 0;
+ // Maximum integer value across all components.
+ for (int i = 0; i < num_values; i += num_components) {
+ // Get the maximum value for a given entry across all attribute components.
+ uint32_t max_component_value = symbols[i];
+ for (int j = 1; j < num_components; ++j) {
+ if (max_component_value < symbols[i + j])
+ max_component_value = symbols[i + j];
+ }
+ int value_msb_pos = 0;
+ if (max_component_value > 0) {
+ value_msb_pos = MostSignificantBit(max_component_value);
+ }
+ if (max_component_value > *out_max_value) {
+ *out_max_value = max_component_value;
+ }
+ out_bit_lengths->push_back(value_msb_pos + 1);
+ }
+}
+
+static int64_t ApproximateTaggedSchemeBits(
+ const std::vector<uint32_t> bit_lengths, int num_components) {
+ // Compute the total bit length used by all values (the length of data encode
+ // after tags).
+ uint64_t total_bit_length = 0;
+ for (size_t i = 0; i < bit_lengths.size(); ++i) {
+ total_bit_length += bit_lengths[i];
+ }
+ // Compute the number of entropy bits for tags.
+ int num_unique_symbols;
+ const int64_t tag_bits = ComputeShannonEntropy(
+ bit_lengths.data(), static_cast<int>(bit_lengths.size()), 32,
+ &num_unique_symbols);
+ const int64_t tag_table_bits =
+ ApproximateRAnsFrequencyTableBits(num_unique_symbols, num_unique_symbols);
+ return tag_bits + tag_table_bits + total_bit_length * num_components;
+}
+
+static int64_t ApproximateRawSchemeBits(const uint32_t *symbols,
+ int num_symbols, uint32_t max_value,
+ int *out_num_unique_symbols) {
+ int num_unique_symbols;
+ const int64_t data_bits = ComputeShannonEntropy(
+ symbols, num_symbols, max_value, &num_unique_symbols);
+ const int64_t table_bits =
+ ApproximateRAnsFrequencyTableBits(max_value, num_unique_symbols);
+ *out_num_unique_symbols = num_unique_symbols;
+ return table_bits + data_bits;
+}
+
+template <template <int> class SymbolEncoderT>
+bool EncodeTaggedSymbols(const uint32_t *symbols, int num_values,
+ int num_components,
+ const std::vector<uint32_t> &bit_lengths,
+ EncoderBuffer *target_buffer);
+
+template <template <int> class SymbolEncoderT>
+bool EncodeRawSymbols(const uint32_t *symbols, int num_values,
+ uint32_t max_entry_value, int32_t num_unique_symbols,
+ const Options *options, EncoderBuffer *target_buffer);
+
+bool EncodeSymbols(const uint32_t *symbols, int num_values, int num_components,
+ const Options *options, EncoderBuffer *target_buffer) {
+ if (num_values < 0)
+ return false;
+ if (num_values == 0)
+ return true;
+ if (num_components <= 0)
+ num_components = 1;
+ std::vector<uint32_t> bit_lengths;
+ uint32_t max_value;
+ ComputeBitLengths(symbols, num_values, num_components, &bit_lengths,
+ &max_value);
+
+ // Approximate number of bits needed for storing the symbols using the tagged
+ // scheme.
+ const int64_t tagged_scheme_total_bits =
+ ApproximateTaggedSchemeBits(bit_lengths, num_components);
+
+ // Approximate number of bits needed for storing the symbols using the raw
+ // scheme.
+ int num_unique_symbols = 0;
+ const int64_t raw_scheme_total_bits = ApproximateRawSchemeBits(
+ symbols, num_values, max_value, &num_unique_symbols);
+
+ // The maximum bit length of a single entry value that we can encode using
+ // the raw scheme.
+ const int max_value_bit_length =
+ MostSignificantBit(std::max(1u, max_value)) + 1;
+
+ int method = -1;
+ if (options != nullptr && options->IsOptionSet("symbol_encoding_method")) {
+ method = options->GetInt("symbol_encoding_method");
+ } else {
+ if (tagged_scheme_total_bits < raw_scheme_total_bits ||
+ max_value_bit_length > kMaxRawEncodingBitLength) {
+ method = SYMBOL_CODING_TAGGED;
+ } else {
+ method = SYMBOL_CODING_RAW;
+ }
+ }
+ // Use the tagged scheme.
+ target_buffer->Encode(static_cast<uint8_t>(method));
+ if (method == SYMBOL_CODING_TAGGED) {
+ return EncodeTaggedSymbols<RAnsSymbolEncoder>(
+ symbols, num_values, num_components, bit_lengths, target_buffer);
+ }
+ if (method == SYMBOL_CODING_RAW) {
+ return EncodeRawSymbols<RAnsSymbolEncoder>(symbols, num_values, max_value,
+ num_unique_symbols, options,
+ target_buffer);
+ }
+ // Unknown method selected.
+ return false;
+}
+
+template <template <int> class SymbolEncoderT>
+bool EncodeTaggedSymbols(const uint32_t *symbols, int num_values,
+ int num_components,
+ const std::vector<uint32_t> &bit_lengths,
+ EncoderBuffer *target_buffer) {
+ // Create entries for entropy coding. Each entry corresponds to a different
+ // number of bits that are necessary to encode a given value. Every value
+ // has at most 32 bits. Therefore, we need 32 different entries (for
+ // bit_length [1-32]). For each entry we compute the frequency of a given
+ // bit-length in our data set.
+ TaggedBitLengthFrequencies frequencies;
+ // Set frequency for each entry to zero.
+ memset(frequencies, 0, sizeof(frequencies));
+
+ // Compute the frequencies from input data.
+ // Maximum integer value for the values across all components.
+ for (size_t i = 0; i < bit_lengths.size(); ++i) {
+ // Update the frequency of the associated entry id.
+ ++frequencies[bit_lengths[i]];
+ }
+
+ // Create one extra buffer to store raw value.
+ EncoderBuffer value_buffer;
+ // Number of expected bits we need to store the values (can be optimized if
+ // needed).
+ const uint64_t value_bits =
+ kMaxTagSymbolBitLength * static_cast<uint64_t>(num_values);
+
+ // Create encoder for encoding the bit tags.
+ SymbolEncoderT<5> tag_encoder;
+ tag_encoder.Create(frequencies, kMaxTagSymbolBitLength, target_buffer);
+
+ // Start encoding bit tags.
+ tag_encoder.StartEncoding(target_buffer);
+
+ // Also start encoding the values.
+ value_buffer.StartBitEncoding(value_bits, false);
+
+ if (tag_encoder.needs_reverse_encoding()) {
+ // Encoder needs the values to be encoded in the reverse order.
+ for (int i = num_values - num_components; i >= 0; i -= num_components) {
+ const int bit_length = bit_lengths[i / num_components];
+ tag_encoder.EncodeSymbol(bit_length);
+
+ // Values are always encoded in the normal order
+ 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.EncodeLeastSignificantBits32(value_bit_length,
+ symbols[j + c]);
+ }
+ }
+ } else {
+ for (int i = 0; i < num_values; i += num_components) {
+ const int bit_length = bit_lengths[i / num_components];
+ // First encode the tag.
+ tag_encoder.EncodeSymbol(bit_length);
+ // Now encode all values using the stored bit_length.
+ for (int j = 0; j < num_components; ++j) {
+ value_buffer.EncodeLeastSignificantBits32(bit_length, symbols[i + j]);
+ }
+ }
+ }
+ tag_encoder.EndEncoding(target_buffer);
+ value_buffer.EndBitEncoding();
+
+ // Append the values to the end of the target buffer.
+ target_buffer->Encode(value_buffer.data(), value_buffer.size());
+ return true;
+}
+
+template <class SymbolEncoderT>
+bool EncodeRawSymbolsInternal(const uint32_t *symbols, int num_values,
+ uint32_t max_entry_value,
+ EncoderBuffer *target_buffer) {
+ // Count the frequency of each entry value.
+ std::vector<uint64_t> frequencies(max_entry_value + 1, 0);
+ for (int i = 0; i < num_values; ++i) {
+ ++frequencies[symbols[i]];
+ }
+
+ SymbolEncoderT encoder;
+ encoder.Create(frequencies.data(), static_cast<int>(frequencies.size()),
+ target_buffer);
+ encoder.StartEncoding(target_buffer);
+ // Encode all values.
+ if (SymbolEncoderT::needs_reverse_encoding()) {
+ for (int i = num_values - 1; i >= 0; --i) {
+ encoder.EncodeSymbol(symbols[i]);
+ }
+ } else {
+ for (int i = 0; i < num_values; ++i) {
+ encoder.EncodeSymbol(symbols[i]);
+ }
+ }
+ encoder.EndEncoding(target_buffer);
+ return true;
+}
+
+template <template <int> class SymbolEncoderT>
+bool EncodeRawSymbols(const uint32_t *symbols, int num_values,
+ uint32_t max_entry_value, int32_t num_unique_symbols,
+ const Options *options, EncoderBuffer *target_buffer) {
+ int symbol_bits = 0;
+ if (num_unique_symbols > 0) {
+ symbol_bits = MostSignificantBit(num_unique_symbols);
+ }
+ int unique_symbols_bit_length = symbol_bits + 1;
+ // Currently, we don't support encoding of more than 2^18 unique symbols.
+ if (unique_symbols_bit_length > kMaxRawEncodingBitLength)
+ return false;
+ int compression_level = kDefaultSymbolCodingCompressionLevel;
+ if (options != nullptr &&
+ options->IsOptionSet("symbol_encoding_compression_level")) {
+ compression_level = options->GetInt("symbol_encoding_compression_level");
+ }
+
+ // Adjust the bit_length based on compression level. Lower compression levels
+ // will use fewer bits while higher compression levels use more bits. Note
+ // that this is going to work for all valid bit_lengths because the actual
+ // number of bits allocated for rANS encoding is hard coded as:
+ // std::max(12, 3 * bit_length / 2) , therefore there will be always a
+ // sufficient number of bits available for all symbols.
+ // See ComputeRAnsPrecisionFromUniqueSymbolsBitLength() for the formula.
+ // This hardcoded equation cannot be changed without changing the bitstream.
+ if (compression_level < 4) {
+ unique_symbols_bit_length -= 2;
+ } else if (compression_level < 6) {
+ unique_symbols_bit_length -= 1;
+ } else if (compression_level > 9) {
+ unique_symbols_bit_length += 2;
+ } else if (compression_level > 7) {
+ unique_symbols_bit_length += 1;
+ }
+ // Clamp the bit_length to a valid range.
+ unique_symbols_bit_length = std::min(std::max(1, unique_symbols_bit_length),
+ kMaxRawEncodingBitLength);
+ target_buffer->Encode(static_cast<uint8_t>(unique_symbols_bit_length));
+ // Use appropriate symbol encoder based on the maximum symbol bit length.
+ switch (unique_symbols_bit_length) {
+ case 0:
+ FALLTHROUGH_INTENDED;
+ case 1:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<1>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 2:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<2>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 3:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<3>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 4:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<4>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 5:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<5>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 6:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<6>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 7:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<7>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 8:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<8>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 9:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<9>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 10:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<10>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 11:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<11>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 12:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<12>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 13:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<13>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 14:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<14>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 15:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<15>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 16:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<16>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 17:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<17>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ case 18:
+ return EncodeRawSymbolsInternal<SymbolEncoderT<18>>(
+ symbols, num_values, max_entry_value, target_buffer);
+ default:
+ return false;
+ }
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/entropy/symbol_encoding.h b/extern/draco/dracoenc/src/draco/compression/entropy/symbol_encoding.h
new file mode 100644
index 00000000000..839b28b17a5
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/entropy/symbol_encoding.h
@@ -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.
+//
+#ifndef DRACO_COMPRESSION_ENTROPY_SYMBOL_ENCODING_H_
+#define DRACO_COMPRESSION_ENTROPY_SYMBOL_ENCODING_H_
+
+#include "draco/compression/config/compression_shared.h"
+#include "draco/core/encoder_buffer.h"
+#include "draco/core/options.h"
+
+namespace draco {
+
+// Encodes an array of symbols using an entropy coding. This function
+// automatically decides whether to encode the symbol values using bit
+// length tags (see EncodeTaggedSymbols), or whether to encode them directly
+// (see EncodeRawSymbols). The symbols can be grouped into separate components
+// that can be used for better compression. |options| is an optional parameter
+// that allows more direct control over various stages of the symbol encoding
+// (see below for functions that are used to set valid options).
+// Returns false on error.
+bool EncodeSymbols(const uint32_t *symbols, int num_values, int num_components,
+ const Options *options, EncoderBuffer *target_buffer);
+
+// Sets an option that forces symbol encoder to use the specified encoding
+// method.
+void SetSymbolEncodingMethod(Options *options, SymbolCodingMethod method);
+
+// Sets the desired compression level for symbol encoding in range <0, 10> where
+// 0 is the worst but fastest compression and 10 is the best but slowest
+// compression. If the option is not set, default value of 7 is used.
+// Returns false if an invalid level has been set.
+bool SetSymbolEncodingCompressionLevel(Options *options, int compression_level);
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_ENTROPY_SYMBOL_ENCODING_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/expert_encode.cc b/extern/draco/dracoenc/src/draco/compression/expert_encode.cc
new file mode 100644
index 00000000000..f62d16630cb
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/expert_encode.cc
@@ -0,0 +1,171 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/compression/expert_encode.h"
+
+#include "draco/compression/mesh/mesh_edgebreaker_encoder.h"
+#include "draco/compression/mesh/mesh_sequential_encoder.h"
+#include "draco/compression/point_cloud/point_cloud_kd_tree_encoder.h"
+#include "draco/compression/point_cloud/point_cloud_sequential_encoder.h"
+
+namespace draco {
+
+ExpertEncoder::ExpertEncoder(const PointCloud &point_cloud)
+ : point_cloud_(&point_cloud), mesh_(nullptr) {}
+
+ExpertEncoder::ExpertEncoder(const Mesh &mesh)
+ : point_cloud_(&mesh), mesh_(&mesh) {}
+
+Status ExpertEncoder::EncodeToBuffer(EncoderBuffer *out_buffer) {
+ if (point_cloud_ == nullptr)
+ return Status(Status::ERROR, "Invalid input geometry.");
+ if (mesh_ == nullptr) {
+ return EncodePointCloudToBuffer(*point_cloud_, out_buffer);
+ }
+ return EncodeMeshToBuffer(*mesh_, out_buffer);
+}
+
+Status ExpertEncoder::EncodePointCloudToBuffer(const PointCloud &pc,
+ EncoderBuffer *out_buffer) {
+ std::unique_ptr<PointCloudEncoder> encoder;
+ const int encoding_method = options().GetGlobalInt("encoding_method", -1);
+
+ if (encoding_method == POINT_CLOUD_SEQUENTIAL_ENCODING) {
+ // Use sequential encoding if requested.
+ encoder.reset(new PointCloudSequentialEncoder());
+ } else if (encoding_method == -1 && options().GetSpeed() == 10) {
+ // Use sequential encoding if speed is at max.
+ encoder.reset(new PointCloudSequentialEncoder());
+ } else {
+ // Speed < 10, use POINT_CLOUD_KD_TREE_ENCODING if possible.
+ bool kd_tree_possible = true;
+ // Kd-Tree encoder can be currently used only when the following conditions
+ // are satisfied for all attributes:
+ // -data type is float32 and quantization is enabled, OR
+ // -data type is uint32, uint16, uint8 or int32, int16, int8
+ for (int i = 0; i < pc.num_attributes(); ++i) {
+ const PointAttribute *const att = pc.attribute(i);
+ if (kd_tree_possible && att->data_type() != DT_FLOAT32 &&
+ att->data_type() != DT_UINT32 && att->data_type() != DT_UINT16 &&
+ att->data_type() != DT_UINT8 && att->data_type() != DT_INT32 &&
+ att->data_type() != DT_INT16 && att->data_type() != DT_INT8)
+ kd_tree_possible = false;
+ if (kd_tree_possible && att->data_type() == DT_FLOAT32 &&
+ options().GetAttributeInt(0, "quantization_bits", -1) <= 0)
+ kd_tree_possible = false; // Quantization not enabled.
+ if (!kd_tree_possible)
+ break;
+ }
+
+ if (kd_tree_possible) {
+ // Create kD-tree encoder (all checks passed).
+ encoder.reset(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 Status(Status::ERROR, "Invalid encoding method.");
+ }
+ }
+ if (!encoder) {
+ // Default choice.
+ encoder.reset(new PointCloudSequentialEncoder());
+ }
+ encoder->SetPointCloud(pc);
+ DRACO_RETURN_IF_ERROR(encoder->Encode(options(), out_buffer));
+
+ set_num_encoded_points(encoder->num_encoded_points());
+ set_num_encoded_faces(0);
+ return OkStatus();
+}
+
+Status ExpertEncoder::EncodeMeshToBuffer(const Mesh &m,
+ EncoderBuffer *out_buffer) {
+ std::unique_ptr<MeshEncoder> encoder;
+ // Select the encoding method only based on the provided options.
+ int encoding_method = options().GetGlobalInt("encoding_method", -1);
+ if (encoding_method == -1) {
+ // For now select the edgebreaker for all options expect of speed 10
+ if (options().GetSpeed() == 10) {
+ encoding_method = MESH_SEQUENTIAL_ENCODING;
+ } else {
+ encoding_method = MESH_EDGEBREAKER_ENCODING;
+ }
+ }
+ if (encoding_method == MESH_EDGEBREAKER_ENCODING) {
+ encoder = std::unique_ptr<MeshEncoder>(new MeshEdgebreakerEncoder());
+ } else {
+ encoder = std::unique_ptr<MeshEncoder>(new MeshSequentialEncoder());
+ }
+ encoder->SetMesh(m);
+ DRACO_RETURN_IF_ERROR(encoder->Encode(options(), out_buffer));
+
+ set_num_encoded_points(encoder->num_encoded_points());
+ set_num_encoded_faces(encoder->num_encoded_faces());
+ return OkStatus();
+}
+
+void ExpertEncoder::Reset(const EncoderOptions &options) {
+ Base::Reset(options);
+}
+
+void ExpertEncoder::Reset() { Base::Reset(); }
+
+void ExpertEncoder::SetSpeedOptions(int encoding_speed, int decoding_speed) {
+ Base::SetSpeedOptions(encoding_speed, decoding_speed);
+}
+
+void ExpertEncoder::SetAttributeQuantization(int32_t attribute_id,
+ int quantization_bits) {
+ options().SetAttributeInt(attribute_id, "quantization_bits",
+ quantization_bits);
+}
+
+void ExpertEncoder::SetAttributeExplicitQuantization(int32_t attribute_id,
+ int quantization_bits,
+ int num_dims,
+ const float *origin,
+ float range) {
+ options().SetAttributeInt(attribute_id, "quantization_bits",
+ quantization_bits);
+ options().SetAttributeVector(attribute_id, "quantization_origin", num_dims,
+ origin);
+ options().SetAttributeFloat(attribute_id, "quantization_range", range);
+}
+
+void ExpertEncoder::SetUseBuiltInAttributeCompression(bool enabled) {
+ options().SetGlobalBool("use_built_in_attribute_compression", enabled);
+}
+
+void ExpertEncoder::SetEncodingMethod(int encoding_method) {
+ Base::SetEncodingMethod(encoding_method);
+}
+
+void ExpertEncoder::SetEncodingSubmethod(int encoding_submethod) {
+ Base::SetEncodingSubmethod(encoding_submethod);
+}
+
+Status ExpertEncoder::SetAttributePredictionScheme(
+ int32_t attribute_id, int prediction_scheme_method) {
+ auto att = point_cloud_->attribute(attribute_id);
+ auto att_type = att->attribute_type();
+ const Status status =
+ CheckPredictionScheme(att_type, prediction_scheme_method);
+ if (!status.ok())
+ return status;
+ options().SetAttributeInt(attribute_id, "prediction_scheme",
+ prediction_scheme_method);
+ return status;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/expert_encode.h b/extern/draco/dracoenc/src/draco/compression/expert_encode.h
new file mode 100644
index 00000000000..a1aa7b8b3a8
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/expert_encode.h
@@ -0,0 +1,147 @@
+// Copyright 2017 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_SRC_DRACO_COMPRESSION_EXPERT_ENCODE_H_
+#define DRACO_SRC_DRACO_COMPRESSION_EXPERT_ENCODE_H_
+
+#include "draco/compression/config/compression_shared.h"
+#include "draco/compression/config/encoder_options.h"
+#include "draco/compression/encode_base.h"
+#include "draco/core/encoder_buffer.h"
+#include "draco/core/status.h"
+#include "draco/mesh/mesh.h"
+
+namespace draco {
+
+// Advanced helper class for encoding geometry using the Draco compression
+// library. Unlike the basic Encoder (encode.h), this class allows users to
+// specify options for each attribute individually using provided attribute ids.
+// The drawback of this encoder is that it can be used to encode only one model
+// at a time, and for each new model the options need to be set again,
+class ExpertEncoder : public EncoderBase<EncoderOptions> {
+ public:
+ typedef EncoderBase<EncoderOptions> Base;
+ typedef EncoderOptions OptionsType;
+
+ explicit ExpertEncoder(const PointCloud &point_cloud);
+ explicit ExpertEncoder(const Mesh &mesh);
+
+ // Encodes the geometry provided in the constructor to the target buffer.
+ Status EncodeToBuffer(EncoderBuffer *out_buffer);
+
+ // Set encoder options used during the geometry encoding. Note that this call
+ // overwrites any modifications to the options done with the functions below.
+ void Reset(const EncoderOptions &options);
+ void Reset();
+
+ // Sets the desired encoding and decoding speed for the given options.
+ //
+ // 0 = slowest speed, but the best compression.
+ // 10 = fastest, but the worst compression.
+ // -1 = undefined.
+ //
+ // Note that both speed options affect the encoder choice of used methods and
+ // algorithms. For example, a requirement for fast decoding may prevent the
+ // encoder from using the best compression methods even if the encoding speed
+ // is set to 0. In general, the faster of the two options limits the choice of
+ // features that can be used by the encoder. Additionally, setting
+ // |decoding_speed| to be faster than the |encoding_speed| may allow the
+ // encoder to choose the optimal method out of the available features for the
+ // given |decoding_speed|.
+ void SetSpeedOptions(int encoding_speed, int decoding_speed);
+
+ // Sets the quantization compression options for a specific attribute. The
+ // attribute values will be quantized in a box defined by the maximum extent
+ // of the attribute values. I.e., the actual precision of this option depends
+ // on the scale of the attribute values.
+ void SetAttributeQuantization(int32_t attribute_id, int quantization_bits);
+
+ // Sets the explicit quantization compression for a named attribute. The
+ // attribute values will be quantized in a coordinate system defined by the
+ // provided origin and range (the input values should be within interval:
+ // <origin, origin + range>).
+ void SetAttributeExplicitQuantization(int32_t attribute_id,
+ int quantization_bits, int num_dims,
+ const float *origin, float range);
+
+ // Enables/disables built in entropy coding of attribute values. Disabling
+ // this option may be useful to improve the performance when third party
+ // compression is used on top of the Draco compression. Default: [true].
+ void SetUseBuiltInAttributeCompression(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 values defined in
+ // compression/config/compression_shared.h based on the type of the input
+ // geometry that is going to be encoded. For point clouds, allowed entries are
+ // POINT_CLOUD_SEQUENTIAL_ENCODING
+ // POINT_CLOUD_KD_TREE_ENCODING
+ //
+ // For meshes the input can be
+ // 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(int encoding_method);
+
+ // Sets the desired encoding submethod, only for MESH_EDGEBREAKER_ENCODING.
+ // Valid values for |encoding_submethod| are:
+ // MESH_EDGEBREAKER_STANDARD_ENCODING
+ // MESH_EDGEBREAKER_VALENCE_ENCODING
+ // see also compression/config/compression_shared.h.
+ void SetEncodingSubmethod(int encoding_submethod);
+
+ // 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). This function should be called only when a specific prediction is
+ // preferred (e.g., when it is known that the encoder would select a less
+ // optimal prediction for the given input data).
+ //
+ // |prediction_scheme_method| should be one of the entries defined in
+ // compression/config/compression_shared.h :
+ //
+ // PREDICTION_NONE - use no prediction.
+ // PREDICTION_DIFFERENCE - delta coding
+ // MESH_PREDICTION_PARALLELOGRAM - parallelogram prediction for meshes.
+ // MESH_PREDICTION_CONSTRAINED_PARALLELOGRAM
+ // - better and more costly version of the parallelogram prediction.
+ // MESH_PREDICTION_TEX_COORDS_PORTABLE
+ // - specialized predictor for tex coordinates.
+ // MESH_PREDICTION_GEOMETRIC_NORMAL
+ // - specialized predictor for normal coordinates.
+ //
+ // Note that in case the desired prediction cannot be used, the default
+ // prediction will be automatically used instead.
+ Status SetAttributePredictionScheme(int32_t attribute_id,
+ int prediction_scheme_method);
+
+ private:
+ Status EncodePointCloudToBuffer(const PointCloud &pc,
+ EncoderBuffer *out_buffer);
+
+ Status EncodeMeshToBuffer(const Mesh &m, EncoderBuffer *out_buffer);
+
+ const PointCloud *point_cloud_;
+ const Mesh *mesh_;
+};
+
+} // namespace draco
+
+#endif // DRACO_SRC_DRACO_COMPRESSION_EXPERT_ENCODE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_decoder.cc b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_decoder.cc
new file mode 100644
index 00000000000..01913dcd047
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_decoder.cc
@@ -0,0 +1,35 @@
+// 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 "draco/compression/mesh/mesh_decoder.h"
+
+namespace draco {
+
+MeshDecoder::MeshDecoder() : mesh_(nullptr) {}
+
+Status MeshDecoder::Decode(const DecoderOptions &options,
+ DecoderBuffer *in_buffer, Mesh *out_mesh) {
+ mesh_ = out_mesh;
+ return PointCloudDecoder::Decode(options, in_buffer, out_mesh);
+}
+
+bool MeshDecoder::DecodeGeometryData() {
+ if (mesh_ == nullptr)
+ return false;
+ if (!DecodeConnectivity())
+ return false;
+ return PointCloudDecoder::DecodeGeometryData();
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_decoder.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_decoder.h
new file mode 100644
index 00000000000..397a679d440
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_decoder.h
@@ -0,0 +1,68 @@
+// 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_COMPRESSION_MESH_MESH_DECODER_H_
+#define DRACO_COMPRESSION_MESH_MESH_DECODER_H_
+
+#include "draco/compression/attributes/mesh_attribute_indices_encoding_data.h"
+#include "draco/compression/point_cloud/point_cloud_decoder.h"
+#include "draco/mesh/mesh.h"
+#include "draco/mesh/mesh_attribute_corner_table.h"
+
+namespace draco {
+
+// Class that reconstructs a 3D mesh from input data that was encoded by
+// MeshEncoder.
+class MeshDecoder : public PointCloudDecoder {
+ public:
+ MeshDecoder();
+
+ EncodedGeometryType GetGeometryType() const override {
+ return TRIANGULAR_MESH;
+ }
+
+ // The main entry point for mesh decoding.
+ Status Decode(const DecoderOptions &options, DecoderBuffer *in_buffer,
+ Mesh *out_mesh);
+
+ // Returns the base connectivity of the decoded mesh (or nullptr if it is not
+ // initialized).
+ virtual const CornerTable *GetCornerTable() const { return nullptr; }
+
+ // Returns the attribute connectivity data or nullptr if it does not exist.
+ virtual const MeshAttributeCornerTable *GetAttributeCornerTable(
+ 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 {
+ return nullptr;
+ }
+
+ Mesh *mesh() const { return mesh_; }
+
+ protected:
+ bool DecodeGeometryData() override;
+ virtual bool DecodeConnectivity() = 0;
+
+ private:
+ Mesh *mesh_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_decoder_helpers.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_decoder_helpers.h
new file mode 100644
index 00000000000..12ac46b3698
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_decoder_helpers.h
@@ -0,0 +1,84 @@
+// 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_COMPRESSION_MESH_MESH_DECODER_HELPERS_H_
+#define DRACO_COMPRESSION_MESH_MESH_DECODER_HELPERS_H_
+
+#include "draco/compression/mesh/mesh_decoder.h"
+
+namespace draco {
+
+// Function for decoding a stream previously encoded by a MeshEncoder. The
+// result is stored into a stream of single precision floating point numbers
+// in a XYZ|UV format, where one value is stored for every corner of each
+// triangle.
+// On error, the function sets the input stream "is" to an invalid state.
+template <typename InStreamT>
+InStreamT &DecodePos3Tex2DataFromStream(InStreamT &&is,
+ std::vector<float> *out_data) {
+ // Determine the size of the encoded data and write it into a vector.
+ const auto start_pos = is.tellg();
+ is.seekg(0, std::ios::end);
+ const std::streampos is_size = is.tellg() - start_pos;
+ is.seekg(start_pos);
+ std::vector<char> data(is_size);
+ is.read(&data[0], is_size);
+
+ // Create a mesh from the data.
+ std::unique_ptr<Mesh> mesh = draco::DecodeMesh(&data[0], data.size());
+
+ if (mesh == nullptr) {
+ is.setstate(ios_base::badbit);
+ return is;
+ }
+
+ const PointAttribute *pos_att =
+ mesh->GetNamedAttribute(GeometryAttribute::POSITION);
+ const PointAttribute *tex_att =
+ mesh->GetNamedAttribute(GeometryAttribute::TEX_COORD_0);
+
+ // Both position and texture attributes must be present.
+ if (pos_att == nullptr || tex_att == nullptr) {
+ is.setstate(ios_base::badbit);
+ return is;
+ }
+
+ // Copy the mesh data into the provided output.
+ constexpr int data_stride = 5;
+ // Prepare the output storage for 3 output values per face.
+ out_data->resize(mesh->num_faces() * 3 * data_stride);
+
+ std::array<float, 3> pos_val;
+ std::array<float, 2> tex_val;
+ int out_it = 0;
+ for (int f = 0; f < mesh->num_faces(); ++f) {
+ const Mesh::Face &face = mesh->face(f);
+ for (int p = 0; p < 3; ++p) {
+ pos_att->ConvertValue<float, 3>(pos_att->mapped_index(face[p]),
+ &pos_val[0]);
+ memcpy(&out_data->at(0) + out_it, &pos_val[0], sizeof(pos_val));
+ out_it += 3;
+ tex_att->ConvertValue<float, 2>(tex_att->mapped_index(face[p]),
+ &tex_val[0]);
+ memcpy(&out_data->at(0) + out_it, &tex_val[0], sizeof(tex_val));
+ out_it += 2;
+ }
+ }
+
+ return is;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_DECODER_HELPERS_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder.cc b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder.cc
new file mode 100644
index 00000000000..e164f82b12b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder.cc
@@ -0,0 +1,67 @@
+// 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 "draco/compression/mesh/mesh_edgebreaker_decoder.h"
+#include "draco/compression/mesh/mesh_edgebreaker_decoder_impl.h"
+#include "draco/compression/mesh/mesh_edgebreaker_traversal_predictive_decoder.h"
+#include "draco/compression/mesh/mesh_edgebreaker_traversal_valence_decoder.h"
+
+namespace draco {
+
+MeshEdgebreakerDecoder::MeshEdgebreakerDecoder() {}
+
+bool MeshEdgebreakerDecoder::CreateAttributesDecoder(int32_t att_decoder_id) {
+ return impl_->CreateAttributesDecoder(att_decoder_id);
+}
+
+bool MeshEdgebreakerDecoder::InitializeDecoder() {
+ uint8_t traversal_decoder_type;
+ if (!buffer()->Decode(&traversal_decoder_type))
+ return false;
+ impl_ = nullptr;
+ if (traversal_decoder_type == MESH_EDGEBREAKER_STANDARD_ENCODING) {
+#ifdef DRACO_STANDARD_EDGEBREAKER_SUPPORTED
+ impl_ = std::unique_ptr<MeshEdgebreakerDecoderImplInterface>(
+ new MeshEdgebreakerDecoderImpl<MeshEdgebreakerTraversalDecoder>());
+#endif
+ } else if (traversal_decoder_type == MESH_EDGEBREAKER_PREDICTIVE_ENCODING) {
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+#ifdef DRACO_PREDICTIVE_EDGEBREAKER_SUPPORTED
+ impl_ = std::unique_ptr<MeshEdgebreakerDecoderImplInterface>(
+ new MeshEdgebreakerDecoderImpl<
+ MeshEdgebreakerTraversalPredictiveDecoder>());
+#endif
+#endif
+ } else if (traversal_decoder_type == MESH_EDGEBREAKER_VALENCE_ENCODING) {
+ impl_ = std::unique_ptr<MeshEdgebreakerDecoderImplInterface>(
+ new MeshEdgebreakerDecoderImpl<
+ MeshEdgebreakerTraversalValenceDecoder>());
+ }
+ if (!impl_) {
+ return false;
+ }
+ if (!impl_->Init(this))
+ return false;
+ return true;
+}
+
+bool MeshEdgebreakerDecoder::DecodeConnectivity() {
+ return impl_->DecodeConnectivity();
+}
+
+bool MeshEdgebreakerDecoder::OnAttributesDecoded() {
+ return impl_->OnAttributesDecoded();
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder.h
new file mode 100644
index 00000000000..c3569405220
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder.h
@@ -0,0 +1,55 @@
+// 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_COMPRESSION_MESH_MESH_EDGEBREAKER_DECODER_H_
+#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_DECODER_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/mesh/mesh_decoder.h"
+#include "draco/compression/mesh/mesh_edgebreaker_decoder_impl_interface.h"
+
+namespace draco {
+
+// Class for decoding data encoded by MeshEdgebreakerEncoder.
+class MeshEdgebreakerDecoder : public MeshDecoder {
+ public:
+ MeshEdgebreakerDecoder();
+
+ const CornerTable *GetCornerTable() const override {
+ return impl_->GetCornerTable();
+ }
+
+ const MeshAttributeCornerTable *GetAttributeCornerTable(
+ int att_id) const override {
+ return impl_->GetAttributeCornerTable(att_id);
+ }
+
+ const MeshAttributeIndicesEncodingData *GetAttributeEncodingData(
+ int att_id) const override {
+ return impl_->GetAttributeEncodingData(att_id);
+ }
+
+ protected:
+ bool InitializeDecoder() override;
+ bool CreateAttributesDecoder(int32_t att_decoder_id) override;
+ bool DecodeConnectivity() override;
+ bool OnAttributesDecoded() override;
+
+ std::unique_ptr<MeshEdgebreakerDecoderImplInterface> impl_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl.cc b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl.cc
new file mode 100644
index 00000000000..9b91a6f07c4
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl.cc
@@ -0,0 +1,1150 @@
+// 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 "draco/compression/mesh/mesh_edgebreaker_decoder_impl.h"
+
+#include <algorithm>
+
+#include "draco/compression/attributes/sequential_attribute_decoders_controller.h"
+#include "draco/compression/mesh/mesh_edgebreaker_decoder.h"
+#include "draco/compression/mesh/mesh_edgebreaker_traversal_predictive_decoder.h"
+#include "draco/compression/mesh/mesh_edgebreaker_traversal_valence_decoder.h"
+#include "draco/compression/mesh/traverser/depth_first_traverser.h"
+#include "draco/compression/mesh/traverser/max_prediction_degree_traverser.h"
+#include "draco/compression/mesh/traverser/mesh_attribute_indices_encoding_observer.h"
+#include "draco/compression/mesh/traverser/mesh_traversal_sequencer.h"
+#include "draco/compression/mesh/traverser/traverser_base.h"
+#include "draco/mesh/corner_table_iterators.h"
+
+namespace draco {
+
+// Types of "free" edges that are used during topology decoding.
+// A free edge is an edge that is connected to one face only.
+// All edge types are stored in the opposite_corner_id_ array, where each
+// edge "e" is uniquely identified by the opposite corner "C" in its parent
+// triangle:
+// *
+// /C\
+// / \
+// / e \
+// *-------*
+// For more description about how the edges are used, see comment inside
+// ZipConnectivity() method.
+
+template <class TraversalDecoder>
+MeshEdgebreakerDecoderImpl<TraversalDecoder>::MeshEdgebreakerDecoderImpl()
+ : decoder_(nullptr),
+ last_symbol_id_(-1),
+ last_vert_id_(-1),
+ last_face_id_(-1),
+ num_new_vertices_(0),
+ num_encoded_vertices_(0),
+ pos_data_decoder_id_(-1) {}
+
+template <class TraversalDecoder>
+bool MeshEdgebreakerDecoderImpl<TraversalDecoder>::Init(
+ MeshEdgebreakerDecoder *decoder) {
+ decoder_ = decoder;
+ return true;
+}
+
+template <class TraversalDecoder>
+const MeshAttributeCornerTable *
+MeshEdgebreakerDecoderImpl<TraversalDecoder>::GetAttributeCornerTable(
+ int att_id) const {
+ for (uint32_t i = 0; i < attribute_data_.size(); ++i) {
+ const int decoder_id = attribute_data_[i].decoder_id;
+ if (decoder_id < 0 || decoder_id >= decoder_->num_attributes_decoders())
+ continue;
+ const AttributesDecoderInterface *const dec =
+ decoder_->attributes_decoder(decoder_id);
+ for (int j = 0; j < dec->GetNumAttributes(); ++j) {
+ if (dec->GetAttributeId(j) == att_id) {
+ if (attribute_data_[i].is_connectivity_used)
+ return &attribute_data_[i].connectivity_data;
+ return nullptr;
+ }
+ }
+ }
+ return nullptr;
+}
+
+template <class TraversalDecoder>
+const MeshAttributeIndicesEncodingData *
+MeshEdgebreakerDecoderImpl<TraversalDecoder>::GetAttributeEncodingData(
+ int att_id) const {
+ for (uint32_t i = 0; i < attribute_data_.size(); ++i) {
+ const int decoder_id = attribute_data_[i].decoder_id;
+ if (decoder_id < 0 || decoder_id >= decoder_->num_attributes_decoders())
+ continue;
+ const AttributesDecoderInterface *const dec =
+ decoder_->attributes_decoder(decoder_id);
+ for (int j = 0; j < dec->GetNumAttributes(); ++j) {
+ if (dec->GetAttributeId(j) == att_id)
+ return &attribute_data_[i].encoding_data;
+ }
+ }
+ return &pos_encoding_data_;
+}
+
+template <class TraversalDecoder>
+template <class TraverserT>
+std::unique_ptr<PointsSequencer>
+MeshEdgebreakerDecoderImpl<TraversalDecoder>::CreateVertexTraversalSequencer(
+ MeshAttributeIndicesEncodingData *encoding_data) {
+ typedef typename TraverserT::TraversalObserver AttObserver;
+ typedef typename TraverserT::CornerTable CornerTable;
+
+ const Mesh *mesh = decoder_->mesh();
+ std::unique_ptr<MeshTraversalSequencer<TraverserT>> traversal_sequencer(
+ new MeshTraversalSequencer<TraverserT>(mesh, encoding_data));
+
+ AttObserver att_observer(corner_table_.get(), mesh, traversal_sequencer.get(),
+ encoding_data);
+
+ TraverserT att_traverser;
+ att_traverser.Init(corner_table_.get(), att_observer);
+
+ traversal_sequencer->SetTraverser(att_traverser);
+ return std::move(traversal_sequencer);
+}
+
+template <class TraversalDecoder>
+bool MeshEdgebreakerDecoderImpl<TraversalDecoder>::CreateAttributesDecoder(
+ int32_t att_decoder_id) {
+ int8_t att_data_id;
+ if (!decoder_->buffer()->Decode(&att_data_id))
+ return false;
+ uint8_t decoder_type;
+ if (!decoder_->buffer()->Decode(&decoder_type))
+ return false;
+
+ if (att_data_id >= 0) {
+ if (att_data_id >= attribute_data_.size()) {
+ return false; // Unexpected attribute data.
+ }
+
+ // Ensure that the attribute data is not mapped to a different attributes
+ // decoder already.
+ if (attribute_data_[att_data_id].decoder_id >= 0)
+ return false;
+
+ attribute_data_[att_data_id].decoder_id = att_decoder_id;
+ } else {
+ // Assign the attributes decoder to |pos_encoding_data_|.
+ if (pos_data_decoder_id_ >= 0)
+ return false; // Some other decoder is already using the data. Error.
+ pos_data_decoder_id_ = att_decoder_id;
+ }
+
+ MeshTraversalMethod traversal_method = MESH_TRAVERSAL_DEPTH_FIRST;
+ if (decoder_->bitstream_version() >= DRACO_BITSTREAM_VERSION(1, 2)) {
+ uint8_t traversal_method_encoded;
+ if (!decoder_->buffer()->Decode(&traversal_method_encoded))
+ return false;
+ traversal_method =
+ static_cast<MeshTraversalMethod>(traversal_method_encoded);
+ }
+
+ const Mesh *mesh = decoder_->mesh();
+ std::unique_ptr<PointsSequencer> sequencer;
+
+ if (decoder_type == MESH_VERTEX_ATTRIBUTE) {
+ // Per-vertex attribute decoder.
+
+ MeshAttributeIndicesEncodingData *encoding_data = nullptr;
+ if (att_data_id < 0) {
+ encoding_data = &pos_encoding_data_;
+ } else {
+ encoding_data = &attribute_data_[att_data_id].encoding_data;
+ // Mark the attribute connectivity data invalid to ensure it's not used
+ // later on.
+ attribute_data_[att_data_id].is_connectivity_used = false;
+ }
+ // Defining sequencer via a traversal scheme.
+ if (traversal_method == MESH_TRAVERSAL_PREDICTION_DEGREE) {
+ typedef MeshAttributeIndicesEncodingObserver<CornerTable> AttObserver;
+ typedef MaxPredictionDegreeTraverser<CornerTable, AttObserver>
+ AttTraverser;
+ sequencer = CreateVertexTraversalSequencer<AttTraverser>(encoding_data);
+ } else if (traversal_method == MESH_TRAVERSAL_DEPTH_FIRST) {
+ typedef MeshAttributeIndicesEncodingObserver<CornerTable> AttObserver;
+ typedef DepthFirstTraverser<CornerTable, AttObserver> AttTraverser;
+ sequencer = CreateVertexTraversalSequencer<AttTraverser>(encoding_data);
+ } else {
+ return false; // Unsupported method
+ }
+ } else {
+ if (traversal_method != MESH_TRAVERSAL_DEPTH_FIRST)
+ return false; // Unsupported method.
+ if (att_data_id < 0)
+ return false; // Attribute data must be specified.
+
+ // Per-corner attribute decoder.
+
+ typedef MeshAttributeIndicesEncodingObserver<MeshAttributeCornerTable>
+ AttObserver;
+ typedef DepthFirstTraverser<MeshAttributeCornerTable, AttObserver>
+ AttTraverser;
+
+ MeshAttributeIndicesEncodingData *const encoding_data =
+ &attribute_data_[att_data_id].encoding_data;
+ const MeshAttributeCornerTable *const corner_table =
+ &attribute_data_[att_data_id].connectivity_data;
+
+ std::unique_ptr<MeshTraversalSequencer<AttTraverser>> traversal_sequencer(
+ new MeshTraversalSequencer<AttTraverser>(mesh, encoding_data));
+
+ AttObserver att_observer(corner_table, mesh, traversal_sequencer.get(),
+ encoding_data);
+
+ AttTraverser att_traverser;
+ att_traverser.Init(corner_table, att_observer);
+
+ traversal_sequencer->SetTraverser(att_traverser);
+ sequencer = std::move(traversal_sequencer);
+ }
+
+ if (!sequencer)
+ return false;
+
+ std::unique_ptr<SequentialAttributeDecodersController> att_controller(
+ new SequentialAttributeDecodersController(std::move(sequencer)));
+
+ return decoder_->SetAttributesDecoder(att_decoder_id,
+ std::move(att_controller));
+}
+
+template <class TraversalDecoder>
+bool MeshEdgebreakerDecoderImpl<TraversalDecoder>::DecodeConnectivity() {
+ num_new_vertices_ = 0;
+ new_to_parent_vertex_map_.clear();
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ uint32_t num_new_verts;
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0)) {
+ if (!decoder_->buffer()->Decode(&num_new_verts))
+ return false;
+ } else {
+ if (!DecodeVarint(&num_new_verts, decoder_->buffer()))
+ return false;
+ }
+ num_new_vertices_ = num_new_verts;
+ }
+#endif
+
+ uint32_t num_encoded_vertices;
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0)) {
+ if (!decoder_->buffer()->Decode(&num_encoded_vertices))
+ return false;
+
+ } else
+#endif
+ {
+ if (!DecodeVarint(&num_encoded_vertices, decoder_->buffer()))
+ return false;
+ }
+ num_encoded_vertices_ = num_encoded_vertices;
+
+ uint32_t num_faces;
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0)) {
+ if (!decoder_->buffer()->Decode(&num_faces))
+ return false;
+
+ } else
+#endif
+ {
+ if (!DecodeVarint(&num_faces, decoder_->buffer()))
+ return false;
+ }
+ if (num_faces > std::numeric_limits<CornerIndex::ValueType>::max() / 3)
+ return false; // Draco cannot handle this many faces.
+
+ if (static_cast<uint32_t>(num_encoded_vertices_) > num_faces * 3) {
+ return false; // There cannot be more vertices than 3 * num_faces.
+ }
+ uint8_t num_attribute_data;
+ if (!decoder_->buffer()->Decode(&num_attribute_data))
+ return false;
+
+ uint32_t num_encoded_symbols;
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0)) {
+ if (!decoder_->buffer()->Decode(&num_encoded_symbols))
+ return false;
+
+ } else
+#endif
+ {
+ if (!DecodeVarint(&num_encoded_symbols, decoder_->buffer()))
+ return false;
+ }
+
+ if (num_faces < num_encoded_symbols) {
+ // Number of faces needs to be the same or greater than the number of
+ // symbols (it can be greater because the initial face may not be encoded as
+ // a symbol).
+ return false;
+ }
+ const uint32_t max_encoded_faces =
+ num_encoded_symbols + (num_encoded_symbols / 3);
+ if (num_faces > max_encoded_faces) {
+ // Faces can only be 1 1/3 times bigger than number of encoded symbols. This
+ // could only happen if all new encoded components started with interior
+ // triangles. E.g. A mesh with multiple tetrahedrons.
+ return false;
+ }
+
+ uint32_t num_encoded_split_symbols;
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0)) {
+ if (!decoder_->buffer()->Decode(&num_encoded_split_symbols))
+ return false;
+
+ } else
+#endif
+ {
+ if (!DecodeVarint(&num_encoded_split_symbols, decoder_->buffer()))
+ return false;
+ }
+
+ if (num_encoded_split_symbols > num_encoded_symbols) {
+ return false; // Split symbols are a sub-set of all symbols.
+ }
+
+ // Decode topology (connectivity).
+ vertex_traversal_length_.clear();
+ corner_table_ = std::unique_ptr<CornerTable>(new CornerTable());
+ if (corner_table_ == nullptr)
+ return false;
+ processed_corner_ids_.clear();
+ processed_corner_ids_.reserve(num_faces);
+ processed_connectivity_corners_.clear();
+ processed_connectivity_corners_.reserve(num_faces);
+ topology_split_data_.clear();
+ hole_event_data_.clear();
+ init_face_configurations_.clear();
+ init_corners_.clear();
+
+ last_symbol_id_ = -1;
+ last_face_id_ = -1;
+ last_vert_id_ = -1;
+
+ attribute_data_.clear();
+ // Add one attribute data for each attribute decoder.
+ attribute_data_.resize(num_attribute_data);
+
+ if (!corner_table_->Reset(num_faces,
+ num_encoded_vertices_ + num_encoded_split_symbols))
+ return false;
+
+ // Start with all vertices marked as holes (boundaries).
+ // Only vertices decoded with TOPOLOGY_C symbol (and the initial face) will
+ // be marked as non hole vertices. We need to allocate the array larger
+ // because split symbols can create extra vertices during the decoding
+ // process (these extra vertices are then eliminated during deduplication).
+ is_vert_hole_.assign(num_encoded_vertices_ + num_encoded_split_symbols, true);
+
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ int32_t topology_split_decoded_bytes = -1;
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ uint32_t encoded_connectivity_size;
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0)) {
+ if (!decoder_->buffer()->Decode(&encoded_connectivity_size))
+ return false;
+ } else {
+ if (!DecodeVarint(&encoded_connectivity_size, decoder_->buffer()))
+ return false;
+ }
+ if (encoded_connectivity_size == 0 ||
+ encoded_connectivity_size > decoder_->buffer()->remaining_size())
+ return false;
+ DecoderBuffer event_buffer;
+ event_buffer.Init(
+ decoder_->buffer()->data_head() + encoded_connectivity_size,
+ decoder_->buffer()->remaining_size() - encoded_connectivity_size,
+ decoder_->buffer()->bitstream_version());
+ // Decode hole and topology split events.
+ topology_split_decoded_bytes =
+ DecodeHoleAndTopologySplitEvents(&event_buffer);
+ if (topology_split_decoded_bytes == -1)
+ return false;
+
+ } else
+#endif
+ {
+ if (DecodeHoleAndTopologySplitEvents(decoder_->buffer()) == -1)
+ return false;
+ }
+
+ traversal_decoder_.Init(this);
+ // Add one extra vertex for each split symbol.
+ traversal_decoder_.SetNumEncodedVertices(num_encoded_vertices_ +
+ num_encoded_split_symbols);
+ traversal_decoder_.SetNumAttributeData(num_attribute_data);
+
+ DecoderBuffer traversal_end_buffer;
+ if (!traversal_decoder_.Start(&traversal_end_buffer))
+ return false;
+
+ const int num_connectivity_verts = DecodeConnectivity(num_encoded_symbols);
+ if (num_connectivity_verts == -1)
+ return false;
+
+ // Set the main buffer to the end of the traversal.
+ decoder_->buffer()->Init(traversal_end_buffer.data_head(),
+ traversal_end_buffer.remaining_size(),
+ decoder_->buffer()->bitstream_version());
+
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ // Skip topology split data that was already decoded earlier.
+ decoder_->buffer()->Advance(topology_split_decoded_bytes);
+ }
+#endif
+
+ // Decode connectivity of non-position attributes.
+ if (attribute_data_.size() > 0) {
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 1)) {
+ for (CornerIndex ci(0); ci < corner_table_->num_corners(); ci += 3) {
+ if (!DecodeAttributeConnectivitiesOnFaceLegacy(ci))
+ return false;
+ }
+
+ } else
+#endif
+ {
+ for (CornerIndex ci(0); ci < corner_table_->num_corners(); ci += 3) {
+ if (!DecodeAttributeConnectivitiesOnFace(ci))
+ return false;
+ }
+ }
+ }
+ traversal_decoder_.Done();
+
+ // Decode attribute connectivity.
+ // Prepare data structure for decoding non-position attribute connectivity.
+ 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) {
+ attribute_data_[i].connectivity_data.AddSeamEdge(CornerIndex(c));
+ }
+ // Recompute vertices from the newly added seam edges.
+ attribute_data_[i].connectivity_data.RecomputeVertices(nullptr, nullptr);
+ }
+
+ pos_encoding_data_.Init(corner_table_->num_vertices());
+ 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
+ // it).
+ int32_t att_connectivity_verts =
+ attribute_data_[i].connectivity_data.num_vertices();
+ if (att_connectivity_verts < corner_table_->num_vertices())
+ att_connectivity_verts = corner_table_->num_vertices();
+ attribute_data_[i].encoding_data.Init(att_connectivity_verts);
+ }
+ if (!AssignPointsToCorners(num_connectivity_verts))
+ return false;
+ return true;
+}
+
+template <class TraversalDecoder>
+bool MeshEdgebreakerDecoderImpl<TraversalDecoder>::OnAttributesDecoded() {
+ return true;
+}
+
+template <class TraversalDecoder>
+int MeshEdgebreakerDecoderImpl<TraversalDecoder>::DecodeConnectivity(
+ int num_symbols) {
+ // Algorithm does the reverse decoding of the symbols encoded with the
+ // edgebreaker method. The reverse decoding always keeps track of the active
+ // edge identified by its opposite corner (active corner). New faces are
+ // always added to this active edge. There may be multiple active corners at
+ // one time that either correspond to separate mesh components or to
+ // sub-components of one mesh that are going to be merged together using the
+ // TOPOLOGY_S symbol. We can store these active edges on a stack, because the
+ // decoder always processes only the latest active edge. TOPOLOGY_S then
+ // removes the top edge from the stack and TOPOLOGY_E adds a new edge to the
+ // stack.
+ std::vector<CornerIndex> active_corner_stack;
+
+ // Additional active edges may be added as a result of topology split events.
+ // They can be added in arbitrary order, but we always know the split symbol
+ // id they belong to, so we can address them using this symbol id.
+ std::unordered_map<int, CornerIndex> topology_split_active_corners;
+
+ // Vector used for storing vertices that were marked as isolated during the
+ // decoding process. Currently used only when the mesh doesn't contain any
+ // non-position connectivity data.
+ std::vector<VertexIndex> invalid_vertices;
+ const bool remove_invalid_vertices = attribute_data_.empty();
+
+ int max_num_vertices = static_cast<int>(is_vert_hole_.size());
+ int num_faces = 0;
+ for (int symbol_id = 0; symbol_id < num_symbols; ++symbol_id) {
+ const FaceIndex face(num_faces++);
+ // Used to flag cases where we need to look for topology split events.
+ bool check_topology_split = false;
+ const uint32_t symbol = traversal_decoder_.DecodeSymbol();
+ if (symbol == TOPOLOGY_C) {
+ // Create a new face between two edges on the open boundary.
+ // The first edge is opposite to the corner "a" from the image below.
+ // The other edge is opposite to the corner "b" that can be reached
+ // through a CCW traversal around the vertex "v".
+ // One new active boundary edge is created, opposite to the new corner
+ // "x".
+ //
+ // *-------*
+ // / \ / \
+ // / \ / \
+ // / \ / \
+ // *-------v-------*
+ // \b /x\ a/
+ // \ / \ /
+ // \ / C \ /
+ // *.......*
+
+ // Find the corner "b" from the corner "a" which is the corner on the
+ // top of the active stack.
+ if (active_corner_stack.empty())
+ return -1;
+
+ const CornerIndex corner_a = active_corner_stack.back();
+ const VertexIndex vertex_x =
+ corner_table_->Vertex(corner_table_->Next(corner_a));
+ const CornerIndex corner_b =
+ corner_table_->Next(corner_table_->LeftMostCorner(vertex_x));
+
+ // New tip corner.
+ const CornerIndex corner(3 * face.value());
+ // Update opposite corner mappings.
+ SetOppositeCorners(corner_a, corner + 1);
+ SetOppositeCorners(corner_b, corner + 2);
+
+ // Update vertex mapping.
+ corner_table_->MapCornerToVertex(corner, vertex_x);
+ corner_table_->MapCornerToVertex(
+ corner + 1, corner_table_->Vertex(corner_table_->Next(corner_b)));
+ const VertexIndex vert_a_prev =
+ corner_table_->Vertex(corner_table_->Previous(corner_a));
+ corner_table_->MapCornerToVertex(corner + 2, vert_a_prev);
+ corner_table_->SetLeftMostCorner(vert_a_prev, corner + 2);
+ // Mark the vertex |x| as interior.
+ is_vert_hole_[vertex_x.value()] = false;
+ // Update the corner on the active stack.
+ active_corner_stack.back() = corner;
+ } else if (symbol == TOPOLOGY_R || symbol == TOPOLOGY_L) {
+ // Create a new face extending from the open boundary edge opposite to the
+ // corner "a" from the image below. Two new boundary edges are created
+ // opposite to corners "r" and "l". New active corner is set to either "r"
+ // or "l" depending on the decoded symbol. One new vertex is created
+ // at the opposite corner to corner "a".
+ // *-------*
+ // /a\ / \
+ // / \ / \
+ // / \ / \
+ // *-------v-------*
+ // .l r.
+ // . .
+ // . .
+ // *
+ if (active_corner_stack.empty())
+ return -1;
+ const CornerIndex corner_a = active_corner_stack.back();
+
+ // First corner on the new face is either corner "l" or "r".
+ const CornerIndex corner(3 * face.value());
+ CornerIndex opp_corner, corner_l, corner_r;
+ if (symbol == TOPOLOGY_R) {
+ // "r" is the new first corner.
+ opp_corner = corner + 2;
+ corner_l = corner + 1;
+ corner_r = corner;
+ } else {
+ // "l" is the new first corner.
+ opp_corner = corner + 1;
+ corner_l = corner;
+ corner_r = corner + 2;
+ }
+ SetOppositeCorners(opp_corner, corner_a);
+ // Update vertex mapping.
+ const VertexIndex new_vert_index = corner_table_->AddNewVertex();
+
+ if (corner_table_->num_vertices() > max_num_vertices)
+ return -1; // Unexpected number of decoded vertices.
+
+ corner_table_->MapCornerToVertex(opp_corner, new_vert_index);
+ corner_table_->SetLeftMostCorner(new_vert_index, opp_corner);
+
+ const VertexIndex vertex_r =
+ corner_table_->Vertex(corner_table_->Previous(corner_a));
+ corner_table_->MapCornerToVertex(corner_r, vertex_r);
+ // Update left-most corner on the vertex on the |corner_r|.
+ corner_table_->SetLeftMostCorner(vertex_r, corner_r);
+
+ corner_table_->MapCornerToVertex(
+ corner_l, corner_table_->Vertex(corner_table_->Next(corner_a)));
+ active_corner_stack.back() = corner;
+ check_topology_split = true;
+ } else if (symbol == TOPOLOGY_S) {
+ // Create a new face that merges two last active edges from the active
+ // stack. No new vertex is created, but two vertices at corners "p" and
+ // "n" need to be merged into a single vertex.
+ //
+ // *-------v-------*
+ // \a p/x\n b/
+ // \ / \ /
+ // \ / S \ /
+ // *.......*
+ //
+ if (active_corner_stack.empty())
+ return -1;
+ const CornerIndex corner_b = active_corner_stack.back();
+ active_corner_stack.pop_back();
+
+ // Corner "a" can correspond either to a normal active edge, or to an edge
+ // created from the topology split event.
+ const auto it = topology_split_active_corners.find(symbol_id);
+ if (it != topology_split_active_corners.end()) {
+ // Topology split event. Move the retrieved edge to the stack.
+ active_corner_stack.push_back(it->second);
+ }
+ if (active_corner_stack.empty())
+ return -1;
+ const CornerIndex corner_a = active_corner_stack.back();
+
+ if (corner_table_->Opposite(corner_a) != kInvalidCornerIndex ||
+ corner_table_->Opposite(corner_b) != kInvalidCornerIndex) {
+ // One of the corners is already opposite to an existing face, which
+ // should not happen unless the input was tempered with.
+ return -1;
+ }
+
+ // First corner on the new face is corner "x" from the image above.
+ const CornerIndex corner(3 * face.value());
+ // Update the opposite corner mapping.
+ SetOppositeCorners(corner_a, corner + 2);
+ SetOppositeCorners(corner_b, corner + 1);
+ // Update vertices. For the vertex at corner "x", use the vertex id from
+ // the corner "p".
+ const VertexIndex vertex_p =
+ corner_table_->Vertex(corner_table_->Previous(corner_a));
+ corner_table_->MapCornerToVertex(corner, vertex_p);
+ corner_table_->MapCornerToVertex(
+ corner + 1, corner_table_->Vertex(corner_table_->Next(corner_a)));
+ const VertexIndex vert_b_prev =
+ corner_table_->Vertex(corner_table_->Previous(corner_b));
+ corner_table_->MapCornerToVertex(corner + 2, vert_b_prev);
+ corner_table_->SetLeftMostCorner(vert_b_prev, corner + 2);
+ CornerIndex corner_n = corner_table_->Next(corner_b);
+ const VertexIndex vertex_n = corner_table_->Vertex(corner_n);
+ traversal_decoder_.MergeVertices(vertex_p, vertex_n);
+ // Update the left most corner on the newly merged vertex.
+ corner_table_->SetLeftMostCorner(vertex_p,
+ corner_table_->LeftMostCorner(vertex_n));
+
+ // Also update the vertex id at corner "n" and all corners that are
+ // connected to it in the CCW direction.
+ while (corner_n != kInvalidCornerIndex) {
+ corner_table_->MapCornerToVertex(corner_n, vertex_p);
+ corner_n = corner_table_->SwingLeft(corner_n);
+ }
+ // Make sure the old vertex n is now mapped to an invalid corner (make it
+ // isolated).
+ corner_table_->MakeVertexIsolated(vertex_n);
+ if (remove_invalid_vertices)
+ invalid_vertices.push_back(vertex_n);
+ active_corner_stack.back() = corner;
+ } else if (symbol == TOPOLOGY_E) {
+ const CornerIndex corner(3 * face.value());
+ const VertexIndex first_vert_index = corner_table_->AddNewVertex();
+ // Create three new vertices at the corners of the new face.
+ corner_table_->MapCornerToVertex(corner, first_vert_index);
+ corner_table_->MapCornerToVertex(corner + 1,
+ corner_table_->AddNewVertex());
+ corner_table_->MapCornerToVertex(corner + 2,
+ corner_table_->AddNewVertex());
+
+ if (corner_table_->num_vertices() > max_num_vertices)
+ return -1; // Unexpected number of decoded vertices.
+
+ corner_table_->SetLeftMostCorner(first_vert_index, corner);
+ corner_table_->SetLeftMostCorner(first_vert_index + 1, corner + 1);
+ corner_table_->SetLeftMostCorner(first_vert_index + 2, corner + 2);
+ // Add the tip corner to the active stack.
+ active_corner_stack.push_back(corner);
+ check_topology_split = true;
+ } else {
+ // Error. Unknown symbol decoded.
+ return -1;
+ }
+ // Inform the traversal decoder that a new corner has been reached.
+ traversal_decoder_.NewActiveCornerReached(active_corner_stack.back());
+
+ if (check_topology_split) {
+ // Check for topology splits happens only for TOPOLOGY_L, TOPOLOGY_R and
+ // TOPOLOGY_E symbols because those are the symbols that correspond to
+ // faces that can be directly connected a TOPOLOGY_S face through the
+ // topology split event.
+ // If a topology split is detected, we need to add a new active edge
+ // onto the active_corner_stack because it will be used later when the
+ // corresponding TOPOLOGY_S event is decoded.
+
+ // Symbol id used by the encoder (reverse).
+ const int encoder_symbol_id = num_symbols - symbol_id - 1;
+ EdgeFaceName split_edge;
+ int encoder_split_symbol_id;
+ while (IsTopologySplit(encoder_symbol_id, &split_edge,
+ &encoder_split_symbol_id)) {
+ if (encoder_split_symbol_id < 0)
+ return -1; // Wrong split symbol id.
+ // Symbol was part of a topology split. Now we need to determine which
+ // edge should be added to the active edges stack.
+ const CornerIndex act_top_corner = active_corner_stack.back();
+ // The current symbol has one active edge (stored in act_top_corner) and
+ // two remaining inactive edges that are attached to it.
+ // *
+ // / \
+ // left_edge / \ right_edge
+ // / \
+ // *.......*
+ // active_edge
+
+ CornerIndex new_active_corner;
+ if (split_edge == RIGHT_FACE_EDGE) {
+ new_active_corner = corner_table_->Next(act_top_corner);
+ } else {
+ new_active_corner = corner_table_->Previous(act_top_corner);
+ }
+ // Add the new active edge.
+ // Convert the encoder split symbol id to decoder symbol id.
+ const int decoder_split_symbol_id =
+ num_symbols - encoder_split_symbol_id - 1;
+ topology_split_active_corners[decoder_split_symbol_id] =
+ new_active_corner;
+ }
+ }
+ }
+ if (corner_table_->num_vertices() > max_num_vertices)
+ return -1; // Unexpected number of decoded vertices.
+ // Decode start faces and connect them to the faces from the active stack.
+ while (active_corner_stack.size() > 0) {
+ const CornerIndex corner = active_corner_stack.back();
+ active_corner_stack.pop_back();
+ const bool interior_face =
+ traversal_decoder_.DecodeStartFaceConfiguration();
+ if (interior_face) {
+ // The start face is interior, we need to find three corners that are
+ // opposite to it. The first opposite corner "a" is the corner from the
+ // top of the active corner stack and the remaining two corners "b" and
+ // "c" are then the next corners from the left-most corners of vertices
+ // "n" and "x" respectively.
+ //
+ // *-------*
+ // / \ / \
+ // / \ / \
+ // / \ / \
+ // *-------p-------*
+ // / \a . . c/ \
+ // / \ . . / \
+ // / \ . I . / \
+ // *-------n.......x------*
+ // \ / \ / \ /
+ // \ / \ / \ /
+ // \ / \b/ \ /
+ // *-------*-------*
+ //
+
+ if (num_faces >= corner_table_->num_faces()) {
+ return -1; // More faces than expected added to the mesh.
+ }
+
+ const CornerIndex corner_a = corner;
+ const VertexIndex vert_n =
+ corner_table_->Vertex(corner_table_->Next(corner_a));
+ const CornerIndex corner_b =
+ corner_table_->Next(corner_table_->LeftMostCorner(vert_n));
+
+ const VertexIndex vert_x =
+ corner_table_->Vertex(corner_table_->Next(corner_b));
+ const CornerIndex corner_c =
+ corner_table_->Next(corner_table_->LeftMostCorner(vert_x));
+
+ const VertexIndex vert_p =
+ corner_table_->Vertex(corner_table_->Next(corner_c));
+
+ const FaceIndex face(num_faces++);
+ // The first corner of the initial face is the corner opposite to "a".
+ const CornerIndex new_corner(3 * face.value());
+ SetOppositeCorners(new_corner, corner);
+ SetOppositeCorners(new_corner + 1, corner_b);
+ SetOppositeCorners(new_corner + 2, corner_c);
+
+ // Map new corners to existing vertices.
+ corner_table_->MapCornerToVertex(new_corner, vert_x);
+ corner_table_->MapCornerToVertex(new_corner + 1, vert_p);
+ corner_table_->MapCornerToVertex(new_corner + 2, vert_n);
+
+ // Mark all three vertices as interior.
+ for (int ci = 0; ci < 3; ++ci) {
+ is_vert_hole_[corner_table_->Vertex(new_corner + ci).value()] = false;
+ }
+
+ init_face_configurations_.push_back(true);
+ init_corners_.push_back(new_corner);
+ } else {
+ // The initial face wasn't interior and the traversal had to start from
+ // an open boundary. In this case no new face is added, but we need to
+ // keep record about the first opposite corner to this boundary.
+ init_face_configurations_.push_back(false);
+ init_corners_.push_back(corner);
+ }
+ }
+ if (num_faces != corner_table_->num_faces())
+ return -1; // Unexpected number of decoded faces.
+
+ int num_vertices = corner_table_->num_vertices();
+ // If any vertex was marked as isolated, we want to remove it from the corner
+ // table to ensure that all vertices in range <0, num_vertices> are valid.
+ for (const VertexIndex invalid_vert : invalid_vertices) {
+ // Find the last valid vertex and swap it with the isolated vertex.
+ VertexIndex src_vert(num_vertices - 1);
+ while (corner_table_->LeftMostCorner(src_vert) == kInvalidCornerIndex) {
+ // The last vertex is invalid, proceed to the previous one.
+ src_vert = VertexIndex(--num_vertices - 1);
+ }
+ if (src_vert < invalid_vert)
+ continue; // No need to swap anything.
+
+ // Remap all corners mapped to |src_vert| to |invalid_vert|.
+ VertexCornersIterator<CornerTable> vcit(corner_table_.get(), src_vert);
+ for (; !vcit.End(); ++vcit) {
+ const CornerIndex cid = vcit.Corner();
+ corner_table_->MapCornerToVertex(cid, invalid_vert);
+ }
+ corner_table_->SetLeftMostCorner(invalid_vert,
+ corner_table_->LeftMostCorner(src_vert));
+
+ // Make the |src_vert| invalid.
+ corner_table_->MakeVertexIsolated(src_vert);
+ is_vert_hole_[invalid_vert.value()] = is_vert_hole_[src_vert.value()];
+ is_vert_hole_[src_vert.value()] = false;
+
+ // The last vertex is now invalid.
+ num_vertices--;
+ }
+ return num_vertices;
+}
+
+template <class TraversalDecoder>
+int32_t
+MeshEdgebreakerDecoderImpl<TraversalDecoder>::DecodeHoleAndTopologySplitEvents(
+ DecoderBuffer *decoder_buffer) {
+ // Prepare a new decoder from the provided buffer offset.
+ uint32_t num_topology_splits;
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0)) {
+ if (!decoder_buffer->Decode(&num_topology_splits))
+ return -1;
+
+ } else
+#endif
+ {
+ if (!DecodeVarint(&num_topology_splits, decoder_buffer))
+ return -1;
+ }
+ if (num_topology_splits > 0) {
+ if (num_topology_splits > static_cast<uint32_t>(corner_table_->num_faces()))
+ return -1;
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(1, 2)) {
+ for (uint32_t i = 0; i < num_topology_splits; ++i) {
+ TopologySplitEventData event_data;
+ if (!decoder_buffer->Decode(&event_data.split_symbol_id))
+ return -1;
+ if (!decoder_buffer->Decode(&event_data.source_symbol_id))
+ return -1;
+ uint8_t edge_data;
+ if (!decoder_buffer->Decode(&edge_data))
+ return -1;
+ event_data.source_edge = edge_data & 1;
+ topology_split_data_.push_back(event_data);
+ }
+
+ } else
+#endif
+ {
+ // Decode source and split symbol ids using delta and varint coding. See
+ // description in mesh_edgebreaker_encoder_impl.cc for more details.
+ int last_source_symbol_id = 0;
+ for (uint32_t i = 0; i < num_topology_splits; ++i) {
+ TopologySplitEventData event_data;
+ uint32_t delta;
+ DecodeVarint<uint32_t>(&delta, decoder_buffer);
+ event_data.source_symbol_id = delta + last_source_symbol_id;
+ DecodeVarint<uint32_t>(&delta, decoder_buffer);
+ if (delta > event_data.source_symbol_id)
+ return -1;
+ event_data.split_symbol_id =
+ event_data.source_symbol_id - static_cast<int32_t>(delta);
+ last_source_symbol_id = event_data.source_symbol_id;
+ topology_split_data_.push_back(event_data);
+ }
+ // Split edges are decoded from a direct bit decoder.
+ decoder_buffer->StartBitDecoding(false, nullptr);
+ for (uint32_t i = 0; i < num_topology_splits; ++i) {
+ uint32_t edge_data;
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ decoder_buffer->DecodeLeastSignificantBits32(2, &edge_data);
+ } else {
+ decoder_buffer->DecodeLeastSignificantBits32(1, &edge_data);
+ }
+ TopologySplitEventData &event_data = topology_split_data_[i];
+ event_data.source_edge = edge_data & 1;
+ }
+ decoder_buffer->EndBitDecoding();
+ }
+ }
+ uint32_t num_hole_events = 0;
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 0)) {
+ if (!decoder_buffer->Decode(&num_hole_events))
+ return -1;
+ } else if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(2, 1)) {
+ if (!DecodeVarint(&num_hole_events, decoder_buffer))
+ return -1;
+ }
+#endif
+ if (num_hole_events > 0) {
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (decoder_->bitstream_version() < DRACO_BITSTREAM_VERSION(1, 2)) {
+ for (uint32_t i = 0; i < num_hole_events; ++i) {
+ HoleEventData event_data;
+ if (!decoder_buffer->Decode(&event_data))
+ return -1;
+ hole_event_data_.push_back(event_data);
+ }
+
+ } else
+#endif
+ {
+ // Decode hole symbol ids using delta and varint coding.
+ int last_symbol_id = 0;
+ for (uint32_t i = 0; i < num_hole_events; ++i) {
+ HoleEventData event_data;
+ uint32_t delta;
+ DecodeVarint<uint32_t>(&delta, decoder_buffer);
+ event_data.symbol_id = delta + last_symbol_id;
+ last_symbol_id = event_data.symbol_id;
+ hole_event_data_.push_back(event_data);
+ }
+ }
+ }
+ return static_cast<int32_t>(decoder_buffer->decoded_size());
+}
+
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+template <class TraversalDecoder>
+bool MeshEdgebreakerDecoderImpl<TraversalDecoder>::
+ DecodeAttributeConnectivitiesOnFaceLegacy(CornerIndex corner) {
+ // Three corners of the face.
+ const CornerIndex corners[3] = {corner, corner_table_->Next(corner),
+ corner_table_->Previous(corner)};
+
+ for (int c = 0; c < 3; ++c) {
+ const CornerIndex opp_corner = corner_table_->Opposite(corners[c]);
+ if (opp_corner == kInvalidCornerIndex) {
+ // Don't decode attribute seams on boundary edges (every boundary edge
+ // is automatically an attribute seam).
+ for (uint32_t i = 0; i < attribute_data_.size(); ++i) {
+ attribute_data_[i].attribute_seam_corners.push_back(corners[c].value());
+ }
+ continue;
+ }
+
+ 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());
+ }
+ }
+ return true;
+}
+#endif
+
+template <class TraversalDecoder>
+bool MeshEdgebreakerDecoderImpl<
+ TraversalDecoder>::DecodeAttributeConnectivitiesOnFace(CornerIndex corner) {
+ // Three corners of the face.
+ const CornerIndex corners[3] = {corner, corner_table_->Next(corner),
+ corner_table_->Previous(corner)};
+
+ const FaceIndex src_face_id = corner_table_->Face(corner);
+ for (int c = 0; c < 3; ++c) {
+ const CornerIndex opp_corner = corner_table_->Opposite(corners[c]);
+ if (opp_corner == kInvalidCornerIndex) {
+ // Don't decode attribute seams on boundary edges (every boundary edge
+ // is automatically an attribute seam).
+ for (uint32_t i = 0; i < attribute_data_.size(); ++i) {
+ attribute_data_[i].attribute_seam_corners.push_back(corners[c].value());
+ }
+ continue;
+ }
+ const FaceIndex opp_face_id = corner_table_->Face(opp_corner);
+ // Don't decode edges when the opposite face has been already processed.
+ if (opp_face_id < src_face_id)
+ continue;
+
+ 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());
+ }
+ }
+ return true;
+}
+
+template <class TraversalDecoder>
+bool MeshEdgebreakerDecoderImpl<TraversalDecoder>::AssignPointsToCorners(
+ int num_connectivity_verts) {
+ // Map between the existing and deduplicated point ids.
+ // Note that at this point we have one point id for each corner of the
+ // mesh so there is corner_table_->num_corners() point ids.
+ decoder_->mesh()->SetNumFaces(corner_table_->num_faces());
+
+ if (attribute_data_.empty()) {
+ // We have connectivity for position only. In this case all vertex indices
+ // are equal to point indices.
+ for (FaceIndex f(0); f < decoder_->mesh()->num_faces(); ++f) {
+ Mesh::Face face;
+ const CornerIndex start_corner(3 * f.value());
+ for (int c = 0; c < 3; ++c) {
+ // Get the vertex index on the corner and use it as a point index.
+ const int32_t vert_id = corner_table_->Vertex(start_corner + c).value();
+ face[c] = vert_id;
+ }
+ decoder_->mesh()->SetFace(f, face);
+ }
+ decoder_->point_cloud()->set_num_points(num_connectivity_verts);
+ return true;
+ }
+ // Else we need to deduplicate multiple attributes.
+
+ // Map between point id and an associated corner id. Only one corner for
+ // each point is stored. The corners are used to sample the attribute values
+ // in the last stage of the deduplication.
+ std::vector<int32_t> point_to_corner_map;
+ // Map between every corner and their new point ids.
+ std::vector<int32_t> corner_to_point_map(corner_table_->num_corners());
+ for (int v = 0; v < corner_table_->num_vertices(); ++v) {
+ CornerIndex c = corner_table_->LeftMostCorner(VertexIndex(v));
+ if (c == kInvalidCornerIndex)
+ continue; // Isolated vertex.
+ CornerIndex deduplication_first_corner = c;
+ if (is_vert_hole_[v]) {
+ // If the vertex is on a boundary, start deduplication from the left most
+ // corner that is guaranteed to lie on the boundary.
+ deduplication_first_corner = c;
+ } else {
+ // If we are not on the boundary we need to find the first seam (of any
+ // attribute).
+ 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.
+
+ // At this point, we use identity mapping between corners and point ids.
+ const VertexIndex vert_id =
+ attribute_data_[i].connectivity_data.Vertex(c);
+ CornerIndex act_c = corner_table_->SwingRight(c);
+ bool seam_found = false;
+ while (act_c != c) {
+ if (act_c == kInvalidCornerIndex)
+ return false;
+ if (attribute_data_[i].connectivity_data.Vertex(act_c) != vert_id) {
+ // Attribute seam found. Stop.
+ deduplication_first_corner = act_c;
+ seam_found = true;
+ break;
+ }
+ act_c = corner_table_->SwingRight(act_c);
+ }
+ if (seam_found)
+ break; // No reason to process other attributes if we found a seam.
+ }
+ }
+
+ // Do a deduplication pass over the corners on the processed vertex.
+ // At this point each corner corresponds to one point id and our goal is to
+ // merge similar points into a single point id.
+ // We do a single pass in a clockwise direction over the corners and we add
+ // a new point id whenever one of the attributes change.
+ c = deduplication_first_corner;
+ // Create a new point.
+ corner_to_point_map[c.value()] =
+ static_cast<uint32_t>(point_to_corner_map.size());
+ point_to_corner_map.push_back(c.value());
+ // Traverse in CW direction.
+ CornerIndex prev_c = c;
+ c = corner_table_->SwingRight(c);
+ while (c != kInvalidCornerIndex && c != deduplication_first_corner) {
+ bool attribute_seam = false;
+ 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
+ // new point here.
+ attribute_seam = true;
+ break;
+ }
+ }
+ if (attribute_seam) {
+ corner_to_point_map[c.value()] =
+ static_cast<uint32_t>(point_to_corner_map.size());
+ point_to_corner_map.push_back(c.value());
+ } else {
+ corner_to_point_map[c.value()] = corner_to_point_map[prev_c.value()];
+ }
+ prev_c = c;
+ c = corner_table_->SwingRight(c);
+ }
+ }
+ // Add faces.
+ for (FaceIndex f(0); f < decoder_->mesh()->num_faces(); ++f) {
+ Mesh::Face face;
+ for (int c = 0; c < 3; ++c) {
+ // Remap old points to the new ones.
+ face[c] = corner_to_point_map[3 * f.value() + c];
+ }
+ decoder_->mesh()->SetFace(f, face);
+ }
+ decoder_->point_cloud()->set_num_points(
+ static_cast<uint32_t>(point_to_corner_map.size()));
+ return true;
+}
+
+template class MeshEdgebreakerDecoderImpl<MeshEdgebreakerTraversalDecoder>;
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+template class MeshEdgebreakerDecoderImpl<
+ MeshEdgebreakerTraversalPredictiveDecoder>;
+#endif
+template class MeshEdgebreakerDecoderImpl<
+ MeshEdgebreakerTraversalValenceDecoder>;
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl.h
new file mode 100644
index 00000000000..5299a189d48
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl.h
@@ -0,0 +1,226 @@
+// 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_COMPRESSION_MESH_MESH_EDGEBREAKER_DECODER_IMPL_H_
+#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_DECODER_IMPL_H_
+
+#include <unordered_map>
+#include <unordered_set>
+#include "draco/compression/mesh/traverser/mesh_traversal_sequencer.h"
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/attributes/mesh_attribute_indices_encoding_data.h"
+#include "draco/compression/mesh/mesh_edgebreaker_decoder_impl_interface.h"
+#include "draco/compression/mesh/mesh_edgebreaker_shared.h"
+#include "draco/core/decoder_buffer.h"
+#include "draco/mesh/corner_table.h"
+#include "draco/mesh/mesh_attribute_corner_table.h"
+
+namespace draco {
+
+// Implementation of the edgebreaker decoder that decodes data encoded with the
+// MeshEdgebreakerEncoderImpl class. The implementation of the decoder is based
+// on the algorithm presented in Isenburg et al'02 "Spirale Reversi: Reverse
+// decoding of the Edgebreaker encoding". Note that the encoding is still based
+// on the standard edgebreaker method as presented in "3D Compression
+// Made Simple: Edgebreaker on a Corner-Table" by Rossignac at al.'01.
+// http://www.cc.gatech.edu/~jarek/papers/CornerTableSMI.pdf. One difference is
+// caused by the properties of the spirale reversi algorithm that decodes the
+// symbols from the last one to the first one. To make the decoding more
+// efficient, we encode all symbols in the reverse order, therefore the decoder
+// can process them one by one.
+// The main advantage of the spirale reversi method is that the partially
+// decoded mesh has valid connectivity data at any time during the decoding
+// process (valid with respect to the decoded portion of the mesh). The standard
+// Edgebreaker decoder used two passes (forward decoding + zipping) which not
+// only prevented us from having a valid connectivity but it was also slower.
+// The main benefit of having the valid connectivity is that we can use the
+// known connectivity to predict encoded symbols that can improve the
+// compression rate.
+template <class TraversalDecoderT>
+class MeshEdgebreakerDecoderImpl : public MeshEdgebreakerDecoderImplInterface {
+ public:
+ MeshEdgebreakerDecoderImpl();
+ bool Init(MeshEdgebreakerDecoder *decoder) override;
+
+ const MeshAttributeCornerTable *GetAttributeCornerTable(
+ int att_id) const override;
+ const MeshAttributeIndicesEncodingData *GetAttributeEncodingData(
+ int att_id) const override;
+
+ bool CreateAttributesDecoder(int32_t att_decoder_id) override;
+ bool DecodeConnectivity() override;
+ bool OnAttributesDecoded() override;
+ MeshEdgebreakerDecoder *GetDecoder() const override { return decoder_; }
+ const CornerTable *GetCornerTable() const override {
+ return corner_table_.get();
+ }
+
+ private:
+ // Creates a vertex traversal sequencer for the specified |TraverserT| type.
+ template <class TraverserT>
+ std::unique_ptr<PointsSequencer> CreateVertexTraversalSequencer(
+ MeshAttributeIndicesEncodingData *encoding_data);
+
+ // Decodes connectivity between vertices (vertex indices).
+ // Returns the number of vertices created by the decoder or -1 on error.
+ int DecodeConnectivity(int num_symbols);
+
+ // Returns true if the current symbol was part of a topology split event. This
+ // means that the current face was connected to the left edge of a face
+ // encoded with the TOPOLOGY_S symbol. |out_symbol_edge| can be used to
+ // identify which edge of the source symbol was connected to the TOPOLOGY_S
+ // symbol.
+ bool IsTopologySplit(int encoder_symbol_id, EdgeFaceName *out_face_edge,
+ int *out_encoder_split_symbol_id) {
+ if (topology_split_data_.size() == 0)
+ return false;
+ if (topology_split_data_.back().source_symbol_id >
+ static_cast<uint32_t>(encoder_symbol_id)) {
+ // Something is wrong; if the desired source symbol is greater than the
+ // current encoder_symbol_id, we missed it, or the input was tampered
+ // (|encoder_symbol_id| keeps decreasing).
+ // Return invalid symbol id to notify the decoder that there was an
+ // error.
+ *out_encoder_split_symbol_id = -1;
+ return true;
+ }
+ if (topology_split_data_.back().source_symbol_id != encoder_symbol_id)
+ return false;
+ *out_face_edge =
+ static_cast<EdgeFaceName>(topology_split_data_.back().source_edge);
+ *out_encoder_split_symbol_id = topology_split_data_.back().split_symbol_id;
+ // Remove the latest split event.
+ topology_split_data_.pop_back();
+ return true;
+ }
+
+ // Decodes event data for hole and topology split events and stores them for
+ // future use.
+ // Returns the number of parsed bytes, or -1 on error.
+ int32_t DecodeHoleAndTopologySplitEvents(DecoderBuffer *decoder_buffer);
+
+ // Decodes all non-position attribute connectivity on the currently
+ // processed face.
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ bool DecodeAttributeConnectivitiesOnFaceLegacy(CornerIndex corner);
+#endif
+ bool DecodeAttributeConnectivitiesOnFace(CornerIndex corner);
+
+ // Initializes mapping between corners and point ids.
+ bool AssignPointsToCorners(int num_connectivity_verts);
+
+ bool IsFaceVisited(CornerIndex corner_id) const {
+ if (corner_id < 0)
+ return true; // Invalid corner signalizes that the face does not exist.
+ return visited_faces_[corner_table_->Face(corner_id).value()];
+ }
+
+ void SetOppositeCorners(CornerIndex corner_0, CornerIndex corner_1) {
+ corner_table_->SetOppositeCorner(corner_0, corner_1);
+ corner_table_->SetOppositeCorner(corner_1, corner_0);
+ }
+
+ MeshEdgebreakerDecoder *decoder_;
+
+ std::unique_ptr<CornerTable> corner_table_;
+
+ // Stack used for storing corners that need to be traversed when decoding
+ // mesh vertices. New corner is added for each initial face and a split
+ // symbol, and one corner is removed when the end symbol is reached.
+ // Stored as member variable to prevent frequent memory reallocations when
+ // handling meshes with lots of disjoint components. Originally, we used
+ // recursive functions to handle this behavior, but that can cause stack
+ // memory overflow when compressing huge meshes.
+ std::vector<CornerIndex> corner_traversal_stack_;
+
+ // Array stores the number of visited visited for each mesh traversal.
+ std::vector<int> vertex_traversal_length_;
+
+ // List of decoded topology split events.
+ std::vector<TopologySplitEventData> topology_split_data_;
+
+ // List of decoded hole events.
+ std::vector<HoleEventData> hole_event_data_;
+
+ // Configuration of the initial face for each mesh component.
+ std::vector<bool> init_face_configurations_;
+
+ // Initial corner for each traversal.
+ std::vector<CornerIndex> init_corners_;
+
+ // Id of the last processed input symbol.
+ int last_symbol_id_;
+
+ // Id of the last decoded vertex.
+ int last_vert_id_;
+
+ // Id of the last decoded face.
+ int last_face_id_;
+
+ // Array for marking visited faces.
+ std::vector<bool> visited_faces_;
+ // Array for marking visited vertices.
+ std::vector<bool> visited_verts_;
+ // Array for marking vertices on open boundaries.
+ std::vector<bool> is_vert_hole_;
+
+ // The number of new vertices added by the encoder (because of non-manifold
+ // vertices on the input mesh).
+ // If there are no non-manifold edges/vertices on the input mesh, this should
+ // be 0.
+ int num_new_vertices_;
+ // For every newly added vertex, this array stores it's mapping to the
+ // parent vertex id of the encoded mesh.
+ std::unordered_map<int, int> new_to_parent_vertex_map_;
+ // The number of vertices that were encoded (can be different from the number
+ // of vertices of the input mesh).
+ int num_encoded_vertices_;
+
+ // Array for storing the encoded corner ids in the order their associated
+ // vertices were decoded.
+ std::vector<int32_t> processed_corner_ids_;
+
+ // Array storing corners in the order they were visited during the
+ // connectivity decoding (always storing the tip corner of each newly visited
+ // face).
+ std::vector<int> processed_connectivity_corners_;
+
+ MeshAttributeIndicesEncodingData pos_encoding_data_;
+
+ // Id of an attributes decoder that uses |pos_encoding_data_|.
+ int pos_data_decoder_id_;
+
+ // Data for non-position attributes used by the decoder.
+ struct AttributeData {
+ AttributeData() : decoder_id(-1), is_connectivity_used(true) {}
+ // Id of the attribute decoder that was used to decode this attribute data.
+ int decoder_id;
+ MeshAttributeCornerTable connectivity_data;
+ // Flag that can mark the connectivity_data invalid. In such case the base
+ // corner table of the mesh should be used instead.
+ bool is_connectivity_used;
+ MeshAttributeIndicesEncodingData encoding_data;
+ // Opposite corners to attribute seam edges.
+ std::vector<int32_t> attribute_seam_corners;
+ };
+ std::vector<AttributeData> attribute_data_;
+
+ TraversalDecoderT traversal_decoder_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_DECODER_IMPL_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl_interface.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl_interface.h
new file mode 100644
index 00000000000..31fabf252d8
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_decoder_impl_interface.h
@@ -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.
+//
+#ifndef DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_DECODER_IMPL_INTERFACE_H_
+#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_DECODER_IMPL_INTERFACE_H_
+
+#include "draco/compression/attributes/mesh_attribute_indices_encoding_data.h"
+#include "draco/mesh/mesh_attribute_corner_table.h"
+
+namespace draco {
+
+// Forward declaration is necessary here to avoid circular dependencies.
+class MeshEdgebreakerDecoder;
+
+// Abstract interface used by MeshEdgebreakerDecoder to interact with the actual
+// implementation of the edgebreaker decoding method.
+class MeshEdgebreakerDecoderImplInterface {
+ public:
+ virtual ~MeshEdgebreakerDecoderImplInterface() = default;
+ virtual bool Init(MeshEdgebreakerDecoder *decoder) = 0;
+
+ virtual const MeshAttributeCornerTable *GetAttributeCornerTable(
+ int att_id) const = 0;
+ virtual const MeshAttributeIndicesEncodingData *GetAttributeEncodingData(
+ int att_id) const = 0;
+ virtual bool CreateAttributesDecoder(int32_t att_decoder_id) = 0;
+ virtual bool DecodeConnectivity() = 0;
+ virtual bool OnAttributesDecoded() = 0;
+
+ virtual MeshEdgebreakerDecoder *GetDecoder() const = 0;
+ virtual const CornerTable *GetCornerTable() const = 0;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_DECODER_IMPL_INTERFACE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder.cc b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder.cc
new file mode 100644
index 00000000000..ad87f9403af
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder.cc
@@ -0,0 +1,184 @@
+// 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 "draco/compression/mesh/mesh_edgebreaker_encoder.h"
+
+#include "draco/compression/mesh/mesh_edgebreaker_encoder_impl.h"
+#include "draco/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h"
+#include "draco/compression/mesh/mesh_edgebreaker_traversal_valence_encoder.h"
+
+namespace draco {
+
+MeshEdgebreakerEncoder::MeshEdgebreakerEncoder() {}
+
+bool MeshEdgebreakerEncoder::InitializeEncoder() {
+ const bool is_standard_edgebreaker_available =
+ options()->IsFeatureSupported(features::kEdgebreaker);
+ const bool is_predictive_edgebreaker_available =
+ options()->IsFeatureSupported(features::kPredictiveEdgebreaker);
+
+ impl_ = nullptr;
+ // For tiny meshes it's usually better to use the basic edgebreaker as the
+ // overhead of the predictive one may turn out to be too big.
+ // TODO(ostava): For now we have a set limit for forcing the basic edgebreaker
+ // based on the number of faces, but a more complex heuristic may be used if
+ // needed.
+ const bool is_tiny_mesh = mesh()->num_faces() < 1000;
+
+ int selected_edgebreaker_method =
+ options()->GetGlobalInt("edgebreaker_method", -1);
+ if (selected_edgebreaker_method == -1) {
+ if (is_standard_edgebreaker_available &&
+ (options()->GetSpeed() >= 5 || !is_predictive_edgebreaker_available ||
+ is_tiny_mesh)) {
+ selected_edgebreaker_method = MESH_EDGEBREAKER_STANDARD_ENCODING;
+ } else {
+ selected_edgebreaker_method = MESH_EDGEBREAKER_VALENCE_ENCODING;
+ }
+ }
+
+ if (selected_edgebreaker_method == MESH_EDGEBREAKER_STANDARD_ENCODING) {
+ if (is_standard_edgebreaker_available) {
+ buffer()->Encode(
+ static_cast<uint8_t>(MESH_EDGEBREAKER_STANDARD_ENCODING));
+ impl_ = std::unique_ptr<MeshEdgebreakerEncoderImplInterface>(
+ new MeshEdgebreakerEncoderImpl<MeshEdgebreakerTraversalEncoder>());
+ }
+ } else if (selected_edgebreaker_method == MESH_EDGEBREAKER_VALENCE_ENCODING) {
+ buffer()->Encode(static_cast<uint8_t>(MESH_EDGEBREAKER_VALENCE_ENCODING));
+ impl_ = std::unique_ptr<MeshEdgebreakerEncoderImplInterface>(
+ new MeshEdgebreakerEncoderImpl<
+ MeshEdgebreakerTraversalValenceEncoder>());
+ }
+ if (!impl_)
+ return false;
+ if (!impl_->Init(this))
+ return false;
+ return true;
+}
+
+bool MeshEdgebreakerEncoder::GenerateAttributesEncoder(int32_t att_id) {
+ if (!impl_->GenerateAttributesEncoder(att_id))
+ return false;
+ return true;
+}
+
+bool MeshEdgebreakerEncoder::EncodeAttributesEncoderIdentifier(
+ int32_t att_encoder_id) {
+ if (!impl_->EncodeAttributesEncoderIdentifier(att_encoder_id))
+ return false;
+ return true;
+}
+
+bool MeshEdgebreakerEncoder::EncodeConnectivity() {
+ return impl_->EncodeConnectivity();
+}
+
+void MeshEdgebreakerEncoder::ComputeNumberOfEncodedPoints() {
+ if (!impl_)
+ return;
+ const CornerTable *const corner_table = impl_->GetCornerTable();
+ if (!corner_table)
+ return;
+ size_t num_points =
+ corner_table->num_vertices() - corner_table->NumIsolatedVertices();
+
+ if (mesh()->num_attributes() > 1) {
+ // Gather all corner tables for all non-position attributes.
+ std::vector<const MeshAttributeCornerTable *> attribute_corner_tables;
+ for (int i = 0; i < mesh()->num_attributes(); ++i) {
+ if (mesh()->attribute(i)->attribute_type() == GeometryAttribute::POSITION)
+ continue;
+ const MeshAttributeCornerTable *const att_corner_table =
+ GetAttributeCornerTable(i);
+ // Attribute corner table may not be used in some configurations. For
+ // these cases we can assume the attribute connectivity to be the same as
+ // the connectivity of the position data.
+ if (att_corner_table)
+ attribute_corner_tables.push_back(att_corner_table);
+ }
+
+ // Add a new point based on the configuration of interior attribute seams
+ // (replicating what the decoder would do).
+ for (VertexIndex vi(0); vi < corner_table->num_vertices(); ++vi) {
+ if (corner_table->IsVertexIsolated(vi))
+ continue;
+ // Go around all corners of the vertex and keep track of the observed
+ // attribute seams.
+ const CornerIndex first_corner_index = corner_table->LeftMostCorner(vi);
+ const PointIndex first_point_index =
+ mesh()->CornerToPointId(first_corner_index);
+
+ PointIndex last_point_index = first_point_index;
+ CornerIndex last_corner_index = first_corner_index;
+ CornerIndex corner_index = corner_table->SwingRight(first_corner_index);
+ size_t num_attribute_seams = 0;
+ while (corner_index != kInvalidCornerIndex) {
+ const PointIndex point_index = mesh()->CornerToPointId(corner_index);
+ bool seam_found = false;
+ if (point_index != last_point_index) {
+ // Point index changed - new attribute seam detected.
+ seam_found = true;
+ last_point_index = point_index;
+ } else {
+ // Even though point indices matches, there still may be a seam caused
+ // by non-manifold connectivity of non-position attribute data.
+ for (int i = 0; i < attribute_corner_tables.size(); ++i) {
+ if (attribute_corner_tables[i]->Vertex(corner_index) !=
+ attribute_corner_tables[i]->Vertex(last_corner_index)) {
+ seam_found = true;
+ break; // No need to process other attributes.
+ }
+ }
+ }
+ if (seam_found) {
+ ++num_attribute_seams;
+ }
+
+ if (corner_index == first_corner_index)
+ break;
+
+ // Proceed to the next corner
+ last_corner_index = corner_index;
+ corner_index = corner_table->SwingRight(corner_index);
+ }
+
+ if (!corner_table->IsOnBoundary(vi) && num_attribute_seams > 0) {
+ // If the last visited point index is the same as the first point index
+ // we traveled all the way around the vertex. In this case the number of
+ // new points should be num_attribute_seams - 1
+ num_points += num_attribute_seams - 1;
+ } else {
+ // Else the vertex was either on a boundary (i.e. we couldn't travel all
+ // around the vertex), or we ended up at a different point. In both of
+ // these cases, the number of new points is equal to the number of
+ // attribute seams.
+ num_points += num_attribute_seams;
+ }
+ }
+ }
+ set_num_encoded_points(num_points);
+}
+
+void MeshEdgebreakerEncoder::ComputeNumberOfEncodedFaces() {
+ if (!impl_)
+ return;
+ const CornerTable *const corner_table = impl_->GetCornerTable();
+ if (!corner_table)
+ return;
+ set_num_encoded_faces(corner_table->num_faces() -
+ corner_table->NumDegeneratedFaces());
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder.h
new file mode 100644
index 00000000000..78615fb0287
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder.h
@@ -0,0 +1,73 @@
+// 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_COMPRESSION_MESH_MESH_EDGEBREAKER_ENCODER_H_
+#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_ENCODER_H_
+
+#include <unordered_map>
+
+#include "draco/compression/mesh/mesh_edgebreaker_encoder_impl_interface.h"
+#include "draco/compression/mesh/mesh_edgebreaker_shared.h"
+#include "draco/compression/mesh/mesh_encoder.h"
+#include "draco/mesh/corner_table.h"
+
+namespace draco {
+
+// Class implements the edge breaker geometry compression method as described
+// in "3D Compression Made Simple: Edgebreaker on a Corner-Table" by Rossignac
+// at al.'01. http://www.cc.gatech.edu/~jarek/papers/CornerTableSMI.pdf
+class MeshEdgebreakerEncoder : public MeshEncoder {
+ public:
+ MeshEdgebreakerEncoder();
+
+ const CornerTable *GetCornerTable() const override {
+ return impl_->GetCornerTable();
+ }
+
+ const MeshAttributeCornerTable *GetAttributeCornerTable(
+ int att_id) const override {
+ return impl_->GetAttributeCornerTable(att_id);
+ }
+
+ const MeshAttributeIndicesEncodingData *GetAttributeEncodingData(
+ int att_id) const override {
+ return impl_->GetAttributeEncodingData(att_id);
+ }
+
+ uint8_t GetEncodingMethod() const override {
+ return MESH_EDGEBREAKER_ENCODING;
+ }
+
+ protected:
+ bool InitializeEncoder() override;
+ bool EncodeConnectivity() override;
+ bool GenerateAttributesEncoder(int32_t att_id) override;
+ bool EncodeAttributesEncoderIdentifier(int32_t att_encoder_id) override;
+ void ComputeNumberOfEncodedPoints() override;
+ void ComputeNumberOfEncodedFaces() override;
+
+ private:
+ // The actual implementation of the edge breaker method. The implementations
+ // are in general specializations of a template class
+ // MeshEdgebreakerEncoderImpl where the template arguments control encoding
+ // of the connectivity data. The actual implementation is selected in this
+ // class based on the provided encoding options. Because this choice is done
+ // in run-time, the actual implementation has to be hidden behind the
+ // abstract interface MeshEdgebreakerEncoderImplInterface.
+ std::unique_ptr<MeshEdgebreakerEncoderImplInterface> impl_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder_impl.cc b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder_impl.cc
new file mode 100644
index 00000000000..89e6f0b5b12
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder_impl.cc
@@ -0,0 +1,830 @@
+// 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 "draco/compression/mesh/mesh_edgebreaker_encoder_impl.h"
+
+#include <algorithm>
+
+#include "draco/compression/attributes/sequential_attribute_encoders_controller.h"
+#include "draco/compression/mesh/mesh_edgebreaker_encoder.h"
+#include "draco/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h"
+#include "draco/compression/mesh/mesh_edgebreaker_traversal_valence_encoder.h"
+#include "draco/compression/mesh/traverser/depth_first_traverser.h"
+#include "draco/compression/mesh/traverser/max_prediction_degree_traverser.h"
+#include "draco/compression/mesh/traverser/mesh_attribute_indices_encoding_observer.h"
+#include "draco/compression/mesh/traverser/mesh_traversal_sequencer.h"
+#include "draco/compression/mesh/traverser/traverser_base.h"
+#include "draco/mesh/corner_table_iterators.h"
+#include "draco/mesh/mesh_misc_functions.h"
+
+namespace draco {
+// TODO(draco-eng) consider converting 'typedef' to 'using' and deduplicate.
+typedef CornerIndex CornerIndex;
+typedef FaceIndex FaceIndex;
+typedef VertexIndex VertexIndex;
+
+template <class TraversalEncoder>
+MeshEdgebreakerEncoderImpl<TraversalEncoder>::MeshEdgebreakerEncoderImpl()
+ : encoder_(nullptr),
+ mesh_(nullptr),
+ last_encoded_symbol_id_(-1),
+ num_split_symbols_(0),
+ use_single_connectivity_(false) {}
+
+template <class TraversalEncoder>
+bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::Init(
+ MeshEdgebreakerEncoder *encoder) {
+ encoder_ = encoder;
+ mesh_ = encoder->mesh();
+ attribute_encoder_to_data_id_map_.clear();
+
+ if (encoder_->options()->IsGlobalOptionSet("split_mesh_on_seams")) {
+ use_single_connectivity_ =
+ encoder_->options()->GetGlobalBool("split_mesh_on_seams", false);
+ } else if (encoder_->options()->GetSpeed() >= 6) {
+ // Else use default setting based on speed.
+ use_single_connectivity_ = true;
+ } else {
+ use_single_connectivity_ = false;
+ }
+ return true;
+}
+
+template <class TraversalEncoder>
+const MeshAttributeCornerTable *
+MeshEdgebreakerEncoderImpl<TraversalEncoder>::GetAttributeCornerTable(
+ int att_id) const {
+ 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;
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+template <class TraversalEncoder>
+const MeshAttributeIndicesEncodingData *
+MeshEdgebreakerEncoderImpl<TraversalEncoder>::GetAttributeEncodingData(
+ int att_id) const {
+ for (uint32_t i = 0; i < attribute_data_.size(); ++i) {
+ if (attribute_data_[i].attribute_index == att_id)
+ return &attribute_data_[i].encoding_data;
+ }
+ return &pos_encoding_data_;
+}
+
+template <class TraversalEncoder>
+template <class TraverserT>
+std::unique_ptr<PointsSequencer>
+MeshEdgebreakerEncoderImpl<TraversalEncoder>::CreateVertexTraversalSequencer(
+ MeshAttributeIndicesEncodingData *encoding_data) {
+ typedef typename TraverserT::TraversalObserver AttObserver;
+ typedef typename TraverserT::CornerTable CornerTable;
+
+ std::unique_ptr<MeshTraversalSequencer<TraverserT>> traversal_sequencer(
+ new MeshTraversalSequencer<TraverserT>(mesh_, encoding_data));
+
+ AttObserver att_observer(corner_table_.get(), mesh_,
+ traversal_sequencer.get(), encoding_data);
+
+ TraverserT att_traverser;
+ att_traverser.Init(corner_table_.get(), att_observer);
+
+ // Set order of corners to simulate the corner order of the decoder.
+ traversal_sequencer->SetCornerOrder(processed_connectivity_corners_);
+ traversal_sequencer->SetTraverser(att_traverser);
+ return std::move(traversal_sequencer);
+}
+
+template <class TraversalEncoder>
+bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::GenerateAttributesEncoder(
+ int32_t att_id) {
+ // For now, either create one encoder for each attribute or use a single
+ // encoder for all attributes. Ideally we can share the same encoder for
+ // a sub-set of attributes with the same connectivity (this is especially true
+ // for per-vertex attributes).
+ if (use_single_connectivity_ && GetEncoder()->num_attributes_encoders() > 0) {
+ // We are using single connectivity and we already have an attribute
+ // encoder. Add the attribute to the encoder and return.
+ GetEncoder()->attributes_encoder(0)->AddAttributeId(att_id);
+ return true;
+ }
+ const int32_t element_type =
+ GetEncoder()->mesh()->GetAttributeElementType(att_id);
+ const PointAttribute *const att =
+ GetEncoder()->point_cloud()->attribute(att_id);
+ int32_t att_data_id = -1;
+ for (uint32_t i = 0; i < attribute_data_.size(); ++i) {
+ if (attribute_data_[i].attribute_index == att_id) {
+ att_data_id = i;
+ break;
+ }
+ }
+ MeshTraversalMethod traversal_method = MESH_TRAVERSAL_DEPTH_FIRST;
+ std::unique_ptr<PointsSequencer> sequencer;
+ if (use_single_connectivity_ ||
+ att->attribute_type() == GeometryAttribute::POSITION ||
+ element_type == MESH_VERTEX_ATTRIBUTE ||
+ (element_type == MESH_CORNER_ATTRIBUTE &&
+ attribute_data_[att_data_id].connectivity_data.no_interior_seams())) {
+ // Per-vertex attribute reached, use the basic corner table to traverse the
+ // mesh.
+ MeshAttributeIndicesEncodingData *encoding_data;
+ if (use_single_connectivity_ ||
+ att->attribute_type() == GeometryAttribute::POSITION) {
+ encoding_data = &pos_encoding_data_;
+ } else {
+ encoding_data = &attribute_data_[att_data_id].encoding_data;
+
+ // Ensure we use the correct number of vertices in the encoding data.
+ encoding_data->vertex_to_encoded_attribute_value_index_map.assign(
+ corner_table_->num_vertices(), -1);
+
+ // Mark the attribute specific connectivity data as not used as we use the
+ // position attribute connectivity data.
+ attribute_data_[att_data_id].is_connectivity_used = false;
+ }
+
+ if (GetEncoder()->options()->GetSpeed() == 0 &&
+ att->attribute_type() == GeometryAttribute::POSITION) {
+ traversal_method = MESH_TRAVERSAL_PREDICTION_DEGREE;
+ if (use_single_connectivity_ && mesh_->num_attributes() > 1) {
+ // Make sure we don't use the prediction degree traversal when we encode
+ // multiple attributes using the same connectivity.
+ // TODO(ostava): We should investigate this and see if the prediction
+ // degree can be actually used efficiently for non-position attributes.
+ traversal_method = MESH_TRAVERSAL_DEPTH_FIRST;
+ }
+ }
+ // Defining sequencer via a traversal scheme.
+ if (traversal_method == MESH_TRAVERSAL_PREDICTION_DEGREE) {
+ typedef MeshAttributeIndicesEncodingObserver<CornerTable> AttObserver;
+ typedef MaxPredictionDegreeTraverser<CornerTable, AttObserver>
+ AttTraverser;
+ sequencer = CreateVertexTraversalSequencer<AttTraverser>(encoding_data);
+ } else if (traversal_method == MESH_TRAVERSAL_DEPTH_FIRST) {
+ typedef MeshAttributeIndicesEncodingObserver<CornerTable> AttObserver;
+ typedef DepthFirstTraverser<CornerTable, AttObserver> AttTraverser;
+ sequencer = CreateVertexTraversalSequencer<AttTraverser>(encoding_data);
+ }
+ } else {
+ // Per-corner attribute encoder.
+ typedef MeshAttributeIndicesEncodingObserver<MeshAttributeCornerTable>
+ AttObserver;
+ typedef DepthFirstTraverser<MeshAttributeCornerTable, AttObserver>
+ AttTraverser;
+
+ MeshAttributeIndicesEncodingData *const encoding_data =
+ &attribute_data_[att_data_id].encoding_data;
+ const MeshAttributeCornerTable *const corner_table =
+ &attribute_data_[att_data_id].connectivity_data;
+
+ // Ensure we use the correct number of vertices in the encoding data.
+ attribute_data_[att_data_id]
+ .encoding_data.vertex_to_encoded_attribute_value_index_map.assign(
+ attribute_data_[att_data_id].connectivity_data.num_vertices(), -1);
+
+ std::unique_ptr<MeshTraversalSequencer<AttTraverser>> traversal_sequencer(
+ new MeshTraversalSequencer<AttTraverser>(mesh_, encoding_data));
+
+ AttObserver att_observer(corner_table, mesh_, traversal_sequencer.get(),
+ encoding_data);
+
+ AttTraverser att_traverser;
+ att_traverser.Init(corner_table, att_observer);
+
+ // Set order of corners to simulate the corner order of the decoder.
+ traversal_sequencer->SetCornerOrder(processed_connectivity_corners_);
+ traversal_sequencer->SetTraverser(att_traverser);
+ sequencer = std::move(traversal_sequencer);
+ }
+
+ if (!sequencer)
+ return false;
+
+ if (att_data_id == -1) {
+ pos_traversal_method_ = traversal_method;
+ } else {
+ attribute_data_[att_data_id].traversal_method = traversal_method;
+ }
+
+ std::unique_ptr<SequentialAttributeEncodersController> att_controller(
+ new SequentialAttributeEncodersController(std::move(sequencer), att_id));
+
+ // Update the mapping between the encoder id and the attribute data id.
+ // This will be used by the decoder to select the appropriate attribute
+ // decoder and the correct connectivity.
+ attribute_encoder_to_data_id_map_.push_back(att_data_id);
+ GetEncoder()->AddAttributesEncoder(std::move(att_controller));
+ return true;
+} // namespace draco
+
+template <class TraversalEncoder>
+bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::
+ EncodeAttributesEncoderIdentifier(int32_t att_encoder_id) {
+ const int8_t att_data_id = attribute_encoder_to_data_id_map_[att_encoder_id];
+ encoder_->buffer()->Encode(att_data_id);
+
+ // Also encode the type of the encoder that we used.
+ int32_t element_type = MESH_VERTEX_ATTRIBUTE;
+ MeshTraversalMethod traversal_method;
+ if (att_data_id >= 0) {
+ const int32_t att_id = attribute_data_[att_data_id].attribute_index;
+ element_type = GetEncoder()->mesh()->GetAttributeElementType(att_id);
+ traversal_method = attribute_data_[att_data_id].traversal_method;
+ } else {
+ traversal_method = pos_traversal_method_;
+ }
+ if (element_type == MESH_VERTEX_ATTRIBUTE ||
+ (element_type == MESH_CORNER_ATTRIBUTE &&
+ attribute_data_[att_data_id].connectivity_data.no_interior_seams())) {
+ // Per-vertex encoder.
+ encoder_->buffer()->Encode(static_cast<uint8_t>(MESH_VERTEX_ATTRIBUTE));
+ } else {
+ // Per-corner encoder.
+ encoder_->buffer()->Encode(static_cast<uint8_t>(MESH_CORNER_ATTRIBUTE));
+ }
+ // Encode the mesh traversal method.
+ encoder_->buffer()->Encode(static_cast<uint8_t>(traversal_method));
+ return true;
+}
+
+template <class TraversalEncoder>
+bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::EncodeConnectivity() {
+ // To encode the mesh, we need face connectivity data stored in a corner
+ // table. To compute the connectivity we must use indices associated with
+ // POSITION attribute, because they define which edges can be connected
+ // together, unless the option |use_single_connectivity_| is set in which case
+ // we break the mesh along attribute seams and use the same connectivity for
+ // all attributes.
+ if (use_single_connectivity_) {
+ corner_table_ = CreateCornerTableFromAllAttributes(mesh_);
+ } else {
+ corner_table_ = CreateCornerTableFromPositionAttribute(mesh_);
+ }
+ if (corner_table_ == nullptr ||
+ corner_table_->num_faces() == corner_table_->NumDegeneratedFaces()) {
+ // Failed to construct the corner table.
+ // TODO(ostava): Add better error reporting.
+ return false;
+ }
+
+ traversal_encoder_.Init(this);
+
+ // Also encode the total number of vertices that is going to be encoded.
+ // This can be different from the mesh_->num_points() + num_new_vertices,
+ // because some of the vertices of the input mesh can be ignored (e.g.
+ // vertices on degenerated faces or isolated vertices not attached to any
+ // face).
+ const uint32_t num_vertices_to_be_encoded =
+ corner_table_->num_vertices() - corner_table_->NumIsolatedVertices();
+ EncodeVarint(num_vertices_to_be_encoded, encoder_->buffer());
+
+ const uint32_t num_faces =
+ corner_table_->num_faces() - corner_table_->NumDegeneratedFaces();
+ EncodeVarint(num_faces, encoder_->buffer());
+
+ // Reset encoder data that may have been initialized in previous runs.
+ visited_faces_.assign(mesh_->num_faces(), false);
+ pos_encoding_data_.vertex_to_encoded_attribute_value_index_map.assign(
+ corner_table_->num_vertices(), -1);
+ pos_encoding_data_.encoded_attribute_value_index_to_corner_map.clear();
+ pos_encoding_data_.encoded_attribute_value_index_to_corner_map.reserve(
+ corner_table_->num_faces() * 3);
+ visited_vertex_ids_.assign(corner_table_->num_vertices(), false);
+ vertex_traversal_length_.clear();
+ last_encoded_symbol_id_ = -1;
+ num_split_symbols_ = 0;
+ topology_split_event_data_.clear();
+ face_to_split_symbol_map_.clear();
+ visited_holes_.clear();
+ vertex_hole_id_.assign(corner_table_->num_vertices(), -1);
+ processed_connectivity_corners_.clear();
+ processed_connectivity_corners_.reserve(corner_table_->num_faces());
+ pos_encoding_data_.num_values = 0;
+
+ if (!FindHoles())
+ return false;
+
+ if (!InitAttributeData())
+ return false;
+
+ const uint8_t num_attribute_data =
+ static_cast<uint8_t>(attribute_data_.size());
+ encoder_->buffer()->Encode(num_attribute_data);
+ traversal_encoder_.SetNumAttributeData(num_attribute_data);
+
+ const int num_corners = corner_table_->num_corners();
+
+ traversal_encoder_.Start();
+
+ std::vector<CornerIndex> init_face_connectivity_corners;
+ // Traverse the surface starting from each unvisited corner.
+ for (int c_id = 0; c_id < num_corners; ++c_id) {
+ CornerIndex corner_index(c_id);
+ const FaceIndex face_id = corner_table_->Face(corner_index);
+ if (visited_faces_[face_id.value()])
+ continue; // Face has been already processed.
+ if (corner_table_->IsDegenerated(face_id))
+ continue; // Ignore degenerated faces.
+
+ CornerIndex start_corner;
+ const bool interior_config =
+ FindInitFaceConfiguration(face_id, &start_corner);
+ traversal_encoder_.EncodeStartFaceConfiguration(interior_config);
+
+ if (interior_config) {
+ // Select the correct vertex on the face as the root.
+ corner_index = start_corner;
+ const VertexIndex vert_id = corner_table_->Vertex(corner_index);
+ // Mark all vertices of a given face as visited.
+ const VertexIndex next_vert_id =
+ corner_table_->Vertex(corner_table_->Next(corner_index));
+ const VertexIndex prev_vert_id =
+ corner_table_->Vertex(corner_table_->Previous(corner_index));
+
+ visited_vertex_ids_[vert_id.value()] = true;
+ visited_vertex_ids_[next_vert_id.value()] = true;
+ visited_vertex_ids_[prev_vert_id.value()] = true;
+ // New traversal started. Initiate it's length with the first vertex.
+ vertex_traversal_length_.push_back(1);
+
+ // Mark the face as visited.
+ visited_faces_[face_id.value()] = true;
+ // Start compressing from the opposite face of the "next" corner. This way
+ // the first encoded corner corresponds to the tip corner of the regular
+ // edgebreaker traversal (essentially the initial face can be then viewed
+ // as a TOPOLOGY_C face).
+ init_face_connectivity_corners.push_back(
+ corner_table_->Next(corner_index));
+ const CornerIndex opp_id =
+ corner_table_->Opposite(corner_table_->Next(corner_index));
+ const FaceIndex opp_face_id = corner_table_->Face(opp_id);
+ if (opp_face_id != kInvalidFaceIndex &&
+ !visited_faces_[opp_face_id.value()]) {
+ if (!EncodeConnectivityFromCorner(opp_id))
+ return false;
+ }
+ } else {
+ // Boundary configuration. We start on a boundary rather than on a face.
+ // First encode the hole that's opposite to the start_corner.
+ EncodeHole(corner_table_->Next(start_corner), true);
+ // Start processing the face opposite to the boundary edge (the face
+ // containing the start_corner).
+ if (!EncodeConnectivityFromCorner(start_corner))
+ return false;
+ }
+ }
+ // Reverse the order of connectivity corners to match the order in which
+ // they are going to be decoded.
+ std::reverse(processed_connectivity_corners_.begin(),
+ processed_connectivity_corners_.end());
+ // Append the init face connectivity corners (which are processed in order by
+ // the decoder after the regular corners.
+ processed_connectivity_corners_.insert(processed_connectivity_corners_.end(),
+ init_face_connectivity_corners.begin(),
+ init_face_connectivity_corners.end());
+ // Encode connectivity for all non-position attributes.
+ if (attribute_data_.size() > 0) {
+ // Use the same order of corner that will be used by the decoder.
+ visited_faces_.assign(mesh_->num_faces(), false);
+ for (CornerIndex ci : processed_connectivity_corners_) {
+ EncodeAttributeConnectivitiesOnFace(ci);
+ }
+ }
+ traversal_encoder_.Done();
+
+ // Encode the number of symbols.
+ const uint32_t num_encoded_symbols =
+ static_cast<uint32_t>(traversal_encoder_.NumEncodedSymbols());
+ EncodeVarint(num_encoded_symbols, encoder_->buffer());
+
+ // Encode the number of split symbols.
+ EncodeVarint(num_split_symbols_, encoder_->buffer());
+
+ // Append the traversal buffer.
+ if (!EncodeSplitData())
+ return false;
+ encoder_->buffer()->Encode(traversal_encoder_.buffer().data(),
+ traversal_encoder_.buffer().size());
+
+ return true;
+}
+
+template <class TraversalEncoder>
+bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::EncodeSplitData() {
+ uint32_t num_events =
+ static_cast<uint32_t>(topology_split_event_data_.size());
+ EncodeVarint(num_events, encoder_->buffer());
+ if (num_events > 0) {
+ // Encode split symbols using delta and varint coding. Split edges are
+ // encoded using direct bit coding.
+ int last_source_symbol_id = 0; // Used for delta coding.
+ for (uint32_t i = 0; i < num_events; ++i) {
+ const TopologySplitEventData &event_data = topology_split_event_data_[i];
+ // Encode source symbol id as delta from the previous source symbol id.
+ // Source symbol ids are always stored in increasing order so the delta is
+ // going to be positive.
+ EncodeVarint<uint32_t>(
+ event_data.source_symbol_id - last_source_symbol_id,
+ encoder_->buffer());
+ // Encode split symbol id as delta from the current source symbol id.
+ // Split symbol id is always smaller than source symbol id so the below
+ // delta is going to be positive.
+ EncodeVarint<uint32_t>(
+ event_data.source_symbol_id - event_data.split_symbol_id,
+ encoder_->buffer());
+ last_source_symbol_id = event_data.source_symbol_id;
+ }
+ encoder_->buffer()->StartBitEncoding(num_events, false);
+ for (uint32_t i = 0; i < num_events; ++i) {
+ const TopologySplitEventData &event_data = topology_split_event_data_[i];
+ encoder_->buffer()->EncodeLeastSignificantBits32(1,
+ event_data.source_edge);
+ }
+ encoder_->buffer()->EndBitEncoding();
+ }
+ return true;
+}
+
+template <class TraversalEncoder>
+bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::FindInitFaceConfiguration(
+ FaceIndex face_id, CornerIndex *out_corner) const {
+ CornerIndex corner_index = CornerIndex(3 * face_id.value());
+ for (int i = 0; i < 3; ++i) {
+ if (corner_table_->Opposite(corner_index) == kInvalidCornerIndex) {
+ // If there is a boundary edge, the configuration is exterior and return
+ // the CornerIndex opposite to the first boundary edge.
+ *out_corner = corner_index;
+ return false;
+ }
+ if (vertex_hole_id_[corner_table_->Vertex(corner_index).value()] != -1) {
+ // Boundary vertex found. Find the first boundary edge attached to the
+ // point and return the corner opposite to it.
+ CornerIndex right_corner = corner_index;
+ while (right_corner != kInvalidCornerIndex) {
+ corner_index = right_corner;
+ right_corner = corner_table_->SwingRight(right_corner);
+ }
+ // |corner_index| now lies on a boundary edge and its previous corner is
+ // guaranteed to be the opposite corner of the boundary edge.
+ *out_corner = corner_table_->Previous(corner_index);
+ return false;
+ }
+ corner_index = corner_table_->Next(corner_index);
+ }
+ // Else we have an interior configuration. Return the first corner id.
+ *out_corner = corner_index;
+ return true;
+}
+
+template <class TraversalEncoder>
+bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::EncodeConnectivityFromCorner(
+ CornerIndex corner_id) {
+ corner_traversal_stack_.clear();
+ corner_traversal_stack_.push_back(corner_id);
+ const int num_faces = mesh_->num_faces();
+ while (!corner_traversal_stack_.empty()) {
+ // Currently processed corner.
+ corner_id = corner_traversal_stack_.back();
+ // Make sure the face hasn't been visited yet.
+ if (corner_id == kInvalidCornerIndex ||
+ visited_faces_[corner_table_->Face(corner_id).value()]) {
+ // This face has been already traversed.
+ corner_traversal_stack_.pop_back();
+ continue;
+ }
+ int num_visited_faces = 0;
+ while (num_visited_faces < num_faces) {
+ // Mark the current face as visited.
+ ++num_visited_faces;
+ ++last_encoded_symbol_id_;
+
+ const FaceIndex face_id = corner_table_->Face(corner_id);
+ visited_faces_[face_id.value()] = true;
+ processed_connectivity_corners_.push_back(corner_id);
+ traversal_encoder_.NewCornerReached(corner_id);
+ const VertexIndex vert_id = corner_table_->Vertex(corner_id);
+ const bool on_boundary = (vertex_hole_id_[vert_id.value()] != -1);
+ if (!IsVertexVisited(vert_id)) {
+ // A new unvisited vertex has been reached. We need to store its
+ // position difference using next, prev, and opposite vertices.
+ visited_vertex_ids_[vert_id.value()] = true;
+ if (!on_boundary) {
+ // If the vertex is on boundary it must correspond to an unvisited
+ // hole and it will be encoded with TOPOLOGY_S symbol later).
+ traversal_encoder_.EncodeSymbol(TOPOLOGY_C);
+ // Move to the right triangle.
+ corner_id = GetRightCorner(corner_id);
+ continue;
+ }
+ }
+ // The current vertex has been already visited or it was on a boundary.
+ // We need to determine whether we can visit any of it's neighboring
+ // faces.
+ const CornerIndex right_corner_id = GetRightCorner(corner_id);
+ const CornerIndex left_corner_id = GetLeftCorner(corner_id);
+ const FaceIndex right_face_id = corner_table_->Face(right_corner_id);
+ const FaceIndex left_face_id = corner_table_->Face(left_corner_id);
+ if (IsRightFaceVisited(corner_id)) {
+ // Right face has been already visited.
+ // Check whether there is a topology split event.
+ if (right_face_id != kInvalidFaceIndex)
+ CheckAndStoreTopologySplitEvent(last_encoded_symbol_id_,
+ face_id.value(), RIGHT_FACE_EDGE,
+ right_face_id.value());
+ if (IsLeftFaceVisited(corner_id)) {
+ // Both neighboring faces are visited. End reached.
+ // Check whether there is a topology split event on the left face.
+ if (left_face_id != kInvalidFaceIndex)
+ CheckAndStoreTopologySplitEvent(last_encoded_symbol_id_,
+ face_id.value(), LEFT_FACE_EDGE,
+ left_face_id.value());
+ traversal_encoder_.EncodeSymbol(TOPOLOGY_E);
+ corner_traversal_stack_.pop_back();
+ break; // Break from the while (num_visited_faces < num_faces) loop.
+ } else {
+ traversal_encoder_.EncodeSymbol(TOPOLOGY_R);
+ // Go to the left face.
+ corner_id = left_corner_id;
+ }
+ } else {
+ // Right face was not visited.
+ if (IsLeftFaceVisited(corner_id)) {
+ // Check whether there is a topology split event on the left face.
+ if (left_face_id != kInvalidFaceIndex)
+ CheckAndStoreTopologySplitEvent(last_encoded_symbol_id_,
+ face_id.value(), LEFT_FACE_EDGE,
+ left_face_id.value());
+ traversal_encoder_.EncodeSymbol(TOPOLOGY_L);
+ // Left face visited, go to the right one.
+ corner_id = right_corner_id;
+ } else {
+ traversal_encoder_.EncodeSymbol(TOPOLOGY_S);
+ ++num_split_symbols_;
+ // Both neighboring faces are unvisited, we need to visit both of
+ // them.
+ if (on_boundary) {
+ // The tip vertex is on a hole boundary. If the hole hasn't been
+ // visited yet we need to encode it.
+ const int hole_id = vertex_hole_id_[vert_id.value()];
+ if (!visited_holes_[hole_id]) {
+ EncodeHole(corner_id, false);
+ }
+ }
+ face_to_split_symbol_map_[face_id.value()] = last_encoded_symbol_id_;
+ // Split the traversal.
+ // First make the top of the current corner stack point to the left
+ // face (this one will be processed second).
+ corner_traversal_stack_.back() = left_corner_id;
+ // Add a new corner to the top of the stack (right face needs to
+ // be traversed first).
+ corner_traversal_stack_.push_back(right_corner_id);
+ // Break from the while (num_visited_faces < num_faces) loop.
+ break;
+ }
+ }
+ }
+ }
+ return true; // All corners have been processed.
+}
+
+template <class TraversalEncoder>
+int MeshEdgebreakerEncoderImpl<TraversalEncoder>::EncodeHole(
+ CornerIndex start_corner_id, bool encode_first_vertex) {
+ // We know that the start corner lies on a hole but we first need to find the
+ // boundary edge going from that vertex. It is the first edge in CW
+ // direction.
+ CornerIndex corner_id = start_corner_id;
+ corner_id = corner_table_->Previous(corner_id);
+ while (corner_table_->Opposite(corner_id) != kInvalidCornerIndex) {
+ corner_id = corner_table_->Opposite(corner_id);
+ corner_id = corner_table_->Next(corner_id);
+ }
+ const VertexIndex start_vertex_id = corner_table_->Vertex(start_corner_id);
+
+ int num_encoded_hole_verts = 0;
+ if (encode_first_vertex) {
+ visited_vertex_ids_[start_vertex_id.value()] = true;
+ ++num_encoded_hole_verts;
+ }
+
+ // corner_id is now opposite to the boundary edge.
+ // Mark the hole as visited.
+ visited_holes_[vertex_hole_id_[start_vertex_id.value()]] = true;
+ // Get the start vertex of the edge and use it as a reference.
+ VertexIndex start_vert_id =
+ corner_table_->Vertex(corner_table_->Next(corner_id));
+ // Get the end vertex of the edge.
+ VertexIndex act_vertex_id =
+ corner_table_->Vertex(corner_table_->Previous(corner_id));
+ while (act_vertex_id != start_vertex_id) {
+ // Encode the end vertex of the boundary edge.
+
+ start_vert_id = act_vertex_id;
+
+ // Mark the vertex as visited.
+ visited_vertex_ids_[act_vertex_id.value()] = true;
+ ++num_encoded_hole_verts;
+ corner_id = corner_table_->Next(corner_id);
+ // Look for the next attached open boundary edge.
+ while (corner_table_->Opposite(corner_id) != kInvalidCornerIndex) {
+ corner_id = corner_table_->Opposite(corner_id);
+ corner_id = corner_table_->Next(corner_id);
+ }
+ act_vertex_id = corner_table_->Vertex(corner_table_->Previous(corner_id));
+ }
+ return num_encoded_hole_verts;
+}
+
+template <class TraversalEncoder>
+CornerIndex MeshEdgebreakerEncoderImpl<TraversalEncoder>::GetRightCorner(
+ CornerIndex corner_id) const {
+ const CornerIndex next_corner_id = corner_table_->Next(corner_id);
+ return corner_table_->Opposite(next_corner_id);
+}
+
+template <class TraversalEncoder>
+CornerIndex MeshEdgebreakerEncoderImpl<TraversalEncoder>::GetLeftCorner(
+ CornerIndex corner_id) const {
+ const CornerIndex prev_corner_id = corner_table_->Previous(corner_id);
+ return corner_table_->Opposite(prev_corner_id);
+}
+
+template <class TraversalEncoder>
+bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::IsRightFaceVisited(
+ CornerIndex corner_id) const {
+ const CornerIndex next_corner_id = corner_table_->Next(corner_id);
+ const CornerIndex opp_corner_id = corner_table_->Opposite(next_corner_id);
+ if (opp_corner_id != kInvalidCornerIndex)
+ return visited_faces_[corner_table_->Face(opp_corner_id).value()];
+ // Else we are on a boundary.
+ return true;
+}
+
+template <class TraversalEncoder>
+bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::IsLeftFaceVisited(
+ CornerIndex corner_id) const {
+ const CornerIndex prev_corner_id = corner_table_->Previous(corner_id);
+ const CornerIndex opp_corner_id = corner_table_->Opposite(prev_corner_id);
+ if (opp_corner_id != kInvalidCornerIndex)
+ return visited_faces_[corner_table_->Face(opp_corner_id).value()];
+ // Else we are on a boundary.
+ return true;
+}
+
+template <class TraversalEncoder>
+bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::FindHoles() {
+ // TODO(ostava): Add more error checking for invalid geometry data.
+ const int num_corners = corner_table_->num_corners();
+ // Go over all corners and detect non-visited open boundaries
+ for (CornerIndex i(0); i < num_corners; ++i) {
+ if (corner_table_->IsDegenerated(corner_table_->Face(i)))
+ continue; // Don't process corners assigned to degenerated faces.
+ if (corner_table_->Opposite(i) == kInvalidCornerIndex) {
+ // No opposite corner means no opposite face, so the opposite edge
+ // of the corner is an open boundary.
+ // Check whether we have already traversed the boundary.
+ VertexIndex boundary_vert_id =
+ corner_table_->Vertex(corner_table_->Next(i));
+ if (vertex_hole_id_[boundary_vert_id.value()] != -1) {
+ // The start vertex of the boundary edge is already assigned to an
+ // open boundary. No need to traverse it again.
+ continue;
+ }
+ // Else we found a new open boundary and we are going to traverse along it
+ // and mark all visited vertices.
+ const int boundary_id = static_cast<int>(visited_holes_.size());
+ visited_holes_.push_back(false);
+
+ CornerIndex corner_id = i;
+ while (vertex_hole_id_[boundary_vert_id.value()] == -1) {
+ // Mark the first vertex on the open boundary.
+ vertex_hole_id_[boundary_vert_id.value()] = boundary_id;
+ corner_id = corner_table_->Next(corner_id);
+ // Look for the next attached open boundary edge.
+ while (corner_table_->Opposite(corner_id) != kInvalidCornerIndex) {
+ corner_id = corner_table_->Opposite(corner_id);
+ corner_id = corner_table_->Next(corner_id);
+ }
+ // Id of the next vertex in the vertex on the hole.
+ boundary_vert_id =
+ corner_table_->Vertex(corner_table_->Next(corner_id));
+ }
+ }
+ }
+ return true;
+}
+
+template <class TraversalEncoder>
+int MeshEdgebreakerEncoderImpl<TraversalEncoder>::GetSplitSymbolIdOnFace(
+ int face_id) const {
+ auto it = face_to_split_symbol_map_.find(face_id);
+ if (it == face_to_split_symbol_map_.end())
+ return -1;
+ return it->second;
+}
+
+template <class TraversalEncoder>
+void MeshEdgebreakerEncoderImpl<
+ TraversalEncoder>::CheckAndStoreTopologySplitEvent(int src_symbol_id,
+ int /* src_face_id */,
+ EdgeFaceName src_edge,
+ int neighbor_face_id) {
+ const int symbol_id = GetSplitSymbolIdOnFace(neighbor_face_id);
+ if (symbol_id == -1)
+ return; // Not a split symbol, no topology split event could happen.
+ TopologySplitEventData event_data;
+
+ event_data.split_symbol_id = symbol_id;
+ event_data.source_symbol_id = src_symbol_id;
+ event_data.source_edge = src_edge;
+ topology_split_event_data_.push_back(event_data);
+}
+
+template <class TraversalEncoder>
+bool MeshEdgebreakerEncoderImpl<TraversalEncoder>::InitAttributeData() {
+ if (use_single_connectivity_)
+ return true; // All attributes use the same connectivity.
+
+ const int num_attributes = mesh_->num_attributes();
+ // Ignore the position attribute. It's decoded separately.
+ attribute_data_.resize(num_attributes - 1);
+ if (num_attributes == 1)
+ return true;
+ int data_index = 0;
+ for (int i = 0; i < num_attributes; ++i) {
+ const int32_t att_index = i;
+ if (mesh_->attribute(att_index)->attribute_type() ==
+ GeometryAttribute::POSITION)
+ continue;
+ const PointAttribute *const att = mesh_->attribute(att_index);
+ attribute_data_[data_index].attribute_index = att_index;
+ attribute_data_[data_index]
+ .encoding_data.encoded_attribute_value_index_to_corner_map.clear();
+ attribute_data_[data_index]
+ .encoding_data.encoded_attribute_value_index_to_corner_map.reserve(
+ corner_table_->num_corners());
+ attribute_data_[data_index].encoding_data.num_values = 0;
+ attribute_data_[data_index].connectivity_data.InitFromAttribute(
+ mesh_, corner_table_.get(), att);
+ ++data_index;
+ }
+ return true;
+}
+
+// TODO(ostava): Note that if the input mesh used the same attribute index on
+// multiple different vertices, such attribute will be duplicated using the
+// encoding below. Eventually, we may consider either using a different encoding
+// scheme for such cases, or at least deduplicating the attributes in the
+// decoder.
+template <class TraversalEncoder>
+bool MeshEdgebreakerEncoderImpl<
+ TraversalEncoder>::EncodeAttributeConnectivitiesOnFace(CornerIndex corner) {
+ // Three corners of the face.
+ const CornerIndex corners[3] = {corner, corner_table_->Next(corner),
+ corner_table_->Previous(corner)};
+
+ const FaceIndex src_face_id = corner_table_->Face(corner);
+ visited_faces_[src_face_id.value()] = true;
+ for (int c = 0; c < 3; ++c) {
+ const CornerIndex opp_corner = corner_table_->Opposite(corners[c]);
+ if (opp_corner == kInvalidCornerIndex)
+ continue; // Don't encode attribute seams on boundary edges.
+ const FaceIndex opp_face_id = corner_table_->Face(opp_corner);
+ // Don't encode edges when the opposite face has been already processed.
+ if (visited_faces_[opp_face_id.value()])
+ continue;
+
+ for (uint32_t i = 0; i < attribute_data_.size(); ++i) {
+ if (attribute_data_[i].connectivity_data.IsCornerOppositeToSeamEdge(
+ corners[c])) {
+ traversal_encoder_.EncodeAttributeSeam(i, true);
+ } else {
+ traversal_encoder_.EncodeAttributeSeam(i, false);
+ }
+ }
+ }
+ return true;
+}
+
+template class MeshEdgebreakerEncoderImpl<MeshEdgebreakerTraversalEncoder>;
+template class MeshEdgebreakerEncoderImpl<
+ MeshEdgebreakerTraversalPredictiveEncoder>;
+template class MeshEdgebreakerEncoderImpl<
+ MeshEdgebreakerTraversalValenceEncoder>;
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder_impl.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder_impl.h
new file mode 100644
index 00000000000..997bd1565e3
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder_impl.h
@@ -0,0 +1,210 @@
+// 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_COMPRESSION_MESH_MESH_EDGEBREAKER_ENCODER_IMPL_H_
+#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_ENCODER_IMPL_H_
+
+#include <unordered_map>
+
+#include "draco/compression/attributes/mesh_attribute_indices_encoding_data.h"
+#include "draco/compression/config/compression_shared.h"
+#include "draco/compression/mesh/mesh_edgebreaker_encoder_impl_interface.h"
+#include "draco/compression/mesh/mesh_edgebreaker_shared.h"
+#include "draco/compression/mesh/traverser/mesh_traversal_sequencer.h"
+#include "draco/core/encoder_buffer.h"
+#include "draco/mesh/mesh_attribute_corner_table.h"
+
+namespace draco {
+
+// Class implementing the edgebreaker encoding as described in "3D Compression
+// Made Simple: Edgebreaker on a Corner-Table" by Rossignac at al.'01.
+// http://www.cc.gatech.edu/~jarek/papers/CornerTableSMI.pdf
+template <class TraversalEncoderT>
+class MeshEdgebreakerEncoderImpl : public MeshEdgebreakerEncoderImplInterface {
+ public:
+ MeshEdgebreakerEncoderImpl();
+ explicit MeshEdgebreakerEncoderImpl(
+ const TraversalEncoderT &traversal_encoder);
+ bool Init(MeshEdgebreakerEncoder *encoder) override;
+
+ const MeshAttributeCornerTable *GetAttributeCornerTable(
+ int att_id) const override;
+ const MeshAttributeIndicesEncodingData *GetAttributeEncodingData(
+ int att_id) const override;
+
+ bool GenerateAttributesEncoder(int32_t att_id) override;
+ bool EncodeAttributesEncoderIdentifier(int32_t att_encoder_id) override;
+ bool EncodeConnectivity() override;
+
+ const CornerTable *GetCornerTable() const override {
+ return corner_table_.get();
+ }
+ bool IsFaceEncoded(FaceIndex fi) const override {
+ return visited_faces_[fi.value()];
+ }
+ MeshEdgebreakerEncoder *GetEncoder() const override { return encoder_; }
+
+ private:
+ // Initializes data needed for encoding non-position attributes.
+ // Returns false on error.
+ bool InitAttributeData();
+
+ // Creates a vertex traversal sequencer for the specified |TraverserT| type.
+ template <class TraverserT>
+ std::unique_ptr<PointsSequencer> CreateVertexTraversalSequencer(
+ MeshAttributeIndicesEncodingData *encoding_data);
+
+ // Finds the configuration of the initial face that starts the traversal.
+ // Configurations are determined by location of holes around the init face
+ // and they are described in mesh_edgebreaker_shared.h.
+ // Returns true if the face configuration is interior and false if it is
+ // exterior.
+ bool FindInitFaceConfiguration(FaceIndex face_id,
+ CornerIndex *out_corner_id) const;
+
+ // Encodes the connectivity between vertices.
+ bool EncodeConnectivityFromCorner(CornerIndex corner_id);
+
+ // Encodes all vertices of a hole starting at start_corner_id.
+ // The vertex associated with the first corner is encoded only if
+ // |encode_first_vertex| is true.
+ // Returns the number of encoded hole vertices.
+ int EncodeHole(CornerIndex start_corner_id, bool encode_first_vertex);
+
+ // Encodes topology split data.
+ // Returns nullptr on error.
+ bool EncodeSplitData();
+
+ CornerIndex GetRightCorner(CornerIndex corner_id) const;
+ CornerIndex GetLeftCorner(CornerIndex corner_id) const;
+
+ bool IsRightFaceVisited(CornerIndex corner_id) const;
+ bool IsLeftFaceVisited(CornerIndex corner_id) const;
+ bool IsVertexVisited(VertexIndex vert_id) const {
+ return visited_vertex_ids_[vert_id.value()];
+ }
+
+ // Finds and stores data about all holes in the input mesh.
+ bool FindHoles();
+
+ // For faces encoded with symbol TOPOLOGY_S (split), this method returns
+ // the encoded symbol id or -1 if the face wasn't encoded by a split symbol.
+ int GetSplitSymbolIdOnFace(int face_id) const;
+
+ // Checks whether there is a topology split event on a neighboring face and
+ // stores the event data if necessary. For more info about topology split
+ // events, see description of TopologySplitEventData in
+ // mesh_edgebreaker_shared.h.
+ void CheckAndStoreTopologySplitEvent(int src_symbol_id, int src_face_id,
+ EdgeFaceName src_edge,
+ int neighbor_face_id);
+
+ // Encodes connectivity of all attributes on a newly traversed face.
+ bool EncodeAttributeConnectivitiesOnFace(CornerIndex corner);
+
+ // This function is used to to assign correct encoding order of attributes
+ // to unprocessed corners. The encoding order is equal to the order in which
+ // the attributes are going to be processed by the decoder and it is necessary
+ // for proper prediction of attribute values.
+ bool AssignPositionEncodingOrderToAllCorners();
+
+ // This function is used to generate encoding order for all non-position
+ // attributes.
+ // Returns false when one or more attributes failed to be processed.
+ bool GenerateEncodingOrderForAttributes();
+
+ // The main encoder that owns this class.
+ MeshEdgebreakerEncoder *encoder_;
+ // Mesh that's being encoded.
+ const Mesh *mesh_;
+ // Corner table stores the mesh face connectivity data.
+ std::unique_ptr<CornerTable> corner_table_;
+ // Stack used for storing corners that need to be traversed when encoding
+ // the connectivity. New corner is added for each initial face and a split
+ // symbol, and one corner is removed when the end symbol is reached.
+ // Stored as member variable to prevent frequent memory reallocations when
+ // handling meshes with lots of disjoint components. Originally, we used
+ // recursive functions to handle this behavior, but that can cause stack
+ // memory overflow when compressing huge meshes.
+ std::vector<CornerIndex> corner_traversal_stack_;
+ // Array for marking visited faces.
+ std::vector<bool> visited_faces_;
+
+ // Attribute data for position encoding.
+ MeshAttributeIndicesEncodingData pos_encoding_data_;
+
+ // Traversal method used for the position attribute.
+ MeshTraversalMethod pos_traversal_method_;
+
+ // Array storing corners in the order they were visited during the
+ // connectivity encoding (always storing the tip corner of each newly visited
+ // face).
+ std::vector<CornerIndex> processed_connectivity_corners_;
+
+ // Array for storing visited vertex ids of all input vertices.
+ std::vector<bool> visited_vertex_ids_;
+
+ // For each traversal, this array stores the number of visited vertices.
+ std::vector<int> vertex_traversal_length_;
+ // Array for storing all topology split events encountered during the mesh
+ // traversal.
+ std::vector<TopologySplitEventData> topology_split_event_data_;
+ // Map between face_id and symbol_id. Contains entries only for faces that
+ // were encoded with TOPOLOGY_S symbol.
+ std::unordered_map<int, int> face_to_split_symbol_map_;
+
+ // Array for marking holes that has been reached during the traversal.
+ std::vector<bool> visited_holes_;
+ // Array for mapping vertices to hole ids. If a vertex is not on a hole, the
+ // stored value is -1.
+ std::vector<int> vertex_hole_id_;
+
+ // Id of the last encoded symbol.
+ int last_encoded_symbol_id_;
+
+ // The number of encoded split symbols.
+ uint32_t num_split_symbols_;
+
+ // Struct holding data used for encoding each non-position attribute.
+ // TODO(ostava): This should be probably renamed to something better.
+ struct AttributeData {
+ AttributeData() : attribute_index(-1), is_connectivity_used(true) {}
+ int attribute_index;
+ MeshAttributeCornerTable connectivity_data;
+ // Flag that can mark the connectivity_data invalid. In such case the base
+ // corner table of the mesh should be used instead.
+ bool is_connectivity_used;
+ // Data about attribute encoding order.
+ MeshAttributeIndicesEncodingData encoding_data;
+ // Traversal method used to generate the encoding data for this attribute.
+ MeshTraversalMethod traversal_method;
+ };
+ std::vector<AttributeData> attribute_data_;
+
+ // Array storing mapping between attribute encoder id and attribute data id.
+ std::vector<int32_t> attribute_encoder_to_data_id_map_;
+
+ TraversalEncoderT traversal_encoder_;
+
+ // If set, the encoder is going to use the same connectivity for all
+ // attributes. This effectively breaks the mesh along all attribute seams.
+ // In general, this approach should be much faster compared to encoding each
+ // connectivity separately, but the decoded model may contain higher number of
+ // duplicate attribute values which may decrease the compression ratio.
+ bool use_single_connectivity_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_ENCODER_IMPL_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder_impl_interface.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder_impl_interface.h
new file mode 100644
index 00000000000..8da3541ec86
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoder_impl_interface.h
@@ -0,0 +1,57 @@
+// 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_COMPRESSION_MESH_MESH_EDGEBREAKER_ENCODER_IMPL_INTERFACE_H_
+#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_ENCODER_IMPL_INTERFACE_H_
+
+#include "draco/compression/attributes/mesh_attribute_indices_encoding_data.h"
+#include "draco/mesh/corner_table.h"
+#include "draco/mesh/mesh_attribute_corner_table.h"
+
+namespace draco {
+
+// Forward declaration is necessary here to avoid circular dependencies.
+class MeshEdgebreakerEncoder;
+
+// Abstract interface used by MeshEdgebreakerEncoder to interact with the actual
+// implementation of the edgebreaker method. The implementations are in general
+// specializations of a template class MeshEdgebreakerEncoderImpl where the
+// template arguments control encoding of the connectivity data. Because the
+// choice of the implementation is done in run-time, we need to hide it behind
+// the abstract interface MeshEdgebreakerEncoderImplInterface.
+class MeshEdgebreakerEncoderImplInterface {
+ public:
+ virtual ~MeshEdgebreakerEncoderImplInterface() = default;
+ virtual bool Init(MeshEdgebreakerEncoder *encoder) = 0;
+
+ virtual const MeshAttributeCornerTable *GetAttributeCornerTable(
+ int att_id) const = 0;
+ virtual const MeshAttributeIndicesEncodingData *GetAttributeEncodingData(
+ int att_id) const = 0;
+ virtual bool GenerateAttributesEncoder(int32_t att_id) = 0;
+ virtual bool EncodeAttributesEncoderIdentifier(int32_t att_encoder_id) = 0;
+ virtual bool EncodeConnectivity() = 0;
+
+ // Returns corner table of the encoded mesh.
+ virtual const CornerTable *GetCornerTable() const = 0;
+
+ // Returns true if a given face has been already encoded.
+ virtual bool IsFaceEncoded(FaceIndex fi) const = 0;
+
+ virtual MeshEdgebreakerEncoder *GetEncoder() const = 0;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_ENCODER_IMPL_INTERFACE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoding_test.cc b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoding_test.cc
new file mode 100644
index 00000000000..8313882455b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_encoding_test.cc
@@ -0,0 +1,247 @@
+// 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 <sstream>
+
+#include "draco/compression/encode.h"
+#include "draco/compression/mesh/mesh_edgebreaker_decoder.h"
+#include "draco/compression/mesh/mesh_edgebreaker_encoder.h"
+#include "draco/core/draco_test_base.h"
+#include "draco/core/draco_test_utils.h"
+#include "draco/io/mesh_io.h"
+#include "draco/io/obj_decoder.h"
+#include "draco/mesh/mesh_are_equivalent.h"
+#include "draco/mesh/mesh_cleanup.h"
+#include "draco/mesh/triangle_soup_mesh_builder.h"
+
+namespace draco {
+
+class MeshEdgebreakerEncodingTest : public ::testing::Test {
+ protected:
+ void TestFile(const std::string &file_name) { TestFile(file_name, -1); }
+
+ void TestFile(const std::string &file_name, int compression_level) {
+ const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name));
+ ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
+
+ TestMesh(mesh.get(), compression_level);
+ }
+
+ void TestMesh(Mesh *mesh, int compression_level) {
+ EncoderBuffer buffer;
+ MeshEdgebreakerEncoder encoder;
+ EncoderOptions encoder_options = EncoderOptions::CreateDefaultOptions();
+ encoder_options.SetSpeed(10 - compression_level, 10 - compression_level);
+ encoder.SetMesh(*mesh);
+ ASSERT_TRUE(encoder.Encode(encoder_options, &buffer).ok());
+
+ DecoderBuffer dec_buffer;
+ dec_buffer.Init(buffer.data(), buffer.size());
+ MeshEdgebreakerDecoder decoder;
+
+ std::unique_ptr<Mesh> decoded_mesh(new Mesh());
+ DecoderOptions dec_options;
+ ASSERT_TRUE(
+ decoder.Decode(dec_options, &dec_buffer, decoded_mesh.get()).ok());
+
+ // Cleanup the input mesh to make sure that input and output can be
+ // compared (edgebreaker method discards degenerated triangles and isolated
+ // vertices).
+ const MeshCleanupOptions options;
+ MeshCleanup cleanup;
+ ASSERT_TRUE(cleanup(mesh, options)) << "Failed to clean the input mesh.";
+
+ MeshAreEquivalent eq;
+ ASSERT_TRUE(eq(*mesh, *decoded_mesh.get()))
+ << "Decoded mesh is not the same as the input";
+ }
+};
+
+TEST_F(MeshEdgebreakerEncodingTest, TestNmOBJ) {
+ const std::string file_name = "test_nm.obj";
+ TestFile(file_name);
+}
+
+TEST_F(MeshEdgebreakerEncodingTest, ThreeFacesOBJ) {
+ const std::string file_name = "extra_vertex.obj";
+ TestFile(file_name);
+}
+
+TEST_F(MeshEdgebreakerEncodingTest, TestPly) {
+ // Tests whether the edgebreaker successfully encodes and decodes the test
+ // file (ply with color).
+ const std::string file_name = "test_pos_color.ply";
+ TestFile(file_name);
+}
+
+TEST_F(MeshEdgebreakerEncodingTest, TestMultiAttributes) {
+ // Tests encoding of model with many attributes.
+ const std::string file_name = "cube_att.obj";
+ TestFile(file_name, 10);
+}
+
+TEST_F(MeshEdgebreakerEncodingTest, TestEncoderReuse) {
+ // Tests whether the edgebreaker encoder can be reused multiple times to
+ // encode a given mesh.
+ const std::string file_name = "test_pos_color.ply";
+ const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name));
+ ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
+
+ MeshEdgebreakerEncoder encoder;
+ EncoderOptions encoder_options = EncoderOptions::CreateDefaultOptions();
+ encoder.SetMesh(*mesh);
+ EncoderBuffer buffer_0, buffer_1;
+ ASSERT_TRUE(encoder.Encode(encoder_options, &buffer_0).ok());
+ ASSERT_TRUE(encoder.Encode(encoder_options, &buffer_1).ok());
+
+ // Make sure both buffer are identical.
+ ASSERT_EQ(buffer_0.size(), buffer_1.size());
+ for (int i = 0; i < buffer_0.size(); ++i) {
+ ASSERT_EQ(buffer_0.data()[i], buffer_1.data()[i]);
+ }
+}
+
+TEST_F(MeshEdgebreakerEncodingTest, TestDecoderReuse) {
+ // Tests whether the edgebreaker decoder can be reused multiple times to
+ // decode a given mesh.
+ const std::string file_name = "test_pos_color.ply";
+ const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name));
+ ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
+
+ MeshEdgebreakerEncoder encoder;
+ EncoderOptions encoder_options = EncoderOptions::CreateDefaultOptions();
+ encoder.SetMesh(*mesh);
+ EncoderBuffer buffer;
+ ASSERT_TRUE(encoder.Encode(encoder_options, &buffer).ok());
+
+ DecoderBuffer dec_buffer;
+ dec_buffer.Init(buffer.data(), buffer.size());
+
+ MeshEdgebreakerDecoder decoder;
+
+ // Decode the mesh two times.
+ std::unique_ptr<Mesh> decoded_mesh_0(new Mesh());
+ DecoderOptions dec_options;
+ ASSERT_TRUE(
+ decoder.Decode(dec_options, &dec_buffer, decoded_mesh_0.get()).ok());
+
+ dec_buffer.Init(buffer.data(), buffer.size());
+ std::unique_ptr<Mesh> decoded_mesh_1(new Mesh());
+ ASSERT_TRUE(
+ decoder.Decode(dec_options, &dec_buffer, decoded_mesh_1.get()).ok());
+
+ // Make sure both of the meshes are identical.
+ MeshAreEquivalent eq;
+ ASSERT_TRUE(eq(*decoded_mesh_0.get(), *decoded_mesh_1.get()))
+ << "Decoded meshes are not the same";
+}
+
+TEST_F(MeshEdgebreakerEncodingTest, TestSingleConnectivityEncoding) {
+ // Tests whether the edgebreaker method successfully encodes a mesh with
+ // multiple attributes using single connectivity by breaking the mesh along
+ // attribute seams.
+ const std::string file_name = "cube_att.obj";
+ const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name));
+ ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
+
+ for (int i = 0; i < 2; ++i) {
+ // Set the option to enable/disable single connectivity encoding.
+ EncoderOptionsBase<GeometryAttribute::Type> options =
+ EncoderOptionsBase<GeometryAttribute::Type>::CreateDefaultOptions();
+ options.SetGlobalBool("split_mesh_on_seams", i == 0 ? true : false);
+
+ EncoderBuffer buffer;
+ draco::Encoder encoder;
+ encoder.Reset(options);
+ encoder.SetSpeedOptions(0, 0);
+ encoder.SetAttributeQuantization(GeometryAttribute::POSITION, 8);
+ encoder.SetAttributeQuantization(GeometryAttribute::TEX_COORD, 8);
+ encoder.SetAttributeQuantization(GeometryAttribute::NORMAL, 8);
+ encoder.SetEncodingMethod(MESH_EDGEBREAKER_ENCODING);
+ ASSERT_TRUE(encoder.EncodeMeshToBuffer(*mesh, &buffer).ok());
+
+ DecoderBuffer dec_buffer;
+ dec_buffer.Init(buffer.data(), buffer.size());
+
+ Decoder decoder;
+ auto dec_mesh = decoder.DecodeMeshFromBuffer(&dec_buffer).value();
+ ASSERT_NE(dec_mesh, nullptr);
+ ASSERT_EQ(dec_mesh->num_points(), 24);
+ ASSERT_EQ(dec_mesh->num_attributes(), 3);
+ ASSERT_EQ(dec_mesh->attribute(0)->size(), i == 0 ? 24 : 8);
+ ASSERT_EQ(dec_mesh->attribute(1)->size(), 24);
+ ASSERT_EQ(dec_mesh->attribute(2)->size(), 24);
+ }
+}
+
+TEST_F(MeshEdgebreakerEncodingTest, TestWrongAttributeOrder) {
+ // Tests whether the edgebreaker method successfully encodes a mesh where the
+ // input attributes are in wrong order (because of their internal
+ // dependencies). In such case the attributes should be rearranged to the
+ // correct order.
+ TriangleSoupMeshBuilder mb;
+ mb.Start(1);
+ const int32_t norm_att_id =
+ mb.AddAttribute(GeometryAttribute::NORMAL, 3, DT_FLOAT32);
+ const int32_t pos_att_id =
+ mb.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32);
+
+ 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(
+ norm_att_id, FaceIndex(0), Vector3f(0.f, 0.f, 1.f).data(),
+ Vector3f(0.f, 0.f, 0.f).data(), Vector3f(0.f, 0.f, 1.f).data());
+ std::unique_ptr<Mesh> mesh = mb.Finalize();
+ ASSERT_NE(mesh, nullptr);
+ ASSERT_EQ(mesh->num_attributes(), 2);
+ ASSERT_EQ(mesh->attribute(0)->attribute_type(), GeometryAttribute::NORMAL);
+ ASSERT_EQ(mesh->attribute(1)->attribute_type(), GeometryAttribute::POSITION);
+
+ EncoderBuffer buffer;
+ draco::Encoder encoder;
+ encoder.SetSpeedOptions(3, 3);
+ encoder.SetAttributeQuantization(GeometryAttribute::POSITION, 8);
+ encoder.SetAttributeQuantization(GeometryAttribute::NORMAL, 8);
+ encoder.SetEncodingMethod(MESH_EDGEBREAKER_ENCODING);
+ ASSERT_TRUE(encoder.EncodeMeshToBuffer(*mesh, &buffer).ok());
+
+ DecoderBuffer dec_buffer;
+ dec_buffer.Init(buffer.data(), buffer.size());
+
+ Decoder decoder;
+ auto dec_mesh = decoder.DecodeMeshFromBuffer(&dec_buffer).value();
+ ASSERT_NE(dec_mesh, nullptr);
+ ASSERT_EQ(dec_mesh->num_attributes(), 2);
+ ASSERT_EQ(dec_mesh->attribute(0)->attribute_type(),
+ GeometryAttribute::POSITION);
+ ASSERT_EQ(dec_mesh->attribute(1)->attribute_type(),
+ GeometryAttribute::NORMAL);
+}
+
+TEST_F(MeshEdgebreakerEncodingTest, TestDegenerateMesh) {
+ // Tests whether we can process a mesh that contains degenerate faces only.
+ const std::string file_name = "degenerate_mesh.obj";
+ const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name));
+ ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
+ EncoderBuffer buffer;
+ MeshEdgebreakerEncoder encoder;
+ EncoderOptions encoder_options = EncoderOptions::CreateDefaultOptions();
+ encoder.SetMesh(*mesh);
+ // We expect the encoding to fail as edgebreaker can only process valid faces.
+ ASSERT_FALSE(encoder.Encode(encoder_options, &buffer).ok());
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_shared.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_shared.h
new file mode 100644
index 00000000000..cb3c29dd669
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_shared.h
@@ -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.
+//
+#ifndef DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_SHARED_H_
+#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_SHARED_H_
+
+#include <stdint.h>
+
+namespace draco {
+
+// Shared declarations used by both edgebreaker encoder and decoder.
+
+// A variable length encoding for storing all possible topology configurations
+// during traversal of mesh's surface. The configurations are based on visited
+// state of neighboring triangles around a currently processed face corner.
+// Note that about half of the encountered configurations is expected to be of
+// type TOPOLOGY_C. It's guaranteed that the encoding will use at most 2 bits
+// per triangle for meshes with no holes and up to 6 bits per triangle for
+// general meshes. In addition, the encoding will take up to 4 bits per triangle
+// for each non-position attribute attached to the mesh.
+//
+// *-------* *-------* *-------*
+// / \ / \ / \ / \ / \ / \
+// / \ / \ / \ / \ / \ / \
+// / \ / \ / \ / \ / \ / \
+// *-------v-------* *-------v-------* *-------v-------*
+// \ /x\ / /x\ / \ /x\
+// \ / \ / / \ / \ / \
+// \ / C \ / / L \ / \ / R \
+// *-------* *-------* *-------*
+//
+// * *
+// / \ / \
+// / \ / \
+// / \ / \
+// *-------v-------* v
+// \ /x\ / /x\
+// \ / \ / / \
+// \ / S \ / / E \
+// *-------* *-------*
+//
+// TODO(ostava): Get rid of the topology bit pattern. It's important only for
+// encoding but the algorithms should use EdgebreakerSymbol instead.
+enum EdgebreakerTopologyBitPattern {
+ TOPOLOGY_C = 0x0, // 0
+ TOPOLOGY_S = 0x1, // 1 0 0
+ TOPOLOGY_L = 0x3, // 1 1 0
+ TOPOLOGY_R = 0x5, // 1 0 1
+ TOPOLOGY_E = 0x7, // 1 1 1
+ // A special symbol that's not actually encoded, but it can be used to mark
+ // the initial face that triggers the mesh encoding of a single connected
+ // component.
+ TOPOLOGY_INIT_FACE,
+ // A special value used to indicate an invalid symbol.
+ TOPOLOGY_INVALID
+};
+
+enum EdgebreakerSymbol {
+ EDGEBREAKER_SYMBOL_C = 0,
+ EDGEBREAKER_SYMBOL_S,
+ EDGEBREAKER_SYMBOL_L,
+ EDGEBREAKER_SYMBOL_R,
+ EDGEBREAKER_SYMBOL_E,
+ EDGEBREAKER_SYMBOL_INVALID
+};
+
+// Bit-length of symbols in the EdgebreakerTopologyBitPattern stored as a
+// lookup table for faster indexing.
+constexpr int32_t edge_breaker_topology_bit_pattern_length[] = {1, 3, 0, 3,
+ 0, 3, 0, 3};
+
+// Zero-indexed symbol id for each of topology pattern.
+constexpr EdgebreakerSymbol edge_breaker_topology_to_symbol_id[] = {
+ EDGEBREAKER_SYMBOL_C, EDGEBREAKER_SYMBOL_S,
+ EDGEBREAKER_SYMBOL_INVALID, EDGEBREAKER_SYMBOL_L,
+ EDGEBREAKER_SYMBOL_INVALID, EDGEBREAKER_SYMBOL_R,
+ EDGEBREAKER_SYMBOL_INVALID, EDGEBREAKER_SYMBOL_E};
+
+// Reverse mapping between symbol id and topology pattern symbol.
+constexpr EdgebreakerTopologyBitPattern edge_breaker_symbol_to_topology_id[] = {
+ TOPOLOGY_C, TOPOLOGY_S, TOPOLOGY_L, TOPOLOGY_R, TOPOLOGY_E};
+
+// Types of edges used during mesh traversal relative to the tip vertex of a
+// visited triangle.
+enum EdgeFaceName : uint8_t { LEFT_FACE_EDGE = 0, RIGHT_FACE_EDGE = 1 };
+
+// Struct used for storing data about a source face that connects to an
+// already traversed face that was either the initial face or a face encoded
+// with either topology S (split) symbol. Such connection can be only caused by
+// topology changes on the traversed surface (if its genus != 0, i.e. when the
+// surface has topological handles or holes).
+// For each occurrence of such event we always encode the split symbol id,
+// source symbol id and source edge id (left, or right). There will be always
+// exactly two occurrences of this event for every topological handle on the
+// traversed mesh and one occurrence for a hole.
+struct TopologySplitEventData {
+ uint32_t split_symbol_id;
+ uint32_t source_symbol_id;
+ // We need to use uint32_t instead of EdgeFaceName because the most recent
+ // version of gcc does not allow that when optimizations are turned on.
+ uint32_t source_edge : 1;
+};
+
+// Hole event is used to store info about the first symbol that reached a
+// vertex of so far unvisited hole. This can happen only on either the initial
+// face or during a regular traversal when TOPOLOGY_S is encountered.
+struct HoleEventData {
+ int32_t symbol_id;
+ HoleEventData() : symbol_id(0) {}
+ explicit HoleEventData(int32_t sym_id) : symbol_id(sym_id) {}
+};
+
+// List of supported modes for valence based edgebreaker coding.
+enum EdgebreakerValenceCodingMode {
+ EDGEBREAKER_VALENCE_MODE_2_7 = 0, // Use contexts for valences in range 2-7.
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_SHARED_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_decoder.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_decoder.h
new file mode 100644
index 00000000000..128d7f5d988
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_decoder.h
@@ -0,0 +1,193 @@
+// 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_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_DECODER_H_
+#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_DECODER_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/bit_coders/rans_bit_decoder.h"
+#include "draco/compression/mesh/mesh_edgebreaker_decoder.h"
+#include "draco/compression/mesh/mesh_edgebreaker_decoder_impl_interface.h"
+#include "draco/compression/mesh/mesh_edgebreaker_shared.h"
+
+namespace draco {
+
+typedef RAnsBitDecoder BinaryDecoder;
+
+// Default implementation of the edgebreaker traversal decoder that reads the
+// traversal data directly from a buffer.
+class MeshEdgebreakerTraversalDecoder {
+ public:
+ MeshEdgebreakerTraversalDecoder()
+ : attribute_connectivity_decoders_(nullptr),
+ num_attribute_data_(0),
+ decoder_impl_(nullptr) {}
+ void Init(MeshEdgebreakerDecoderImplInterface *decoder) {
+ decoder_impl_ = decoder;
+ buffer_.Init(decoder->GetDecoder()->buffer()->data_head(),
+ decoder->GetDecoder()->buffer()->remaining_size(),
+ decoder->GetDecoder()->buffer()->bitstream_version());
+ }
+
+ // Returns the Draco bitstream version.
+ uint16_t BitstreamVersion() const {
+ return decoder_impl_->GetDecoder()->bitstream_version();
+ }
+
+ // Used to tell the decoder what is the number of expected decoded vertices.
+ // Ignored by default.
+ void SetNumEncodedVertices(int /* num_vertices */) {}
+
+ // Set the number of non-position attribute data for which we need to decode
+ // the connectivity.
+ void SetNumAttributeData(int num_data) { num_attribute_data_ = num_data; }
+
+ // Called before the traversal decoding is started.
+ // Returns a buffer decoder that points to data that was encoded after the
+ // traversal.
+ bool Start(DecoderBuffer *out_buffer) {
+ // Decode symbols from the main buffer decoder and face configurations from
+ // the start_face_buffer decoder.
+ if (!DecodeTraversalSymbols())
+ return false;
+
+ if (!DecodeStartFaces())
+ return false;
+
+ if (!DecodeAttributeSeams())
+ return false;
+ *out_buffer = buffer_;
+ return true;
+ }
+
+ // Returns the configuration of a new initial face.
+ inline bool DecodeStartFaceConfiguration() {
+ uint32_t face_configuration;
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (buffer_.bitstream_version() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ start_face_buffer_.DecodeLeastSignificantBits32(1, &face_configuration);
+
+ } else
+#endif
+ {
+ face_configuration = start_face_decoder_.DecodeNextBit();
+ }
+ return face_configuration;
+ }
+
+ // Returns the next edgebreaker symbol that was reached during the traversal.
+ inline uint32_t DecodeSymbol() {
+ uint32_t symbol;
+ symbol_buffer_.DecodeLeastSignificantBits32(1, &symbol);
+ if (symbol == TOPOLOGY_C) {
+ return symbol;
+ }
+ // Else decode two additional bits.
+ uint32_t symbol_suffix;
+ symbol_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 */) {}
+
+ // Called whenever |source| vertex is about to be merged into the |dest|
+ // vertex.
+ inline void MergeVertices(VertexIndex /* dest */, VertexIndex /* source */) {}
+
+ // Returns true if there is an attribute seam for the next processed pair
+ // of visited faces.
+ // |attribute| is used to mark the id of the non-position attribute (in range
+ // of <0, num_attributes - 1>).
+ inline bool DecodeAttributeSeam(int attribute) {
+ return attribute_connectivity_decoders_[attribute].DecodeNextBit();
+ }
+
+ // Called when the traversal is finished.
+ void Done() {
+ if (symbol_buffer_.bit_decoder_active())
+ symbol_buffer_.EndBitDecoding();
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (buffer_.bitstream_version() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ start_face_buffer_.EndBitDecoding();
+
+ } else
+#endif
+ {
+ start_face_decoder_.EndDecoding();
+ }
+ }
+
+ protected:
+ DecoderBuffer *buffer() { return &buffer_; }
+
+ bool DecodeTraversalSymbols() {
+ uint64_t traversal_size;
+ symbol_buffer_ = buffer_;
+ if (!symbol_buffer_.StartBitDecoding(true, &traversal_size))
+ return false;
+ buffer_ = symbol_buffer_;
+ if (traversal_size > static_cast<uint64_t>(buffer_.remaining_size()))
+ return false;
+ buffer_.Advance(traversal_size);
+ return true;
+ }
+
+ bool DecodeStartFaces() {
+ // Create a decoder that is set to the end of the encoded traversal data.
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (buffer_.bitstream_version() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ start_face_buffer_ = buffer_;
+ uint64_t traversal_size;
+ if (!start_face_buffer_.StartBitDecoding(true, &traversal_size))
+ return false;
+ buffer_ = start_face_buffer_;
+ if (traversal_size > static_cast<uint64_t>(buffer_.remaining_size()))
+ return false;
+ buffer_.Advance(traversal_size);
+ return true;
+ }
+#endif
+ return start_face_decoder_.StartDecoding(&buffer_);
+ }
+
+ bool DecodeAttributeSeams() {
+ // Prepare attribute decoding.
+ if (num_attribute_data_ > 0) {
+ attribute_connectivity_decoders_ = std::unique_ptr<BinaryDecoder[]>(
+ new BinaryDecoder[num_attribute_data_]);
+ for (int i = 0; i < num_attribute_data_; ++i) {
+ if (!attribute_connectivity_decoders_[i].StartDecoding(&buffer_))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ // Buffer that contains the encoded data.
+ DecoderBuffer buffer_;
+ DecoderBuffer symbol_buffer_;
+ BinaryDecoder start_face_decoder_;
+ DecoderBuffer start_face_buffer_;
+ std::unique_ptr<BinaryDecoder[]> attribute_connectivity_decoders_;
+ int num_attribute_data_;
+ const MeshEdgebreakerDecoderImplInterface *decoder_impl_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_encoder.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_encoder.h
new file mode 100644
index 00000000000..08cb66e4cdc
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_encoder.h
@@ -0,0 +1,139 @@
+// 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_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_ENCODER_H_
+#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_ENCODER_H_
+
+#include "draco/compression/bit_coders/rans_bit_encoder.h"
+#include "draco/compression/mesh/mesh_edgebreaker_encoder.h"
+#include "draco/compression/mesh/mesh_edgebreaker_encoder_impl_interface.h"
+#include "draco/core/macros.h"
+
+namespace draco {
+
+typedef RAnsBitEncoder BinaryEncoder;
+
+// Default implementation of the edgebreaker traversal encoder. Face
+// configurations are stored directly into the output buffer and the symbols
+// are first collected and then encoded in the reverse order to make the
+// decoding faster.
+class MeshEdgebreakerTraversalEncoder {
+ public:
+ MeshEdgebreakerTraversalEncoder()
+ : encoder_impl_(nullptr),
+ attribute_connectivity_encoders_(nullptr),
+ num_attribute_data_(0) {}
+ bool Init(MeshEdgebreakerEncoderImplInterface *encoder) {
+ encoder_impl_ = encoder;
+ return true;
+ }
+
+ // Set the number of non-position attribute data for which we need to encode
+ // the connectivity.
+ void SetNumAttributeData(int num_data) { num_attribute_data_ = num_data; }
+
+ // Called before the traversal encoding is started.
+ void Start() {
+ start_face_encoder_.StartEncoding();
+ if (num_attribute_data_ > 0) {
+ // Init and start arithmetic encoders for storing configuration types
+ // of non-position attributes.
+ attribute_connectivity_encoders_ = std::unique_ptr<BinaryEncoder[]>(
+ new BinaryEncoder[num_attribute_data_]);
+ for (int i = 0; i < num_attribute_data_; ++i) {
+ attribute_connectivity_encoders_[i].StartEncoding();
+ }
+ }
+ }
+
+ // Called when a traversal starts from a new initial face.
+ inline void EncodeStartFaceConfiguration(bool interior) {
+ start_face_encoder_.EncodeBit(interior);
+ }
+
+ // Called when a new corner is reached during the traversal. No-op for the
+ // default encoder.
+ inline void NewCornerReached(CornerIndex /* corner */) {}
+
+ // Called whenever a new symbol is reached during the edgebreaker traversal.
+ inline void EncodeSymbol(EdgebreakerTopologyBitPattern symbol) {
+ // Store the symbol. It will be encoded after all symbols are processed.
+ symbols_.push_back(symbol);
+ }
+
+ // Called for every pair of connected and visited faces. |is_seam| specifies
+ // whether there is an attribute seam between the two faces.
+
+ inline void EncodeAttributeSeam(int attribute, bool is_seam) {
+ attribute_connectivity_encoders_[attribute].EncodeBit(is_seam ? 1 : 0);
+ }
+
+ // Called when the traversal is finished.
+ void Done() {
+ EncodeTraversalSymbols();
+ EncodeStartFaces();
+ EncodeAttributeSeams();
+ }
+
+ // Returns the number of encoded symbols.
+ int NumEncodedSymbols() const { return static_cast<int>(symbols_.size()); }
+
+ const EncoderBuffer &buffer() const { return traversal_buffer_; }
+
+ protected:
+ void EncodeTraversalSymbols() {
+ // Bit encode the collected symbols.
+ // Allocate enough storage for the bit encoder.
+ // It's guaranteed that each face will need only up to 3 bits.
+ traversal_buffer_.StartBitEncoding(
+ encoder_impl_->GetEncoder()->mesh()->num_faces() * 3, true);
+ for (int i = static_cast<int>(symbols_.size() - 1); i >= 0; --i) {
+ traversal_buffer_.EncodeLeastSignificantBits32(
+ edge_breaker_topology_bit_pattern_length[symbols_[i]], symbols_[i]);
+ }
+ traversal_buffer_.EndBitEncoding();
+ }
+
+ void EncodeStartFaces() {
+ start_face_encoder_.EndEncoding(&traversal_buffer_);
+ }
+
+ void EncodeAttributeSeams() {
+ if (attribute_connectivity_encoders_ != nullptr) {
+ for (int i = 0; i < num_attribute_data_; ++i) {
+ attribute_connectivity_encoders_[i].EndEncoding(&traversal_buffer_);
+ }
+ }
+ }
+
+ EncoderBuffer *GetOutputBuffer() { return &traversal_buffer_; }
+ const MeshEdgebreakerEncoderImplInterface *encoder_impl() const {
+ return encoder_impl_;
+ }
+
+ private:
+ BinaryEncoder start_face_encoder_;
+ EncoderBuffer traversal_buffer_;
+ const MeshEdgebreakerEncoderImplInterface *encoder_impl_;
+ // Symbols collected during the traversal.
+ std::vector<EdgebreakerTopologyBitPattern> symbols_;
+ // Arithmetic encoder for encoding attribute seams.
+ // One context for each non-position attribute.
+ std::unique_ptr<BinaryEncoder[]> attribute_connectivity_encoders_;
+ int num_attribute_data_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_predictive_decoder.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_predictive_decoder.h
new file mode 100644
index 00000000000..d3289e28c76
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_predictive_decoder.h
@@ -0,0 +1,132 @@
+// 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.
+//
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+#ifndef DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_PREDICTIVE_DECODER_H_
+#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_PREDICTIVE_DECODER_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/mesh/mesh_edgebreaker_traversal_decoder.h"
+
+namespace draco {
+
+// Decoder for traversal encoded with the
+// MeshEdgebreakerTraversalPredictiveEncoder. The decoder maintains valences
+// of the decoded portion of the traversed mesh and it uses them to predict
+// symbols that are about to be decoded.
+class MeshEdgebreakerTraversalPredictiveDecoder
+ : public MeshEdgebreakerTraversalDecoder {
+ public:
+ MeshEdgebreakerTraversalPredictiveDecoder()
+ : corner_table_(nullptr),
+ num_vertices_(0),
+ last_symbol_(-1),
+ predicted_symbol_(-1) {}
+ void Init(MeshEdgebreakerDecoderImplInterface *decoder) {
+ MeshEdgebreakerTraversalDecoder::Init(decoder);
+ corner_table_ = decoder->GetCornerTable();
+ }
+ void SetNumEncodedVertices(int num_vertices) { num_vertices_ = num_vertices; }
+
+ bool Start(DecoderBuffer *out_buffer) {
+ if (!MeshEdgebreakerTraversalDecoder::Start(out_buffer))
+ return false;
+ int32_t num_split_symbols;
+ if (!out_buffer->Decode(&num_split_symbols) || num_split_symbols < 0)
+ return false;
+ if (num_split_symbols >= num_vertices_)
+ return false;
+ // Set the valences of all initial vertices to 0.
+ vertex_valences_.resize(num_vertices_, 0);
+ if (!prediction_decoder_.StartDecoding(out_buffer))
+ return false;
+ return true;
+ }
+
+ inline uint32_t DecodeSymbol() {
+ // First check if we have a predicted symbol.
+ if (predicted_symbol_ != -1) {
+ // Double check that the predicted symbol was predicted correctly.
+ if (prediction_decoder_.DecodeNextBit()) {
+ last_symbol_ = predicted_symbol_;
+ return predicted_symbol_;
+ }
+ }
+ // We don't have a predicted symbol or the symbol was mis-predicted.
+ // Decode it directly.
+ last_symbol_ = MeshEdgebreakerTraversalDecoder::DecodeSymbol();
+ return last_symbol_;
+ }
+
+ inline void NewActiveCornerReached(CornerIndex corner) {
+ const CornerIndex next = corner_table_->Next(corner);
+ const CornerIndex prev = corner_table_->Previous(corner);
+ // Update valences.
+ switch (last_symbol_) {
+ case TOPOLOGY_C:
+ case TOPOLOGY_S:
+ vertex_valences_[corner_table_->Vertex(next).value()] += 1;
+ vertex_valences_[corner_table_->Vertex(prev).value()] += 1;
+ break;
+ case TOPOLOGY_R:
+ vertex_valences_[corner_table_->Vertex(corner).value()] += 1;
+ vertex_valences_[corner_table_->Vertex(next).value()] += 1;
+ vertex_valences_[corner_table_->Vertex(prev).value()] += 2;
+ break;
+ case TOPOLOGY_L:
+ vertex_valences_[corner_table_->Vertex(corner).value()] += 1;
+ vertex_valences_[corner_table_->Vertex(next).value()] += 2;
+ vertex_valences_[corner_table_->Vertex(prev).value()] += 1;
+ break;
+ case TOPOLOGY_E:
+ vertex_valences_[corner_table_->Vertex(corner).value()] += 2;
+ vertex_valences_[corner_table_->Vertex(next).value()] += 2;
+ vertex_valences_[corner_table_->Vertex(prev).value()] += 2;
+ break;
+ default:
+ break;
+ }
+ // Compute the new predicted symbol.
+ if (last_symbol_ == TOPOLOGY_C || last_symbol_ == TOPOLOGY_R) {
+ const VertexIndex pivot =
+ corner_table_->Vertex(corner_table_->Next(corner));
+ if (vertex_valences_[pivot.value()] < 6) {
+ predicted_symbol_ = TOPOLOGY_R;
+ } else {
+ predicted_symbol_ = TOPOLOGY_C;
+ }
+ } else {
+ predicted_symbol_ = -1;
+ }
+ }
+
+ inline void MergeVertices(VertexIndex dest, VertexIndex source) {
+ // Update valences on the merged vertices.
+ vertex_valences_[dest.value()] += vertex_valences_[source.value()];
+ }
+
+ private:
+ const CornerTable *corner_table_;
+ int num_vertices_;
+ std::vector<int> vertex_valences_;
+ BinaryDecoder prediction_decoder_;
+ int last_symbol_;
+ int predicted_symbol_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_PREDICTIVE_DECODER_H_
+#endif
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h
new file mode 100644
index 00000000000..118687cc769
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h
@@ -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.
+//
+#ifndef DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_PREDICTIVE_ENCODER_H_
+#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_PREDICTIVE_ENCODER_H_
+
+#include "draco/compression/mesh/mesh_edgebreaker_traversal_encoder.h"
+
+namespace draco {
+
+// Encoder that tries to predict the edgebreaker traversal symbols based on the
+// vertex valences of the unencoded portion of the mesh. The current prediction
+// scheme assumes that each vertex has valence 6 which can be used to predict
+// the symbol preceding the one that is currently encoded. Predictions are
+// encoded using an arithmetic coding which can lead to less than 1 bit per
+// triangle encoding for highly regular meshes.
+class MeshEdgebreakerTraversalPredictiveEncoder
+ : public MeshEdgebreakerTraversalEncoder {
+ public:
+ MeshEdgebreakerTraversalPredictiveEncoder()
+ : corner_table_(nullptr),
+ prev_symbol_(-1),
+ num_split_symbols_(0),
+ last_corner_(kInvalidCornerIndex),
+ num_symbols_(0) {}
+
+ bool Init(MeshEdgebreakerEncoderImplInterface *encoder) {
+ if (!MeshEdgebreakerTraversalEncoder::Init(encoder))
+ return false;
+ corner_table_ = encoder->GetCornerTable();
+ // Initialize valences of all vertices.
+ vertex_valences_.resize(corner_table_->num_vertices());
+ for (uint32_t i = 0; i < vertex_valences_.size(); ++i) {
+ vertex_valences_[i] = corner_table_->Valence(VertexIndex(i));
+ }
+ return true;
+ }
+
+ inline void NewCornerReached(CornerIndex corner) { last_corner_ = corner; }
+
+ inline int32_t ComputePredictedSymbol(VertexIndex pivot) {
+ const int valence = vertex_valences_[pivot.value()];
+ if (valence < 0) {
+ // This situation can happen only for split vertices. Returning
+ // TOPOLOGY_INVALID always cases misprediction.
+ return TOPOLOGY_INVALID;
+ }
+ if (valence < 6) {
+ return TOPOLOGY_R;
+ }
+ return TOPOLOGY_C;
+ }
+
+ inline void EncodeSymbol(EdgebreakerTopologyBitPattern symbol) {
+ ++num_symbols_;
+ // Update valences on the mesh. And compute the predicted preceding symbol.
+ // Note that the valences are computed for the so far unencoded part of the
+ // mesh. Adding a new symbol either reduces valences on the vertices or
+ // leaves the valence unchanged.
+ int32_t predicted_symbol = -1;
+ const CornerIndex next = corner_table_->Next(last_corner_);
+ const CornerIndex prev = corner_table_->Previous(last_corner_);
+ switch (symbol) {
+ case TOPOLOGY_C:
+ // Compute prediction.
+ predicted_symbol = ComputePredictedSymbol(corner_table_->Vertex(next));
+ FALLTHROUGH_INTENDED;
+ case TOPOLOGY_S:
+ // Update valences.
+ vertex_valences_[corner_table_->Vertex(next).value()] -= 1;
+ vertex_valences_[corner_table_->Vertex(prev).value()] -= 1;
+ if (symbol == TOPOLOGY_S) {
+ // Whenever we reach a split symbol, mark its tip vertex as invalid by
+ // setting the valence to a negative value. Any prediction that will
+ // use this vertex will then cause a misprediction. This is currently
+ // necessary because the decoding works in the reverse direction and
+ // the decoder doesn't know about these vertices until the split
+ // symbol is decoded at which point two vertices are merged into one.
+ // This can be most likely solved on the encoder side by splitting the
+ // tip vertex into two, but since split symbols are relatively rare,
+ // it's probably not worth doing it.
+ vertex_valences_[corner_table_->Vertex(last_corner_).value()] = -1;
+ ++num_split_symbols_;
+ }
+ break;
+ case TOPOLOGY_R:
+ // Compute prediction.
+ predicted_symbol = ComputePredictedSymbol(corner_table_->Vertex(next));
+ // Update valences.
+ vertex_valences_[corner_table_->Vertex(last_corner_).value()] -= 1;
+ vertex_valences_[corner_table_->Vertex(next).value()] -= 1;
+ vertex_valences_[corner_table_->Vertex(prev).value()] -= 2;
+ break;
+ case TOPOLOGY_L:
+ vertex_valences_[corner_table_->Vertex(last_corner_).value()] -= 1;
+ vertex_valences_[corner_table_->Vertex(next).value()] -= 2;
+ vertex_valences_[corner_table_->Vertex(prev).value()] -= 1;
+ break;
+ case TOPOLOGY_E:
+ vertex_valences_[corner_table_->Vertex(last_corner_).value()] -= 2;
+ vertex_valences_[corner_table_->Vertex(next).value()] -= 2;
+ vertex_valences_[corner_table_->Vertex(prev).value()] -= 2;
+ break;
+ default:
+ break;
+ }
+ // Flag used when it's necessary to explicitly store the previous symbol.
+ bool store_prev_symbol = true;
+ if (predicted_symbol != -1) {
+ if (predicted_symbol == prev_symbol_) {
+ predictions_.push_back(true);
+ store_prev_symbol = false;
+ } else if (prev_symbol_ != -1) {
+ predictions_.push_back(false);
+ }
+ }
+ if (store_prev_symbol && prev_symbol_ != -1) {
+ MeshEdgebreakerTraversalEncoder::EncodeSymbol(
+ static_cast<EdgebreakerTopologyBitPattern>(prev_symbol_));
+ }
+ prev_symbol_ = symbol;
+ }
+
+ void Done() {
+ // We still need to store the last encoded symbol.
+ if (prev_symbol_ != -1) {
+ MeshEdgebreakerTraversalEncoder::EncodeSymbol(
+ static_cast<EdgebreakerTopologyBitPattern>(prev_symbol_));
+ }
+ // Store the init face configurations and the explicitly encoded symbols.
+ MeshEdgebreakerTraversalEncoder::Done();
+ // Encode the number of split symbols.
+ GetOutputBuffer()->Encode(num_split_symbols_);
+ // Store the predictions.
+ BinaryEncoder prediction_encoder;
+ prediction_encoder.StartEncoding();
+ for (int i = static_cast<int>(predictions_.size()) - 1; i >= 0; --i) {
+ prediction_encoder.EncodeBit(predictions_[i]);
+ }
+ prediction_encoder.EndEncoding(GetOutputBuffer());
+ }
+
+ int NumEncodedSymbols() const { return num_symbols_; }
+
+ private:
+ const CornerTable *corner_table_;
+ std::vector<int> vertex_valences_;
+ std::vector<bool> predictions_;
+ // Previously encoded symbol.
+ int32_t prev_symbol_;
+ // The total number of encoded split symbols.
+ int32_t num_split_symbols_;
+ CornerIndex last_corner_;
+ // Explicitly count the number of encoded symbols.
+ int num_symbols_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_PREDICTIVE_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_valence_decoder.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_valence_decoder.h
new file mode 100644
index 00000000000..4da9d0e3a8b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_valence_decoder.h
@@ -0,0 +1,202 @@
+// 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_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_VALENCE_DECODER_H_
+#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_VALENCE_DECODER_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/compression/entropy/symbol_decoding.h"
+#include "draco/compression/mesh/mesh_edgebreaker_traversal_decoder.h"
+#include "draco/core/varint_decoding.h"
+
+namespace draco {
+
+// Decoder for traversal encoded with MeshEdgebreakerTraversalValenceEncoder.
+// The decoder maintains valences of the decoded portion of the traversed mesh
+// and it uses them to select entropy context used for decoding of the actual
+// symbols.
+class MeshEdgebreakerTraversalValenceDecoder
+ : public MeshEdgebreakerTraversalDecoder {
+ public:
+ MeshEdgebreakerTraversalValenceDecoder()
+ : corner_table_(nullptr),
+ num_vertices_(0),
+ last_symbol_(-1),
+ active_context_(-1),
+ min_valence_(2),
+ max_valence_(7) {}
+ void Init(MeshEdgebreakerDecoderImplInterface *decoder) {
+ MeshEdgebreakerTraversalDecoder::Init(decoder);
+ corner_table_ = decoder->GetCornerTable();
+ }
+ void SetNumEncodedVertices(int num_vertices) { num_vertices_ = num_vertices; }
+
+ bool Start(DecoderBuffer *out_buffer) {
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (BitstreamVersion() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ if (!MeshEdgebreakerTraversalDecoder::DecodeTraversalSymbols())
+ return false;
+ }
+#endif
+ if (!MeshEdgebreakerTraversalDecoder::DecodeStartFaces())
+ return false;
+ if (!MeshEdgebreakerTraversalDecoder::DecodeAttributeSeams())
+ return false;
+ *out_buffer = *buffer();
+
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (BitstreamVersion() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ uint32_t num_split_symbols;
+ if (BitstreamVersion() < DRACO_BITSTREAM_VERSION(2, 0)) {
+ if (!out_buffer->Decode(&num_split_symbols))
+ return false;
+ } else {
+ if (!DecodeVarint(&num_split_symbols, out_buffer))
+ return false;
+ }
+ if (num_split_symbols >= static_cast<uint32_t>(num_vertices_))
+ return false;
+
+ int8_t mode;
+ if (!out_buffer->Decode(&mode))
+ return false;
+ if (mode == EDGEBREAKER_VALENCE_MODE_2_7) {
+ min_valence_ = 2;
+ max_valence_ = 7;
+ } else {
+ // Unsupported mode.
+ return false;
+ }
+
+ } else
+#endif
+ {
+ min_valence_ = 2;
+ max_valence_ = 7;
+ }
+
+ if (num_vertices_ < 0)
+ return false;
+ // Set the valences of all initial vertices to 0.
+ vertex_valences_.resize(num_vertices_, 0);
+
+ const int num_unique_valences = max_valence_ - min_valence_ + 1;
+
+ // Decode all symbols for all contexts.
+ context_symbols_.resize(num_unique_valences);
+ context_counters_.resize(context_symbols_.size());
+ for (int i = 0; i < context_symbols_.size(); ++i) {
+ uint32_t num_symbols;
+ DecodeVarint<uint32_t>(&num_symbols, out_buffer);
+ if (num_symbols > 0) {
+ context_symbols_[i].resize(num_symbols);
+ DecodeSymbols(num_symbols, 1, out_buffer, context_symbols_[i].data());
+ // All symbols are going to be processed from the back.
+ context_counters_[i] = num_symbols;
+ }
+ }
+ return true;
+ }
+
+ inline uint32_t DecodeSymbol() {
+ // First check if we have a valid context.
+ if (active_context_ != -1) {
+ const int context_counter = --context_counters_[active_context_];
+ if (context_counter < 0)
+ return TOPOLOGY_INVALID;
+ const int symbol_id = context_symbols_[active_context_][context_counter];
+ last_symbol_ = edge_breaker_symbol_to_topology_id[symbol_id];
+ } else {
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (BitstreamVersion() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ // We don't have a predicted symbol or the symbol was mis-predicted.
+ // Decode it directly.
+ last_symbol_ = MeshEdgebreakerTraversalDecoder::DecodeSymbol();
+
+ } else
+#endif
+ {
+ // The first symbol must be E.
+ last_symbol_ = TOPOLOGY_E;
+ }
+ }
+ return last_symbol_;
+ }
+
+ inline void NewActiveCornerReached(CornerIndex corner) {
+ const CornerIndex next = corner_table_->Next(corner);
+ const CornerIndex prev = corner_table_->Previous(corner);
+ // Update valences.
+ switch (last_symbol_) {
+ case TOPOLOGY_C:
+ case TOPOLOGY_S:
+ vertex_valences_[corner_table_->Vertex(next)] += 1;
+ vertex_valences_[corner_table_->Vertex(prev)] += 1;
+ break;
+ case TOPOLOGY_R:
+ vertex_valences_[corner_table_->Vertex(corner)] += 1;
+ vertex_valences_[corner_table_->Vertex(next)] += 1;
+ vertex_valences_[corner_table_->Vertex(prev)] += 2;
+ break;
+ case TOPOLOGY_L:
+ vertex_valences_[corner_table_->Vertex(corner)] += 1;
+ vertex_valences_[corner_table_->Vertex(next)] += 2;
+ vertex_valences_[corner_table_->Vertex(prev)] += 1;
+ break;
+ case TOPOLOGY_E:
+ vertex_valences_[corner_table_->Vertex(corner)] += 2;
+ vertex_valences_[corner_table_->Vertex(next)] += 2;
+ vertex_valences_[corner_table_->Vertex(prev)] += 2;
+ break;
+ default:
+ break;
+ }
+ // Compute the new context that is going to be used to decode the next
+ // symbol.
+ const int active_valence = vertex_valences_[corner_table_->Vertex(next)];
+ int clamped_valence;
+ if (active_valence < min_valence_) {
+ clamped_valence = min_valence_;
+ } else if (active_valence > max_valence_) {
+ clamped_valence = max_valence_;
+ } else {
+ clamped_valence = active_valence;
+ }
+
+ active_context_ = (clamped_valence - min_valence_);
+ }
+
+ inline void MergeVertices(VertexIndex dest, VertexIndex source) {
+ // Update valences on the merged vertices.
+ vertex_valences_[dest] += vertex_valences_[source];
+ }
+
+ private:
+ const CornerTable *corner_table_;
+ int num_vertices_;
+ IndexTypeVector<VertexIndex, int> vertex_valences_;
+ int last_symbol_;
+ int active_context_;
+
+ int min_valence_;
+ int max_valence_;
+ std::vector<std::vector<uint32_t>> context_symbols_;
+ // Points to the active symbol in each context.
+ std::vector<int> context_counters_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_VALENCE_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_valence_encoder.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_valence_encoder.h
new file mode 100644
index 00000000000..ef3a86bf983
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_edgebreaker_traversal_valence_encoder.h
@@ -0,0 +1,223 @@
+// 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_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_VALENCE_ENCODER_H_
+#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_VALENCE_ENCODER_H_
+
+#include "draco/compression/entropy/symbol_encoding.h"
+#include "draco/compression/mesh/mesh_edgebreaker_traversal_encoder.h"
+#include "draco/core/varint_encoding.h"
+
+namespace draco {
+
+// Predictive encoder for the Edgebreaker symbols based on valences of the
+// previously encoded vertices, following the method described in: Szymczak'02,
+// "Optimized Edgebreaker Encoding for Large and Regular Triangle Meshes". Each
+// valence is used to specify a different entropy context for encoding of the
+// symbols.
+// Encoder can operate in various predefined modes that can be used to select
+// the way in which the entropy contexts are computed (e.g. using different
+// clamping for valences, or even using different inputs to compute the
+// contexts), see EdgebreakerValenceCodingMode in mesh_edgebreaker_shared.h for
+// a list of supported modes.
+class MeshEdgebreakerTraversalValenceEncoder
+ : public MeshEdgebreakerTraversalEncoder {
+ public:
+ MeshEdgebreakerTraversalValenceEncoder()
+ : corner_table_(nullptr),
+ prev_symbol_(-1),
+ last_corner_(kInvalidCornerIndex),
+ num_symbols_(0),
+ min_valence_(2),
+ max_valence_(7) {}
+
+ bool Init(MeshEdgebreakerEncoderImplInterface *encoder) {
+ if (!MeshEdgebreakerTraversalEncoder::Init(encoder))
+ return false;
+ min_valence_ = 2;
+ max_valence_ = 7;
+ corner_table_ = encoder->GetCornerTable();
+
+ // Initialize valences of all vertices.
+ vertex_valences_.resize(corner_table_->num_vertices());
+ for (VertexIndex i(0); i < static_cast<uint32_t>(vertex_valences_.size());
+ ++i) {
+ vertex_valences_[i] = corner_table_->Valence(VertexIndex(i));
+ }
+
+ // Replicate the corner to vertex map from the corner table. We need to do
+ // this because the map may get updated during encoding because we add new
+ // vertices when we encounter split symbols.
+ corner_to_vertex_map_.resize(corner_table_->num_corners());
+ for (CornerIndex i(0); i < corner_table_->num_corners(); ++i) {
+ corner_to_vertex_map_[i] = corner_table_->Vertex(i);
+ }
+ const int32_t num_unique_valences = max_valence_ - min_valence_ + 1;
+
+ context_symbols_.resize(num_unique_valences);
+ return true;
+ }
+
+ inline void NewCornerReached(CornerIndex corner) { last_corner_ = corner; }
+
+ inline void EncodeSymbol(EdgebreakerTopologyBitPattern symbol) {
+ ++num_symbols_;
+ // Update valences on the mesh and compute the context that is going to be
+ // used to encode the processed symbol.
+ // Note that the valences are computed for the so far unencoded part of the
+ // mesh (i.e. the decoding is reverse). Adding a new symbol either reduces
+ // valences on the vertices or leaves the valence unchanged.
+
+ const CornerIndex next = corner_table_->Next(last_corner_);
+ const CornerIndex prev = corner_table_->Previous(last_corner_);
+
+ // Get valence on the tip corner of the active edge (outgoing edge that is
+ // going to be used in reverse decoding of the connectivity to predict the
+ // next symbol).
+ const int active_valence = vertex_valences_[corner_to_vertex_map_[next]];
+ switch (symbol) {
+ case TOPOLOGY_C:
+ // Compute prediction.
+ FALLTHROUGH_INTENDED;
+ case TOPOLOGY_S:
+ // Update valences.
+ vertex_valences_[corner_to_vertex_map_[next]] -= 1;
+ vertex_valences_[corner_to_vertex_map_[prev]] -= 1;
+ if (symbol == TOPOLOGY_S) {
+ // Whenever we reach a split symbol, we need to split the vertex into
+ // two and attach all corners on the left and right sides of the split
+ // vertex to the respective vertices (see image below). This is
+ // necessary since the decoder works in the reverse order and it
+ // merges the two vertices only after the split symbol is processed.
+ //
+ // * -----
+ // / \--------
+ // / \--------
+ // / \-------
+ // *-------v-------*
+ // \ /c\ /
+ // \ / \ /
+ // \ /n S p\ /
+ // *.......*
+ //
+
+ // Count the number of faces on the left side of the split vertex and
+ // update the valence on the "left vertex".
+ int num_left_faces = 0;
+ CornerIndex act_c = corner_table_->Opposite(prev);
+ while (act_c != kInvalidCornerIndex) {
+ if (encoder_impl()->IsFaceEncoded(corner_table_->Face(act_c)))
+ break; // Stop when we reach the first visited face.
+ ++num_left_faces;
+ act_c = corner_table_->Opposite(corner_table_->Next(act_c));
+ }
+ vertex_valences_[corner_to_vertex_map_[last_corner_]] =
+ num_left_faces + 1;
+
+ // Create a new vertex for the right side and count the number of
+ // faces that should be attached to this vertex.
+ const int new_vert_id = static_cast<int>(vertex_valences_.size());
+ int num_right_faces = 0;
+
+ act_c = corner_table_->Opposite(next);
+ while (act_c != kInvalidCornerIndex) {
+ if (encoder_impl()->IsFaceEncoded(corner_table_->Face(act_c)))
+ break; // Stop when we reach the first visited face.
+ ++num_right_faces;
+ // Map corners on the right side to the newly created vertex.
+ corner_to_vertex_map_[corner_table_->Next(act_c)] = new_vert_id;
+ act_c = corner_table_->Opposite(corner_table_->Previous(act_c));
+ }
+ vertex_valences_.push_back(num_right_faces + 1);
+ }
+ break;
+ case TOPOLOGY_R:
+ // Update valences.
+ vertex_valences_[corner_to_vertex_map_[last_corner_]] -= 1;
+ vertex_valences_[corner_to_vertex_map_[next]] -= 1;
+ vertex_valences_[corner_to_vertex_map_[prev]] -= 2;
+ break;
+ case TOPOLOGY_L:
+
+ vertex_valences_[corner_to_vertex_map_[last_corner_]] -= 1;
+ vertex_valences_[corner_to_vertex_map_[next]] -= 2;
+ vertex_valences_[corner_to_vertex_map_[prev]] -= 1;
+ break;
+ case TOPOLOGY_E:
+ vertex_valences_[corner_to_vertex_map_[last_corner_]] -= 2;
+ vertex_valences_[corner_to_vertex_map_[next]] -= 2;
+ vertex_valences_[corner_to_vertex_map_[prev]] -= 2;
+ break;
+ default:
+ break;
+ }
+
+ if (prev_symbol_ != -1) {
+ int clamped_valence;
+ if (active_valence < min_valence_) {
+ clamped_valence = min_valence_;
+ } else if (active_valence > max_valence_) {
+ clamped_valence = max_valence_;
+ } else {
+ clamped_valence = active_valence;
+ }
+
+ const int context = clamped_valence - min_valence_;
+ context_symbols_[context].push_back(
+ edge_breaker_topology_to_symbol_id[prev_symbol_]);
+ }
+
+ prev_symbol_ = symbol;
+ }
+
+ void Done() {
+ // Store the init face configurations and attribute seam data
+ MeshEdgebreakerTraversalEncoder::EncodeStartFaces();
+ MeshEdgebreakerTraversalEncoder::EncodeAttributeSeams();
+
+ // Store the contexts.
+ for (int i = 0; i < context_symbols_.size(); ++i) {
+ EncodeVarint<uint32_t>(static_cast<uint32_t>(context_symbols_[i].size()),
+ GetOutputBuffer());
+ if (context_symbols_[i].size() > 0) {
+ EncodeSymbols(context_symbols_[i].data(),
+ static_cast<int>(context_symbols_[i].size()), 1, nullptr,
+ GetOutputBuffer());
+ }
+ }
+ }
+
+ int NumEncodedSymbols() const { return num_symbols_; }
+
+ private:
+ const CornerTable *corner_table_;
+ // Explicit map between corners and vertices. We cannot use the one stored
+ // in the |corner_table_| because we may need to add additional vertices to
+ // handle split symbols.
+ IndexTypeVector<CornerIndex, VertexIndex> corner_to_vertex_map_;
+ IndexTypeVector<VertexIndex, int> vertex_valences_;
+ // Previously encoded symbol.
+ int32_t prev_symbol_;
+ CornerIndex last_corner_;
+ // Explicitly count the number of encoded symbols.
+ int num_symbols_;
+
+ int min_valence_;
+ int max_valence_;
+ std::vector<std::vector<uint32_t>> context_symbols_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_VALENCE_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder.cc b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder.cc
new file mode 100644
index 00000000000..19d1355a443
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder.cc
@@ -0,0 +1,34 @@
+// 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 "draco/compression/mesh/mesh_encoder.h"
+
+namespace draco {
+
+MeshEncoder::MeshEncoder() : mesh_(nullptr), num_encoded_faces_(0) {}
+
+void MeshEncoder::SetMesh(const Mesh &m) {
+ mesh_ = &m;
+ SetPointCloud(m);
+}
+
+bool MeshEncoder::EncodeGeometryData() {
+ if (!EncodeConnectivity())
+ return false;
+ if (options()->GetGlobalBool("store_number_of_encoded_faces", false))
+ ComputeNumberOfEncodedFaces();
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder.h
new file mode 100644
index 00000000000..41387d569c6
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder.h
@@ -0,0 +1,84 @@
+// 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_COMPRESSION_MESH_MESH_ENCODER_H_
+#define DRACO_COMPRESSION_MESH_MESH_ENCODER_H_
+
+#include "draco/compression/attributes/mesh_attribute_indices_encoding_data.h"
+#include "draco/compression/point_cloud/point_cloud_encoder.h"
+#include "draco/mesh/mesh.h"
+#include "draco/mesh/mesh_attribute_corner_table.h"
+
+namespace draco {
+
+// Abstract base class for all mesh encoders. It provides some basic
+// functionality that's shared between different encoders.
+class MeshEncoder : public PointCloudEncoder {
+ public:
+ MeshEncoder();
+
+ // Sets the mesh that is going be encoded. Must be called before the Encode()
+ // method.
+ void SetMesh(const Mesh &m);
+
+ EncodedGeometryType GetGeometryType() const override {
+ return TRIANGULAR_MESH;
+ }
+
+ // Returns the number of faces that were encoded during the last Encode().
+ // function call. Valid only if "store_number_of_encoded_faces" flag was set
+ // in the provided EncoderOptions.
+ size_t num_encoded_faces() const { return num_encoded_faces_; }
+
+ // Returns the base connectivity of the encoded mesh (or nullptr if it is not
+ // initialized).
+ virtual const CornerTable *GetCornerTable() const { return nullptr; }
+
+ // Returns the attribute connectivity data or nullptr if it does not exist.
+ virtual const MeshAttributeCornerTable *GetAttributeCornerTable(
+ 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 {
+ return nullptr;
+ }
+
+ const Mesh *mesh() const { return mesh_; }
+
+ protected:
+ bool EncodeGeometryData() override;
+
+ // Needs to be implemented by the derived classes.
+ virtual bool EncodeConnectivity() = 0;
+
+ // Computes and sets the num_encoded_faces_ for the encoder.
+ virtual void ComputeNumberOfEncodedFaces() = 0;
+
+ void set_mesh(const Mesh *mesh) { mesh_ = mesh; }
+ void set_num_encoded_faces(size_t num_faces) {
+ num_encoded_faces_ = num_faces;
+ }
+
+ private:
+ const Mesh *mesh_;
+ size_t num_encoded_faces_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder_helpers.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder_helpers.h
new file mode 100644
index 00000000000..d809a521369
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder_helpers.h
@@ -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.
+//
+#ifndef DRACO_COMPRESSION_MESH_MESH_ENCODER_HELPERS_H_
+#define DRACO_COMPRESSION_MESH_MESH_ENCODER_HELPERS_H_
+
+#include "draco/compression/mesh/mesh_encoder.h"
+#include "draco/mesh/triangle_soup_mesh_builder.h"
+
+namespace draco {
+
+// Helper class for encoding data supplied in a stream of single precision
+// floating point numbers formatted as XYZ|UV. The stream must contain three
+// XYZ|UV values for every face of the mesh.
+// The encoded data is written into the output stream "os".
+// In case of error, the stream is set to an invalid state (ios_base::bad_bit).
+template <typename OStreamT>
+OStreamT EncodePos3Tex2DataToStream(
+ const float *data, int num_faces, CompressionMethod method,
+ const MeshCompressionOptions &compression_options,
+ const MeshAttributeCompressionOptions &pos_options,
+ const MeshAttributeCompressionOptions &tex_options, OStreamT &&os) {
+ // Build the mesh.
+ TriangleSoupMeshBuilder mb;
+ mb.Start(num_faces);
+ const int pos_att_id =
+ mb.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32);
+ const int tex_att_id =
+ mb.AddAttribute(GeometryAttribute::TEX_COORD_0, 2, DT_FLOAT32);
+ constexpr int data_stride = 5;
+ constexpr int tex_offset = 3;
+ for (int f = 0; f < num_faces; ++f) {
+ int offset = 3 * f * data_stride;
+ // Add position data for the face.
+ mb.SetAttributeValuesForFace(pos_att_id, f, data + offset,
+ data + offset + data_stride,
+ data + offset + 2 * data_stride);
+ // Add texture data for the face.
+ offset += tex_offset;
+ mb.SetAttributeValuesForFace(tex_att_id, f, data + offset,
+ data + offset + data_stride,
+ data + offset + 2 * data_stride);
+ }
+ std::unique_ptr<Mesh> mesh = mb.Finalize();
+ if (mesh == nullptr) {
+ os.setstate(ios_base::badbit);
+ return os;
+ }
+
+ // Set up the encoder.
+ std::unique_ptr<MeshEncoder> encoder =
+ MeshEncoder::CreateEncoderForMethod(method);
+ encoder->SetGlobalOptions(compression_options);
+ encoder->SetAttributeOptions(GeometryAttribute::POSITION, pos_options);
+ encoder->SetAttributeOptions(GeometryAttribute::TEX_COORD_0, tex_options);
+
+ if (!encoder->EncodeMesh(mesh.get())) {
+ os.setstate(ios_base::badbit);
+ return os;
+ }
+
+ // Write the encoded data into the stream.
+ os.write(static_cast<const char *>(encoder->buffer()->data()),
+ encoder->buffer()->size());
+ return os;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_ENCODER_HELPERS_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder_test.cc b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_encoder_test.cc
new file mode 100644
index 00000000000..e67861a2ec2
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/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 "draco/compression/mesh/mesh_encoder.h"
+
+#include "draco/compression/expert_encode.h"
+#include "draco/core/decoder_buffer.h"
+#include "draco/core/draco_test_base.h"
+#include "draco/core/draco_test_utils.h"
+#include "draco/io/obj_decoder.h"
+
+namespace draco {
+
+class MeshEncoderTest : public ::testing::TestWithParam<const char *> {
+ 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;
+ }
+};
+
+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 += ".1.2.0.drc";
+ const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name));
+ ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
+
+ ExpertEncoder encoder(*mesh.get());
+ encoder.SetEncodingMethod(method);
+ encoder.SetAttributeQuantization(0, 20);
+ EncoderBuffer buffer;
+ ASSERT_TRUE(encoder.EncodeToBuffer(&buffer).ok())
+ << "Failed encoding test mesh " << file_name << " with method "
+ << GetParam();
+ // Check that the encoded mesh was really encoded with the selected method.
+ DecoderBuffer decoder_buffer;
+ decoder_buffer.Init(buffer.data(), buffer.size());
+ decoder_buffer.Advance(8); // Skip the header to the encoding method id.
+ uint8_t encoded_method;
+ decoder_buffer.Decode(&encoded_method);
+ ASSERT_EQ(encoded_method, method);
+ 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/extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_decoder.cc b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_decoder.cc
new file mode 100644
index 00000000000..de71700e160
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_decoder.cc
@@ -0,0 +1,150 @@
+// 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 "draco/compression/mesh/mesh_sequential_decoder.h"
+
+#include "draco/compression/attributes/linear_sequencer.h"
+#include "draco/compression/attributes/sequential_attribute_decoders_controller.h"
+#include "draco/compression/entropy/symbol_decoding.h"
+#include "draco/core/varint_decoding.h"
+
+namespace draco {
+
+MeshSequentialDecoder::MeshSequentialDecoder() {}
+
+bool MeshSequentialDecoder::DecodeConnectivity() {
+ uint32_t num_faces;
+ uint32_t num_points;
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (bitstream_version() < DRACO_BITSTREAM_VERSION(2, 2)) {
+ if (!buffer()->Decode(&num_faces))
+ return false;
+ if (!buffer()->Decode(&num_points))
+ return false;
+
+ } else
+#endif
+ {
+ if (!DecodeVarint(&num_faces, buffer()))
+ return false;
+ if (!DecodeVarint(&num_points, buffer()))
+ return false;
+ }
+
+ // Check that num_faces and num_points are valid values.
+ const uint64_t faces_64 = static_cast<uint64_t>(num_faces);
+ const uint64_t points_64 = static_cast<uint64_t>(num_points);
+ // Compressed sequential encoding can only handle (2^32 - 1) / 3 indices.
+ if (faces_64 > 0xffffffff / 3)
+ return false;
+ if (points_64 > faces_64 * 3)
+ return false;
+ uint8_t connectivity_method;
+ if (!buffer()->Decode(&connectivity_method))
+ return false;
+ if (connectivity_method == 0) {
+ if (!DecodeAndDecompressIndices(num_faces))
+ return false;
+ } else {
+ if (num_points < 256) {
+ // Decode indices as uint8_t.
+ for (uint32_t i = 0; i < num_faces; ++i) {
+ Mesh::Face face;
+ for (int j = 0; j < 3; ++j) {
+ uint8_t val;
+ if (!buffer()->Decode(&val))
+ return false;
+ face[j] = val;
+ }
+ mesh()->AddFace(face);
+ }
+ } else if (num_points < (1 << 16)) {
+ // Decode indices as uint16_t.
+ for (uint32_t i = 0; i < num_faces; ++i) {
+ Mesh::Face face;
+ for (int j = 0; j < 3; ++j) {
+ uint16_t val;
+ if (!buffer()->Decode(&val))
+ return false;
+ face[j] = val;
+ }
+ mesh()->AddFace(face);
+ }
+ } else if (mesh()->num_points() < (1 << 21) &&
+ bitstream_version() >= DRACO_BITSTREAM_VERSION(2, 2)) {
+ // Decode indices as uint32_t.
+ for (uint32_t i = 0; i < num_faces; ++i) {
+ Mesh::Face face;
+ for (int j = 0; j < 3; ++j) {
+ uint32_t val;
+ if (!DecodeVarint(&val, buffer()))
+ return false;
+ face[j] = val;
+ }
+ mesh()->AddFace(face);
+ }
+ } else {
+ // Decode faces as uint32_t (default).
+ for (uint32_t i = 0; i < num_faces; ++i) {
+ Mesh::Face face;
+ for (int j = 0; j < 3; ++j) {
+ uint32_t val;
+ if (!buffer()->Decode(&val))
+ return false;
+ face[j] = val;
+ }
+ mesh()->AddFace(face);
+ }
+ }
+ }
+ point_cloud()->set_num_points(num_points);
+ return true;
+}
+
+bool MeshSequentialDecoder::CreateAttributesDecoder(int32_t att_decoder_id) {
+ // Always create the basic attribute decoder.
+ return SetAttributesDecoder(
+ att_decoder_id,
+ std::unique_ptr<AttributesDecoder>(
+ new SequentialAttributeDecodersController(
+ std::unique_ptr<PointsSequencer>(
+ new LinearSequencer(point_cloud()->num_points())))));
+}
+
+bool MeshSequentialDecoder::DecodeAndDecompressIndices(uint32_t num_faces) {
+ // Get decoded indices differences that were encoded with an entropy code.
+ std::vector<uint32_t> indices_buffer(num_faces * 3);
+ if (!DecodeSymbols(num_faces * 3, 1, buffer(), indices_buffer.data()))
+ return false;
+ // Reconstruct the indices from the differences.
+ // See MeshSequentialEncoder::CompressAndEncodeIndices() for more details.
+ int32_t last_index_value = 0;
+ int vertex_index = 0;
+ for (uint32_t i = 0; i < num_faces; ++i) {
+ Mesh::Face face;
+ for (int j = 0; j < 3; ++j) {
+ const uint32_t encoded_val = indices_buffer[vertex_index++];
+ int32_t index_diff = (encoded_val >> 1);
+ if (encoded_val & 1)
+ index_diff = -index_diff;
+ const int32_t index_value = index_diff + last_index_value;
+ face[j] = index_value;
+ last_index_value = index_value;
+ }
+ mesh()->AddFace(face);
+ }
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_decoder.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_decoder.h
new file mode 100644
index 00000000000..3a86c75d5e9
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_decoder.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_COMPRESSION_MESH_MESH_SEQUENTIAL_DECODER_H_
+#define DRACO_COMPRESSION_MESH_MESH_SEQUENTIAL_DECODER_H_
+
+#include "draco/compression/mesh/mesh_decoder.h"
+
+namespace draco {
+
+// Class for decoding data encoded by MeshSequentialEncoder.
+class MeshSequentialDecoder : public MeshDecoder {
+ public:
+ MeshSequentialDecoder();
+
+ protected:
+ bool DecodeConnectivity() override;
+ bool CreateAttributesDecoder(int32_t att_decoder_id) override;
+
+ private:
+ // Decodes face indices that were compressed with an entropy code.
+ // Returns false on error.
+ bool DecodeAndDecompressIndices(uint32_t num_faces);
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_SEQUENTIAL_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_encoder.cc b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_encoder.cc
new file mode 100644
index 00000000000..44b3b20b63c
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_encoder.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 "draco/compression/mesh/mesh_sequential_encoder.h"
+
+#include <cstdlib>
+
+#include "draco/compression/attributes/linear_sequencer.h"
+#include "draco/compression/attributes/sequential_attribute_encoders_controller.h"
+#include "draco/compression/entropy/symbol_encoding.h"
+#include "draco/core/varint_encoding.h"
+
+namespace draco {
+
+MeshSequentialEncoder::MeshSequentialEncoder() {}
+
+bool MeshSequentialEncoder::EncodeConnectivity() {
+ // Serialize indices.
+ const uint32_t num_faces = mesh()->num_faces();
+ EncodeVarint(num_faces, buffer());
+ EncodeVarint(static_cast<uint32_t>(mesh()->num_points()), buffer());
+
+ // We encode all attributes in the original (possibly duplicated) format.
+ // TODO(ostava): This may not be optimal if we have only one attribute or if
+ // all attributes share the same index mapping.
+ if (options()->GetGlobalBool("compress_connectivity", false)) {
+ // 0 = Encode compressed indices.
+ buffer()->Encode(static_cast<uint8_t>(0));
+ if (!CompressAndEncodeIndices())
+ return false;
+ } else {
+ // 1 = Encode indices directly.
+ buffer()->Encode(static_cast<uint8_t>(1));
+ // Store vertex indices using a smallest data type that fits their range.
+ // TODO(ostava): This can be potentially improved by using a tighter
+ // fit that is not bound by a bit-length of any particular data type.
+ if (mesh()->num_points() < 256) {
+ // Serialize indices as uint8_t.
+ for (FaceIndex i(0); i < num_faces; ++i) {
+ const auto &face = mesh()->face(i);
+ buffer()->Encode(static_cast<uint8_t>(face[0].value()));
+ buffer()->Encode(static_cast<uint8_t>(face[1].value()));
+ buffer()->Encode(static_cast<uint8_t>(face[2].value()));
+ }
+ } else if (mesh()->num_points() < (1 << 16)) {
+ // Serialize indices as uint16_t.
+ for (FaceIndex i(0); i < num_faces; ++i) {
+ const auto &face = mesh()->face(i);
+ buffer()->Encode(static_cast<uint16_t>(face[0].value()));
+ buffer()->Encode(static_cast<uint16_t>(face[1].value()));
+ buffer()->Encode(static_cast<uint16_t>(face[2].value()));
+ }
+ } else if (mesh()->num_points() < (1 << 21)) {
+ // Serialize indices as varint.
+ for (FaceIndex i(0); i < num_faces; ++i) {
+ const auto &face = mesh()->face(i);
+ EncodeVarint(static_cast<uint32_t>(face[0].value()), buffer());
+ EncodeVarint(static_cast<uint32_t>(face[1].value()), buffer());
+ EncodeVarint(static_cast<uint32_t>(face[2].value()), buffer());
+ }
+ } else {
+ // Serialize faces as uint32_t (default).
+ for (FaceIndex i(0); i < num_faces; ++i) {
+ const auto &face = mesh()->face(i);
+ buffer()->Encode(face);
+ }
+ }
+ }
+ return true;
+}
+
+bool MeshSequentialEncoder::GenerateAttributesEncoder(int32_t att_id) {
+ // Create only one attribute encoder that is going to encode all points in a
+ // linear sequence.
+ if (att_id == 0) {
+ // Create a new attribute encoder only for the first attribute.
+ AddAttributesEncoder(std::unique_ptr<AttributesEncoder>(
+ new SequentialAttributeEncodersController(
+ std::unique_ptr<PointsSequencer>(
+ new LinearSequencer(point_cloud()->num_points())),
+ att_id)));
+ } else {
+ // Reuse the existing attribute encoder for other attributes.
+ attributes_encoder(0)->AddAttributeId(att_id);
+ }
+ return true;
+}
+
+bool MeshSequentialEncoder::CompressAndEncodeIndices() {
+ // Collect all indices to a buffer and encode them.
+ // Each new index is a difference from the previous value.
+ std::vector<uint32_t> indices_buffer;
+ int32_t last_index_value = 0;
+ const int num_faces = mesh()->num_faces();
+ for (FaceIndex i(0); i < num_faces; ++i) {
+ const auto &face = mesh()->face(i);
+ for (int j = 0; j < 3; ++j) {
+ const int32_t index_value = face[j].value();
+ const int32_t index_diff = index_value - last_index_value;
+ // Encode signed value to an unsigned one (put the sign to lsb pos).
+ const uint32_t encoded_val =
+ (abs(index_diff) << 1) | (index_diff < 0 ? 1 : 0);
+ indices_buffer.push_back(encoded_val);
+ last_index_value = index_value;
+ }
+ }
+ EncodeSymbols(indices_buffer.data(), static_cast<int>(indices_buffer.size()),
+ 1, nullptr, buffer());
+ return true;
+}
+
+void MeshSequentialEncoder::ComputeNumberOfEncodedPoints() {
+ set_num_encoded_points(mesh()->num_points());
+}
+
+void MeshSequentialEncoder::ComputeNumberOfEncodedFaces() {
+ set_num_encoded_faces(mesh()->num_faces());
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_encoder.h b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_encoder.h
new file mode 100644
index 00000000000..47a4fec837c
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/mesh_sequential_encoder.h
@@ -0,0 +1,57 @@
+// 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.
+//
+// The encoder compresses all attribute values using an order preserving
+// attribute encoder (that can still support quantization, prediction schemes,
+// and other features).
+// The mesh connectivity data can be encoded using two modes that are controlled
+// using a global encoder options flag called "compress_connectivity"
+// 1. When "compress_connectivity" == true:
+// All point ids are first delta coded and then compressed using an entropy
+// coding.
+// 2. When "compress_connectivity" == false:
+// All point ids are encoded directly using either 8, 16, or 32 bits per
+// value based on the maximum point id value.
+
+#ifndef DRACO_COMPRESSION_MESH_MESH_SEQUENTIAL_ENCODER_H_
+#define DRACO_COMPRESSION_MESH_MESH_SEQUENTIAL_ENCODER_H_
+
+#include "draco/compression/mesh/mesh_encoder.h"
+
+namespace draco {
+
+// Class that encodes mesh data using a simple binary representation of mesh's
+// connectivity and geometry.
+// TODO(ostava): Use a better name.
+class MeshSequentialEncoder : public MeshEncoder {
+ public:
+ MeshSequentialEncoder();
+ uint8_t GetEncodingMethod() const override {
+ return MESH_SEQUENTIAL_ENCODING;
+ }
+
+ protected:
+ bool EncodeConnectivity() override;
+ bool GenerateAttributesEncoder(int32_t att_id) override;
+ void ComputeNumberOfEncodedPoints() override;
+ void ComputeNumberOfEncodedFaces() override;
+
+ private:
+ // Returns false on error.
+ bool CompressAndEncodeIndices();
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_MESH_SEQUENTIAL_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/traverser/depth_first_traverser.h b/extern/draco/dracoenc/src/draco/compression/mesh/traverser/depth_first_traverser.h
new file mode 100644
index 00000000000..84420cb3888
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/traverser/depth_first_traverser.h
@@ -0,0 +1,169 @@
+// 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_COMPRESSION_MESH_TRAVERSER_DEPTH_FIRST_TRAVERSER_H_
+#define DRACO_COMPRESSION_MESH_TRAVERSER_DEPTH_FIRST_TRAVERSER_H_
+
+#include <vector>
+
+#include "draco/compression/mesh/traverser/traverser_base.h"
+#include "draco/mesh/corner_table.h"
+
+namespace draco {
+
+// Basic traverser that traverses a mesh in a DFS like fashion using the
+// CornerTable data structure. The necessary bookkeeping is available via the
+// TraverserBase. Callbacks are handled through template argument
+// TraversalObserverT.
+//
+// TraversalObserverT can perform an action on a traversal event such as newly
+// visited face, or corner, but it does not affect the traversal itself.
+//
+// Concept TraversalObserverT requires:
+//
+// public:
+// void OnNewFaceVisited(FaceIndex face);
+// - Called whenever a previously unvisited face is reached.
+//
+// void OnNewVertexVisited(VertexIndex vert, CornerIndex corner)
+// - Called when a new vertex is visited. |corner| is used to indicate the
+// which of the vertex's corners has been reached.
+
+template <class CornerTableT, class TraversalObserverT>
+class DepthFirstTraverser
+ : public TraverserBase<CornerTableT, TraversalObserverT> {
+ public:
+ typedef CornerTableT CornerTable;
+ typedef TraversalObserverT TraversalObserver;
+ typedef TraverserBase<CornerTable, TraversalObserver> Base;
+
+ DepthFirstTraverser() {}
+
+ // Called before any traversing starts.
+ void OnTraversalStart() {}
+
+ // Called when all the traversing is done.
+ void OnTraversalEnd() {}
+
+ bool TraverseFromCorner(CornerIndex corner_id) {
+ if (this->IsFaceVisited(corner_id))
+ return true; // Already traversed.
+
+ corner_traversal_stack_.clear();
+ corner_traversal_stack_.push_back(corner_id);
+ // For the first face, check the remaining corners as they may not be
+ // processed yet.
+ const VertexIndex next_vert =
+ this->corner_table()->Vertex(this->corner_table()->Next(corner_id));
+ const VertexIndex prev_vert =
+ this->corner_table()->Vertex(this->corner_table()->Previous(corner_id));
+ if (next_vert == kInvalidVertexIndex || prev_vert == kInvalidVertexIndex)
+ return false;
+ if (!this->IsVertexVisited(next_vert)) {
+ this->MarkVertexVisited(next_vert);
+ this->traversal_observer().OnNewVertexVisited(
+ next_vert, this->corner_table()->Next(corner_id));
+ }
+ if (!this->IsVertexVisited(prev_vert)) {
+ this->MarkVertexVisited(prev_vert);
+ this->traversal_observer().OnNewVertexVisited(
+ prev_vert, this->corner_table()->Previous(corner_id));
+ }
+
+ // Start the actual traversal.
+ while (!corner_traversal_stack_.empty()) {
+ // Currently processed corner.
+ corner_id = corner_traversal_stack_.back();
+ FaceIndex face_id(corner_id.value() / 3);
+ // Make sure the face hasn't been visited yet.
+ if (corner_id == kInvalidCornerIndex || this->IsFaceVisited(face_id)) {
+ // This face has been already traversed.
+ corner_traversal_stack_.pop_back();
+ continue;
+ }
+ while (true) {
+ this->MarkFaceVisited(face_id);
+ this->traversal_observer().OnNewFaceVisited(face_id);
+ const VertexIndex vert_id = this->corner_table()->Vertex(corner_id);
+ if (vert_id == kInvalidVertexIndex)
+ return false;
+ if (!this->IsVertexVisited(vert_id)) {
+ const bool on_boundary = this->corner_table()->IsOnBoundary(vert_id);
+ this->MarkVertexVisited(vert_id);
+ this->traversal_observer().OnNewVertexVisited(vert_id, corner_id);
+ if (!on_boundary) {
+ corner_id = this->corner_table()->GetRightCorner(corner_id);
+ face_id = FaceIndex(corner_id.value() / 3);
+ continue;
+ }
+ }
+ // The current vertex has been already visited or it was on a boundary.
+ // We need to determine whether we can visit any of it's neighboring
+ // faces.
+ const CornerIndex right_corner_id =
+ this->corner_table()->GetRightCorner(corner_id);
+ const CornerIndex left_corner_id =
+ this->corner_table()->GetLeftCorner(corner_id);
+ const FaceIndex right_face_id(
+ (right_corner_id == kInvalidCornerIndex
+ ? kInvalidFaceIndex
+ : FaceIndex(right_corner_id.value() / 3)));
+ const FaceIndex left_face_id(
+ (left_corner_id == kInvalidCornerIndex
+ ? kInvalidFaceIndex
+ : FaceIndex(left_corner_id.value() / 3)));
+ if (this->IsFaceVisited(right_face_id)) {
+ // Right face has been already visited.
+ if (this->IsFaceVisited(left_face_id)) {
+ // Both neighboring faces are visited. End reached.
+ corner_traversal_stack_.pop_back();
+ break; // Break from the while (true) loop.
+ } else {
+ // Go to the left face.
+ corner_id = left_corner_id;
+ face_id = left_face_id;
+ }
+ } else {
+ // Right face was not visited.
+ if (this->IsFaceVisited(left_face_id)) {
+ // Left face visited, go to the right one.
+ corner_id = right_corner_id;
+ face_id = right_face_id;
+ } else {
+ // Both neighboring faces are unvisited, we need to visit both of
+ // them.
+
+ // Split the traversal.
+ // First make the top of the current corner stack point to the left
+ // face (this one will be processed second).
+ corner_traversal_stack_.back() = left_corner_id;
+ // Add a new corner to the top of the stack (right face needs to
+ // be traversed first).
+ corner_traversal_stack_.push_back(right_corner_id);
+ // Break from the while (true) loop.
+ break;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ private:
+ std::vector<CornerIndex> corner_traversal_stack_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_TRAVERSER_DEPTH_FIRST_TRAVERSER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/traverser/max_prediction_degree_traverser.h b/extern/draco/dracoenc/src/draco/compression/mesh/traverser/max_prediction_degree_traverser.h
new file mode 100644
index 00000000000..b60d2c159f2
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/traverser/max_prediction_degree_traverser.h
@@ -0,0 +1,223 @@
+// 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_COMPRESSION_MESH_TRAVERSER_MAX_PREDICTION_DEGREE_TRAVERSER_H_
+#define DRACO_COMPRESSION_MESH_TRAVERSER_MAX_PREDICTION_DEGREE_TRAVERSER_H_
+
+#include <vector>
+
+#include "draco/compression/mesh/traverser/traverser_base.h"
+#include "draco/mesh/corner_table.h"
+
+namespace draco {
+
+// PredictionDegreeTraverser provides framework for traversal over a corner
+// table data structure following paper "Multi-way Geometry Encoding" by
+// Cohen-or at al.'02. The traversal is implicitly guided by prediction degree
+// of the destination vertices. A prediction degree is computed as the number of
+// possible faces that can be used as source points for traversal to the given
+// destination vertex (see image below, where faces F1 and F2 are already
+// traversed and face F0 is not traversed yet. The prediction degree of vertex
+// V is then equal to two).
+//
+// X-----V-----o
+// / \ / \ / \
+// / F0\ / \ / F2\
+// X-----o-----o-----B
+// \ F1/
+// \ /
+// A
+//
+// The class implements the same interface as the DepthFirstTraverser
+// (depth_first_traverser.h) and it can be controlled via the same template
+// trait classes |CornerTableT| and |TraversalObserverT|, that are used
+// for controlling and monitoring of the traversal respectively. For details,
+// please see depth_first_traverser.h.
+template <class CornerTableT, class TraversalObserverT>
+class MaxPredictionDegreeTraverser
+ : public TraverserBase<CornerTable, TraversalObserverT> {
+ public:
+ typedef CornerTableT CornerTable;
+ typedef TraversalObserverT TraversalObserver;
+ typedef TraverserBase<CornerTable, TraversalObserver> Base;
+
+ MaxPredictionDegreeTraverser() {}
+
+ // Called before any traversing starts.
+ void OnTraversalStart() {
+ prediction_degree_.resize(this->corner_table()->num_vertices(), 0);
+ }
+
+ // Called when all the traversing is done.
+ void OnTraversalEnd() {}
+
+ bool TraverseFromCorner(CornerIndex corner_id) {
+ if (prediction_degree_.size() == 0)
+ return true;
+
+ // Traversal starts from the |corner_id|. It's going to follow either the
+ // right or the left neighboring faces to |corner_id| based on their
+ // prediction degree.
+ traversal_stacks_[0].push_back(corner_id);
+ best_priority_ = 0;
+ // For the first face, check the remaining corners as they may not be
+ // processed yet.
+ const VertexIndex next_vert =
+ this->corner_table()->Vertex(this->corner_table()->Next(corner_id));
+ const VertexIndex prev_vert =
+ this->corner_table()->Vertex(this->corner_table()->Previous(corner_id));
+ if (!this->IsVertexVisited(next_vert)) {
+ this->MarkVertexVisited(next_vert);
+ this->traversal_observer().OnNewVertexVisited(
+ next_vert, this->corner_table()->Next(corner_id));
+ }
+ if (!this->IsVertexVisited(prev_vert)) {
+ this->MarkVertexVisited(prev_vert);
+ this->traversal_observer().OnNewVertexVisited(
+ prev_vert, this->corner_table()->Previous(corner_id));
+ }
+ const VertexIndex tip_vertex = this->corner_table()->Vertex(corner_id);
+ if (!this->IsVertexVisited(tip_vertex)) {
+ this->MarkVertexVisited(tip_vertex);
+ this->traversal_observer().OnNewVertexVisited(tip_vertex, corner_id);
+ }
+ // Start the actual traversal.
+ while ((corner_id = PopNextCornerToTraverse()) != kInvalidCornerIndex) {
+ FaceIndex face_id(corner_id.value() / 3);
+ // Make sure the face hasn't been visited yet.
+ if (this->IsFaceVisited(face_id)) {
+ // This face has been already traversed.
+ continue;
+ }
+
+ while (true) {
+ face_id = FaceIndex(corner_id.value() / 3);
+ this->MarkFaceVisited(face_id);
+ this->traversal_observer().OnNewFaceVisited(face_id);
+
+ // If the newly reached vertex hasn't been visited, mark it and notify
+ // the observer.
+ const VertexIndex vert_id = this->corner_table()->Vertex(corner_id);
+ if (!this->IsVertexVisited(vert_id)) {
+ this->MarkVertexVisited(vert_id);
+ this->traversal_observer().OnNewVertexVisited(vert_id, corner_id);
+ }
+
+ // Check whether we can traverse to the right and left neighboring
+ // faces.
+ const CornerIndex right_corner_id =
+ this->corner_table()->GetRightCorner(corner_id);
+ const CornerIndex left_corner_id =
+ this->corner_table()->GetLeftCorner(corner_id);
+ const FaceIndex right_face_id(
+ (right_corner_id == kInvalidCornerIndex
+ ? kInvalidFaceIndex
+ : FaceIndex(right_corner_id.value() / 3)));
+ const FaceIndex left_face_id(
+ (left_corner_id == kInvalidCornerIndex
+ ? kInvalidFaceIndex
+ : FaceIndex(left_corner_id.value() / 3)));
+ const bool is_right_face_visited = this->IsFaceVisited(right_face_id);
+ const bool is_left_face_visited = this->IsFaceVisited(left_face_id);
+
+ if (!is_left_face_visited) {
+ // We can go to the left face.
+ const int priority = ComputePriority(left_corner_id);
+ if (is_right_face_visited && priority <= best_priority_) {
+ // Right face has been already visited and the priority is equal or
+ // better than the best priority. We are sure that the left face
+ // would be traversed next so there is no need to put it onto the
+ // stack.
+ corner_id = left_corner_id;
+ continue;
+ } else {
+ AddCornerToTraversalStack(left_corner_id, priority);
+ }
+ }
+ if (!is_right_face_visited) {
+ // Go to the right face.
+ const int priority = ComputePriority(right_corner_id);
+ if (priority <= best_priority_) {
+ // We are sure that the right face would be traversed next so there
+ // is no need to put it onto the stack.
+ corner_id = right_corner_id;
+ continue;
+ } else {
+ AddCornerToTraversalStack(right_corner_id, priority);
+ }
+ }
+
+ // Couldn't proceed directly to the next corner
+ break;
+ }
+ }
+ return true;
+ }
+
+ private:
+ // Retrieves the next available corner (edge) to traverse. Edges are processed
+ // based on their priorities.
+ // Returns kInvalidCornerIndex when there is no edge available.
+ CornerIndex PopNextCornerToTraverse() {
+ for (int i = best_priority_; i < kMaxPriority; ++i) {
+ if (!traversal_stacks_[i].empty()) {
+ const CornerIndex ret = traversal_stacks_[i].back();
+ traversal_stacks_[i].pop_back();
+ best_priority_ = i;
+ return ret;
+ }
+ }
+ return kInvalidCornerIndex;
+ }
+
+ inline void AddCornerToTraversalStack(CornerIndex ci, int priority) {
+ traversal_stacks_[priority].push_back(ci);
+ // Make sure that the best available priority is up to date.
+ if (priority < best_priority_)
+ best_priority_ = priority;
+ }
+
+ // Returns the priority of traversing edge leading to |corner_id|.
+ inline int ComputePriority(CornerIndex corner_id) {
+ const VertexIndex v_tip = this->corner_table()->Vertex(corner_id);
+ // Priority 0 when traversing to already visited vertices.
+ int priority = 0;
+ if (!this->IsVertexVisited(v_tip)) {
+ const int degree = ++prediction_degree_[v_tip];
+ // Priority 1 when prediction degree > 1, otherwise 2.
+ priority = (degree > 1 ? 1 : 2);
+ }
+ // Clamp the priority to the maximum number of buckets.
+ if (priority >= kMaxPriority)
+ priority = kMaxPriority - 1;
+ return priority;
+ }
+
+ // For efficiency reasons, the priority traversal is implemented using buckets
+ // where each buckets represent a stack of available corners for a given
+ // priority. Corners with the highest priority are always processed first.
+ static constexpr int kMaxPriority = 3;
+ std::vector<CornerIndex> traversal_stacks_[kMaxPriority];
+
+ // Keep the track of the best available priority to improve the performance
+ // of PopNextCornerToTraverse() method.
+ int best_priority_;
+
+ // Prediction degree available for each vertex.
+ IndexTypeVector<VertexIndex, int> prediction_degree_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_TRAVERSER_MAX_PREDICTION_DEGREE_TRAVERSER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/traverser/mesh_attribute_indices_encoding_observer.h b/extern/draco/dracoenc/src/draco/compression/mesh/traverser/mesh_attribute_indices_encoding_observer.h
new file mode 100644
index 00000000000..e66dd14b238
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/traverser/mesh_attribute_indices_encoding_observer.h
@@ -0,0 +1,76 @@
+// 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_COMPRESSION_MESH_TRAVERSER_MESH_ATTRIBUTE_INDICES_ENCODING_OBSERVER_H_
+#define DRACO_COMPRESSION_MESH_TRAVERSER_MESH_ATTRIBUTE_INDICES_ENCODING_OBSERVER_H_
+
+#include "draco/compression/attributes/mesh_attribute_indices_encoding_data.h"
+#include "draco/compression/attributes/points_sequencer.h"
+#include "draco/mesh/mesh.h"
+
+namespace draco {
+
+// Class that can be used to generate encoding (and decoding) order of attribute
+// values based on the traversal of the encoded mesh. The class should be used
+// as the TraversalObserverT member of a Traverser class such as the
+// DepthFirstTraverser (depth_first_traverser.h).
+// TODO(hemmer): rename to AttributeIndicesCodingTraverserObserver
+template <class CornerTableT>
+class MeshAttributeIndicesEncodingObserver {
+ public:
+ MeshAttributeIndicesEncodingObserver()
+ : att_connectivity_(nullptr),
+ encoding_data_(nullptr),
+ mesh_(nullptr),
+ sequencer_(nullptr) {}
+ MeshAttributeIndicesEncodingObserver(
+ const CornerTableT *connectivity, const Mesh *mesh,
+ PointsSequencer *sequencer,
+ MeshAttributeIndicesEncodingData *encoding_data)
+ : att_connectivity_(connectivity),
+ encoding_data_(encoding_data),
+ mesh_(mesh),
+ sequencer_(sequencer) {}
+
+ // Interface for TraversalObserverT
+
+ void OnNewFaceVisited(FaceIndex /* face */) {}
+
+ inline void OnNewVertexVisited(VertexIndex vertex, CornerIndex corner) {
+ const PointIndex point_id =
+ mesh_->face(FaceIndex(corner.value() / 3))[corner.value() % 3];
+ // Append the visited attribute to the encoding order.
+ sequencer_->AddPointId(point_id);
+
+ // Keep track of visited corners.
+ encoding_data_->encoded_attribute_value_index_to_corner_map.push_back(
+ corner);
+
+ encoding_data_
+ ->vertex_to_encoded_attribute_value_index_map[vertex.value()] =
+ encoding_data_->num_values;
+
+ encoding_data_->num_values++;
+ }
+
+ private:
+ const CornerTableT *att_connectivity_;
+ MeshAttributeIndicesEncodingData *encoding_data_;
+ const Mesh *mesh_;
+ PointsSequencer *sequencer_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_TRAVERSER_MESH_ATTRIBUTE_INDICES_ENCODING_OBSERVER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/traverser/mesh_traversal_sequencer.h b/extern/draco/dracoenc/src/draco/compression/mesh/traverser/mesh_traversal_sequencer.h
new file mode 100644
index 00000000000..fbcda53b792
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/traverser/mesh_traversal_sequencer.h
@@ -0,0 +1,110 @@
+// 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_COMPRESSION_MESH_TRAVERSER_MESH_TRAVERSAL_SEQUENCER_H_
+#define DRACO_COMPRESSION_MESH_TRAVERSER_MESH_TRAVERSAL_SEQUENCER_H_
+
+#include "draco/attributes/geometry_indices.h"
+#include "draco/compression/attributes/mesh_attribute_indices_encoding_data.h"
+#include "draco/compression/attributes/points_sequencer.h"
+#include "draco/mesh/mesh.h"
+
+namespace draco {
+
+// Sequencer that generates point sequence in an order given by a deterministic
+// traversal on the mesh surface. Note that all attributes encoded with this
+// sequence must share the same connectivity.
+// TODO(hemmer): Consider refactoring such that this is an observer.
+template <class TraverserT>
+class MeshTraversalSequencer : public PointsSequencer {
+ public:
+ MeshTraversalSequencer(const Mesh *mesh,
+ const MeshAttributeIndicesEncodingData *encoding_data)
+ : mesh_(mesh), encoding_data_(encoding_data), corner_order_(nullptr) {}
+ void SetTraverser(const TraverserT &t) { traverser_ = t; }
+
+ // Function that can be used to set an order in which the mesh corners should
+ // be processed. This is an optional flag used usually only by the encoder
+ // to match the same corner order that is going to be used by the decoder.
+ // Note that |corner_order| should contain only one corner per face (it can
+ // have all corners but only the first encountered corner for each face is
+ // going to be used to start a traversal). If the corner order is not set, the
+ // corners are processed sequentially based on their ids.
+ void SetCornerOrder(const std::vector<CornerIndex> &corner_order) {
+ corner_order_ = &corner_order;
+ }
+
+ bool UpdatePointToAttributeIndexMapping(PointAttribute *attribute) override {
+ const auto *corner_table = traverser_.corner_table();
+ attribute->SetExplicitMapping(mesh_->num_points());
+ const size_t num_faces = mesh_->num_faces();
+ const size_t num_points = mesh_->num_points();
+ for (FaceIndex f(0); f < static_cast<uint32_t>(num_faces); ++f) {
+ const auto &face = mesh_->face(f);
+ for (int p = 0; p < 3; ++p) {
+ const PointIndex point_id = face[p];
+ const VertexIndex vert_id =
+ corner_table->Vertex(CornerIndex(3 * f.value() + p));
+ if (vert_id == kInvalidVertexIndex)
+ return false;
+ const AttributeValueIndex att_entry_id(
+ encoding_data_
+ ->vertex_to_encoded_attribute_value_index_map[vert_id.value()]);
+ if (att_entry_id.value() >= num_points) {
+ // There cannot be more attribute values than the number of points.
+ return false;
+ }
+ attribute->SetPointMapEntry(point_id, att_entry_id);
+ }
+ }
+ return true;
+ }
+
+ protected:
+ bool GenerateSequenceInternal() override {
+ // Preallocate memory for storing point indices. We expect the number of
+ // points to be the same as the number of corner table vertices.
+ out_point_ids()->reserve(traverser_.corner_table()->num_vertices());
+
+ traverser_.OnTraversalStart();
+ if (corner_order_) {
+ for (uint32_t i = 0; i < corner_order_->size(); ++i) {
+ if (!ProcessCorner(corner_order_->at(i)))
+ return false;
+ }
+ } else {
+ const int32_t num_faces = traverser_.corner_table()->num_faces();
+ for (int i = 0; i < num_faces; ++i) {
+ if (!ProcessCorner(CornerIndex(3 * i)))
+ return false;
+ }
+ }
+ traverser_.OnTraversalEnd();
+ return true;
+ }
+
+ private:
+ bool ProcessCorner(CornerIndex corner_id) {
+ return traverser_.TraverseFromCorner(corner_id);
+ }
+
+ TraverserT traverser_;
+ const Mesh *mesh_;
+ const MeshAttributeIndicesEncodingData *encoding_data_;
+ const std::vector<CornerIndex> *corner_order_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_TRAVERSER_MESH_TRAVERSAL_SEQUENCER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/mesh/traverser/traverser_base.h b/extern/draco/dracoenc/src/draco/compression/mesh/traverser/traverser_base.h
new file mode 100644
index 00000000000..643c5db565b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/mesh/traverser/traverser_base.h
@@ -0,0 +1,85 @@
+// 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_COMPRESSION_MESH_TRAVERSER_TRAVERSER_BASE_H_
+#define DRACO_COMPRESSION_MESH_TRAVERSER_TRAVERSER_BASE_H_
+
+#include "draco/mesh/corner_table.h"
+
+namespace draco {
+
+// Class providing the basic traversal functionality needed by traversers (such
+// as the DepthFirstTraverser, see depth_first_traverser.h). It keeps a pointer
+// to the corner table that is used for the traversal, plus it provides a basic
+// bookkeeping of visited faces and vertices during the traversal.
+template <class CornerTableT, class TraversalObserverT>
+class TraverserBase {
+ public:
+ typedef CornerTableT CornerTable;
+ typedef TraversalObserverT TraversalObserver;
+
+ TraverserBase() : corner_table_(nullptr) {}
+ virtual ~TraverserBase() = default;
+
+ virtual void Init(const CornerTable *corner_table,
+ TraversalObserver traversal_observer) {
+ corner_table_ = corner_table;
+ is_face_visited_.assign(corner_table->num_faces(), false);
+ is_vertex_visited_.assign(corner_table_->num_vertices(), false);
+ traversal_observer_ = traversal_observer;
+ }
+
+ const CornerTable &GetCornerTable() const { return *corner_table_; }
+
+ inline bool IsFaceVisited(FaceIndex face_id) const {
+ if (face_id == kInvalidFaceIndex)
+ return true; // Invalid faces are always considered as visited.
+ return is_face_visited_[face_id.value()];
+ }
+
+ // Returns true if the face containing the given corner was visited.
+ inline bool IsFaceVisited(CornerIndex corner_id) const {
+ if (corner_id == kInvalidCornerIndex)
+ return true; // Invalid faces are always considered as visited.
+ return is_face_visited_[corner_id.value() / 3];
+ }
+
+ inline void MarkFaceVisited(FaceIndex face_id) {
+ is_face_visited_[face_id.value()] = true;
+ }
+ inline bool IsVertexVisited(VertexIndex vert_id) const {
+ return is_vertex_visited_[vert_id.value()];
+ }
+ inline void MarkVertexVisited(VertexIndex vert_id) {
+ is_vertex_visited_[vert_id.value()] = true;
+ }
+
+ inline const CornerTable *corner_table() const { return corner_table_; }
+ inline const TraversalObserverT &traversal_observer() const {
+ return traversal_observer_;
+ }
+ inline TraversalObserverT &traversal_observer() {
+ return traversal_observer_;
+ }
+
+ private:
+ const CornerTable *corner_table_;
+ TraversalObserverT traversal_observer_;
+ std::vector<bool> is_face_visited_;
+ std::vector<bool> is_vertex_visited_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_MESH_TRAVERSER_TRAVERSER_BASE_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.cc b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.cc
new file mode 100644
index 00000000000..de46f05af67
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.cc
@@ -0,0 +1,26 @@
+// 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 "draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.h"
+
+#include "draco/compression/point_cloud/algorithms/point_cloud_types.h"
+
+namespace draco {
+
+template class DynamicIntegerPointsKdTreeDecoder<0>;
+template class DynamicIntegerPointsKdTreeDecoder<2>;
+template class DynamicIntegerPointsKdTreeDecoder<4>;
+template class DynamicIntegerPointsKdTreeDecoder<6>;
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.h
new file mode 100644
index 00000000000..e220a2ab166
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.h
@@ -0,0 +1,305 @@
+// 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.
+//
+// See dynamic_integer_points_kd_tree_encoder.h for documentation.
+
+#ifndef DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_DYNAMIC_INTEGER_POINTS_KD_TREE_DECODER_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_DYNAMIC_INTEGER_POINTS_KD_TREE_DECODER_H_
+
+#include <array>
+#include <memory>
+#include <stack>
+
+#include "draco/compression/bit_coders/adaptive_rans_bit_decoder.h"
+#include "draco/compression/bit_coders/direct_bit_decoder.h"
+#include "draco/compression/bit_coders/folded_integer_bit_decoder.h"
+#include "draco/compression/bit_coders/rans_bit_decoder.h"
+#include "draco/compression/point_cloud/algorithms/point_cloud_types.h"
+#include "draco/core/bit_utils.h"
+#include "draco/core/decoder_buffer.h"
+#include "draco/core/math_utils.h"
+
+namespace draco {
+
+template <int compression_level_t>
+struct DynamicIntegerPointsKdTreeDecoderCompressionPolicy
+ : public DynamicIntegerPointsKdTreeDecoderCompressionPolicy<
+ compression_level_t - 1> {};
+
+template <>
+struct DynamicIntegerPointsKdTreeDecoderCompressionPolicy<0> {
+ typedef DirectBitDecoder NumbersDecoder;
+ typedef DirectBitDecoder AxisDecoder;
+ typedef DirectBitDecoder HalfDecoder;
+ typedef DirectBitDecoder RemainingBitsDecoder;
+ static constexpr bool select_axis = false;
+};
+
+template <>
+struct DynamicIntegerPointsKdTreeDecoderCompressionPolicy<2>
+ : public DynamicIntegerPointsKdTreeDecoderCompressionPolicy<1> {
+ typedef RAnsBitDecoder NumbersDecoder;
+};
+
+template <>
+struct DynamicIntegerPointsKdTreeDecoderCompressionPolicy<4>
+ : public DynamicIntegerPointsKdTreeDecoderCompressionPolicy<3> {
+ typedef FoldedBit32Decoder<RAnsBitDecoder> NumbersDecoder;
+};
+
+template <>
+struct DynamicIntegerPointsKdTreeDecoderCompressionPolicy<6>
+ : public DynamicIntegerPointsKdTreeDecoderCompressionPolicy<5> {
+ static constexpr bool select_axis = true;
+};
+
+// Decodes a point cloud encoded by DynamicIntegerPointsKdTreeEncoder.
+template <int compression_level_t>
+class DynamicIntegerPointsKdTreeDecoder {
+ static_assert(compression_level_t >= 0, "Compression level must in [0..6].");
+ static_assert(compression_level_t <= 6, "Compression level must in [0..6].");
+ typedef DynamicIntegerPointsKdTreeDecoderCompressionPolicy<
+ compression_level_t>
+ Policy;
+
+ typedef typename Policy::NumbersDecoder NumbersDecoder;
+ typedef typename Policy::AxisDecoder AxisDecoder;
+ typedef typename Policy::HalfDecoder HalfDecoder;
+ typedef typename Policy::RemainingBitsDecoder RemainingBitsDecoder;
+ typedef std::vector<uint32_t> VectorUint32;
+
+ public:
+ explicit DynamicIntegerPointsKdTreeDecoder(uint32_t dimension)
+ : bit_length_(0),
+ num_points_(0),
+ num_decoded_points_(0),
+ dimension_(dimension),
+ p_(dimension, 0),
+ axes_(dimension, 0),
+ // Init the stack with the maximum depth of the tree.
+ // +1 for a second leaf.
+ base_stack_(32 * dimension + 1, VectorUint32(dimension, 0)),
+ levels_stack_(32 * dimension + 1, VectorUint32(dimension, 0)) {}
+
+ // Decodes a integer point cloud from |buffer|.
+ template <class OutputIteratorT>
+ bool DecodePoints(DecoderBuffer *buffer, OutputIteratorT &oit);
+ template <class OutputIteratorT>
+ bool DecodePoints(DecoderBuffer *buffer, OutputIteratorT &&oit);
+
+ const uint32_t dimension() const { return dimension_; }
+
+ private:
+ uint32_t GetAxis(uint32_t num_remaining_points, const VectorUint32 &levels,
+ uint32_t last_axis);
+
+ template <class OutputIteratorT>
+ bool DecodeInternal(uint32_t num_points, OutputIteratorT &oit);
+
+ void DecodeNumber(int nbits, uint32_t *value) {
+ numbers_decoder_.DecodeLeastSignificantBits32(nbits, value);
+ }
+
+ struct DecodingStatus {
+ DecodingStatus(uint32_t num_remaining_points_, uint32_t last_axis_,
+ uint32_t stack_pos_)
+ : num_remaining_points(num_remaining_points_),
+ last_axis(last_axis_),
+ stack_pos(stack_pos_) {}
+
+ uint32_t num_remaining_points;
+ uint32_t last_axis;
+ uint32_t stack_pos; // used to get base and levels
+ };
+
+ uint32_t bit_length_;
+ uint32_t num_points_;
+ uint32_t num_decoded_points_;
+ uint32_t dimension_;
+ NumbersDecoder numbers_decoder_;
+ RemainingBitsDecoder remaining_bits_decoder_;
+ AxisDecoder axis_decoder_;
+ HalfDecoder half_decoder_;
+ VectorUint32 p_;
+ VectorUint32 axes_;
+ std::vector<VectorUint32> base_stack_;
+ std::vector<VectorUint32> levels_stack_;
+};
+
+// Decodes a point cloud from |buffer|.
+template <int compression_level_t>
+template <class OutputIteratorT>
+bool DynamicIntegerPointsKdTreeDecoder<compression_level_t>::DecodePoints(
+ DecoderBuffer *buffer, OutputIteratorT &&oit) {
+ OutputIteratorT local = std::forward<OutputIteratorT>(oit);
+ return DecodePoints(buffer, local);
+}
+
+template <int compression_level_t>
+template <class OutputIteratorT>
+bool DynamicIntegerPointsKdTreeDecoder<compression_level_t>::DecodePoints(
+ DecoderBuffer *buffer, OutputIteratorT &oit) {
+ buffer->Decode(&bit_length_);
+ if (bit_length_ > 32)
+ return false;
+ buffer->Decode(&num_points_);
+ if (num_points_ == 0)
+ return true;
+ num_decoded_points_ = 0;
+
+ if (!numbers_decoder_.StartDecoding(buffer))
+ return false;
+ if (!remaining_bits_decoder_.StartDecoding(buffer))
+ return false;
+ if (!axis_decoder_.StartDecoding(buffer))
+ return false;
+ if (!half_decoder_.StartDecoding(buffer))
+ return false;
+
+ if (!DecodeInternal(num_points_, oit))
+ return false;
+
+ numbers_decoder_.EndDecoding();
+ remaining_bits_decoder_.EndDecoding();
+ axis_decoder_.EndDecoding();
+ half_decoder_.EndDecoding();
+
+ return true;
+}
+
+template <int compression_level_t>
+uint32_t DynamicIntegerPointsKdTreeDecoder<compression_level_t>::GetAxis(
+ uint32_t num_remaining_points, const VectorUint32 &levels,
+ uint32_t last_axis) {
+ if (!Policy::select_axis)
+ return DRACO_INCREMENT_MOD(last_axis, dimension_);
+
+ uint32_t best_axis = 0;
+ if (num_remaining_points < 64) {
+ for (uint32_t axis = 1; axis < dimension_; ++axis) {
+ if (levels[best_axis] > levels[axis]) {
+ best_axis = axis;
+ }
+ }
+ } else {
+ axis_decoder_.DecodeLeastSignificantBits32(4, &best_axis);
+ }
+
+ return best_axis;
+}
+
+template <int compression_level_t>
+template <class OutputIteratorT>
+bool DynamicIntegerPointsKdTreeDecoder<compression_level_t>::DecodeInternal(
+ uint32_t num_points, OutputIteratorT &oit) {
+ typedef DecodingStatus Status;
+ base_stack_[0] = VectorUint32(dimension_, 0);
+ levels_stack_[0] = VectorUint32(dimension_, 0);
+ DecodingStatus init_status(num_points, 0, 0);
+ std::stack<Status> status_stack;
+ status_stack.push(init_status);
+
+ // TODO(hemmer): use preallocated vector instead of stack.
+ while (!status_stack.empty()) {
+ const DecodingStatus status = status_stack.top();
+ status_stack.pop();
+
+ const uint32_t num_remaining_points = status.num_remaining_points;
+ const uint32_t last_axis = status.last_axis;
+ const uint32_t stack_pos = status.stack_pos;
+ const VectorUint32 &old_base = base_stack_[stack_pos];
+ const VectorUint32 &levels = levels_stack_[stack_pos];
+
+ if (num_remaining_points > num_points)
+ return false;
+
+ const uint32_t axis = GetAxis(num_remaining_points, levels, last_axis);
+ if (axis >= dimension_)
+ return false;
+
+ const uint32_t level = levels[axis];
+
+ // All axes have been fully subdivided, just output points.
+ if ((bit_length_ - level) == 0) {
+ for (uint32_t i = 0; i < num_remaining_points; i++) {
+ *oit = old_base;
+ ++oit;
+ ++num_decoded_points_;
+ }
+ continue;
+ }
+
+ DRACO_DCHECK_EQ(true, num_remaining_points != 0);
+
+ // Fast decoding of remaining bits if number of points is 1 or 2.
+ if (num_remaining_points <= 2) {
+ // TODO(hemmer): axes_ not necessary, remove would change bitstream!
+ axes_[0] = axis;
+ for (uint32_t i = 1; i < dimension_; i++) {
+ axes_[i] = DRACO_INCREMENT_MOD(axes_[i - 1], dimension_);
+ }
+ for (uint32_t i = 0; i < num_remaining_points; ++i) {
+ for (uint32_t j = 0; j < dimension_; j++) {
+ p_[axes_[j]] = 0;
+ const uint32_t num_remaining_bits = bit_length_ - levels[axes_[j]];
+ if (num_remaining_bits)
+ remaining_bits_decoder_.DecodeLeastSignificantBits32(
+ num_remaining_bits, &p_[axes_[j]]);
+ p_[axes_[j]] = old_base[axes_[j]] | p_[axes_[j]];
+ }
+ *oit = p_;
+ ++oit;
+ ++num_decoded_points_;
+ }
+ continue;
+ }
+
+ if (num_decoded_points_ > num_points_)
+ return false;
+
+ const int num_remaining_bits = bit_length_ - level;
+ const uint32_t modifier = 1 << (num_remaining_bits - 1);
+ base_stack_[stack_pos + 1] = old_base; // copy
+ base_stack_[stack_pos + 1][axis] += modifier; // new base
+
+ const int incoming_bits = MostSignificantBit(num_remaining_points);
+
+ uint32_t number = 0;
+ DecodeNumber(incoming_bits, &number);
+
+ uint32_t first_half = num_remaining_points / 2 - number;
+ uint32_t second_half = num_remaining_points - first_half;
+
+ if (first_half != second_half)
+ if (!half_decoder_.DecodeNextBit())
+ std::swap(first_half, second_half);
+
+ levels_stack_[stack_pos][axis] += 1;
+ levels_stack_[stack_pos + 1] = levels_stack_[stack_pos]; // copy
+ if (first_half)
+ status_stack.push(DecodingStatus(first_half, axis, stack_pos));
+ if (second_half)
+ status_stack.push(DecodingStatus(second_half, axis, stack_pos + 1));
+ }
+ return true;
+}
+
+extern template class DynamicIntegerPointsKdTreeDecoder<0>;
+extern template class DynamicIntegerPointsKdTreeDecoder<2>;
+extern template class DynamicIntegerPointsKdTreeDecoder<4>;
+extern template class DynamicIntegerPointsKdTreeDecoder<6>;
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_DYNAMIC_INTEGER_POINTS_KD_TREE_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.cc b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.cc
new file mode 100644
index 00000000000..e7abf52c4a2
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.cc
@@ -0,0 +1,26 @@
+// 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 "draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.h"
+
+#include "draco/compression/point_cloud/algorithms/point_cloud_types.h"
+
+namespace draco {
+
+template class DynamicIntegerPointsKdTreeEncoder<0>;
+template class DynamicIntegerPointsKdTreeEncoder<2>;
+template class DynamicIntegerPointsKdTreeEncoder<4>;
+template class DynamicIntegerPointsKdTreeEncoder<6>;
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.h
new file mode 100644
index 00000000000..47ac653c85f
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.h
@@ -0,0 +1,365 @@
+// 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_COMPRESSION_POINT_CLOUD_ALGORITHMS_DYNAMIC_INTEGER_POINTS_KD_TREE_ENCODER_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_DYNAMIC_INTEGER_POINTS_KD_TREE_ENCODER_H_
+
+#include <algorithm>
+#include <array>
+#include <memory>
+#include <stack>
+#include <vector>
+
+#include "draco/compression/bit_coders/adaptive_rans_bit_encoder.h"
+#include "draco/compression/bit_coders/direct_bit_encoder.h"
+#include "draco/compression/bit_coders/folded_integer_bit_encoder.h"
+#include "draco/compression/bit_coders/rans_bit_encoder.h"
+#include "draco/compression/point_cloud/algorithms/point_cloud_types.h"
+#include "draco/core/bit_utils.h"
+#include "draco/core/encoder_buffer.h"
+#include "draco/core/math_utils.h"
+
+namespace draco {
+
+// This policy class provides several configurations for the encoder that allow
+// to trade speed vs compression rate. Level 0 is fastest while 6 is the best
+// compression rate. The decoder must select the same level.
+template <int compression_level_t>
+struct DynamicIntegerPointsKdTreeEncoderCompressionPolicy
+ : public DynamicIntegerPointsKdTreeEncoderCompressionPolicy<
+ compression_level_t - 1> {};
+
+template <>
+struct DynamicIntegerPointsKdTreeEncoderCompressionPolicy<0> {
+ typedef DirectBitEncoder NumbersEncoder;
+ typedef DirectBitEncoder AxisEncoder;
+ typedef DirectBitEncoder HalfEncoder;
+ typedef DirectBitEncoder RemainingBitsEncoder;
+ static constexpr bool select_axis = false;
+};
+
+template <>
+struct DynamicIntegerPointsKdTreeEncoderCompressionPolicy<2>
+ : public DynamicIntegerPointsKdTreeEncoderCompressionPolicy<1> {
+ typedef RAnsBitEncoder NumbersEncoder;
+};
+
+template <>
+struct DynamicIntegerPointsKdTreeEncoderCompressionPolicy<4>
+ : public DynamicIntegerPointsKdTreeEncoderCompressionPolicy<3> {
+ typedef FoldedBit32Encoder<RAnsBitEncoder> NumbersEncoder;
+};
+
+template <>
+struct DynamicIntegerPointsKdTreeEncoderCompressionPolicy<6>
+ : public DynamicIntegerPointsKdTreeEncoderCompressionPolicy<5> {
+ static constexpr bool select_axis = true;
+};
+
+// This class encodes a given integer point cloud based on the point cloud
+// compression algorithm in:
+// Olivier Devillers and Pierre-Marie Gandoin
+// "Geometric compression for interactive transmission"
+//
+// In principle the algorithm keeps on splitting the point cloud in the middle
+// while alternating the axes. In 3D this results in an Octree like structure.
+// In each step we encode the number of points in the first half.
+// The algorithm does not preserve the order of points.
+//
+// However, the algorithm here differs from the original as follows:
+// The algorithm keeps on splitting the point cloud in the middle of the axis
+// that keeps the point cloud as clustered as possible, which gives a better
+// compression rate.
+// The number of points is encode by the deviation from the half of the points
+// in the smaller half of the two. This results in a better compression rate as
+// there are more leading zeros, which is then compressed better by the
+// arithmetic encoding.
+template <int compression_level_t>
+class DynamicIntegerPointsKdTreeEncoder {
+ static_assert(compression_level_t >= 0, "Compression level must in [0..6].");
+ static_assert(compression_level_t <= 6, "Compression level must in [0..6].");
+ typedef DynamicIntegerPointsKdTreeEncoderCompressionPolicy<
+ compression_level_t>
+ Policy;
+ typedef typename Policy::NumbersEncoder NumbersEncoder;
+ typedef typename Policy::AxisEncoder AxisEncoder;
+ typedef typename Policy::HalfEncoder HalfEncoder;
+ typedef typename Policy::RemainingBitsEncoder RemainingBitsEncoder;
+ typedef std::vector<uint32_t> VectorUint32;
+
+ public:
+ explicit DynamicIntegerPointsKdTreeEncoder(uint32_t dimension)
+ : bit_length_(0),
+ dimension_(dimension),
+ deviations_(dimension, 0),
+ num_remaining_bits_(dimension, 0),
+ axes_(dimension, 0),
+ base_stack_(32 * dimension + 1, VectorUint32(dimension, 0)),
+ levels_stack_(32 * dimension + 1, VectorUint32(dimension, 0)) {}
+
+ // Encodes an integer point cloud given by [begin,end) into buffer.
+ // |bit_length| gives the highest bit used for all coordinates.
+ template <class RandomAccessIteratorT>
+ bool EncodePoints(RandomAccessIteratorT begin, RandomAccessIteratorT end,
+ const uint32_t &bit_length, EncoderBuffer *buffer);
+
+ // Encodes an integer point cloud given by [begin,end) into buffer.
+ template <class RandomAccessIteratorT>
+ bool EncodePoints(RandomAccessIteratorT begin, RandomAccessIteratorT end,
+ EncoderBuffer *buffer) {
+ return EncodePoints(begin, end, 32, buffer);
+ }
+
+ const uint32_t dimension() const { return dimension_; }
+
+ private:
+ template <class RandomAccessIteratorT>
+ uint32_t GetAndEncodeAxis(RandomAccessIteratorT begin,
+ RandomAccessIteratorT end,
+ const VectorUint32 &old_base,
+ const VectorUint32 &levels, uint32_t last_axis);
+ template <class RandomAccessIteratorT>
+ void EncodeInternal(RandomAccessIteratorT begin, RandomAccessIteratorT end);
+
+ class Splitter {
+ public:
+ Splitter(uint32_t axis, uint32_t value) : axis_(axis), value_(value) {}
+ template <class PointT>
+ bool operator()(const PointT &a) const {
+ return a[axis_] < value_;
+ }
+
+ private:
+ const uint32_t axis_;
+ const uint32_t value_;
+ };
+
+ void EncodeNumber(int nbits, uint32_t value) {
+ numbers_encoder_.EncodeLeastSignificantBits32(nbits, value);
+ }
+
+ template <class RandomAccessIteratorT>
+ struct EncodingStatus {
+ EncodingStatus(RandomAccessIteratorT begin_, RandomAccessIteratorT end_,
+ uint32_t last_axis_, uint32_t stack_pos_)
+ : begin(begin_),
+ end(end_),
+ last_axis(last_axis_),
+ stack_pos(stack_pos_) {
+ num_remaining_points = static_cast<uint32_t>(end - begin);
+ }
+
+ RandomAccessIteratorT begin;
+ RandomAccessIteratorT end;
+ uint32_t last_axis;
+ uint32_t num_remaining_points;
+ uint32_t stack_pos; // used to get base and levels
+ };
+
+ uint32_t bit_length_;
+ uint32_t num_points_;
+ uint32_t dimension_;
+ NumbersEncoder numbers_encoder_;
+ RemainingBitsEncoder remaining_bits_encoder_;
+ AxisEncoder axis_encoder_;
+ HalfEncoder half_encoder_;
+ VectorUint32 deviations_;
+ VectorUint32 num_remaining_bits_;
+ VectorUint32 axes_;
+ std::vector<VectorUint32> base_stack_;
+ std::vector<VectorUint32> levels_stack_;
+};
+
+template <int compression_level_t>
+template <class RandomAccessIteratorT>
+bool DynamicIntegerPointsKdTreeEncoder<compression_level_t>::EncodePoints(
+ RandomAccessIteratorT begin, RandomAccessIteratorT end,
+ const uint32_t &bit_length, EncoderBuffer *buffer) {
+ bit_length_ = bit_length;
+ num_points_ = static_cast<uint32_t>(end - begin);
+
+ buffer->Encode(bit_length_);
+ buffer->Encode(num_points_);
+ if (num_points_ == 0)
+ return true;
+
+ numbers_encoder_.StartEncoding();
+ remaining_bits_encoder_.StartEncoding();
+ axis_encoder_.StartEncoding();
+ half_encoder_.StartEncoding();
+
+ EncodeInternal(begin, end);
+
+ numbers_encoder_.EndEncoding(buffer);
+ remaining_bits_encoder_.EndEncoding(buffer);
+ axis_encoder_.EndEncoding(buffer);
+ half_encoder_.EndEncoding(buffer);
+
+ return true;
+}
+template <int compression_level_t>
+template <class RandomAccessIteratorT>
+uint32_t
+DynamicIntegerPointsKdTreeEncoder<compression_level_t>::GetAndEncodeAxis(
+ RandomAccessIteratorT begin, RandomAccessIteratorT end,
+ const VectorUint32 &old_base, const VectorUint32 &levels,
+ uint32_t last_axis) {
+ if (!Policy::select_axis)
+ return DRACO_INCREMENT_MOD(last_axis, dimension_);
+
+ // For many points this function selects the axis that should be used
+ // for the split by keeping as many points as possible bundled.
+ // In the best case we do not split the point cloud at all.
+ // For lower number of points, we simply choose the axis that is refined the
+ // least so far.
+
+ DRACO_DCHECK_EQ(true, end - begin != 0);
+
+ uint32_t best_axis = 0;
+ if (end - begin < 64) {
+ for (uint32_t axis = 1; axis < dimension_; ++axis) {
+ if (levels[best_axis] > levels[axis]) {
+ best_axis = axis;
+ }
+ }
+ } else {
+ const uint32_t size = static_cast<uint32_t>(end - begin);
+ for (uint32_t i = 0; i < dimension_; i++) {
+ deviations_[i] = 0;
+ num_remaining_bits_[i] = bit_length_ - levels[i];
+ if (num_remaining_bits_[i] > 0) {
+ const uint32_t split =
+ old_base[i] + (1 << (num_remaining_bits_[i] - 1));
+ for (auto it = begin; it != end; ++it) {
+ deviations_[i] += ((*it)[i] < split);
+ }
+ deviations_[i] = std::max(size - deviations_[i], deviations_[i]);
+ }
+ }
+
+ uint32_t max_value = 0;
+ best_axis = 0;
+ for (uint32_t i = 0; i < dimension_; i++) {
+ // If axis can be subdivided.
+ if (num_remaining_bits_[i]) {
+ // Check if this is the better axis.
+ if (max_value < deviations_[i]) {
+ max_value = deviations_[i];
+ best_axis = i;
+ }
+ }
+ }
+ axis_encoder_.EncodeLeastSignificantBits32(4, best_axis);
+ }
+
+ return best_axis;
+}
+
+template <int compression_level_t>
+template <class RandomAccessIteratorT>
+void DynamicIntegerPointsKdTreeEncoder<compression_level_t>::EncodeInternal(
+ RandomAccessIteratorT begin, RandomAccessIteratorT end) {
+ typedef EncodingStatus<RandomAccessIteratorT> Status;
+
+ base_stack_[0] = VectorUint32(dimension_, 0);
+ levels_stack_[0] = VectorUint32(dimension_, 0);
+ Status init_status(begin, end, 0, 0);
+ std::stack<Status> status_stack;
+ status_stack.push(init_status);
+
+ // TODO(hemmer): use preallocated vector instead of stack.
+ while (!status_stack.empty()) {
+ Status status = status_stack.top();
+ status_stack.pop();
+
+ begin = status.begin;
+ end = status.end;
+ const uint32_t last_axis = status.last_axis;
+ const uint32_t stack_pos = status.stack_pos;
+ const VectorUint32 &old_base = base_stack_[stack_pos];
+ const VectorUint32 &levels = levels_stack_[stack_pos];
+
+ const uint32_t axis =
+ GetAndEncodeAxis(begin, end, old_base, levels, last_axis);
+ const uint32_t level = levels[axis];
+ const uint32_t num_remaining_points = static_cast<uint32_t>(end - begin);
+
+ // If this happens all axis are subdivided to the end.
+ if ((bit_length_ - level) == 0)
+ continue;
+
+ // Fast encoding of remaining bits if number of points is 1 or 2.
+ // Doing this also for 2 gives a slight additional speed up.
+ if (num_remaining_points <= 2) {
+ // TODO(hemmer): axes_ not necessary, remove would change bitstream!
+ axes_[0] = axis;
+ for (uint32_t i = 1; i < dimension_; i++) {
+ axes_[i] = DRACO_INCREMENT_MOD(axes_[i - 1], dimension_);
+ }
+ for (uint32_t i = 0; i < num_remaining_points; ++i) {
+ const auto &p = *(begin + i);
+ for (uint32_t j = 0; j < dimension_; j++) {
+ const uint32_t num_remaining_bits = bit_length_ - levels[axes_[j]];
+ if (num_remaining_bits) {
+ remaining_bits_encoder_.EncodeLeastSignificantBits32(
+ num_remaining_bits, p[axes_[j]]);
+ }
+ }
+ }
+ continue;
+ }
+
+ const uint32_t num_remaining_bits = bit_length_ - level;
+ const uint32_t modifier = 1 << (num_remaining_bits - 1);
+ base_stack_[stack_pos + 1] = old_base; // copy
+ base_stack_[stack_pos + 1][axis] += modifier;
+ const VectorUint32 &new_base = base_stack_[stack_pos + 1];
+
+ const RandomAccessIteratorT split =
+ std::partition(begin, end, Splitter(axis, new_base[axis]));
+
+ DRACO_DCHECK_EQ(true, (end - begin) > 0);
+
+ // Encode number of points in first and second half.
+ const int required_bits = MostSignificantBit(num_remaining_points);
+
+ const uint32_t first_half = static_cast<uint32_t>(split - begin);
+ const uint32_t second_half = static_cast<uint32_t>(end - split);
+ const bool left = first_half < second_half;
+
+ if (first_half != second_half)
+ half_encoder_.EncodeBit(left);
+
+ if (left) {
+ EncodeNumber(required_bits, num_remaining_points / 2 - first_half);
+ } else {
+ EncodeNumber(required_bits, num_remaining_points / 2 - second_half);
+ }
+
+ levels_stack_[stack_pos][axis] += 1;
+ levels_stack_[stack_pos + 1] = levels_stack_[stack_pos]; // copy
+ if (split != begin)
+ status_stack.push(Status(begin, split, axis, stack_pos));
+ if (split != end)
+ status_stack.push(Status(split, end, axis, stack_pos + 1));
+ }
+}
+extern template class DynamicIntegerPointsKdTreeEncoder<0>;
+extern template class DynamicIntegerPointsKdTreeEncoder<2>;
+extern template class DynamicIntegerPointsKdTreeEncoder<4>;
+extern template class DynamicIntegerPointsKdTreeEncoder<6>;
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_DYNAMIC_INTEGER_POINTS_KD_TREE_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_decoder.cc b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_decoder.cc
new file mode 100644
index 00000000000..98526c4e63d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_decoder.cc
@@ -0,0 +1,143 @@
+// 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 "draco/compression/point_cloud/algorithms/float_points_tree_decoder.h"
+
+#include <algorithm>
+
+#include "draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_decoder.h"
+#include "draco/compression/point_cloud/algorithms/quantize_points_3.h"
+#include "draco/core/math_utils.h"
+#include "draco/core/quantization_utils.h"
+
+namespace draco {
+
+struct Converter {
+ typedef std::vector<uint32_t> SourceType;
+ typedef Point3ui TargetType;
+ Point3ui operator()(const std::vector<uint32_t> &v) {
+ return Point3ui(v[0], v[1], v[2]);
+ }
+};
+
+// Output iterator that is used to decode values directly into the data buffer
+// of the modified PointAttribute.
+template <class OutputIterator, class Converter>
+class ConversionOutputIterator {
+ typedef ConversionOutputIterator<OutputIterator, Converter> Self;
+ typedef typename Converter::SourceType SourceType;
+ typedef typename Converter::TargetType TargetType;
+
+ public:
+ explicit ConversionOutputIterator(OutputIterator oit) : oit_(oit) {}
+
+ const Self &operator++() {
+ ++oit_;
+ return *this;
+ }
+ Self operator++(int) {
+ Self copy = *this;
+ ++oit_;
+ return copy;
+ }
+ Self &operator*() { return *this; }
+ const Self &operator=(const SourceType &source) {
+ *oit_ = Converter()(source);
+ return *this;
+ }
+
+ private:
+ OutputIterator oit_;
+};
+
+FloatPointsTreeDecoder::FloatPointsTreeDecoder()
+ : num_points_(0), compression_level_(0) {
+ qinfo_.quantization_bits = 0;
+ qinfo_.range = 0;
+}
+
+bool FloatPointsTreeDecoder::DecodePointCloudKdTreeInternal(
+ DecoderBuffer *buffer, std::vector<Point3ui> *qpoints) {
+ if (!buffer->Decode(&qinfo_.quantization_bits))
+ return false;
+ if (qinfo_.quantization_bits > 31)
+ return false;
+ if (!buffer->Decode(&qinfo_.range))
+ return false;
+ if (!buffer->Decode(&num_points_))
+ return false;
+ if (!buffer->Decode(&compression_level_))
+ return false;
+
+ // Only allow compression level in [0..6].
+ if (6 < compression_level_) {
+ LOGE("FloatPointsTreeDecoder: compression level %i not supported.\n",
+ compression_level_);
+ return false;
+ }
+
+ std::back_insert_iterator<std::vector<Point3ui>> oit_qpoints =
+ std::back_inserter(*qpoints);
+ ConversionOutputIterator<std::back_insert_iterator<std::vector<Point3ui>>,
+ Converter>
+ oit(oit_qpoints);
+ if (num_points_ > 0) {
+ qpoints->reserve(num_points_);
+ switch (compression_level_) {
+ case 0: {
+ DynamicIntegerPointsKdTreeDecoder<0> qpoints_decoder(3);
+ qpoints_decoder.DecodePoints(buffer, oit);
+ break;
+ }
+ case 1: {
+ DynamicIntegerPointsKdTreeDecoder<1> qpoints_decoder(3);
+ qpoints_decoder.DecodePoints(buffer, oit);
+ break;
+ }
+ case 2: {
+ DynamicIntegerPointsKdTreeDecoder<2> qpoints_decoder(3);
+ qpoints_decoder.DecodePoints(buffer, oit);
+ break;
+ }
+ case 3: {
+ DynamicIntegerPointsKdTreeDecoder<3> qpoints_decoder(3);
+ qpoints_decoder.DecodePoints(buffer, oit);
+ break;
+ }
+ case 4: {
+ DynamicIntegerPointsKdTreeDecoder<4> qpoints_decoder(3);
+ qpoints_decoder.DecodePoints(buffer, oit);
+ break;
+ }
+ case 5: {
+ DynamicIntegerPointsKdTreeDecoder<5> qpoints_decoder(3);
+ qpoints_decoder.DecodePoints(buffer, oit);
+ break;
+ }
+ case 6: {
+ DynamicIntegerPointsKdTreeDecoder<6> qpoints_decoder(3);
+ qpoints_decoder.DecodePoints(buffer, oit);
+ break;
+ }
+ default:
+ return false;
+ }
+ }
+
+ if (qpoints->size() != num_points_)
+ return false;
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_decoder.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_decoder.h
new file mode 100644
index 00000000000..80be0c9d373
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_decoder.h
@@ -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.
+//
+#ifndef DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_FLOAT_POINTS_TREE_DECODER_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_FLOAT_POINTS_TREE_DECODER_H_
+
+#include <memory>
+
+#include "draco/compression/config/compression_shared.h"
+#include "draco/compression/point_cloud/algorithms/point_cloud_compression_method.h"
+#include "draco/compression/point_cloud/algorithms/point_cloud_types.h"
+#include "draco/compression/point_cloud/algorithms/quantize_points_3.h"
+#include "draco/core/decoder_buffer.h"
+
+namespace draco {
+
+// Decodes a point cloud encoded by PointCloudTreeEncoder.
+class FloatPointsTreeDecoder {
+ public:
+ FloatPointsTreeDecoder();
+
+ // Decodes a point cloud from |buffer|.
+ template <class OutputIteratorT>
+ bool DecodePointCloud(DecoderBuffer *buffer, OutputIteratorT &out);
+ template <class OutputIteratorT>
+ bool DecodePointCloud(DecoderBuffer *buffer, OutputIteratorT &&out);
+ // Initializes a DecoderBuffer from |data|, and calls function above.
+ template <class OutputIteratorT>
+ bool DecodePointCloud(const char *data, size_t data_size,
+ OutputIteratorT out) {
+ if (data == 0 || data_size <= 0)
+ return false;
+
+ DecoderBuffer buffer;
+ buffer.Init(data, data_size);
+ buffer.set_bitstream_version(kDracoPointCloudBitstreamVersion);
+ return DecodePointCloud(&buffer, out);
+ }
+
+ uint32_t quantization_bits() const { return qinfo_.quantization_bits; }
+ uint32_t compression_level() const { return compression_level_; }
+ float range() const { return qinfo_.range; }
+ uint32_t num_points() const { return num_points_; }
+ uint32_t version() const { return version_; }
+ std::string identification_string() const {
+ if (method_ == KDTREE) {
+ return "FloatPointsTreeDecoder: IntegerPointsKDTreeDecoder";
+ } else {
+ return "FloatPointsTreeDecoder: Unsupported Method";
+ }
+ }
+
+ private:
+ bool DecodePointCloudKdTreeInternal(DecoderBuffer *buffer,
+ std::vector<Point3ui> *qpoints);
+
+ static const uint32_t version_ = 3;
+ QuantizationInfo qinfo_;
+ PointCloudCompressionMethod method_;
+ uint32_t num_points_;
+ uint32_t compression_level_;
+};
+
+template <class OutputIteratorT>
+bool FloatPointsTreeDecoder::DecodePointCloud(DecoderBuffer *buffer,
+ OutputIteratorT &&out) {
+ OutputIteratorT local = std::forward<OutputIteratorT>(out);
+ return DecodePointCloud(buffer, local);
+}
+
+template <class OutputIteratorT>
+bool FloatPointsTreeDecoder::DecodePointCloud(DecoderBuffer *buffer,
+ OutputIteratorT &out) {
+ std::vector<Point3ui> qpoints;
+
+ uint32_t decoded_version;
+ if (!buffer->Decode(&decoded_version))
+ return false;
+
+ if (decoded_version == 3) {
+ int8_t method_number;
+ if (!buffer->Decode(&method_number))
+ return false;
+
+ method_ = static_cast<PointCloudCompressionMethod>(method_number);
+
+ if (method_ == KDTREE) {
+ if (!DecodePointCloudKdTreeInternal(buffer, &qpoints))
+ return false;
+ } else { // Unsupported method.
+ fprintf(stderr, "Method not supported. \n");
+ return false;
+ }
+ } else if (decoded_version == 2) { // Version 2 only uses KDTREE method.
+ if (!DecodePointCloudKdTreeInternal(buffer, &qpoints))
+ return false;
+ } else { // Unsupported version.
+ fprintf(stderr, "Version not supported. \n");
+ return false;
+ }
+
+ DequantizePoints3(qpoints.begin(), qpoints.end(), qinfo_, out);
+ return true;
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_FLOAT_POINTS_TREE_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_encoder.cc b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_encoder.cc
new file mode 100644
index 00000000000..317430f2b8e
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_encoder.cc
@@ -0,0 +1,94 @@
+// 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 "draco/compression/point_cloud/algorithms/float_points_tree_encoder.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "draco/compression/point_cloud/algorithms/dynamic_integer_points_kd_tree_encoder.h"
+#include "draco/core/math_utils.h"
+
+namespace draco {
+
+const uint32_t FloatPointsTreeEncoder::version_ = 3;
+
+FloatPointsTreeEncoder::FloatPointsTreeEncoder(
+ PointCloudCompressionMethod method)
+ : method_(method), num_points_(0), compression_level_(6) {
+ qinfo_.quantization_bits = 16;
+ qinfo_.range = 0;
+}
+
+FloatPointsTreeEncoder::FloatPointsTreeEncoder(
+ PointCloudCompressionMethod method, uint32_t quantization_bits,
+ uint32_t compression_level)
+ : method_(method), num_points_(0), compression_level_(compression_level) {
+ DRACO_DCHECK_LE(compression_level_, 6);
+ qinfo_.quantization_bits = quantization_bits;
+ qinfo_.range = 0;
+}
+
+bool FloatPointsTreeEncoder::EncodePointCloudKdTreeInternal(
+ std::vector<Point3ui> *qpoints) {
+ DRACO_DCHECK_LE(compression_level_, 6);
+ switch (compression_level_) {
+ case 0: {
+ DynamicIntegerPointsKdTreeEncoder<0> qpoints_encoder(3);
+ qpoints_encoder.EncodePoints(qpoints->begin(), qpoints->end(),
+ qinfo_.quantization_bits + 1, &buffer_);
+ break;
+ }
+ case 1: {
+ DynamicIntegerPointsKdTreeEncoder<1> qpoints_encoder(3);
+ qpoints_encoder.EncodePoints(qpoints->begin(), qpoints->end(),
+ qinfo_.quantization_bits + 1, &buffer_);
+ break;
+ }
+ case 2: {
+ DynamicIntegerPointsKdTreeEncoder<2> qpoints_encoder(3);
+ qpoints_encoder.EncodePoints(qpoints->begin(), qpoints->end(),
+ qinfo_.quantization_bits + 1, &buffer_);
+ break;
+ }
+ case 3: {
+ DynamicIntegerPointsKdTreeEncoder<3> qpoints_encoder(3);
+ qpoints_encoder.EncodePoints(qpoints->begin(), qpoints->end(),
+ qinfo_.quantization_bits + 1, &buffer_);
+ break;
+ }
+ case 4: {
+ DynamicIntegerPointsKdTreeEncoder<4> qpoints_encoder(3);
+ qpoints_encoder.EncodePoints(qpoints->begin(), qpoints->end(),
+ qinfo_.quantization_bits + 1, &buffer_);
+ break;
+ }
+ case 5: {
+ DynamicIntegerPointsKdTreeEncoder<5> qpoints_encoder(3);
+ qpoints_encoder.EncodePoints(qpoints->begin(), qpoints->end(),
+ qinfo_.quantization_bits + 1, &buffer_);
+ break;
+ }
+ default: {
+ DynamicIntegerPointsKdTreeEncoder<6> qpoints_encoder(3);
+ qpoints_encoder.EncodePoints(qpoints->begin(), qpoints->end(),
+ qinfo_.quantization_bits + 1, &buffer_);
+ break;
+ }
+ }
+
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_encoder.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_encoder.h
new file mode 100644
index 00000000000..1fd807427f5
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/float_points_tree_encoder.h
@@ -0,0 +1,124 @@
+// 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_COMPRESSION_POINT_CLOUD_ALGORITHMS_FLOAT_POINTS_TREE_ENCODER_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_FLOAT_POINTS_TREE_ENCODER_H_
+
+#include <memory>
+#include <vector>
+
+#include "draco/compression/point_cloud/algorithms/point_cloud_compression_method.h"
+#include "draco/compression/point_cloud/algorithms/point_cloud_types.h"
+#include "draco/compression/point_cloud/algorithms/quantize_points_3.h"
+#include "draco/core/encoder_buffer.h"
+
+namespace draco {
+
+// This class encodes a given point cloud based on the point cloud compression
+// algorithm in:
+// Olivier Devillers and Pierre-Marie Gandoin
+// "Geometric compression for interactive transmission"
+//
+// In principle the algorithm keeps on splitting the point cloud in the middle
+// while alternating the axes. For 3D this results in an Octree like structure.
+// In each step we encode the number of points in the first half.
+// The algorithm uses quantization and does not preserve the order of points.
+//
+// However, the algorithm here differs from the original as follows:
+// The algorithm keeps on splitting the point cloud in the middle of the axis
+// that keeps the point cloud as clustered as possible, which gives a better
+// compression rate.
+// The number of points is encode by the deviation from the half of the points
+// in the smaller half of the two. This results in a better compression rate as
+// there are more leading zeros, which is then compressed better by the
+// arithmetic encoding.
+
+// TODO(hemmer): Remove class because it duplicates quantization code.
+class FloatPointsTreeEncoder {
+ public:
+ explicit FloatPointsTreeEncoder(PointCloudCompressionMethod method);
+ explicit FloatPointsTreeEncoder(PointCloudCompressionMethod method,
+ uint32_t quantization_bits,
+ uint32_t compression_level);
+
+ template <class InputIteratorT>
+ bool EncodePointCloud(InputIteratorT points_begin, InputIteratorT points_end);
+ EncoderBuffer *buffer() { return &buffer_; }
+
+ uint32_t version() const { return version_; }
+ uint32_t quantization_bits() const { return qinfo_.quantization_bits; }
+ uint32_t &quantization_bits() { return qinfo_.quantization_bits; }
+ uint32_t compression_level() const { return compression_level_; }
+ uint32_t &compression_level() { return compression_level_; }
+ float range() const { return qinfo_.range; }
+ uint32_t num_points() const { return num_points_; }
+ std::string identification_string() const {
+ if (method_ == KDTREE) {
+ return "FloatPointsTreeEncoder: IntegerPointsKDTreeEncoder";
+ } else {
+ return "FloatPointsTreeEncoder: Unsupported Method";
+ }
+ }
+
+ private:
+ void Clear() { buffer_.Clear(); }
+ bool EncodePointCloudKdTreeInternal(std::vector<Point3ui> *qpoints);
+
+ static const uint32_t version_;
+ QuantizationInfo qinfo_;
+ PointCloudCompressionMethod method_;
+ uint32_t num_points_;
+ EncoderBuffer buffer_;
+ uint32_t compression_level_;
+};
+
+template <class InputIteratorT>
+bool FloatPointsTreeEncoder::EncodePointCloud(InputIteratorT points_begin,
+ InputIteratorT points_end) {
+ Clear();
+
+ // Collect necessary data for encoding.
+ num_points_ = std::distance(points_begin, points_end);
+
+ // TODO(hemmer): Extend quantization tools to make this more automatic.
+ // Compute range of points for quantization
+ std::vector<Point3ui> qpoints;
+ qpoints.reserve(num_points_);
+ QuantizePoints3(points_begin, points_end, &qinfo_,
+ std::back_inserter(qpoints));
+
+ // Encode header.
+ buffer()->Encode(version_);
+ buffer()->Encode(static_cast<int8_t>(method_));
+ buffer()->Encode(qinfo_.quantization_bits);
+ buffer()->Encode(qinfo_.range);
+ buffer()->Encode(num_points_);
+
+ if (method_ == KDTREE)
+ buffer()->Encode(compression_level_);
+
+ if (num_points_ == 0)
+ return true;
+
+ if (method_ == KDTREE) {
+ return EncodePointCloudKdTreeInternal(&qpoints);
+ } else { // Unsupported method.
+ fprintf(stderr, "Method not supported. \n");
+ return false;
+ }
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_FLOAT_POINTS_TREE_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.cc b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.cc
new file mode 100644
index 00000000000..d0428a28e6a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.cc
@@ -0,0 +1,45 @@
+// 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 "draco/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.h"
+
+#include "draco/compression/point_cloud/algorithms/point_cloud_types.h"
+
+namespace draco {
+
+template class IntegerPointsKdTreeDecoder<Point3ui, 0>;
+template class IntegerPointsKdTreeDecoder<Point3ui, 1>;
+template class IntegerPointsKdTreeDecoder<Point3ui, 2>;
+template class IntegerPointsKdTreeDecoder<Point3ui, 3>;
+template class IntegerPointsKdTreeDecoder<Point3ui, 4>;
+template class IntegerPointsKdTreeDecoder<Point3ui, 5>;
+template class IntegerPointsKdTreeDecoder<Point3ui, 6>;
+template class IntegerPointsKdTreeDecoder<Point3ui, 7>;
+template class IntegerPointsKdTreeDecoder<Point3ui, 8>;
+template class IntegerPointsKdTreeDecoder<Point3ui, 9>;
+template class IntegerPointsKdTreeDecoder<Point3ui, 10>;
+
+template class IntegerPointsKdTreeDecoder<Point4ui, 0>;
+template class IntegerPointsKdTreeDecoder<Point4ui, 1>;
+template class IntegerPointsKdTreeDecoder<Point4ui, 2>;
+template class IntegerPointsKdTreeDecoder<Point4ui, 3>;
+template class IntegerPointsKdTreeDecoder<Point4ui, 4>;
+template class IntegerPointsKdTreeDecoder<Point4ui, 5>;
+template class IntegerPointsKdTreeDecoder<Point4ui, 6>;
+template class IntegerPointsKdTreeDecoder<Point4ui, 7>;
+template class IntegerPointsKdTreeDecoder<Point4ui, 8>;
+template class IntegerPointsKdTreeDecoder<Point4ui, 9>;
+template class IntegerPointsKdTreeDecoder<Point4ui, 10>;
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.h
new file mode 100644
index 00000000000..06ee718e0f5
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_decoder.h
@@ -0,0 +1,299 @@
+// 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.
+//
+// TODO(hemmer): Make this a wrapper using DynamicIntegerPointsKdTreeDecoder.
+//
+// See integer_points_kd_tree_encoder.h for documentation.
+
+#ifndef DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_INTEGER_POINTS_KD_TREE_DECODER_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_INTEGER_POINTS_KD_TREE_DECODER_H_
+
+#include <array>
+#include <memory>
+
+#include "draco/compression/bit_coders/adaptive_rans_bit_decoder.h"
+#include "draco/compression/bit_coders/direct_bit_decoder.h"
+#include "draco/compression/bit_coders/folded_integer_bit_decoder.h"
+#include "draco/compression/bit_coders/rans_bit_decoder.h"
+#include "draco/compression/point_cloud/algorithms/point_cloud_types.h"
+#include "draco/compression/point_cloud/algorithms/queuing_policy.h"
+#include "draco/core/bit_utils.h"
+#include "draco/core/decoder_buffer.h"
+#include "draco/core/math_utils.h"
+
+namespace draco {
+
+template <int compression_level_t>
+struct IntegerPointsKdTreeDecoderCompressionPolicy
+ : public IntegerPointsKdTreeDecoderCompressionPolicy<compression_level_t -
+ 1> {};
+
+template <>
+struct IntegerPointsKdTreeDecoderCompressionPolicy<0> {
+ typedef DirectBitDecoder NumbersDecoder;
+ typedef DirectBitDecoder AxisDecoder;
+ typedef DirectBitDecoder HalfDecoder;
+ typedef DirectBitDecoder RemainingBitsDecoder;
+ static constexpr bool select_axis = false;
+
+ template <class T>
+ using QueuingStrategy = Stack<T>;
+};
+
+template <>
+struct IntegerPointsKdTreeDecoderCompressionPolicy<2>
+ : public IntegerPointsKdTreeDecoderCompressionPolicy<1> {
+ typedef RAnsBitDecoder NumbersDecoder;
+};
+
+template <>
+struct IntegerPointsKdTreeDecoderCompressionPolicy<4>
+ : public IntegerPointsKdTreeDecoderCompressionPolicy<3> {
+ typedef FoldedBit32Decoder<RAnsBitDecoder> NumbersDecoder;
+};
+
+template <>
+struct IntegerPointsKdTreeDecoderCompressionPolicy<6>
+ : public IntegerPointsKdTreeDecoderCompressionPolicy<5> {
+ static constexpr bool select_axis = true;
+};
+
+template <>
+struct IntegerPointsKdTreeDecoderCompressionPolicy<8>
+ : public IntegerPointsKdTreeDecoderCompressionPolicy<7> {
+ typedef FoldedBit32Decoder<AdaptiveRAnsBitDecoder> NumbersDecoder;
+ template <class T>
+ using QueuingStrategy = Queue<T>;
+};
+
+template <>
+struct IntegerPointsKdTreeDecoderCompressionPolicy<10>
+ : public IntegerPointsKdTreeDecoderCompressionPolicy<9> {
+ template <class T>
+ using QueuingStrategy = PriorityQueue<T>;
+};
+
+// 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[].
+template <class PointDiT, int compression_level_t>
+class IntegerPointsKdTreeDecoder {
+ typedef IntegerPointsKdTreeDecoderCompressionPolicy<compression_level_t>
+ Policy;
+
+ typedef typename Policy::NumbersDecoder NumbersDecoder;
+ typedef typename Policy::AxisDecoder AxisDecoder;
+ typedef typename Policy::HalfDecoder HalfDecoder;
+ typedef typename Policy::RemainingBitsDecoder RemainingBitsDecoder;
+
+ public:
+ IntegerPointsKdTreeDecoder() : bit_length_(0) {}
+
+ // Decodes a integer point cloud from |buffer|.
+ template <class OutputIteratorT>
+ bool DecodePoints(DecoderBuffer *buffer, OutputIteratorT oit);
+
+ private:
+ // For the sake of readability of code, we decided to make this exception
+ // from the naming scheme.
+ static constexpr int D = PointTraits<PointDiT>::Dimension();
+
+ uint32_t GetAxis(uint32_t num_remaining_points, const PointDiT &base,
+ std::array<uint32_t, D> levels, uint32_t last_axis);
+
+ template <class OutputIteratorT>
+ void DecodeInternal(uint32_t num_remaining_points, PointDiT base,
+ std::array<uint32_t, D> levels, uint32_t last_axis,
+ OutputIteratorT oit);
+
+ void DecodeNumber(int nbits, uint32_t *value) {
+ numbers_decoder_.DecodeLeastSignificantBits32(nbits, value);
+ }
+
+ struct DecodingStatus {
+ DecodingStatus(
+ uint32_t num_remaining_points_, const PointDiT &old_base_,
+ std::array<uint32_t, PointTraits<PointDiT>::Dimension()> levels_,
+ uint32_t last_axis_)
+ : num_remaining_points(num_remaining_points_),
+ old_base(old_base_),
+ levels(levels_),
+ last_axis(last_axis_) {}
+
+ uint32_t num_remaining_points;
+ PointDiT old_base;
+ std::array<uint32_t, D> levels;
+ uint32_t last_axis;
+ friend bool operator<(const DecodingStatus &l, const DecodingStatus &r) {
+ return l.num_remaining_points < r.num_remaining_points;
+ }
+ };
+
+ uint32_t bit_length_;
+ uint32_t num_points_;
+ NumbersDecoder numbers_decoder_;
+ RemainingBitsDecoder remaining_bits_decoder_;
+ AxisDecoder axis_decoder_;
+ HalfDecoder half_decoder_;
+};
+
+// Decodes a point cloud from |buffer|.
+template <class PointDiT, int compression_level_t>
+template <class OutputIteratorT>
+bool IntegerPointsKdTreeDecoder<PointDiT, compression_level_t>::DecodePoints(
+ DecoderBuffer *buffer, OutputIteratorT oit) {
+ buffer->Decode(&bit_length_);
+ buffer->Decode(&num_points_);
+ if (num_points_ == 0)
+ return true;
+
+ if (!numbers_decoder_.StartDecoding(buffer))
+ return false;
+ if (!remaining_bits_decoder_.StartDecoding(buffer))
+ return false;
+ if (!axis_decoder_.StartDecoding(buffer))
+ return false;
+ if (!half_decoder_.StartDecoding(buffer))
+ return false;
+
+ DecodeInternal(num_points_, PointTraits<PointDiT>::Origin(),
+ PointTraits<PointDiT>::ZeroArray(), 0, oit);
+
+ numbers_decoder_.EndDecoding();
+ remaining_bits_decoder_.EndDecoding();
+ axis_decoder_.EndDecoding();
+ half_decoder_.EndDecoding();
+
+ return true;
+}
+
+template <class PointDiT, int compression_level_t>
+uint32_t IntegerPointsKdTreeDecoder<PointDiT, compression_level_t>::GetAxis(
+ uint32_t num_remaining_points, const PointDiT & /* base */,
+ std::array<uint32_t, D> levels, uint32_t last_axis) {
+ if (!Policy::select_axis)
+ return DRACO_INCREMENT_MOD(last_axis, D);
+
+ uint32_t best_axis = 0;
+ if (num_remaining_points < 64) {
+ for (uint32_t axis = 1; axis < D; ++axis) {
+ if (levels[best_axis] > levels[axis]) {
+ best_axis = axis;
+ }
+ }
+ } else {
+ axis_decoder_.DecodeLeastSignificantBits32(4, &best_axis);
+ }
+
+ return best_axis;
+}
+
+template <class PointDiT, int compression_level_t>
+template <class OutputIteratorT>
+void IntegerPointsKdTreeDecoder<PointDiT, compression_level_t>::DecodeInternal(
+ uint32_t num_remaining_points, PointDiT old_base,
+ std::array<uint32_t, D> levels, uint32_t last_axis, OutputIteratorT oit) {
+ DecodingStatus init_status(num_remaining_points, old_base, levels, last_axis);
+ typename Policy::template QueuingStrategy<DecodingStatus> status_q;
+ status_q.push(init_status);
+
+ while (!status_q.empty()) {
+ const DecodingStatus status = status_q.front();
+ status_q.pop();
+
+ num_remaining_points = status.num_remaining_points;
+ old_base = status.old_base;
+ levels = status.levels;
+ last_axis = status.last_axis;
+
+ const uint32_t axis =
+ GetAxis(num_remaining_points, old_base, levels, last_axis);
+
+ const uint32_t level = levels[axis];
+
+ // All axes have been fully subdivided, just output points.
+ if ((bit_length_ - level) == 0) {
+ for (int i = 0; i < static_cast<int>(num_remaining_points); i++) {
+ *oit++ = old_base;
+ }
+ continue;
+ }
+
+ DRACO_DCHECK_EQ(true, num_remaining_points != 0);
+ if (num_remaining_points <= 2) {
+ std::array<uint32_t, D> axes;
+ axes[0] = axis;
+ for (int i = 1; i < D; i++) {
+ axes[i] = DRACO_INCREMENT_MOD(axes[i - 1], D);
+ }
+
+ std::array<uint32_t, D> num_remaining_bits;
+ for (int i = 0; i < D; i++) {
+ num_remaining_bits[i] = bit_length_ - levels[axes[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<PointDiT>::Origin();
+ for (int j = 0; j < static_cast<int>(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;
+ }
+ continue;
+ }
+
+ const int num_remaining_bits = bit_length_ - level;
+ const uint32_t modifier = 1 << (num_remaining_bits - 1);
+ PointDiT new_base(old_base);
+ new_base[axis] += modifier;
+
+ const int incoming_bits = MostSignificantBit(num_remaining_points);
+
+ uint32_t number = 0;
+ DecodeNumber(incoming_bits, &number);
+
+ uint32_t first_half = num_remaining_points / 2 - number;
+ uint32_t second_half = num_remaining_points - first_half;
+
+ if (first_half != second_half)
+ if (!half_decoder_.DecodeNextBit())
+ std::swap(first_half, second_half);
+
+ levels[axis] += 1;
+ if (first_half)
+ status_q.push(DecodingStatus(first_half, old_base, levels, axis));
+ if (second_half)
+ status_q.push(DecodingStatus(second_half, new_base, levels, axis));
+ }
+}
+
+extern template class IntegerPointsKdTreeDecoder<Point3ui, 0>;
+extern template class IntegerPointsKdTreeDecoder<Point3ui, 1>;
+extern template class IntegerPointsKdTreeDecoder<Point3ui, 2>;
+extern template class IntegerPointsKdTreeDecoder<Point3ui, 3>;
+extern template class IntegerPointsKdTreeDecoder<Point3ui, 4>;
+extern template class IntegerPointsKdTreeDecoder<Point3ui, 5>;
+extern template class IntegerPointsKdTreeDecoder<Point3ui, 6>;
+extern template class IntegerPointsKdTreeDecoder<Point3ui, 7>;
+extern template class IntegerPointsKdTreeDecoder<Point3ui, 8>;
+extern template class IntegerPointsKdTreeDecoder<Point3ui, 9>;
+extern template class IntegerPointsKdTreeDecoder<Point3ui, 10>;
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_INTEGER_POINTS_KD_TREE_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.cc b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.cc
new file mode 100644
index 00000000000..ee10595003b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.cc
@@ -0,0 +1,45 @@
+// 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 "draco/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.h"
+
+#include "draco/compression/point_cloud/algorithms/point_cloud_types.h"
+
+namespace draco {
+
+template class IntegerPointsKdTreeEncoder<Point3ui, 0>;
+template class IntegerPointsKdTreeEncoder<Point3ui, 1>;
+template class IntegerPointsKdTreeEncoder<Point3ui, 2>;
+template class IntegerPointsKdTreeEncoder<Point3ui, 3>;
+template class IntegerPointsKdTreeEncoder<Point3ui, 4>;
+template class IntegerPointsKdTreeEncoder<Point3ui, 5>;
+template class IntegerPointsKdTreeEncoder<Point3ui, 6>;
+template class IntegerPointsKdTreeEncoder<Point3ui, 7>;
+template class IntegerPointsKdTreeEncoder<Point3ui, 8>;
+template class IntegerPointsKdTreeEncoder<Point3ui, 9>;
+template class IntegerPointsKdTreeEncoder<Point3ui, 10>;
+
+template class IntegerPointsKdTreeEncoder<Point4ui, 0>;
+template class IntegerPointsKdTreeEncoder<Point4ui, 1>;
+template class IntegerPointsKdTreeEncoder<Point4ui, 2>;
+template class IntegerPointsKdTreeEncoder<Point4ui, 3>;
+template class IntegerPointsKdTreeEncoder<Point4ui, 4>;
+template class IntegerPointsKdTreeEncoder<Point4ui, 5>;
+template class IntegerPointsKdTreeEncoder<Point4ui, 6>;
+template class IntegerPointsKdTreeEncoder<Point4ui, 7>;
+template class IntegerPointsKdTreeEncoder<Point4ui, 8>;
+template class IntegerPointsKdTreeEncoder<Point4ui, 9>;
+template class IntegerPointsKdTreeEncoder<Point4ui, 10>;
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.h
new file mode 100644
index 00000000000..9a3e8d76f8c
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/integer_points_kd_tree_encoder.h
@@ -0,0 +1,397 @@
+// 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.
+//
+// TODO(hemmer): Make this a wrapper using DynamicIntegerPointsKdTreeEncoder.
+#ifndef DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_INTEGER_POINTS_KD_TREE_ENCODER_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_INTEGER_POINTS_KD_TREE_ENCODER_H_
+
+#include <algorithm>
+#include <array>
+#include <memory>
+#include <vector>
+
+#include "draco/compression/bit_coders/adaptive_rans_bit_encoder.h"
+#include "draco/compression/bit_coders/direct_bit_encoder.h"
+#include "draco/compression/bit_coders/folded_integer_bit_encoder.h"
+#include "draco/compression/bit_coders/rans_bit_encoder.h"
+#include "draco/compression/point_cloud/algorithms/point_cloud_types.h"
+#include "draco/compression/point_cloud/algorithms/queuing_policy.h"
+#include "draco/core/bit_utils.h"
+#include "draco/core/encoder_buffer.h"
+#include "draco/core/math_utils.h"
+
+namespace draco {
+
+// This policy class provides several configurations for the encoder that allow
+// to trade speed vs compression rate. Level 0 is fastest while 10 is the best
+// compression rate. The decoder must select the same level.
+template <int compression_level_t>
+struct IntegerPointsKdTreeEncoderCompressionPolicy
+ : public IntegerPointsKdTreeEncoderCompressionPolicy<compression_level_t -
+ 1> {};
+
+template <>
+struct IntegerPointsKdTreeEncoderCompressionPolicy<0> {
+ typedef DirectBitEncoder NumbersEncoder;
+ typedef DirectBitEncoder AxisEncoder;
+ typedef DirectBitEncoder HalfEncoder;
+ typedef DirectBitEncoder RemainingBitsEncoder;
+ static constexpr bool select_axis = false;
+
+ template <class T>
+ using QueuingStrategy = Stack<T>;
+};
+
+template <>
+struct IntegerPointsKdTreeEncoderCompressionPolicy<2>
+ : public IntegerPointsKdTreeEncoderCompressionPolicy<1> {
+ typedef RAnsBitEncoder NumbersEncoder;
+};
+
+template <>
+struct IntegerPointsKdTreeEncoderCompressionPolicy<4>
+ : public IntegerPointsKdTreeEncoderCompressionPolicy<3> {
+ typedef FoldedBit32Encoder<RAnsBitEncoder> NumbersEncoder;
+};
+
+template <>
+struct IntegerPointsKdTreeEncoderCompressionPolicy<6>
+ : public IntegerPointsKdTreeEncoderCompressionPolicy<5> {
+ static constexpr bool select_axis = true;
+};
+
+template <>
+struct IntegerPointsKdTreeEncoderCompressionPolicy<8>
+ : public IntegerPointsKdTreeEncoderCompressionPolicy<7> {
+ typedef FoldedBit32Encoder<AdaptiveRAnsBitEncoder> NumbersEncoder;
+ template <class T>
+ using QueuingStrategy = Queue<T>;
+};
+
+template <>
+struct IntegerPointsKdTreeEncoderCompressionPolicy<10>
+ : public IntegerPointsKdTreeEncoderCompressionPolicy<9> {
+ template <class T>
+ using QueuingStrategy = PriorityQueue<T>;
+};
+
+// This class encodes a given integer point cloud based on the point cloud
+// compression algorithm in:
+// Olivier Devillers and Pierre-Marie Gandoin
+// "Geometric compression for interactive transmission"
+//
+// In principle the algorithm keeps on splitting the point cloud in the middle
+// while alternating the axes. In 3D this results in an Octree like structure.
+// In each step we encode the number of points in the first half.
+// The algorithm does not preserve the order of points.
+//
+// However, the algorithm here differs from the original as follows:
+// The algorithm keeps on splitting the point cloud in the middle of the axis
+// that keeps the point cloud as clustered as possible, which gives a better
+// compression rate.
+// The number of points is encode by the deviation from the half of the points
+// in the smaller half of the two. This results in a better compression rate as
+// there are more leading zeros, which is then compressed better by the
+// arithmetic encoding.
+//
+// |PointDiT| is a type representing a point with uint32_t coordinates.
+// must provide construction from three uint32_t and operator[].
+template <class PointDiT, int compression_level_t>
+class IntegerPointsKdTreeEncoder {
+ typedef IntegerPointsKdTreeEncoderCompressionPolicy<compression_level_t>
+ Policy;
+ typedef typename Policy::NumbersEncoder NumbersEncoder;
+ typedef typename Policy::AxisEncoder AxisEncoder;
+ typedef typename Policy::HalfEncoder HalfEncoder;
+ typedef typename Policy::RemainingBitsEncoder RemainingBitsEncoder;
+
+ public:
+ IntegerPointsKdTreeEncoder() : bit_length_(0) {}
+
+ // Encodes an integer point cloud given by [begin,end) into buffer.
+ // |bit_length| gives the highest bit used for all coordinates.
+ template <class RandomAccessIteratorT>
+ bool EncodePoints(RandomAccessIteratorT begin, RandomAccessIteratorT end,
+ const uint32_t &bit_length, EncoderBuffer *buffer);
+
+ // Encodes an integer point cloud given by [begin,end) into buffer.
+ template <class RandomAccessIteratorT>
+ bool EncodePoints(RandomAccessIteratorT begin, RandomAccessIteratorT end,
+ EncoderBuffer *buffer) {
+ return EncodePoints(begin, end, 32, buffer);
+ }
+
+ private:
+ // For the sack of readability of code, we decided to make this exception
+ // from the naming scheme.
+ static constexpr int D = PointTraits<PointDiT>::Dimension();
+ template <class RandomAccessIteratorT>
+ uint32_t GetAxis(RandomAccessIteratorT begin, RandomAccessIteratorT end,
+ const PointDiT &old_base, std::array<uint32_t, D> levels,
+ uint32_t last_axis);
+
+ template <class RandomAccessIteratorT>
+ void EncodeInternal(RandomAccessIteratorT begin, RandomAccessIteratorT end,
+ PointDiT old_base, std::array<uint32_t, D> levels,
+ uint32_t last_axis);
+
+ class Splitter {
+ public:
+ Splitter(int axis, uint32_t value) : axis_(axis), value_(value) {}
+ bool operator()(const PointDiT &a) { return a[axis_] < value_; }
+
+ private:
+ int axis_;
+ uint32_t value_;
+ };
+
+ void EncodeNumber(int nbits, uint32_t value) {
+ numbers_encoder_.EncodeLeastSignificantBits32(nbits, value);
+ }
+
+ template <class RandomAccessIteratorT>
+ struct EncodingStatus {
+ EncodingStatus(
+ RandomAccessIteratorT begin_, RandomAccessIteratorT end_,
+ const PointDiT &old_base_,
+ std::array<uint32_t, PointTraits<PointDiT>::Dimension()> levels_,
+ uint32_t last_axis_)
+ : begin(begin_),
+ end(end_),
+ old_base(old_base_),
+ levels(levels_),
+ last_axis(last_axis_) {
+ num_remaining_points = end - begin;
+ }
+
+ RandomAccessIteratorT begin;
+ RandomAccessIteratorT end;
+ PointDiT old_base;
+ std::array<uint32_t, D> levels;
+ uint32_t last_axis;
+ uint32_t num_remaining_points;
+ friend bool operator<(const EncodingStatus &l, const EncodingStatus &r) {
+ return l.num_remaining_points < r.num_remaining_points;
+ }
+ };
+
+ uint32_t bit_length_;
+ uint32_t num_points_;
+ NumbersEncoder numbers_encoder_;
+ RemainingBitsEncoder remaining_bits_encoder_;
+ AxisEncoder axis_encoder_;
+ HalfEncoder half_encoder_;
+};
+
+template <class PointDiT, int compression_level_t>
+template <class RandomAccessIteratorT>
+bool IntegerPointsKdTreeEncoder<PointDiT, compression_level_t>::EncodePoints(
+ RandomAccessIteratorT begin, RandomAccessIteratorT end,
+ const uint32_t &bit_length, EncoderBuffer *buffer) {
+ bit_length_ = bit_length;
+ num_points_ = end - begin;
+
+ buffer->Encode(bit_length_);
+ buffer->Encode(num_points_);
+ if (num_points_ == 0)
+ return true;
+
+ numbers_encoder_.StartEncoding();
+ remaining_bits_encoder_.StartEncoding();
+ axis_encoder_.StartEncoding();
+ half_encoder_.StartEncoding();
+
+ EncodeInternal(begin, end, PointTraits<PointDiT>::Origin(),
+ PointTraits<PointDiT>::ZeroArray(), 0);
+
+ numbers_encoder_.EndEncoding(buffer);
+ remaining_bits_encoder_.EndEncoding(buffer);
+ axis_encoder_.EndEncoding(buffer);
+ half_encoder_.EndEncoding(buffer);
+
+ return true;
+}
+template <class PointDiT, int compression_level_t>
+template <class RandomAccessIteratorT>
+uint32_t IntegerPointsKdTreeEncoder<PointDiT, compression_level_t>::GetAxis(
+ RandomAccessIteratorT begin, RandomAccessIteratorT end,
+ const PointDiT &old_base, std::array<uint32_t, D> levels,
+ uint32_t last_axis) {
+ if (!Policy::select_axis)
+ return DRACO_INCREMENT_MOD(last_axis, D);
+
+ // For many points this function selects the axis that should be used
+ // for the split by keeping as many points as possible bundled.
+ // In the best case we do not split the point cloud at all.
+ // For lower number of points, we simply choose the axis that is refined the
+ // least so far.
+
+ DRACO_DCHECK_EQ(true, end - begin != 0);
+
+ uint32_t best_axis = 0;
+ if (end - begin < 64) {
+ for (uint32_t axis = 1; axis < D; ++axis) {
+ if (levels[best_axis] > levels[axis]) {
+ best_axis = axis;
+ }
+ }
+ } else {
+ const uint32_t size = (end - begin);
+ std::array<uint32_t, D> num_remaining_bits =
+ PointTraits<PointDiT>::ZeroArray();
+ for (int i = 0; i < D; i++) {
+ num_remaining_bits[i] = bit_length_ - levels[i];
+ }
+ PointDiT split(old_base);
+
+ for (int i = 0; i < D; i++) {
+ if (num_remaining_bits[i])
+ split[i] += 1 << (num_remaining_bits[i] - 1);
+ }
+
+ std::array<uint32_t, D> deviations = PointTraits<PointDiT>::ZeroArray();
+ for (auto it = begin; it != end; ++it) {
+ for (int i = 0; i < D; i++) {
+ deviations[i] += ((*it)[i] < split[i]);
+ }
+ }
+ for (int i = 0; i < D; i++) {
+ deviations[i] = std::max(size - deviations[i], deviations[i]);
+ }
+
+ uint32_t max_value = 0;
+ best_axis = 0;
+ for (int i = 0; i < D; i++) {
+ // If axis can be subdivided.
+ if (num_remaining_bits[i]) {
+ // Check if this is the better axis.
+ if (max_value < deviations[i]) {
+ max_value = deviations[i];
+ best_axis = i;
+ }
+ }
+ }
+ axis_encoder_.EncodeLeastSignificantBits32(4, best_axis);
+ }
+
+ return best_axis;
+}
+
+template <class PointDiT, int compression_level_t>
+template <class RandomAccessIteratorT>
+void IntegerPointsKdTreeEncoder<PointDiT, compression_level_t>::EncodeInternal(
+ RandomAccessIteratorT begin, RandomAccessIteratorT end, PointDiT old_base,
+ std::array<uint32_t, D> levels, uint32_t last_axis) {
+ EncodingStatus<RandomAccessIteratorT> init_status(begin, end, old_base,
+ levels, last_axis);
+ typename Policy::template QueuingStrategy<
+ EncodingStatus<RandomAccessIteratorT>>
+ status_q;
+
+ status_q.push(init_status);
+
+ while (!status_q.empty()) {
+ EncodingStatus<RandomAccessIteratorT> status = status_q.front();
+ status_q.pop();
+
+ begin = status.begin;
+ end = status.end;
+ old_base = status.old_base;
+ levels = status.levels;
+ last_axis = status.last_axis;
+
+ const uint32_t axis = GetAxis(begin, end, old_base, levels, last_axis);
+ const uint32_t level = levels[axis];
+ const uint32_t num_remaining_points = end - begin;
+
+ // If this happens all axis are subdivided to the end.
+ if ((bit_length_ - level) == 0)
+ continue;
+
+ // Fast encoding of remaining bits if number of points is 1.
+ // Doing this also for 2 gives a slight additional speed up.
+ if (num_remaining_points <= 2) {
+ std::array<uint32_t, D> axes;
+ axes[0] = axis;
+ for (int i = 1; i < D; i++) {
+ axes[i] = DRACO_INCREMENT_MOD(axes[i - 1], D);
+ }
+
+ std::array<uint32_t, D> num_remaining_bits;
+ for (int i = 0; i < D; i++) {
+ num_remaining_bits[i] = bit_length_ - levels[axes[i]];
+ }
+
+ for (uint32_t i = 0; i < num_remaining_points; ++i) {
+ const PointDiT &p = *(begin + 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;
+ }
+
+ const uint32_t num_remaining_bits = bit_length_ - level;
+ const uint32_t modifier = 1 << (num_remaining_bits - 1);
+ PointDiT new_base(old_base);
+ new_base[axis] += modifier;
+ const RandomAccessIteratorT split =
+ std::partition(begin, end, Splitter(axis, new_base[axis]));
+
+ DRACO_DCHECK_EQ(true, (end - begin) > 0);
+
+ // Encode number of points in first and second half.
+ const int required_bits = MostSignificantBit(num_remaining_points);
+
+ const uint32_t first_half = split - begin;
+ const uint32_t second_half = end - split;
+ const bool left = first_half < second_half;
+
+ if (first_half != second_half)
+ half_encoder_.EncodeBit(left);
+
+ if (left) {
+ EncodeNumber(required_bits, num_remaining_points / 2 - first_half);
+ } else {
+ EncodeNumber(required_bits, num_remaining_points / 2 - second_half);
+ }
+
+ levels[axis] += 1;
+ if (split != begin)
+ status_q.push(EncodingStatus<RandomAccessIteratorT>(
+ begin, split, old_base, levels, axis));
+ if (split != end)
+ status_q.push(EncodingStatus<RandomAccessIteratorT>(split, end, new_base,
+ levels, axis));
+ }
+}
+
+extern template class IntegerPointsKdTreeEncoder<Point3ui, 0>;
+extern template class IntegerPointsKdTreeEncoder<Point3ui, 1>;
+extern template class IntegerPointsKdTreeEncoder<Point3ui, 2>;
+extern template class IntegerPointsKdTreeEncoder<Point3ui, 3>;
+extern template class IntegerPointsKdTreeEncoder<Point3ui, 4>;
+extern template class IntegerPointsKdTreeEncoder<Point3ui, 5>;
+extern template class IntegerPointsKdTreeEncoder<Point3ui, 6>;
+extern template class IntegerPointsKdTreeEncoder<Point3ui, 7>;
+extern template class IntegerPointsKdTreeEncoder<Point3ui, 8>;
+extern template class IntegerPointsKdTreeEncoder<Point3ui, 9>;
+extern template class IntegerPointsKdTreeEncoder<Point3ui, 10>;
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_INTEGER_POINTS_KD_TREE_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/point_cloud_compression_method.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/point_cloud_compression_method.h
new file mode 100644
index 00000000000..18307b57209
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/point_cloud_compression_method.h
@@ -0,0 +1,34 @@
+// 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_COMPRESSION_POINT_CLOUD_ALGORITHMS_POINT_CLOUD_COMPRESSION_METHOD_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_POINT_CLOUD_COMPRESSION_METHOD_H_
+
+namespace draco {
+
+// Enum indicating the used compression method, used by Encoder and Decoder.
+enum PointCloudCompressionMethod {
+ RESERVED_POINT_CLOUD_METHOD_0 = 0, // Reserved for internal use.
+ // Generalized version of Encoding using the Octree method by Olivier
+ // Devillers to d dimensions.
+ // "Progressive lossless compression of arbitrary simplicial complexes"
+ // http://dx.doi.org/10.1145/566570.566591
+ KDTREE = 1,
+ RESERVED_POINT_CLOUD_METHOD_2 = 2, // Reserved for internal use.
+ RESERVED_POINT_CLOUD_METHOD_3 = 0, // Reserved for internal use.
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_POINT_CLOUD_COMPRESSION_METHOD_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/point_cloud_types.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/point_cloud_types.h
new file mode 100644
index 00000000000..3ed1d13e054
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/point_cloud_types.h
@@ -0,0 +1,75 @@
+// 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_COMPRESSION_POINT_CLOUD_ALGORITHMS_POINT_CLOUD_TYPES_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_POINT_CLOUD_TYPES_H_
+
+#include <inttypes.h>
+#include <vector>
+
+#include "draco/core/vector_d.h"
+
+namespace draco {
+
+// Using Eigen as this is favored by project Cartographer.
+typedef Vector3f Point3f;
+typedef Vector4f Point4f;
+typedef Vector3ui Point3ui;
+typedef Vector4ui Point4ui;
+typedef Vector5ui Point5ui;
+typedef Vector6ui Point6ui;
+typedef Vector7ui Point7ui;
+
+typedef std::vector<Point3f> PointCloud3f;
+
+template <class PointDT>
+struct PointDLess;
+
+template <class CoeffT, int dimension_t>
+struct PointDLess<VectorD<CoeffT, dimension_t>> {
+ bool operator()(const VectorD<CoeffT, dimension_t> &a,
+ const VectorD<CoeffT, dimension_t> &b) const {
+ return a < b;
+ }
+};
+
+template <class PointDT>
+class PointTraits {};
+
+template <class CoordinateTypeT, int dimension_t>
+class PointTraits<VectorD<CoordinateTypeT, dimension_t>> {
+ public:
+ typedef VectorD<CoordinateTypeT, dimension_t> PointD;
+ typedef CoordinateTypeT CoordinateType;
+
+ static constexpr uint32_t Dimension() { return dimension_t; }
+ static PointD Origin() {
+ PointD origin;
+ for (uint32_t i = 0; i < dimension_t; i++) {
+ origin(i) = 0;
+ }
+ return origin;
+ }
+ static std::array<uint32_t, dimension_t> ZeroArray() {
+ std::array<uint32_t, dimension_t> zero;
+ for (uint32_t i = 0; i < dimension_t; i++) {
+ zero[i] = 0;
+ }
+ return zero;
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_POINT_CLOUD_TYPES_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/quantize_points_3.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/quantize_points_3.h
new file mode 100644
index 00000000000..771173032f6
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/quantize_points_3.h
@@ -0,0 +1,83 @@
+// 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_COMPRESSION_POINT_CLOUD_ALGORITHMS_QUANTIZE_POINTS_3_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_QUANTIZE_POINTS_3_H_
+
+#include <inttypes.h>
+#include "draco/compression/point_cloud/algorithms/point_cloud_types.h"
+#include "draco/core/quantization_utils.h"
+
+namespace draco {
+
+// TODO(hemmer): Make this a stable bounding box.
+struct QuantizationInfo {
+ uint32_t quantization_bits;
+ float range;
+};
+
+template <class PointIterator, class OutputIterator>
+OutputIterator QuantizePoints3(const PointIterator &begin,
+ const PointIterator &end, QuantizationInfo *info,
+ OutputIterator oit) {
+ DRACO_DCHECK_GE(info->quantization_bits, 0);
+
+ float max_range = 0;
+ for (auto it = begin; it != end; ++it) {
+ max_range = std::max(std::fabs((*it)[0]), max_range);
+ max_range = std::max(std::fabs((*it)[1]), max_range);
+ max_range = std::max(std::fabs((*it)[2]), max_range);
+ }
+
+ const uint32_t max_quantized_value((1 << info->quantization_bits) - 1);
+ Quantizer quantize;
+ quantize.Init(max_range, max_quantized_value);
+ info->range = max_range;
+
+ Point3ui qpoint;
+ for (auto it = begin; it != end; ++it) {
+ // Quantize and all positive.
+ qpoint[0] = quantize((*it)[0]) + max_quantized_value;
+ qpoint[1] = quantize((*it)[1]) + max_quantized_value;
+ qpoint[2] = quantize((*it)[2]) + max_quantized_value;
+ *oit++ = (qpoint);
+ }
+
+ return oit;
+}
+
+template <class QPointIterator, class OutputIterator>
+void DequantizePoints3(const QPointIterator &begin, const QPointIterator &end,
+ const QuantizationInfo &info, OutputIterator &oit) {
+ DRACO_DCHECK_GE(info.quantization_bits, 0);
+ DRACO_DCHECK_GE(info.range, 0);
+
+ const uint32_t quantization_bits = info.quantization_bits;
+ const float range = info.range;
+ const uint32_t max_quantized_value((1 << quantization_bits) - 1);
+ Dequantizer dequantize;
+ dequantize.Init(range, max_quantized_value);
+
+ for (auto it = begin; it != end; ++it) {
+ const float x = dequantize((*it)[0] - max_quantized_value);
+ const float y = dequantize((*it)[1] - max_quantized_value);
+ const float z = dequantize((*it)[2] - max_quantized_value);
+ *oit = Point3f(x, y, z);
+ ++oit;
+ }
+}
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_QUANTIZE_POINTS_3_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/queuing_policy.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/queuing_policy.h
new file mode 100644
index 00000000000..2db0ea213b8
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/algorithms/queuing_policy.h
@@ -0,0 +1,75 @@
+// 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.
+//
+// File defining a coherent interface for different queuing strategies.
+
+#ifndef DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_QUEUING_POLICY_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_QUEUING_POLICY_H_
+
+#include <queue>
+#include <stack>
+#include <utility>
+
+namespace draco {
+
+template <class T>
+class Queue {
+ public:
+ bool empty() const { return q_.empty(); }
+ typename std::queue<T>::size_type size() const { return q_.size(); }
+ void clear() { return q_.clear(); }
+ void push(const T &value) { q_.push(value); }
+ void push(T &&value) { q_.push(std::move(value)); }
+ void pop() { q_.pop(); }
+ typename std::queue<T>::const_reference front() const { return q_.front(); }
+
+ private:
+ std::queue<T> q_;
+};
+
+template <class T>
+class Stack {
+ public:
+ bool empty() const { return s_.empty(); }
+ typename std::stack<T>::size_type size() const { return s_.size(); }
+ void clear() { return s_.clear(); }
+ void push(const T &value) { s_.push(value); }
+ void push(T &&value) { s_.push(std::move(value)); }
+ void pop() { s_.pop(); }
+ typename std::stack<T>::const_reference front() const { return s_.top(); }
+
+ private:
+ std::stack<T> s_;
+};
+
+template <class T, class Compare = std::less<T> >
+class PriorityQueue {
+ typedef std::priority_queue<T, std::vector<T>, Compare> QType;
+
+ public:
+ bool empty() const { return s_.empty(); }
+ typename QType::size_type size() const { return s_.size(); }
+ void clear() { return s_.clear(); }
+ void push(const T &value) { s_.push(value); }
+ void push(T &&value) { s_.push(std::move(value)); }
+ void pop() { s_.pop(); }
+ typename QType::const_reference front() const { return s_.top(); }
+
+ private:
+ QType s_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_ALGORITHMS_QUEUING_POLICY_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_decoder.cc b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_decoder.cc
new file mode 100644
index 00000000000..5f3bff4bbc9
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_decoder.cc
@@ -0,0 +1,167 @@
+// 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 "draco/compression/point_cloud/point_cloud_decoder.h"
+
+#include "draco/metadata/metadata_decoder.h"
+
+namespace draco {
+
+PointCloudDecoder::PointCloudDecoder()
+ : point_cloud_(nullptr),
+ buffer_(nullptr),
+ version_major_(0),
+ version_minor_(0),
+ options_(nullptr) {}
+
+Status PointCloudDecoder::DecodeHeader(DecoderBuffer *buffer,
+ DracoHeader *out_header) {
+ constexpr char kIoErrorMsg[] = "Failed to parse Draco header.";
+ if (!buffer->Decode(out_header->draco_string, 5))
+ return Status(Status::IO_ERROR, kIoErrorMsg);
+ if (memcmp(out_header->draco_string, "DRACO", 5) != 0)
+ return Status(Status::ERROR, "Not a Draco file.");
+ if (!buffer->Decode(&(out_header->version_major)))
+ return Status(Status::IO_ERROR, kIoErrorMsg);
+ if (!buffer->Decode(&(out_header->version_minor)))
+ return Status(Status::IO_ERROR, kIoErrorMsg);
+ if (!buffer->Decode(&(out_header->encoder_type)))
+ return Status(Status::IO_ERROR, kIoErrorMsg);
+ if (!buffer->Decode(&(out_header->encoder_method)))
+ return Status(Status::IO_ERROR, kIoErrorMsg);
+ if (!buffer->Decode(&(out_header->flags)))
+ return Status(Status::IO_ERROR, kIoErrorMsg);
+ return OkStatus();
+}
+
+Status PointCloudDecoder::DecodeMetadata() {
+ std::unique_ptr<GeometryMetadata> metadata =
+ std::unique_ptr<GeometryMetadata>(new GeometryMetadata());
+ MetadataDecoder metadata_decoder;
+ if (!metadata_decoder.DecodeGeometryMetadata(buffer_, metadata.get()))
+ return Status(Status::ERROR, "Failed to decode metadata.");
+ point_cloud_->AddMetadata(std::move(metadata));
+ return OkStatus();
+}
+
+Status PointCloudDecoder::Decode(const DecoderOptions &options,
+ DecoderBuffer *in_buffer,
+ PointCloud *out_point_cloud) {
+ options_ = &options;
+ buffer_ = in_buffer;
+ point_cloud_ = out_point_cloud;
+ DracoHeader header;
+ DRACO_RETURN_IF_ERROR(DecodeHeader(buffer_, &header))
+ // Sanity check that we are really using the right decoder (mostly for cases
+ // where the Decode method was called manually outside of our main API.
+ if (header.encoder_type != GetGeometryType())
+ return Status(Status::ERROR,
+ "Using incompatible decoder for the input geometry.");
+ // TODO(ostava): We should check the method as well, but currently decoders
+ // don't expose the decoding method id.
+ version_major_ = header.version_major;
+ version_minor_ = header.version_minor;
+
+ const uint8_t max_supported_major_version =
+ header.encoder_type == POINT_CLOUD ? kDracoPointCloudBitstreamVersionMajor
+ : kDracoMeshBitstreamVersionMajor;
+ const uint8_t max_supported_minor_version =
+ header.encoder_type == POINT_CLOUD ? kDracoPointCloudBitstreamVersionMinor
+ : kDracoMeshBitstreamVersionMinor;
+ // Check for version compatibility.
+ if (version_major_ < 1 || version_major_ > max_supported_major_version)
+ return Status(Status::UNKNOWN_VERSION, "Unknown major version.");
+ if (version_major_ == max_supported_major_version &&
+ version_minor_ > max_supported_minor_version)
+ return Status(Status::UNKNOWN_VERSION, "Unknown minor version.");
+ buffer_->set_bitstream_version(
+ DRACO_BITSTREAM_VERSION(version_major_, version_minor_));
+
+ if (bitstream_version() >= DRACO_BITSTREAM_VERSION(1, 3) &&
+ (header.flags & METADATA_FLAG_MASK)) {
+ DRACO_RETURN_IF_ERROR(DecodeMetadata())
+ }
+ if (!InitializeDecoder())
+ return Status(Status::ERROR, "Failed to initialize the decoder.");
+ if (!DecodeGeometryData())
+ return Status(Status::ERROR, "Failed to decode geometry data.");
+ if (!DecodePointAttributes())
+ return Status(Status::ERROR, "Failed to decode point attributes.");
+ return OkStatus();
+}
+
+bool PointCloudDecoder::DecodePointAttributes() {
+ uint8_t num_attributes_decoders;
+ if (!buffer_->Decode(&num_attributes_decoders))
+ return false;
+ // Create all attribute decoders. This is implementation specific and the
+ // derived classes can use any data encoded in the
+ // PointCloudEncoder::EncodeAttributesEncoderIdentifier() call.
+ for (int i = 0; i < num_attributes_decoders; ++i) {
+ if (!CreateAttributesDecoder(i))
+ return false;
+ }
+
+ // Initialize all attributes decoders. No data is decoded here.
+ for (auto &att_dec : attributes_decoders_) {
+ if (!att_dec->Init(this, point_cloud_))
+ return false;
+ }
+
+ // Decode any data needed by the attribute decoders.
+ for (int i = 0; i < num_attributes_decoders; ++i) {
+ if (!attributes_decoders_[i]->DecodeAttributesDecoderData(buffer_))
+ return false;
+ }
+
+ // Create map between attribute and decoder ids.
+ for (int i = 0; i < num_attributes_decoders; ++i) {
+ const int32_t num_attributes = attributes_decoders_[i]->GetNumAttributes();
+ for (int j = 0; j < num_attributes; ++j) {
+ int att_id = attributes_decoders_[i]->GetAttributeId(j);
+ if (att_id >= attribute_to_decoder_map_.size()) {
+ attribute_to_decoder_map_.resize(att_id + 1);
+ }
+ attribute_to_decoder_map_[att_id] = i;
+ }
+ }
+
+ // Decode the actual attributes using the created attribute decoders.
+ if (!DecodeAllAttributes())
+ return false;
+
+ if (!OnAttributesDecoded())
+ return false;
+ return true;
+}
+
+bool PointCloudDecoder::DecodeAllAttributes() {
+ for (auto &att_dec : attributes_decoders_) {
+ if (!att_dec->DecodeAttributes(buffer_))
+ return false;
+ }
+ return true;
+}
+
+const PointAttribute *PointCloudDecoder::GetPortableAttribute(
+ int32_t parent_att_id) {
+ if (parent_att_id < 0 || parent_att_id >= point_cloud_->num_attributes())
+ return nullptr;
+ const int32_t parent_att_decoder_id =
+ attribute_to_decoder_map_[parent_att_id];
+ return attributes_decoders_[parent_att_decoder_id]->GetPortableAttribute(
+ parent_att_id);
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_decoder.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_decoder.h
new file mode 100644
index 00000000000..abcb5e068f7
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_decoder.h
@@ -0,0 +1,116 @@
+// 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_COMPRESSION_POINT_CLOUD_POINT_CLOUD_DECODER_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_DECODER_H_
+
+#include "draco/compression/attributes/attributes_decoder_interface.h"
+#include "draco/compression/config/compression_shared.h"
+#include "draco/compression/config/decoder_options.h"
+#include "draco/core/status.h"
+#include "draco/point_cloud/point_cloud.h"
+
+namespace draco {
+
+// Abstract base class for all point cloud and mesh decoders. It provides a
+// basic functionality that is shared between different decoders.
+class PointCloudDecoder {
+ public:
+ PointCloudDecoder();
+ virtual ~PointCloudDecoder() = default;
+
+ virtual EncodedGeometryType GetGeometryType() const { return POINT_CLOUD; }
+
+ // Decodes a Draco header int other provided |out_header|.
+ // Returns false on error.
+ static Status DecodeHeader(DecoderBuffer *buffer, DracoHeader *out_header);
+
+ // The main entry point for point cloud decoding.
+ Status Decode(const DecoderOptions &options, DecoderBuffer *in_buffer,
+ PointCloud *out_point_cloud);
+
+ bool SetAttributesDecoder(
+ int att_decoder_id, std::unique_ptr<AttributesDecoderInterface> decoder) {
+ if (att_decoder_id < 0)
+ return false;
+ if (att_decoder_id >= static_cast<int>(attributes_decoders_.size()))
+ attributes_decoders_.resize(att_decoder_id + 1);
+ attributes_decoders_[att_decoder_id] = std::move(decoder);
+ return true;
+ }
+
+ // Returns an attribute containing decoded data in their portable form that
+ // is guaranteed to be the same for both encoder and decoder. I.e., it returns
+ // an attribute before it was transformed back into its final form which may
+ // be slightly different (non-portable) across platforms. For example, for
+ // attributes encoded with quantization, this method returns an attribute
+ // that contains the quantized values (before the dequantization step).
+ const PointAttribute *GetPortableAttribute(int32_t point_attribute_id);
+
+ uint16_t bitstream_version() const {
+ return DRACO_BITSTREAM_VERSION(version_major_, version_minor_);
+ }
+
+ const AttributesDecoderInterface *attributes_decoder(int dec_id) {
+ return attributes_decoders_[dec_id].get();
+ }
+ int32_t num_attributes_decoders() const {
+ return static_cast<int32_t>(attributes_decoders_.size());
+ }
+
+ // Get a mutable pointer to the decoded point cloud. This is intended to be
+ // used mostly by other decoder subsystems.
+ PointCloud *point_cloud() { return point_cloud_; }
+ const PointCloud *point_cloud() const { return point_cloud_; }
+
+ DecoderBuffer *buffer() { return buffer_; }
+ const DecoderOptions *options() const { return options_; }
+
+ protected:
+ // Can be implemented by derived classes to perform any custom initialization
+ // of the decoder. Called in the Decode() method.
+ virtual bool InitializeDecoder() { return true; }
+
+ // Creates an attribute decoder.
+ virtual bool CreateAttributesDecoder(int32_t att_decoder_id) = 0;
+ virtual bool DecodeGeometryData() { return true; }
+ virtual bool DecodePointAttributes();
+
+ virtual bool DecodeAllAttributes();
+ virtual bool OnAttributesDecoded() { return true; }
+
+ Status DecodeMetadata();
+
+ private:
+ // Point cloud that is being filled in by the decoder.
+ PointCloud *point_cloud_;
+
+ std::vector<std::unique_ptr<AttributesDecoderInterface>> attributes_decoders_;
+
+ // Map between attribute id and decoder id.
+ std::vector<int32_t> attribute_to_decoder_map_;
+
+ // Input buffer holding the encoded data.
+ DecoderBuffer *buffer_;
+
+ // Bit-stream version of the encoder that encoded the input data.
+ uint8_t version_major_;
+ uint8_t version_minor_;
+
+ const DecoderOptions *options_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_encoder.cc b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_encoder.cc
new file mode 100644
index 00000000000..edf0e4ab20a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_encoder.cc
@@ -0,0 +1,284 @@
+// 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 "draco/compression/point_cloud/point_cloud_encoder.h"
+
+#include "draco/metadata/metadata_encoder.h"
+
+namespace draco {
+
+PointCloudEncoder::PointCloudEncoder()
+ : point_cloud_(nullptr), buffer_(nullptr), num_encoded_points_(0) {}
+
+void PointCloudEncoder::SetPointCloud(const PointCloud &pc) {
+ point_cloud_ = &pc;
+}
+
+Status PointCloudEncoder::Encode(const EncoderOptions &options,
+ EncoderBuffer *out_buffer) {
+ options_ = &options;
+ buffer_ = out_buffer;
+
+ // Cleanup from previous runs.
+ attributes_encoders_.clear();
+ attribute_to_encoder_map_.clear();
+ attributes_encoder_ids_order_.clear();
+
+ if (!point_cloud_)
+ return Status(Status::ERROR, "Invalid input geometry.");
+ DRACO_RETURN_IF_ERROR(EncodeHeader())
+ DRACO_RETURN_IF_ERROR(EncodeMetadata())
+ if (!InitializeEncoder())
+ return Status(Status::ERROR, "Failed to initialize encoder.");
+ if (!EncodeEncoderData())
+ return Status(Status::ERROR, "Failed to encode internal data.");
+ if (!EncodeGeometryData())
+ return Status(Status::ERROR, "Failed to encode geometry data.");
+ if (!EncodePointAttributes())
+ return Status(Status::ERROR, "Failed to encode point attributes.");
+ if (options.GetGlobalBool("store_number_of_encoded_points", false))
+ ComputeNumberOfEncodedPoints();
+ return OkStatus();
+}
+
+Status PointCloudEncoder::EncodeHeader() {
+ // Encode the header according to our v1 specification.
+ // Five bytes for Draco format.
+ buffer_->Encode("DRACO", 5);
+ // Version (major, minor).
+ const uint8_t encoder_type = GetGeometryType();
+ const uint8_t version_major = encoder_type == POINT_CLOUD
+ ? kDracoPointCloudBitstreamVersionMajor
+ : kDracoMeshBitstreamVersionMajor;
+ const uint8_t version_minor = encoder_type == POINT_CLOUD
+ ? kDracoPointCloudBitstreamVersionMinor
+ : kDracoMeshBitstreamVersionMinor;
+ buffer_->Encode(version_major);
+ buffer_->Encode(version_minor);
+ // Type of the encoder (point cloud, mesh, ...).
+ buffer_->Encode(encoder_type);
+ // Unique identifier for the selected encoding method (edgebreaker, etc...).
+ buffer_->Encode(GetEncodingMethod());
+ // Reserved for flags.
+ uint16_t flags = 0;
+ // First bit of |flags| is reserved for metadata.
+ if (point_cloud_->GetMetadata()) {
+ flags |= METADATA_FLAG_MASK;
+ }
+ buffer_->Encode(flags);
+ return OkStatus();
+}
+
+Status PointCloudEncoder::EncodeMetadata() {
+ if (!point_cloud_->GetMetadata()) {
+ return OkStatus();
+ }
+ MetadataEncoder metadata_encoder;
+ if (!metadata_encoder.EncodeGeometryMetadata(buffer_,
+ point_cloud_->GetMetadata())) {
+ return Status(Status::ERROR, "Failed to encode metadata.");
+ }
+ return OkStatus();
+}
+
+bool PointCloudEncoder::EncodePointAttributes() {
+ if (!GenerateAttributesEncoders())
+ return false;
+
+ // Encode the number of attribute encoders.
+ buffer_->Encode(static_cast<uint8_t>(attributes_encoders_.size()));
+
+ // Initialize all the encoders (this is used for example to init attribute
+ // dependencies, no data is encoded in this step).
+ for (auto &att_enc : attributes_encoders_) {
+ if (!att_enc->Init(this, point_cloud_))
+ return false;
+ }
+
+ // Rearrange attributes to respect dependencies between individual attributes.
+ if (!RearrangeAttributesEncoders())
+ return false;
+
+ // Encode any data that is necessary to create the corresponding attribute
+ // decoder.
+ for (int att_encoder_id : attributes_encoder_ids_order_) {
+ if (!EncodeAttributesEncoderIdentifier(att_encoder_id))
+ return false;
+ }
+
+ // Also encode any attribute encoder data (such as the info about encoded
+ // attributes).
+ for (int att_encoder_id : attributes_encoder_ids_order_) {
+ if (!attributes_encoders_[att_encoder_id]->EncodeAttributesEncoderData(
+ buffer_))
+ return false;
+ }
+
+ // Lastly encode all the attributes using the provided attribute encoders.
+ if (!EncodeAllAttributes())
+ return false;
+ return true;
+}
+
+bool PointCloudEncoder::GenerateAttributesEncoders() {
+ for (int i = 0; i < point_cloud_->num_attributes(); ++i) {
+ if (!GenerateAttributesEncoder(i))
+ return false;
+ }
+ attribute_to_encoder_map_.resize(point_cloud_->num_attributes());
+ for (uint32_t i = 0; i < attributes_encoders_.size(); ++i) {
+ for (uint32_t j = 0; j < attributes_encoders_[i]->num_attributes(); ++j) {
+ attribute_to_encoder_map_[attributes_encoders_[i]->GetAttributeId(j)] = i;
+ }
+ }
+ return true;
+}
+
+bool PointCloudEncoder::EncodeAllAttributes() {
+ for (int att_encoder_id : attributes_encoder_ids_order_) {
+ if (!attributes_encoders_[att_encoder_id]->EncodeAttributes(buffer_))
+ return false;
+ }
+ return true;
+}
+
+bool PointCloudEncoder::MarkParentAttribute(int32_t parent_att_id) {
+ if (parent_att_id < 0 || parent_att_id >= point_cloud_->num_attributes())
+ return false;
+ const int32_t parent_att_encoder_id =
+ attribute_to_encoder_map_[parent_att_id];
+ if (!attributes_encoders_[parent_att_encoder_id]->MarkParentAttribute(
+ parent_att_id))
+ return false;
+ return true;
+}
+
+const PointAttribute *PointCloudEncoder::GetPortableAttribute(
+ int32_t parent_att_id) {
+ if (parent_att_id < 0 || parent_att_id >= point_cloud_->num_attributes())
+ return nullptr;
+ const int32_t parent_att_encoder_id =
+ attribute_to_encoder_map_[parent_att_id];
+ return attributes_encoders_[parent_att_encoder_id]->GetPortableAttribute(
+ parent_att_id);
+}
+
+bool PointCloudEncoder::RearrangeAttributesEncoders() {
+ // Find the encoding order of the attribute encoders that is determined by
+ // the parent dependencies between individual encoders. Instead of traversing
+ // a graph we encode the attributes in multiple iterations where encoding of
+ // attributes that depend on other attributes may get postponed until the
+ // parent attributes are processed.
+ // This is simpler to implement than graph traversal and it automatically
+ // detects any cycles in the dependency graph.
+ // TODO(ostava): Current implementation needs to encode all attributes of a
+ // single encoder to be encoded in a single "chunk", therefore we need to sort
+ // attribute encoders before we sort individual attributes. This requirement
+ // can be lifted for encoders that can encode individual attributes separately
+ // but it will require changes in the current API.
+ attributes_encoder_ids_order_.resize(attributes_encoders_.size());
+ std::vector<bool> is_encoder_processed(attributes_encoders_.size(), false);
+ 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 (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.
+ bool can_be_processed = true;
+ for (uint32_t p = 0; p < attributes_encoders_[i]->num_attributes(); ++p) {
+ const int32_t att_id = attributes_encoders_[i]->GetAttributeId(p);
+ for (int ap = 0;
+ ap < attributes_encoders_[i]->NumParentAttributes(att_id); ++ap) {
+ 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];
+ if (parent_att_id != i && !is_encoder_processed[parent_encoder_id]) {
+ can_be_processed = false;
+ break;
+ }
+ }
+ }
+ if (!can_be_processed)
+ continue; // Try to process the encoder in the next iteration.
+ // Encoder can be processed. Update the encoding order.
+ attributes_encoder_ids_order_[num_processed_encoders++] = i;
+ is_encoder_processed[i] = true;
+ encoder_processed = true;
+ }
+ if (!encoder_processed &&
+ num_processed_encoders < attributes_encoders_.size()) {
+ // No encoder was processed but there are still some remaining unprocessed
+ // encoders.
+ return false;
+ }
+ }
+
+ // Now for every encoder, reorder the attributes to satisfy their
+ // dependencies (an attribute may still depend on other attributes within an
+ // encoder).
+ std::vector<int32_t> attribute_encoding_order;
+ std::vector<bool> is_attribute_processed(point_cloud_->num_attributes(),
+ false);
+ int num_processed_attributes;
+ 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();
+ if (num_encoder_attributes < 2)
+ continue; // No need to resolve dependencies for a single attribute.
+ num_processed_attributes = 0;
+ attribute_encoding_order.resize(num_encoder_attributes);
+ while (num_processed_attributes < num_encoder_attributes) {
+ // Flagged when any of the attributes get processed.
+ bool attribute_processed = false;
+ for (int i = 0; i < num_encoder_attributes; ++i) {
+ const int32_t att_id = attributes_encoders_[ae]->GetAttributeId(i);
+ if (is_attribute_processed[i])
+ continue; // Attribute already processed.
+ // Check if all parent attributes are already processed.
+ bool can_be_processed = true;
+ for (int p = 0;
+ p < attributes_encoders_[ae]->NumParentAttributes(att_id); ++p) {
+ const int32_t parent_att_id =
+ attributes_encoders_[ae]->GetParentAttributeId(att_id, p);
+ if (!is_attribute_processed[parent_att_id]) {
+ can_be_processed = false;
+ break;
+ }
+ }
+ if (!can_be_processed)
+ continue; // Try to process the attribute in the next iteration.
+ // Attribute can be processed. Update the encoding order.
+ attribute_encoding_order[num_processed_attributes++] = i;
+ is_attribute_processed[i] = true;
+ attribute_processed = true;
+ }
+ if (!attribute_processed &&
+ num_processed_attributes < num_encoder_attributes) {
+ // No attribute was processed but there are still some remaining
+ // unprocessed attributes.
+ return false;
+ }
+ }
+ // Update the order of the attributes within the encoder.
+ attributes_encoders_[ae]->SetAttributeIds(attribute_encoding_order);
+ }
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_encoder.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_encoder.h
new file mode 100644
index 00000000000..b15d1401a57
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_encoder.h
@@ -0,0 +1,158 @@
+// 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_COMPRESSION_POINT_CLOUD_POINT_CLOUD_ENCODER_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_ENCODER_H_
+
+#include "draco/compression/attributes/attributes_encoder.h"
+#include "draco/compression/config/compression_shared.h"
+#include "draco/compression/config/encoder_options.h"
+#include "draco/core/encoder_buffer.h"
+#include "draco/core/status.h"
+#include "draco/point_cloud/point_cloud.h"
+
+namespace draco {
+
+// Abstract base class for all point cloud and mesh encoders. It provides a
+// basic functionality that's shared between different encoders.
+class PointCloudEncoder {
+ public:
+ PointCloudEncoder();
+ virtual ~PointCloudEncoder() = default;
+
+ // Sets the point cloud that is going be encoded. Must be called before the
+ // Encode() method.
+ void SetPointCloud(const PointCloud &pc);
+
+ // The main entry point that encodes provided point cloud.
+ Status Encode(const EncoderOptions &options, EncoderBuffer *out_buffer);
+
+ virtual EncodedGeometryType GetGeometryType() const { return POINT_CLOUD; }
+
+ // Returns the unique identifier of the encoding method (such as Edgebreaker
+ // for mesh compression).
+ virtual uint8_t GetEncodingMethod() const = 0;
+
+ // Returns the number of points that were encoded during the last Encode()
+ // function call. Valid only if "store_number_of_encoded_points" flag was set
+ // in the provided EncoderOptions.
+ size_t num_encoded_points() const { return num_encoded_points_; }
+
+ int num_attributes_encoders() const {
+ return static_cast<int>(attributes_encoders_.size());
+ }
+ AttributesEncoder *attributes_encoder(int i) {
+ return attributes_encoders_[i].get();
+ }
+
+ // Adds a new attribute encoder, returning its id.
+ int AddAttributesEncoder(std::unique_ptr<AttributesEncoder> att_enc) {
+ attributes_encoders_.push_back(std::move(att_enc));
+ return static_cast<int>(attributes_encoders_.size() - 1);
+ }
+
+ // Marks one attribute as a parent of another attribute. Must be called after
+ // all attribute encoders are created (usually in the
+ // AttributeEncoder::Init() method).
+ bool MarkParentAttribute(int32_t parent_att_id);
+
+ // Returns an attribute containing portable version of the attribute data that
+ // is guaranteed to be encoded losslessly. This attribute can be used safely
+ // as predictor for other attributes.
+ const PointAttribute *GetPortableAttribute(int32_t point_attribute_id);
+
+ EncoderBuffer *buffer() { return buffer_; }
+ const EncoderOptions *options() const { return options_; }
+ const PointCloud *point_cloud() const { return point_cloud_; }
+
+ protected:
+ // Can be implemented by derived classes to perform any custom initialization
+ // of the encoder. Called in the Encode() method.
+ virtual bool InitializeEncoder() { return true; }
+
+ // Should be used to encode any encoder-specific data.
+ virtual bool EncodeEncoderData() { return true; }
+
+ // Encodes any global geometry data (such as the number of points).
+ virtual bool EncodeGeometryData() { return true; }
+
+ // encode all attribute values. The attribute encoders are sorted to resolve
+ // any attribute dependencies and all the encoded data is stored into the
+ // |buffer_|.
+ // Returns false if the encoding failed.
+ virtual bool EncodePointAttributes();
+
+ // Generate attribute encoders that are going to be used for encoding
+ // point attribute data. Calls GenerateAttributesEncoder() for every attribute
+ // of the encoded PointCloud.
+ virtual bool GenerateAttributesEncoders();
+
+ // Creates attribute encoder for a specific point attribute. This function
+ // needs to be implemented by the derived classes. The derived classes need
+ // to either 1. Create a new attribute encoder and add it using the
+ // AddAttributeEncoder method, or 2. add the attribute to an existing
+ // attribute encoder (using AttributesEncoder::AddAttributeId() method).
+ virtual bool GenerateAttributesEncoder(int32_t att_id) = 0;
+
+ // 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 */) {
+ return true;
+ }
+
+ // Encodes all the attribute data using the created attribute encoders.
+ virtual bool EncodeAllAttributes();
+
+ // Computes and sets the num_encoded_points_ for the encoder.
+ virtual void ComputeNumberOfEncodedPoints() = 0;
+
+ void set_num_encoded_points(size_t num_points) {
+ num_encoded_points_ = num_points;
+ }
+
+ private:
+ // Encodes Draco header that is the same for all encoders.
+ Status EncodeHeader();
+
+ // Encode metadata.
+ Status EncodeMetadata();
+
+ // Rearranges attribute encoders and their attributes to reflect the
+ // underlying attribute dependencies. This ensures that the attributes are
+ // encoded in the correct order (parent attributes before their children).
+ bool RearrangeAttributesEncoders();
+
+ const PointCloud *point_cloud_;
+ std::vector<std::unique_ptr<AttributesEncoder>> attributes_encoders_;
+
+ // Map between attribute id and encoder id.
+ std::vector<int32_t> attribute_to_encoder_map_;
+
+ // Encoding order of individual attribute encoders (i.e., the order in which
+ // they are processed during encoding that may be different from the order
+ // in which they were created because of attribute dependencies.
+ std::vector<int32_t> attributes_encoder_ids_order_;
+
+ // This buffer holds the final encoded data.
+ EncoderBuffer *buffer_;
+
+ const EncoderOptions *options_;
+
+ size_t num_encoded_points_;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_decoder.cc b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_decoder.cc
new file mode 100644
index 00000000000..c35fd79df60
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_decoder.cc
@@ -0,0 +1,38 @@
+// 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 "draco/compression/point_cloud/point_cloud_kd_tree_decoder.h"
+
+#include "draco/compression/attributes/kd_tree_attributes_decoder.h"
+
+namespace draco {
+
+bool PointCloudKdTreeDecoder::DecodeGeometryData() {
+ int32_t num_points;
+ if (!buffer()->Decode(&num_points))
+ return false;
+ if (num_points < 0)
+ return false;
+ point_cloud()->set_num_points(num_points);
+ return true;
+}
+
+bool PointCloudKdTreeDecoder::CreateAttributesDecoder(int32_t att_decoder_id) {
+ // Always create the basic attribute decoder.
+ return SetAttributesDecoder(
+ att_decoder_id,
+ std::unique_ptr<AttributesDecoder>(new KdTreeAttributesDecoder()));
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_decoder.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_decoder.h
new file mode 100644
index 00000000000..6e192f2a5a0
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_decoder.h
@@ -0,0 +1,31 @@
+// 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_COMPRESSION_POINT_CLOUD_POINT_CLOUD_KD_TREE_DECODER_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_KD_TREE_DECODER_H_
+
+#include "draco/compression/point_cloud/point_cloud_decoder.h"
+
+namespace draco {
+
+// Decodes PointCloud encoded with the PointCloudKdTreeEncoder.
+class PointCloudKdTreeDecoder : public PointCloudDecoder {
+ protected:
+ bool DecodeGeometryData() override;
+ bool CreateAttributesDecoder(int32_t att_decoder_id) override;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_KD_TREE_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_encoder.cc b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_encoder.cc
new file mode 100644
index 00000000000..7ecfca2b456
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_encoder.cc
@@ -0,0 +1,42 @@
+// 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 "draco/compression/point_cloud/point_cloud_kd_tree_encoder.h"
+#include "draco/compression/attributes/kd_tree_attributes_encoder.h"
+
+namespace draco {
+
+bool PointCloudKdTreeEncoder::EncodeGeometryData() {
+ const int32_t num_points = point_cloud()->num_points();
+ buffer()->Encode(num_points);
+ return true;
+}
+
+bool PointCloudKdTreeEncoder::GenerateAttributesEncoder(int32_t att_id) {
+ if (num_attributes_encoders() == 0) {
+ // Create a new attribute encoder only for the first attribute.
+ AddAttributesEncoder(std::unique_ptr<AttributesEncoder>(
+ new KdTreeAttributesEncoder(att_id)));
+ return true;
+ }
+ // Add a new attribute to the attribute encoder.
+ attributes_encoder(0)->AddAttributeId(att_id);
+ return true;
+}
+
+void PointCloudKdTreeEncoder::ComputeNumberOfEncodedPoints() {
+ set_num_encoded_points(point_cloud()->num_points());
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_encoder.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_encoder.h
new file mode 100644
index 00000000000..98bc3d67fde
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_encoder.h
@@ -0,0 +1,45 @@
+// 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_COMPRESSION_POINT_CLOUD_POINT_CLOUD_KD_TREE_ENCODER_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_KD_TREE_ENCODER_H_
+
+#include "draco/compression/point_cloud/point_cloud_encoder.h"
+
+namespace draco {
+
+// Encodes a PointCloud using one of the available Kd-tree compression methods.
+// See FloatPointsKdTreeEncoder and DynamicIntegerPointsKdTreeEncoder for more
+// details. Currently, the input PointCloud must satisfy the following
+// requirements to use this encoder:
+// 1. PointCloud has only one attribute of type GeometryAttribute::POSITION.
+// 2. The position attribute has three components (x,y,z).
+// 3. The position values are stored as either DT_FLOAT32 or DT_UINT32.
+// 4. If the position values are stored as DT_FLOAT32, quantization needs to
+// be enabled for the position attribute.
+class PointCloudKdTreeEncoder : public PointCloudEncoder {
+ public:
+ uint8_t GetEncodingMethod() const override {
+ return POINT_CLOUD_KD_TREE_ENCODING;
+ }
+
+ protected:
+ bool EncodeGeometryData() override;
+ bool GenerateAttributesEncoder(int32_t att_id) override;
+ void ComputeNumberOfEncodedPoints() override;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_KD_TREE_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_encoding_test.cc b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_encoding_test.cc
new file mode 100644
index 00000000000..6156cfe02b5
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_kd_tree_encoding_test.cc
@@ -0,0 +1,456 @@
+// 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 "draco/compression/point_cloud/point_cloud_kd_tree_decoder.h"
+#include "draco/compression/point_cloud/point_cloud_kd_tree_encoder.h"
+#include "draco/core/draco_test_base.h"
+#include "draco/core/draco_test_utils.h"
+#include "draco/core/vector_d.h"
+#include "draco/io/obj_decoder.h"
+#include "draco/point_cloud/point_cloud_builder.h"
+
+namespace draco {
+
+class PointCloudKdTreeEncodingTest : public ::testing::Test {
+ protected:
+ 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(), p1.num_attributes());
+ for (auto index = 0; index < p0.num_attributes(); index += 1) {
+ ASSERT_EQ(p0.attribute(index)->num_components(),
+ p1.attribute(index)->num_components());
+ std::vector<double> points_0, points_1;
+ std::vector<double> att_entry_0(p0.attribute(index)->num_components());
+ std::vector<double> att_entry_1(p0.attribute(index)->num_components());
+ for (PointIndex i(0); i < p0.num_points(); ++i) {
+ p0.attribute(index)->ConvertValue(p0.attribute(index)->mapped_index(i),
+ &att_entry_0[0]);
+ p1.attribute(index)->ConvertValue(p1.attribute(index)->mapped_index(i),
+ &att_entry_1[0]);
+ for (int d = 0; d < p0.attribute(index)->num_components(); ++d) {
+ points_0.push_back(att_entry_0[d]);
+ points_1.push_back(att_entry_1[d]);
+ }
+ }
+ // To compare the point clouds we sort points components from both inputs
+ // separately, and then we compare all matching coordinates 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(points_0.begin(), points_0.end());
+ std::sort(points_1.begin(), points_1.end());
+ for (uint32_t i = 0; i < points_0.size(); ++i) {
+ ASSERT_LE(std::fabs(points_0[i] - points_1[i]), 1e-2);
+ }
+ }
+ }
+
+ void TestKdTreeEncoding(const PointCloud &pc) {
+ EncoderBuffer buffer;
+ PointCloudKdTreeEncoder encoder;
+ EncoderOptions options = EncoderOptions::CreateDefaultOptions();
+ options.SetGlobalInt("quantization_bits", 16);
+ for (int compression_level = 0; compression_level <= 6;
+ ++compression_level) {
+ options.SetSpeed(10 - compression_level, 10 - compression_level);
+ encoder.SetPointCloud(pc);
+ ASSERT_TRUE(encoder.Encode(options, &buffer).ok());
+
+ DecoderBuffer dec_buffer;
+ dec_buffer.Init(buffer.data(), buffer.size());
+ PointCloudKdTreeDecoder decoder;
+
+ std::unique_ptr<PointCloud> out_pc(new PointCloud());
+ DecoderOptions dec_options;
+ ASSERT_TRUE(decoder.Decode(dec_options, &dec_buffer, out_pc.get()).ok());
+
+ ComparePointClouds(pc, *out_pc);
+ }
+ }
+
+ void TestFloatEncoding(const std::string &file_name) {
+ std::unique_ptr<PointCloud> pc = ReadPointCloudFromTestFile(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<std::array<uint32_t, 3>> points(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<uint32_t, 3> 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<PointCloud> pc = builder.Finalize(false);
+ ASSERT_NE(pc, nullptr);
+
+ TestKdTreeEncoding(*pc);
+}
+
+// test higher dimensions with more attributes
+TEST_F(PointCloudKdTreeEncodingTest, TestIntKdTreeEncodingHigherDimension) {
+ constexpr int num_points = 120;
+ std::vector<std::array<uint32_t, 3>> points3(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<uint32_t, 3> pos;
+ // Generate some pseudo-random points.
+ pos[0] = 8 * ((i * 7) % 127);
+ pos[1] = 13 * ((i * 3) % 321);
+ pos[2] = 29 * ((i * 19) % 450);
+ points3[i] = pos;
+ }
+ std::vector<std::array<uint32_t, 2>> points2(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<uint32_t, 2> pos;
+ // Generate some pseudo-random points.
+ pos[0] = 8 * ((i * 7) % 127) + 1;
+ pos[1] = 13 * ((i * 3) % 321) + 1;
+ points2[i] = pos;
+ }
+ std::vector<std::array<uint32_t, 1>> points1(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<uint32_t, 1> pos;
+ // Generate some pseudo-random points.
+ pos[0] = 8 * ((i * 7) % 127) + 11;
+ points1[i] = pos;
+ }
+
+ PointCloudBuilder builder;
+ builder.Start(num_points);
+ const int att_id3 =
+ builder.AddAttribute(GeometryAttribute::POSITION, 3, DT_UINT32);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id3, PointIndex(i),
+ &(points3[i.value()])[0]);
+ }
+ const int att_id2 =
+ builder.AddAttribute(GeometryAttribute::POSITION, 2, DT_UINT32);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id2, PointIndex(i),
+ &(points2[i.value()])[0]);
+ }
+ const int att_id1 =
+ builder.AddAttribute(GeometryAttribute::GENERIC, 1, DT_UINT32);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id1, PointIndex(i),
+ &(points1[i.value()])[0]);
+ }
+
+ std::unique_ptr<PointCloud> pc = builder.Finalize(false);
+ ASSERT_NE(pc, nullptr);
+
+ TestKdTreeEncoding(*pc);
+}
+
+// Test 16 and 8 bit encoding.
+TEST_F(PointCloudKdTreeEncodingTest,
+ TestIntKdTreeEncodingHigherDimensionVariedTypes) {
+ constexpr int num_points = 120;
+ std::vector<std::array<uint32_t, 3>> points3(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<uint32_t, 3> pos;
+ // Generate some pseudo-random points.
+ pos[0] = 8 * ((i * 7) % 127);
+ pos[1] = 13 * ((i * 3) % 321);
+ pos[2] = 29 * ((i * 19) % 450);
+ points3[i] = pos;
+ }
+ std::vector<std::array<uint16_t, 2>> points2(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<uint16_t, 2> pos;
+ // Generate some pseudo-random points.
+ pos[0] = 8 * ((i * 7) % 127) + 1;
+ pos[1] = 13 * ((i * 3) % 321) + 1;
+ points2[i] = pos;
+ }
+ std::vector<std::array<uint8_t, 1>> points1(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<uint8_t, 1> pos;
+ // Generate some pseudo-random points.
+ pos[0] = 8 * ((i * 7) % 127) + 11;
+ points1[i] = pos;
+ }
+
+ PointCloudBuilder builder;
+ builder.Start(num_points);
+ const int att_id3 =
+ builder.AddAttribute(GeometryAttribute::POSITION, 3, DT_UINT32);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id3, PointIndex(i),
+ &(points3[i.value()])[0]);
+ }
+ const int att_id2 =
+ builder.AddAttribute(GeometryAttribute::POSITION, 2, DT_UINT16);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id2, PointIndex(i),
+ &(points2[i.value()])[0]);
+ }
+ const int att_id1 =
+ builder.AddAttribute(GeometryAttribute::GENERIC, 1, DT_UINT8);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id1, PointIndex(i),
+ &(points1[i.value()])[0]);
+ }
+
+ std::unique_ptr<PointCloud> pc = builder.Finalize(false);
+ ASSERT_NE(pc, nullptr);
+
+ TestKdTreeEncoding(*pc);
+}
+
+// Test 16 only encoding for one attribute.
+TEST_F(PointCloudKdTreeEncodingTest, TestIntKdTreeEncoding16Bit) {
+ constexpr int num_points = 120;
+ std::vector<std::array<uint16_t, 3>> points3(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<uint16_t, 3> pos;
+ // Generate some pseudo-random points.
+ pos[0] = 8 * ((i * 7) % 127);
+ pos[1] = 13 * ((i * 3) % 321);
+ pos[2] = 29 * ((i * 19) % 450);
+ points3[i] = pos;
+ }
+
+ PointCloudBuilder builder;
+ builder.Start(num_points);
+ const int att_id3 =
+ builder.AddAttribute(GeometryAttribute::POSITION, 3, DT_UINT16);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id3, PointIndex(i),
+ &(points3[i.value()])[0]);
+ }
+
+ std::unique_ptr<PointCloud> pc = builder.Finalize(false);
+ ASSERT_NE(pc, nullptr);
+
+ TestKdTreeEncoding(*pc);
+}
+
+// Test 16 and 8 bit encoding with size bigger than 32bit encoding.
+TEST_F(PointCloudKdTreeEncodingTest,
+ TestIntKdTreeEncodingHigherDimensionVariedTypesBig16BitEncoding) {
+ constexpr int num_points = 120;
+ std::vector<std::array<uint32_t, 3>> points3(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<uint32_t, 3> pos;
+ // Generate some pseudo-random points.
+ pos[0] = 8 * ((i * 7) % 127);
+ pos[1] = 13 * ((i * 3) % 321);
+ pos[2] = 29 * ((i * 19) % 450);
+ points3[i] = pos;
+ }
+ // The total size of the 16bit encoding must be bigger than the total size of
+ // the 32bit encoding.
+ std::vector<std::array<uint16_t, 7>> points7(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<uint16_t, 7> pos;
+ // Generate some pseudo-random points.
+ pos[0] = 8 * ((i * 7) % 127) + 1;
+ pos[1] = 13 * ((i * 3) % 321) + 1;
+ pos[2] = pos[0] + 13;
+ pos[3] = pos[2] + 13;
+ pos[4] = pos[3] + 13;
+ pos[5] = pos[4] + 13;
+ pos[6] = pos[5] + 13;
+ points7[i] = pos;
+ }
+ std::vector<std::array<uint8_t, 1>> points1(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<uint8_t, 1> pos;
+ // Generate some pseudo-random points.
+ pos[0] = 8 * ((i * 7) % 127) + 11;
+ points1[i] = pos;
+ }
+
+ PointCloudBuilder builder;
+ builder.Start(num_points);
+ const int att_id3 =
+ builder.AddAttribute(GeometryAttribute::POSITION, 3, DT_UINT32);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id3, PointIndex(i),
+ &(points3[i.value()])[0]);
+ }
+ const int att_id2 =
+ builder.AddAttribute(GeometryAttribute::POSITION, 7, DT_UINT16);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id2, PointIndex(i),
+ &(points7[i.value()])[0]);
+ }
+ const int att_id1 =
+ builder.AddAttribute(GeometryAttribute::GENERIC, 1, DT_UINT8);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id1, PointIndex(i),
+ &(points1[i.value()])[0]);
+ }
+
+ std::unique_ptr<PointCloud> pc = builder.Finalize(false);
+ ASSERT_NE(pc, nullptr);
+
+ TestKdTreeEncoding(*pc);
+}
+
+// Test encoding of quantized values.
+TEST_F(PointCloudKdTreeEncodingTest,
+ TestIntKdTreeEncodingHigherDimensionFloatTypes) {
+ constexpr int num_points = 130;
+ std::vector<std::array<uint32_t, 3>> points3(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<uint32_t, 3> pos;
+ // Generate some pseudo-random points.
+ pos[0] = 8 * ((i * 7) % 125);
+ pos[1] = 13 * ((i * 3) % 334);
+ pos[2] = 29 * ((i * 19) % 470);
+ points3[i] = pos;
+ }
+ std::vector<std::array<float, 2>> points_float(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<float, 2> pos;
+ // Generate some pseudo-random points.
+ pos[0] = static_cast<float>(8 * ((i * 7) % 127) + 1) / 2.5f;
+ pos[1] = static_cast<float>(13 * ((i * 3) % 321) + 1) / 3.2f;
+ points_float[i] = pos;
+ }
+
+ PointCloudBuilder builder;
+ builder.Start(num_points);
+ const int att_id3 =
+ builder.AddAttribute(GeometryAttribute::POSITION, 3, DT_UINT32);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id3, PointIndex(i),
+ &(points3[i.value()])[0]);
+ }
+ const int att_id_float =
+ builder.AddAttribute(GeometryAttribute::GENERIC, 2, DT_FLOAT32);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id_float, PointIndex(i),
+ &(points_float[i.value()])[0]);
+ }
+
+ std::unique_ptr<PointCloud> pc = builder.Finalize(false);
+ ASSERT_NE(pc, nullptr);
+
+ TestKdTreeEncoding(*pc);
+}
+
+// Test encoding of signed integer values
+TEST_F(PointCloudKdTreeEncodingTest, TestIntKdTreeEncodingSignedTypes) {
+ constexpr int num_points = 120;
+ std::vector<std::array<uint32_t, 3>> points3(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<uint32_t, 3> pos;
+ // Generate some pseudo-random points.
+ pos[0] = 8 * ((i * 7) % 127);
+ pos[1] = 13 * ((i * 3) % 321);
+ pos[2] = 29 * ((i * 19) % 450);
+ points3[i] = pos;
+ }
+ std::vector<std::array<int32_t, 2>> points2(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<int32_t, 2> pos;
+ // Generate some pseudo-random points.
+ pos[0] = 8 * ((i * 7) % 127) + 1;
+ if (i % 3 == 0)
+ pos[0] = -pos[0];
+ pos[1] = 13 * ((i * 3) % 321) + 1;
+ points2[i] = pos;
+ }
+ std::vector<std::array<int16_t, 1>> points1(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<int16_t, 1> pos;
+ // Generate some pseudo-random points.
+ pos[0] = 8 * ((i * 7) % 127) + 11;
+ if (i % 5 == 0)
+ pos[0] = -pos[0];
+ points1[i] = pos;
+ }
+
+ PointCloudBuilder builder;
+ builder.Start(num_points);
+ const int att_id3 =
+ builder.AddAttribute(GeometryAttribute::POSITION, 3, DT_UINT32);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id3, PointIndex(i),
+ &(points3[i.value()])[0]);
+ }
+ const int att_id2 =
+ builder.AddAttribute(GeometryAttribute::POSITION, 2, DT_INT32);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id2, PointIndex(i),
+ &(points2[i.value()])[0]);
+ }
+
+ const int att_id1 =
+ builder.AddAttribute(GeometryAttribute::GENERIC, 1, DT_INT16);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id1, PointIndex(i),
+ &(points1[i.value()])[0]);
+ }
+
+ std::unique_ptr<PointCloud> pc = builder.Finalize(false);
+ ASSERT_NE(pc, nullptr);
+
+ TestKdTreeEncoding(*pc);
+}
+
+// Test encoding of integer point clouds with > 16 dimensions.
+TEST_F(PointCloudKdTreeEncodingTest, TestIntKdTreeEncodingHighDimensional) {
+ constexpr int num_points = 120;
+ constexpr int num_dims = 42;
+ std::vector<std::array<uint32_t, num_dims>> points(num_points);
+ for (int i = 0; i < num_points; ++i) {
+ std::array<uint32_t, num_dims> pos;
+ // Generate some pseudo-random points.
+ for (int d = 0; d < num_dims; ++d) {
+ pos[d] = 8 * ((i + d) * (7 + (d % 4)) % (127 + d % 3));
+ }
+ points[i] = pos;
+ }
+ PointCloudBuilder builder;
+ builder.Start(num_points);
+ const int att_id =
+ builder.AddAttribute(GeometryAttribute::POSITION, num_dims, DT_UINT32);
+ for (PointIndex i(0); i < num_points; ++i) {
+ builder.SetAttributeValueForPoint(att_id, PointIndex(i),
+ &(points[i.value()])[0]);
+ }
+
+ std::unique_ptr<PointCloud> pc = builder.Finalize(false);
+ ASSERT_NE(pc, nullptr);
+
+ TestKdTreeEncoding(*pc);
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_decoder.cc b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_decoder.cc
new file mode 100644
index 00000000000..614cf37b266
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_decoder.cc
@@ -0,0 +1,41 @@
+// 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 "draco/compression/point_cloud/point_cloud_sequential_decoder.h"
+
+#include "draco/compression/attributes/linear_sequencer.h"
+#include "draco/compression/attributes/sequential_attribute_decoders_controller.h"
+
+namespace draco {
+
+bool PointCloudSequentialDecoder::DecodeGeometryData() {
+ int32_t num_points;
+ if (!buffer()->Decode(&num_points))
+ return false;
+ point_cloud()->set_num_points(num_points);
+ return true;
+}
+
+bool PointCloudSequentialDecoder::CreateAttributesDecoder(
+ int32_t att_decoder_id) {
+ // Always create the basic attribute decoder.
+ return SetAttributesDecoder(
+ att_decoder_id,
+ std::unique_ptr<AttributesDecoder>(
+ new SequentialAttributeDecodersController(
+ std::unique_ptr<PointsSequencer>(
+ new LinearSequencer(point_cloud()->num_points())))));
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_decoder.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_decoder.h
new file mode 100644
index 00000000000..9968dc27517
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_decoder.h
@@ -0,0 +1,33 @@
+// 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_COMPRESSION_POINT_CLOUD_POINT_CLOUD_SEQUENTIAL_DECODER_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_SEQUENTIAL_DECODER_H_
+
+#include "draco/compression/point_cloud/point_cloud_decoder.h"
+
+namespace draco {
+
+// Point cloud decoder for data encoded by the PointCloudSequentialEncoder.
+// All attribute values are decoded using an identity mapping between point ids
+// and attribute value ids.
+class PointCloudSequentialDecoder : public PointCloudDecoder {
+ protected:
+ bool DecodeGeometryData() override;
+ bool CreateAttributesDecoder(int32_t att_decoder_id) override;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_SEQUENTIAL_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_encoder.cc b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_encoder.cc
new file mode 100644
index 00000000000..9b92994b7a6
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_encoder.cc
@@ -0,0 +1,49 @@
+// 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 "draco/compression/point_cloud/point_cloud_sequential_encoder.h"
+
+#include "draco/compression/attributes/linear_sequencer.h"
+#include "draco/compression/attributes/sequential_attribute_encoders_controller.h"
+
+namespace draco {
+
+bool PointCloudSequentialEncoder::EncodeGeometryData() {
+ const int32_t num_points = point_cloud()->num_points();
+ buffer()->Encode(num_points);
+ return true;
+}
+
+bool PointCloudSequentialEncoder::GenerateAttributesEncoder(int32_t att_id) {
+ // Create only one attribute encoder that is going to encode all points in a
+ // linear sequence.
+ if (att_id == 0) {
+ // Create a new attribute encoder only for the first attribute.
+ AddAttributesEncoder(std::unique_ptr<AttributesEncoder>(
+ new SequentialAttributeEncodersController(
+ std::unique_ptr<PointsSequencer>(
+ new LinearSequencer(point_cloud()->num_points())),
+ att_id)));
+ } else {
+ // Reuse the existing attribute encoder for other attributes.
+ attributes_encoder(0)->AddAttributeId(att_id);
+ }
+ return true;
+}
+
+void PointCloudSequentialEncoder::ComputeNumberOfEncodedPoints() {
+ set_num_encoded_points(point_cloud()->num_points());
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_encoder.h b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_encoder.h
new file mode 100644
index 00000000000..f73b5a551e4
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_encoder.h
@@ -0,0 +1,43 @@
+// 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_COMPRESSION_POINT_CLOUD_POINT_CLOUD_SEQUENTIAL_ENCODER_H_
+#define DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_SEQUENTIAL_ENCODER_H_
+
+#include "draco/compression/point_cloud/point_cloud_encoder.h"
+
+namespace draco {
+
+// A basic point cloud encoder that iterates over all points and encodes all
+// attribute values for every visited point. The attribute values encoding
+// can be controlled using provided encoding option to enable features such
+// as quantization or prediction schemes.
+// This encoder preserves the order and the number of input points, but the
+// mapping between point ids and attribute values may be different for the
+// decoded point cloud.
+class PointCloudSequentialEncoder : public PointCloudEncoder {
+ public:
+ uint8_t GetEncodingMethod() const override {
+ return POINT_CLOUD_SEQUENTIAL_ENCODING;
+ }
+
+ protected:
+ bool EncodeGeometryData() override;
+ bool GenerateAttributesEncoder(int32_t att_id) override;
+ void ComputeNumberOfEncodedPoints() override;
+};
+
+} // namespace draco
+
+#endif // DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_SEQUENTIAL_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_encoding_test.cc b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_encoding_test.cc
new file mode 100644
index 00000000000..beab4bcd7e9
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/compression/point_cloud/point_cloud_sequential_encoding_test.cc
@@ -0,0 +1,90 @@
+// 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 "draco/compression/point_cloud/point_cloud_sequential_decoder.h"
+#include "draco/compression/point_cloud/point_cloud_sequential_encoder.h"
+#include "draco/core/draco_test_base.h"
+#include "draco/core/draco_test_utils.h"
+#include "draco/io/obj_decoder.h"
+
+namespace draco {
+
+class PointCloudSequentialEncodingTest : public ::testing::Test {
+ protected:
+ std::unique_ptr<PointCloud> EncodeAndDecodePointCloud(const PointCloud *pc) {
+ EncoderBuffer buffer;
+ PointCloudSequentialEncoder encoder;
+ EncoderOptions options = EncoderOptions::CreateDefaultOptions();
+ encoder.SetPointCloud(*pc);
+ if (!encoder.Encode(options, &buffer).ok())
+ return nullptr;
+
+ DecoderBuffer dec_buffer;
+ dec_buffer.Init(buffer.data(), buffer.size());
+ PointCloudSequentialDecoder decoder;
+
+ std::unique_ptr<PointCloud> out_pc(new PointCloud());
+ DecoderOptions dec_options;
+ if (!decoder.Decode(dec_options, &dec_buffer, out_pc.get()).ok())
+ return nullptr;
+ return out_pc;
+ }
+
+ void TestEncoding(const std::string &file_name) {
+ std::unique_ptr<PointCloud> pc = ReadPointCloudFromTestFile(file_name);
+ ASSERT_NE(pc, nullptr);
+
+ std::unique_ptr<PointCloud> decoded_pc =
+ EncodeAndDecodePointCloud(pc.get());
+ ASSERT_NE(decoded_pc.get(), nullptr);
+ ASSERT_EQ(decoded_pc->num_points(), pc->num_points());
+ }
+};
+
+TEST_F(PointCloudSequentialEncodingTest, DoesEncodeAndDecode) {
+ TestEncoding("test_nm.obj");
+}
+
+TEST_F(PointCloudSequentialEncodingTest, EncodingPointCloudWithMetadata) {
+ std::unique_ptr<PointCloud> pc = ReadPointCloudFromTestFile("test_nm.obj");
+ ASSERT_NE(pc, nullptr);
+ // Add metadata to point cloud.
+ std::unique_ptr<GeometryMetadata> metadata =
+ std::unique_ptr<GeometryMetadata>(new GeometryMetadata());
+ const uint32_t pos_att_id =
+ pc->GetNamedAttributeId(GeometryAttribute::POSITION);
+ std::unique_ptr<AttributeMetadata> pos_metadata =
+ std::unique_ptr<AttributeMetadata>(new AttributeMetadata());
+ pos_metadata->AddEntryString("name", "position");
+ pc->AddAttributeMetadata(pos_att_id, std::move(pos_metadata));
+
+ std::unique_ptr<PointCloud> decoded_pc = EncodeAndDecodePointCloud(pc.get());
+ ASSERT_NE(decoded_pc.get(), nullptr);
+
+ const GeometryMetadata *const pc_metadata = decoded_pc->GetMetadata();
+ ASSERT_NE(pc_metadata, nullptr);
+ // Test getting attribute metadata by id.
+ ASSERT_NE(pc->GetAttributeMetadataByAttributeId(pos_att_id), nullptr);
+ // Test getting attribute metadata by entry name value pair.
+ const AttributeMetadata *const requested_att_metadata =
+ pc_metadata->GetAttributeMetadataByStringEntry("name", "position");
+ ASSERT_NE(requested_att_metadata, nullptr);
+ ASSERT_EQ(requested_att_metadata->att_unique_id(),
+ pc->attribute(pos_att_id)->unique_id());
+}
+
+// TODO(ostava): Test the reusability of a single instance of the encoder and
+// decoder class.
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/core/bit_utils.cc b/extern/draco/dracoenc/src/draco/core/bit_utils.cc
new file mode 100644
index 00000000000..37119a7171b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/bit_utils.cc
@@ -0,0 +1,36 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/core/bit_utils.h"
+
+namespace draco {
+
+void ConvertSignedIntsToSymbols(const int32_t *in, int in_values,
+ uint32_t *out) {
+ // Convert the quantized values into a format more suitable for entropy
+ // encoding.
+ // Put the sign bit into LSB pos and shift the rest one bit left.
+ for (int i = 0; i < in_values; ++i) {
+ out[i] = ConvertSignedIntToSymbol(in[i]);
+ }
+}
+
+void ConvertSymbolsToSignedInts(const uint32_t *in, int in_values,
+ int32_t *out) {
+ for (int i = 0; i < in_values; ++i) {
+ out[i] = ConvertSymbolToSignedInt(in[i]);
+ }
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/core/bit_utils.h b/extern/draco/dracoenc/src/draco/core/bit_utils.h
new file mode 100644
index 00000000000..f63cd0750c9
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/bit_utils.h
@@ -0,0 +1,123 @@
+// 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.
+//
+// File containing a basic set of bit manipulation utilities used within the
+// Draco library.
+
+#ifndef DRACO_CORE_BIT_UTILS_H_
+#define DRACO_CORE_BIT_UTILS_H_
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <type_traits>
+
+#if defined(_MSC_VER)
+#include <intrin.h>
+#endif // defined(_MSC_VER)
+
+namespace draco {
+
+// Returns the number of '1' bits within the input 32 bit integer.
+inline int CountOneBits32(uint32_t n) {
+ n -= ((n >> 1) & 0x55555555);
+ n = ((n >> 2) & 0x33333333) + (n & 0x33333333);
+ return (((n + (n >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
+}
+
+inline uint32_t ReverseBits32(uint32_t n) {
+ n = ((n >> 1) & 0x55555555) | ((n & 0x55555555) << 1);
+ n = ((n >> 2) & 0x33333333) | ((n & 0x33333333) << 2);
+ n = ((n >> 4) & 0x0F0F0F0F) | ((n & 0x0F0F0F0F) << 4);
+ n = ((n >> 8) & 0x00FF00FF) | ((n & 0x00FF00FF) << 8);
+ return (n >> 16) | (n << 16);
+}
+
+// Copies the |nbits| from the src integer into the |dst| integer using the
+// provided bit offsets |dst_offset| and |src_offset|.
+inline void CopyBits32(uint32_t *dst, int dst_offset, uint32_t src,
+ int src_offset, int nbits) {
+ const uint32_t mask = (~static_cast<uint32_t>(0)) >> (32 - nbits)
+ << dst_offset;
+ *dst = (*dst & (~mask)) | (((src >> src_offset) << dst_offset) & mask);
+}
+
+// Returns the location of the most significant bit in the input integer |n|.
+// The functionality is not defined for |n == 0|.
+inline int MostSignificantBit(uint32_t n) {
+#if defined(__GNUC__)
+ return 31 ^ __builtin_clz(n);
+#elif defined(_MSC_VER)
+
+ unsigned long where;
+ _BitScanReverse(&where, n);
+ return (int)where;
+#else
+ // TODO(fgalligan): Optimize this code.
+ int msb = -1;
+ while (n != 0) {
+ msb++;
+ n >>= 1;
+ }
+ return msb;
+#endif
+}
+
+// Helper function that converts signed integer values into unsigned integer
+// symbols that can be encoded using an entropy encoder.
+void ConvertSignedIntsToSymbols(const int32_t *in, int in_values,
+ uint32_t *out);
+
+// Converts unsigned integer symbols encoded with an entropy encoder back to
+// signed values.
+void ConvertSymbolsToSignedInts(const uint32_t *in, int in_values,
+ int32_t *out);
+
+// Helper function that converts a single signed integer value into an unsigned
+// integer symbol that can be encoded using an entropy encoder.
+template <class IntTypeT>
+typename std::make_unsigned<IntTypeT>::type ConvertSignedIntToSymbol(
+ IntTypeT val) {
+ typedef typename std::make_unsigned<IntTypeT>::type UnsignedType;
+ static_assert(std::is_integral<IntTypeT>::value, "IntTypeT is not integral.");
+ // Early exit if val is positive.
+ if (val >= 0) {
+ return static_cast<UnsignedType>(val) << 1;
+ }
+ val = -(val + 1); // Map -1 to 0, -2 to -1, etc..
+ UnsignedType ret = static_cast<UnsignedType>(val);
+ ret <<= 1;
+ ret |= 1;
+ return ret;
+}
+
+// Converts a single unsigned integer symbol encoded with an entropy encoder
+// back to a signed value.
+template <class IntTypeT>
+typename std::make_signed<IntTypeT>::type ConvertSymbolToSignedInt(
+ IntTypeT val) {
+ static_assert(std::is_integral<IntTypeT>::value, "IntTypeT is not integral.");
+ typedef typename std::make_signed<IntTypeT>::type SignedType;
+ const bool is_positive = !static_cast<bool>(val & 1);
+ val >>= 1;
+ if (is_positive) {
+ return static_cast<SignedType>(val);
+ }
+ SignedType ret = static_cast<SignedType>(val);
+ ret = -ret - 1;
+ return ret;
+}
+
+} // namespace draco
+
+#endif // DRACO_CORE_BIT_UTILS_H_
diff --git a/extern/draco/dracoenc/src/draco/core/bounding_box.cc b/extern/draco/dracoenc/src/draco/core/bounding_box.cc
new file mode 100644
index 00000000000..d95b1e90759
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/bounding_box.cc
@@ -0,0 +1,23 @@
+// Copyright 2018 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "draco/core/bounding_box.h"
+
+namespace draco {
+
+BoundingBox::BoundingBox(const Vector3f &min_point_in,
+ const Vector3f &max_point_in)
+ : min_point_(min_point_in), max_point_(max_point_in) {}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/core/bounding_box.h b/extern/draco/dracoenc/src/draco/core/bounding_box.h
new file mode 100644
index 00000000000..0259267c99c
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/bounding_box.h
@@ -0,0 +1,52 @@
+// Copyright 2018 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_BOUNDING_BOX_H_
+#define DRACO_CORE_BOUNDING_BOX_H_
+
+#include "draco/core/vector_d.h"
+
+namespace draco {
+
+// Class for detecting the bounding box of a point_cloud or mesh.
+// Use the minimum point and the maximum point to define the bounding box.
+// TODO(xiaoxumeng): Change the class of BoundingBox to a template, similar to
+// draco/src/draco/core/vector_d.h
+class BoundingBox {
+ public:
+ // Initialization
+ BoundingBox(const Vector3f &min_point_in, const Vector3f &max_point_in);
+
+ inline const Vector3f &min_point() const { return min_point_; }
+ inline const Vector3f &max_point() const { return max_point_; }
+
+ // Conditionally updates the bounding box.
+ // TODO(xiaoxumeng): Change the function to a template function and change the
+ // argument to an iterator.
+ inline void update_bounding_box(const Vector3f &new_point) {
+ for (int i = 0; i < 3; i++) {
+ if (new_point[i] < min_point_[i])
+ min_point_[i] = new_point[i];
+ if (new_point[i] > max_point_[i])
+ max_point_[i] = new_point[i];
+ }
+ }
+
+ private:
+ Vector3f min_point_;
+ Vector3f max_point_;
+};
+} // namespace draco
+
+#endif // DRACO_CORE_BOUNDING_BOX_H_
diff --git a/extern/draco/dracoenc/src/draco/core/buffer_bit_coding_test.cc b/extern/draco/dracoenc/src/draco/core/buffer_bit_coding_test.cc
new file mode 100644
index 00000000000..e761284e236
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/buffer_bit_coding_test.cc
@@ -0,0 +1,116 @@
+// 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 "draco/core/decoder_buffer.h"
+#include "draco/core/encoder_buffer.h"
+
+#include "draco/core/draco_test_base.h"
+
+namespace draco {
+
+class BufferBitCodingTest : public ::testing::Test {
+ public:
+ typedef DecoderBuffer::BitDecoder BitDecoder;
+ typedef EncoderBuffer::BitEncoder BitEncoder;
+};
+
+TEST_F(BufferBitCodingTest, 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<const void *>(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(BufferBitCodingTest, 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<const void *>(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(BufferBitCodingTest, TestSingleBits) {
+ const int data = 0xaaaa;
+
+ BitDecoder decoder;
+ decoder.reset(static_cast<const void *>(&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(BufferBitCodingTest, TestMultipleBits) {
+ const uint8_t data[] = {0x76, 0x54, 0x32, 0x10, 0x76, 0x54, 0x32, 0x10};
+
+ BitDecoder decoder;
+ decoder.reset(static_cast<const void *>(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/extern/draco/dracoenc/src/draco/core/cycle_timer.cc b/extern/draco/dracoenc/src/draco/core/cycle_timer.cc
new file mode 100644
index 00000000000..1eea9f59702
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/cycle_timer.cc
@@ -0,0 +1,49 @@
+// 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 "draco/core/cycle_timer.h"
+
+namespace draco {
+void DracoTimer::Start() {
+#ifdef _WIN32
+ QueryPerformanceCounter(&tv_start);
+#else
+ gettimeofday(&tv_start, NULL);
+#endif
+}
+
+void DracoTimer::Stop() {
+#ifdef _WIN32
+ QueryPerformanceCounter(&tv_end);
+#else
+ gettimeofday(&tv_end, NULL);
+#endif
+}
+
+int64_t DracoTimer::GetInMs() {
+#ifdef _WIN32
+ LARGE_INTEGER elapsed = {0};
+ elapsed.QuadPart = tv_end.QuadPart - tv_start.QuadPart;
+
+ LARGE_INTEGER frequency = {0};
+ QueryPerformanceFrequency(&frequency);
+ return elapsed.QuadPart * 1000 / frequency.QuadPart;
+#else
+ const int64_t seconds = (tv_end.tv_sec - tv_start.tv_sec) * 1000;
+ const int64_t milliseconds = (tv_end.tv_usec - tv_start.tv_usec) / 1000;
+ return seconds + milliseconds;
+#endif
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/core/cycle_timer.h b/extern/draco/dracoenc/src/draco/core/cycle_timer.h
new file mode 100644
index 00000000000..172f1c2e9b5
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/cycle_timer.h
@@ -0,0 +1,50 @@
+// 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_CYCLE_TIMER_H_
+#define DRACO_CORE_CYCLE_TIMER_H_
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+typedef LARGE_INTEGER timeval;
+#else
+#include <sys/time.h>
+#endif
+
+#include <cinttypes>
+#include <cstddef>
+
+namespace draco {
+
+class DracoTimer {
+ public:
+ DracoTimer() {}
+ ~DracoTimer() {}
+ void Start();
+ void Stop();
+ int64_t GetInMs();
+
+ private:
+ timeval tv_start;
+ timeval tv_end;
+};
+
+typedef DracoTimer CycleTimer;
+
+} // namespace draco
+
+#endif // DRACO_CORE_CYCLE_TIMER_H_
diff --git a/extern/draco/dracoenc/src/draco/core/data_buffer.cc b/extern/draco/dracoenc/src/draco/core/data_buffer.cc
new file mode 100644
index 00000000000..3b57367bbf1
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/data_buffer.cc
@@ -0,0 +1,55 @@
+// 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 "draco/core/data_buffer.h"
+
+namespace draco {
+
+DataBuffer::DataBuffer() {}
+
+bool DataBuffer::Update(const void *data, int64_t size) {
+ const int64_t offset = 0;
+ return this->Update(data, size, offset);
+}
+
+bool DataBuffer::Update(const void *data, int64_t size, int64_t offset) {
+ if (data == nullptr) {
+ if (size + offset < 0)
+ return false;
+ // If no data is provided, just resize the buffer.
+ data_.resize(size + offset);
+ } else {
+ if (size < 0)
+ return false;
+ if (size + offset > static_cast<int64_t>(data_.size()))
+ data_.resize(size + offset);
+ const uint8_t *const byte_data = static_cast<const uint8_t *>(data);
+ std::copy(byte_data, byte_data + size, data_.data() + offset);
+ }
+ descriptor_.buffer_update_count++;
+ return true;
+}
+
+void DataBuffer::Resize(int64_t size) {
+ data_.resize(size);
+ descriptor_.buffer_update_count++;
+}
+
+void DataBuffer::WriteDataToStream(std::ostream &stream) {
+ if (data_.size() == 0)
+ return;
+ stream.write(reinterpret_cast<char *>(data_.data()), data_.size());
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/core/data_buffer.h b/extern/draco/dracoenc/src/draco/core/data_buffer.h
new file mode 100644
index 00000000000..8ee690540bc
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/data_buffer.h
@@ -0,0 +1,82 @@
+// 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_DATA_BUFFER_H_
+#define DRACO_CORE_DATA_BUFFER_H_
+
+#include <cstring>
+#include <ostream>
+#include <vector>
+
+#include "draco/core/draco_types.h"
+
+namespace draco {
+
+// Buffer descriptor servers as a unique identifier of a buffer.
+struct DataBufferDescriptor {
+ DataBufferDescriptor() : buffer_id(0), buffer_update_count(0) {}
+ // Id of the data buffer.
+ int64_t buffer_id;
+ // The number of times the buffer content was updated.
+ int64_t buffer_update_count;
+};
+
+// Class used for storing raw buffer data.
+class DataBuffer {
+ public:
+ DataBuffer();
+ bool Update(const void *data, int64_t size);
+ bool Update(const void *data, int64_t size, int64_t offset);
+
+ // Reallocate the buffer storage to a new size keeping the data unchanged.
+ void Resize(int64_t new_size);
+ void WriteDataToStream(std::ostream &stream);
+ // Reads data from the buffer. Potentially unsafe, called needs to ensure
+ // the accessed memory is valid.
+ void Read(int64_t byte_pos, void *out_data, size_t data_size) const {
+ memcpy(out_data, data() + byte_pos, data_size);
+ }
+
+ // Writes data to the buffer. Unsafe, caller must ensure the accessed memory
+ // is valid.
+ void Write(int64_t byte_pos, const void *in_data, size_t data_size) {
+ memcpy(const_cast<uint8_t *>(data()) + byte_pos, in_data, data_size);
+ }
+
+ // Copies data from another buffer to this buffer.
+ void Copy(int64_t dst_offset, const DataBuffer *src_buf, int64_t src_offset,
+ int64_t size) {
+ memcpy(const_cast<uint8_t *>(data()) + dst_offset,
+ src_buf->data() + src_offset, size);
+ }
+
+ void set_update_count(int64_t buffer_update_count) {
+ descriptor_.buffer_update_count = buffer_update_count;
+ }
+ int64_t update_count() const { return descriptor_.buffer_update_count; }
+ size_t data_size() const { return data_.size(); }
+ const uint8_t *data() const { return data_.data(); }
+ uint8_t *data() { return &data_[0]; }
+ int64_t buffer_id() const { return descriptor_.buffer_id; }
+ void set_buffer_id(int64_t buffer_id) { descriptor_.buffer_id = buffer_id; }
+
+ private:
+ std::vector<uint8_t> data_;
+ // Counter incremented by Update() calls.
+ DataBufferDescriptor descriptor_;
+};
+
+} // namespace draco
+
+#endif // DRACO_CORE_DATA_BUFFER_H_
diff --git a/extern/draco/dracoenc/src/draco/core/decoder_buffer.cc b/extern/draco/dracoenc/src/draco/core/decoder_buffer.cc
new file mode 100644
index 00000000000..5ce1f064ac2
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/decoder_buffer.cc
@@ -0,0 +1,70 @@
+// 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 "draco/core/decoder_buffer.h"
+
+#include "draco/core/macros.h"
+#include "draco/core/varint_decoding.h"
+
+namespace draco {
+
+DecoderBuffer::DecoderBuffer()
+ : data_(nullptr),
+ data_size_(0),
+ pos_(0),
+ bit_mode_(false),
+ bitstream_version_(0) {}
+
+void DecoderBuffer::Init(const char *data, size_t data_size) {
+ Init(data, data_size, bitstream_version_);
+}
+
+void DecoderBuffer::Init(const char *data, size_t data_size, uint16_t version) {
+ data_ = data;
+ data_size_ = data_size;
+ bitstream_version_ = version;
+ pos_ = 0;
+}
+
+bool DecoderBuffer::StartBitDecoding(bool decode_size, uint64_t *out_size) {
+ if (decode_size) {
+#ifdef DRACO_BACKWARDS_COMPATIBILITY_SUPPORTED
+ if (bitstream_version_ < DRACO_BITSTREAM_VERSION(2, 2)) {
+ if (!Decode(out_size))
+ return false;
+ } else
+#endif
+ {
+ if (!DecodeVarint(out_size, this))
+ return false;
+ }
+ }
+ bit_mode_ = true;
+ bit_decoder_.reset(data_head(), remaining_size());
+ return true;
+}
+
+void DecoderBuffer::EndBitDecoding() {
+ bit_mode_ = false;
+ const uint64_t bits_decoded = bit_decoder_.BitsDecoded();
+ const uint64_t bytes_decoded = (bits_decoded + 7) / 8;
+ pos_ += bytes_decoded;
+}
+
+DecoderBuffer::BitDecoder::BitDecoder()
+ : bit_buffer_(nullptr), bit_buffer_end_(nullptr), bit_offset_(0) {}
+
+DecoderBuffer::BitDecoder::~BitDecoder() {}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/core/decoder_buffer.h b/extern/draco/dracoenc/src/draco/core/decoder_buffer.h
new file mode 100644
index 00000000000..5e3d1ac738d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/decoder_buffer.h
@@ -0,0 +1,210 @@
+// 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_DECODER_BUFFER_H_
+#define DRACO_CORE_DECODER_BUFFER_H_
+
+#include <stdint.h>
+#include <cstring>
+#include <memory>
+
+#include "draco/draco_features.h"
+
+#include "draco/core/macros.h"
+
+namespace draco {
+
+// Class is a wrapper around input data used by MeshDecoder. It provides a
+// basic interface for decoding either typed or variable-bit sized data.
+class DecoderBuffer {
+ public:
+ DecoderBuffer();
+ DecoderBuffer(const DecoderBuffer &buf) = default;
+
+ DecoderBuffer &operator=(const DecoderBuffer &buf) = default;
+
+ // Sets the buffer's internal data. Note that no copy of the input data is
+ // made so the data owner needs to keep the data valid and unchanged for
+ // runtime of the decoder.
+ void Init(const char *data, size_t data_size);
+
+ // Sets the buffer's internal data. |version| is the Draco bitstream version.
+ void Init(const char *data, size_t data_size, uint16_t version);
+
+ // Starts decoding a bit sequence.
+ // decode_size must be true if the size of the encoded bit data was included,
+ // during encoding. The size is then returned to out_size.
+ // Returns false on error.
+ bool StartBitDecoding(bool decode_size, uint64_t *out_size);
+
+ // Ends the decoding of the bit sequence and return to the default
+ // byte-aligned decoding.
+ void EndBitDecoding();
+
+ // Decodes up to 32 bits into out_val. Can be called only in between
+ // StartBitDecoding and EndBitDecoding. Otherwise returns false.
+ bool DecodeLeastSignificantBits32(int nbits, uint32_t *out_value) {
+ if (!bit_decoder_active())
+ return false;
+ bit_decoder_.GetBits(nbits, out_value);
+ return true;
+ }
+
+ // Decodes an arbitrary data type.
+ // Can be used only when we are not decoding a bit-sequence.
+ // Returns false on error.
+ template <typename T>
+ bool Decode(T *out_val) {
+ if (!Peek(out_val))
+ return false;
+ pos_ += sizeof(T);
+ return true;
+ }
+
+ bool Decode(void *out_data, size_t size_to_decode) {
+ if (data_size_ < static_cast<int64_t>(pos_ + size_to_decode))
+ return false; // Buffer overflow.
+ memcpy(out_data, (data_ + pos_), size_to_decode);
+ pos_ += size_to_decode;
+ return true;
+ }
+
+ // Decodes an arbitrary data, but does not advance the reading position.
+ template <typename T>
+ bool Peek(T *out_val) {
+ const size_t size_to_decode = sizeof(T);
+ if (data_size_ < static_cast<int64_t>(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_ < static_cast<int64_t>(pos_ + size_to_peek))
+ return false; // Buffer overflow.
+ memcpy(out_data, (data_ + pos_), size_to_peek);
+ return true;
+ }
+
+ // Discards #bytes from the input buffer.
+ void Advance(int64_t bytes) { pos_ += bytes; }
+
+ // Moves the parsing position to a specific offset from the beginning of the
+ // input data.
+ void StartDecodingFrom(int64_t offset) { pos_ = offset; }
+
+ void set_bitstream_version(uint16_t version) { bitstream_version_ = version; }
+
+ // Returns the data array at the current decoder position.
+ const char *data_head() const { return data_ + pos_; }
+ int64_t remaining_size() const { return data_size_ - pos_; }
+ int64_t decoded_size() const { return pos_; }
+ bool bit_decoder_active() const { return bit_mode_; }
+
+ // Returns the bitstream associated with the data. Returns 0 if unknown.
+ uint16_t bitstream_version() const { return bitstream_version_; }
+
+ private:
+ // Internal helper class to decode bits from a bit buffer.
+ class BitDecoder {
+ public:
+ BitDecoder();
+ ~BitDecoder();
+
+ // Sets the bit buffer to |b|. |s| is the size of |b| in bytes.
+ inline void reset(const void *b, size_t s) {
+ bit_offset_ = 0;
+ bit_buffer_ = static_cast<const uint8_t *>(b);
+ bit_buffer_end_ = bit_buffer_ + s;
+ }
+
+ // Returns number of bits decoded so far.
+ inline uint64_t BitsDecoded() const {
+ return static_cast<uint64_t>(bit_offset_);
+ }
+
+ // Return number of bits available for decoding
+ inline uint64_t AvailBits() const {
+ return ((bit_buffer_end_ - bit_buffer_) * 8) - bit_offset_;
+ }
+
+ inline uint32_t EnsureBits(int k) {
+ DRACO_DCHECK_LE(k, 24);
+ DRACO_DCHECK_LE(static_cast<uint64_t>(k), AvailBits());
+
+ uint32_t buf = 0;
+ for (int i = 0; i < k; ++i) {
+ buf |= PeekBit(i) << i;
+ }
+ return buf; // Okay to return extra bits
+ }
+
+ inline void ConsumeBits(int k) { bit_offset_ += k; }
+
+ // Returns |nbits| bits in |x|.
+ inline bool GetBits(int32_t nbits, uint32_t *x) {
+ DRACO_DCHECK_GE(nbits, 0);
+ DRACO_DCHECK_LE(nbits, 32);
+ uint32_t value = 0;
+ for (int32_t bit = 0; bit < nbits; ++bit)
+ value |= GetBit() << bit;
+ *x = value;
+ return true;
+ }
+
+ private:
+ // TODO(fgalligan): Add support for error reporting on range check.
+ // Returns one bit from the bit buffer.
+ inline int GetBit() {
+ const size_t off = bit_offset_;
+ const size_t byte_offset = off >> 3;
+ const int bit_shift = static_cast<int>(off & 0x7);
+ if (bit_buffer_ + byte_offset < bit_buffer_end_) {
+ const int bit = (bit_buffer_[byte_offset] >> bit_shift) & 1;
+ bit_offset_ = off + 1;
+ return bit;
+ }
+ return 0;
+ }
+
+ inline int PeekBit(int offset) {
+ const size_t off = bit_offset_ + offset;
+ const size_t byte_offset = off >> 3;
+ const int bit_shift = static_cast<int>(off & 0x7);
+ if (bit_buffer_ + byte_offset < bit_buffer_end_) {
+ const int bit = (bit_buffer_[byte_offset] >> bit_shift) & 1;
+ return bit;
+ }
+ return 0;
+ }
+
+ const uint8_t *bit_buffer_;
+ const uint8_t *bit_buffer_end_;
+ size_t bit_offset_;
+ };
+ friend class BufferBitCodingTest;
+
+ const char *data_;
+ int64_t data_size_;
+
+ // Current parsing position of the decoder.
+ int64_t pos_;
+ BitDecoder bit_decoder_;
+ bool bit_mode_;
+ uint16_t bitstream_version_;
+};
+
+} // namespace draco
+
+#endif // DRACO_CORE_DECODER_BUFFER_H_
diff --git a/extern/draco/dracoenc/src/draco/core/divide.cc b/extern/draco/dracoenc/src/draco/core/divide.cc
new file mode 100644
index 00000000000..6d2e57120c8
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/divide.cc
@@ -0,0 +1,88 @@
+// 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.
+//
+// This file is based off libvpx's divide.c.
+
+#include "draco/core/divide.h"
+
+namespace draco {
+
+const struct fastdiv_elem vp10_fastdiv_tab[256] = {
+ {0, 0}, {0, 0}, {0, 1}, {1431655766, 2},
+ {0, 2}, {2576980378, 3}, {1431655766, 3}, {613566757, 3},
+ {0, 3}, {3340530120, 4}, {2576980378, 4}, {1952257862, 4},
+ {1431655766, 4}, {991146300, 4}, {613566757, 4}, {286331154, 4},
+ {0, 4}, {3789677026, 5}, {3340530120, 5}, {2938661835, 5},
+ {2576980378, 5}, {2249744775, 5}, {1952257862, 5}, {1680639377, 5},
+ {1431655766, 5}, {1202590843, 5}, {991146300, 5}, {795364315, 5},
+ {613566757, 5}, {444306962, 5}, {286331154, 5}, {138547333, 5},
+ {0, 5}, {4034666248, 6}, {3789677026, 6}, {3558687189, 6},
+ {3340530120, 6}, {3134165325, 6}, {2938661835, 6}, {2753184165, 6},
+ {2576980378, 6}, {2409371898, 6}, {2249744775, 6}, {2097542168, 6},
+ {1952257862, 6}, {1813430637, 6}, {1680639377, 6}, {1553498810, 6},
+ {1431655766, 6}, {1314785907, 6}, {1202590843, 6}, {1094795586, 6},
+ {991146300, 6}, {891408307, 6}, {795364315, 6}, {702812831, 6},
+ {613566757, 6}, {527452125, 6}, {444306962, 6}, {363980280, 6},
+ {286331154, 6}, {211227900, 6}, {138547333, 6}, {68174085, 6},
+ {0, 6}, {4162814457, 7}, {4034666248, 7}, {3910343360, 7},
+ {3789677026, 7}, {3672508268, 7}, {3558687189, 7}, {3448072337, 7},
+ {3340530120, 7}, {3235934265, 7}, {3134165325, 7}, {3035110223, 7},
+ {2938661835, 7}, {2844718599, 7}, {2753184165, 7}, {2663967058, 7},
+ {2576980378, 7}, {2492141518, 7}, {2409371898, 7}, {2328596727, 7},
+ {2249744775, 7}, {2172748162, 7}, {2097542168, 7}, {2024065048, 7},
+ {1952257862, 7}, {1882064321, 7}, {1813430637, 7}, {1746305385, 7},
+ {1680639377, 7}, {1616385542, 7}, {1553498810, 7}, {1491936009, 7},
+ {1431655766, 7}, {1372618415, 7}, {1314785907, 7}, {1258121734, 7},
+ {1202590843, 7}, {1148159575, 7}, {1094795586, 7}, {1042467791, 7},
+ {991146300, 7}, {940802361, 7}, {891408307, 7}, {842937507, 7},
+ {795364315, 7}, {748664025, 7}, {702812831, 7}, {657787785, 7},
+ {613566757, 7}, {570128403, 7}, {527452125, 7}, {485518043, 7},
+ {444306962, 7}, {403800345, 7}, {363980280, 7}, {324829460, 7},
+ {286331154, 7}, {248469183, 7}, {211227900, 7}, {174592167, 7},
+ {138547333, 7}, {103079216, 7}, {68174085, 7}, {33818641, 7},
+ {0, 7}, {4228378656, 8}, {4162814457, 8}, {4098251237, 8},
+ {4034666248, 8}, {3972037425, 8}, {3910343360, 8}, {3849563281, 8},
+ {3789677026, 8}, {3730665024, 8}, {3672508268, 8}, {3615188300, 8},
+ {3558687189, 8}, {3502987511, 8}, {3448072337, 8}, {3393925206, 8},
+ {3340530120, 8}, {3287871517, 8}, {3235934265, 8}, {3184703642, 8},
+ {3134165325, 8}, {3084305374, 8}, {3035110223, 8}, {2986566663, 8},
+ {2938661835, 8}, {2891383213, 8}, {2844718599, 8}, {2798656110, 8},
+ {2753184165, 8}, {2708291480, 8}, {2663967058, 8}, {2620200175, 8},
+ {2576980378, 8}, {2534297473, 8}, {2492141518, 8}, {2450502814, 8},
+ {2409371898, 8}, {2368739540, 8}, {2328596727, 8}, {2288934667, 8},
+ {2249744775, 8}, {2211018668, 8}, {2172748162, 8}, {2134925265, 8},
+ {2097542168, 8}, {2060591247, 8}, {2024065048, 8}, {1987956292, 8},
+ {1952257862, 8}, {1916962805, 8}, {1882064321, 8}, {1847555765, 8},
+ {1813430637, 8}, {1779682582, 8}, {1746305385, 8}, {1713292966, 8},
+ {1680639377, 8}, {1648338801, 8}, {1616385542, 8}, {1584774030, 8},
+ {1553498810, 8}, {1522554545, 8}, {1491936009, 8}, {1461638086, 8},
+ {1431655766, 8}, {1401984144, 8}, {1372618415, 8}, {1343553873, 8},
+ {1314785907, 8}, {1286310003, 8}, {1258121734, 8}, {1230216764, 8},
+ {1202590843, 8}, {1175239808, 8}, {1148159575, 8}, {1121346142, 8},
+ {1094795586, 8}, {1068504060, 8}, {1042467791, 8}, {1016683080, 8},
+ {991146300, 8}, {965853890, 8}, {940802361, 8}, {915988286, 8},
+ {891408307, 8}, {867059126, 8}, {842937507, 8}, {819040276, 8},
+ {795364315, 8}, {771906565, 8}, {748664025, 8}, {725633745, 8},
+ {702812831, 8}, {680198441, 8}, {657787785, 8}, {635578121, 8},
+ {613566757, 8}, {591751050, 8}, {570128403, 8}, {548696263, 8},
+ {527452125, 8}, {506393524, 8}, {485518043, 8}, {464823301, 8},
+ {444306962, 8}, {423966729, 8}, {403800345, 8}, {383805589, 8},
+ {363980280, 8}, {344322273, 8}, {324829460, 8}, {305499766, 8},
+ {286331154, 8}, {267321616, 8}, {248469183, 8}, {229771913, 8},
+ {211227900, 8}, {192835267, 8}, {174592167, 8}, {156496785, 8},
+ {138547333, 8}, {120742053, 8}, {103079216, 8}, {85557118, 8},
+ {68174085, 8}, {50928466, 8}, {33818641, 8}, {16843010, 8},
+};
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/core/divide.h b/extern/draco/dracoenc/src/draco/core/divide.h
new file mode 100644
index 00000000000..2217c861e0b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/divide.h
@@ -0,0 +1,41 @@
+// 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_DIVIDE_H_
+#define DRACO_CORE_DIVIDE_H_
+// An implementation of the divide by multiply algorithm
+// https://gmplib.org/~tege/divcnst-pldi94.pdf
+// This file is based off libvpx's divide.h.
+
+#include <stdint.h>
+#include <climits>
+
+namespace draco {
+
+struct fastdiv_elem {
+ unsigned mult;
+ unsigned shift;
+};
+
+extern const struct fastdiv_elem vp10_fastdiv_tab[256];
+
+static inline unsigned fastdiv(unsigned x, int y) {
+ unsigned t =
+ ((uint64_t)x * vp10_fastdiv_tab[y].mult) >> (sizeof(x) * CHAR_BIT);
+ return (t + x) >> vp10_fastdiv_tab[y].shift;
+}
+
+} // namespace draco
+
+#endif // DRACO_CORE_DIVIDE_H_
diff --git a/extern/draco/dracoenc/src/draco/core/draco_index_type.h b/extern/draco/dracoenc/src/draco/core/draco_index_type.h
new file mode 100644
index 00000000000..d9dd3f64fa8
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/draco_index_type.h
@@ -0,0 +1,183 @@
+// 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.
+//
+// This files provides a basic framework for strongly typed indices that are
+// used within the Draco library. The motivation of using strongly typed indices
+// is to prevent bugs caused by mixing up incompatible indices, such as indexing
+// mesh faces with point indices and vice versa.
+//
+// Usage:
+// Define strongly typed index using macro:
+//
+// DEFINE_NEW_DRACO_INDEX_TYPE(value_type, name)
+//
+// where |value_type| is the data type of the index value (such as int32_t)
+// and |name| is a unique typename of the new index.
+//
+// E.g., we can define new index types as:
+//
+// DEFINE_NEW_DRACO_INDEX_TYPE(int, PointIndex)
+// DEFINE_NEW_DRACO_INDEX_TYPE(int, FaceIndex)
+//
+// The new types can then be used in the similar way as the regular weakly
+// typed indices (such as int32, int64, ...), but they cannot be
+// accidentally misassigned. E.g.:
+//
+// PointIndex point_index(10);
+// FaceIndex face_index;
+// face_index = point_index; // Compile error!
+//
+// One can still cast one type to another explicitly by accessing the index
+// value directly using the .value() method:
+//
+// face_index = FaceIndex(point_index.value()); // Compiles OK.
+//
+// Strongly typed indices support most of the common binary and unary
+// operators and support for additional operators can be added if
+// necessary.
+
+#ifndef DRACO_CORE_DRACO_INDEX_TYPE_H_
+#define DRACO_CORE_DRACO_INDEX_TYPE_H_
+
+#include <ostream>
+
+#include "draco/draco_features.h"
+
+namespace draco {
+
+#define DEFINE_NEW_DRACO_INDEX_TYPE(value_type, name) \
+ struct name##_tag_type_ {}; \
+ typedef IndexType<value_type, name##_tag_type_> name;
+
+template <class ValueTypeT, class TagT>
+class IndexType {
+ public:
+ typedef IndexType<ValueTypeT, TagT> ThisIndexType;
+ typedef ValueTypeT ValueType;
+
+ constexpr IndexType() : value_(ValueTypeT()) {}
+ constexpr explicit IndexType(ValueTypeT value) : value_(value) {}
+
+ constexpr ValueTypeT value() const { return value_; }
+
+ constexpr bool operator==(const IndexType &i) const {
+ return value_ == i.value_;
+ }
+ constexpr bool operator==(const ValueTypeT &val) const {
+ return value_ == val;
+ }
+ constexpr bool operator!=(const IndexType &i) const {
+ return value_ != i.value_;
+ }
+ constexpr bool operator!=(const ValueTypeT &val) const {
+ return value_ != val;
+ }
+ constexpr bool operator<(const IndexType &i) const {
+ return value_ < i.value_;
+ }
+ constexpr bool operator<(const ValueTypeT &val) const { return value_ < val; }
+ constexpr bool operator>(const IndexType &i) const {
+ return value_ > i.value_;
+ }
+ constexpr bool operator>(const ValueTypeT &val) const { return value_ > val; }
+ constexpr bool operator>=(const IndexType &i) const {
+ return value_ >= i.value_;
+ }
+ constexpr bool operator>=(const ValueTypeT &val) const {
+ return value_ >= val;
+ }
+
+ inline ThisIndexType &operator++() {
+ ++value_;
+ return *this;
+ }
+ inline ThisIndexType operator++(int) {
+ const ThisIndexType ret(value_);
+ ++value_;
+ return ret;
+ }
+
+ inline ThisIndexType &operator--() {
+ --value_;
+ return *this;
+ }
+ inline ThisIndexType operator--(int) {
+ const ThisIndexType ret(value_);
+ --value_;
+ return ret;
+ }
+
+ constexpr ThisIndexType operator+(const IndexType &i) const {
+ return ThisIndexType(value_ + i.value_);
+ }
+ constexpr ThisIndexType operator+(const ValueTypeT &val) const {
+ return ThisIndexType(value_ + val);
+ }
+ constexpr ThisIndexType operator-(const IndexType &i) const {
+ return ThisIndexType(value_ - i.value_);
+ }
+ constexpr ThisIndexType operator-(const ValueTypeT &val) const {
+ return ThisIndexType(value_ - val);
+ }
+
+ inline ThisIndexType &operator+=(const IndexType &i) {
+ value_ += i.value_;
+ return *this;
+ }
+ inline ThisIndexType operator+=(const ValueTypeT &val) {
+ value_ += val;
+ return *this;
+ }
+ inline ThisIndexType &operator-=(const IndexType &i) {
+ value_ -= i.value_;
+ return *this;
+ }
+ inline ThisIndexType operator-=(const ValueTypeT &val) {
+ value_ -= val;
+ return *this;
+ }
+ inline ThisIndexType &operator=(const ThisIndexType &i) {
+ value_ = i.value_;
+ return *this;
+ }
+ inline ThisIndexType &operator=(const ValueTypeT &val) {
+ value_ = val;
+ return *this;
+ }
+
+ private:
+ ValueTypeT value_;
+};
+
+// Stream operator << provided for logging purposes.
+template <class ValueTypeT, class TagT>
+std::ostream &operator<<(std::ostream &os, IndexType<ValueTypeT, TagT> index) {
+ return os << index.value();
+}
+
+} // namespace draco
+
+// Specialize std::hash for the strongly indexed types.
+namespace std {
+
+template <class ValueTypeT, class TagT>
+struct hash<draco::IndexType<ValueTypeT, TagT>> {
+ size_t operator()(const draco::IndexType<ValueTypeT, TagT> &i) const {
+ return static_cast<size_t>(i.value());
+ }
+};
+
+} // namespace std
+
+#endif // DRACO_CORE_DRACO_INDEX_TYPE_H_
diff --git a/extern/draco/dracoenc/src/draco/core/draco_index_type_vector.h b/extern/draco/dracoenc/src/draco/core/draco_index_type_vector.h
new file mode 100644
index 00000000000..e2062ef3f35
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/draco_index_type_vector.h
@@ -0,0 +1,77 @@
+// 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_INDEX_TYPE_VECTOR_H_
+#define DRACO_CORE_DRACO_INDEX_TYPE_VECTOR_H_
+
+#include <cstddef>
+#include <utility>
+#include <vector>
+
+#include "draco/core/draco_index_type.h"
+
+namespace draco {
+
+// A wrapper around the standard std::vector that supports indexing of the
+// vector entries using the strongly typed indices as defined in
+// draco_index_type.h .
+// TODO(ostava): Make the interface more complete. It's currently missing
+// features such as iterators.
+// TODO(draco-eng): Make unit tests for this class.
+template <class IndexTypeT, class ValueTypeT>
+class IndexTypeVector {
+ public:
+ typedef typename std::vector<ValueTypeT>::const_reference const_reference;
+ typedef typename std::vector<ValueTypeT>::reference reference;
+
+ IndexTypeVector() {}
+ explicit IndexTypeVector(size_t size) : vector_(size) {}
+ IndexTypeVector(size_t size, const ValueTypeT &val) : vector_(size, val) {}
+
+ void clear() { vector_.clear(); }
+ void reserve(size_t size) { vector_.reserve(size); }
+ void resize(size_t size) { vector_.resize(size); }
+ void resize(size_t size, const ValueTypeT &val) { vector_.resize(size, val); }
+ void assign(size_t size, const ValueTypeT &val) { vector_.assign(size, val); }
+
+ void swap(IndexTypeVector<IndexTypeT, ValueTypeT> &arg) {
+ vector_.swap(arg.vector_);
+ }
+
+ size_t size() const { return vector_.size(); }
+
+ void push_back(const ValueTypeT &val) { vector_.push_back(val); }
+ void push_back(ValueTypeT &&val) { vector_.push_back(std::move(val)); }
+
+ inline reference operator[](const IndexTypeT &index) {
+ return vector_[index.value()];
+ }
+ inline const_reference operator[](const IndexTypeT &index) const {
+ return vector_[index.value()];
+ }
+ inline reference at(const IndexTypeT &index) {
+ return vector_[index.value()];
+ }
+ inline const_reference at(const IndexTypeT &index) const {
+ return vector_[index.value()];
+ }
+ const ValueTypeT *data() const { return vector_.data(); }
+
+ private:
+ std::vector<ValueTypeT> vector_;
+};
+
+} // namespace draco
+
+#endif // DRACO_CORE_DRACO_INDEX_TYPE_VECTOR_H_
diff --git a/extern/draco/dracoenc/src/draco/core/draco_test_base.h b/extern/draco/dracoenc/src/draco/core/draco_test_base.h
new file mode 100644
index 00000000000..f5c9d751e03
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/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/extern/draco/dracoenc/src/draco/core/draco_test_utils.cc b/extern/draco/dracoenc/src/draco/core/draco_test_utils.cc
new file mode 100644
index 00000000000..fa225576d9c
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/draco_test_utils.cc
@@ -0,0 +1,83 @@
+// 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 "draco/core/draco_test_utils.h"
+
+#include <fstream>
+
+#include "draco/core/macros.h"
+#include "draco_test_base.h"
+
+namespace draco {
+
+namespace {
+static constexpr char kTestDataDir[] = DRACO_TEST_DATA_DIR;
+static constexpr char kTestTempDir[] = DRACO_TEST_TEMP_DIR;
+} // namespace
+
+std::string GetTestFileFullPath(const std::string &file_name) {
+ return std::string(kTestDataDir) + std::string("/") + file_name;
+}
+
+std::string GetTestTempFileFullPath(const std::string &file_name) {
+ return std::string(kTestTempDir) + std::string("/") + file_name;
+}
+
+bool GenerateGoldenFile(const std::string &golden_file_name, const void *data,
+ int data_size) {
+ const std::string path = GetTestFileFullPath(golden_file_name);
+ std::ofstream file(path, std::ios::binary);
+ if (!file)
+ return false;
+ file.write(static_cast<const char *>(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<const char *>(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/extern/draco/dracoenc/src/draco/core/draco_test_utils.h b/extern/draco/dracoenc/src/draco/core/draco_test_utils.h
new file mode 100644
index 00000000000..2ed93cd960b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/draco_test_utils.h
@@ -0,0 +1,65 @@
+// 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 "draco/core/draco_test_base.h"
+#include "draco/io/mesh_io.h"
+#include "draco/io/point_cloud_io.h"
+
+namespace draco {
+
+// Returns the full path to a given file system entry, such as test file or test
+// directory.
+std::string GetTestFileFullPath(const std::string &entry_name);
+
+// Returns the full path to a given temporary file (a location where tests store
+// generated files).
+std::string GetTestTempFileFullPath(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);
+
+// Loads a mesh / point cloud specified by a |file_name| that is going to be
+// automatically converted to the correct path available to the testing
+// instance.
+inline std::unique_ptr<Mesh> ReadMeshFromTestFile(
+ const std::string &file_name) {
+ const std::string path = GetTestFileFullPath(file_name);
+ return ReadMeshFromFile(path).value();
+}
+inline std::unique_ptr<Mesh> ReadMeshFromTestFile(const std::string &file_name,
+ bool use_metadata) {
+ const std::string path = GetTestFileFullPath(file_name);
+ return ReadMeshFromFile(path, use_metadata).value();
+}
+
+inline std::unique_ptr<PointCloud> ReadPointCloudFromTestFile(
+ const std::string &file_name) {
+ const std::string path = GetTestFileFullPath(file_name);
+ return ReadPointCloudFromFile(path).value();
+}
+
+} // namespace draco
+
+#endif // DRACO_CORE_DRACO_TEST_UTILS_H_
diff --git a/extern/draco/dracoenc/src/draco/core/draco_tests.cc b/extern/draco/dracoenc/src/draco/core/draco_tests.cc
new file mode 100644
index 00000000000..fdaa14da508
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/draco_tests.cc
@@ -0,0 +1,6 @@
+#include "draco/core/draco_test_base.h"
+
+int main(int argc, char *argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/extern/draco/dracoenc/src/draco/core/draco_types.cc b/extern/draco/dracoenc/src/draco/core/draco_types.cc
new file mode 100644
index 00000000000..45b22470057
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/draco_types.cc
@@ -0,0 +1,44 @@
+// 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 "draco/core/draco_types.h"
+
+namespace draco {
+
+int32_t DataTypeLength(DataType dt) {
+ switch (dt) {
+ case DT_INT8:
+ case DT_UINT8:
+ return 1;
+ case DT_INT16:
+ case DT_UINT16:
+ return 2;
+ case DT_INT32:
+ case DT_UINT32:
+ return 4;
+ case DT_INT64:
+ case DT_UINT64:
+ return 8;
+ case DT_FLOAT32:
+ return 4;
+ case DT_FLOAT64:
+ return 8;
+ case DT_BOOL:
+ return 1;
+ default:
+ return -1;
+ }
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/core/draco_types.h b/extern/draco/dracoenc/src/draco/core/draco_types.h
new file mode 100644
index 00000000000..4a34d7045a3
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/draco_types.h
@@ -0,0 +1,46 @@
+// 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_TYPES_H_
+#define DRACO_CORE_DRACO_TYPES_H_
+
+#include <stdint.h>
+#include <string>
+
+#include "draco/draco_features.h"
+
+namespace draco {
+
+enum DataType {
+ // Not a legal value for DataType. Used to indicate a field has not been set.
+ DT_INVALID = 0,
+ DT_INT8,
+ DT_UINT8,
+ DT_INT16,
+ DT_UINT16,
+ DT_INT32,
+ DT_UINT32,
+ DT_INT64,
+ DT_UINT64,
+ DT_FLOAT32,
+ DT_FLOAT64,
+ DT_BOOL,
+ DT_TYPES_COUNT
+};
+
+int32_t DataTypeLength(DataType dt);
+
+} // namespace draco
+
+#endif // DRACO_CORE_DRACO_TYPES_H_
diff --git a/extern/draco/dracoenc/src/draco/core/draco_version.h b/extern/draco/dracoenc/src/draco/core/draco_version.h
new file mode 100644
index 00000000000..45dce22a1d8
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/draco_version.h
@@ -0,0 +1,27 @@
+// 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_VERSION_H_
+#define DRACO_CORE_DRACO_VERSION_H_
+
+namespace draco {
+
+// Draco version is comprised of <major>.<minor>.<revision>.
+static const char kDracoVersion[] = "1.3.4";
+
+const char *Version() { return kDracoVersion; }
+
+} // namespace draco
+
+#endif // DRACO_CORE_DRACO_VERSION_H_
diff --git a/extern/draco/dracoenc/src/draco/core/encoder_buffer.cc b/extern/draco/dracoenc/src/draco/core/encoder_buffer.cc
new file mode 100644
index 00000000000..a5e936fd89b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/encoder_buffer.cc
@@ -0,0 +1,90 @@
+// 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 "draco/core/encoder_buffer.h"
+
+#include <cstring> // for memcpy
+
+#include "draco/core/varint_encoding.h"
+
+namespace draco {
+
+EncoderBuffer::EncoderBuffer()
+ : bit_encoder_reserved_bytes_(false), encode_bit_sequence_size_(false) {}
+
+void EncoderBuffer::Clear() {
+ buffer_.clear();
+ bit_encoder_reserved_bytes_ = 0;
+}
+
+void EncoderBuffer::Resize(int64_t nbytes) { buffer_.resize(nbytes); }
+
+bool EncoderBuffer::StartBitEncoding(int64_t required_bits, bool encode_size) {
+ if (bit_encoder_active())
+ return false; // Bit encoding mode already active.
+ if (required_bits <= 0)
+ return false; // Invalid size.
+ encode_bit_sequence_size_ = encode_size;
+ const int64_t required_bytes = (required_bits + 7) / 8;
+ bit_encoder_reserved_bytes_ = required_bytes;
+ uint64_t buffer_start_size = buffer_.size();
+ if (encode_size) {
+ // Reserve memory for storing the encoded bit sequence size. It will be
+ // filled once the bit encoding ends.
+ buffer_start_size += sizeof(uint64_t);
+ }
+ // Resize buffer to fit the maximum size of encoded bit data.
+ 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<BitEncoder>(new BitEncoder(const_cast<char *>(data)));
+ return true;
+}
+
+void EncoderBuffer::EndBitEncoding() {
+ if (!bit_encoder_active())
+ return;
+ // Get the number of encoded bits and bytes (rounded up).
+ const uint64_t encoded_bits = bit_encoder_->Bits();
+ const uint64_t encoded_bytes = (encoded_bits + 7) / 8;
+ // Flush all cached bits that are not in the bit encoder's main buffer.
+ bit_encoder_->Flush(0);
+ // Encode size if needed.
+ if (encode_bit_sequence_size_) {
+ char *out_mem = const_cast<char *>(data() + size());
+ // Make the out_mem point to the memory reserved for storing the size.
+ out_mem = out_mem - (bit_encoder_reserved_bytes_ + sizeof(uint64_t));
+
+ EncoderBuffer var_size_buffer;
+ EncodeVarint(encoded_bytes, &var_size_buffer);
+ const uint32_t size_len = static_cast<uint32_t>(var_size_buffer.size());
+ char *const dst = out_mem + size_len;
+ const char *const src = out_mem + sizeof(uint64_t);
+ memmove(dst, src, encoded_bytes);
+
+ // Store the size of the encoded data.
+ memcpy(out_mem, var_size_buffer.data(), size_len);
+
+ // We need to account for the difference between the preallocated and actual
+ // storage needed for storing the encoded length. This will be used later to
+ // compute the correct size of |buffer_|.
+ bit_encoder_reserved_bytes_ += sizeof(uint64_t) - size_len;
+ }
+ // Resize the underlying buffer to match the number of encoded bits.
+ buffer_.resize(buffer_.size() - bit_encoder_reserved_bytes_ + encoded_bytes);
+ bit_encoder_reserved_bytes_ = 0;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/core/encoder_buffer.h b/extern/draco/dracoenc/src/draco/core/encoder_buffer.h
new file mode 100644
index 00000000000..ff3e89ba270
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/encoder_buffer.h
@@ -0,0 +1,148 @@
+// 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_ENCODER_BUFFER_H_
+#define DRACO_CORE_ENCODER_BUFFER_H_
+
+#include <memory>
+#include <vector>
+
+#include "draco/core/bit_utils.h"
+#include "draco/core/macros.h"
+
+namespace draco {
+
+// Class representing a buffer that can be used for either for byte-aligned
+// encoding of arbitrary data structures or for encoding of variable-length
+// bit data.
+class EncoderBuffer {
+ public:
+ EncoderBuffer();
+ void Clear();
+ void Resize(int64_t nbytes);
+
+ // Start encoding a bit sequence. A maximum size of the sequence needs to
+ // be known upfront.
+ // If encode_size is true, the size of encoded bit sequence is stored before
+ // the sequence. Decoder can then use this size to skip over the bit sequence
+ // if needed.
+ // Returns false on error.
+ bool StartBitEncoding(int64_t required_bits, bool encode_size);
+
+ // End the encoding of the bit sequence and return to the default byte-aligned
+ // encoding.
+ void EndBitEncoding();
+
+ // Encode up to 32 bits into the buffer. Can be called only in between
+ // StartBitEncoding and EndBitEncoding. Otherwise returns false.
+ bool EncodeLeastSignificantBits32(int nbits, uint32_t value) {
+ if (!bit_encoder_active())
+ return false;
+ bit_encoder_->PutBits(value, nbits);
+ return true;
+ }
+ // 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.
+ template <typename T>
+ bool Encode(const T &data) {
+ if (bit_encoder_active())
+ return false;
+ const uint8_t *src_data = reinterpret_cast<const uint8_t *>(&data);
+ buffer_.insert(buffer_.end(), src_data, src_data + sizeof(T));
+ return true;
+ }
+ bool Encode(const void *data, size_t data_size) {
+ if (bit_encoder_active())
+ return false;
+ const uint8_t *src_data = reinterpret_cast<const uint8_t *>(data);
+ buffer_.insert(buffer_.end(), src_data, src_data + data_size);
+ return true;
+ }
+
+ bool bit_encoder_active() const { return bit_encoder_reserved_bytes_ > 0; }
+ const char *data() const { return buffer_.data(); }
+ size_t size() const { return buffer_.size(); }
+ std::vector<char> *buffer() { return &buffer_; }
+
+ private:
+ // Internal helper class to encode bits to a bit buffer.
+ class BitEncoder {
+ public:
+ // |data| is the buffer to write the bits into.
+ explicit BitEncoder(char *data) : bit_buffer_(data), bit_offset_(0) {}
+
+ // Write |nbits| of |data| into the bit buffer.
+ void PutBits(uint32_t data, int32_t nbits) {
+ DRACO_DCHECK_GE(nbits, 0);
+ DRACO_DCHECK_LE(nbits, 32);
+ for (int32_t bit = 0; bit < nbits; ++bit)
+ PutBit((data >> bit) & 1);
+ }
+
+ // Return number of bits encoded so far.
+ uint64_t Bits() const { return static_cast<uint64_t>(bit_offset_); }
+
+ // 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 */) {}
+
+ // Return the number of bits required to store the given number
+ static uint32_t BitsRequired(uint32_t x) {
+ return static_cast<uint32_t>(MostSignificantBit(x));
+ }
+
+ private:
+ void PutBit(uint8_t value) {
+ const int byte_size = 8;
+ const uint64_t off = static_cast<uint64_t>(bit_offset_);
+ const uint64_t byte_offset = off / byte_size;
+ const int bit_shift = off % byte_size;
+
+ // TODO(fgalligan): Check performance if we add a branch and only do one
+ // memory write if bit_shift is 7. Also try using a temporary variable to
+ // hold the bits before writing to the buffer.
+
+ bit_buffer_[byte_offset] &= ~(1 << bit_shift);
+ bit_buffer_[byte_offset] |= value << bit_shift;
+ bit_offset_++;
+ }
+
+ char *bit_buffer_;
+ size_t bit_offset_;
+ };
+ friend class BufferBitCodingTest;
+ // All data is stored in this vector.
+ std::vector<char> buffer_;
+
+ // Bit encoder is used when encoding variable-length bit data.
+ // TODO(ostava): Currently encoder needs to be recreated each time
+ // StartBitEncoding method is called. This is not necessary if BitEncoder
+ // supported reset function which can easily added but let's leave that for
+ // later.
+ std::unique_ptr<BitEncoder> bit_encoder_;
+
+ // The number of bytes reserved for bit encoder.
+ // Values > 0 indicate we are in the bit encoding mode.
+ int64_t bit_encoder_reserved_bytes_;
+
+ // Flag used indicating that we need to store the length of the currently
+ // processed bit sequence.
+ bool encode_bit_sequence_size_;
+};
+
+} // namespace draco
+
+#endif // DRACO_CORE_ENCODER_BUFFER_H_
diff --git a/extern/draco/dracoenc/src/draco/core/hash_utils.cc b/extern/draco/dracoenc/src/draco/core/hash_utils.cc
new file mode 100644
index 00000000000..9a78c2cfafd
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/hash_utils.cc
@@ -0,0 +1,57 @@
+// 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 "draco/core/hash_utils.h"
+
+#include <cstddef>
+#include <functional>
+#include <limits>
+
+namespace draco {
+
+// Will never return 1 or 0.
+uint64_t FingerprintString(const char *s, size_t len) {
+ const uint64_t seed = 0x87654321;
+ const int hash_loop_count = static_cast<int>(len / 8) + 1;
+ uint64_t hash = seed;
+
+ for (int i = 0; i < hash_loop_count; ++i) {
+ const int off = i * 8;
+ const int num_chars_left = static_cast<int>(len) - off;
+ uint64_t new_hash = seed;
+
+ if (num_chars_left > 7) {
+ const int off2 = i * 8;
+ new_hash = static_cast<uint64_t>(s[off2]) << 56 |
+ static_cast<uint64_t>(s[off2 + 1]) << 48 |
+ static_cast<uint64_t>(s[off2 + 2]) << 40 |
+ static_cast<uint64_t>(s[off2 + 3]) << 32 |
+ static_cast<uint64_t>(s[off2 + 4]) << 24 |
+ static_cast<uint64_t>(s[off2 + 5]) << 16 |
+ static_cast<uint64_t>(s[off2 + 6]) << 8 | s[off2 + 7];
+ } else {
+ for (int j = 0; j < num_chars_left; ++j) {
+ new_hash |= static_cast<uint64_t>(s[off + j])
+ << (64 - ((num_chars_left - j) * 8));
+ }
+ }
+
+ hash = HashCombine(new_hash, hash);
+ }
+
+ if (hash < std::numeric_limits<uint64_t>::max() - 1)
+ hash += 2;
+ return hash;
+}
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/core/hash_utils.h b/extern/draco/dracoenc/src/draco/core/hash_utils.h
new file mode 100644
index 00000000000..0e8da60aa54
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/hash_utils.h
@@ -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.
+//
+#ifndef DRACO_CORE_HASH_UTILS_H_
+#define DRACO_CORE_HASH_UTILS_H_
+
+#include <stdint.h>
+#include <functional>
+
+// TODO(fgalligan): Move this to core.
+
+namespace draco {
+
+template <typename T1, typename T2>
+size_t HashCombine(T1 a, T2 b) {
+ const size_t hash1 = std::hash<T1>()(a);
+ const size_t hash2 = std::hash<T2>()(b);
+ return (hash1 << 2) ^ (hash2 << 1);
+}
+
+template <typename T>
+size_t HashCombine(T a, size_t hash) {
+ const size_t hasha = std::hash<T>()(a);
+ return (hash) ^ (hasha + 239);
+}
+
+inline uint64_t HashCombine(uint64_t a, uint64_t b) {
+ return (a + 1013) ^ (b + 107) << 1;
+}
+
+// Will never return 1 or 0.
+uint64_t FingerprintString(const char *s, size_t len);
+
+// Hash for std::array.
+template <typename T>
+struct HashArray {
+ size_t operator()(const T &a) const {
+ size_t hash = 79; // Magic number.
+ for (unsigned int i = 0; i < std::tuple_size<T>::value; ++i) {
+ hash = HashCombine(hash, ValueHash(a[i]));
+ }
+ return hash;
+ }
+
+ template <typename V>
+ size_t ValueHash(const V &val) const {
+ return std::hash<V>()(val);
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_CORE_HASH_UTILS_H_
diff --git a/extern/draco/dracoenc/src/draco/core/macros.h b/extern/draco/dracoenc/src/draco/core/macros.h
new file mode 100644
index 00000000000..e968cbb330b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/macros.h
@@ -0,0 +1,96 @@
+// 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_MACROS_H_
+#define DRACO_CORE_MACROS_H_
+
+#include "assert.h"
+
+#include "draco/draco_features.h"
+
+#ifdef ANDROID_LOGGING
+#include <android/log.h>
+#define LOG_TAG "draco"
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+#else
+#define LOGI printf
+#define LOGE printf
+#endif
+
+#include <iostream>
+namespace draco {
+
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName &) = delete; \
+ void operator=(const TypeName &) = delete;
+
+#ifndef FALLTHROUGH_INTENDED
+#define FALLTHROUGH_INTENDED void(0);
+#endif
+
+#ifndef LOG
+#define LOG(...) std::cout
+#endif
+
+#ifndef VLOG
+#define VLOG(...) std::cout
+#endif
+
+} // namespace draco
+
+#ifdef DRACO_DEBUG
+#define DRACO_DCHECK(x) (assert(x));
+#define DRACO_DCHECK_EQ(a, b) assert((a) == (b));
+#define DRACO_DCHECK_NE(a, b) assert((a) != (b));
+#define DRACO_DCHECK_GE(a, b) assert((a) >= (b));
+#define DRACO_DCHECK_GT(a, b) assert((a) > (b));
+#define DRACO_DCHECK_LE(a, b) assert((a) <= (b));
+#define DRACO_DCHECK_LT(a, b) assert((a) < (b));
+#define DRACO_DCHECK_NOTNULL(x) assert((x) != NULL);
+#else
+#define DRACO_DCHECK(x)
+#define DRACO_DCHECK_EQ(a, b)
+#define DRACO_DCHECK_NE(a, b)
+#define DRACO_DCHECK_GE(a, b)
+#define DRACO_DCHECK_GT(a, b)
+#define DRACO_DCHECK_LE(a, b)
+#define DRACO_DCHECK_LT(a, b)
+#define DRACO_DCHECK_NOTNULL(x)
+#endif
+
+// Helper macros for concatenating macro values.
+#define DRACO_MACROS_IMPL_CONCAT_INNER_(x, y) x##y
+#define DRACO_MACROS_IMPL_CONCAT_(x, y) DRACO_MACROS_IMPL_CONCAT_INNER_(x, y)
+
+// Expand the n-th argument of the macro. Used to select an argument based on
+// the number of entries in a variadic macro argument. Example usage:
+//
+// #define FUNC_1(x) x
+// #define FUNC_2(x, y) x + y
+// #define FUNC_3(x, y, z) x + y + z
+//
+// #define VARIADIC_MACRO(...)
+// DRACO_SELECT_NTH_FROM_3(__VA_ARGS__, FUNC_3, FUNC_2, FUNC_1) __VA_ARGS__
+//
+#define DRACO_SELECT_NTH_FROM_2(_1, _2, NAME) NAME
+#define DRACO_SELECT_NTH_FROM_3(_1, _2, _3, NAME) NAME
+#define DRACO_SELECT_NTH_FROM_4(_1, _2, _3, _4, NAME) NAME
+
+// Macro that converts the Draco bit-stream into one uint16_t number.
+// Useful mostly when checking version numbers.
+#define DRACO_BITSTREAM_VERSION(MAJOR, MINOR) \
+ ((static_cast<uint16_t>(MAJOR) << 8) | MINOR)
+
+#endif // DRACO_CORE_MACROS_H_
diff --git a/extern/draco/dracoenc/src/draco/core/math_utils.h b/extern/draco/dracoenc/src/draco/core/math_utils.h
new file mode 100644
index 00000000000..6bf237d4560
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/math_utils.h
@@ -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.
+//
+#ifndef DRACO_CORE_MATH_UTILS_H_
+#define DRACO_CORE_MATH_UTILS_H_
+
+#include <inttypes.h>
+
+#define DRACO_INCREMENT_MOD(I, M) (((I) == ((M)-1)) ? 0 : ((I) + 1))
+
+// Returns floor(sqrt(x)) where x is an integer number. The main intend of this
+// function is to provide a cross platform and deterministic implementation of
+// square root for integer numbers. This function is not intended to be a
+// replacement for std::sqrt() for general cases. IntSqrt is in fact about 3X
+// slower compared to most implementation of std::sqrt().
+inline uint64_t IntSqrt(uint64_t number) {
+ if (number == 0)
+ return 0;
+ // First estimate good initial value of the square root as log2(number).
+ uint64_t act_number = number;
+ uint64_t square_root = 1;
+ while (act_number >= 2) {
+ // Double the square root until |square_root * square_root > number|.
+ square_root *= 2;
+ act_number /= 4;
+ }
+ // Perform Newton's (or Babylonian) method to find the true floor(sqrt()).
+ do {
+ // New |square_root| estimate is computed as the average between
+ // |square_root| and |number / square_root|.
+ square_root = (square_root + number / square_root) / 2;
+
+ // Note that after the first iteration, the estimate is always going to be
+ // larger or equal to the true square root value. Therefore to check
+ // convergence, we can simply detect condition when the square of the
+ // estimated square root is larger than the input.
+ } while (square_root * square_root > number);
+ return square_root;
+}
+
+#endif // DRACO_CORE_MATH_UTILS_H_
diff --git a/extern/draco/dracoenc/src/draco/core/math_utils_test.cc b/extern/draco/dracoenc/src/draco/core/math_utils_test.cc
new file mode 100644
index 00000000000..b12b3431e66
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/math_utils_test.cc
@@ -0,0 +1,19 @@
+#include "draco/core/math_utils.h"
+
+#include <random>
+
+#include "draco/core/draco_test_base.h"
+
+TEST(MathUtils, Mod) { EXPECT_EQ(DRACO_INCREMENT_MOD(1, 1 << 1), 0); }
+
+TEST(MathUtils, IntSqrt) {
+ ASSERT_EQ(IntSqrt(0), 0);
+ // 64-bit pseudo random number generator seeded with a predefined number.
+ std::mt19937_64 generator(109);
+ std::uniform_int_distribution<uint64_t> distribution(0, 1ull << 60);
+
+ for (int i = 0; i < 10000; ++i) {
+ const uint64_t number = distribution(generator);
+ ASSERT_EQ(IntSqrt(number), static_cast<uint64_t>(floor(std::sqrt(number))));
+ }
+}
diff --git a/extern/draco/dracoenc/src/draco/core/options.cc b/extern/draco/dracoenc/src/draco/core/options.cc
new file mode 100644
index 00000000000..c4f6d6a66aa
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/options.cc
@@ -0,0 +1,83 @@
+// 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 "draco/core/options.h"
+
+#include <cstdlib>
+#include <string>
+
+namespace draco {
+
+Options::Options() {}
+
+void Options::SetInt(const std::string &name, int val) {
+ options_[name] = std::to_string(val);
+}
+
+void Options::SetFloat(const std::string &name, float val) {
+ options_[name] = std::to_string(val);
+}
+
+void Options::SetBool(const std::string &name, bool val) {
+ options_[name] = std::to_string(val ? 1 : 0);
+}
+
+void Options::SetString(const std::string &name, const std::string &val) {
+ options_[name] = val;
+}
+
+int Options::GetInt(const std::string &name) const { return GetInt(name, -1); }
+
+int Options::GetInt(const std::string &name, int default_val) const {
+ const auto it = options_.find(name);
+ if (it == options_.end())
+ return default_val;
+ return std::atoi(it->second.c_str());
+}
+
+float Options::GetFloat(const std::string &name) const {
+ return GetFloat(name, -1);
+}
+
+float Options::GetFloat(const std::string &name, float default_val) const {
+ const auto it = options_.find(name);
+ if (it == options_.end())
+ return default_val;
+ return static_cast<float>(std::atof(it->second.c_str()));
+}
+
+bool Options::GetBool(const std::string &name) const {
+ return GetBool(name, false);
+}
+
+bool Options::GetBool(const std::string &name, bool default_val) const {
+ const int ret = GetInt(name, -1);
+ if (ret == -1)
+ return default_val;
+ return static_cast<bool>(ret);
+}
+
+std::string Options::GetString(const std::string &name) const {
+ return GetString(name, "");
+}
+
+std::string Options::GetString(const std::string &name,
+ const std::string &default_val) const {
+ const auto it = options_.find(name);
+ if (it == options_.end())
+ return default_val;
+ return it->second;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/core/options.h b/extern/draco/dracoenc/src/draco/core/options.h
new file mode 100644
index 00000000000..0e69844911d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/options.h
@@ -0,0 +1,140 @@
+// 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_OPTIONS_H_
+#define DRACO_CORE_OPTIONS_H_
+
+#include <cstdlib>
+#include <map>
+#include <string>
+
+namespace draco {
+
+// Class for storing generic options as a <name, value> pair in a string map.
+// The API provides helper methods for directly storing values of various types
+// such as ints and bools. One named option should be set with only a single
+// data type.
+class Options {
+ public:
+ Options();
+ void SetInt(const std::string &name, int val);
+ void SetFloat(const std::string &name, float val);
+ void SetBool(const std::string &name, bool val);
+ void SetString(const std::string &name, const std::string &val);
+ template <class VectorT>
+ void SetVector(const std::string &name, const VectorT &vec) {
+ SetVector(name, &vec[0], VectorT::dimension);
+ }
+ template <typename DataTypeT>
+ void SetVector(const std::string &name, const DataTypeT *vec, int num_dims);
+
+ // Getters will return a default value if the entry is not found. The default
+ // value can be specified in the overloaded version of each function.
+ int GetInt(const std::string &name) const;
+ int GetInt(const std::string &name, int default_val) const;
+ float GetFloat(const std::string &name) const;
+ float GetFloat(const std::string &name, float default_val) const;
+ bool GetBool(const std::string &name) const;
+ bool GetBool(const std::string &name, bool default_val) const;
+ std::string GetString(const std::string &name) const;
+ std::string GetString(const std::string &name,
+ const std::string &default_val) const;
+ template <class VectorT>
+ VectorT GetVector(const std::string &name, const VectorT &default_val) const;
+ // Unlike other Get functions, this function returns false if the option does
+ // not exist, otherwise it fills |out_val| with the vector values. If a
+ // default value is needed, it can be set in |out_val|.
+ template <typename DataTypeT>
+ bool GetVector(const std::string &name, int num_dims,
+ DataTypeT *out_val) const;
+
+ bool IsOptionSet(const std::string &name) const {
+ return options_.count(name) > 0;
+ }
+
+ private:
+ // All entries are internally stored as strings and converted to the desired
+ // return type based on the used Get* method.
+ // TODO(ostava): Consider adding type safety mechanism that would prevent
+ // unsafe operations such as a conversion from vector to int.
+ std::map<std::string, std::string> options_;
+};
+
+template <typename DataTypeT>
+void Options::SetVector(const std::string &name, const DataTypeT *vec,
+ int num_dims) {
+ std::string out;
+ for (int i = 0; i < num_dims; ++i) {
+ if (i > 0)
+ out += " ";
+
+// GNU STL on android doesn't include a proper std::to_string, but the libc++
+// version does
+#if defined(ANDROID) && !defined(_LIBCPP_VERSION)
+ out += to_string(vec[i]);
+#else
+ out += std::to_string(vec[i]);
+#endif
+ }
+ options_[name] = out;
+}
+
+template <class VectorT>
+VectorT Options::GetVector(const std::string &name,
+ const VectorT &default_val) const {
+ VectorT ret = default_val;
+ GetVector(name, VectorT::dimension, &ret[0]);
+ return ret;
+}
+
+template <typename DataTypeT>
+bool Options::GetVector(const std::string &name, int num_dims,
+ DataTypeT *out_val) const {
+ const auto it = options_.find(name);
+ if (it == options_.end())
+ return false;
+ const std::string value = it->second;
+ if (value.length() == 0)
+ return true; // Option set but no data is present
+ const char *act_str = value.c_str();
+ char *next_str;
+ for (int i = 0; i < num_dims; ++i) {
+ if (std::is_integral<DataTypeT>::value) {
+#ifdef ANDROID
+ const int val = strtol(act_str, &next_str, 10);
+#else
+ const int val = std::strtol(act_str, &next_str, 10);
+#endif
+ if (act_str == next_str)
+ return true; // End reached.
+ act_str = next_str;
+ out_val[i] = static_cast<DataTypeT>(val);
+ } else {
+#ifdef ANDROID
+ const float val = strtof(act_str, &next_str);
+#else
+ const float val = std::strtof(act_str, &next_str);
+#endif
+ if (act_str == next_str)
+ return true; // End reached.
+ act_str = next_str;
+ out_val[i] = static_cast<DataTypeT>(val);
+ }
+ }
+ return true;
+}
+
+} // namespace draco
+
+#endif // DRACO_CORE_OPTIONS_H_
diff --git a/extern/draco/dracoenc/src/draco/core/quantization_utils.cc b/extern/draco/dracoenc/src/draco/core/quantization_utils.cc
new file mode 100644
index 00000000000..26417b01998
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/quantization_utils.cc
@@ -0,0 +1,41 @@
+// 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 "draco/core/quantization_utils.h"
+
+namespace draco {
+
+Quantizer::Quantizer() : inverse_delta_(1.f) {}
+
+void Quantizer::Init(float range, int32_t max_quantized_value) {
+ inverse_delta_ = static_cast<float>(max_quantized_value) / range;
+}
+
+void Quantizer::Init(float delta) { inverse_delta_ = 1.f / delta; }
+
+Dequantizer::Dequantizer() : delta_(1.f) {}
+
+bool Dequantizer::Init(float range, int32_t max_quantized_value) {
+ if (max_quantized_value <= 0)
+ return false;
+ delta_ = range / static_cast<float>(max_quantized_value);
+ return true;
+}
+
+bool Dequantizer::Init(float delta) {
+ delta_ = delta;
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/core/quantization_utils.h b/extern/draco/dracoenc/src/draco/core/quantization_utils.h
new file mode 100644
index 00000000000..54910467407
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/quantization_utils.h
@@ -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.
+//
+// A set of classes for quantizing and dequantizing of floating point values
+// into integers.
+// The quantization works on all floating point numbers within (-range, +range)
+// interval producing integers in range
+// (-max_quantized_value, +max_quantized_value).
+
+#ifndef DRACO_CORE_QUANTIZATION_UTILS_H_
+#define DRACO_CORE_QUANTIZATION_UTILS_H_
+
+#include <stdint.h>
+#include <cmath>
+
+#include "draco/core/macros.h"
+
+namespace draco {
+
+// Class for quantizing single precision floating point values. The values
+// should be centered around zero and be within interval (-range, +range), where
+// the range is specified in the Init() method. Alternatively, the quantization
+// can be defined by |delta| that specifies the distance between two quantized
+// values. Note that the quantizer always snaps the values to the nearest
+// integer value. E.g. for |delta| == 1.f, values -0.4f and 0.4f would be
+// both quantized to 0 while value 0.6f would be quantized to 1. If a value
+// lies exactly between two quantized states, it is always rounded up. E.g.,
+// for |delta| == 1.f, value -0.5f would be quantized to 0 while 0.5f would be
+// quantized to 1.
+class Quantizer {
+ public:
+ Quantizer();
+ void Init(float range, int32_t max_quantized_value);
+ void Init(float delta);
+ inline int32_t QuantizeFloat(float val) const {
+ val *= inverse_delta_;
+ return static_cast<int32_t>(floor(val + 0.5f));
+ }
+ inline int32_t operator()(float val) const { return QuantizeFloat(val); }
+
+ private:
+ float inverse_delta_;
+};
+
+// Class for dequantizing values that were previously quantized using the
+// Quantizer class.
+class Dequantizer {
+ public:
+ Dequantizer();
+
+ // Initializes the dequantizer. Both parameters must correspond to the values
+ // provided to the initializer of the Quantizer class.
+ // Returns false when the initialization fails.
+ bool Init(float range, int32_t max_quantized_value);
+
+ // Initializes the dequantizer using the |delta| between two quantized values.
+ bool Init(float delta);
+
+ inline float DequantizeFloat(int32_t val) const {
+ return static_cast<float>(val) * delta_;
+ }
+ inline float operator()(int32_t val) const { return DequantizeFloat(val); }
+
+ private:
+ float delta_;
+};
+
+} // namespace draco
+
+#endif // DRACO_CORE_QUANTIZATION_UTILS_H_
diff --git a/extern/draco/dracoenc/src/draco/core/quantization_utils_test.cc b/extern/draco/dracoenc/src/draco/core/quantization_utils_test.cc
new file mode 100644
index 00000000000..b4f0473f204
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/quantization_utils_test.cc
@@ -0,0 +1,91 @@
+// 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 "draco/core/quantization_utils.h"
+
+#include "draco/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);
+ // Note: Both -5.f and +5.f lie exactly on the boundary between two
+ // quantized values (127.5f and -127.5f). Due to rounding, both values are
+ // then converted to 128 and -127 respectively.
+ EXPECT_EQ(quantizer.QuantizeFloat(-5.f), -127);
+ 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;
+ ASSERT_TRUE(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));
+
+ // Test that the dequantizer fails to initialize with invalid input
+ // parameters.
+ ASSERT_FALSE(dequantizer.Init(1.f, 0));
+ ASSERT_FALSE(dequantizer.Init(1.f, -4));
+}
+
+TEST_F(QuantizationUtilsTest, TestDeltaQuantization) {
+ // Test verifies that the quantizer and dequantizer work correctly when
+ // initialized with a delta value.
+ Quantizer quantizer_delta;
+ quantizer_delta.Init(0.5f);
+
+ Quantizer quantizer_range;
+ quantizer_range.Init(50.f, 100);
+
+ EXPECT_EQ(quantizer_delta.QuantizeFloat(1.2f), 2);
+ EXPECT_EQ(quantizer_delta.QuantizeFloat(10.f),
+ quantizer_range.QuantizeFloat(10.f));
+ EXPECT_EQ(quantizer_delta.QuantizeFloat(-3.3f),
+ quantizer_range.QuantizeFloat(-3.3f));
+ EXPECT_EQ(quantizer_delta.QuantizeFloat(0.25f),
+ quantizer_range.QuantizeFloat(0.25f));
+
+ Dequantizer dequantizer_delta;
+ dequantizer_delta.Init(0.5f);
+
+ Dequantizer dequantizer_range;
+ dequantizer_range.Init(50.f, 100);
+
+ EXPECT_EQ(dequantizer_delta.DequantizeFloat(2), 1.f);
+ EXPECT_EQ(dequantizer_delta.DequantizeFloat(-4),
+ dequantizer_range.DequantizeFloat(-4));
+ EXPECT_EQ(dequantizer_delta.DequantizeFloat(9),
+ dequantizer_range.DequantizeFloat(9));
+ EXPECT_EQ(dequantizer_delta.DequantizeFloat(0),
+ dequantizer_range.DequantizeFloat(0));
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/core/status.h b/extern/draco/dracoenc/src/draco/core/status.h
new file mode 100644
index 00000000000..0a483f09369
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/status.h
@@ -0,0 +1,75 @@
+// Copyright 2017 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_STATUS_H_
+#define DRACO_CORE_STATUS_H_
+
+#include <string>
+
+namespace draco {
+
+// Class encapsulating a return status of an operation with an optional error
+// message. Intended to be used as a return type for functions instead of bool.
+class Status {
+ public:
+ enum Code {
+ OK = 0,
+ ERROR = -1, // Used for general errors.
+ IO_ERROR = -2, // Error when handling input or output stream.
+ INVALID_PARAMETER = -3, // Invalid parameter passed to a function.
+ UNSUPPORTED_VERSION = -4, // Input not compatible with the current version.
+ UNKNOWN_VERSION = -5, // Input was created with an unknown version of
+ // the library.
+ };
+
+ Status() : code_(OK) {}
+ Status(const Status &status) = default;
+ Status(Status &&status) = default;
+ explicit Status(Code code) : code_(code) {}
+ Status(Code code, const std::string &error_msg)
+ : code_(code), error_msg_(error_msg) {}
+
+ Code code() const { return code_; }
+ const std::string &error_msg_string() const { return error_msg_; }
+ const char *error_msg() const { return error_msg_.c_str(); }
+
+ bool operator==(Code code) const { return code == code_; }
+ bool ok() const { return code_ == OK; }
+
+ Status &operator=(const Status &) = default;
+
+ private:
+ Code code_;
+ std::string error_msg_;
+};
+
+inline std::ostream &operator<<(std::ostream &os, const Status &status) {
+ os << status.error_msg_string();
+ return os;
+}
+
+inline Status OkStatus() { return Status(Status::OK); }
+
+// Evaluates an expression that returns draco::Status. If the status is not OK,
+// the macro returns the status object.
+#define DRACO_RETURN_IF_ERROR(expression) \
+ { \
+ const draco::Status _local_status = (expression); \
+ if (!_local_status.ok()) \
+ return _local_status; \
+ }
+
+} // namespace draco
+
+#endif // DRACO_CORE_STATUS_H_
diff --git a/extern/draco/dracoenc/src/draco/core/status_test.cc b/extern/draco/dracoenc/src/draco/core/status_test.cc
new file mode 100644
index 00000000000..451ebe2bfab
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/status_test.cc
@@ -0,0 +1,38 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/core/status.h"
+
+#include <sstream>
+
+#include "draco/core/draco_test_base.h"
+
+namespace {
+
+class StatusTest : public ::testing::Test {
+ protected:
+ StatusTest() {}
+};
+
+TEST_F(StatusTest, TestStatusOutput) {
+ // Tests that the Status can be stored in a provided std::ostream.
+ const draco::Status status(draco::Status::ERROR, "Error msg.");
+ ASSERT_EQ(status.code(), draco::Status::ERROR);
+
+ std::stringstream str;
+ str << status;
+ ASSERT_EQ(str.str(), "Error msg.");
+}
+
+} // namespace
diff --git a/extern/draco/dracoenc/src/draco/core/statusor.h b/extern/draco/dracoenc/src/draco/core/statusor.h
new file mode 100644
index 00000000000..7fa42098442
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/statusor.h
@@ -0,0 +1,81 @@
+// Copyright 2017 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_STATUSOR_H_
+#define DRACO_CORE_STATUSOR_H_
+
+#include "draco/core/macros.h"
+#include "draco/core/status.h"
+
+namespace draco {
+
+// Class StatusOr is used to wrap a Status along with a value of a specified
+// type |T|. StatusOr is intended to be returned from functions in situations
+// where it is desirable to carry over more information about the potential
+// errors encountered during the function execution. If there are not errors,
+// the caller can simply use the return value, otherwise the Status object
+// provides more info about the encountered problem.
+template <class T>
+class StatusOr {
+ public:
+ StatusOr() {}
+ // Note: Constructors are intentionally not explicit to allow returning
+ // Status or the return value directly from functions.
+ StatusOr(const StatusOr &) = default;
+ StatusOr(StatusOr &&) = default;
+ StatusOr(const Status &status) : status_(status) {}
+ StatusOr(const T &value) : status_(OkStatus()), value_(value) {}
+ StatusOr(T &&value) : status_(OkStatus()), value_(std::move(value)) {}
+ StatusOr(const Status &status, const T &value)
+ : status_(status), value_(value) {}
+
+ const Status &status() const { return status_; }
+ const T &value() const & { return value_; }
+ const T &&value() const && { return std::move(value_); }
+ T &&value() && { return std::move(value_); }
+
+ // For consistency with existing Google StatusOr API we also include
+ // ValueOrDie() that currently returns the value().
+ const T &ValueOrDie() const & { return value(); }
+ T &&ValueOrDie() && { return std::move(value()); }
+
+ bool ok() const { return status_.ok(); }
+
+ private:
+ Status status_;
+ T value_;
+};
+
+// In case StatusOr<T> is ok(), this macro assigns value stored in StatusOr<T>
+// to |lhs|, otherwise it returns the error Status.
+//
+// DRACO_ASSIGN_OR_RETURN(lhs, expression)
+//
+#define DRACO_ASSIGN_OR_RETURN(lhs, expression) \
+ DRACO_ASSIGN_OR_RETURN_IMPL_(DRACO_MACROS_IMPL_CONCAT_(_statusor, __LINE__), \
+ lhs, expression, _status)
+
+// The actual implementation of the above macro.
+#define DRACO_ASSIGN_OR_RETURN_IMPL_(statusor, lhs, expression, error_expr) \
+ auto statusor = (expression); \
+ if (!statusor.ok()) { \
+ auto _status = std::move(statusor.status()); \
+ (void)_status; /* error_expression may not use it */ \
+ return error_expr; \
+ } \
+ lhs = std::move(statusor).value();
+
+} // namespace draco
+
+#endif // DRACO_CORE_STATUSOR_H_
diff --git a/extern/draco/dracoenc/src/draco/core/varint_decoding.h b/extern/draco/dracoenc/src/draco/core/varint_decoding.h
new file mode 100644
index 00000000000..6cd41b29220
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/varint_decoding.h
@@ -0,0 +1,59 @@
+// 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_VARINT_DECODING_H_
+#define DRACO_CORE_VARINT_DECODING_H_
+
+#include <type_traits>
+
+#include "draco/core/bit_utils.h"
+#include "draco/core/decoder_buffer.h"
+
+namespace draco {
+
+// Decodes a specified integer as varint. Note that the IntTypeT must be the
+// same as the one used in the corresponding EncodeVarint() call.
+template <typename IntTypeT>
+bool DecodeVarint(IntTypeT *out_val, DecoderBuffer *buffer) {
+ if (std::is_unsigned<IntTypeT>::value) {
+ // Coding of unsigned values.
+ // 0-6 bit - data
+ // 7 bit - next byte?
+ uint8_t in;
+ if (!buffer->Decode(&in))
+ return false;
+ if (in & (1 << 7)) {
+ // Next byte is available, decode it first.
+ if (!DecodeVarint<IntTypeT>(out_val, buffer))
+ return false;
+ // Append decoded info from this byte.
+ *out_val <<= 7;
+ *out_val |= in & ((1 << 7) - 1);
+ } else {
+ // Last byte reached
+ *out_val = in;
+ }
+ } else {
+ // IntTypeT is a signed value. Decode the symbol and convert to signed.
+ typename std::make_unsigned<IntTypeT>::type symbol;
+ if (!DecodeVarint(&symbol, buffer))
+ return false;
+ *out_val = ConvertSymbolToSignedInt(symbol);
+ }
+ return true;
+}
+
+} // namespace draco
+
+#endif // DRACO_CORE_VARINT_DECODING_H_
diff --git a/extern/draco/dracoenc/src/draco/core/varint_encoding.h b/extern/draco/dracoenc/src/draco/core/varint_encoding.h
new file mode 100644
index 00000000000..b9b6dcab78d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/varint_encoding.h
@@ -0,0 +1,57 @@
+// 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_VARINT_ENCODING_H_
+#define DRACO_CORE_VARINT_ENCODING_H_
+
+#include <type_traits>
+
+#include "draco/core/bit_utils.h"
+#include "draco/core/encoder_buffer.h"
+
+namespace draco {
+
+// Encodes a specified integer as varint. Note that different coding is used
+// when IntTypeT is an unsigned data type.
+template <typename IntTypeT>
+bool EncodeVarint(IntTypeT val, EncoderBuffer *out_buffer) {
+ if (std::is_unsigned<IntTypeT>::value) {
+ // Coding of unsigned values.
+ // 0-6 bit - data
+ // 7 bit - next byte?
+ uint8_t out = 0;
+ out |= val & ((1 << 7) - 1);
+ if (val >= (1 << 7)) {
+ out |= (1 << 7);
+ if (!out_buffer->Encode(out))
+ return false;
+ if (!EncodeVarint<IntTypeT>(val >> 7, out_buffer))
+ return false;
+ return true;
+ }
+ if (!out_buffer->Encode(out))
+ return false;
+ } else {
+ // IntTypeT is a signed value. Convert to unsigned symbol and encode.
+ const typename std::make_unsigned<IntTypeT>::type symbol =
+ ConvertSignedIntToSymbol(val);
+ if (!EncodeVarint(symbol, out_buffer))
+ return false;
+ }
+ return true;
+}
+
+} // namespace draco
+
+#endif // DRACO_CORE_VARINT_ENCODING_H_
diff --git a/extern/draco/dracoenc/src/draco/core/vector_d.h b/extern/draco/dracoenc/src/draco/core/vector_d.h
new file mode 100644
index 00000000000..57dcd102663
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/vector_d.h
@@ -0,0 +1,260 @@
+// 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_VECTOR_D_H_
+#define DRACO_CORE_VECTOR_D_H_
+
+#include <inttypes.h>
+#include <algorithm>
+#include <array>
+#include <cmath>
+
+#include "draco/core/macros.h"
+
+namespace draco {
+// D-dimensional vector class with basic operations.
+template <class CoeffT, int dimension_t>
+class VectorD {
+ public:
+ typedef VectorD<CoeffT, dimension_t> Self;
+ typedef CoeffT CoefficientType;
+ static constexpr int dimension = dimension_t;
+
+ VectorD() {
+ for (int i = 0; i < dimension_t; ++i)
+ (*this)[i] = CoeffT(0);
+ }
+
+ // The following constructor does not compile in opt mode, which for now led
+ // to the constructors further down, which is not ideal.
+ // TODO(hemmer): fix constructor below and remove others.
+ // template <typename... Args>
+ // explicit VectorD(Args... args) : v_({args...}) {}
+
+ VectorD(const CoeffT &c0, const CoeffT &c1) : v_({{c0, c1}}) {
+ DRACO_DCHECK_EQ(dimension_t, 2);
+ v_[0] = c0;
+ v_[1] = c1;
+ }
+
+ VectorD(const CoeffT &c0, const CoeffT &c1, const CoeffT &c2)
+ : v_({{c0, c1, c2}}) {
+ DRACO_DCHECK_EQ(dimension_t, 3);
+ }
+
+ VectorD(const CoeffT &c0, const CoeffT &c1, const CoeffT &c2,
+ const CoeffT &c3)
+ : v_({{c0, c1, c2, c3}}) {
+ DRACO_DCHECK_EQ(dimension_t, 4);
+ }
+
+ VectorD(const CoeffT &c0, const CoeffT &c1, const CoeffT &c2,
+ const CoeffT &c3, const CoeffT &c4)
+ : v_({{c0, c1, c2, c3, c4}}) {
+ DRACO_DCHECK_EQ(dimension_t, 5);
+ }
+
+ VectorD(const CoeffT &c0, const CoeffT &c1, const CoeffT &c2,
+ const CoeffT &c3, const CoeffT &c4, const CoeffT &c5)
+ : v_({{c0, c1, c2, c3, c4, c5}}) {
+ DRACO_DCHECK_EQ(dimension_t, 6);
+ }
+
+ VectorD(const CoeffT &c0, const CoeffT &c1, const CoeffT &c2,
+ const CoeffT &c3, const CoeffT &c4, const CoeffT &c5,
+ const CoeffT &c6)
+ : v_({{c0, c1, c2, c3, c4, c5, c6}}) {
+ DRACO_DCHECK_EQ(dimension_t, 7);
+ }
+
+ VectorD(const Self &o) {
+ for (int i = 0; i < dimension_t; ++i)
+ (*this)[i] = o[i];
+ }
+
+ CoeffT &operator[](int i) { return v_[i]; }
+ const CoeffT &operator[](int i) const { return v_[i]; }
+ // TODO(hemmer): remove.
+ // Similar to interface of Eigen library.
+ CoeffT &operator()(int i) { return v_[i]; }
+ const CoeffT &operator()(int i) const { return v_[i]; }
+
+ // Unary operators.
+ Self operator-() const {
+ Self ret;
+ for (int i = 0; i < dimension_t; ++i) {
+ ret[i] = -(*this)[i];
+ }
+ return ret;
+ }
+
+ // Binary operators.
+ Self operator+(const Self &o) const {
+ Self ret;
+ for (int i = 0; i < dimension_t; ++i) {
+ ret[i] = (*this)[i] + o[i];
+ }
+ return ret;
+ }
+
+ Self operator-(const Self &o) const {
+ Self ret;
+ for (int i = 0; i < dimension_t; ++i) {
+ ret[i] = (*this)[i] - o[i];
+ }
+ return ret;
+ }
+
+ Self operator*(const CoeffT &o) const {
+ Self ret;
+ for (int i = 0; i < dimension_t; ++i) {
+ ret[i] = (*this)[i] * o;
+ }
+ return ret;
+ }
+
+ Self operator/(const CoeffT &o) const {
+ Self ret;
+ for (int i = 0; i < dimension_t; ++i) {
+ ret[i] = (*this)[i] / o;
+ }
+ return ret;
+ }
+
+ bool operator==(const Self &o) const {
+ for (int i = 0; i < dimension_t; ++i) {
+ if ((*this)[i] != o[i])
+ return false;
+ }
+ return true;
+ }
+
+ bool operator!=(const Self &x) const { return !((*this) == x); }
+
+ bool operator<(const Self &x) const {
+ for (int i = 0; i < dimension_t - 1; ++i) {
+ if (v_[i] < x.v_[i])
+ return true;
+ if (v_[i] > x.v_[i])
+ return false;
+ }
+ // Only one check needed for the last dimension.
+ if (v_[dimension_t - 1] < x.v_[dimension_t - 1])
+ return true;
+ return false;
+ }
+
+ // Functions.
+ CoeffT SquaredNorm() const { return this->Dot(*this); }
+
+ // Computes L1, the sum of absolute values of all entries.
+ CoeffT AbsSum() const {
+ CoeffT result(0);
+ for (int i = 0; i < dimension_t; ++i) {
+ result += std::abs(v_[i]);
+ }
+ return result;
+ }
+
+ CoeffT Dot(const Self &o) const {
+ CoeffT ret(0);
+ for (int i = 0; i < dimension_t; ++i) {
+ ret += (*this)[i] * o[i];
+ }
+ return ret;
+ }
+
+ void Normalize() {
+ const CoeffT magnitude = std::sqrt(this->SquaredNorm());
+ if (magnitude == 0) {
+ return;
+ }
+ for (int i = 0; i < dimension_t; ++i) {
+ (*this)[i] /= magnitude;
+ }
+ }
+
+ CoeffT *data() { return &(v_[0]); }
+
+ private:
+ std::array<CoeffT, dimension_t> v_;
+};
+
+// Scalar multiplication from the other side too.
+template <class CoeffT, int dimension_t>
+VectorD<CoeffT, dimension_t> operator*(const CoeffT &o,
+ const VectorD<CoeffT, dimension_t> &v) {
+ return v * o;
+}
+
+// Calculates the squared distance between two points.
+template <class CoeffT, int dimension_t>
+CoeffT SquaredDistance(const VectorD<CoeffT, dimension_t> &v1,
+ const VectorD<CoeffT, dimension_t> &v2) {
+ CoeffT difference;
+ CoeffT squared_distance = 0;
+ // Check each index separately so difference is never negative and underflow
+ // is avoided for unsigned types.
+ for (int i = 0; i < dimension_t; ++i) {
+ if (v1[i] >= v2[i]) {
+ difference = v1[i] - v2[i];
+ } else {
+ difference = v2[i] - v1[i];
+ }
+ squared_distance += (difference * difference);
+ }
+ return squared_distance;
+}
+
+// Global function computing the cross product of two 3D vectors.
+template <class CoeffT>
+VectorD<CoeffT, 3> CrossProduct(const VectorD<CoeffT, 3> &u,
+ const VectorD<CoeffT, 3> &v) {
+ // Preventing accidental use with uint32_t and the like.
+ static_assert(std::is_signed<CoeffT>::value,
+ "CoeffT must be a signed type. ");
+ VectorD<CoeffT, 3> r;
+ r[0] = (u[1] * v[2]) - (u[2] * v[1]);
+ r[1] = (u[2] * v[0]) - (u[0] * v[2]);
+ r[2] = (u[0] * v[1]) - (u[1] * v[0]);
+ return r;
+}
+
+template <class CoeffT, int dimension_t>
+inline std::ostream &operator<<(
+ std::ostream &out, const draco::VectorD<CoeffT, dimension_t> &vec) {
+ for (int i = 0; i < dimension_t - 1; ++i) {
+ out << vec[i] << " ";
+ }
+ out << vec[dimension_t - 1];
+ return out;
+}
+
+typedef VectorD<float, 2> Vector2f;
+typedef VectorD<float, 3> Vector3f;
+typedef VectorD<float, 4> Vector4f;
+typedef VectorD<float, 5> Vector5f;
+typedef VectorD<float, 6> Vector6f;
+typedef VectorD<float, 7> Vector7f;
+
+typedef VectorD<uint32_t, 2> Vector2ui;
+typedef VectorD<uint32_t, 3> Vector3ui;
+typedef VectorD<uint32_t, 4> Vector4ui;
+typedef VectorD<uint32_t, 5> Vector5ui;
+typedef VectorD<uint32_t, 6> Vector6ui;
+typedef VectorD<uint32_t, 7> Vector7ui;
+
+} // namespace draco
+
+#endif // DRACO_CORE_VECTOR_D_H_
diff --git a/extern/draco/dracoenc/src/draco/core/vector_d_test.cc b/extern/draco/dracoenc/src/draco/core/vector_d_test.cc
new file mode 100644
index 00000000000..967043bb926
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/core/vector_d_test.cc
@@ -0,0 +1,205 @@
+// 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 "draco/core/vector_d.h"
+
+#include <sstream>
+
+#include "draco/core/draco_test_base.h"
+
+namespace {
+
+typedef draco::Vector2f Vector2f;
+typedef draco::Vector3f Vector3f;
+typedef draco::Vector4f Vector4f;
+typedef draco::Vector5f Vector5f;
+typedef draco::Vector2ui Vector2ui;
+typedef draco::Vector3ui Vector3ui;
+typedef draco::Vector4ui Vector4ui;
+typedef draco::Vector5ui Vector5ui;
+
+typedef draco::VectorD<int32_t, 3> Vector3i;
+typedef draco::VectorD<int32_t, 4> Vector4i;
+
+class VectorDTest : public ::testing::Test {
+ protected:
+ template <class CoeffT, int dimension_t>
+ void TestSquaredDistance(const draco::VectorD<CoeffT, dimension_t> v1,
+ const draco::VectorD<CoeffT, dimension_t> v2,
+ const CoeffT result) {
+ CoeffT squared_distance = SquaredDistance(v1, v2);
+ ASSERT_EQ(squared_distance, result);
+ squared_distance = SquaredDistance(v2, v1);
+ ASSERT_EQ(squared_distance, result);
+ }
+};
+
+TEST_F(VectorDTest, TestOperators) {
+ {
+ const Vector3f v;
+ ASSERT_EQ(v[0], 0);
+ ASSERT_EQ(v[1], 0);
+ ASSERT_EQ(v[2], 0);
+ }
+ 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 * 2.f;
+ 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);
+
+ Vector3f new_v = v;
+ new_v.Normalize();
+ const float eps = 0.001;
+ const float magnitude = std::sqrt(v.SquaredNorm());
+ const float new_magnitude = std::sqrt(new_v.SquaredNorm());
+ ASSERT_LE(new_magnitude, 1 + eps);
+ ASSERT_GE(new_magnitude, 1 - eps);
+ for (int i = 0; i < 3; ++i) {
+ new_v[i] *= magnitude;
+ ASSERT_LE(new_v[i], v[i] + eps);
+ ASSERT_GE(new_v[i], v[i] - eps);
+ }
+
+ Vector3f x(0, 0, 0);
+ x.Normalize();
+ for (int i = 0; i < 3; ++i) {
+ ASSERT_EQ(0, x[i]);
+ }
+}
+
+TEST_F(VectorDTest, TestSquaredDistance) {
+ // Test Vector2f: float, 2D.
+ Vector2f v1_2f(5.5, 10.5);
+ Vector2f v2_2f(3.5, 15.5);
+ float result_f = 29;
+ TestSquaredDistance(v1_2f, v2_2f, result_f);
+
+ // Test Vector3f: float, 3D.
+ Vector3f v1_3f(5.5, 10.5, 2.3);
+ Vector3f v2_3f(3.5, 15.5, 0);
+ result_f = 34.29;
+ TestSquaredDistance(v1_3f, v2_3f, result_f);
+
+ // Test Vector4f: float, 4D.
+ Vector4f v1_4f(5.5, 10.5, 2.3, 7.2);
+ Vector4f v2_4f(3.5, 15.5, 0, 9.9);
+ result_f = 41.58;
+ TestSquaredDistance(v1_4f, v2_4f, result_f);
+
+ // Test Vector5f: float, 5D.
+ Vector5f v1_5f(5.5, 10.5, 2.3, 7.2, 1.0);
+ Vector5f v2_5f(3.5, 15.5, 0, 9.9, 0.2);
+ result_f = 42.22;
+ TestSquaredDistance(v1_5f, v2_5f, result_f);
+
+ // Test Vector 2ui: uint32_t, 2D.
+ Vector2ui v1_2ui(5, 10);
+ Vector2ui v2_2ui(3, 15);
+ uint32_t result_ui = 29;
+ TestSquaredDistance(v1_2ui, v2_2ui, result_ui);
+
+ // Test Vector 3ui: uint32_t, 3D.
+ Vector3ui v1_3ui(5, 10, 2);
+ Vector3ui v2_3ui(3, 15, 0);
+ result_ui = 33;
+ TestSquaredDistance(v1_3ui, v2_3ui, result_ui);
+
+ // Test Vector 4ui: uint32_t, 4D.
+ Vector4ui v1_4ui(5, 10, 2, 7);
+ Vector4ui v2_4ui(3, 15, 0, 9);
+ result_ui = 37;
+ TestSquaredDistance(v1_4ui, v2_4ui, result_ui);
+
+ // Test Vector 5ui: uint32_t, 5D.
+ Vector5ui v1_5ui(5, 10, 2, 7, 1);
+ Vector5ui v2_5ui(3, 15, 0, 9, 12);
+ result_ui = 158;
+ TestSquaredDistance(v1_5ui, v2_5ui, result_ui);
+}
+TEST_F(VectorDTest, TestCrossProduct3D) {
+ const Vector3i e1(1, 0, 0);
+ const Vector3i e2(0, 1, 0);
+ const Vector3i e3(0, 0, 1);
+ const Vector3i o(0, 0, 0);
+ ASSERT_EQ(e3, draco::CrossProduct(e1, e2));
+ ASSERT_EQ(e1, draco::CrossProduct(e2, e3));
+ ASSERT_EQ(e2, draco::CrossProduct(e3, e1));
+ ASSERT_EQ(-e3, draco::CrossProduct(e2, e1));
+ ASSERT_EQ(-e1, draco::CrossProduct(e3, e2));
+ ASSERT_EQ(-e2, draco::CrossProduct(e1, e3));
+ ASSERT_EQ(o, draco::CrossProduct(e1, e1));
+ ASSERT_EQ(o, draco::CrossProduct(e2, e2));
+ ASSERT_EQ(o, draco::CrossProduct(e3, e3));
+
+ // Orthogonality of result for some general vectors.
+ const Vector3i v1(123, -62, 223);
+ const Vector3i v2(734, 244, -13);
+ const Vector3i orth = draco::CrossProduct(v1, v2);
+ ASSERT_EQ(0, v1.Dot(orth));
+ ASSERT_EQ(0, v2.Dot(orth));
+}
+
+TEST_F(VectorDTest, TestAbsSum) {
+ // Testing const of function and zero.
+ const Vector3i v(0, 0, 0);
+ ASSERT_EQ(v.AbsSum(), 0);
+ // Testing semantic.
+ ASSERT_EQ(Vector3i(0, 0, 0).AbsSum(), 0);
+ ASSERT_EQ(Vector3i(1, 2, 3).AbsSum(), 6);
+ ASSERT_EQ(Vector3i(-1, -2, -3).AbsSum(), 6);
+ ASSERT_EQ(Vector3i(-2, 4, -8).AbsSum(), 14);
+ // Other dimension.
+ ASSERT_EQ(Vector4i(-2, 4, -8, 3).AbsSum(), 17);
+}
+
+TEST_F(VectorDTest, TestOstream) {
+ // Tests that the vector can be stored in a provided std::ostream.
+ const draco::VectorD<int64_t, 3> vector(1, 2, 3);
+ std::stringstream str;
+ str << vector << " ";
+ ASSERT_EQ(str.str(), "1 2 3 ");
+}
+
+} // namespace
diff --git a/extern/draco/dracoenc/src/draco/draco_features.h b/extern/draco/dracoenc/src/draco/draco_features.h
new file mode 100644
index 00000000000..f067ca44dd0
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/draco_features.h
@@ -0,0 +1,8 @@
+// GENERATED FILE -- DO NOT EDIT
+
+#ifndef DRACO_FEATURES_H_
+#define DRACO_FEATURES_H_
+
+#define DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
+
+#endif // DRACO_FEATURES_H_ \ No newline at end of file
diff --git a/extern/draco/dracoenc/src/draco/io/mesh_io.cc b/extern/draco/dracoenc/src/draco/io/mesh_io.cc
new file mode 100644
index 00000000000..807dcfbe06b
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/mesh_io.cc
@@ -0,0 +1,74 @@
+// 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 "draco/io/mesh_io.h"
+
+#include <fstream>
+
+#include "draco/io/obj_decoder.h"
+#include "draco/io/parser_utils.h"
+#include "draco/io/ply_decoder.h"
+
+namespace draco {
+
+namespace {
+
+// Returns the file extension in lowercase if present, else ""
+inline std::string LowercaseFileExtension(const std::string &filename) {
+ size_t pos = filename.find_last_of('.');
+ if (pos == std::string::npos || pos >= filename.length() - 1)
+ return "";
+ return parser::ToLower(filename.substr(pos + 1));
+}
+
+} // namespace
+
+StatusOr<std::unique_ptr<Mesh>> ReadMeshFromFile(const std::string &file_name) {
+ return ReadMeshFromFile(file_name, false);
+}
+
+StatusOr<std::unique_ptr<Mesh>> ReadMeshFromFile(const std::string &file_name,
+ bool use_metadata) {
+ std::unique_ptr<Mesh> mesh(new Mesh());
+ // Analyze file extension.
+ const std::string extension = LowercaseFileExtension(file_name);
+ if (extension == "obj") {
+ // Wavefront OBJ file format.
+ ObjDecoder obj_decoder;
+ obj_decoder.set_use_metadata(use_metadata);
+ const Status obj_status = obj_decoder.DecodeFromFile(file_name, mesh.get());
+ if (!obj_status.ok())
+ return obj_status;
+ return std::move(mesh);
+ }
+ if (extension == "ply") {
+ // Wavefront PLY file format.
+ PlyDecoder ply_decoder;
+ if (!ply_decoder.DecodeFromFile(file_name, mesh.get()))
+ return Status(Status::ERROR, "Unknown error.");
+ return std::move(mesh);
+ }
+
+ // Otherwise not an obj file. Assume the file was encoded with one of the
+ // draco encoding methods.
+ std::ifstream is(file_name.c_str(), std::ios::binary);
+ if (!is)
+ return Status(Status::ERROR, "Invalid input stream.");
+ if (!ReadMeshFromStream(&mesh, is).good())
+ return Status(Status::ERROR,
+ "Unknown error."); // Error reading the stream.
+ return std::move(mesh);
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/io/mesh_io.h b/extern/draco/dracoenc/src/draco/io/mesh_io.h
new file mode 100644
index 00000000000..7649a736265
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/mesh_io.h
@@ -0,0 +1,94 @@
+// 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_MESH_MESH_IO_H_
+#define DRACO_MESH_MESH_IO_H_
+
+#include "draco/compression/config/compression_shared.h"
+#include "draco/compression/decode.h"
+#include "draco/compression/expert_encode.h"
+
+namespace draco {
+
+template <typename OutStreamT>
+OutStreamT WriteMeshIntoStream(const Mesh *mesh, OutStreamT &&os,
+ MeshEncoderMethod method,
+ const EncoderOptions &options) {
+ EncoderBuffer buffer;
+ EncoderOptions local_options = options;
+ ExpertEncoder encoder(*mesh);
+ encoder.Reset(local_options);
+ encoder.SetEncodingMethod(method);
+ if (!encoder.EncodeToBuffer(&buffer).ok()) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+
+ os.write(static_cast<const char *>(buffer.data()), buffer.size());
+
+ return os;
+}
+
+template <typename OutStreamT>
+OutStreamT WriteMeshIntoStream(const Mesh *mesh, OutStreamT &&os,
+ MeshEncoderMethod method) {
+ const EncoderOptions options = EncoderOptions::CreateDefaultOptions();
+ return WriteMeshIntoStream(mesh, os, method, options);
+}
+
+template <typename OutStreamT>
+OutStreamT &WriteMeshIntoStream(const Mesh *mesh, OutStreamT &&os) {
+ return WriteMeshIntoStream(mesh, os, MESH_EDGEBREAKER_ENCODING);
+}
+
+template <typename InStreamT>
+InStreamT &ReadMeshFromStream(std::unique_ptr<Mesh> *mesh, InStreamT &&is) {
+ // Determine size of stream and write into a vector
+ const auto start_pos = is.tellg();
+ is.seekg(0, std::ios::end);
+ const std::streampos is_size = is.tellg() - start_pos;
+ is.seekg(start_pos);
+ std::vector<char> data(is_size);
+ is.read(&data[0], is_size);
+
+ // Create a mesh from that data.
+ DecoderBuffer buffer;
+ buffer.Init(&data[0], data.size());
+ Decoder decoder;
+ auto statusor = decoder.DecodeMeshFromBuffer(&buffer);
+ *mesh = std::move(statusor).value();
+ if (!statusor.ok() || *mesh == nullptr) {
+ is.setstate(std::ios_base::badbit);
+ }
+
+ return is;
+}
+
+// Reads a mesh from a file. The function automatically chooses the correct
+// decoder based on the extension of the files. Currently, .obj and .ply files
+// are supported. Other file extensions are processed by the default
+// draco::MeshDecoder.
+// Returns nullptr with an error status if the decoding failed.
+StatusOr<std::unique_ptr<Mesh>> ReadMeshFromFile(const std::string &file_name);
+
+// Reads a mesh from a file. The function does the same thing as the previous
+// one except using metadata to encode additional information when
+// |use_metadata| is set to true.
+// Returns nullptr with an error status if the decoding failed.
+StatusOr<std::unique_ptr<Mesh>> ReadMeshFromFile(const std::string &file_name,
+ bool use_metadata);
+
+} // namespace draco
+
+#endif // DRACO_MESH_MESH_IO_H_
diff --git a/extern/draco/dracoenc/src/draco/io/obj_decoder.cc b/extern/draco/dracoenc/src/draco/io/obj_decoder.cc
new file mode 100644
index 00000000000..5aaa9f72888
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/obj_decoder.cc
@@ -0,0 +1,691 @@
+// 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 "draco/io/obj_decoder.h"
+
+#include <cctype>
+#include <cmath>
+#include <fstream>
+
+#include "draco/io/parser_utils.h"
+#include "draco/metadata/geometry_metadata.h"
+
+namespace draco {
+
+ObjDecoder::ObjDecoder()
+ : counting_mode_(true),
+ num_obj_faces_(0),
+ num_positions_(0),
+ num_tex_coords_(0),
+ num_normals_(0),
+ num_materials_(0),
+ last_sub_obj_id_(0),
+ pos_att_id_(-1),
+ tex_att_id_(-1),
+ norm_att_id_(-1),
+ material_att_id_(-1),
+ sub_obj_att_id_(-1),
+ deduplicate_input_values_(true),
+ last_material_id_(0),
+ use_metadata_(false),
+ out_mesh_(nullptr),
+ out_point_cloud_(nullptr) {}
+
+Status ObjDecoder::DecodeFromFile(const std::string &file_name,
+ Mesh *out_mesh) {
+ out_mesh_ = out_mesh;
+ return DecodeFromFile(file_name, static_cast<PointCloud *>(out_mesh));
+}
+
+Status ObjDecoder::DecodeFromFile(const std::string &file_name,
+ PointCloud *out_point_cloud) {
+ std::ifstream file(file_name, std::ios::binary);
+ if (!file)
+ return Status(Status::IO_ERROR);
+ // Read the whole file into a buffer.
+ auto pos0 = file.tellg();
+ file.seekg(0, std::ios::end);
+ auto file_size = file.tellg() - pos0;
+ if (file_size == 0)
+ return Status(Status::IO_ERROR);
+ file.seekg(0, std::ios::beg);
+ std::vector<char> data(file_size);
+ file.read(&data[0], file_size);
+ buffer_.Init(&data[0], file_size);
+
+ out_point_cloud_ = out_point_cloud;
+ input_file_name_ = file_name;
+ return DecodeInternal();
+}
+
+Status ObjDecoder::DecodeFromBuffer(DecoderBuffer *buffer, Mesh *out_mesh) {
+ out_mesh_ = out_mesh;
+ return DecodeFromBuffer(buffer, static_cast<PointCloud *>(out_mesh));
+}
+
+Status ObjDecoder::DecodeFromBuffer(DecoderBuffer *buffer,
+ PointCloud *out_point_cloud) {
+ out_point_cloud_ = out_point_cloud;
+ buffer_.Init(buffer->data_head(), buffer->remaining_size());
+ return DecodeInternal();
+}
+
+Status ObjDecoder::DecodeInternal() {
+ // In the first pass, count the number of different elements in the geometry.
+ // In case the desired output is just a point cloud (i.e., when
+ // out_mesh_ == nullptr) the decoder will ignore all information about the
+ // connectivity that may be included in the source data.
+ counting_mode_ = true;
+ ResetCounters();
+ material_name_to_id_.clear();
+ last_sub_obj_id_ = 0;
+ // Parse all lines.
+ Status status(Status::OK);
+ while (ParseDefinition(&status) && status.ok()) {
+ }
+ if (!status.ok())
+ return status;
+ bool use_identity_mapping = false;
+ if (num_obj_faces_ == 0) {
+ // Mesh has no faces. In this case we try to read the geometry as a point
+ // cloud where every attribute entry is a point.
+
+ // Ensure the number of all entries is same for all attributes.
+ if (num_positions_ == 0)
+ return Status(Status::ERROR, "No position attribute");
+ if (num_tex_coords_ > 0 && num_tex_coords_ != num_positions_)
+ return Status(Status::ERROR,
+ "Invalid number of texture coordinates for a point cloud");
+ if (num_normals_ > 0 && num_normals_ != num_positions_)
+ return Status(Status::ERROR,
+ "Invalid number of normals for a point cloud");
+
+ out_mesh_ = nullptr; // Treat the output geometry as a point cloud.
+ use_identity_mapping = true;
+ }
+
+ // Initialize point cloud and mesh properties.
+ if (out_mesh_) {
+ // Start decoding a mesh with the given number of faces. For point clouds we
+ // silently ignore all data about the mesh connectivity.
+ out_mesh_->SetNumFaces(num_obj_faces_);
+ }
+ if (num_obj_faces_ > 0) {
+ out_point_cloud_->set_num_points(3 * num_obj_faces_);
+ } else {
+ out_point_cloud_->set_num_points(num_positions_);
+ }
+
+ // Add attributes if they are present in the input data.
+ if (num_positions_ > 0) {
+ GeometryAttribute va;
+ va.Init(GeometryAttribute::POSITION, nullptr, 3, DT_FLOAT32, false,
+ sizeof(float) * 3, 0);
+ pos_att_id_ = out_point_cloud_->AddAttribute(va, use_identity_mapping,
+ num_positions_);
+ }
+ if (num_tex_coords_ > 0) {
+ GeometryAttribute va;
+ va.Init(GeometryAttribute::TEX_COORD, nullptr, 2, DT_FLOAT32, false,
+ sizeof(float) * 2, 0);
+ tex_att_id_ = out_point_cloud_->AddAttribute(va, use_identity_mapping,
+ num_tex_coords_);
+ }
+ if (num_normals_ > 0) {
+ GeometryAttribute va;
+ va.Init(GeometryAttribute::NORMAL, nullptr, 3, DT_FLOAT32, false,
+ sizeof(float) * 3, 0);
+ norm_att_id_ =
+ out_point_cloud_->AddAttribute(va, use_identity_mapping, num_normals_);
+ }
+ if (num_materials_ > 0 && num_obj_faces_ > 0) {
+ GeometryAttribute va;
+ if (num_materials_ < 256) {
+ va.Init(GeometryAttribute::GENERIC, nullptr, 1, DT_UINT8, false, 1, 0);
+ } else if (num_materials_ < (1 << 16)) {
+ va.Init(GeometryAttribute::GENERIC, nullptr, 1, DT_UINT16, false, 2, 0);
+ } else {
+ va.Init(GeometryAttribute::GENERIC, nullptr, 1, DT_UINT32, false, 4, 0);
+ }
+ material_att_id_ =
+ out_point_cloud_->AddAttribute(va, false, num_materials_);
+
+ // Fill the material entries.
+ for (int i = 0; i < num_materials_; ++i) {
+ const AttributeValueIndex avi(i);
+ out_point_cloud_->attribute(material_att_id_)->SetAttributeValue(avi, &i);
+ }
+
+ if (use_metadata_) {
+ // Use metadata to store the name of materials.
+ std::unique_ptr<AttributeMetadata> material_metadata =
+ std::unique_ptr<AttributeMetadata>(new AttributeMetadata());
+ material_metadata->AddEntryString("name", "material");
+ // Add all material names.
+ for (const auto &itr : material_name_to_id_) {
+ material_metadata->AddEntryInt(itr.first, itr.second);
+ }
+ if (!material_file_name_.empty()) {
+ material_metadata->AddEntryString("file_name", material_file_name_);
+ }
+
+ out_point_cloud_->AddAttributeMetadata(material_att_id_,
+ std::move(material_metadata));
+ }
+ }
+ if (!obj_name_to_id_.empty() && num_obj_faces_ > 0) {
+ GeometryAttribute va;
+ if (obj_name_to_id_.size() < 256) {
+ va.Init(GeometryAttribute::GENERIC, nullptr, 1, DT_UINT8, false, 1, 0);
+ } else if (obj_name_to_id_.size() < (1 << 16)) {
+ va.Init(GeometryAttribute::GENERIC, nullptr, 1, DT_UINT16, false, 2, 0);
+ } else {
+ va.Init(GeometryAttribute::GENERIC, nullptr, 1, DT_UINT32, false, 4, 0);
+ }
+ sub_obj_att_id_ = out_point_cloud_->AddAttribute(
+ va, false, static_cast<uint32_t>(obj_name_to_id_.size()));
+ // Fill the sub object id entries.
+ for (const auto &itr : obj_name_to_id_) {
+ const AttributeValueIndex i(itr.second);
+ out_point_cloud_->attribute(sub_obj_att_id_)->SetAttributeValue(i, &i);
+ }
+ if (use_metadata_) {
+ // Use metadata to store the name of materials.
+ std::unique_ptr<AttributeMetadata> sub_obj_metadata =
+ std::unique_ptr<AttributeMetadata>(new AttributeMetadata());
+ sub_obj_metadata->AddEntryString("name", "sub_obj");
+ // Add all sub object names.
+ for (const auto &itr : obj_name_to_id_) {
+ const AttributeValueIndex i(itr.second);
+ sub_obj_metadata->AddEntryInt(itr.first, itr.second);
+ }
+ out_point_cloud_->AddAttributeMetadata(sub_obj_att_id_,
+ std::move(sub_obj_metadata));
+ }
+ }
+
+ // Perform a second iteration of parsing and fill all the data.
+ counting_mode_ = false;
+ ResetCounters();
+ // Start parsing from the beginning of the buffer again.
+ buffer()->StartDecodingFrom(0);
+ while (ParseDefinition(&status) && status.ok()) {
+ }
+ if (!status.ok())
+ return status;
+ if (out_mesh_) {
+ // Add faces with identity mapping between vertex and corner indices.
+ // Duplicate vertices will get removed later.
+ Mesh::Face face;
+ for (FaceIndex i(0); i < num_obj_faces_; ++i) {
+ for (int c = 0; c < 3; ++c)
+ face[c] = 3 * i.value() + c;
+ out_mesh_->SetFace(i, face);
+ }
+ }
+#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
+ if (deduplicate_input_values_) {
+ out_point_cloud_->DeduplicateAttributeValues();
+ }
+ out_point_cloud_->DeduplicatePointIds();
+#endif
+ return status;
+}
+
+void ObjDecoder::ResetCounters() {
+ num_obj_faces_ = 0;
+ num_positions_ = 0;
+ num_tex_coords_ = 0;
+ num_normals_ = 0;
+ last_material_id_ = 0;
+ last_sub_obj_id_ = 0;
+}
+
+bool ObjDecoder::ParseDefinition(Status *status) {
+ char c;
+ parser::SkipWhitespace(buffer());
+ if (!buffer()->Peek(&c)) {
+ // End of file reached?.
+ return false;
+ }
+ if (c == '#') {
+ // Comment, ignore the line.
+ parser::SkipLine(buffer());
+ return true;
+ }
+ if (ParseVertexPosition(status))
+ return true;
+ if (ParseNormal(status))
+ return true;
+ if (ParseTexCoord(status))
+ return true;
+ if (ParseFace(status))
+ return true;
+ if (ParseMaterial(status))
+ return true;
+ if (ParseMaterialLib(status))
+ return true;
+ if (ParseObject(status))
+ return true;
+ // No known definition was found. Ignore the line.
+ parser::SkipLine(buffer());
+ return true;
+}
+
+bool ObjDecoder::ParseVertexPosition(Status *status) {
+ std::array<char, 2> c;
+ if (!buffer()->Peek(&c)) {
+ return false;
+ }
+ if (c[0] != 'v' || c[1] != ' ')
+ return false;
+ // Vertex definition found!
+ buffer()->Advance(2);
+ if (!counting_mode_) {
+ // Parse three float numbers for vertex position coordinates.
+ float val[3];
+ for (int i = 0; i < 3; ++i) {
+ parser::SkipWhitespace(buffer());
+ if (!parser::ParseFloat(buffer(), val + i)) {
+ *status = Status(Status::ERROR, "Failed to parse a float number");
+ // The definition is processed so return true.
+ return true;
+ }
+ }
+ out_point_cloud_->attribute(pos_att_id_)
+ ->SetAttributeValue(AttributeValueIndex(num_positions_), val);
+ }
+ ++num_positions_;
+ parser::SkipLine(buffer());
+ return true;
+}
+
+bool ObjDecoder::ParseNormal(Status *status) {
+ std::array<char, 2> c;
+ if (!buffer()->Peek(&c)) {
+ return false;
+ }
+ if (c[0] != 'v' || c[1] != 'n')
+ return false;
+ // Normal definition found!
+ buffer()->Advance(2);
+ if (!counting_mode_) {
+ // Parse three float numbers for the normal vector.
+ float val[3];
+ for (int i = 0; i < 3; ++i) {
+ parser::SkipWhitespace(buffer());
+ if (!parser::ParseFloat(buffer(), val + i)) {
+ *status = Status(Status::ERROR, "Failed to parse a float number");
+ // The definition is processed so return true.
+ return true;
+ }
+ }
+ out_point_cloud_->attribute(norm_att_id_)
+ ->SetAttributeValue(AttributeValueIndex(num_normals_), val);
+ }
+ ++num_normals_;
+ parser::SkipLine(buffer());
+ return true;
+}
+
+bool ObjDecoder::ParseTexCoord(Status *status) {
+ std::array<char, 2> c;
+ if (!buffer()->Peek(&c)) {
+ return false;
+ }
+ if (c[0] != 'v' || c[1] != 't')
+ return false;
+ // Texture coord definition found!
+ buffer()->Advance(2);
+ if (!counting_mode_) {
+ // Parse two float numbers for the texture coordinate.
+ float val[2];
+ for (int i = 0; i < 2; ++i) {
+ parser::SkipWhitespace(buffer());
+ if (!parser::ParseFloat(buffer(), val + i)) {
+ *status = Status(Status::ERROR, "Failed to parse a float number");
+ // The definition is processed so return true.
+ return true;
+ }
+ }
+ out_point_cloud_->attribute(tex_att_id_)
+ ->SetAttributeValue(AttributeValueIndex(num_tex_coords_), val);
+ }
+ ++num_tex_coords_;
+ parser::SkipLine(buffer());
+ return true;
+}
+
+bool ObjDecoder::ParseFace(Status *status) {
+ char c;
+ if (!buffer()->Peek(&c)) {
+ return false;
+ }
+ if (c != 'f')
+ return false;
+ // Face definition found!
+ buffer()->Advance(1);
+ if (!counting_mode_) {
+ std::array<int32_t, 3> indices[4];
+ // Parse face indices (we try to look for up to four to support quads).
+ int num_valid_indices = 0;
+ for (int i = 0; i < 4; ++i) {
+ if (!ParseVertexIndices(&indices[i])) {
+ if (i == 3) {
+ break; // It's OK if there is no fourth vertex index.
+ }
+ *status = Status(Status::ERROR, "Failed to parse vertex indices");
+ return true;
+ }
+ ++num_valid_indices;
+ }
+ // Process the first face.
+ for (int i = 0; i < 3; ++i) {
+ const PointIndex vert_id(3 * num_obj_faces_ + i);
+ MapPointToVertexIndices(vert_id, indices[i]);
+ }
+ ++num_obj_faces_;
+ if (num_valid_indices == 4) {
+ // Add an additional triangle for the quad.
+ //
+ // 3----2
+ // | / |
+ // | / |
+ // 0----1
+ //
+ const PointIndex vert_id(3 * num_obj_faces_);
+ MapPointToVertexIndices(vert_id, indices[0]);
+ MapPointToVertexIndices(vert_id + 1, indices[2]);
+ MapPointToVertexIndices(vert_id + 2, indices[3]);
+ ++num_obj_faces_;
+ }
+ } else {
+ // We are in the counting mode.
+ // We need to determine how many triangles are in the obj face.
+ // Go over the line and check how many gaps there are between non-empty
+ // sub-strings.
+ parser::SkipWhitespace(buffer());
+ int num_indices = 0;
+ bool is_end = false;
+ while (buffer()->Peek(&c) && c != '\n') {
+ if (parser::PeekWhitespace(buffer(), &is_end)) {
+ buffer()->Advance(1);
+ } else {
+ // Non-whitespace reached.. assume it's index declaration, skip it.
+ num_indices++;
+ while (!parser::PeekWhitespace(buffer(), &is_end) && !is_end) {
+ buffer()->Advance(1);
+ }
+ }
+ }
+ if (num_indices < 3 || num_indices > 4) {
+ *status = Status(Status::ERROR, "Invalid number of indices on a face");
+ return false;
+ }
+ // Either one or two new triangles.
+ num_obj_faces_ += num_indices - 2;
+ }
+ parser::SkipLine(buffer());
+ return true;
+}
+
+bool ObjDecoder::ParseMaterialLib(Status *status) {
+ // Allow only one material library per file for now.
+ if (material_name_to_id_.size() > 0)
+ return false;
+ std::array<char, 6> c;
+ if (!buffer()->Peek(&c)) {
+ return false;
+ }
+ if (std::memcmp(&c[0], "mtllib", 6) != 0)
+ return false;
+ buffer()->Advance(6);
+ DecoderBuffer line_buffer = parser::ParseLineIntoDecoderBuffer(buffer());
+ parser::SkipWhitespace(&line_buffer);
+ material_file_name_.clear();
+ if (!parser::ParseString(&line_buffer, &material_file_name_)) {
+ *status = Status(Status::ERROR, "Failed to parse material file name");
+ return true;
+ }
+ parser::SkipLine(&line_buffer);
+
+ if (material_file_name_.size() > 0) {
+ if (!ParseMaterialFile(material_file_name_, status)) {
+ // Silently ignore problems with material files for now.
+ return true;
+ }
+ }
+ return true;
+}
+
+bool ObjDecoder::ParseMaterial(Status * /* status */) {
+ // In second pass, skip when we don't use materials.
+ if (!counting_mode_ && material_att_id_ < 0)
+ return false;
+ std::array<char, 6> c;
+ if (!buffer()->Peek(&c)) {
+ return false;
+ }
+ if (std::memcmp(&c[0], "usemtl", 6) != 0)
+ return false;
+ buffer()->Advance(6);
+ DecoderBuffer line_buffer = parser::ParseLineIntoDecoderBuffer(buffer());
+ parser::SkipWhitespace(&line_buffer);
+ std::string mat_name;
+ parser::ParseLine(&line_buffer, &mat_name);
+ if (mat_name.length() == 0)
+ return false;
+ auto it = material_name_to_id_.find(mat_name);
+ if (it == material_name_to_id_.end()) {
+ // In first pass, materials found in obj that's not in the .mtl file
+ // will be added to the list.
+ last_material_id_ = num_materials_;
+ material_name_to_id_[mat_name] = num_materials_++;
+ return true;
+ }
+ last_material_id_ = it->second;
+ return true;
+}
+
+bool ObjDecoder::ParseObject(Status *status) {
+ std::array<char, 2> c;
+ if (!buffer()->Peek(&c)) {
+ return false;
+ }
+ if (std::memcmp(&c[0], "o ", 2) != 0)
+ return false;
+ buffer()->Advance(1);
+ DecoderBuffer line_buffer = parser::ParseLineIntoDecoderBuffer(buffer());
+ parser::SkipWhitespace(&line_buffer);
+ std::string obj_name;
+ if (!parser::ParseString(&line_buffer, &obj_name))
+ return false;
+ if (obj_name.length() == 0)
+ return true; // Ignore empty name entries.
+ auto it = obj_name_to_id_.find(obj_name);
+ if (it == obj_name_to_id_.end()) {
+ const int num_obj = static_cast<int>(obj_name_to_id_.size());
+ obj_name_to_id_[obj_name] = num_obj;
+ last_sub_obj_id_ = num_obj;
+ } else {
+ last_sub_obj_id_ = it->second;
+ }
+ return true;
+}
+
+bool ObjDecoder::ParseVertexIndices(std::array<int32_t, 3> *out_indices) {
+ // Parsed attribute indices can be in format:
+ // 1. POS_INDEX
+ // 2. POS_INDEX/TEX_COORD_INDEX
+ // 3. POS_INDEX/TEX_COORD_INDEX/NORMAL_INDEX
+ // 4. POS_INDEX//NORMAL_INDEX
+ parser::SkipCharacters(buffer(), " \t");
+ if (!parser::ParseSignedInt(buffer(), &(*out_indices)[0]) ||
+ (*out_indices)[0] == 0)
+ return false; // Position index must be present and valid.
+ (*out_indices)[1] = (*out_indices)[2] = 0;
+ char ch;
+ if (!buffer()->Peek(&ch))
+ return true; // It may be OK if we cannot read any more characters.
+ if (ch != '/')
+ return true;
+ buffer()->Advance(1);
+ // Check if we should skip texture index or not.
+ if (!buffer()->Peek(&ch))
+ return false; // Here, we should be always able to read the next char.
+ if (ch != '/') {
+ // Must be texture coord index.
+ if (!parser::ParseSignedInt(buffer(), &(*out_indices)[1]) ||
+ (*out_indices)[1] == 0)
+ return false; // Texture index must be present and valid.
+ }
+ if (!buffer()->Peek(&ch))
+ return true;
+ if (ch == '/') {
+ buffer()->Advance(1);
+ // Read normal index.
+ if (!parser::ParseSignedInt(buffer(), &(*out_indices)[2]) ||
+ (*out_indices)[2] == 0)
+ return false; // Normal index must be present and valid.
+ }
+ return true;
+}
+
+void ObjDecoder::MapPointToVertexIndices(
+ PointIndex vert_id, const std::array<int32_t, 3> &indices) {
+ // Use face entries to store mapping between vertex and attribute indices
+ // (positions, texture coordinates and normal indices).
+ // Any given index is used when indices[x] != 0. For positive values, the
+ // point is mapped directly to the specified attribute index. Negative input
+ // indices indicate addressing from the last element (e.g. -1 is the last
+ // attribute value of a given type, -2 the second last, etc.).
+ if (indices[0] > 0) {
+ out_point_cloud_->attribute(pos_att_id_)
+ ->SetPointMapEntry(vert_id, AttributeValueIndex(indices[0] - 1));
+ } else if (indices[0] < 0) {
+ out_point_cloud_->attribute(pos_att_id_)
+ ->SetPointMapEntry(vert_id,
+ AttributeValueIndex(num_positions_ + indices[0]));
+ }
+
+ if (tex_att_id_ >= 0) {
+ if (indices[1] > 0) {
+ out_point_cloud_->attribute(tex_att_id_)
+ ->SetPointMapEntry(vert_id, AttributeValueIndex(indices[1] - 1));
+ } else if (indices[1] < 0) {
+ out_point_cloud_->attribute(tex_att_id_)
+ ->SetPointMapEntry(vert_id,
+ AttributeValueIndex(num_tex_coords_ + indices[1]));
+ } else {
+ // Texture index not provided but expected. Insert 0 entry as the
+ // default value.
+ out_point_cloud_->attribute(tex_att_id_)
+ ->SetPointMapEntry(vert_id, AttributeValueIndex(0));
+ }
+ }
+
+ if (norm_att_id_ >= 0) {
+ if (indices[2] > 0) {
+ out_point_cloud_->attribute(norm_att_id_)
+ ->SetPointMapEntry(vert_id, AttributeValueIndex(indices[2] - 1));
+ } else if (indices[2] < 0) {
+ out_point_cloud_->attribute(norm_att_id_)
+ ->SetPointMapEntry(vert_id,
+ AttributeValueIndex(num_normals_ + indices[2]));
+ } else {
+ // Normal index not provided but expected. Insert 0 entry as the default
+ // value.
+ out_point_cloud_->attribute(norm_att_id_)
+ ->SetPointMapEntry(vert_id, AttributeValueIndex(0));
+ }
+ }
+
+ // Assign material index to the point if it is available.
+ if (material_att_id_ >= 0) {
+ out_point_cloud_->attribute(material_att_id_)
+ ->SetPointMapEntry(vert_id, AttributeValueIndex(last_material_id_));
+ }
+
+ // Assign sub-object index to the point if it is available.
+ if (sub_obj_att_id_ >= 0) {
+ out_point_cloud_->attribute(sub_obj_att_id_)
+ ->SetPointMapEntry(vert_id, AttributeValueIndex(last_sub_obj_id_));
+ }
+}
+
+bool ObjDecoder::ParseMaterialFile(const std::string &file_name,
+ Status *status) {
+ // Get the correct path to the |file_name| using the folder from
+ // |input_file_name_| as the root folder.
+ const auto pos = input_file_name_.find_last_of("/\\");
+ std::string full_path;
+ if (pos != std::string::npos) {
+ full_path = input_file_name_.substr(0, pos + 1);
+ }
+ full_path += file_name;
+
+ std::ifstream file(full_path, std::ios::binary);
+ if (!file)
+ return false;
+ // Read the whole file into a buffer.
+ file.seekg(0, std::ios::end);
+ const std::string::size_type file_size = file.tellg();
+ if (file_size == 0)
+ return false;
+ file.seekg(0, std::ios::beg);
+ std::vector<char> data(file_size);
+ file.read(&data[0], file_size);
+
+ // Backup the original decoder buffer.
+ DecoderBuffer old_buffer = buffer_;
+
+ buffer_.Init(&data[0], file_size);
+
+ num_materials_ = 0;
+ while (ParseMaterialFileDefinition(status)) {
+ }
+
+ // Restore the original buffer.
+ buffer_ = old_buffer;
+ return true;
+}
+
+bool ObjDecoder::ParseMaterialFileDefinition(Status * /* status */) {
+ char c;
+ parser::SkipWhitespace(buffer());
+ if (!buffer()->Peek(&c)) {
+ // End of file reached?.
+ return false;
+ }
+ if (c == '#') {
+ // Comment, ignore the line.
+ parser::SkipLine(buffer());
+ return true;
+ }
+ std::string str;
+ if (!parser::ParseString(buffer(), &str))
+ return false;
+ if (str.compare("newmtl") == 0) {
+ parser::SkipWhitespace(buffer());
+ parser::ParseLine(buffer(), &str);
+ if (str.length() == 0)
+ return false;
+ // Add new material to our map.
+ material_name_to_id_[str] = num_materials_++;
+ }
+ parser::SkipLine(buffer());
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/io/obj_decoder.h b/extern/draco/dracoenc/src/draco/io/obj_decoder.h
new file mode 100644
index 00000000000..33b9dee2414
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/obj_decoder.h
@@ -0,0 +1,130 @@
+// 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_IO_OBJ_DECODER_H_
+#define DRACO_IO_OBJ_DECODER_H_
+
+#include <string>
+#include <unordered_map>
+
+#include "draco/draco_features.h"
+
+#include "draco/core/decoder_buffer.h"
+#include "draco/core/status.h"
+#include "draco/mesh/mesh.h"
+
+namespace draco {
+
+// Decodes a Wavefront OBJ file into draco::Mesh (or draco::PointCloud if the
+// connectivity data is not needed).. This decoder can handle decoding of
+// positions, texture coordinates, normals and triangular faces.
+// All other geometry properties are ignored.
+class ObjDecoder {
+ public:
+ ObjDecoder();
+
+ // Decodes an obj file stored in the input file.
+ // Returns nullptr if the decoding failed.
+ Status DecodeFromFile(const std::string &file_name, Mesh *out_mesh);
+ Status DecodeFromFile(const std::string &file_name,
+ PointCloud *out_point_cloud);
+
+ Status DecodeFromBuffer(DecoderBuffer *buffer, Mesh *out_mesh);
+ Status DecodeFromBuffer(DecoderBuffer *buffer, PointCloud *out_point_cloud);
+
+ // Flag that can be used to turn on/off deduplication of input values.
+ // This should be disabled only when we are sure that the input data does not
+ // contain any duplicate entries.
+ // Default: true
+ void set_deduplicate_input_values(bool v) { deduplicate_input_values_ = v; }
+ // Flag for whether using metadata to record other information in the obj
+ // file, e.g. material names, object names.
+ void set_use_metadata(bool flag) { use_metadata_ = flag; }
+
+ protected:
+ Status DecodeInternal();
+ DecoderBuffer *buffer() { return &buffer_; }
+
+ private:
+ // Resets internal counters for attributes and faces.
+ void ResetCounters();
+
+ // Parses the next mesh property definition (position, tex coord, normal, or
+ // face). If the parsed data is unrecognized, it will be skipped.
+ // Returns false when the end of file was reached.
+ bool ParseDefinition(Status *status);
+
+ // Attempts to parse definition of position, normal, tex coord, or face
+ // respectively.
+ // Returns false when the parsed data didn't contain the given definition.
+ bool ParseVertexPosition(Status *status);
+ bool ParseNormal(Status *status);
+ bool ParseTexCoord(Status *status);
+ bool ParseFace(Status *status);
+ bool ParseMaterialLib(Status *status);
+ bool ParseMaterial(Status *status);
+ bool ParseObject(Status *status);
+
+ // Parses triplet of position, tex coords and normal indices.
+ // Returns false on error.
+ bool ParseVertexIndices(std::array<int32_t, 3> *out_indices);
+
+ // Maps specified point index to the parsed vertex indices (triplet of
+ // position, texture coordinate, and normal indices) .
+ void MapPointToVertexIndices(PointIndex pi,
+ const std::array<int32_t, 3> &indices);
+
+ // Parses material file definitions from a separate file.
+ bool ParseMaterialFile(const std::string &file_name, Status *status);
+ bool ParseMaterialFileDefinition(Status *status);
+
+ // If set to true, the parser will count the number of various definitions
+ // but it will not parse the actual data or add any new entries to the mesh.
+ bool counting_mode_;
+ int num_obj_faces_;
+ int num_positions_;
+ int num_tex_coords_;
+ int num_normals_;
+ int num_materials_;
+ int last_sub_obj_id_;
+
+ int pos_att_id_;
+ int tex_att_id_;
+ int norm_att_id_;
+ int material_att_id_;
+ int sub_obj_att_id_; // Attribute id for storing sub-objects.
+
+ bool deduplicate_input_values_;
+
+ int last_material_id_;
+ std::string material_file_name_;
+
+ std::string input_file_name_;
+
+ std::unordered_map<std::string, int> material_name_to_id_;
+ std::unordered_map<std::string, int> obj_name_to_id_;
+
+ bool use_metadata_;
+
+ DecoderBuffer buffer_;
+
+ // Data structure that stores the decoded data. |out_point_cloud_| must be
+ // always set but |out_mesh_| is optional.
+ Mesh *out_mesh_;
+ PointCloud *out_point_cloud_;
+};
+
+} // namespace draco
+
+#endif // DRACO_IO_OBJ_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/io/obj_decoder_test.cc b/extern/draco/dracoenc/src/draco/io/obj_decoder_test.cc
new file mode 100644
index 00000000000..3d319a9d44a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/obj_decoder_test.cc
@@ -0,0 +1,190 @@
+// 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 <sstream>
+
+#include "draco/core/draco_test_base.h"
+#include "draco/core/draco_test_utils.h"
+#include "draco/io/obj_decoder.h"
+
+namespace draco {
+
+class ObjDecoderTest : public ::testing::Test {
+ protected:
+ template <class Geometry>
+ std::unique_ptr<Geometry> DecodeObj(const std::string &file_name) const {
+ return DecodeObj<Geometry>(file_name, false);
+ }
+
+ template <class Geometry>
+ std::unique_ptr<Geometry> DecodeObj(const std::string &file_name,
+ bool deduplicate_input_values) const {
+ const std::string path = GetTestFileFullPath(file_name);
+ ObjDecoder decoder;
+ decoder.set_deduplicate_input_values(deduplicate_input_values);
+ std::unique_ptr<Geometry> geometry(new Geometry());
+ if (!decoder.DecodeFromFile(path, geometry.get()).ok())
+ return nullptr;
+ return geometry;
+ }
+
+ template <class Geometry>
+ std::unique_ptr<Geometry> DecodeObjWithMetadata(
+ const std::string &file_name) const {
+ const std::string path = GetTestFileFullPath(file_name);
+ ObjDecoder decoder;
+ decoder.set_use_metadata(true);
+ std::unique_ptr<Geometry> geometry(new Geometry());
+ if (!decoder.DecodeFromFile(path, geometry.get()).ok())
+ return nullptr;
+ return geometry;
+ }
+
+ void test_decoding(const std::string &file_name) {
+ const std::unique_ptr<Mesh> mesh(DecodeObj<Mesh>(file_name));
+ ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
+ ASSERT_GT(mesh->num_faces(), 0);
+
+ const std::unique_ptr<PointCloud> pc(DecodeObj<PointCloud>(file_name));
+ ASSERT_NE(pc, nullptr) << "Failed to load test model " << file_name;
+ ASSERT_GT(pc->num_points(), 0);
+ }
+};
+
+TEST_F(ObjDecoderTest, ExtraVertexOBJ) {
+ const std::string file_name = "extra_vertex.obj";
+ test_decoding(file_name);
+}
+
+TEST_F(ObjDecoderTest, PartialAttributesOBJ) {
+ const std::string file_name = "cube_att_partial.obj";
+ test_decoding(file_name);
+}
+
+TEST_F(ObjDecoderTest, SubObjects) {
+ // Tests loading an Obj with sub objects.
+ const std::string file_name = "cube_att_sub_o.obj";
+ const std::unique_ptr<Mesh> mesh(DecodeObj<Mesh>(file_name));
+ ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
+ ASSERT_GT(mesh->num_faces(), 0);
+
+ // A sub object attribute should be the fourth attribute of the mesh (in this
+ // case).
+ ASSERT_EQ(mesh->num_attributes(), 4);
+ ASSERT_EQ(mesh->attribute(3)->attribute_type(), GeometryAttribute::GENERIC);
+ // There should be 3 different sub objects used in the model.
+ ASSERT_EQ(mesh->attribute(3)->size(), 3);
+ // Verify that the sub object attribute has unique id == 3.
+ ASSERT_EQ(mesh->attribute(3)->unique_id(), 3);
+}
+
+TEST_F(ObjDecoderTest, SubObjectsWithMetadata) {
+ // Tests loading an Obj with sub objects.
+ const std::string file_name = "cube_att_sub_o.obj";
+ const std::unique_ptr<Mesh> mesh(DecodeObjWithMetadata<Mesh>(file_name));
+ ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
+ ASSERT_GT(mesh->num_faces(), 0);
+
+ ASSERT_EQ(mesh->num_attributes(), 4);
+ ASSERT_EQ(mesh->attribute(3)->attribute_type(), GeometryAttribute::GENERIC);
+ // There should be 3 different sub objects used in the model.
+ ASSERT_EQ(mesh->attribute(3)->size(), 3);
+
+ // Test material names stored in metadata.
+ ASSERT_NE(mesh->GetMetadata(), nullptr);
+ ASSERT_NE(mesh->GetAttributeMetadataByAttributeId(3), nullptr);
+ int32_t sub_obj_id = 0;
+ ASSERT_TRUE(mesh->GetAttributeMetadataByAttributeId(3)->GetEntryInt(
+ "obj2", &sub_obj_id));
+ ASSERT_EQ(sub_obj_id, 2);
+}
+
+TEST_F(ObjDecoderTest, QuadOBJ) {
+ // Tests loading an Obj with quad faces.
+ const std::string file_name = "cube_quads.obj";
+ const std::unique_ptr<Mesh> mesh(DecodeObj<Mesh>(file_name));
+ ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
+ ASSERT_EQ(mesh->num_faces(), 12);
+
+ ASSERT_EQ(mesh->num_attributes(), 3);
+ ASSERT_EQ(mesh->num_points(), 4 * 6); // Four points per quad face.
+}
+
+TEST_F(ObjDecoderTest, ComplexPolyOBJ) {
+ // Tests that we fail to load an obj with complex polygon (expected failure).
+ const std::string file_name = "invalid/complex_poly.obj";
+ const std::unique_ptr<Mesh> mesh(DecodeObj<Mesh>(file_name));
+ ASSERT_EQ(mesh, nullptr);
+}
+
+TEST_F(ObjDecoderTest, EmptyNameOBJ) {
+ // Tests that we load an obj file that has an sub-object defined with an empty
+ // name.
+ const std::string file_name = "empty_name.obj";
+ const std::unique_ptr<Mesh> mesh(DecodeObj<Mesh>(file_name));
+ ASSERT_NE(mesh, nullptr);
+ ASSERT_EQ(mesh->num_attributes(), 1);
+ // Three valid entries in the attribute are expected.
+ ASSERT_EQ(mesh->attribute(0)->size(), 3);
+}
+
+TEST_F(ObjDecoderTest, PointCloudOBJ) {
+ // Tests that we load an obj file that does not contain any faces.
+ const std::string file_name = "test_lines.obj";
+ const std::unique_ptr<Mesh> mesh(DecodeObj<Mesh>(file_name, false));
+ ASSERT_NE(mesh, nullptr);
+ ASSERT_EQ(mesh->num_faces(), 0);
+ ASSERT_EQ(mesh->num_attributes(), 1);
+ ASSERT_EQ(mesh->attribute(0)->size(), 484);
+}
+
+TEST_F(ObjDecoderTest, WrongAttributeMapping) {
+ // Tests that we load an obj file that contains invalid mapping between
+ // attribute indices and values. In such case the invalid indices should be
+ // ignored.
+ const std::string file_name = "test_wrong_attribute_mapping.obj";
+ const std::unique_ptr<Mesh> mesh(DecodeObj<Mesh>(file_name, false));
+ ASSERT_NE(mesh, nullptr);
+ ASSERT_EQ(mesh->num_faces(), 1);
+ ASSERT_EQ(mesh->num_attributes(), 1);
+ ASSERT_EQ(mesh->attribute(0)->size(), 3);
+}
+
+TEST_F(ObjDecoderTest, TestObjDecodingAll) {
+ // test if we can read all obj that are currently in test folder.
+ test_decoding("bunny_norm.obj");
+ // test_decoding("complex_poly.obj"); // not supported see test above
+ test_decoding("cube_att.obj");
+ test_decoding("cube_att_partial.obj");
+ test_decoding("cube_att_sub_o.obj");
+ test_decoding("cube_quads.obj");
+ test_decoding("cube_subd.obj");
+ test_decoding("eof_test.obj");
+ test_decoding("extra_vertex.obj");
+ test_decoding("mat_test.obj");
+ test_decoding("one_face_123.obj");
+ test_decoding("one_face_312.obj");
+ test_decoding("one_face_321.obj");
+ test_decoding("sphere.obj");
+ test_decoding("test_nm.obj");
+ test_decoding("test_nm_trans.obj");
+ test_decoding("test_sphere.obj");
+ test_decoding("three_faces_123.obj");
+ test_decoding("three_faces_312.obj");
+ test_decoding("two_faces_123.obj");
+ test_decoding("two_faces_312.obj");
+ test_decoding("inf_nan.obj");
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/io/obj_encoder.cc b/extern/draco/dracoenc/src/draco/io/obj_encoder.cc
new file mode 100644
index 00000000000..807506e3536
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/obj_encoder.cc
@@ -0,0 +1,314 @@
+// 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 "draco/io/obj_encoder.h"
+
+#include <fstream>
+
+#include "draco/metadata/geometry_metadata.h"
+
+namespace draco {
+
+ObjEncoder::ObjEncoder()
+ : pos_att_(nullptr),
+ tex_coord_att_(nullptr),
+ normal_att_(nullptr),
+ material_att_(nullptr),
+ sub_obj_att_(nullptr),
+ out_buffer_(nullptr),
+ in_point_cloud_(nullptr),
+ in_mesh_(nullptr),
+ current_sub_obj_id_(-1),
+ current_material_id_(-1) {}
+
+bool ObjEncoder::EncodeToFile(const PointCloud &pc,
+ const std::string &file_name) {
+ std::ofstream file(file_name);
+ if (!file)
+ return false; // File could not be opened.
+ file_name_ = file_name;
+ // Encode the mesh into a buffer.
+ EncoderBuffer buffer;
+ if (!EncodeToBuffer(pc, &buffer))
+ return false;
+ // Write the buffer into the file.
+ file.write(buffer.data(), buffer.size());
+ return true;
+}
+
+bool ObjEncoder::EncodeToFile(const Mesh &mesh, const std::string &file_name) {
+ in_mesh_ = &mesh;
+ return EncodeToFile(static_cast<const PointCloud &>(mesh), file_name);
+}
+
+bool ObjEncoder::EncodeToBuffer(const PointCloud &pc,
+ EncoderBuffer *out_buffer) {
+ in_point_cloud_ = &pc;
+ out_buffer_ = out_buffer;
+ if (!EncodeInternal())
+ return ExitAndCleanup(false);
+ return ExitAndCleanup(true);
+}
+
+bool ObjEncoder::EncodeToBuffer(const Mesh &mesh, EncoderBuffer *out_buffer) {
+ in_mesh_ = &mesh;
+ return EncodeToBuffer(static_cast<const PointCloud &>(mesh), out_buffer);
+}
+
+bool ObjEncoder::EncodeInternal() {
+ pos_att_ = nullptr;
+ tex_coord_att_ = nullptr;
+ normal_att_ = nullptr;
+ material_att_ = nullptr;
+ sub_obj_att_ = nullptr;
+ current_sub_obj_id_ = -1;
+ current_material_id_ = -1;
+ if (!GetSubObjects())
+ return false;
+ if (!EncodeMaterialFileName())
+ return false;
+ if (!EncodePositions())
+ return false;
+ if (!EncodeTextureCoordinates())
+ return false;
+ if (!EncodeNormals())
+ return false;
+ if (in_mesh_ && !EncodeFaces())
+ return false;
+ return true;
+}
+
+bool ObjEncoder::ExitAndCleanup(bool return_value) {
+ in_mesh_ = nullptr;
+ in_point_cloud_ = nullptr;
+ out_buffer_ = nullptr;
+ pos_att_ = nullptr;
+ tex_coord_att_ = nullptr;
+ normal_att_ = nullptr;
+ material_att_ = nullptr;
+ sub_obj_att_ = nullptr;
+ current_sub_obj_id_ = -1;
+ current_material_id_ = -1;
+ file_name_.clear();
+ return return_value;
+}
+
+bool ObjEncoder::GetSubObjects() {
+ const GeometryMetadata *pc_metadata = in_point_cloud_->GetMetadata();
+ if (!pc_metadata)
+ return true;
+ const AttributeMetadata *sub_obj_metadata =
+ pc_metadata->GetAttributeMetadataByStringEntry("name", "sub_obj");
+ if (!sub_obj_metadata)
+ return true;
+ sub_obj_id_to_name_.clear();
+ for (const auto &entry : sub_obj_metadata->entries()) {
+ // Sub-object id must be int.
+ int value = 0;
+ if (!entry.second.GetValue(&value))
+ continue;
+ sub_obj_id_to_name_[value] = entry.first;
+ }
+ sub_obj_att_ = in_point_cloud_->GetAttributeByUniqueId(
+ sub_obj_metadata->att_unique_id());
+ if (sub_obj_att_ == nullptr || sub_obj_att_->size() == 0)
+ return false;
+ return true;
+}
+
+bool ObjEncoder::EncodeMaterialFileName() {
+ const GeometryMetadata *pc_metadata = in_point_cloud_->GetMetadata();
+ const AttributeMetadata *material_metadata = nullptr;
+ if (pc_metadata) {
+ material_metadata =
+ pc_metadata->GetAttributeMetadataByStringEntry("name", "material");
+ }
+ std::string material_file_name;
+ std::string material_full_path;
+ if (!material_metadata)
+ return true;
+ if (!material_metadata->GetEntryString("file_name", &material_file_name))
+ return false;
+ buffer()->Encode("mtllib ", 7);
+ buffer()->Encode(material_file_name.c_str(), material_file_name.size());
+ buffer()->Encode("\n", 1);
+ material_id_to_name_.clear();
+ for (const auto &entry : material_metadata->entries()) {
+ // Material id must be int.
+ int value = 0;
+ // Found entry that are not material id, e.g. file name as a string.
+ if (!entry.second.GetValue(&value))
+ continue;
+ material_id_to_name_[value] = entry.first;
+ }
+ material_att_ = in_point_cloud_->GetAttributeByUniqueId(
+ material_metadata->att_unique_id());
+ if (material_att_ == nullptr || material_att_->size() == 0)
+ return false;
+ return true;
+}
+
+bool ObjEncoder::EncodePositions() {
+ const PointAttribute *const att =
+ in_point_cloud_->GetNamedAttribute(GeometryAttribute::POSITION);
+ if (att == nullptr || att->size() == 0)
+ return false; // Position attribute must be valid.
+ std::array<float, 3> value;
+ for (AttributeValueIndex i(0); i < static_cast<uint32_t>(att->size()); ++i) {
+ if (!att->ConvertValue<float, 3>(i, &value[0]))
+ return false;
+ buffer()->Encode("v ", 2);
+ EncodeFloatList(&value[0], 3);
+ buffer()->Encode("\n", 1);
+ }
+ pos_att_ = att;
+ return true;
+}
+
+bool ObjEncoder::EncodeTextureCoordinates() {
+ const PointAttribute *const att =
+ in_point_cloud_->GetNamedAttribute(GeometryAttribute::TEX_COORD);
+ if (att == nullptr || att->size() == 0)
+ return true; // It's OK if we don't have texture coordinates.
+ std::array<float, 2> value;
+ for (AttributeValueIndex i(0); i < static_cast<uint32_t>(att->size()); ++i) {
+ if (!att->ConvertValue<float, 2>(i, &value[0]))
+ return false;
+ buffer()->Encode("vt ", 3);
+ EncodeFloatList(&value[0], 2);
+ buffer()->Encode("\n", 1);
+ }
+ tex_coord_att_ = att;
+ return true;
+}
+
+bool ObjEncoder::EncodeNormals() {
+ const PointAttribute *const att =
+ in_point_cloud_->GetNamedAttribute(GeometryAttribute::NORMAL);
+ if (att == nullptr || att->size() == 0)
+ return true; // It's OK if we don't have normals.
+ std::array<float, 3> value;
+ for (AttributeValueIndex i(0); i < static_cast<uint32_t>(att->size()); ++i) {
+ if (!att->ConvertValue<float, 3>(i, &value[0]))
+ return false;
+ buffer()->Encode("vn ", 3);
+ EncodeFloatList(&value[0], 3);
+ buffer()->Encode("\n", 1);
+ }
+ normal_att_ = att;
+ return true;
+}
+
+bool ObjEncoder::EncodeFaces() {
+ for (FaceIndex i(0); i < in_mesh_->num_faces(); ++i) {
+ if (sub_obj_att_)
+ if (!EncodeSubObject(i))
+ return false;
+ if (material_att_)
+ if (!EncodeMaterial(i))
+ return false;
+ buffer()->Encode('f');
+ for (int j = 0; j < 3; ++j) {
+ if (!EncodeFaceCorner(i, j))
+ return false;
+ }
+ buffer()->Encode("\n", 1);
+ }
+ return true;
+}
+
+bool ObjEncoder::EncodeMaterial(FaceIndex face_id) {
+ int material_id = 0;
+ // Pick the first corner, all corners of a face should have same id.
+ const PointIndex vert_index = in_mesh_->face(face_id)[0];
+ const AttributeValueIndex index_id(material_att_->mapped_index(vert_index));
+ if (!material_att_->ConvertValue<int>(index_id, &material_id)) {
+ return false;
+ }
+
+ if (material_id != current_material_id_) {
+ // Update material information.
+ buffer()->Encode("usemtl ", 7);
+ const auto mat_ptr = material_id_to_name_.find(material_id);
+ // If the material id is not found.
+ if (mat_ptr == material_id_to_name_.end())
+ return false;
+ buffer()->Encode(mat_ptr->second.c_str(), mat_ptr->second.size());
+ buffer()->Encode("\n", 1);
+ current_material_id_ = material_id;
+ }
+ return true;
+}
+
+bool ObjEncoder::EncodeSubObject(FaceIndex face_id) {
+ int sub_obj_id = 0;
+ // Pick the first corner, all corners of a face should have same id.
+ const PointIndex vert_index = in_mesh_->face(face_id)[0];
+ const AttributeValueIndex index_id(sub_obj_att_->mapped_index(vert_index));
+ if (!sub_obj_att_->ConvertValue<int>(index_id, &sub_obj_id)) {
+ return false;
+ }
+ if (sub_obj_id != current_sub_obj_id_) {
+ buffer()->Encode("o ", 2);
+ const auto sub_obj_ptr = sub_obj_id_to_name_.find(sub_obj_id);
+ if (sub_obj_ptr == sub_obj_id_to_name_.end())
+ return false;
+ buffer()->Encode(sub_obj_ptr->second.c_str(), sub_obj_ptr->second.size());
+ buffer()->Encode("\n", 1);
+ current_sub_obj_id_ = sub_obj_id;
+ }
+ return true;
+}
+
+bool ObjEncoder::EncodeFaceCorner(FaceIndex face_id, int local_corner_id) {
+ buffer()->Encode(' ');
+ const PointIndex vert_index = in_mesh_->face(face_id)[local_corner_id];
+ // Note that in the OBJ format, all indices are encoded starting from index 1.
+ // Encode position index.
+ EncodeInt(pos_att_->mapped_index(vert_index).value() + 1);
+ if (tex_coord_att_ || normal_att_) {
+ // Encoding format is pos_index/tex_coord_index/normal_index.
+ // If tex_coords are not present, we must encode pos_index//normal_index.
+ buffer()->Encode('/');
+ if (tex_coord_att_) {
+ EncodeInt(tex_coord_att_->mapped_index(vert_index).value() + 1);
+ }
+ if (normal_att_) {
+ buffer()->Encode('/');
+ EncodeInt(normal_att_->mapped_index(vert_index).value() + 1);
+ }
+ }
+ return true;
+}
+
+void ObjEncoder::EncodeFloat(float val) {
+ snprintf(num_buffer_, sizeof(num_buffer_), "%f", val);
+ buffer()->Encode(num_buffer_, strlen(num_buffer_));
+}
+
+void ObjEncoder::EncodeFloatList(float *vals, int num_vals) {
+ for (int i = 0; i < num_vals; ++i) {
+ if (i > 0) {
+ buffer()->Encode(' ');
+ }
+ EncodeFloat(vals[i]);
+ }
+}
+
+void ObjEncoder::EncodeInt(int32_t val) {
+ snprintf(num_buffer_, sizeof(num_buffer_), "%d", val);
+ buffer()->Encode(num_buffer_, strlen(num_buffer_));
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/io/obj_encoder.h b/extern/draco/dracoenc/src/draco/io/obj_encoder.h
new file mode 100644
index 00000000000..352a04774c2
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/obj_encoder.h
@@ -0,0 +1,90 @@
+// 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_IO_OBJ_ENCODER_H_
+#define DRACO_IO_OBJ_ENCODER_H_
+
+#include "draco/core/encoder_buffer.h"
+#include "draco/mesh/mesh.h"
+
+namespace draco {
+
+// Class for encoding input draco::Mesh or draco::PointCloud into the Wavefront
+// OBJ format.
+class ObjEncoder {
+ public:
+ ObjEncoder();
+
+ // Encodes the mesh or a point cloud and saves it into a file.
+ // Returns false when either the encoding failed or when the file couldn't be
+ // opened.
+ bool EncodeToFile(const PointCloud &pc, const std::string &file_name);
+ bool EncodeToFile(const Mesh &mesh, const std::string &file_name);
+
+ // Encodes the mesh or the point cloud into a buffer.
+ bool EncodeToBuffer(const PointCloud &pc, EncoderBuffer *out_buffer);
+ bool EncodeToBuffer(const Mesh &mesh, EncoderBuffer *out_buffer);
+
+ protected:
+ bool EncodeInternal();
+ EncoderBuffer *buffer() const { return out_buffer_; }
+ bool ExitAndCleanup(bool return_value);
+
+ private:
+ bool GetSubObjects();
+ bool EncodeMaterialFileName();
+ bool EncodePositions();
+ bool EncodeTextureCoordinates();
+ bool EncodeNormals();
+ bool EncodeFaces();
+ bool EncodeSubObject(FaceIndex face_id);
+ bool EncodeMaterial(FaceIndex face_id);
+ bool EncodeFaceCorner(FaceIndex face_id, int local_corner_id);
+
+ void EncodeFloat(float val);
+ void EncodeFloatList(float *vals, int num_vals);
+ void EncodeInt(int32_t val);
+
+ // Various attributes used by the encoder. If an attribute is not used, it is
+ // set to nullptr.
+ const PointAttribute *pos_att_;
+ const PointAttribute *tex_coord_att_;
+ const PointAttribute *normal_att_;
+ const PointAttribute *material_att_;
+ const PointAttribute *sub_obj_att_;
+
+ // Buffer used for encoding float/int numbers.
+ char num_buffer_[20];
+
+ EncoderBuffer *out_buffer_;
+
+ const PointCloud *in_point_cloud_;
+ const Mesh *in_mesh_;
+
+ // Store sub object name for each value.
+ std::unordered_map<int, std::string> sub_obj_id_to_name_;
+ // Current sub object id of faces.
+ int current_sub_obj_id_;
+
+ // Store material name for each value in material attribute.
+ std::unordered_map<int, std::string> material_id_to_name_;
+ // Current material id of faces.
+ int current_material_id_;
+
+ std::string file_name_;
+};
+
+} // namespace draco
+
+#endif // DRACO_IO_OBJ_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/io/obj_encoder_test.cc b/extern/draco/dracoenc/src/draco/io/obj_encoder_test.cc
new file mode 100644
index 00000000000..79ea1712806
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/obj_encoder_test.cc
@@ -0,0 +1,106 @@
+// Copyright 2017 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 <fstream>
+#include <sstream>
+
+#include "draco/core/draco_test_base.h"
+#include "draco/core/draco_test_utils.h"
+#include "draco/io/obj_decoder.h"
+#include "draco/io/obj_encoder.h"
+
+namespace draco {
+
+class ObjEncoderTest : public ::testing::Test {
+ protected:
+ void CompareMeshes(const Mesh *mesh0, const Mesh *mesh1) {
+ ASSERT_EQ(mesh0->num_faces(), mesh1->num_faces());
+ ASSERT_EQ(mesh0->num_attributes(), mesh1->num_attributes());
+ for (size_t att_id = 0; att_id < mesh0->num_attributes(); ++att_id) {
+ ASSERT_EQ(mesh0->attribute(att_id)->size(),
+ mesh1->attribute(att_id)->size());
+ }
+ }
+
+ // Encode a mesh using the ObjEncoder and then decode to verify the encoding.
+ std::unique_ptr<Mesh> EncodeAndDecodeMesh(const Mesh *mesh) {
+ EncoderBuffer encoder_buffer;
+ ObjEncoder encoder;
+ if (!encoder.EncodeToBuffer(*mesh, &encoder_buffer))
+ return nullptr;
+
+ DecoderBuffer decoder_buffer;
+ decoder_buffer.Init(encoder_buffer.data(), encoder_buffer.size());
+ std::unique_ptr<Mesh> decoded_mesh(new Mesh());
+ ObjDecoder decoder;
+ decoder.set_use_metadata(true);
+ if (!decoder.DecodeFromBuffer(&decoder_buffer, decoded_mesh.get()).ok())
+ return nullptr;
+ return decoded_mesh;
+ }
+
+ void test_encoding(const std::string &file_name) {
+ const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name, true));
+
+ ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
+ ASSERT_GT(mesh->num_faces(), 0);
+
+ const std::unique_ptr<Mesh> decoded_mesh = EncodeAndDecodeMesh(mesh.get());
+ CompareMeshes(mesh.get(), decoded_mesh.get());
+ }
+};
+
+TEST_F(ObjEncoderTest, HasSubObject) { test_encoding("cube_att_sub_o.obj"); }
+
+TEST_F(ObjEncoderTest, HasMaterial) {
+ const std::unique_ptr<Mesh> mesh0(ReadMeshFromTestFile("mat_test.obj", true));
+ ASSERT_NE(mesh0, nullptr);
+ const std::unique_ptr<Mesh> mesh1 = EncodeAndDecodeMesh(mesh0.get());
+ ASSERT_NE(mesh1, nullptr);
+ ASSERT_EQ(mesh0->num_faces(), mesh1->num_faces());
+ ASSERT_EQ(mesh0->num_attributes(), mesh1->num_attributes());
+ // Position attribute should be the same.
+ ASSERT_EQ(mesh0->attribute(0)->size(), mesh1->attribute(0)->size());
+ // Since |mesh1| is decoded from buffer, it has not material file. So the
+ // size of material attribute is the number of materials used in the obj
+ // file which is 7. The size of material attribute of |mesh0| decoded from
+ // the obj file will be the number of materials defined in the .mtl file.
+ ASSERT_EQ(mesh0->attribute(1)->size(), 29);
+ ASSERT_EQ(mesh1->attribute(1)->size(), 7);
+}
+
+TEST_F(ObjEncoderTest, TestObjEncodingAll) {
+ // Test decoded mesh from encoded obj file stays the same.
+ test_encoding("bunny_norm.obj");
+ test_encoding("cube_att.obj");
+ test_encoding("cube_att_partial.obj");
+ test_encoding("cube_quads.obj");
+ test_encoding("cube_subd.obj");
+ test_encoding("extra_vertex.obj");
+ test_encoding("multiple_isolated_triangles.obj");
+ test_encoding("multiple_tetrahedrons.obj");
+ test_encoding("one_face_123.obj");
+ test_encoding("one_face_312.obj");
+ test_encoding("one_face_321.obj");
+ test_encoding("sphere.obj");
+ test_encoding("test_nm.obj");
+ test_encoding("test_nm_trans.obj");
+ test_encoding("test_sphere.obj");
+ test_encoding("three_faces_123.obj");
+ test_encoding("three_faces_312.obj");
+ test_encoding("two_faces_123.obj");
+ test_encoding("two_faces_312.obj");
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/io/parser_utils.cc b/extern/draco/dracoenc/src/draco/io/parser_utils.cc
new file mode 100644
index 00000000000..e68abb1f263
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/parser_utils.cc
@@ -0,0 +1,232 @@
+// 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 "draco/io/parser_utils.h"
+
+#include <algorithm>
+#include <cctype>
+#include <cmath>
+#include <iterator>
+
+namespace draco {
+namespace parser {
+
+void SkipCharacters(DecoderBuffer *buffer, const char *skip_chars) {
+ if (skip_chars == nullptr)
+ return;
+ const int num_skip_chars = static_cast<int>(strlen(skip_chars));
+ char c;
+ while (buffer->Peek(&c)) {
+ // Check all characters in the pattern.
+ bool skip = false;
+ for (int i = 0; i < num_skip_chars; ++i) {
+ if (c == skip_chars[i]) {
+ skip = true;
+ break;
+ }
+ }
+ if (!skip)
+ return;
+ buffer->Advance(1);
+ }
+}
+
+void SkipWhitespace(DecoderBuffer *buffer) {
+ bool end_reached = false;
+ while (PeekWhitespace(buffer, &end_reached) && !end_reached) {
+ // Skip the whitespace character
+ buffer->Advance(1);
+ }
+}
+
+bool PeekWhitespace(DecoderBuffer *buffer, bool *end_reached) {
+ uint8_t c;
+ if (!buffer->Peek(&c)) {
+ *end_reached = true;
+ return false; // eof reached.
+ }
+ if (!isspace(c))
+ return false; // Non-whitespace character reached.
+ return true;
+}
+
+void SkipLine(DecoderBuffer *buffer) {
+ char c;
+ while (buffer->Peek(&c)) {
+ // Skip the character.
+ buffer->Advance(1);
+ if (c == '\n')
+ return; // Return at the end of line
+ }
+}
+
+bool ParseFloat(DecoderBuffer *buffer, float *value) {
+ // Read optional sign.
+ char ch;
+ if (!buffer->Peek(&ch))
+ return false;
+ int sign = GetSignValue(ch);
+ if (sign != 0) {
+ buffer->Advance(1);
+ } else {
+ sign = 1;
+ }
+
+ // Parse integer component.
+ bool have_digits = false;
+ double v = 0.0;
+ while (buffer->Peek(&ch) && ch >= '0' && ch <= '9') {
+ v *= 10.0;
+ v += (ch - '0');
+ buffer->Advance(1);
+ have_digits = true;
+ }
+ if (ch == '.') {
+ // Parse fractional component.
+ buffer->Advance(1);
+ double fraction = 1.0;
+ while (buffer->Peek(&ch) && ch >= '0' && ch <= '9') {
+ fraction *= 0.1;
+ v += (ch - '0') * fraction;
+ buffer->Advance(1);
+ have_digits = true;
+ }
+ }
+
+ if (!have_digits) {
+ // Check for special constants (inf, nan, ...).
+ std::string text;
+ if (!ParseString(buffer, &text))
+ return false;
+ if (text == "inf" || text == "Inf") {
+ v = std::numeric_limits<double>::infinity();
+ } else if (text == "nan" || text == "NaN") {
+ v = nan("");
+ } else {
+ // Invalid string.
+ return false;
+ }
+ } else {
+ // Handle exponent if present.
+ if (ch == 'e' || ch == 'E') {
+ buffer->Advance(1); // Skip 'e' marker.
+
+ // Parse integer exponent.
+ int32_t exponent = 0;
+ if (!ParseSignedInt(buffer, &exponent))
+ return false;
+
+ // Apply exponent scaling to value.
+ v *= pow(10.0, exponent);
+ }
+ }
+
+ *value = (sign < 0) ? static_cast<float>(-v) : static_cast<float>(v);
+ return true;
+}
+
+bool ParseSignedInt(DecoderBuffer *buffer, int32_t *value) {
+ // Parse any explicit sign and set the appropriate largest magnitude
+ // value that can be represented without overflow.
+ char ch;
+ if (!buffer->Peek(&ch))
+ return false;
+ const int sign = GetSignValue(ch);
+ if (sign != 0)
+ buffer->Advance(1);
+
+ // Attempt to parse integer body.
+ uint32_t v;
+ if (!ParseUnsignedInt(buffer, &v))
+ return false;
+ *value = (sign < 0) ? -v : v;
+ return true;
+}
+
+bool ParseUnsignedInt(DecoderBuffer *buffer, uint32_t *value) {
+ // Parse the number until we run out of digits.
+ uint32_t v = 0;
+ char ch;
+ bool have_digits = false;
+ while (buffer->Peek(&ch) && ch >= '0' && ch <= '9') {
+ v *= 10;
+ v += (ch - '0');
+ buffer->Advance(1);
+ have_digits = true;
+ }
+ if (!have_digits)
+ return false;
+ *value = v;
+ return true;
+}
+
+int GetSignValue(char c) {
+ if (c == '-')
+ return -1;
+ if (c == '+')
+ return 1;
+ return 0;
+}
+
+bool ParseString(DecoderBuffer *buffer, std::string *out_string) {
+ out_string->clear();
+ SkipWhitespace(buffer);
+ bool end_reached = false;
+ while (!PeekWhitespace(buffer, &end_reached) && !end_reached) {
+ char c;
+ if (!buffer->Decode(&c))
+ return false;
+ *out_string += c;
+ }
+ return true;
+}
+
+void ParseLine(DecoderBuffer *buffer, std::string *out_string) {
+ out_string->clear();
+ char c;
+ while (buffer->Peek(&c)) {
+ // Skip the character.
+ buffer->Advance(1);
+ if (c == '\n')
+ return; // Return at the end of line.
+ if (c == '\r')
+ continue; // Ignore extra line ending characters.
+ *out_string += c;
+ }
+}
+
+DecoderBuffer ParseLineIntoDecoderBuffer(DecoderBuffer *buffer) {
+ const char *const head = buffer->data_head();
+ char c;
+ while (buffer->Peek(&c)) {
+ // Skip the character.
+ buffer->Advance(1);
+ if (c == '\n')
+ break; // End of the line reached.
+ if (c == '\r')
+ continue; // Ignore extra line ending characters.
+ }
+ DecoderBuffer out_buffer;
+ out_buffer.Init(head, buffer->data_head() - head);
+ return out_buffer;
+}
+
+std::string ToLower(const std::string &str) {
+ std::string out;
+ std::transform(str.begin(), str.end(), std::back_inserter(out), tolower);
+ return out;
+}
+
+} // namespace parser
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/io/parser_utils.h b/extern/draco/dracoenc/src/draco/io/parser_utils.h
new file mode 100644
index 00000000000..aa629053068
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/parser_utils.h
@@ -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.
+//
+#ifndef DRACO_IO_PARSER_UTILS_H_
+#define DRACO_IO_PARSER_UTILS_H_
+
+#include "draco/core/decoder_buffer.h"
+
+namespace draco {
+namespace parser {
+
+// Skips to first character not included in |skip_chars|.
+void SkipCharacters(DecoderBuffer *buffer, const char *skip_chars);
+
+// Skips any whitespace until a regular character is reached.
+void SkipWhitespace(DecoderBuffer *buffer);
+
+// Returns true if the next character is a whitespace.
+// |end_reached| is set to true when the end of the stream is reached.
+bool PeekWhitespace(DecoderBuffer *buffer, bool *end_reached);
+void SkipLine(DecoderBuffer *buffer);
+
+// Parses signed floating point number or returns false on error.
+bool ParseFloat(DecoderBuffer *buffer, float *value);
+
+// Parses a signed integer (can be preceded by '-' or '+' characters.
+bool ParseSignedInt(DecoderBuffer *buffer, int32_t *value);
+
+// Parses an unsigned integer. It cannot be preceded by '-' or '+'
+// characters.
+bool ParseUnsignedInt(DecoderBuffer *buffer, uint32_t *value);
+
+// Returns -1 if c == '-'.
+// Returns +1 if c == '+'.
+// Returns 0 otherwise.
+int GetSignValue(char c);
+
+// Parses a string until a whitespace or end of file is reached.
+bool ParseString(DecoderBuffer *buffer, std::string *out_string);
+
+// Parses the entire line into the buffer (excluding the new line character).
+void ParseLine(DecoderBuffer *buffer, std::string *out_string);
+
+// Parses line and stores into a new decoder buffer.
+DecoderBuffer ParseLineIntoDecoderBuffer(DecoderBuffer *buffer);
+
+// Returns a string with all characters converted to lower case.
+std::string ToLower(const std::string &str);
+
+} // namespace parser
+} // namespace draco
+
+#endif // DRACO_IO_PARSER_UTILS_H_
diff --git a/extern/draco/dracoenc/src/draco/io/ply_decoder.cc b/extern/draco/dracoenc/src/draco/io/ply_decoder.cc
new file mode 100644
index 00000000000..01a97e3dacc
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/ply_decoder.cc
@@ -0,0 +1,285 @@
+// 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 "draco/io/ply_decoder.h"
+
+#include <fstream>
+
+#include "draco/core/macros.h"
+#include "draco/io/ply_property_reader.h"
+
+namespace draco {
+
+PlyDecoder::PlyDecoder() : out_mesh_(nullptr), out_point_cloud_(nullptr) {}
+
+bool PlyDecoder::DecodeFromFile(const std::string &file_name, Mesh *out_mesh) {
+ out_mesh_ = out_mesh;
+ return DecodeFromFile(file_name, static_cast<PointCloud *>(out_mesh));
+}
+
+bool PlyDecoder::DecodeFromFile(const std::string &file_name,
+ PointCloud *out_point_cloud) {
+ std::ifstream file(file_name, std::ios::binary);
+ if (!file)
+ return false;
+ // Read the whole file into a buffer.
+ auto pos0 = file.tellg();
+ file.seekg(0, std::ios::end);
+ auto file_size = file.tellg() - pos0;
+ if (file_size == 0)
+ return false;
+ file.seekg(0, std::ios::beg);
+ std::vector<char> data(file_size);
+ file.read(&data[0], file_size);
+
+ buffer_.Init(&data[0], file_size);
+ return DecodeFromBuffer(&buffer_, out_point_cloud);
+}
+
+bool PlyDecoder::DecodeFromBuffer(DecoderBuffer *buffer, Mesh *out_mesh) {
+ out_mesh_ = out_mesh;
+ return DecodeFromBuffer(buffer, static_cast<PointCloud *>(out_mesh));
+}
+
+bool PlyDecoder::DecodeFromBuffer(DecoderBuffer *buffer,
+ PointCloud *out_point_cloud) {
+ out_point_cloud_ = out_point_cloud;
+ buffer_.Init(buffer->data_head(), buffer->remaining_size());
+ return DecodeInternal();
+}
+
+bool PlyDecoder::DecodeInternal() {
+ PlyReader ply_reader;
+ if (!ply_reader.Read(buffer()))
+ return false;
+ // First, decode the connectivity data.
+ if (out_mesh_ && !DecodeFaceData(ply_reader.GetElementByName("face")))
+ return false;
+ // Decode all attributes.
+ if (!DecodeVertexData(ply_reader.GetElementByName("vertex")))
+ return false;
+#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
+ // In case there are no faces this is just a point cloud which does
+ // not require deduplication.
+ if (out_mesh_ && out_mesh_->num_faces() != 0) {
+ if (!out_point_cloud_->DeduplicateAttributeValues())
+ return false;
+ out_point_cloud_->DeduplicatePointIds();
+ }
+#endif
+ return true;
+}
+
+bool PlyDecoder::DecodeFaceData(const PlyElement *face_element) {
+ // We accept point clouds now.
+ if (face_element == nullptr) {
+ return true;
+ }
+ const int64_t num_faces = face_element->num_entries();
+ out_mesh_->SetNumFaces(num_faces);
+ const PlyProperty *vertex_indices =
+ face_element->GetPropertyByName("vertex_indices");
+ if (vertex_indices == nullptr) {
+ // The property name may be named either "vertex_indices" or "vertex_index".
+ vertex_indices = face_element->GetPropertyByName("vertex_index");
+ }
+ if (vertex_indices == nullptr || !vertex_indices->is_list()) {
+ return false; // No faces defined.
+ }
+
+ PlyPropertyReader<PointIndex::ValueType> vertex_index_reader(vertex_indices);
+ Mesh::Face face;
+ FaceIndex face_index(0);
+ for (int i = 0; i < num_faces; ++i) {
+ const int64_t list_offset = vertex_indices->GetListEntryOffset(i);
+ const int64_t list_size = vertex_indices->GetListEntryNumValues(i);
+ // TODO(ostava): Assume triangular faces only for now.
+ if (list_size != 3)
+ continue; // All non-triangular faces are skipped.
+ for (int64_t c = 0; c < 3; ++c)
+ face[c] =
+ vertex_index_reader.ReadValue(static_cast<int>(list_offset + c));
+ out_mesh_->SetFace(face_index, face);
+ face_index++;
+ }
+ out_mesh_->SetNumFaces(face_index.value());
+ return true;
+}
+
+template <typename DataTypeT>
+bool PlyDecoder::ReadPropertiesToAttribute(
+ const std::vector<const PlyProperty *> &properties,
+ PointAttribute *attribute, int num_vertices) {
+ std::vector<std::unique_ptr<PlyPropertyReader<DataTypeT>>> readers;
+ readers.reserve(properties.size());
+ for (int prop = 0; prop < properties.size(); ++prop) {
+ readers.push_back(std::unique_ptr<PlyPropertyReader<DataTypeT>>(
+ new PlyPropertyReader<DataTypeT>(properties[prop])));
+ }
+ std::vector<DataTypeT> memory(properties.size());
+ for (PointIndex::ValueType i = 0; i < static_cast<uint32_t>(num_vertices);
+ ++i) {
+ for (int prop = 0; prop < properties.size(); ++prop) {
+ memory[prop] = readers[prop]->ReadValue(i);
+ }
+ attribute->SetAttributeValue(AttributeValueIndex(i), memory.data());
+ }
+ return true;
+}
+
+bool PlyDecoder::DecodeVertexData(const PlyElement *vertex_element) {
+ if (vertex_element == nullptr)
+ return false;
+ // TODO(ostava): For now, try to load x,y,z vertices and red,green,blue,alpha
+ // colors. We need to add other properties later.
+ const PlyProperty *const x_prop = vertex_element->GetPropertyByName("x");
+ const PlyProperty *const y_prop = vertex_element->GetPropertyByName("y");
+ const PlyProperty *const z_prop = vertex_element->GetPropertyByName("z");
+ if (!x_prop || !y_prop || !z_prop) {
+ // Currently, we require 3 vertex coordinates (this should be generalized
+ // later on).
+ return false;
+ }
+ const PointIndex::ValueType num_vertices = vertex_element->num_entries();
+ out_point_cloud_->set_num_points(num_vertices);
+ // Decode vertex positions.
+ {
+ // All properties must have the same type.
+ if (x_prop->data_type() != y_prop->data_type() ||
+ y_prop->data_type() != z_prop->data_type()) {
+ return false;
+ }
+ // TODO(ostava): For now assume the position types are float32 or int32.
+ const DataType dt = x_prop->data_type();
+ if (dt != DT_FLOAT32 && dt != DT_INT32)
+ return false;
+
+ GeometryAttribute va;
+ va.Init(GeometryAttribute::POSITION, nullptr, 3, dt, false,
+ DataTypeLength(dt) * 3, 0);
+ const int att_id = out_point_cloud_->AddAttribute(va, true, num_vertices);
+ std::vector<const PlyProperty *> properties;
+ properties.push_back(x_prop);
+ properties.push_back(y_prop);
+ properties.push_back(z_prop);
+ if (dt == DT_FLOAT32) {
+ ReadPropertiesToAttribute<float>(
+ properties, out_point_cloud_->attribute(att_id), num_vertices);
+ } else if (dt == DT_INT32) {
+ ReadPropertiesToAttribute<int32_t>(
+ properties, out_point_cloud_->attribute(att_id), num_vertices);
+ }
+ }
+
+ // Decode normals if present.
+ const PlyProperty *const n_x_prop = vertex_element->GetPropertyByName("nx");
+ const PlyProperty *const n_y_prop = vertex_element->GetPropertyByName("ny");
+ const PlyProperty *const n_z_prop = vertex_element->GetPropertyByName("nz");
+ if (n_x_prop != nullptr && n_y_prop != nullptr && n_z_prop != nullptr) {
+ // For now, all normal properties must be set and of type float32
+ if (n_x_prop->data_type() == DT_FLOAT32 &&
+ n_y_prop->data_type() == DT_FLOAT32 &&
+ n_z_prop->data_type() == DT_FLOAT32) {
+ PlyPropertyReader<float> x_reader(n_x_prop);
+ PlyPropertyReader<float> y_reader(n_y_prop);
+ PlyPropertyReader<float> z_reader(n_z_prop);
+ GeometryAttribute va;
+ va.Init(GeometryAttribute::NORMAL, nullptr, 3, DT_FLOAT32, false,
+ sizeof(float) * 3, 0);
+ const int att_id = out_point_cloud_->AddAttribute(va, true, num_vertices);
+ for (PointIndex::ValueType i = 0; i < num_vertices; ++i) {
+ std::array<float, 3> val;
+ val[0] = x_reader.ReadValue(i);
+ val[1] = y_reader.ReadValue(i);
+ val[2] = z_reader.ReadValue(i);
+ out_point_cloud_->attribute(att_id)->SetAttributeValue(
+ AttributeValueIndex(i), &val[0]);
+ }
+ }
+ }
+
+ // Decode color data if present.
+ int num_colors = 0;
+ const PlyProperty *const r_prop = vertex_element->GetPropertyByName("red");
+ const PlyProperty *const g_prop = vertex_element->GetPropertyByName("green");
+ const PlyProperty *const b_prop = vertex_element->GetPropertyByName("blue");
+ const PlyProperty *const a_prop = vertex_element->GetPropertyByName("alpha");
+ if (r_prop)
+ ++num_colors;
+ if (g_prop)
+ ++num_colors;
+ if (b_prop)
+ ++num_colors;
+ if (a_prop)
+ ++num_colors;
+
+ if (num_colors) {
+ std::vector<std::unique_ptr<PlyPropertyReader<uint8_t>>> color_readers;
+ const PlyProperty *p;
+ if (r_prop) {
+ p = r_prop;
+ // TODO(ostava): For now ensure the data type of all components is uint8.
+ DRACO_DCHECK_EQ(true, p->data_type() == DT_UINT8);
+ if (p->data_type() != DT_UINT8)
+ return false;
+ color_readers.push_back(std::unique_ptr<PlyPropertyReader<uint8_t>>(
+ new PlyPropertyReader<uint8_t>(p)));
+ }
+ if (g_prop) {
+ p = g_prop;
+ // TODO(ostava): For now ensure the data type of all components is uint8.
+ DRACO_DCHECK_EQ(true, p->data_type() == DT_UINT8);
+ if (p->data_type() != DT_UINT8)
+ return false;
+ color_readers.push_back(std::unique_ptr<PlyPropertyReader<uint8_t>>(
+ new PlyPropertyReader<uint8_t>(p)));
+ }
+ if (b_prop) {
+ p = b_prop;
+ // TODO(ostava): For now ensure the data type of all components is uint8.
+ DRACO_DCHECK_EQ(true, p->data_type() == DT_UINT8);
+ if (p->data_type() != DT_UINT8)
+ return false;
+ color_readers.push_back(std::unique_ptr<PlyPropertyReader<uint8_t>>(
+ new PlyPropertyReader<uint8_t>(p)));
+ }
+ if (a_prop) {
+ p = a_prop;
+ // TODO(ostava): For now ensure the data type of all components is uint8.
+ DRACO_DCHECK_EQ(true, p->data_type() == DT_UINT8);
+ if (p->data_type() != DT_UINT8)
+ return false;
+ color_readers.push_back(std::unique_ptr<PlyPropertyReader<uint8_t>>(
+ new PlyPropertyReader<uint8_t>(p)));
+ }
+
+ GeometryAttribute va;
+ va.Init(GeometryAttribute::COLOR, nullptr, num_colors, DT_UINT8, true,
+ sizeof(uint8_t) * num_colors, 0);
+ const int32_t att_id =
+ out_point_cloud_->AddAttribute(va, true, num_vertices);
+ for (PointIndex::ValueType i = 0; i < num_vertices; ++i) {
+ std::array<uint8_t, 4> val;
+ for (int j = 0; j < num_colors; j++) {
+ val[j] = color_readers[j]->ReadValue(i);
+ }
+ out_point_cloud_->attribute(att_id)->SetAttributeValue(
+ AttributeValueIndex(i), &val[0]);
+ }
+ }
+
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/io/ply_decoder.h b/extern/draco/dracoenc/src/draco/io/ply_decoder.h
new file mode 100644
index 00000000000..60a48de730e
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/ply_decoder.h
@@ -0,0 +1,69 @@
+// 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_IO_MESH_PLY_DECODER_H_
+#define DRACO_IO_MESH_PLY_DECODER_H_
+
+#include <string>
+
+#include "draco/draco_features.h"
+
+#include "draco/core/decoder_buffer.h"
+#include "draco/io/ply_reader.h"
+#include "draco/mesh/mesh.h"
+
+namespace draco {
+
+// Decodes a Wavefront OBJ file into draco::Mesh (or draco::PointCloud if the
+// connectivity data is not needed).
+// TODO(ostava): The current implementation assumes that the input vertices are
+// defined with x, y, z properties. The decoder also reads uint8 red, green,
+// blue, alpha color information, but all other attributes are ignored for now.
+class PlyDecoder {
+ public:
+ PlyDecoder();
+
+ // Decodes an obj file stored in the input file.
+ // Returns nullptr if the decoding failed.
+ bool DecodeFromFile(const std::string &file_name, Mesh *out_mesh);
+ bool DecodeFromFile(const std::string &file_name,
+ PointCloud *out_point_cloud);
+
+ bool DecodeFromBuffer(DecoderBuffer *buffer, Mesh *out_mesh);
+ bool DecodeFromBuffer(DecoderBuffer *buffer, PointCloud *out_point_cloud);
+
+ protected:
+ bool DecodeInternal();
+ DecoderBuffer *buffer() { return &buffer_; }
+
+ private:
+ bool DecodeFaceData(const PlyElement *face_element);
+ bool DecodeVertexData(const PlyElement *vertex_element);
+
+ template <typename DataTypeT>
+ bool ReadPropertiesToAttribute(
+ const std::vector<const PlyProperty *> &properties,
+ PointAttribute *attribute, int num_vertices);
+
+ DecoderBuffer buffer_;
+
+ // Data structure that stores the decoded data. |out_point_cloud_| must be
+ // always set but |out_mesh_| is optional.
+ Mesh *out_mesh_;
+ PointCloud *out_point_cloud_;
+};
+
+} // namespace draco
+
+#endif // DRACO_IO_MESH_PLY_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/io/ply_decoder_test.cc b/extern/draco/dracoenc/src/draco/io/ply_decoder_test.cc
new file mode 100644
index 00000000000..39a52274ce6
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/ply_decoder_test.cc
@@ -0,0 +1,87 @@
+// 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 "draco/io/ply_decoder.h"
+
+#include "draco/core/draco_test_base.h"
+#include "draco/core/draco_test_utils.h"
+
+namespace draco {
+
+class PlyDecoderTest : public ::testing::Test {
+ protected:
+ template <class Geometry>
+ std::unique_ptr<Geometry> DecodePly(const std::string &file_name) const {
+ const std::string path = GetTestFileFullPath(file_name);
+ PlyDecoder decoder;
+ std::unique_ptr<Geometry> geometry(new Geometry());
+ if (!decoder.DecodeFromFile(path, geometry.get()))
+ return nullptr;
+ return geometry;
+ }
+
+ void test_decoding(const std::string &file_name, int num_faces,
+ uint32_t num_points, std::unique_ptr<Mesh> *out_mesh) {
+ // Don't test mesh decoding when the input is point cloud.
+ if (num_faces > 0) {
+ std::unique_ptr<Mesh> mesh(DecodePly<Mesh>(file_name));
+ ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
+ ASSERT_EQ(mesh->num_faces(), num_faces);
+ if (out_mesh)
+ *out_mesh = std::move(mesh);
+ }
+
+ const std::unique_ptr<PointCloud> pc(DecodePly<PointCloud>(file_name));
+ ASSERT_NE(pc, nullptr) << "Failed to load test model " << file_name;
+ ASSERT_EQ(pc->num_points(), num_points);
+ }
+ void test_decoding(const std::string &file_name) {
+ const std::unique_ptr<Mesh> mesh(DecodePly<Mesh>(file_name));
+ ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
+ ASSERT_GT(mesh->num_faces(), 0);
+
+ const std::unique_ptr<PointCloud> pc(DecodePly<PointCloud>(file_name));
+ ASSERT_NE(pc, nullptr) << "Failed to load test model " << file_name;
+ ASSERT_GT(pc->num_points(), 0);
+ }
+};
+
+TEST_F(PlyDecoderTest, TestPlyDecoding) {
+ const std::string file_name = "test_pos_color.ply";
+ test_decoding(file_name, 224, 114, nullptr);
+}
+
+TEST_F(PlyDecoderTest, TestPlyNormals) {
+ const std::string file_name = "cube_att.ply";
+ std::unique_ptr<Mesh> mesh;
+ test_decoding(file_name, 12, 3 * 8, &mesh);
+ ASSERT_NE(mesh, nullptr);
+ const int att_id = mesh->GetNamedAttributeId(GeometryAttribute::NORMAL);
+ ASSERT_GE(att_id, 0);
+ const PointAttribute *const att = mesh->attribute(att_id);
+ ASSERT_EQ(att->size(), 6); // 6 unique normal values.
+}
+
+TEST_F(PlyDecoderTest, TestPlyDecodingAll) {
+ // test if we can read all ply that are currently in test folder.
+ test_decoding("bun_zipper.ply");
+ // test_decoding("cube_att.ply"); // tested
+ test_decoding("test_extra_whitespace.ply");
+ test_decoding("test_more_datatypes.ply");
+ test_decoding("test_pos_color_ascii.ply");
+ test_decoding("int_point_cloud.ply", 0, 16, nullptr);
+ // test_decoding("test_pos_color.ply"); // tested
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/io/ply_encoder.cc b/extern/draco/dracoenc/src/draco/io/ply_encoder.cc
new file mode 100644
index 00000000000..cb25b21d4f8
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/ply_encoder.cc
@@ -0,0 +1,201 @@
+// 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 "draco/io/ply_encoder.h"
+
+#include <fstream>
+#include <sstream>
+
+namespace draco {
+
+PlyEncoder::PlyEncoder()
+ : out_buffer_(nullptr), in_point_cloud_(nullptr), in_mesh_(nullptr) {}
+
+bool PlyEncoder::EncodeToFile(const PointCloud &pc,
+ const std::string &file_name) {
+ std::ofstream file(file_name, std::ios::binary);
+ if (!file)
+ return false; // File couldn't be opened.
+ // Encode the mesh into a buffer.
+ EncoderBuffer buffer;
+ if (!EncodeToBuffer(pc, &buffer))
+ return false;
+ // Write the buffer into the file.
+ file.write(buffer.data(), buffer.size());
+ return true;
+}
+
+bool PlyEncoder::EncodeToFile(const Mesh &mesh, const std::string &file_name) {
+ in_mesh_ = &mesh;
+ return EncodeToFile(static_cast<const PointCloud &>(mesh), file_name);
+}
+
+bool PlyEncoder::EncodeToBuffer(const PointCloud &pc,
+ EncoderBuffer *out_buffer) {
+ in_point_cloud_ = &pc;
+ out_buffer_ = out_buffer;
+ if (!EncodeInternal())
+ return ExitAndCleanup(false);
+ return ExitAndCleanup(true);
+}
+
+bool PlyEncoder::EncodeToBuffer(const Mesh &mesh, EncoderBuffer *out_buffer) {
+ in_mesh_ = &mesh;
+ return EncodeToBuffer(static_cast<const PointCloud &>(mesh), out_buffer);
+}
+bool PlyEncoder::EncodeInternal() {
+ // Write PLY header.
+ // TODO(ostava): Currently works only for xyz positions and rgb(a) colors.
+ std::stringstream out;
+ out << "ply" << std::endl;
+ out << "format binary_little_endian 1.0" << std::endl;
+ out << "element vertex " << in_point_cloud_->num_points() << std::endl;
+
+ const int pos_att_id =
+ in_point_cloud_->GetNamedAttributeId(GeometryAttribute::POSITION);
+ int normal_att_id =
+ in_point_cloud_->GetNamedAttributeId(GeometryAttribute::NORMAL);
+ int tex_coord_att_id =
+ in_point_cloud_->GetNamedAttributeId(GeometryAttribute::TEX_COORD);
+ const int color_att_id =
+ in_point_cloud_->GetNamedAttributeId(GeometryAttribute::COLOR);
+
+ if (pos_att_id < 0)
+ return false;
+
+ // Ensure normals are 3 component. Don't encode them otherwise.
+ if (normal_att_id >= 0 &&
+ in_point_cloud_->attribute(normal_att_id)->num_components() != 3)
+ normal_att_id = -1;
+
+ // Ensure texture coordinates have only 2 components. Don't encode them
+ // otherwise. TODO(ostava): Add support for 3 component normals (uvw).
+ if (tex_coord_att_id >= 0 &&
+ in_point_cloud_->attribute(tex_coord_att_id)->num_components() != 2)
+ tex_coord_att_id = -1;
+
+ out << "property " << GetAttributeDataType(pos_att_id) << " x" << std::endl;
+ out << "property " << GetAttributeDataType(pos_att_id) << " y" << std::endl;
+ out << "property " << GetAttributeDataType(pos_att_id) << " z" << std::endl;
+ if (normal_att_id >= 0) {
+ out << "property " << GetAttributeDataType(normal_att_id) << " nx"
+ << std::endl;
+ out << "property " << GetAttributeDataType(normal_att_id) << " ny"
+ << std::endl;
+ out << "property " << GetAttributeDataType(normal_att_id) << " nz"
+ << std::endl;
+ }
+ if (color_att_id >= 0) {
+ const auto *const attribute = in_point_cloud_->attribute(color_att_id);
+ if (attribute->num_components() > 0) {
+ out << "property " << GetAttributeDataType(color_att_id) << " red"
+ << std::endl;
+ }
+ if (attribute->num_components() > 1) {
+ out << "property " << GetAttributeDataType(color_att_id) << " green"
+ << std::endl;
+ }
+ if (attribute->num_components() > 2) {
+ out << "property " << GetAttributeDataType(color_att_id) << " blue"
+ << std::endl;
+ }
+ if (attribute->num_components() > 3) {
+ out << "property " << GetAttributeDataType(color_att_id) << " alpha"
+ << std::endl;
+ }
+ }
+ if (in_mesh_) {
+ out << "element face " << in_mesh_->num_faces() << std::endl;
+ out << "property list uchar int vertex_indices" << std::endl;
+ if (tex_coord_att_id >= 0) {
+ // Texture coordinates are usually encoded in the property list (one value
+ // per corner).
+ out << "property list uchar " << GetAttributeDataType(tex_coord_att_id)
+ << " texcoord" << std::endl;
+ }
+ }
+ out << "end_header" << std::endl;
+
+ // Not very efficient but the header should be small so just copy the stream
+ // to a string.
+ const std::string header_str = out.str();
+ buffer()->Encode(header_str.data(), header_str.length());
+
+ // Store point attributes.
+ for (PointIndex v(0); v < in_point_cloud_->num_points(); ++v) {
+ const auto *const pos_att = in_point_cloud_->attribute(pos_att_id);
+ buffer()->Encode(pos_att->GetAddress(pos_att->mapped_index(v)),
+ pos_att->byte_stride());
+ if (normal_att_id >= 0) {
+ const auto *const normal_att = in_point_cloud_->attribute(normal_att_id);
+ buffer()->Encode(normal_att->GetAddress(normal_att->mapped_index(v)),
+ normal_att->byte_stride());
+ }
+ if (color_att_id >= 0) {
+ const auto *const color_att = in_point_cloud_->attribute(color_att_id);
+ buffer()->Encode(color_att->GetAddress(color_att->mapped_index(v)),
+ color_att->byte_stride());
+ }
+ }
+
+ if (in_mesh_) {
+ // Write face data.
+ for (FaceIndex i(0); i < in_mesh_->num_faces(); ++i) {
+ // Write the number of face indices (always 3).
+ buffer()->Encode(static_cast<uint8_t>(3));
+
+ const auto &f = in_mesh_->face(i);
+ buffer()->Encode(f[0]);
+ buffer()->Encode(f[1]);
+ buffer()->Encode(f[2]);
+
+ if (tex_coord_att_id >= 0) {
+ // Two coordinates for every corner -> 6.
+ buffer()->Encode(static_cast<uint8_t>(6));
+
+ const auto *const tex_att =
+ in_point_cloud_->attribute(tex_coord_att_id);
+ for (int c = 0; c < 3; ++c) {
+ buffer()->Encode(tex_att->GetAddress(tex_att->mapped_index(f[c])),
+ tex_att->byte_stride());
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool PlyEncoder::ExitAndCleanup(bool return_value) {
+ in_mesh_ = nullptr;
+ in_point_cloud_ = nullptr;
+ out_buffer_ = nullptr;
+ return return_value;
+}
+
+const char *PlyEncoder::GetAttributeDataType(int attribute) {
+ // TODO(ostava): Add support for more types.
+ switch (in_point_cloud_->attribute(attribute)->data_type()) {
+ case DT_FLOAT32:
+ return "float";
+ case DT_UINT8:
+ return "uchar";
+ case DT_INT32:
+ return "int";
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/io/ply_encoder.h b/extern/draco/dracoenc/src/draco/io/ply_encoder.h
new file mode 100644
index 00000000000..242bbd6d464
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/ply_encoder.h
@@ -0,0 +1,54 @@
+// 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_IO_PLY_ENCODER_H_
+#define DRACO_IO_PLY_ENCODER_H_
+
+#include "draco/core/encoder_buffer.h"
+#include "draco/mesh/mesh.h"
+
+namespace draco {
+
+// Class for encoding draco::Mesh or draco::PointCloud into the PLY file format.
+class PlyEncoder {
+ public:
+ PlyEncoder();
+
+ // Encodes the mesh or a point cloud and saves it into a file.
+ // Returns false when either the encoding failed or when the file couldn't be
+ // opened.
+ bool EncodeToFile(const PointCloud &pc, const std::string &file_name);
+ bool EncodeToFile(const Mesh &mesh, const std::string &file_name);
+
+ // Encodes the mesh or the point cloud into a buffer.
+ bool EncodeToBuffer(const PointCloud &pc, EncoderBuffer *out_buffer);
+ bool EncodeToBuffer(const Mesh &mesh, EncoderBuffer *out_buffer);
+
+ protected:
+ bool EncodeInternal();
+ EncoderBuffer *buffer() const { return out_buffer_; }
+ bool ExitAndCleanup(bool return_value);
+
+ private:
+ const char *GetAttributeDataType(int attribute);
+
+ EncoderBuffer *out_buffer_;
+
+ const PointCloud *in_point_cloud_;
+ const Mesh *in_mesh_;
+};
+
+} // namespace draco
+
+#endif // DRACO_IO_PLY_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/io/ply_property_reader.h b/extern/draco/dracoenc/src/draco/io/ply_property_reader.h
new file mode 100644
index 00000000000..efb8a3a1f9d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/ply_property_reader.h
@@ -0,0 +1,96 @@
+// 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_IO_PLY_PROPERTY_READER_H_
+#define DRACO_IO_PLY_PROPERTY_READER_H_
+
+#include <functional>
+
+#include "draco/io/ply_reader.h"
+
+namespace draco {
+
+// Class for reading PlyProperty with a given type, performing data conversion
+// if necessary.
+template <typename ReadTypeT>
+class PlyPropertyReader {
+ public:
+ explicit PlyPropertyReader(const PlyProperty *property)
+ : property_(property) {
+ // Find the suitable function for converting values.
+ switch (property->data_type()) {
+ case DT_UINT8:
+ convert_value_func_ = [=](int val_id) {
+ return this->ConvertValue<uint8_t>(val_id);
+ };
+ break;
+ case DT_INT8:
+ convert_value_func_ = [=](int val_id) {
+ return this->ConvertValue<int8_t>(val_id);
+ };
+ break;
+ case DT_UINT16:
+ convert_value_func_ = [=](int val_id) {
+ return this->ConvertValue<uint16_t>(val_id);
+ };
+ break;
+ case DT_INT16:
+ convert_value_func_ = [=](int val_id) {
+ return this->ConvertValue<int16_t>(val_id);
+ };
+ break;
+ case DT_UINT32:
+ convert_value_func_ = [=](int val_id) {
+ return this->ConvertValue<uint32_t>(val_id);
+ };
+ break;
+ case DT_INT32:
+ convert_value_func_ = [=](int val_id) {
+ return this->ConvertValue<int32_t>(val_id);
+ };
+ break;
+ case DT_FLOAT32:
+ convert_value_func_ = [=](int val_id) {
+ return this->ConvertValue<float>(val_id);
+ };
+ break;
+ case DT_FLOAT64:
+ convert_value_func_ = [=](int val_id) {
+ return this->ConvertValue<double>(val_id);
+ };
+ break;
+ default:
+ break;
+ }
+ }
+
+ ReadTypeT ReadValue(int value_id) const {
+ return convert_value_func_(value_id);
+ }
+
+ private:
+ template <typename SourceTypeT>
+ ReadTypeT ConvertValue(int value_id) const {
+ const void *const address = property_->GetDataEntryAddress(value_id);
+ const SourceTypeT src_val = *reinterpret_cast<const SourceTypeT *>(address);
+ return static_cast<ReadTypeT>(src_val);
+ }
+
+ const PlyProperty *property_;
+ std::function<ReadTypeT(int)> convert_value_func_;
+};
+
+} // namespace draco
+
+#endif // DRACO_IO_PLY_PROPERTY_READER_H_
diff --git a/extern/draco/dracoenc/src/draco/io/ply_property_writer.h b/extern/draco/dracoenc/src/draco/io/ply_property_writer.h
new file mode 100644
index 00000000000..4f243b2860f
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/ply_property_writer.h
@@ -0,0 +1,94 @@
+// 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_IO_PLY_PROPERTY_WRITER_H_
+#define DRACO_IO_PLY_PROPERTY_WRITER_H_
+
+#include <functional>
+
+#include "draco/io/ply_reader.h"
+
+namespace draco {
+
+// Class for writing PlyProperty with a given type, performing data conversion
+// if necessary.
+template <typename WriteTypeT>
+class PlyPropertyWriter {
+ public:
+ explicit PlyPropertyWriter(PlyProperty *property) : property_(property) {
+ // Find the suitable function for converting values.
+ switch (property->data_type()) {
+ case DT_UINT8:
+ convert_value_func_ = [=](WriteTypeT val) {
+ return this->ConvertValue<uint8_t>(val);
+ };
+ break;
+ case DT_INT8:
+ convert_value_func_ = [=](WriteTypeT val) {
+ return this->ConvertValue<int8_t>(val);
+ };
+ break;
+ case DT_UINT16:
+ convert_value_func_ = [=](WriteTypeT val) {
+ return this->ConvertValue<uint16_t>(val);
+ };
+ break;
+ case DT_INT16:
+ convert_value_func_ = [=](WriteTypeT val) {
+ return this->ConvertValue<int16_t>(val);
+ };
+ break;
+ case DT_UINT32:
+ convert_value_func_ = [=](WriteTypeT val) {
+ return this->ConvertValue<uint32_t>(val);
+ };
+ break;
+ case DT_INT32:
+ convert_value_func_ = [=](WriteTypeT val) {
+ return this->ConvertValue<int32_t>(val);
+ };
+ break;
+ case DT_FLOAT32:
+ convert_value_func_ = [=](WriteTypeT val) {
+ return this->ConvertValue<float>(val);
+ };
+ break;
+ case DT_FLOAT64:
+ convert_value_func_ = [=](WriteTypeT val) {
+ return this->ConvertValue<double>(val);
+ };
+ break;
+ default:
+ break;
+ }
+ }
+
+ void PushBackValue(WriteTypeT value) const {
+ return convert_value_func_(value);
+ }
+
+ private:
+ template <typename SourceTypeT>
+ void ConvertValue(WriteTypeT value) const {
+ const SourceTypeT src_val = static_cast<SourceTypeT>(value);
+ property_->push_back_value(&src_val);
+ }
+
+ PlyProperty *property_;
+ std::function<void(WriteTypeT)> convert_value_func_;
+};
+
+} // namespace draco
+
+#endif // DRACO_IO_PLY_PROPERTY_WRITER_H_
diff --git a/extern/draco/dracoenc/src/draco/io/ply_reader.cc b/extern/draco/dracoenc/src/draco/io/ply_reader.cc
new file mode 100644
index 00000000000..f924550e23f
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/ply_reader.cc
@@ -0,0 +1,301 @@
+// 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 "draco/io/ply_reader.h"
+
+#include <array>
+#include <regex>
+
+#include "draco/io/parser_utils.h"
+#include "draco/io/ply_property_writer.h"
+
+namespace draco {
+
+PlyProperty::PlyProperty(const std::string &name, DataType data_type,
+ DataType list_type)
+ : name_(name), data_type_(data_type), list_data_type_(list_type) {
+ data_type_num_bytes_ = DataTypeLength(data_type);
+ list_data_type_num_bytes_ = DataTypeLength(list_type);
+}
+
+PlyElement::PlyElement(const std::string &name, int64_t num_entries)
+ : name_(name), num_entries_(num_entries) {}
+
+PlyReader::PlyReader() : format_(kLittleEndian) {}
+
+bool PlyReader::Read(DecoderBuffer *buffer) {
+ error_message_.clear();
+ std::string value;
+ // The first line needs to by "ply".
+ if (!parser::ParseString(buffer, &value) || value != "ply") {
+ error_message_ = "Not a valid ply file.";
+ return false;
+ }
+ parser::SkipLine(buffer);
+
+ // The second line needs to be the format of the ply file.
+ parser::ParseLine(buffer, &value);
+ std::string format, version;
+ const std::vector<std::string> words = SplitWords(value);
+ if (words.size() >= 3 && words[0] == "format") {
+ format = words[1];
+ version = words[2];
+ } else {
+ error_message_ = "Missing or wrong format line.";
+ return false;
+ }
+ if (version != "1.0") {
+ error_message_ = "Unsupported PLY version.";
+ return false; // Wrong version.
+ }
+ if (format == "binary_big_endian") {
+ error_message_ =
+ "Unsupported format. Currently we support only ascii and"
+ " binary_little_endian format.";
+ return false;
+ }
+ if (format == "ascii") {
+ format_ = kAscii;
+ } else {
+ format_ = kLittleEndian;
+ }
+ if (!ParseHeader(buffer))
+ return false;
+ if (!ParsePropertiesData(buffer))
+ return false;
+ return true;
+}
+
+bool PlyReader::ParseHeader(DecoderBuffer *buffer) {
+ while (error_message_.length() == 0 && !ParseEndHeader(buffer)) {
+ if (ParseElement(buffer))
+ continue;
+ if (ParseProperty(buffer))
+ continue;
+ parser::SkipLine(buffer);
+ }
+ if (error_message_.length() > 0) {
+ printf("ERROR %s\n", error_message_.c_str());
+ return false;
+ }
+ return true;
+}
+
+bool PlyReader::ParseEndHeader(DecoderBuffer *buffer) {
+ parser::SkipWhitespace(buffer);
+ std::array<char, 10> c;
+ if (!buffer->Peek(&c)) {
+ error_message_ = "End of file reached before the end_header.";
+ return false;
+ }
+ if (std::memcmp(&c[0], "end_header", 10) != 0)
+ return false;
+ parser::SkipLine(buffer);
+ return true;
+}
+
+bool PlyReader::ParseElement(DecoderBuffer *buffer) {
+ DecoderBuffer line_buffer(*buffer);
+ std::string line;
+ parser::ParseLine(&line_buffer, &line);
+
+ std::string element_name;
+ int64_t count;
+ const std::vector<std::string> words = SplitWords(line);
+ if (words.size() >= 3 && words[0] == "element") {
+ element_name = words[1];
+ const std::string count_str = words[2];
+ count = strtoll(count_str.c_str(), NULL, 10);
+ } else {
+ return false;
+ }
+ element_index_[element_name] = static_cast<uint32_t>(elements_.size());
+ elements_.emplace_back(PlyElement(element_name, count));
+ *buffer = line_buffer;
+ return true;
+}
+
+bool PlyReader::ParseProperty(DecoderBuffer *buffer) {
+ if (elements_.empty())
+ return false; // Ignore properties if there is no active element.
+ DecoderBuffer line_buffer(*buffer);
+ std::string line;
+ parser::ParseLine(&line_buffer, &line);
+
+ std::string data_type_str, list_type_str, property_name;
+ bool property_search = false;
+ const std::vector<std::string> words = SplitWords(line);
+ if (words.size() >= 3 && words[0] == "property" && words[1] != "list") {
+ property_search = true;
+ data_type_str = words[1];
+ property_name = words[2];
+ }
+
+ bool property_list_search = false;
+ if (words.size() >= 5 && words[0] == "property" && words[1] == "list") {
+ property_list_search = true;
+ list_type_str = words[2];
+ data_type_str = words[3];
+ property_name = words[4];
+ }
+ if (!property_search && !property_list_search) {
+ return false;
+ }
+ const DataType data_type = GetDataTypeFromString(data_type_str);
+ if (data_type == DT_INVALID) {
+ error_message_ = "Wrong property data type.";
+ return true; // Parsed.
+ }
+ DataType list_type = DT_INVALID;
+ if (property_list_search) {
+ list_type = GetDataTypeFromString(list_type_str);
+ if (list_type == DT_INVALID) {
+ error_message_ = "Wrong property list type.";
+ return true; // Parsed.
+ }
+ }
+ elements_.back().AddProperty(
+ PlyProperty(property_name, data_type, list_type));
+ *buffer = line_buffer;
+ return true;
+}
+
+bool PlyReader::ParsePropertiesData(DecoderBuffer *buffer) {
+ for (int i = 0; i < static_cast<int>(elements_.size()); ++i) {
+ if (format_ == kLittleEndian) {
+ if (!ParseElementData(buffer, i)) {
+ return false;
+ }
+ } else if (format_ == kAscii) {
+ if (!ParseElementDataAscii(buffer, i)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool PlyReader::ParseElementData(DecoderBuffer *buffer, int element_index) {
+ PlyElement &element = elements_[element_index];
+ for (int entry = 0; entry < element.num_entries(); ++entry) {
+ for (int i = 0; i < element.num_properties(); ++i) {
+ PlyProperty &prop = element.property(i);
+ if (prop.is_list()) {
+ // Parse the number of entries for the list element.
+ int64_t num_entries = 0;
+ buffer->Decode(&num_entries, prop.list_data_type_num_bytes());
+ // Store offset to the main data entry.
+ prop.list_data_.push_back(prop.data_.size() /
+ prop.data_type_num_bytes_);
+ // Store the number of entries.
+ prop.list_data_.push_back(num_entries);
+ // Read and store the actual property data
+ const int64_t num_bytes_to_read =
+ prop.data_type_num_bytes() * num_entries;
+ prop.data_.insert(prop.data_.end(), buffer->data_head(),
+ buffer->data_head() + num_bytes_to_read);
+ buffer->Advance(num_bytes_to_read);
+ } else {
+ // Non-list property
+ prop.data_.insert(prop.data_.end(), buffer->data_head(),
+ buffer->data_head() + prop.data_type_num_bytes());
+ buffer->Advance(prop.data_type_num_bytes());
+ }
+ }
+ }
+ return true;
+}
+
+bool PlyReader::ParseElementDataAscii(DecoderBuffer *buffer,
+ int element_index) {
+ PlyElement &element = elements_[element_index];
+ for (int entry = 0; entry < element.num_entries(); ++entry) {
+ for (int i = 0; i < element.num_properties(); ++i) {
+ PlyProperty &prop = element.property(i);
+ PlyPropertyWriter<double> prop_writer(&prop);
+ int32_t num_entries = 1;
+ if (prop.is_list()) {
+ parser::SkipWhitespace(buffer);
+ // Parse the number of entries for the list element.
+ if (!parser::ParseSignedInt(buffer, &num_entries))
+ return false;
+
+ // Store offset to the main data entry.
+ prop.list_data_.push_back(prop.data_.size() /
+ prop.data_type_num_bytes_);
+ // Store the number of entries.
+ prop.list_data_.push_back(num_entries);
+ }
+ // Read and store the actual property data.
+ for (int v = 0; v < num_entries; ++v) {
+ parser::SkipWhitespace(buffer);
+ if (prop.data_type() == DT_FLOAT32 || prop.data_type() == DT_FLOAT64) {
+ float val;
+ if (!parser::ParseFloat(buffer, &val))
+ return false;
+ prop_writer.PushBackValue(val);
+ } else {
+ int32_t val;
+ if (!parser::ParseSignedInt(buffer, &val))
+ return false;
+ prop_writer.PushBackValue(val);
+ }
+ }
+ }
+ }
+ return true;
+}
+
+std::vector<std::string> PlyReader::SplitWords(const std::string &line) {
+ std::vector<std::string> output;
+ std::string::size_type start = 0;
+ std::string::size_type end = 0;
+
+ // Check for isspace chars.
+ while ((end = line.find_first_of(" \t\n\v\f\r", start)) !=
+ std::string::npos) {
+ const std::string word(line.substr(start, end - start));
+ if (!std::all_of(word.begin(), word.end(), isspace))
+ output.push_back(word);
+ start = end + 1;
+ }
+
+ const std::string last_word(line.substr(start));
+ if (!std::all_of(last_word.begin(), last_word.end(), isspace))
+ output.push_back(last_word);
+ return output;
+}
+
+DataType PlyReader::GetDataTypeFromString(const std::string &name) const {
+ if (name == "char" || name == "int8")
+ return DT_INT8;
+ if (name == "uchar" || name == "uint8")
+ return DT_UINT8;
+ if (name == "short" || name == "int16")
+ return DT_INT16;
+ if (name == "ushort" || name == "uint16")
+ return DT_UINT16;
+ if (name == "int" || name == "int32")
+ return DT_INT32;
+ if (name == "uint" || name == "uint32")
+ return DT_UINT32;
+ if (name == "float" || name == "float32")
+ return DT_FLOAT32;
+ if (name == "double" || name == "float64") {
+ return DT_FLOAT64;
+ }
+ return DT_INVALID;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/io/ply_reader.h b/extern/draco/dracoenc/src/draco/io/ply_reader.h
new file mode 100644
index 00000000000..8d0f21ff1cb
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/ply_reader.h
@@ -0,0 +1,151 @@
+// 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.
+//
+// File contains helper classes used for parsing of PLY files. The classes are
+// used by the PlyDecoder (ply_decoder.h) to read a point cloud or mesh from a
+// source PLY file.
+// TODO(ostava): Currently, we support only binary PLYs encoded in the little
+// endian format ("format binary_little_endian 1.0").
+
+#ifndef DRACO_IO_PLY_READER_H_
+#define DRACO_IO_PLY_READER_H_
+
+#include <map>
+#include <vector>
+
+#include "draco/core/decoder_buffer.h"
+#include "draco/core/draco_types.h"
+
+namespace draco {
+
+// A single PLY property of a given PLY element. For "vertex" element this can
+// contain data such as "x", "y", or "z" coordinate of the vertex, while for
+// "face" element this usually contains corner indices.
+class PlyProperty {
+ public:
+ friend class PlyReader;
+
+ PlyProperty(const std::string &name, DataType data_type, DataType list_type);
+ void ReserveData(int num_entries) {
+ data_.reserve(DataTypeLength(data_type_) * num_entries);
+ }
+
+ int64_t GetListEntryOffset(int entry_id) const {
+ return list_data_[entry_id * 2];
+ }
+ int64_t GetListEntryNumValues(int entry_id) const {
+ return list_data_[entry_id * 2 + 1];
+ }
+ const void *GetDataEntryAddress(int entry_id) const {
+ return data_.data() + entry_id * data_type_num_bytes_;
+ }
+ void push_back_value(const void *data) {
+ data_.insert(data_.end(), static_cast<const uint8_t *>(data),
+ static_cast<const uint8_t *>(data) + data_type_num_bytes_);
+ }
+
+ const std::string &name() const { return name_; }
+ bool is_list() const { return list_data_type_ != DT_INVALID; }
+ DataType data_type() const { return data_type_; }
+ int data_type_num_bytes() const { return data_type_num_bytes_; }
+ DataType list_data_type() const { return list_data_type_; }
+ int list_data_type_num_bytes() const { return list_data_type_num_bytes_; }
+
+ private:
+ std::string name_;
+ std::vector<uint8_t> data_;
+ // List data contain pairs of <offset, number_of_values>
+ std::vector<int64_t> list_data_;
+ DataType data_type_;
+ int data_type_num_bytes_;
+ DataType list_data_type_;
+ int list_data_type_num_bytes_;
+};
+
+// A single PLY element such as "vertex" or "face". Each element can store
+// arbitrary properties such as vertex coordinates or face indices.
+class PlyElement {
+ public:
+ PlyElement(const std::string &name, int64_t num_entries);
+ void AddProperty(const PlyProperty &prop) {
+ property_index_[prop.name()] = static_cast<int>(properties_.size());
+ properties_.emplace_back(prop);
+ if (!properties_.back().is_list())
+ properties_.back().ReserveData(static_cast<int>(num_entries_));
+ }
+
+ const PlyProperty *GetPropertyByName(const std::string &name) const {
+ const auto it = property_index_.find(name);
+ if (it != property_index_.end())
+ return &properties_[it->second];
+ return nullptr;
+ }
+
+ int num_properties() const { return static_cast<int>(properties_.size()); }
+ int num_entries() const { return static_cast<int>(num_entries_); }
+ const PlyProperty &property(int prop_index) const {
+ return properties_[prop_index];
+ }
+ PlyProperty &property(int prop_index) { return properties_[prop_index]; }
+
+ private:
+ std::string name_;
+ int64_t num_entries_;
+ std::vector<PlyProperty> properties_;
+ std::map<std::string, int> property_index_;
+};
+
+// Class responsible for parsing PLY data. It produces a list of PLY elements
+// and their properties that can be used to construct a mesh or a point cloud.
+class PlyReader {
+ public:
+ PlyReader();
+ bool Read(DecoderBuffer *buffer);
+
+ const PlyElement *GetElementByName(const std::string &name) const {
+ const auto it = element_index_.find(name);
+ if (it != element_index_.end())
+ return &elements_[it->second];
+ return nullptr;
+ }
+
+ int num_elements() const { return static_cast<int>(elements_.size()); }
+ const PlyElement &element(int element_index) const {
+ return elements_[element_index];
+ }
+
+ private:
+ enum Format { kLittleEndian = 0, kAscii };
+
+ bool ParseHeader(DecoderBuffer *buffer);
+ bool ParseEndHeader(DecoderBuffer *buffer);
+ bool ParseElement(DecoderBuffer *buffer);
+ bool ParseProperty(DecoderBuffer *buffer);
+ bool ParsePropertiesData(DecoderBuffer *buffer);
+ bool ParseElementData(DecoderBuffer *buffer, int element_index);
+ bool ParseElementDataAscii(DecoderBuffer *buffer, int element_index);
+
+ // Splits |line| by whitespace characters.
+ std::vector<std::string> SplitWords(const std::string &line);
+ DataType GetDataTypeFromString(const std::string &name) const;
+
+ std::vector<PlyElement> elements_;
+ std::string error_message_;
+ std::map<std::string, int> element_index_;
+ Format format_;
+};
+
+} // namespace draco
+
+#endif // DRACO_IO_PLY_READER_H_
diff --git a/extern/draco/dracoenc/src/draco/io/ply_reader_test.cc b/extern/draco/dracoenc/src/draco/io/ply_reader_test.cc
new file mode 100644
index 00000000000..98f9c601971
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/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 "draco/io/ply_reader.h"
+
+#include <fstream>
+
+#include "draco/core/draco_test_base.h"
+#include "draco/core/draco_test_utils.h"
+#include "draco/io/ply_property_reader.h"
+
+namespace draco {
+
+class PlyReaderTest : public ::testing::Test {
+ protected:
+ std::vector<char> 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<char>();
+ 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<char> 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<char> 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<uint8_t> reader_uint8(prop);
+ PlyPropertyReader<uint32_t> reader_uint32(prop);
+ PlyPropertyReader<float> 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<char> 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<char> 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<float> reader_float(prop);
+ PlyPropertyReader<float> 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<char> 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<uint8_t> reader_uint8(prop);
+ PlyPropertyReader<uint32_t> reader_uint32(prop);
+ PlyPropertyReader<float> 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<char> 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<uint8_t> reader_uint8(prop);
+ PlyPropertyReader<uint32_t> reader_uint32(prop);
+ PlyPropertyReader<float> 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/extern/draco/dracoenc/src/draco/io/point_cloud_io.cc b/extern/draco/dracoenc/src/draco/io/point_cloud_io.cc
new file mode 100644
index 00000000000..068f7a139e4
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/point_cloud_io.cc
@@ -0,0 +1,59 @@
+// 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 "draco/io/point_cloud_io.h"
+
+#include <fstream>
+
+#include "draco/io/obj_decoder.h"
+#include "draco/io/parser_utils.h"
+#include "draco/io/ply_decoder.h"
+
+namespace draco {
+
+StatusOr<std::unique_ptr<PointCloud>> ReadPointCloudFromFile(
+ const std::string &file_name) {
+ std::unique_ptr<PointCloud> pc(new PointCloud());
+ // Analyze file extension.
+ const std::string extension = parser::ToLower(
+ file_name.size() >= 4 ? file_name.substr(file_name.size() - 4)
+ : file_name);
+ if (extension == ".obj") {
+ // Wavefront OBJ file format.
+ ObjDecoder obj_decoder;
+ const Status obj_status = obj_decoder.DecodeFromFile(file_name, pc.get());
+ if (!obj_status.ok())
+ return obj_status;
+ return std::move(pc);
+ }
+ if (extension == ".ply") {
+ // Wavefront PLY file format.
+ PlyDecoder ply_decoder;
+ if (!ply_decoder.DecodeFromFile(file_name, pc.get()))
+ return Status(Status::ERROR, "Unknown error.");
+ return std::move(pc);
+ }
+
+ // Otherwise not an obj file. Assume the file was encoded with one of the
+ // draco encoding methods.
+ std::ifstream is(file_name.c_str(), std::ios::binary);
+ if (!is)
+ return Status(Status::ERROR, "Invalid input stream.");
+ if (!ReadPointCloudFromStream(&pc, is).good())
+ return Status(Status::ERROR,
+ "Unknown error."); // Error reading the stream.
+ return std::move(pc);
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/io/point_cloud_io.h b/extern/draco/dracoenc/src/draco/io/point_cloud_io.h
new file mode 100644
index 00000000000..4e1eb359644
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/point_cloud_io.h
@@ -0,0 +1,89 @@
+// 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_IO_POINT_CLOUD_IO_H_
+#define DRACO_IO_POINT_CLOUD_IO_H_
+
+#include "draco/compression/config/compression_shared.h"
+#include "draco/compression/decode.h"
+#include "draco/compression/expert_encode.h"
+
+namespace draco {
+
+template <typename OutStreamT>
+OutStreamT WritePointCloudIntoStream(const PointCloud *pc, OutStreamT &&os,
+ PointCloudEncodingMethod method,
+ const EncoderOptions &options) {
+ EncoderBuffer buffer;
+ EncoderOptions local_options = options;
+ ExpertEncoder encoder(*pc);
+ encoder.Reset(local_options);
+ encoder.SetEncodingMethod(method);
+ if (!encoder.EncodeToBuffer(&buffer).ok()) {
+ os.setstate(std::ios_base::badbit);
+ return os;
+ }
+
+ os.write(static_cast<const char *>(buffer.data()), buffer.size());
+
+ return os;
+}
+
+template <typename OutStreamT>
+OutStreamT WritePointCloudIntoStream(const PointCloud *pc, OutStreamT &&os,
+ PointCloudEncodingMethod method) {
+ const EncoderOptions options = EncoderOptions::CreateDefaultOptions();
+ return WritePointCloudIntoStream(pc, os, method, options);
+}
+
+template <typename OutStreamT>
+OutStreamT &WritePointCloudIntoStream(const PointCloud *pc, OutStreamT &&os) {
+ return WritePointCloudIntoStream(pc, os, POINT_CLOUD_SEQUENTIAL_ENCODING);
+}
+
+template <typename InStreamT>
+InStreamT &ReadPointCloudFromStream(std::unique_ptr<PointCloud> *point_cloud,
+ InStreamT &&is) {
+ // Determine size of stream and write into a vector
+ const auto start_pos = is.tellg();
+ is.seekg(0, std::ios::end);
+ const std::streampos is_size = is.tellg() - start_pos;
+ is.seekg(start_pos);
+ std::vector<char> data(is_size);
+ is.read(&data[0], is_size);
+
+ // Create a point cloud from that data.
+ DecoderBuffer buffer;
+ buffer.Init(&data[0], data.size());
+ Decoder decoder;
+ auto statusor = decoder.DecodePointCloudFromBuffer(&buffer);
+ *point_cloud = std::move(statusor).value();
+ if (!statusor.ok() || *point_cloud == nullptr) {
+ is.setstate(std::ios_base::badbit);
+ }
+
+ return is;
+}
+
+// Reads a point cloud from a file. The function automatically chooses the
+// correct decoder based on the extension of the files. Currently, .obj and .ply
+// files are supported. Other file extensions are processed by the default
+// draco::PointCloudDecoder.
+// Returns nullptr with an error status if the decoding failed.
+StatusOr<std::unique_ptr<PointCloud>> ReadPointCloudFromFile(
+ const std::string &file_name);
+
+} // namespace draco
+
+#endif // DRACO_IO_POINT_CLOUD_IO_H_
diff --git a/extern/draco/dracoenc/src/draco/io/point_cloud_io_test.cc b/extern/draco/dracoenc/src/draco/io/point_cloud_io_test.cc
new file mode 100644
index 00000000000..73674d06f55
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/io/point_cloud_io_test.cc
@@ -0,0 +1,115 @@
+// 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 "draco/io/point_cloud_io.h"
+
+#include <sstream>
+
+#include "draco/core/draco_test_base.h"
+#include "draco/core/draco_test_utils.h"
+#include "draco/io/obj_decoder.h"
+
+namespace draco {
+
+class IoPointCloudIoTest : public ::testing::Test {
+ protected:
+ void test_compression_method(PointCloudEncodingMethod method,
+ int expected_num_attributes,
+ const std::string &file_name) {
+ const std::unique_ptr<PointCloud> encoded_pc =
+ ReadPointCloudFromTestFile(file_name);
+ ASSERT_NE(encoded_pc, nullptr) << "Failed to load test model " << file_name;
+ ASSERT_GE(encoded_pc->num_attributes(), expected_num_attributes)
+ << "Failed to load test model: " << file_name
+ << " wrong number of attributes" << std::endl;
+
+ // Set quantization.
+ EncoderOptions options = EncoderOptions::CreateDefaultOptions();
+ for (int i = 0; i <= GeometryAttribute::NAMED_ATTRIBUTES_COUNT; i++) {
+ options.SetAttributeInt(GeometryAttribute::Type(i), "quantization_bits",
+ 14);
+ }
+
+ std::stringstream ss;
+ WritePointCloudIntoStream(encoded_pc.get(), ss, method, options);
+ ASSERT_TRUE(ss.good());
+
+ std::unique_ptr<PointCloud> decoded_pc;
+ ReadPointCloudFromStream(&decoded_pc, ss);
+ ASSERT_TRUE(ss.good());
+
+ for (int i = 0; i <= GeometryAttribute::NAMED_ATTRIBUTES_COUNT; i++) {
+ ASSERT_EQ(encoded_pc->NumNamedAttributes(GeometryAttribute::Type(i)),
+ decoded_pc->NumNamedAttributes(GeometryAttribute::Type(i)));
+ }
+
+ ASSERT_EQ(encoded_pc->num_points(), decoded_pc->num_points());
+ }
+};
+
+TEST_F(IoPointCloudIoTest, EncodeSequentialPointCloudTestNmObj) {
+ test_compression_method(POINT_CLOUD_SEQUENTIAL_ENCODING, 2, "test_nm.obj");
+}
+TEST_F(IoPointCloudIoTest, EncodeSequentialPointCloudTestPosObj) {
+ test_compression_method(POINT_CLOUD_SEQUENTIAL_ENCODING, 1,
+ "point_cloud_test_pos.obj");
+}
+TEST_F(IoPointCloudIoTest, EncodeSequentialPointCloudTestPosPly) {
+ test_compression_method(POINT_CLOUD_SEQUENTIAL_ENCODING, 1,
+ "point_cloud_test_pos.ply");
+}
+TEST_F(IoPointCloudIoTest, EncodeSequentialPointCloudTestPosNormObj) {
+ test_compression_method(POINT_CLOUD_SEQUENTIAL_ENCODING, 2,
+ "point_cloud_test_pos_norm.obj");
+}
+TEST_F(IoPointCloudIoTest, EncodeSequentialPointCloudTestPosNormPly) {
+ test_compression_method(POINT_CLOUD_SEQUENTIAL_ENCODING, 2,
+ "point_cloud_test_pos_norm.ply");
+}
+
+TEST_F(IoPointCloudIoTest, EncodeKdTreePointCloudTestPosObj) {
+ test_compression_method(POINT_CLOUD_KD_TREE_ENCODING, 1,
+ "point_cloud_test_pos.obj");
+}
+TEST_F(IoPointCloudIoTest, EncodeKdTreePointCloudTestPosPly) {
+ test_compression_method(POINT_CLOUD_KD_TREE_ENCODING, 1,
+ "point_cloud_test_pos.ply");
+}
+
+TEST_F(IoPointCloudIoTest, ObjFileInput) {
+ // Tests whether loading obj point clouds from files works as expected.
+ const std::unique_ptr<PointCloud> pc =
+ ReadPointCloudFromTestFile("test_nm.obj");
+ ASSERT_NE(pc, nullptr) << "Failed to load the obj point cloud.";
+ EXPECT_EQ(pc->num_points(), 97) << "Obj point cloud not loaded properly.";
+}
+
+// Test if we handle wrong input for all file extensions.
+TEST_F(IoPointCloudIoTest, WrongFileObj) {
+ const std::unique_ptr<PointCloud> pc =
+ ReadPointCloudFromTestFile("wrong_file_name.obj");
+ ASSERT_EQ(pc, nullptr);
+}
+TEST_F(IoPointCloudIoTest, WrongFilePly) {
+ const std::unique_ptr<PointCloud> pc =
+ ReadPointCloudFromTestFile("wrong_file_name.ply");
+ ASSERT_EQ(pc, nullptr);
+}
+TEST_F(IoPointCloudIoTest, WrongFile) {
+ const std::unique_ptr<PointCloud> pc =
+ ReadPointCloudFromTestFile("wrong_file_name");
+ ASSERT_EQ(pc, nullptr);
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/mesh/corner_table.cc b/extern/draco/dracoenc/src/draco/mesh/corner_table.cc
new file mode 100644
index 00000000000..e4608fe8f9d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/corner_table.cc
@@ -0,0 +1,312 @@
+// 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 "draco/mesh/corner_table.h"
+
+#include <limits>
+
+#include "draco/mesh/corner_table_iterators.h"
+
+namespace draco {
+
+CornerTable::CornerTable()
+ : num_original_vertices_(0),
+ num_degenerated_faces_(0),
+ num_isolated_vertices_(0),
+ valence_cache_(*this) {}
+
+std::unique_ptr<CornerTable> CornerTable::Create(
+ const IndexTypeVector<FaceIndex, FaceType> &faces) {
+ std::unique_ptr<CornerTable> ct(new CornerTable());
+ if (!ct->Init(faces))
+ return nullptr;
+ return ct;
+}
+
+bool CornerTable::Init(const IndexTypeVector<FaceIndex, FaceType> &faces) {
+ valence_cache_.ClearValenceCache();
+ valence_cache_.ClearValenceCacheInaccurate();
+ corner_to_vertex_map_.resize(faces.size() * 3);
+ for (FaceIndex fi(0); fi < static_cast<uint32_t>(faces.size()); ++fi) {
+ for (int i = 0; i < 3; ++i) {
+ corner_to_vertex_map_[FirstCorner(fi) + i] = faces[fi][i];
+ }
+ }
+ int num_vertices = -1;
+ if (!ComputeOppositeCorners(&num_vertices))
+ return false;
+ if (!ComputeVertexCorners(num_vertices))
+ return false;
+ return true;
+}
+
+bool CornerTable::Reset(int num_faces) {
+ return Reset(num_faces, num_faces * 3);
+}
+
+bool CornerTable::Reset(int num_faces, int num_vertices) {
+ if (num_faces < 0 || num_vertices < 0)
+ return false;
+ if (static_cast<unsigned int>(num_faces) >
+ std::numeric_limits<CornerIndex::ValueType>::max() / 3)
+ return false;
+ corner_to_vertex_map_.assign(num_faces * 3, kInvalidVertexIndex);
+ opposite_corners_.assign(num_faces * 3, kInvalidCornerIndex);
+ vertex_corners_.reserve(num_vertices);
+ valence_cache_.ClearValenceCache();
+ valence_cache_.ClearValenceCacheInaccurate();
+ return true;
+}
+
+bool CornerTable::ComputeOppositeCorners(int *num_vertices) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ if (num_vertices == nullptr)
+ return false;
+ opposite_corners_.resize(num_corners(), kInvalidCornerIndex);
+
+ // Out implementation for finding opposite corners is based on keeping track
+ // of outgoing half-edges for each vertex of the mesh. Half-edges (defined by
+ // their opposite corners) are processed one by one and whenever a new
+ // half-edge (corner) is processed, we check whether the sink vertex of
+ // this half-edge contains its sibling half-edge. If yes, we connect them and
+ // remove the sibling half-edge from the sink vertex, otherwise we add the new
+ // half-edge to its source vertex.
+
+ // First compute the number of outgoing half-edges (corners) attached to each
+ // vertex.
+ std::vector<int> num_corners_on_vertices;
+ num_corners_on_vertices.reserve(num_corners());
+ for (CornerIndex c(0); c < num_corners(); ++c) {
+ const VertexIndex v1 = Vertex(c);
+ if (v1.value() >= static_cast<int>(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.
+ num_corners_on_vertices[v1.value()]++;
+ }
+
+ // Create a storage for half-edges on each vertex. We store all half-edges in
+ // one array, where each entry is identified by the half-edge's sink vertex id
+ // and the associated half-edge corner id (corner opposite to the half-edge).
+ // Each vertex will be assigned storage for up to
+ // |num_corners_on_vertices[vert_id]| half-edges. Unused half-edges are marked
+ // with |sink_vert| == kInvalidVertexIndex.
+ struct VertexEdgePair {
+ VertexEdgePair()
+ : sink_vert(kInvalidVertexIndex), edge_corner(kInvalidCornerIndex) {}
+ VertexIndex sink_vert;
+ CornerIndex edge_corner;
+ };
+ std::vector<VertexEdgePair> vertex_edges(num_corners(), VertexEdgePair());
+
+ // For each vertex compute the offset (location where the first half-edge
+ // entry of a given vertex is going to be stored). This way each vertex is
+ // guaranteed to have a non-overlapping storage with respect to the other
+ // vertices.
+ std::vector<int> vertex_offset(num_corners_on_vertices.size());
+ int offset = 0;
+ for (size_t i = 0; i < num_corners_on_vertices.size(); ++i) {
+ vertex_offset[i] = offset;
+ offset += num_corners_on_vertices[i];
+ }
+
+ // Now go over the all half-edges (using their opposite corners) and either
+ // insert them to the |vertex_edge| array or connect them with existing
+ // half-edges.
+ for (CornerIndex c(0); c < num_corners(); ++c) {
+ const VertexIndex tip_v = Vertex(c);
+ const VertexIndex source_v = Vertex(Next(c));
+ const VertexIndex sink_v = Vertex(Previous(c));
+
+ const FaceIndex face_index = Face(c);
+ if (c == FirstCorner(face_index)) {
+ // Check whether the face is degenerated, if so ignore it.
+ const VertexIndex v0 = Vertex(c);
+ if (v0 == source_v || v0 == sink_v || source_v == sink_v) {
+ ++num_degenerated_faces_;
+ c += 2; // Ignore the next two corners of the same face.
+ continue;
+ }
+ }
+
+ CornerIndex opposite_c(kInvalidCornerIndex);
+ // 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.
+ 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 == kInvalidVertexIndex)
+ break; // No matching half-edge found on the sink vertex.
+ if (other_v == source_v) {
+ if (tip_v == Vertex(vertex_edges[offset].edge_corner))
+ continue; // Don't connect mirrored faces.
+ // A matching half-edge was found on the sink vertex. Mark the
+ // half-edge's opposite corner.
+ opposite_c = vertex_edges[offset].edge_corner;
+ // Remove the half-edge from the sink vertex. We remap all subsequent
+ // half-edges one slot down.
+ // TODO(ostava): This can be optimized a little bit, by remapping only
+ // the half-edge on the last valid slot into the deleted half-edge's
+ // slot.
+ for (int j = i + 1; j < num_corners_on_vert; ++j, ++offset) {
+ vertex_edges[offset] = vertex_edges[offset + 1];
+ if (vertex_edges[offset].sink_vert == kInvalidVertexIndex)
+ break; // Unused half-edge reached.
+ }
+ // Mark the last entry as unused.
+ vertex_edges[offset].sink_vert = kInvalidVertexIndex;
+ break;
+ }
+ }
+ if (opposite_c == kInvalidCornerIndex) {
+ // No opposite corner found. Insert the new edge
+ const int num_corners_on_source_vert =
+ num_corners_on_vertices[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 == kInvalidVertexIndex) {
+ vertex_edges[offset].sink_vert = sink_v;
+ vertex_edges[offset].edge_corner = c;
+ break;
+ }
+ }
+ } else {
+ // Opposite corner found.
+ opposite_corners_[c] = opposite_c;
+ opposite_corners_[opposite_c] = c;
+ }
+ }
+ *num_vertices = static_cast<int>(num_corners_on_vertices.size());
+ return true;
+}
+
+bool CornerTable::ComputeVertexCorners(int num_vertices) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ num_original_vertices_ = num_vertices;
+ vertex_corners_.resize(num_vertices, kInvalidCornerIndex);
+ // Arrays for marking visited vertices and corners that allow us to detect
+ // non-manifold vertices.
+ std::vector<bool> visited_vertices(num_vertices, false);
+ std::vector<bool> visited_corners(num_corners(), false);
+
+ for (FaceIndex f(0); f < num_faces(); ++f) {
+ const CornerIndex first_face_corner = FirstCorner(f);
+ // Check whether the face is degenerated. If so ignore it.
+ if (IsDegenerated(f))
+ continue;
+
+ for (int k = 0; k < 3; ++k) {
+ const CornerIndex c = first_face_corner + k;
+ if (visited_corners[c.value()])
+ continue;
+ VertexIndex v = corner_to_vertex_map_[c];
+ // Note that one vertex maps to many corners, but we just keep track
+ // of the vertex which has a boundary on the left if the vertex lies on
+ // the boundary. This means that all the related corners can be accessed
+ // by iterating over the SwingRight() operator.
+ // In case of a vertex inside the mesh, the choice is arbitrary.
+ bool is_non_manifold_vertex = false;
+ if (visited_vertices[v.value()]) {
+ // A visited vertex of an unvisited corner found. Must be a non-manifold
+ // vertex.
+ // Create a new vertex for it.
+ vertex_corners_.push_back(kInvalidCornerIndex);
+ non_manifold_vertex_parents_.push_back(v);
+ visited_vertices.push_back(false);
+ v = VertexIndex(num_vertices++);
+ is_non_manifold_vertex = true;
+ }
+ // Mark the vertex as visited.
+ visited_vertices[v.value()] = true;
+
+ // First swing all the way to the left and mark all corners on the way.
+ CornerIndex act_c(c);
+ while (act_c != kInvalidCornerIndex) {
+ visited_corners[act_c.value()] = true;
+ // Vertex will eventually point to the left most corner.
+ vertex_corners_[v] = act_c;
+ if (is_non_manifold_vertex) {
+ // Update vertex index in the corresponding face.
+ corner_to_vertex_map_[act_c] = v;
+ }
+ act_c = SwingLeft(act_c);
+ if (act_c == c)
+ break; // Full circle reached.
+ }
+ if (act_c == kInvalidCornerIndex) {
+ // If we have reached an open boundary we need to swing right from the
+ // initial corner to mark all corners in the opposite direction.
+ act_c = SwingRight(c);
+ while (act_c != kInvalidCornerIndex) {
+ visited_corners[act_c.value()] = true;
+ if (is_non_manifold_vertex) {
+ // Update vertex index in the corresponding face.
+ corner_to_vertex_map_[act_c] = v;
+ }
+ act_c = SwingRight(act_c);
+ }
+ }
+ }
+ }
+
+ // Count the number of isolated (unprocessed) vertices.
+ num_isolated_vertices_ = 0;
+ for (bool visited : visited_vertices) {
+ if (!visited)
+ ++num_isolated_vertices_;
+ }
+ return true;
+}
+
+bool CornerTable::IsDegenerated(FaceIndex face) const {
+ if (face == kInvalidFaceIndex)
+ return true;
+ const CornerIndex first_face_corner = FirstCorner(face);
+ const VertexIndex v0 = Vertex(first_face_corner);
+ const VertexIndex v1 = Vertex(Next(first_face_corner));
+ const VertexIndex v2 = Vertex(Previous(first_face_corner));
+ if (v0 == v1 || v0 == v2 || v1 == v2)
+ return true;
+ return false;
+}
+
+int CornerTable::Valence(VertexIndex v) const {
+ if (v == kInvalidVertexIndex)
+ return -1;
+ return ConfidentValence(v);
+}
+
+int CornerTable::ConfidentValence(VertexIndex v) const {
+ DRACO_DCHECK_GE(v.value(), 0);
+ DRACO_DCHECK_LT(v.value(), num_vertices());
+ VertexRingIterator<CornerTable> vi(this, v);
+ int valence = 0;
+ for (; !vi.End(); vi.Next()) {
+ ++valence;
+ }
+ return valence;
+}
+
+void CornerTable::UpdateFaceToVertexMap(const VertexIndex vertex) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ VertexCornersIterator<CornerTable> it(this, vertex);
+ for (; !it.End(); ++it) {
+ const CornerIndex corner = *it;
+ corner_to_vertex_map_[corner] = vertex;
+ }
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/mesh/corner_table.h b/extern/draco/dracoenc/src/draco/mesh/corner_table.h
new file mode 100644
index 00000000000..b916b995f66
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/corner_table.h
@@ -0,0 +1,364 @@
+// 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_MESH_CORNER_TABLE_H_
+#define DRACO_MESH_CORNER_TABLE_H_
+
+#include <array>
+#include <memory>
+
+#include "draco/attributes/geometry_indices.h"
+#include "draco/core/draco_index_type_vector.h"
+#include "draco/core/macros.h"
+#include "draco/mesh/valence_cache.h"
+
+namespace draco {
+
+// CornerTable is used to represent connectivity of triangular meshes.
+// For every corner of all faces, the corner table stores the index of the
+// opposite corner in the neighboring face (if it exists) as illustrated in the
+// figure below (see corner |c| and it's opposite corner |o|).
+//
+// *
+// /c\
+// / \
+// /n p\
+// *-------*
+// \ /
+// \ /
+// \o/
+// *
+//
+// All corners are defined by unique CornerIndex and each triplet of corners
+// that define a single face id always ordered consecutively as:
+// { 3 * FaceIndex, 3 * FaceIndex + 1, 3 * FaceIndex +2 }.
+// This representation of corners allows CornerTable to easily retrieve Next and
+// Previous corners on any face (see corners |n| and |p| in the figure above).
+// Using the Next, Previous, and Opposite corners then enables traversal of any
+// 2-manifold surface.
+// If the CornerTable is constructed from a non-manifold surface, the input
+// non-manifold edges and vertices are automatically split.
+class CornerTable {
+ public:
+ // TODO(hemmer): rename to Face.
+ // Corner table face type.
+ typedef std::array<VertexIndex, 3> FaceType;
+
+ CornerTable();
+ static std::unique_ptr<CornerTable> Create(
+ const IndexTypeVector<FaceIndex, FaceType> &faces);
+
+ // Initializes the CornerTable from provides set of indexed faces.
+ // The input faces can represent a non-manifold topology, in which case the
+ // non-manifold edges and vertices are going to be split.
+ bool Init(const IndexTypeVector<FaceIndex, FaceType> &faces);
+
+ // Resets the corner table to the given number of invalid faces.
+ bool Reset(int num_faces);
+
+ // Resets the corner table to the given number of invalid faces and vertices.
+ bool Reset(int num_faces, int num_vertices);
+
+ inline int num_vertices() const {
+ return static_cast<int>(vertex_corners_.size());
+ }
+ inline int num_corners() const {
+ return static_cast<int>(corner_to_vertex_map_.size());
+ }
+ inline int num_faces() const {
+ return static_cast<int>(corner_to_vertex_map_.size() / 3);
+ }
+
+ inline CornerIndex Opposite(CornerIndex corner) const {
+ if (corner == kInvalidCornerIndex)
+ return corner;
+ return opposite_corners_[corner];
+ }
+ inline CornerIndex Next(CornerIndex corner) const {
+ if (corner == kInvalidCornerIndex)
+ return corner;
+ return LocalIndex(++corner) ? corner : corner - 3;
+ }
+ inline CornerIndex Previous(CornerIndex corner) const {
+ if (corner == kInvalidCornerIndex)
+ return corner;
+ return LocalIndex(corner) ? corner - 1 : corner + 2;
+ }
+ inline VertexIndex Vertex(CornerIndex corner) const {
+ if (corner == kInvalidCornerIndex)
+ return kInvalidVertexIndex;
+ return ConfidentVertex(corner);
+ }
+ inline VertexIndex ConfidentVertex(CornerIndex corner) const {
+ DRACO_DCHECK_GE(corner.value(), 0);
+ DRACO_DCHECK_LT(corner.value(), num_corners());
+ return corner_to_vertex_map_[corner];
+ }
+ inline FaceIndex Face(CornerIndex corner) const {
+ if (corner == kInvalidCornerIndex)
+ return kInvalidFaceIndex;
+ return FaceIndex(corner.value() / 3);
+ }
+ inline CornerIndex FirstCorner(FaceIndex face) const {
+ if (face == kInvalidFaceIndex)
+ return kInvalidCornerIndex;
+ return CornerIndex(face.value() * 3);
+ }
+ inline std::array<CornerIndex, 3> AllCorners(FaceIndex face) const {
+ const CornerIndex ci = CornerIndex(face.value() * 3);
+ return {{ci, ci + 1, ci + 2}};
+ }
+ inline int LocalIndex(CornerIndex corner) const { return corner.value() % 3; }
+
+ inline FaceType FaceData(FaceIndex face) const {
+ const CornerIndex first_corner = FirstCorner(face);
+ FaceType face_data;
+ for (int i = 0; i < 3; ++i) {
+ face_data[i] = corner_to_vertex_map_[first_corner + i];
+ }
+ return face_data;
+ }
+
+ void SetFaceData(FaceIndex face, FaceType data) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ const CornerIndex first_corner = FirstCorner(face);
+ for (int i = 0; i < 3; ++i) {
+ corner_to_vertex_map_[first_corner + i] = data[i];
+ }
+ }
+
+ // Returns the left-most corner of a single vertex 1-ring. If a vertex is not
+ // on a boundary (in which case it has a full 1-ring), this function returns
+ // any of the corners mapped to the given vertex.
+ inline CornerIndex LeftMostCorner(VertexIndex v) const {
+ return vertex_corners_[v];
+ }
+
+ // Returns the parent vertex index of a given corner table vertex.
+ VertexIndex VertexParent(VertexIndex vertex) const {
+ if (vertex.value() < static_cast<uint32_t>(num_original_vertices_))
+ return vertex;
+ return non_manifold_vertex_parents_[vertex - num_original_vertices_];
+ }
+
+ // Returns true if the corner is valid.
+ inline bool IsValid(CornerIndex c) const {
+ return Vertex(c) != kInvalidVertexIndex;
+ }
+
+ // Returns the valence (or degree) of a vertex.
+ // Returns -1 if the given vertex index is not valid.
+ int Valence(VertexIndex v) const;
+ // Same as above but does not check for validity and does not return -1
+ int ConfidentValence(VertexIndex v) const;
+ // Returns the valence of the vertex at the given corner.
+ inline int Valence(CornerIndex c) const {
+ if (c == kInvalidCornerIndex)
+ return -1;
+ return ConfidentValence(c);
+ }
+ inline int ConfidentValence(CornerIndex c) const {
+ DRACO_DCHECK_LT(c.value(), num_corners());
+ return ConfidentValence(ConfidentVertex(c));
+ }
+
+ // Returns true if the specified vertex is on a boundary.
+ inline bool IsOnBoundary(VertexIndex vert) const {
+ const CornerIndex corner = LeftMostCorner(vert);
+ if (SwingLeft(corner) == kInvalidCornerIndex)
+ return true;
+ return false;
+ }
+
+ // *-------*
+ // / \ / \
+ // / \ / \
+ // / sl\c/sr \
+ // *-------v-------*
+ // Returns the corner on the adjacent face on the right that maps to
+ // the same vertex as the given corner (sr in the above diagram).
+ inline CornerIndex SwingRight(CornerIndex corner) const {
+ return Previous(Opposite(Previous(corner)));
+ }
+ // Returns the corner on the left face that maps to the same vertex as the
+ // given corner (sl in the above diagram).
+ inline CornerIndex SwingLeft(CornerIndex corner) const {
+ return Next(Opposite(Next(corner)));
+ }
+
+ // Get opposite corners on the left and right faces respectively (see image
+ // below, where L and R are the left and right corners of a corner X.
+ //
+ // *-------*-------*
+ // \L /X\ R/
+ // \ / \ /
+ // \ / \ /
+ // *-------*
+ inline CornerIndex GetLeftCorner(CornerIndex corner_id) const {
+ if (corner_id == kInvalidCornerIndex)
+ return kInvalidCornerIndex;
+ return Opposite(Previous(corner_id));
+ }
+ inline CornerIndex GetRightCorner(CornerIndex corner_id) const {
+ if (corner_id == kInvalidCornerIndex)
+ return kInvalidCornerIndex;
+ return Opposite(Next(corner_id));
+ }
+
+ // Returns the number of new vertices that were created as a result of
+ // splitting of non-manifold vertices of the input geometry.
+ int NumNewVertices() const { return num_vertices() - num_original_vertices_; }
+ int NumOriginalVertices() const { return num_original_vertices_; }
+
+ // Returns the number of faces with duplicated vertex indices.
+ int NumDegeneratedFaces() const { return num_degenerated_faces_; }
+
+ // Returns the number of isolated vertices (vertices that have
+ // vertex_corners_ mapping set to kInvalidCornerIndex.
+ int NumIsolatedVertices() const { return num_isolated_vertices_; }
+
+ bool IsDegenerated(FaceIndex face) const;
+
+ // Methods that modify an existing corner table.
+ // Sets the opposite corner mapping between two corners. Caller must ensure
+ // that the indices are valid.
+ inline void SetOppositeCorner(CornerIndex corner_id,
+ CornerIndex opp_corner_id) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ opposite_corners_[corner_id] = opp_corner_id;
+ }
+
+ // Sets opposite corners for both input corners.
+ inline void SetOppositeCorners(CornerIndex corner_0, CornerIndex corner_1) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ if (corner_0 != kInvalidCornerIndex)
+ SetOppositeCorner(corner_0, corner_1);
+ if (corner_1 != kInvalidCornerIndex)
+ SetOppositeCorner(corner_1, corner_0);
+ }
+
+ // Updates mapping between a corner and a vertex.
+ inline void MapCornerToVertex(CornerIndex corner_id, VertexIndex vert_id) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ corner_to_vertex_map_[corner_id] = vert_id;
+ }
+
+ VertexIndex AddNewVertex() {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ // Add a new invalid vertex.
+ vertex_corners_.push_back(kInvalidCornerIndex);
+ return VertexIndex(static_cast<uint32_t>(vertex_corners_.size() - 1));
+ }
+
+ // Sets a new left most corner for a given vertex.
+ void SetLeftMostCorner(VertexIndex vert, CornerIndex corner) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ if (vert != kInvalidVertexIndex)
+ vertex_corners_[vert] = corner;
+ }
+
+ // Updates the vertex to corner map on a specified vertex. This should be
+ // called in cases where the mapping may be invalid (e.g. when the corner
+ // table was constructed manually).
+ void UpdateVertexToCornerMap(VertexIndex vert) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ const CornerIndex first_c = vertex_corners_[vert];
+ if (first_c == kInvalidCornerIndex)
+ return; // Isolated vertex.
+ CornerIndex act_c = SwingLeft(first_c);
+ CornerIndex c = first_c;
+ while (act_c != kInvalidCornerIndex && act_c != first_c) {
+ c = act_c;
+ act_c = SwingLeft(act_c);
+ }
+ if (act_c != first_c) {
+ vertex_corners_[vert] = c;
+ }
+ }
+
+ // Sets the new number of vertices. It's a responsibility of the caller to
+ // ensure that no corner is mapped beyond the range of the new number of
+ // vertices.
+ inline void SetNumVertices(int num_vertices) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ vertex_corners_.resize(num_vertices, kInvalidCornerIndex);
+ }
+
+ // Makes a vertex isolated (not attached to any corner).
+ void MakeVertexIsolated(VertexIndex vert) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ vertex_corners_[vert] = kInvalidCornerIndex;
+ }
+
+ // Returns true if a vertex is not attached to any face.
+ inline bool IsVertexIsolated(VertexIndex v) const {
+ return LeftMostCorner(v) == kInvalidCornerIndex;
+ }
+
+ // Makes a given face invalid (all corners are marked as invalid).
+ void MakeFaceInvalid(FaceIndex face) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ if (face != kInvalidFaceIndex) {
+ const CornerIndex first_corner = FirstCorner(face);
+ for (int i = 0; i < 3; ++i) {
+ corner_to_vertex_map_[first_corner + i] = kInvalidVertexIndex;
+ }
+ }
+ }
+
+ // Updates mapping between faces and a vertex using the corners mapped to
+ // the provided vertex.
+ void UpdateFaceToVertexMap(const VertexIndex vertex);
+
+ // Allows access to an internal object for caching valences. The object can
+ // be instructed to cache or uncache all valences and then its interfaces
+ // queried directly for valences with differing performance/confidence
+ // qualities. If the mesh or table is modified the cache should be discarded
+ // and not relied on as it does not automatically update or invalidate for
+ // performance reasons.
+ const draco::ValenceCache<CornerTable> &GetValenceCache() const {
+ return valence_cache_;
+ }
+
+ private:
+ // Computes opposite corners mapping from the data stored in
+ // |corner_to_vertex_map_|. Any non-manifold edge will be split so the result
+ // is always a 2-manifold surface.
+ bool ComputeOppositeCorners(int *num_vertices);
+
+ // Computes the lookup map for going from a vertex to a corner. This method
+ // can handle non-manifold vertices by splitting them into multiple manifold
+ // vertices.
+ bool ComputeVertexCorners(int num_vertices);
+
+ // Each three consecutive corners represent one face.
+ IndexTypeVector<CornerIndex, VertexIndex> corner_to_vertex_map_;
+ IndexTypeVector<CornerIndex, CornerIndex> opposite_corners_;
+ IndexTypeVector<VertexIndex, CornerIndex> vertex_corners_;
+
+ int num_original_vertices_;
+ int num_degenerated_faces_;
+ int num_isolated_vertices_;
+ IndexTypeVector<VertexIndex, VertexIndex> non_manifold_vertex_parents_;
+
+ draco::ValenceCache<CornerTable> valence_cache_;
+};
+
+// A special case to denote an invalid corner table triangle.
+static constexpr CornerTable::FaceType kInvalidFace(
+ {{kInvalidVertexIndex, kInvalidVertexIndex, kInvalidVertexIndex}});
+
+} // namespace draco
+
+#endif // DRACO_MESH_CORNER_TABLE_H_
diff --git a/extern/draco/dracoenc/src/draco/mesh/corner_table_iterators.h b/extern/draco/dracoenc/src/draco/mesh/corner_table_iterators.h
new file mode 100644
index 00000000000..65869dbe109
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/corner_table_iterators.h
@@ -0,0 +1,281 @@
+// 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_MESH_CORNER_TABLE_ITERATORS_H_
+#define DRACO_MESH_CORNER_TABLE_ITERATORS_H_
+
+#include "draco/mesh/corner_table.h"
+
+namespace draco {
+
+// Class for iterating over vertices in a 1-ring around the specified vertex.
+template <class CornerTableT>
+class VertexRingIterator
+ : public std::iterator<std::forward_iterator_tag, VertexIndex> {
+ public:
+ // std::iterator interface requires a default constructor.
+ VertexRingIterator()
+ : corner_table_(nullptr),
+ start_corner_(kInvalidCornerIndex),
+ corner_(start_corner_),
+ left_traversal_(true) {}
+
+ // Create the iterator from the provided corner table and the central vertex.
+ VertexRingIterator(const CornerTableT *table, VertexIndex vert_id)
+ : corner_table_(table),
+ start_corner_(table->LeftMostCorner(vert_id)),
+ corner_(start_corner_),
+ left_traversal_(true) {}
+
+ // Gets the last visited ring vertex.
+ VertexIndex Vertex() const {
+ CornerIndex ring_corner = left_traversal_ ? corner_table_->Previous(corner_)
+ : corner_table_->Next(corner_);
+ return corner_table_->Vertex(ring_corner);
+ }
+
+ // Returns true when all ring vertices have been visited.
+ bool End() const { return corner_ == kInvalidCornerIndex; }
+
+ // Proceeds to the next ring vertex if possible.
+ void Next() {
+ if (left_traversal_) {
+ corner_ = corner_table_->SwingLeft(corner_);
+ if (corner_ == kInvalidCornerIndex) {
+ // Open boundary reached.
+ corner_ = start_corner_;
+ left_traversal_ = false;
+ } else if (corner_ == start_corner_) {
+ // End reached.
+ corner_ = kInvalidCornerIndex;
+ }
+ } else {
+ // Go to the right until we reach a boundary there (no explicit check
+ // is needed in this case).
+ corner_ = corner_table_->SwingRight(corner_);
+ }
+ }
+
+ // std::iterator interface.
+ value_type operator*() const { return Vertex(); }
+ VertexRingIterator &operator++() {
+ Next();
+ return *this;
+ }
+ VertexRingIterator operator++(int) {
+ const VertexRingIterator result = *this;
+ ++(*this);
+ return result;
+ }
+ bool operator!=(const VertexRingIterator &other) const {
+ return corner_ != other.corner_ || start_corner_ != other.start_corner_;
+ }
+ bool operator==(const VertexRingIterator &other) const {
+ return !this->operator!=(other);
+ }
+
+ // Helper function for getting a valid end iterator.
+ static VertexRingIterator EndIterator(VertexRingIterator other) {
+ VertexRingIterator ret = other;
+ ret.corner_ = kInvalidCornerIndex;
+ return ret;
+ }
+
+ private:
+ const CornerTableT *corner_table_;
+ // The first processed corner.
+ CornerIndex start_corner_;
+ // The last processed corner.
+ CornerIndex corner_;
+ // Traversal direction.
+ bool left_traversal_;
+};
+
+// Class for iterating over faces adjacent to the specified input face.
+template <class CornerTableT>
+class FaceAdjacencyIterator
+ : public std::iterator<std::forward_iterator_tag, FaceIndex> {
+ public:
+ // std::iterator interface requires a default constructor.
+ FaceAdjacencyIterator()
+ : corner_table_(nullptr),
+ start_corner_(kInvalidCornerIndex),
+ corner_(start_corner_) {}
+
+ // Create the iterator from the provided corner table and the central vertex.
+ FaceAdjacencyIterator(const CornerTableT *table, FaceIndex face_id)
+ : corner_table_(table),
+ start_corner_(table->FirstCorner(face_id)),
+ corner_(start_corner_) {
+ // We need to start with a corner that has a valid opposite face (if
+ // there is any such corner).
+ if (corner_table_->Opposite(corner_) == kInvalidCornerIndex)
+ FindNextFaceNeighbor();
+ }
+
+ // Gets the last visited adjacent face.
+ FaceIndex Face() const {
+ return corner_table_->Face(corner_table_->Opposite(corner_));
+ }
+
+ // Returns true when all adjacent faces have been visited.
+ bool End() const { return corner_ == kInvalidCornerIndex; }
+
+ // Proceeds to the next adjacent face if possible.
+ void Next() { FindNextFaceNeighbor(); }
+
+ // std::iterator interface.
+ value_type operator*() const { return Face(); }
+ FaceAdjacencyIterator &operator++() {
+ Next();
+ return *this;
+ }
+ FaceAdjacencyIterator operator++(int) {
+ const FaceAdjacencyIterator result = *this;
+ ++(*this);
+ return result;
+ }
+ bool operator!=(const FaceAdjacencyIterator &other) const {
+ return corner_ != other.corner_ || start_corner_ != other.start_corner_;
+ }
+ bool operator==(const FaceAdjacencyIterator &other) const {
+ return !this->operator!=(other);
+ }
+
+ // Helper function for getting a valid end iterator.
+ static FaceAdjacencyIterator EndIterator(FaceAdjacencyIterator other) {
+ FaceAdjacencyIterator ret = other;
+ ret.corner_ = kInvalidCornerIndex;
+ return ret;
+ }
+
+ private:
+ // Finds the next corner with a valid opposite face.
+ void FindNextFaceNeighbor() {
+ while (corner_ != kInvalidCornerIndex) {
+ corner_ = corner_table_->Next(corner_);
+ if (corner_ == start_corner_) {
+ corner_ = kInvalidCornerIndex;
+ return;
+ }
+ if (corner_table_->Opposite(corner_) != kInvalidCornerIndex) {
+ // Valid opposite face.
+ return;
+ }
+ }
+ }
+
+ const CornerTableT *corner_table_;
+ // The first processed corner.
+ CornerIndex start_corner_;
+ // The last processed corner.
+ CornerIndex corner_;
+};
+
+// Class for iterating over corners attached to a specified vertex.
+template <class CornerTableT = CornerTable>
+class VertexCornersIterator
+ : public std::iterator<std::forward_iterator_tag, CornerIndex> {
+ public:
+ // std::iterator interface requires a default constructor.
+ VertexCornersIterator()
+ : corner_table_(nullptr),
+ start_corner_(-1),
+ corner_(start_corner_),
+ left_traversal_(true) {}
+
+ // Create the iterator from the provided corner table and the central vertex.
+ VertexCornersIterator(const CornerTableT *table, VertexIndex vert_id)
+ : corner_table_(table),
+ start_corner_(table->LeftMostCorner(vert_id)),
+ corner_(start_corner_),
+ left_traversal_(true) {}
+
+ // Create the iterator from the provided corner table and the first corner.
+ VertexCornersIterator(const CornerTableT *table, CornerIndex corner_id)
+ : corner_table_(table),
+ start_corner_(corner_id),
+ corner_(start_corner_),
+ left_traversal_(true) {}
+
+ // Gets the last visited corner.
+ CornerIndex Corner() const { return corner_; }
+
+ // Returns true when all ring vertices have been visited.
+ bool End() const { return corner_ == kInvalidCornerIndex; }
+
+ // Proceeds to the next corner if possible.
+ void Next() {
+ if (left_traversal_) {
+ corner_ = corner_table_->SwingLeft(corner_);
+ if (corner_ == kInvalidCornerIndex) {
+ // Open boundary reached.
+ corner_ = corner_table_->SwingRight(start_corner_);
+ left_traversal_ = false;
+ } else if (corner_ == start_corner_) {
+ // End reached.
+ corner_ = kInvalidCornerIndex;
+ }
+ } else {
+ // Go to the right until we reach a boundary there (no explicit check
+ // is needed in this case).
+ corner_ = corner_table_->SwingRight(corner_);
+ }
+ }
+
+ // std::iterator interface.
+ CornerIndex operator*() const { return Corner(); }
+ VertexCornersIterator &operator++() {
+ Next();
+ return *this;
+ }
+ VertexCornersIterator operator++(int) {
+ const VertexCornersIterator result = *this;
+ ++(*this);
+ return result;
+ }
+ bool operator!=(const VertexCornersIterator &other) const {
+ return corner_ != other.corner_ || start_corner_ != other.start_corner_;
+ }
+ bool operator==(const VertexCornersIterator &other) const {
+ return !this->operator!=(other);
+ }
+
+ // Helper function for getting a valid end iterator.
+ static VertexCornersIterator EndIterator(VertexCornersIterator other) {
+ VertexCornersIterator ret = other;
+ ret.corner_ = kInvalidCornerIndex;
+ return ret;
+ }
+
+ protected:
+ const CornerTableT *corner_table() const { return corner_table_; }
+ CornerIndex start_corner() const { return start_corner_; }
+ CornerIndex &corner() { return corner_; }
+ bool is_left_traversal() const { return left_traversal_; }
+ void swap_traversal() { left_traversal_ = !left_traversal_; }
+
+ private:
+ const CornerTableT *corner_table_;
+ // The first processed corner.
+ CornerIndex start_corner_;
+ // The last processed corner.
+ CornerIndex corner_;
+ // Traversal direction.
+ bool left_traversal_;
+};
+
+} // namespace draco
+
+#endif // DRACO_MESH_CORNER_TABLE_ITERATORS_H_
diff --git a/extern/draco/dracoenc/src/draco/mesh/mesh.cc b/extern/draco/dracoenc/src/draco/mesh/mesh.cc
new file mode 100644
index 00000000000..dd37a88d1fa
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/mesh.cc
@@ -0,0 +1,43 @@
+// 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 "draco/mesh/mesh.h"
+
+#include <array>
+#include <unordered_map>
+
+namespace draco {
+
+using std::unordered_map;
+
+// Shortcut for typed conditionals.
+template <bool B, class T, class F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+
+Mesh::Mesh() {}
+
+#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
+void Mesh::ApplyPointIdDeduplication(
+ const IndexTypeVector<PointIndex, PointIndex> &id_map,
+ const std::vector<PointIndex> &unique_point_ids) {
+ PointCloud::ApplyPointIdDeduplication(id_map, unique_point_ids);
+ for (FaceIndex f(0); f < num_faces(); ++f) {
+ for (int32_t c = 0; c < 3; ++c) {
+ faces_[f][c] = id_map[faces_[f][c]];
+ }
+ }
+}
+#endif
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/mesh/mesh.h b/extern/draco/dracoenc/src/draco/mesh/mesh.h
new file mode 100644
index 00000000000..dd41f6cde4a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/mesh.h
@@ -0,0 +1,151 @@
+// 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_MESH_MESH_H_
+#define DRACO_MESH_MESH_H_
+
+#include <memory>
+
+#include "draco/draco_features.h"
+
+#include "draco/attributes/geometry_indices.h"
+#include "draco/core/hash_utils.h"
+#include "draco/core/macros.h"
+#include "draco/point_cloud/point_cloud.h"
+
+namespace draco {
+
+// List of different variants of mesh attributes.
+enum MeshAttributeElementType {
+ // All corners attached to a vertex share the same attribute value. A typical
+ // example are the vertex positions and often vertex colors.
+ MESH_VERTEX_ATTRIBUTE = 0,
+ // The most general attribute where every corner of the mesh can have a
+ // different attribute value. Often used for texture coordinates or normals.
+ MESH_CORNER_ATTRIBUTE,
+ // All corners of a single face share the same value.
+ MESH_FACE_ATTRIBUTE
+};
+
+// Mesh class can be used to represent general triangular meshes. Internally,
+// Mesh is just an extended PointCloud with extra connectivity data that defines
+// what points are connected together in triangles.
+class Mesh : public PointCloud {
+ public:
+ typedef std::array<PointIndex, 3> Face;
+
+ Mesh();
+
+ void AddFace(const Face &face) { faces_.push_back(face); }
+
+ void SetFace(FaceIndex face_id, const Face &face) {
+ if (face_id >= static_cast<uint32_t>(faces_.size())) {
+ faces_.resize(face_id.value() + 1, Face());
+ }
+ faces_[face_id] = face;
+ }
+
+ // Sets the total number of faces. Creates new empty faces or deletes
+ // existing ones if necessary.
+ void SetNumFaces(size_t num_faces) { faces_.resize(num_faces, Face()); }
+
+ FaceIndex::ValueType num_faces() const {
+ return static_cast<uint32_t>(faces_.size());
+ }
+ const Face &face(FaceIndex face_id) const {
+ DRACO_DCHECK_LE(0, face_id.value());
+ DRACO_DCHECK_LT(face_id.value(), static_cast<int>(faces_.size()));
+ return faces_[face_id];
+ }
+
+ void SetAttribute(int att_id, std::unique_ptr<PointAttribute> pa) override {
+ PointCloud::SetAttribute(att_id, std::move(pa));
+ if (static_cast<int>(attribute_data_.size()) <= att_id) {
+ attribute_data_.resize(att_id + 1);
+ }
+ }
+
+ void DeleteAttribute(int att_id) override {
+ PointCloud::DeleteAttribute(att_id);
+ if (att_id >= 0 && att_id < static_cast<int>(attribute_data_.size())) {
+ attribute_data_.erase(attribute_data_.begin() + att_id);
+ }
+ }
+
+ MeshAttributeElementType GetAttributeElementType(int att_id) const {
+ return attribute_data_[att_id].element_type;
+ }
+
+ void SetAttributeElementType(int att_id, MeshAttributeElementType et) {
+ attribute_data_[att_id].element_type = et;
+ }
+
+ // Returns the point id of for a corner |ci|.
+ inline PointIndex CornerToPointId(int ci) const {
+ if (ci == kInvalidCornerIndex.value())
+ return kInvalidPointIndex;
+ return this->face(FaceIndex(ci / 3))[ci % 3];
+ }
+
+ // Returns the point id of a corner |ci|.
+ inline PointIndex CornerToPointId(CornerIndex ci) const {
+ return this->CornerToPointId(ci.value());
+ }
+
+ struct AttributeData {
+ AttributeData() : element_type(MESH_CORNER_ATTRIBUTE) {}
+ MeshAttributeElementType element_type;
+ };
+
+ protected:
+#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
+ // Extends the point deduplication to face corners. This method is called from
+ // the PointCloud::DeduplicatePointIds() and it remaps all point ids stored in
+ // |faces_| to the new deduplicated point ids using the map |id_map|.
+ void ApplyPointIdDeduplication(
+ const IndexTypeVector<PointIndex, PointIndex> &id_map,
+ const std::vector<PointIndex> &unique_point_ids) override;
+#endif
+
+ private:
+ // Mesh specific per-attribute data.
+ std::vector<AttributeData> attribute_data_;
+
+ // Vertex indices valid for all attributes. Each attribute has its own map
+ // that converts vertex indices into attribute indices.
+ IndexTypeVector<FaceIndex, Face> faces_;
+
+ friend struct MeshHasher;
+};
+
+// Functor for computing a hash from data stored within a mesh.
+// Note that this can be quite slow. Two meshes will have the same hash only
+// when they have exactly the same connectivity and attribute values.
+struct MeshHasher {
+ size_t operator()(const Mesh &mesh) const {
+ PointCloudHasher pc_hasher;
+ size_t hash = pc_hasher(mesh);
+ // Hash faces.
+ for (FaceIndex i(0); i < static_cast<uint32_t>(mesh.faces_.size()); ++i) {
+ for (int j = 0; j < 3; ++j) {
+ hash = HashCombine(mesh.faces_[i][j].value(), hash);
+ }
+ }
+ return hash;
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_MESH_MESH_H_
diff --git a/extern/draco/dracoenc/src/draco/mesh/mesh_are_equivalent.cc b/extern/draco/dracoenc/src/draco/mesh/mesh_are_equivalent.cc
new file mode 100644
index 00000000000..deb68940a54
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/mesh_are_equivalent.cc
@@ -0,0 +1,192 @@
+// 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 "draco/mesh/mesh_are_equivalent.h"
+
+#include <algorithm>
+
+namespace draco {
+
+void MeshAreEquivalent::PrintPosition(const Mesh &mesh, FaceIndex f,
+ int32_t c) {
+ fprintf(stderr, "Printing position for (%i,%i)\n", f.value(), c);
+ const auto pos_att = mesh.GetNamedAttribute(GeometryAttribute::POSITION);
+ const PointIndex ver_index = mesh.face(f)[c];
+ const AttributeValueIndex pos_index = pos_att->mapped_index(ver_index);
+ const auto pos = pos_att->GetValue<float, 3>(pos_index);
+ fprintf(stderr, "Position (%f,%f,%f)\n", pos[0], pos[1], pos[2]);
+}
+
+Vector3f MeshAreEquivalent::GetPosition(const Mesh &mesh, FaceIndex f,
+ int32_t c) {
+ const auto pos_att = mesh.GetNamedAttribute(GeometryAttribute::POSITION);
+ const PointIndex ver_index = mesh.face(f)[c];
+ const AttributeValueIndex pos_index = pos_att->mapped_index(ver_index);
+ const auto pos = pos_att->GetValue<float, 3>(pos_index);
+ return Vector3f(pos[0], pos[1], pos[2]);
+}
+
+void MeshAreEquivalent::InitCornerIndexOfSmallestPointXYZ() {
+ DRACO_DCHECK_EQ(mesh_infos_[0].corner_index_of_smallest_vertex.size(), 0);
+ DRACO_DCHECK_EQ(mesh_infos_[1].corner_index_of_smallest_vertex.size(), 0);
+ for (int i = 0; i < 2; ++i) {
+ mesh_infos_[i].corner_index_of_smallest_vertex.reserve(num_faces_);
+ for (FaceIndex f(0); f < num_faces_; ++f) {
+ mesh_infos_[i].corner_index_of_smallest_vertex.push_back(
+ ComputeCornerIndexOfSmallestPointXYZ(mesh_infos_[i].mesh, f));
+ }
+ }
+ DRACO_DCHECK_EQ(mesh_infos_[0].corner_index_of_smallest_vertex.size(),
+ num_faces_);
+ DRACO_DCHECK_EQ(mesh_infos_[1].corner_index_of_smallest_vertex.size(),
+ num_faces_);
+}
+
+void MeshAreEquivalent::InitOrderedFaceIndex() {
+ DRACO_DCHECK_EQ(mesh_infos_[0].ordered_index_of_face.size(), 0);
+ DRACO_DCHECK_EQ(mesh_infos_[1].ordered_index_of_face.size(), 0);
+ for (int32_t i = 0; i < 2; ++i) {
+ mesh_infos_[i].ordered_index_of_face.reserve(num_faces_);
+ for (FaceIndex j(0); j < num_faces_; ++j) {
+ mesh_infos_[i].ordered_index_of_face.push_back(j);
+ }
+ const FaceIndexLess less(mesh_infos_[i]);
+ std::sort(mesh_infos_[i].ordered_index_of_face.begin(),
+ mesh_infos_[i].ordered_index_of_face.end(), less);
+
+ DRACO_DCHECK_EQ(mesh_infos_[i].ordered_index_of_face.size(), num_faces_);
+ DRACO_DCHECK(std::is_sorted(mesh_infos_[i].ordered_index_of_face.begin(),
+ mesh_infos_[i].ordered_index_of_face.end(),
+ less));
+ }
+}
+
+int32_t MeshAreEquivalent::ComputeCornerIndexOfSmallestPointXYZ(
+ const Mesh &mesh, FaceIndex f) {
+ Vector3f pos[3]; // For the three corners.
+ for (int32_t i = 0; i < 3; ++i) {
+ pos[i] = GetPosition(mesh, f, i);
+ }
+ const auto min_it = std::min_element(pos, pos + 3);
+ return static_cast<int32_t>(min_it - pos);
+}
+
+void MeshAreEquivalent::Init(const Mesh &mesh0, const Mesh &mesh1) {
+ mesh_infos_.clear();
+ DRACO_DCHECK_EQ(mesh_infos_.size(), 0);
+
+ num_faces_ = mesh1.num_faces();
+ mesh_infos_.push_back(MeshInfo(mesh0));
+ mesh_infos_.push_back(MeshInfo(mesh1));
+
+ DRACO_DCHECK_EQ(mesh_infos_.size(), 2);
+ DRACO_DCHECK_EQ(mesh_infos_[0].corner_index_of_smallest_vertex.size(), 0);
+ DRACO_DCHECK_EQ(mesh_infos_[1].corner_index_of_smallest_vertex.size(), 0);
+ DRACO_DCHECK_EQ(mesh_infos_[0].ordered_index_of_face.size(), 0);
+ DRACO_DCHECK_EQ(mesh_infos_[1].ordered_index_of_face.size(), 0);
+
+ InitCornerIndexOfSmallestPointXYZ();
+ InitOrderedFaceIndex();
+}
+
+bool MeshAreEquivalent::operator()(const Mesh &mesh0, const Mesh &mesh1) {
+ if (mesh0.num_faces() != mesh1.num_faces())
+ return false;
+ if (mesh0.num_attributes() != mesh1.num_attributes())
+ return false;
+
+ // The following function inits mesh info, i.e., computes the order of
+ // faces with respect to the lex order. This way one can then compare the
+ // the two meshes face by face. It also determines the first corner of each
+ // face with respect to lex order.
+ Init(mesh0, mesh1);
+
+ // Check for every attribute that is valid that every corner is identical.
+ typedef GeometryAttribute::Type AttributeType;
+ const int att_max = AttributeType::NAMED_ATTRIBUTES_COUNT;
+ for (int att_id = 0; att_id < att_max; ++att_id) {
+ // First check for existence of the attribute in both meshes.
+ const PointAttribute *const att0 =
+ mesh0.GetNamedAttribute(AttributeType(att_id));
+ const PointAttribute *const att1 =
+ mesh1.GetNamedAttribute(AttributeType(att_id));
+ if (att0 == nullptr && att1 == nullptr)
+ continue;
+ if (att0 == nullptr)
+ return false;
+ if (att1 == nullptr)
+ return false;
+ if (att0->data_type() != att1->data_type())
+ return false;
+ if (att0->num_components() != att1->num_components())
+ return false;
+ if (att0->normalized() != att1->normalized())
+ return false;
+ if (att0->byte_stride() != att1->byte_stride())
+ return false;
+
+ DRACO_DCHECK(att0->IsValid());
+ DRACO_DCHECK(att1->IsValid());
+
+ // Prepare blocks of memory to hold data of corners for this attribute.
+ std::unique_ptr<uint8_t[]> data0(new uint8_t[att0->byte_stride()]);
+ std::unique_ptr<uint8_t[]> data1(new uint8_t[att0->byte_stride()]);
+
+ // Check every corner of every face.
+ for (int i = 0; i < num_faces_; ++i) {
+ const FaceIndex f0 = mesh_infos_[0].ordered_index_of_face[i];
+ const FaceIndex f1 = mesh_infos_[1].ordered_index_of_face[i];
+ const int c0_off = mesh_infos_[0].corner_index_of_smallest_vertex[f0];
+ const int c1_off = mesh_infos_[1].corner_index_of_smallest_vertex[f1];
+
+ for (int c = 0; c < 3; ++c) {
+ // Get the index of each corner.
+ const PointIndex corner0 = mesh0.face(f0)[(c0_off + c) % 3];
+ const PointIndex corner1 = mesh1.face(f1)[(c1_off + c) % 3];
+ // Map it to the right index for that attribute.
+ const AttributeValueIndex index0 = att0->mapped_index(corner0);
+ const AttributeValueIndex index1 = att1->mapped_index(corner1);
+
+ // Obtaining the data.
+ att0->GetValue(index0, data0.get());
+ att1->GetValue(index1, data1.get());
+ // Compare the data as is in memory.
+ if (memcmp(data0.get(), data1.get(), att0->byte_stride()) != 0)
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool MeshAreEquivalent::FaceIndexLess::operator()(FaceIndex f0,
+ FaceIndex f1) const {
+ if (f0 == f1)
+ return false;
+ const int c0 = mesh_info.corner_index_of_smallest_vertex[f0];
+ const int c1 = mesh_info.corner_index_of_smallest_vertex[f1];
+
+ for (int i = 0; i < 3; ++i) {
+ const Vector3f vf0 = GetPosition(mesh_info.mesh, f0, (c0 + i) % 3);
+ const Vector3f vf1 = GetPosition(mesh_info.mesh, f1, (c1 + i) % 3);
+ if (vf0 < vf1)
+ return true;
+ if (vf1 < vf0)
+ return false;
+ }
+ // In case the two faces are equivalent.
+ return false;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/mesh/mesh_are_equivalent.h b/extern/draco/dracoenc/src/draco/mesh/mesh_are_equivalent.h
new file mode 100644
index 00000000000..71ef4a93c12
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/mesh_are_equivalent.h
@@ -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.
+//
+#ifndef DRACO_MESH_MESH_ARE_EQUIVALENT_H_
+#define DRACO_MESH_MESH_ARE_EQUIVALENT_H_
+
+#include "draco/core/vector_d.h"
+#include "draco/mesh/mesh.h"
+
+// This file defines a functor to compare two meshes for equivalency up
+// to permutation of the vertices.
+namespace draco {
+
+// A functor to compare two meshes for equivalency up to permutation of the
+// vertices.
+class MeshAreEquivalent {
+ public:
+ // Returns true if both meshes are equivalent up to permutation of
+ // the internal order of vertices. This includes all attributes.
+ bool operator()(const Mesh &mesh0, const Mesh &mesh1);
+
+ private:
+ // Internal type to keep overview.
+ struct MeshInfo {
+ explicit MeshInfo(const Mesh &in_mesh) : mesh(in_mesh) {}
+ const Mesh &mesh;
+ std::vector<FaceIndex> ordered_index_of_face;
+ IndexTypeVector<FaceIndex, int> corner_index_of_smallest_vertex;
+ };
+
+ // Prepare functor for actual comparison.
+ void Init(const Mesh &mesh0, const Mesh &mesh1);
+
+ // Get position as Vector3f of corner c of face f.
+ static Vector3f GetPosition(const Mesh &mesh, FaceIndex f, int32_t c);
+ // Internal helper function mostly for debugging.
+ void PrintPosition(const Mesh &mesh, FaceIndex f, int32_t c);
+ // Get the corner index of the lex smallest vertex of face f.
+ static int32_t ComputeCornerIndexOfSmallestPointXYZ(const Mesh &mesh,
+ FaceIndex f);
+
+ // Less compare functor for two faces (represented by their indices)
+ // with respect to their lex order.
+ struct FaceIndexLess {
+ explicit FaceIndexLess(const MeshInfo &in_mesh_info)
+ : mesh_info(in_mesh_info) {}
+ bool operator()(FaceIndex f0, FaceIndex f1) const;
+ const MeshInfo &mesh_info;
+ };
+
+ void InitCornerIndexOfSmallestPointXYZ();
+ void InitOrderedFaceIndex();
+
+ std::vector<MeshInfo> mesh_infos_;
+ int32_t num_faces_;
+};
+
+} // namespace draco
+
+#endif // DRACO_MESH_MESH_ARE_EQUIVALENT_H_
diff --git a/extern/draco/dracoenc/src/draco/mesh/mesh_are_equivalent_test.cc b/extern/draco/dracoenc/src/draco/mesh/mesh_are_equivalent_test.cc
new file mode 100644
index 00000000000..ff633bc73d1
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/mesh_are_equivalent_test.cc
@@ -0,0 +1,99 @@
+// 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 "draco/mesh/mesh_are_equivalent.h"
+
+#include <sstream>
+
+#include "draco/core/draco_test_base.h"
+#include "draco/core/draco_test_utils.h"
+#include "draco/io/mesh_io.h"
+#include "draco/io/obj_decoder.h"
+#include "draco/mesh/mesh.h"
+
+namespace draco {
+
+class MeshAreEquivalentTest : public ::testing::Test {};
+
+TEST_F(MeshAreEquivalentTest, TestOnIndenticalMesh) {
+ const std::string file_name = "test_nm.obj";
+ const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name));
+ ASSERT_NE(mesh, nullptr) << "Failed to load test model." << file_name;
+ MeshAreEquivalent equiv;
+ ASSERT_TRUE(equiv(*mesh, *mesh));
+}
+
+TEST_F(MeshAreEquivalentTest, TestPermutedOneFace) {
+ const std::string file_name_0 = "one_face_123.obj";
+ const std::string file_name_1 = "one_face_312.obj";
+ const std::string file_name_2 = "one_face_321.obj";
+ const std::unique_ptr<Mesh> mesh_0(ReadMeshFromTestFile(file_name_0));
+ const std::unique_ptr<Mesh> mesh_1(ReadMeshFromTestFile(file_name_1));
+ const std::unique_ptr<Mesh> mesh_2(ReadMeshFromTestFile(file_name_2));
+ ASSERT_NE(mesh_0, nullptr) << "Failed to load test model." << file_name_0;
+ ASSERT_NE(mesh_1, nullptr) << "Failed to load test model." << file_name_1;
+ ASSERT_NE(mesh_2, nullptr) << "Failed to load test model." << file_name_2;
+ MeshAreEquivalent equiv;
+ ASSERT_TRUE(equiv(*mesh_0, *mesh_0));
+ ASSERT_TRUE(equiv(*mesh_0, *mesh_1)); // Face rotated.
+ ASSERT_FALSE(equiv(*mesh_0, *mesh_2)); // Face inverted.
+}
+
+TEST_F(MeshAreEquivalentTest, TestPermutedTwoFaces) {
+ const std::string file_name_0 = "two_faces_123.obj";
+ const std::string file_name_1 = "two_faces_312.obj";
+ const std::unique_ptr<Mesh> mesh_0(ReadMeshFromTestFile(file_name_0));
+ const std::unique_ptr<Mesh> mesh_1(ReadMeshFromTestFile(file_name_1));
+ ASSERT_NE(mesh_0, nullptr) << "Failed to load test model." << file_name_0;
+ ASSERT_NE(mesh_1, nullptr) << "Failed to load test model." << file_name_1;
+ MeshAreEquivalent equiv;
+ ASSERT_TRUE(equiv(*mesh_0, *mesh_0));
+ ASSERT_TRUE(equiv(*mesh_1, *mesh_1));
+ ASSERT_TRUE(equiv(*mesh_0, *mesh_1));
+}
+
+//
+TEST_F(MeshAreEquivalentTest, TestPermutedThreeFaces) {
+ const std::string file_name_0 = "three_faces_123.obj";
+ const std::string file_name_1 = "three_faces_312.obj";
+ const std::unique_ptr<Mesh> mesh_0(ReadMeshFromTestFile(file_name_0));
+ const std::unique_ptr<Mesh> mesh_1(ReadMeshFromTestFile(file_name_1));
+ ASSERT_NE(mesh_0, nullptr) << "Failed to load test model." << file_name_0;
+ ASSERT_NE(mesh_1, nullptr) << "Failed to load test model." << file_name_1;
+ MeshAreEquivalent equiv;
+ ASSERT_TRUE(equiv(*mesh_0, *mesh_0));
+ ASSERT_TRUE(equiv(*mesh_1, *mesh_1));
+ ASSERT_TRUE(equiv(*mesh_0, *mesh_1));
+}
+
+// This test checks that the edgebreaker algorithm does not change the mesh up
+// to the order of faces and vertices.
+TEST_F(MeshAreEquivalentTest, TestOnBigMesh) {
+ const std::string file_name = "test_nm.obj";
+ const std::unique_ptr<Mesh> mesh0(ReadMeshFromTestFile(file_name));
+ ASSERT_NE(mesh0, nullptr) << "Failed to load test model." << file_name;
+
+ std::unique_ptr<Mesh> mesh1;
+ std::stringstream ss;
+ WriteMeshIntoStream(mesh0.get(), ss, MESH_EDGEBREAKER_ENCODING);
+ ReadMeshFromStream(&mesh1, ss);
+ ASSERT_TRUE(ss.good()) << "Mesh IO failed.";
+
+ MeshAreEquivalent equiv;
+ ASSERT_TRUE(equiv(*mesh0, *mesh0));
+ ASSERT_TRUE(equiv(*mesh1, *mesh1));
+ ASSERT_TRUE(equiv(*mesh0, *mesh1));
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/mesh/mesh_attribute_corner_table.cc b/extern/draco/dracoenc/src/draco/mesh/mesh_attribute_corner_table.cc
new file mode 100644
index 00000000000..768d5f5e28e
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/mesh_attribute_corner_table.cc
@@ -0,0 +1,202 @@
+// 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 "draco/mesh/mesh_attribute_corner_table.h"
+#include "draco/mesh/corner_table_iterators.h"
+#include "draco/mesh/mesh_misc_functions.h"
+
+namespace draco {
+
+MeshAttributeCornerTable::MeshAttributeCornerTable()
+ : no_interior_seams_(true), corner_table_(nullptr), valence_cache_(*this) {}
+
+bool MeshAttributeCornerTable::InitEmpty(const CornerTable *table) {
+ if (table == nullptr)
+ return false;
+ valence_cache_.ClearValenceCache();
+ valence_cache_.ClearValenceCacheInaccurate();
+ is_edge_on_seam_.assign(table->num_corners(), false);
+ is_vertex_on_seam_.assign(table->num_vertices(), false);
+ corner_to_vertex_map_.assign(table->num_corners(), kInvalidVertexIndex);
+ vertex_to_attribute_entry_id_map_.reserve(table->num_vertices());
+ vertex_to_left_most_corner_map_.reserve(table->num_vertices());
+ corner_table_ = table;
+ no_interior_seams_ = true;
+ return true;
+}
+
+bool MeshAttributeCornerTable::InitFromAttribute(const Mesh *mesh,
+ const CornerTable *table,
+ const PointAttribute *att) {
+ if (!InitEmpty(table))
+ return false;
+ valence_cache_.ClearValenceCache();
+ valence_cache_.ClearValenceCacheInaccurate();
+
+ // Find all necessary data for encoding attributes. For now we check which of
+ // the mesh vertices is part of an attribute seam, because seams require
+ // special handling.
+ for (CornerIndex c(0); c < corner_table_->num_corners(); ++c) {
+ const FaceIndex f = corner_table_->Face(c);
+ if (corner_table_->IsDegenerated(f))
+ continue; // Ignore corners on degenerated faces.
+ const CornerIndex opp_corner = corner_table_->Opposite(c);
+ if (opp_corner == kInvalidCornerIndex) {
+ // Boundary. Mark it as seam edge.
+ is_edge_on_seam_[c.value()] = true;
+ // Mark seam vertices.
+ VertexIndex v;
+ v = corner_table_->Vertex(corner_table_->Next(c));
+ is_vertex_on_seam_[v.value()] = true;
+ v = corner_table_->Vertex(corner_table_->Previous(c));
+ is_vertex_on_seam_[v.value()] = true;
+ continue;
+ }
+ if (opp_corner < c)
+ continue; // Opposite corner was already processed.
+
+ CornerIndex act_c(c), act_sibling_c(opp_corner);
+ for (int i = 0; i < 2; ++i) {
+ // Get the sibling corners. I.e., the two corners attached to the same
+ // vertex but divided by the seam edge.
+ act_c = corner_table_->Next(act_c);
+ act_sibling_c = corner_table_->Previous(act_sibling_c);
+ const PointIndex point_id = mesh->CornerToPointId(act_c.value());
+ const PointIndex sibling_point_id =
+ mesh->CornerToPointId(act_sibling_c.value());
+ if (att->mapped_index(point_id) != att->mapped_index(sibling_point_id)) {
+ no_interior_seams_ = false;
+ is_edge_on_seam_[c.value()] = true;
+ is_edge_on_seam_[opp_corner.value()] = true;
+ // Mark seam vertices.
+ is_vertex_on_seam_[corner_table_
+ ->Vertex(corner_table_->Next(CornerIndex(c)))
+ .value()] = true;
+ is_vertex_on_seam_[corner_table_
+ ->Vertex(corner_table_->Previous(CornerIndex(c)))
+ .value()] = true;
+ is_vertex_on_seam_
+ [corner_table_->Vertex(corner_table_->Next(opp_corner)).value()] =
+ true;
+ is_vertex_on_seam_[corner_table_
+ ->Vertex(corner_table_->Previous(opp_corner))
+ .value()] = true;
+ break;
+ }
+ }
+ }
+ RecomputeVertices(mesh, att);
+ return true;
+}
+
+void MeshAttributeCornerTable::AddSeamEdge(CornerIndex c) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ is_edge_on_seam_[c.value()] = true;
+ // Mark seam vertices.
+ is_vertex_on_seam_[corner_table_->Vertex(corner_table_->Next(c)).value()] =
+ true;
+ is_vertex_on_seam_[corner_table_->Vertex(corner_table_->Previous(c))
+ .value()] = true;
+
+ const CornerIndex opp_corner = corner_table_->Opposite(c);
+ if (opp_corner != kInvalidCornerIndex) {
+ no_interior_seams_ = false;
+ is_edge_on_seam_[opp_corner.value()] = true;
+ is_vertex_on_seam_[corner_table_->Vertex(corner_table_->Next(opp_corner))
+ .value()] = true;
+ is_vertex_on_seam_
+ [corner_table_->Vertex(corner_table_->Previous(opp_corner)).value()] =
+ true;
+ }
+}
+
+void MeshAttributeCornerTable::RecomputeVertices(const Mesh *mesh,
+ const PointAttribute *att) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ if (mesh != nullptr && att != nullptr) {
+ RecomputeVerticesInternal<true>(mesh, att);
+ } else {
+ RecomputeVerticesInternal<false>(nullptr, nullptr);
+ }
+}
+
+template <bool init_vertex_to_attribute_entry_map>
+void MeshAttributeCornerTable::RecomputeVerticesInternal(
+ const Mesh *mesh, const PointAttribute *att) {
+ DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
+ int num_new_vertices = 0;
+ for (VertexIndex v(0); v < corner_table_->num_vertices(); ++v) {
+ const CornerIndex c = corner_table_->LeftMostCorner(v);
+ if (c == kInvalidCornerIndex)
+ continue; // Isolated vertex?
+ AttributeValueIndex first_vert_id(num_new_vertices++);
+ if (init_vertex_to_attribute_entry_map) {
+ const PointIndex point_id = mesh->CornerToPointId(c.value());
+ vertex_to_attribute_entry_id_map_.push_back(att->mapped_index(point_id));
+ } else {
+ // Identity mapping
+ vertex_to_attribute_entry_id_map_.push_back(first_vert_id);
+ }
+ CornerIndex first_c = c;
+ CornerIndex act_c;
+ // Check if the vertex is on a seam edge, if it is we need to find the first
+ // attribute entry on the seam edge when traversing in the CCW direction.
+ if (is_vertex_on_seam_[v.value()]) {
+ // Try to swing left on the modified corner table. We need to get the
+ // first corner that defines an attribute seam.
+ act_c = SwingLeft(first_c);
+ while (act_c != kInvalidCornerIndex) {
+ first_c = act_c;
+ act_c = SwingLeft(act_c);
+ }
+ }
+ corner_to_vertex_map_[first_c.value()] = VertexIndex(first_vert_id.value());
+ vertex_to_left_most_corner_map_.push_back(first_c);
+ act_c = corner_table_->SwingRight(first_c);
+ while (act_c != kInvalidCornerIndex && act_c != first_c) {
+ if (IsCornerOppositeToSeamEdge(corner_table_->Next(act_c))) {
+ first_vert_id = AttributeValueIndex(num_new_vertices++);
+ if (init_vertex_to_attribute_entry_map) {
+ const PointIndex point_id = mesh->CornerToPointId(act_c.value());
+ vertex_to_attribute_entry_id_map_.push_back(
+ att->mapped_index(point_id));
+ } else {
+ // Identity mapping.
+ vertex_to_attribute_entry_id_map_.push_back(first_vert_id);
+ }
+ vertex_to_left_most_corner_map_.push_back(act_c);
+ }
+ corner_to_vertex_map_[act_c.value()] = VertexIndex(first_vert_id.value());
+ act_c = corner_table_->SwingRight(act_c);
+ }
+ }
+}
+
+int MeshAttributeCornerTable::Valence(VertexIndex v) const {
+ if (v == kInvalidVertexIndex)
+ return -1;
+ return ConfidentValence(v);
+}
+
+int MeshAttributeCornerTable::ConfidentValence(VertexIndex v) const {
+ DRACO_DCHECK_LT(v.value(), num_vertices());
+ draco::VertexRingIterator<MeshAttributeCornerTable> vi(this, v);
+ int valence = 0;
+ for (; !vi.End(); vi.Next()) {
+ ++valence;
+ }
+ return valence;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/mesh/mesh_attribute_corner_table.h b/extern/draco/dracoenc/src/draco/mesh/mesh_attribute_corner_table.h
new file mode 100644
index 00000000000..b9e938f5b7d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/mesh_attribute_corner_table.h
@@ -0,0 +1,177 @@
+// 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_MESH_MESH_ATTRIBUTE_CORNER_TABLE_H_
+#define DRACO_MESH_MESH_ATTRIBUTE_CORNER_TABLE_H_
+
+#include "draco/core/macros.h"
+#include "draco/mesh/corner_table.h"
+#include "draco/mesh/mesh.h"
+#include "draco/mesh/valence_cache.h"
+
+namespace draco {
+
+// Class for storing connectivity of mesh attributes. The connectivity is stored
+// as a difference from the base mesh's corner table, where the differences are
+// represented by attribute seam edges. This class provides a basic
+// functionality for detecting the seam edges for a given attribute and for
+// traversing the constrained corner table with the seam edges.
+class MeshAttributeCornerTable {
+ public:
+ MeshAttributeCornerTable();
+ bool InitEmpty(const CornerTable *table);
+ bool InitFromAttribute(const Mesh *mesh, const CornerTable *table,
+ const PointAttribute *att);
+
+ void AddSeamEdge(CornerIndex opp_corner);
+
+ // Recomputes vertices using the newly added seam edges (needs to be called
+ // whenever the seam edges are updated).
+ // |mesh| and |att| can be null, in which case mapping between vertices and
+ // attribute value ids is set to identity.
+ void RecomputeVertices(const Mesh *mesh, const PointAttribute *att);
+
+ inline bool IsCornerOppositeToSeamEdge(CornerIndex corner) const {
+ return is_edge_on_seam_[corner.value()];
+ }
+
+ inline CornerIndex Opposite(CornerIndex corner) const {
+ if (corner == kInvalidCornerIndex || IsCornerOppositeToSeamEdge(corner))
+ return kInvalidCornerIndex;
+ return corner_table_->Opposite(corner);
+ }
+
+ inline CornerIndex Next(CornerIndex corner) const {
+ return corner_table_->Next(corner);
+ }
+
+ inline CornerIndex Previous(CornerIndex corner) const {
+ return corner_table_->Previous(corner);
+ }
+
+ // Returns true when a corner is attached to any attribute seam.
+ inline bool IsCornerOnSeam(CornerIndex corner) const {
+ return is_vertex_on_seam_[corner_table_->Vertex(corner).value()];
+ }
+
+ // Similar to CornerTable::GetLeftCorner and CornerTable::GetRightCorner, but
+ // does not go over seam edges.
+ inline CornerIndex GetLeftCorner(CornerIndex corner) const {
+ return Opposite(Previous(corner));
+ }
+ inline CornerIndex GetRightCorner(CornerIndex corner) const {
+ return Opposite(Next(corner));
+ }
+
+ // Similar to CornerTable::SwingRight, but it does not go over seam edges.
+ inline CornerIndex SwingRight(CornerIndex corner) const {
+ return Previous(Opposite(Previous(corner)));
+ }
+
+ // Similar to CornerTable::SwingLeft, but it does not go over seam edges.
+ inline CornerIndex SwingLeft(CornerIndex corner) const {
+ return Next(Opposite(Next(corner)));
+ }
+
+ int num_vertices() const {
+ return static_cast<int>(vertex_to_attribute_entry_id_map_.size());
+ }
+ int num_faces() const { return static_cast<int>(corner_table_->num_faces()); }
+
+ VertexIndex Vertex(CornerIndex corner) const {
+ DRACO_DCHECK_LT(corner.value(), corner_to_vertex_map_.size());
+ return ConfidentVertex(corner);
+ }
+ VertexIndex ConfidentVertex(CornerIndex corner) const {
+ return corner_to_vertex_map_[corner.value()];
+ }
+ // Returns the attribute entry id associated to the given vertex.
+ VertexIndex VertexParent(VertexIndex vert) const {
+ return VertexIndex(vertex_to_attribute_entry_id_map_[vert.value()].value());
+ }
+
+ inline CornerIndex LeftMostCorner(VertexIndex v) const {
+ return vertex_to_left_most_corner_map_[v.value()];
+ }
+
+ inline bool IsOnBoundary(VertexIndex vert) const {
+ const CornerIndex corner = LeftMostCorner(vert);
+ if (corner == kInvalidCornerIndex)
+ return true;
+ return IsCornerOnSeam(corner);
+ }
+
+ bool no_interior_seams() const { return no_interior_seams_; }
+ const CornerTable *corner_table() const { return corner_table_; }
+
+ // TODO(draco-eng): extract valence functions into a reusable class/object
+ // also from 'corner_table.*'
+
+ // Returns the valence (or degree) of a vertex.
+ // Returns -1 if the given vertex index is not valid.
+ int Valence(VertexIndex v) const;
+ // Same as above but does not check for validity and does not return -1
+ int ConfidentValence(VertexIndex v) const;
+ // Returns the valence of the vertex at the given corner.
+ inline int Valence(CornerIndex c) const {
+ DRACO_DCHECK_LT(c.value(), corner_table_->num_corners());
+ if (c == kInvalidCornerIndex)
+ return -1;
+ return ConfidentValence(c);
+ }
+ inline int ConfidentValence(CornerIndex c) const {
+ DRACO_DCHECK_LT(c.value(), corner_table_->num_corners());
+ return ConfidentValence(Vertex(c));
+ }
+
+ // Allows access to an internal object for caching valences. The object can
+ // be instructed to cache or uncache all valences and then its interfaces
+ // queried directly for valences with differing performance/confidence
+ // qualities. If the mesh or table is modified the cache should be discarded
+ // and not relied on as it does not automatically update or invalidate for
+ // performance reasons.
+ const ValenceCache<MeshAttributeCornerTable> &GetValenceCache() const {
+ return valence_cache_;
+ }
+
+ private:
+ template <bool init_vertex_to_attribute_entry_map>
+ void RecomputeVerticesInternal(const Mesh *mesh, const PointAttribute *att);
+
+ std::vector<bool> is_edge_on_seam_;
+ std::vector<bool> is_vertex_on_seam_;
+
+ // If this is set to true, it means that there are no attribute seams between
+ // two faces. This can be used to speed up some algorithms.
+ bool no_interior_seams_;
+
+ std::vector<VertexIndex> corner_to_vertex_map_;
+
+ // Map between vertices and their associated left most corners. A left most
+ // corner is a corner that is adjacent to a boundary or an attribute seam from
+ // right (i.e., SwingLeft from that corner will return an invalid corner). If
+ // no such corner exists for a given vertex, then any corner attached to the
+ // vertex can be used.
+ std::vector<CornerIndex> vertex_to_left_most_corner_map_;
+
+ // Map between vertex ids and attribute entry ids (i.e. the values stored in
+ // the attribute buffer). The attribute entry id can be retrieved using the
+ // VertexParent() method.
+ std::vector<AttributeValueIndex> vertex_to_attribute_entry_id_map_;
+ const CornerTable *corner_table_;
+ ValenceCache<MeshAttributeCornerTable> valence_cache_;
+};
+
+} // namespace draco
+#endif // DRACO_MESH_MESH_ATTRIBUTE_CORNER_TABLE_H_
diff --git a/extern/draco/dracoenc/src/draco/mesh/mesh_cleanup.cc b/extern/draco/dracoenc/src/draco/mesh/mesh_cleanup.cc
new file mode 100644
index 00000000000..8e2d393b8f3
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/mesh_cleanup.cc
@@ -0,0 +1,185 @@
+// 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 "draco/mesh/mesh_cleanup.h"
+
+namespace draco {
+
+bool MeshCleanup::operator()(Mesh *mesh, const MeshCleanupOptions &options) {
+ if (!options.remove_degenerated_faces && !options.remove_unused_attributes)
+ return true; // Nothing to cleanup.
+ const PointAttribute *const pos_att =
+ mesh->GetNamedAttribute(GeometryAttribute::POSITION);
+ if (pos_att == nullptr)
+ return false;
+ // Array that is going to store whether a corresponding point is used.
+ std::vector<bool> is_point_used;
+ if (options.remove_unused_attributes) {
+ is_point_used.resize(mesh->num_points(), false);
+ }
+
+ FaceIndex::ValueType num_degenerated_faces = 0;
+ PointIndex::ValueType num_new_points = 0;
+ // Array for storing position indices on a face.
+ std::array<AttributeValueIndex, 3> pos_indices;
+ for (FaceIndex f(0); f < mesh->num_faces(); ++f) {
+ const Mesh::Face &face = mesh->face(f);
+ for (int p = 0; p < 3; ++p) {
+ pos_indices[p] = pos_att->mapped_index(face[p]);
+ }
+ bool is_face_valid = true;
+ if (options.remove_degenerated_faces) {
+ if (pos_indices[0] == pos_indices[1] ||
+ pos_indices[0] == pos_indices[2] ||
+ pos_indices[1] == pos_indices[2]) {
+ ++num_degenerated_faces;
+ is_face_valid = false;
+ } else if (num_degenerated_faces > 0) {
+ // Copy the face to its new location.
+ mesh->SetFace(f - num_degenerated_faces, face);
+ }
+ }
+ if (options.remove_unused_attributes && is_face_valid) {
+ for (int p = 0; p < 3; ++p) {
+ if (!is_point_used[face[p].value()]) {
+ is_point_used[face[p].value()] = true;
+ ++num_new_points;
+ }
+ }
+ }
+ }
+ if (num_degenerated_faces > 0) {
+ mesh->SetNumFaces(mesh->num_faces() - num_degenerated_faces);
+ }
+ if (options.remove_unused_attributes) {
+ bool points_changed = false;
+ const PointIndex::ValueType num_original_points = mesh->num_points();
+ // Map from old points to the new ones.
+ IndexTypeVector<PointIndex, PointIndex> point_map(num_original_points);
+ if (num_new_points < static_cast<int>(mesh->num_points())) {
+ // Some of the points were removed. We need to remap the old points to the
+ // new ones.
+ num_new_points = 0;
+ for (PointIndex i(0); i < num_original_points; ++i) {
+ if (is_point_used[i.value()]) {
+ point_map[i] = num_new_points++;
+ } else {
+ point_map[i] = kInvalidPointIndex;
+ }
+ }
+ // Go over faces and update their points.
+ for (FaceIndex f(0); f < mesh->num_faces(); ++f) {
+ Mesh::Face face = mesh->face(f);
+ for (int p = 0; p < 3; ++p) {
+ face[p] = point_map[face[p]];
+ }
+ mesh->SetFace(f, face);
+ }
+ // Set the new number of points.
+ mesh->set_num_points(num_new_points);
+ points_changed = true;
+ } else {
+ // No points were removed. Initialize identity map between the old and new
+ // points.
+ for (PointIndex i(0); i < num_original_points; ++i) {
+ point_map[i] = i;
+ }
+ }
+
+ // Update index mapping for attributes.
+ IndexTypeVector<AttributeValueIndex, uint8_t> is_att_index_used;
+ IndexTypeVector<AttributeValueIndex, AttributeValueIndex> att_index_map;
+ for (int a = 0; a < mesh->num_attributes(); ++a) {
+ PointAttribute *const att = mesh->attribute(a);
+ // First detect which attribute entries are used (included in a point).
+ is_att_index_used.assign(att->size(), 0);
+ att_index_map.clear();
+ AttributeValueIndex::ValueType num_used_entries = 0;
+ for (PointIndex i(0); i < num_original_points; ++i) {
+ if (point_map[i] != kInvalidPointIndex) {
+ const AttributeValueIndex entry_id = att->mapped_index(i);
+ if (!is_att_index_used[entry_id]) {
+ is_att_index_used[entry_id] = 1;
+ ++num_used_entries;
+ }
+ }
+ }
+ bool att_indices_changed = false;
+ // If there are some unused attribute entries, remap the attribute values
+ // in the attribute buffer.
+ if (num_used_entries < static_cast<int>(att->size())) {
+ att_index_map.resize(att->size());
+ num_used_entries = 0;
+ for (AttributeValueIndex i(0); i < static_cast<uint32_t>(att->size());
+ ++i) {
+ if (is_att_index_used[i]) {
+ att_index_map[i] = num_used_entries;
+ if (i > num_used_entries) {
+ const uint8_t *const src_add = att->GetAddress(i);
+ att->buffer()->Write(
+ att->GetBytePos(AttributeValueIndex(num_used_entries)),
+ src_add, att->byte_stride());
+ }
+ ++num_used_entries;
+ }
+ }
+ // Update the number of unique entries in the vertex buffer.
+ att->Resize(num_used_entries);
+ att_indices_changed = true;
+ }
+ // If either the points or attribute indices have changed, we need to
+ // update the attribute index mapping.
+ if (points_changed || att_indices_changed) {
+ 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 != static_cast<int>(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.
+ att->SetExplicitMapping(num_original_points);
+ // Set the entries of the explicit map to identity.
+ for (PointIndex::ValueType i = 0; i < num_original_points; ++i) {
+ att->SetPointMapEntry(PointIndex(i), AttributeValueIndex(i));
+ }
+ }
+ }
+ if (!att->is_mapping_identity()) {
+ // Explicit mapping between points and local attribute indices.
+ for (PointIndex i(0); i < num_original_points; ++i) {
+ // The new point id that maps to the currently processed attribute
+ // entry.
+ const PointIndex new_point_id = point_map[i];
+ if (new_point_id == kInvalidPointIndex)
+ continue;
+ // Index of the currently processed attribute entry in the original
+ // mesh.
+ const AttributeValueIndex original_entry_index =
+ att->mapped_index(i);
+ // New index of the same entry after unused entries were removed.
+ const AttributeValueIndex new_entry_index =
+ att_index_map[original_entry_index];
+ att->SetPointMapEntry(new_point_id, new_entry_index);
+ }
+ // If the number of points changed, we need to set a new explicit map
+ // size.
+ att->SetExplicitMapping(mesh->num_points());
+ }
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/mesh/mesh_cleanup.h b/extern/draco/dracoenc/src/draco/mesh/mesh_cleanup.h
new file mode 100644
index 00000000000..b56129dce58
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/mesh_cleanup.h
@@ -0,0 +1,43 @@
+// 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_MESH_MESH_CLEANUP_H_
+#define DRACO_MESH_MESH_CLEANUP_H_
+
+#include "draco/mesh/mesh.h"
+
+namespace draco {
+
+// Options used by the MeshCleanup class.
+struct MeshCleanupOptions {
+ MeshCleanupOptions()
+ : remove_degenerated_faces(true), remove_unused_attributes(true) {}
+ // If true, the cleanup tool removes any face where two or more vertices
+ // share the same position index.
+ bool remove_degenerated_faces;
+ // If true, the cleanup tool removes any unused attribute value or unused
+ // point id. For example, it can be used to remove isolated vertices.
+ bool remove_unused_attributes;
+};
+
+// Tool that can be used for removing bad or unused data from draco::Meshes.
+class MeshCleanup {
+ public:
+ // Performs in-place cleanup of the input mesh according to the input options.
+ bool operator()(Mesh *mesh, const MeshCleanupOptions &options);
+};
+
+} // namespace draco
+
+#endif // DRACO_MESH_MESH_CLEANUP_H_
diff --git a/extern/draco/dracoenc/src/draco/mesh/mesh_cleanup_test.cc b/extern/draco/dracoenc/src/draco/mesh/mesh_cleanup_test.cc
new file mode 100644
index 00000000000..1051d193d87
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/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 "draco/mesh/mesh_cleanup.h"
+
+#include "draco/core/draco_test_base.h"
+#include "draco/core/vector_d.h"
+#include "draco/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> 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> 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(), 4)
+ << "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(), 3)
+ << "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> 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(), 5)
+ << "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(), 3)
+ << "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/extern/draco/dracoenc/src/draco/mesh/mesh_misc_functions.cc b/extern/draco/dracoenc/src/draco/mesh/mesh_misc_functions.cc
new file mode 100644
index 00000000000..b55527aa85e
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/mesh_misc_functions.cc
@@ -0,0 +1,58 @@
+// 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 "draco/mesh/mesh_misc_functions.h"
+
+namespace draco {
+
+std::unique_ptr<CornerTable> CreateCornerTableFromPositionAttribute(
+ const Mesh *mesh) {
+ typedef CornerTable::FaceType FaceType;
+
+ const PointAttribute *const att =
+ mesh->GetNamedAttribute(GeometryAttribute::POSITION);
+ if (att == nullptr)
+ return nullptr;
+ IndexTypeVector<FaceIndex, FaceType> faces(mesh->num_faces());
+ FaceType new_face;
+ for (FaceIndex i(0); i < mesh->num_faces(); ++i) {
+ const Mesh::Face &face = mesh->face(i);
+ for (int j = 0; j < 3; ++j) {
+ // Map general vertex indices to position indices.
+ new_face[j] = att->mapped_index(face[j]).value();
+ }
+ faces[FaceIndex(i)] = new_face;
+ }
+ // Build the corner table.
+ return CornerTable::Create(faces);
+}
+
+std::unique_ptr<CornerTable> CreateCornerTableFromAllAttributes(
+ const Mesh *mesh) {
+ typedef CornerTable::FaceType FaceType;
+ IndexTypeVector<FaceIndex, FaceType> faces(mesh->num_faces());
+ FaceType new_face;
+ for (FaceIndex i(0); i < mesh->num_faces(); ++i) {
+ const Mesh::Face &face = mesh->face(i);
+ // Each face is identified by point indices that automatically split the
+ // mesh along attribute seams.
+ for (int j = 0; j < 3; ++j) {
+ new_face[j] = face[j].value();
+ }
+ faces[i] = new_face;
+ }
+ // Build the corner table.
+ return CornerTable::Create(faces);
+}
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/mesh/mesh_misc_functions.h b/extern/draco/dracoenc/src/draco/mesh/mesh_misc_functions.h
new file mode 100644
index 00000000000..fc038bd7445
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/mesh_misc_functions.h
@@ -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.
+//
+// This file contains misc functions that are needed by several mesh related
+// algorithms.
+
+#ifndef DRACO_MESH_MESH_MISC_FUNCTIONS_H_
+#define DRACO_MESH_MESH_MISC_FUNCTIONS_H_
+
+#include "draco/mesh/corner_table.h"
+#include "draco/mesh/mesh.h"
+
+// The file contains functions that use both Mesh and CornerTable as inputs.
+namespace draco {
+
+// Creates a CornerTable from the position attribute of |mesh|. Returns nullptr
+// on error.
+std::unique_ptr<CornerTable> CreateCornerTableFromPositionAttribute(
+ const Mesh *mesh);
+
+// Creates a CornerTable from all attributes of |mesh|. Boundaries are
+// automatically introduced on all attribute seams. Returns nullptr on error.
+std::unique_ptr<CornerTable> CreateCornerTableFromAllAttributes(
+ const Mesh *mesh);
+
+// Returns true when the given corner lies opposite to an attribute seam.
+inline bool IsCornerOppositeToAttributeSeam(CornerIndex ci,
+ const PointAttribute &att,
+ const Mesh &mesh,
+ const CornerTable &ct) {
+ const CornerIndex opp_ci = ct.Opposite(ci);
+ if (opp_ci == kInvalidCornerIndex)
+ return false; // No opposite corner == no attribute seam.
+ // Compare attribute value indices on both ends of the opposite edge.
+ CornerIndex c0 = ct.Next(ci);
+ CornerIndex c1 = ct.Previous(opp_ci);
+ if (att.mapped_index(mesh.CornerToPointId(c0)) !=
+ att.mapped_index(mesh.CornerToPointId(c1)))
+ return true;
+ c0 = ct.Previous(ci);
+ c1 = ct.Next(opp_ci);
+ if (att.mapped_index(mesh.CornerToPointId(c0)) !=
+ att.mapped_index(mesh.CornerToPointId(c1)))
+ return true;
+ return false;
+}
+
+} // namespace draco
+
+#endif // DRACO_MESH_MESH_MISC_FUNCTIONS_H_
diff --git a/extern/draco/dracoenc/src/draco/mesh/mesh_stripifier.cc b/extern/draco/dracoenc/src/draco/mesh/mesh_stripifier.cc
new file mode 100644
index 00000000000..5b63da3023d
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/mesh_stripifier.cc
@@ -0,0 +1,99 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/mesh/mesh_stripifier.h"
+
+namespace draco {
+
+void MeshStripifier::GenerateStripsFromCorner(int local_strip_id,
+ CornerIndex ci) {
+ // Clear the storage for strip faces.
+ strip_faces_[local_strip_id].clear();
+ // Start corner of the strip (where the strip starts).
+ CornerIndex start_ci = ci;
+ FaceIndex fi = corner_table_->Face(ci);
+ // We need to grow the strip both forward and backward (2 passes).
+ // Note that the backward pass can change the start corner of the strip (the
+ // start corner is going to be moved to the end of the backward strip).
+ for (int pass = 0; pass < 2; ++pass) {
+ if (pass == 1) {
+ // Backward pass.
+ // Start the traversal from the B that is the left sibling of the next
+ // corner to the start corner C = |start_ci|.
+ //
+ // *-------*-------*-------*
+ // / \ / \C / \ /
+ // / \ / \ / \ /
+ // / \ / B\ / \ /
+ // *-------*-------*-------*
+ //
+ // Perform the backward pass only when there is no attribute seam between
+ // the initial face and the first face of the backward traversal.
+ if (GetOppositeCorner(corner_table_->Previous(start_ci)) ==
+ kInvalidCornerIndex)
+ break; // Attribute seam or a boundary.
+
+ ci = corner_table_->Next(start_ci);
+ ci = corner_table_->SwingLeft(ci);
+ if (ci == kInvalidCornerIndex)
+ break;
+
+ fi = corner_table_->Face(ci);
+ }
+ int num_added_faces = 0;
+ while (!is_face_visited_[fi]) {
+ is_face_visited_[fi] = true;
+ strip_faces_[local_strip_id].push_back(fi);
+ ++num_added_faces;
+ if (num_added_faces > 1) {
+ // Move to the correct source corner to traverse to the next face.
+ if (num_added_faces & 1) {
+ // Odd number of faces added.
+ ci = corner_table_->Next(ci);
+ } else {
+ // Even number of faces added.
+ if (pass == 1) {
+ // If we are processing the backward pass, update the start corner
+ // of the strip on every even face reached (we cannot use odd faces
+ // for start of the strip as the strips would start in a wrong
+ // direction).
+ start_ci = ci;
+ }
+ ci = corner_table_->Previous(ci);
+ }
+ }
+ ci = GetOppositeCorner(ci);
+ if (ci == kInvalidCornerIndex)
+ break;
+ fi = corner_table_->Face(ci);
+ }
+ // Strip end reached.
+ if (pass == 1 && (num_added_faces & 1)) {
+ // If we processed the backward strip and we add an odd number of faces to
+ // the strip, we need to remove the last one as it cannot be used to start
+ // the strip (the strip would start in a wrong direction from that face).
+ is_face_visited_[strip_faces_[local_strip_id].back()] = false;
+ strip_faces_[local_strip_id].pop_back();
+ }
+ }
+ strip_start_corners_[local_strip_id] = start_ci;
+
+ // Reset all visited flags for all faces (we need to process other strips from
+ // the given face before we choose the final strip that we are going to use).
+ for (int i = 0; i < strip_faces_[local_strip_id].size(); ++i) {
+ is_face_visited_[strip_faces_[local_strip_id][i]] = false;
+ }
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/mesh/mesh_stripifier.h b/extern/draco/dracoenc/src/draco/mesh/mesh_stripifier.h
new file mode 100644
index 00000000000..f7c1ead4c44
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/mesh_stripifier.h
@@ -0,0 +1,252 @@
+// Copyright 2017 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_SRC_DRACO_MESH_MESH_STRIPIFIER_H_
+#define DRACO_SRC_DRACO_MESH_MESH_STRIPIFIER_H_
+
+#include "draco/mesh/mesh_misc_functions.h"
+
+namespace draco {
+
+// Class that generates triangle strips from a provided draco::Mesh data
+// structure. The strips represent a more memory efficient storage of triangle
+// connectivity that can be used directly on the GPU (see
+// https://en.wikipedia.org/wiki/Triangle_strip ). In general, a mesh needs to
+// be represented by several triangle strips and it has been proven that finding
+// the optimal set of triangle strips is an NP-complete problem. The algorithm
+// implemented by this class finds this set of triangle strips based on a greedy
+// heuristic that always selects the longest available strip that covers the
+// next unprocessed face. The longest strip is found by analyzing all strips
+// that can cover the given face (three strips corresponding to three
+// directions).
+class MeshStripifier {
+ public:
+ MeshStripifier()
+ : mesh_(nullptr),
+ num_strips_(0),
+ num_encoded_faces_(0),
+ last_encoded_point_(kInvalidPointIndex) {}
+
+ // Generate triangle strips for a given mesh and output them to the output
+ // iterator |out_it|. In most cases |out_it| stores the values in a buffer
+ // that can be used directly on the GPU. Note that the algorithm can generate
+ // multiple strips to represent the whole mesh. In such cases multiple strips
+ // are separated using a so called primitive restart index that is specified
+ // by the |primitive_restart_index| (usually defined as the maximum allowed
+ // value for the given type).
+ // https://www.khronos.org/opengl/wiki/Vertex_Rendering#Primitive_Restart
+ template <typename OutputIteratorT, typename IndexTypeT>
+ bool GenerateTriangleStripsWithPrimitiveRestart(
+ const Mesh &mesh, IndexTypeT primitive_restart_index,
+ OutputIteratorT out_it);
+
+ // The same as above but disjoint triangle strips are separated by degenerate
+ // triangles instead of the primitive restart index. Degenerate triangles are
+ // zero area triangles that are automatically discarded by the GPU. Using
+ // degenerate triangles usually results in a slightly longer output indices
+ // array compared to the similar triangle strips that use primitive restart
+ // index. The advantage of this method is that it is supported by all hardware
+ // and all relevant APIs (including WebGL 1.0).
+ template <typename OutputIteratorT>
+ bool GenerateTriangleStripsWithDegenerateTriangles(const Mesh &mesh,
+ OutputIteratorT out_it);
+
+ // Returns the number of strips generated by the last call of the
+ // GenerateTriangleStrips() method.
+ int num_strips() const { return num_strips_; }
+
+ private:
+ bool Prepare(const Mesh &mesh) {
+ mesh_ = &mesh;
+ num_strips_ = 0;
+ num_encoded_faces_ = 0;
+ // TODO(ostava): We may be able to avoid computing the corner table if we
+ // already have it stored somewhere.
+ corner_table_ = CreateCornerTableFromPositionAttribute(mesh_);
+ if (corner_table_ == nullptr)
+ return false;
+
+ // Mark all faces as unvisited.
+ is_face_visited_.assign(mesh.num_faces(), false);
+ return true;
+ }
+
+ // Returns local id of the longest strip that can be created from the given
+ // face |fi|.
+ int FindLongestStripFromFace(FaceIndex fi) {
+ // There are three possible strip directions that can contain the provided
+ // input face. We try all of them and select the direction that result in
+ // the longest strip.
+ const CornerIndex first_ci = corner_table_->FirstCorner(fi);
+ int longest_strip_id = -1;
+ int longest_strip_length = 0;
+ for (int i = 0; i < 3; ++i) {
+ GenerateStripsFromCorner(i, first_ci + i);
+ if (strip_faces_[i].size() > longest_strip_length) {
+ longest_strip_length = static_cast<int>(strip_faces_[i].size());
+ longest_strip_id = i;
+ }
+ }
+ return longest_strip_id;
+ }
+
+ // Generates strip from the data stored in |strip_faces_| and
+ // |strip_start_start_corners_| and stores it to |out_it|.
+ template <typename OutputIteratorT>
+ void StoreStrip(int local_strip_id, OutputIteratorT out_it) {
+ ++num_strips_;
+
+ const int num_strip_faces = strip_faces_[local_strip_id].size();
+ CornerIndex ci = strip_start_corners_[local_strip_id];
+ for (int i = 0; i < num_strip_faces; ++i) {
+ const FaceIndex fi = corner_table_->Face(ci);
+ is_face_visited_[fi] = true;
+ ++num_encoded_faces_;
+
+ if (i == 0) {
+ // Add the start face (three indices).
+ *out_it++ = CornerToPointIndex(ci).value();
+ *out_it++ = CornerToPointIndex(corner_table_->Next(ci)).value();
+ last_encoded_point_ = CornerToPointIndex(corner_table_->Previous(ci));
+ *out_it++ = last_encoded_point_.value();
+ } else {
+ // Store the point on the newly reached corner.
+ last_encoded_point_ = CornerToPointIndex(ci);
+ *out_it++ = last_encoded_point_.value();
+
+ // Go to the correct source corner to proceed to the next face.
+ if (i & 1) {
+ ci = corner_table_->Previous(ci);
+ } else {
+ ci = corner_table_->Next(ci);
+ }
+ }
+ ci = corner_table_->Opposite(ci);
+ }
+ }
+
+ PointIndex CornerToPointIndex(CornerIndex ci) const {
+ return mesh_->CornerToPointId(ci);
+ }
+
+ // Returns the opposite corner in case the opposite triangle does not lie
+ // across an attribute seam. Otherwise return kInvalidCornerIndex.
+ CornerIndex GetOppositeCorner(CornerIndex ci) const {
+ const CornerIndex oci = corner_table_->Opposite(ci);
+ if (oci < 0)
+ return kInvalidCornerIndex;
+ // Ensure the point ids are same on both sides of the shared edge between
+ // the triangles.
+ if (CornerToPointIndex(corner_table_->Next(ci)) !=
+ CornerToPointIndex(corner_table_->Previous(oci)))
+ return kInvalidCornerIndex;
+ if (CornerToPointIndex(corner_table_->Previous(ci)) !=
+ CornerToPointIndex(corner_table_->Next(oci)))
+ return kInvalidCornerIndex;
+ return oci;
+ }
+
+ void GenerateStripsFromCorner(int local_strip_id, CornerIndex ci);
+
+ const Mesh *mesh_;
+ std::unique_ptr<CornerTable> corner_table_;
+
+ // Store strip faces for each of three possible directions from a given face.
+ std::vector<FaceIndex> strip_faces_[3];
+ // Start corner for each direction of the strip containing the processed face.
+ CornerIndex strip_start_corners_[3];
+ IndexTypeVector<FaceIndex, bool> is_face_visited_;
+ // The number of strips generated by this method.
+ int num_strips_;
+ // The number of encoded triangles.
+ int num_encoded_faces_;
+ // Last encoded point.
+ PointIndex last_encoded_point_;
+};
+
+template <typename OutputIteratorT, typename IndexTypeT>
+bool MeshStripifier::GenerateTriangleStripsWithPrimitiveRestart(
+ const Mesh &mesh, IndexTypeT primitive_restart_index,
+ OutputIteratorT out_it) {
+ if (!Prepare(mesh))
+ return false;
+
+ // Go over all faces and generate strips from the first unvisited one.
+ for (FaceIndex fi(0); fi < mesh.num_faces(); ++fi) {
+ if (is_face_visited_[fi])
+ continue;
+
+ const int longest_strip_id = FindLongestStripFromFace(fi);
+
+ // Separate triangle strips with the primitive restart index.
+ if (num_strips_ > 0) {
+ *out_it++ = primitive_restart_index;
+ }
+
+ StoreStrip(longest_strip_id, out_it);
+ }
+
+ return true;
+}
+
+template <typename OutputIteratorT>
+bool MeshStripifier::GenerateTriangleStripsWithDegenerateTriangles(
+ const Mesh &mesh, OutputIteratorT out_it) {
+ if (!Prepare(mesh))
+ return false;
+
+ // Go over all faces and generate strips from the first unvisited one.
+ for (FaceIndex fi(0); fi < mesh.num_faces(); ++fi) {
+ if (is_face_visited_[fi])
+ continue;
+
+ const int longest_strip_id = FindLongestStripFromFace(fi);
+
+ // Separate triangle strips by degenerate triangles. There will be either
+ // three or four degenerate triangles inserted based on the number of
+ // triangles that are already encoded in the output strip (three degenerate
+ // triangles for even number of existing triangles, four degenerate
+ // triangles for odd number of triangles).
+ if (num_strips_ > 0) {
+ // Duplicate last encoded index (first degenerate face).
+ *out_it++ = last_encoded_point_.value();
+
+ // Connect it to the start point of the new triangle strip (second
+ // degenerate face).
+ const CornerIndex new_start_corner =
+ strip_start_corners_[longest_strip_id];
+ const PointIndex new_start_point = CornerToPointIndex(new_start_corner);
+ *out_it++ = new_start_point.value();
+ num_encoded_faces_ += 2;
+ // If we have previously encoded number of faces we need to duplicate the
+ // point one more time to preserve the correct orientation of the next
+ // strip.
+ if (num_encoded_faces_ & 1) {
+ *out_it++ = new_start_point.value();
+ num_encoded_faces_ += 1;
+ }
+ // The last degenerate face will be added implicitly in the StoreStrip()
+ // function below as the first point index is going to be encoded there
+ // again.
+ }
+
+ StoreStrip(longest_strip_id, out_it);
+ }
+
+ return true;
+}
+
+} // namespace draco
+
+#endif // DRACO_SRC_DRACO_MESH_MESH_STRIPIFIER_H_
diff --git a/extern/draco/dracoenc/src/draco/mesh/triangle_soup_mesh_builder.cc b/extern/draco/dracoenc/src/draco/mesh/triangle_soup_mesh_builder.cc
new file mode 100644
index 00000000000..a21d5e77cf7
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/triangle_soup_mesh_builder.cc
@@ -0,0 +1,85 @@
+// 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 "draco/mesh/triangle_soup_mesh_builder.h"
+
+namespace draco {
+
+void TriangleSoupMeshBuilder::Start(int num_faces) {
+ mesh_ = std::unique_ptr<Mesh>(new Mesh());
+ mesh_->SetNumFaces(num_faces);
+ mesh_->set_num_points(num_faces * 3);
+ attribute_element_types_.clear();
+}
+
+int TriangleSoupMeshBuilder::AddAttribute(
+ GeometryAttribute::Type attribute_type, int8_t num_components,
+ DataType data_type) {
+ GeometryAttribute va;
+ va.Init(attribute_type, nullptr, num_components, data_type, false,
+ DataTypeLength(data_type) * num_components, 0);
+ attribute_element_types_.push_back(-1);
+ return mesh_->AddAttribute(va, true, mesh_->num_points());
+}
+
+void TriangleSoupMeshBuilder::SetAttributeValuesForFace(
+ int att_id, FaceIndex face_id, const void *corner_value_0,
+ const void *corner_value_1, const void *corner_value_2) {
+ const int start_index = 3 * face_id.value();
+ PointAttribute *const att = mesh_->attribute(att_id);
+ att->SetAttributeValue(AttributeValueIndex(start_index), corner_value_0);
+ att->SetAttributeValue(AttributeValueIndex(start_index + 1), corner_value_1);
+ att->SetAttributeValue(AttributeValueIndex(start_index + 2), corner_value_2);
+ // TODO(ostava): The below code should be called only for one attribute.
+ // It will work OK even for multiple attributes, but it's redundant.
+ mesh_->SetFace(face_id,
+ {{PointIndex(start_index), PointIndex(start_index + 1),
+ PointIndex(start_index + 2)}});
+ attribute_element_types_[att_id] = MESH_CORNER_ATTRIBUTE;
+}
+
+void TriangleSoupMeshBuilder::SetPerFaceAttributeValueForFace(
+ int att_id, FaceIndex face_id, const void *value) {
+ const int start_index = 3 * face_id.value();
+ PointAttribute *const att = mesh_->attribute(att_id);
+ att->SetAttributeValue(AttributeValueIndex(start_index), value);
+ att->SetAttributeValue(AttributeValueIndex(start_index + 1), value);
+ att->SetAttributeValue(AttributeValueIndex(start_index + 2), value);
+ mesh_->SetFace(face_id,
+ {{PointIndex(start_index), PointIndex(start_index + 1),
+ PointIndex(start_index + 2)}});
+ int8_t &element_type = attribute_element_types_[att_id];
+ if (element_type < 0)
+ element_type = MESH_FACE_ATTRIBUTE;
+}
+
+std::unique_ptr<Mesh> TriangleSoupMeshBuilder::Finalize() {
+#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
+ // First deduplicate attribute values.
+ if (!mesh_->DeduplicateAttributeValues())
+ return nullptr;
+ // Also deduplicate vertex indices.
+ mesh_->DeduplicatePointIds();
+#endif
+ for (size_t i = 0; i < attribute_element_types_.size(); ++i) {
+ if (attribute_element_types_[i] >= 0) {
+ mesh_->SetAttributeElementType(
+ static_cast<int>(i),
+ static_cast<MeshAttributeElementType>(attribute_element_types_[i]));
+ }
+ }
+ return std::move(mesh_);
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/mesh/triangle_soup_mesh_builder.h b/extern/draco/dracoenc/src/draco/mesh/triangle_soup_mesh_builder.h
new file mode 100644
index 00000000000..3ae652cfeee
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/triangle_soup_mesh_builder.h
@@ -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.
+//
+#ifndef DRACO_MESH_TRIANGLE_SOUP_MESH_BUILDER_H_
+#define DRACO_MESH_TRIANGLE_SOUP_MESH_BUILDER_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/mesh/mesh.h"
+
+namespace draco {
+
+// Class for building meshes directly from attribute values that can be
+// specified for each face corner. All attributes are automatically
+// deduplicated.
+class TriangleSoupMeshBuilder {
+ public:
+ // Starts mesh building for a given number of faces.
+ // TODO(ostava): Currently it's necessary to select the correct number of
+ // faces upfront. This should be generalized, but it will require us to
+ // rewrite our attribute resizing functions.
+ void Start(int num_faces);
+
+ // Adds an empty attribute to the mesh. Returns the new attribute's id.
+ int AddAttribute(GeometryAttribute::Type attribute_type,
+ int8_t num_components, DataType data_type);
+
+ // Sets values for a given attribute on all corners of a given face.
+ void SetAttributeValuesForFace(int att_id, FaceIndex face_id,
+ const void *corner_value_0,
+ const void *corner_value_1,
+ const void *corner_value_2);
+
+ // Sets value for a per-face attribute. If all faces of a given attribute are
+ // set with this method, the attribute will be marked as per-face, otherwise
+ // it will be marked as per-corner attribute.
+ void SetPerFaceAttributeValueForFace(int att_id, FaceIndex face_id,
+ const void *value);
+
+ // Finalizes the mesh or returns nullptr on error.
+ // Once this function is called, the builder becomes invalid and cannot be
+ // used until the method Start() is called again.
+ std::unique_ptr<Mesh> Finalize();
+
+ private:
+ std::vector<int8_t> attribute_element_types_;
+
+ std::unique_ptr<Mesh> mesh_;
+};
+
+} // namespace draco
+
+#endif // DRACO_MESH_TRIANGLE_SOUP_MESH_BUILDER_H_
diff --git a/extern/draco/dracoenc/src/draco/mesh/triangle_soup_mesh_builder_test.cc b/extern/draco/dracoenc/src/draco/mesh/triangle_soup_mesh_builder_test.cc
new file mode 100644
index 00000000000..171f8fe24a1
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/triangle_soup_mesh_builder_test.cc
@@ -0,0 +1,197 @@
+// 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 "draco/mesh/triangle_soup_mesh_builder.h"
+
+#include "draco/core/draco_test_base.h"
+#include "draco/core/vector_d.h"
+
+namespace draco {
+
+class TriangleSoupMeshBuilderTest : public ::testing::Test {};
+
+TEST_F(TriangleSoupMeshBuilderTest, CubeTest) {
+ // This tests, verifies that the mesh builder constructs a valid cube out
+ // of the provided triangle soup data.
+ TriangleSoupMeshBuilder mb;
+ mb.Start(12);
+ const int pos_att_id =
+ mb.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32);
+ // clang-format off
+ // Front face.
+ 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, 1.f, 0.f).data());
+
+ // Back face.
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(2),
+ Vector3f(0.f, 1.f, 1.f).data(),
+ Vector3f(1.f, 0.f, 1.f).data(),
+ Vector3f(0.f, 0.f, 1.f).data());
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(3),
+ Vector3f(1.f, 1.f, 1.f).data(),
+ Vector3f(1.f, 0.f, 1.f).data(),
+ Vector3f(0.f, 1.f, 1.f).data());
+
+ // Top face.
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(4),
+ Vector3f(0.f, 1.f, 0.f).data(),
+ Vector3f(1.f, 1.f, 0.f).data(),
+ Vector3f(0.f, 1.f, 1.f).data());
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(5),
+ Vector3f(0.f, 1.f, 1.f).data(),
+ Vector3f(1.f, 1.f, 0.f).data(),
+ Vector3f(1.f, 1.f, 1.f).data());
+
+ // Bottom face.
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(6),
+ Vector3f(0.f, 0.f, 1.f).data(),
+ Vector3f(1.f, 0.f, 0.f).data(),
+ Vector3f(0.f, 0.f, 0.f).data());
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(7),
+ Vector3f(1.f, 0.f, 1.f).data(),
+ Vector3f(1.f, 0.f, 0.f).data(),
+ Vector3f(0.f, 0.f, 1.f).data());
+
+ // Right face.
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(8),
+ Vector3f(1.f, 0.f, 0.f).data(),
+ Vector3f(1.f, 0.f, 1.f).data(),
+ Vector3f(1.f, 1.f, 0.f).data());
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(9),
+ Vector3f(1.f, 1.f, 0.f).data(),
+ Vector3f(1.f, 0.f, 1.f).data(),
+ Vector3f(1.f, 1.f, 1.f).data());
+
+ // Left face.
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(10),
+ Vector3f(0.f, 1.f, 0.f).data(),
+ Vector3f(0.f, 0.f, 1.f).data(),
+ Vector3f(0.f, 0.f, 0.f).data());
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(11),
+ Vector3f(0.f, 1.f, 1.f).data(),
+ Vector3f(0.f, 0.f, 1.f).data(),
+ Vector3f(0.f, 1.f, 0.f).data());
+ // clang-format on
+
+ std::unique_ptr<Mesh> mesh = mb.Finalize();
+ ASSERT_NE(mesh, nullptr) << "Failed to build the cube mesh.";
+ EXPECT_EQ(mesh->num_points(), 8) << "Unexpected number of vertices.";
+ EXPECT_EQ(mesh->num_faces(), 12) << "Unexpected number of faces.";
+}
+
+TEST_F(TriangleSoupMeshBuilderTest, TestPerFaceAttribs) {
+ // This tests, verifies that the mesh builder constructs a valid cube with
+ // per face Boolean attributes.
+ TriangleSoupMeshBuilder mb;
+ mb.Start(12);
+ const int pos_att_id =
+ mb.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32);
+ const int gen_att_id =
+ mb.AddAttribute(GeometryAttribute::GENERIC, 1, DT_BOOL);
+ uint8_t bool_true = 1;
+ uint8_t bool_false = 0;
+ // clang-format off
+ // Front face.
+ 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.SetPerFaceAttributeValueForFace(gen_att_id, FaceIndex(0), &bool_false);
+
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(1),
+ Vector3f(0.f, 1.f, 0.f).data(),
+ Vector3f(1.f, 0.f, 0.f).data(),
+ Vector3f(1.f, 1.f, 0.f).data());
+ mb.SetPerFaceAttributeValueForFace(gen_att_id, FaceIndex(1), &bool_true);
+
+ // Back face.
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(2),
+ Vector3f(0.f, 1.f, 1.f).data(),
+ Vector3f(1.f, 0.f, 1.f).data(),
+ Vector3f(0.f, 0.f, 1.f).data());
+ mb.SetPerFaceAttributeValueForFace(gen_att_id, FaceIndex(2), &bool_true);
+
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(3),
+ Vector3f(1.f, 1.f, 1.f).data(),
+ Vector3f(1.f, 0.f, 1.f).data(),
+ Vector3f(0.f, 1.f, 1.f).data());
+ mb.SetPerFaceAttributeValueForFace(gen_att_id, FaceIndex(3), &bool_true);
+
+ // Top face.
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(4),
+ Vector3f(0.f, 1.f, 0.f).data(),
+ Vector3f(1.f, 1.f, 0.f).data(),
+ Vector3f(0.f, 1.f, 1.f).data());
+ mb.SetPerFaceAttributeValueForFace(gen_att_id, FaceIndex(4), &bool_false);;
+
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(5),
+ Vector3f(0.f, 1.f, 1.f).data(),
+ Vector3f(1.f, 1.f, 0.f).data(),
+ Vector3f(1.f, 1.f, 1.f).data());
+ mb.SetPerFaceAttributeValueForFace(gen_att_id, FaceIndex(5), &bool_false);
+
+ // Bottom face.
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(6),
+ Vector3f(0.f, 0.f, 1.f).data(),
+ Vector3f(1.f, 0.f, 0.f).data(),
+ Vector3f(0.f, 0.f, 0.f).data());
+ mb.SetPerFaceAttributeValueForFace(gen_att_id, FaceIndex(6), &bool_true);
+
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(7),
+ Vector3f(1.f, 0.f, 1.f).data(),
+ Vector3f(1.f, 0.f, 0.f).data(),
+ Vector3f(0.f, 0.f, 1.f).data());
+ mb.SetPerFaceAttributeValueForFace(gen_att_id, FaceIndex(7), &bool_true);
+
+ // Right face.
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(8),
+ Vector3f(1.f, 0.f, 0.f).data(),
+ Vector3f(1.f, 0.f, 1.f).data(),
+ Vector3f(1.f, 1.f, 0.f).data());
+ mb.SetPerFaceAttributeValueForFace(gen_att_id, FaceIndex(8), &bool_false);
+
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(9),
+ Vector3f(1.f, 1.f, 0.f).data(),
+ Vector3f(1.f, 0.f, 1.f).data(),
+ Vector3f(1.f, 1.f, 1.f).data());
+ mb.SetPerFaceAttributeValueForFace(gen_att_id, FaceIndex(9), &bool_true);
+
+ // Left face.
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(10),
+ Vector3f(0.f, 1.f, 0.f).data(),
+ Vector3f(0.f, 0.f, 1.f).data(),
+ Vector3f(0.f, 0.f, 0.f).data());
+ mb.SetPerFaceAttributeValueForFace(gen_att_id, FaceIndex(10), &bool_true);
+
+ mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(11),
+ Vector3f(0.f, 1.f, 1.f).data(),
+ Vector3f(0.f, 0.f, 1.f).data(),
+ Vector3f(0.f, 1.f, 0.f).data());
+ mb.SetPerFaceAttributeValueForFace(gen_att_id, FaceIndex(11), &bool_false);
+ // clang-format on
+
+ std::unique_ptr<Mesh> mesh = mb.Finalize();
+ ASSERT_NE(mesh, nullptr) << "Failed to build the cube mesh.";
+ EXPECT_EQ(mesh->num_faces(), 12) << "Unexpected number of faces.";
+ EXPECT_EQ(mesh->GetAttributeElementType(gen_att_id), MESH_FACE_ATTRIBUTE)
+ << "Unexpected attribute element type.";
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/mesh/valence_cache.h b/extern/draco/dracoenc/src/draco/mesh/valence_cache.h
new file mode 100644
index 00000000000..f75d66f4b99
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/mesh/valence_cache.h
@@ -0,0 +1,136 @@
+// 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_MESH_VALENCE_CACHE_H_
+#define DRACO_MESH_VALENCE_CACHE_H_
+
+#include "draco/attributes/geometry_indices.h"
+#include "draco/core/draco_index_type_vector.h"
+#include "draco/core/macros.h"
+
+namespace draco {
+
+// ValenceCache provides support for the caching of valences off of some kind of
+// CornerTable 'type' of class.
+// No valences should be queried before Caching is
+// performed and values should be removed/recached when changes to the
+// underlying mesh are taking place.
+
+template <class CornerTableT>
+class ValenceCache {
+ const CornerTableT &table_;
+
+ public:
+ explicit ValenceCache(const CornerTableT &table) : table_(table) {}
+
+ // Do not call before CacheValences() / CacheValencesInaccurate().
+ inline int8_t ValenceFromCacheInaccurate(CornerIndex c) const {
+ if (c == kInvalidCornerIndex)
+ return -1;
+ return ValenceFromCacheInaccurate(table_.Vertex(c));
+ }
+ inline int32_t ValenceFromCache(CornerIndex c) const {
+ if (c == kInvalidCornerIndex)
+ return -1;
+ return ValenceFromCache(table_.Vertex(c));
+ }
+
+ inline int32_t ConfidentValenceFromCache(VertexIndex v) const {
+ DRACO_DCHECK_LT(v.value(), table_.num_vertices());
+ DRACO_DCHECK_EQ(vertex_valence_cache_32_bit_.size(), table_.num_vertices());
+ return vertex_valence_cache_32_bit_[v];
+ }
+
+ // Collect the valence for all vertices so they can be reused later. The
+ // 'inaccurate' versions of this family of functions clips the true valence
+ // of the vertices to 8 signed bits as a space optimization. This clipping
+ // will lead to occasionally wrong results. If accurate results are required
+ // under all circumstances, do not use the 'inaccurate' version or else
+ // use it and fetch the correct result in the event the value appears clipped.
+ // The topology of the mesh should be a constant when Valence Cache functions
+ // are being used. Modification of the mesh while cache(s) are filled will
+ // not guarantee proper results on subsequent calls unless they are rebuilt.
+ void CacheValencesInaccurate() const {
+ if (vertex_valence_cache_8_bit_.size() == 0) {
+ const VertexIndex vertex_count = VertexIndex(table_.num_vertices());
+ vertex_valence_cache_8_bit_.resize(vertex_count.value());
+ for (VertexIndex v = VertexIndex(0); v < vertex_count; v += 1)
+ vertex_valence_cache_8_bit_[v] = static_cast<int8_t>(
+ (std::min)(static_cast<int32_t>(std::numeric_limits<int8_t>::max()),
+ table_.Valence(v)));
+ }
+ }
+ void CacheValences() const {
+ if (vertex_valence_cache_32_bit_.size() == 0) {
+ const VertexIndex vertex_count = VertexIndex(table_.num_vertices());
+ vertex_valence_cache_32_bit_.resize(vertex_count.value());
+ for (VertexIndex v = VertexIndex(0); v < vertex_count; v += 1)
+ vertex_valence_cache_32_bit_[v] = table_.Valence(v);
+ }
+ }
+
+ inline int8_t ConfidentValenceFromCacheInaccurate(CornerIndex c) const {
+ DRACO_DCHECK_GE(c.value(), 0);
+ return ConfidentValenceFromCacheInaccurate(table_.ConfidentVertex(c));
+ }
+ inline int32_t ConfidentValenceFromCache(CornerIndex c) const {
+ DRACO_DCHECK_GE(c.value(), 0);
+ return ConfidentValenceFromCache(table_.ConfidentVertex(c));
+ }
+ inline int8_t ValenceFromCacheInaccurate(VertexIndex v) const {
+ DRACO_DCHECK_EQ(vertex_valence_cache_8_bit_.size(), table_.num_vertices());
+ if (v == kInvalidVertexIndex || v.value() >= table_.num_vertices())
+ return -1;
+ return ConfidentValenceFromCacheInaccurate(v);
+ }
+ inline int8_t ConfidentValenceFromCacheInaccurate(VertexIndex v) const {
+ DRACO_DCHECK_LT(v.value(), table_.num_vertices());
+ DRACO_DCHECK_EQ(vertex_valence_cache_8_bit_.size(), table_.num_vertices());
+ return vertex_valence_cache_8_bit_[v];
+ }
+
+ // TODO(draco-eng) Add unit tests for ValenceCache functions.
+ inline int32_t ValenceFromCache(VertexIndex v) const {
+ DRACO_DCHECK_EQ(vertex_valence_cache_32_bit_.size(), table_.num_vertices());
+ if (v == kInvalidVertexIndex || v.value() >= table_.num_vertices())
+ return -1;
+ return ConfidentValenceFromCache(v);
+ }
+
+ // Clear the cache of valences and deallocate the memory.
+ void ClearValenceCacheInaccurate() const {
+ vertex_valence_cache_8_bit_.clear();
+ // Force erasure.
+ IndexTypeVector<VertexIndex, int8_t>().swap(vertex_valence_cache_8_bit_);
+ }
+ void ClearValenceCache() const {
+ vertex_valence_cache_32_bit_.clear();
+ // Force erasure.
+ IndexTypeVector<VertexIndex, int32_t>().swap(vertex_valence_cache_32_bit_);
+ }
+
+ bool IsCacheEmpty() const {
+ return vertex_valence_cache_8_bit_.size() == 0 &&
+ vertex_valence_cache_32_bit_.size() == 0;
+ }
+
+ private:
+ // Retain valences and clip them to char size.
+ mutable IndexTypeVector<VertexIndex, int8_t> vertex_valence_cache_8_bit_;
+ mutable IndexTypeVector<VertexIndex, int32_t> vertex_valence_cache_32_bit_;
+};
+
+} // namespace draco
+
+#endif // DRACO_MESH_VALENCE_CACHE_H_
diff --git a/extern/draco/dracoenc/src/draco/metadata/geometry_metadata.cc b/extern/draco/dracoenc/src/draco/metadata/geometry_metadata.cc
new file mode 100644
index 00000000000..896663ae8ab
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/metadata/geometry_metadata.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/metadata/geometry_metadata.h"
+#include <utility>
+
+namespace draco {
+
+const AttributeMetadata *GeometryMetadata::GetAttributeMetadataByStringEntry(
+ const std::string &entry_name, const std::string &entry_value) const {
+ for (auto &&att_metadata : att_metadatas_) {
+ std::string value;
+ if (!att_metadata->GetEntryString(entry_name, &value))
+ continue;
+ if (value == entry_value)
+ return att_metadata.get();
+ }
+ // No attribute has the requested entry.
+ return nullptr;
+}
+
+bool GeometryMetadata::AddAttributeMetadata(
+ std::unique_ptr<AttributeMetadata> att_metadata) {
+ if (!att_metadata.get())
+ return false;
+ att_metadatas_.push_back(std::move(att_metadata));
+ return true;
+}
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/metadata/geometry_metadata.h b/extern/draco/dracoenc/src/draco/metadata/geometry_metadata.h
new file mode 100644
index 00000000000..9f668f7fa15
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/metadata/geometry_metadata.h
@@ -0,0 +1,126 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#ifndef DRACO_METADATA_GEOMETRY_METADATA_H_
+#define DRACO_METADATA_GEOMETRY_METADATA_H_
+
+#include "draco/metadata/metadata.h"
+
+namespace draco {
+
+// Class for representing specifically metadata of attributes. It must have an
+// attribute id which should be identical to it's counterpart attribute in
+// the point cloud it belongs to.
+class AttributeMetadata : public Metadata {
+ public:
+ AttributeMetadata() : att_unique_id_(0) {}
+ explicit AttributeMetadata(const Metadata &metadata)
+ : Metadata(metadata), att_unique_id_(0) {}
+
+ void set_att_unique_id(uint32_t att_unique_id) {
+ att_unique_id_ = att_unique_id;
+ }
+ // The unique id of the attribute that this metadata belongs to.
+ uint32_t att_unique_id() const { return att_unique_id_; }
+
+ private:
+ uint32_t att_unique_id_;
+
+ friend struct AttributeMetadataHasher;
+ friend class PointCloud;
+};
+
+// Functor for computing a hash from data stored in a AttributeMetadata class.
+struct AttributeMetadataHasher {
+ size_t operator()(const AttributeMetadata &metadata) const {
+ size_t hash = metadata.att_unique_id_;
+ MetadataHasher metadata_hasher;
+ hash = HashCombine(metadata_hasher(static_cast<const Metadata &>(metadata)),
+ hash);
+ return hash;
+ }
+};
+// Class for representing the metadata for a point cloud. It could have a list
+// of attribute metadata.
+class GeometryMetadata : public Metadata {
+ public:
+ GeometryMetadata(){};
+ explicit GeometryMetadata(const Metadata &metadata) : Metadata(metadata) {}
+
+ const AttributeMetadata *GetAttributeMetadataByStringEntry(
+ const std::string &entry_name, const std::string &entry_value) const;
+ bool AddAttributeMetadata(std::unique_ptr<AttributeMetadata> att_metadata);
+
+ void DeleteAttributeMetadataByUniqueId(int32_t att_unique_id) {
+ for (auto itr = att_metadatas_.begin(); itr != att_metadatas_.end();
+ ++itr) {
+ if (itr->get()->att_unique_id() == att_unique_id) {
+ att_metadatas_.erase(itr);
+ return;
+ }
+ }
+ }
+
+ const AttributeMetadata *GetAttributeMetadataByUniqueId(
+ int32_t att_unique_id) const {
+ // TODO(draco-eng): Consider using unordered_map instead of vector to store
+ // attribute metadata.
+ for (auto &&att_metadata : att_metadatas_) {
+ if (att_metadata->att_unique_id() == att_unique_id) {
+ return att_metadata.get();
+ }
+ }
+ return nullptr;
+ }
+
+ AttributeMetadata *attribute_metadata(int32_t att_unique_id) {
+ // TODO(draco-eng): Consider use unordered_map instead of vector to store
+ // attribute metadata.
+ for (auto &&att_metadata : att_metadatas_) {
+ if (att_metadata->att_unique_id() == att_unique_id) {
+ return att_metadata.get();
+ }
+ }
+ return nullptr;
+ }
+
+ const std::vector<std::unique_ptr<AttributeMetadata>> &attribute_metadatas()
+ const {
+ return att_metadatas_;
+ }
+
+ private:
+ std::vector<std::unique_ptr<AttributeMetadata>> att_metadatas_;
+
+ friend struct GeometryMetadataHasher;
+};
+
+// Functor for computing a hash from data stored in a GeometryMetadata class.
+struct GeometryMetadataHasher {
+ size_t operator()(const GeometryMetadata &metadata) const {
+ size_t hash = metadata.att_metadatas_.size();
+ AttributeMetadataHasher att_metadata_hasher;
+ for (auto &&att_metadata : metadata.att_metadatas_) {
+ hash = HashCombine(att_metadata_hasher(*att_metadata), hash);
+ }
+ MetadataHasher metadata_hasher;
+ hash = HashCombine(metadata_hasher(static_cast<const Metadata &>(metadata)),
+ hash);
+ return hash;
+ }
+};
+
+} // namespace draco
+
+#endif // THIRD_PARTY_DRACO_METADATA_GEOMETRY_METADATA_H_
diff --git a/extern/draco/dracoenc/src/draco/metadata/metadata.cc b/extern/draco/dracoenc/src/draco/metadata/metadata.cc
new file mode 100644
index 00000000000..ca82d163bf8
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/metadata/metadata.cc
@@ -0,0 +1,130 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/metadata/metadata.h"
+#include <utility>
+
+namespace draco {
+
+EntryValue::EntryValue(const EntryValue &value) {
+ data_.resize(value.data_.size());
+ memcpy(&data_[0], &value.data_[0], value.data_.size());
+}
+
+EntryValue::EntryValue(const std::string &value) {
+ data_.resize(value.size());
+ memcpy(&data_[0], &value[0], value.size());
+}
+
+template <>
+bool EntryValue::GetValue(std::string *value) const {
+ if (data_.empty())
+ return false;
+ value->resize(data_.size());
+ memcpy(&value->at(0), &data_[0], data_.size());
+ return true;
+}
+
+Metadata::Metadata(const Metadata &metadata) {
+ entries_.insert(metadata.entries_.begin(), metadata.entries_.end());
+ for (const auto &sub_metadata_entry : metadata.sub_metadatas_) {
+ std::unique_ptr<Metadata> sub_metadata =
+ std::unique_ptr<Metadata>(new Metadata(*sub_metadata_entry.second));
+ sub_metadatas_[sub_metadata_entry.first] = std::move(sub_metadata);
+ }
+}
+
+void Metadata::AddEntryInt(const std::string &name, int32_t value) {
+ AddEntry(name, value);
+}
+
+bool Metadata::GetEntryInt(const std::string &name, int32_t *value) const {
+ return GetEntry(name, value);
+}
+
+void Metadata::AddEntryIntArray(const std::string &name,
+ const std::vector<int32_t> &value) {
+ AddEntry(name, value);
+}
+
+bool Metadata::GetEntryIntArray(const std::string &name,
+ std::vector<int32_t> *value) const {
+ return GetEntry(name, value);
+}
+
+void Metadata::AddEntryDouble(const std::string &name, double value) {
+ AddEntry(name, value);
+}
+
+bool Metadata::GetEntryDouble(const std::string &name, double *value) const {
+ return GetEntry(name, value);
+}
+
+void Metadata::AddEntryDoubleArray(const std::string &name,
+ const std::vector<double> &value) {
+ AddEntry(name, value);
+}
+
+bool Metadata::GetEntryDoubleArray(const std::string &name,
+ std::vector<double> *value) const {
+ return GetEntry(name, value);
+}
+
+void Metadata::AddEntryString(const std::string &name,
+ const std::string &value) {
+ AddEntry(name, value);
+}
+
+bool Metadata::GetEntryString(const std::string &name,
+ std::string *value) const {
+ return GetEntry(name, value);
+}
+
+void Metadata::AddEntryBinary(const std::string &name,
+ const std::vector<uint8_t> &value) {
+ AddEntry(name, value);
+}
+
+bool Metadata::GetEntryBinary(const std::string &name,
+ std::vector<uint8_t> *value) const {
+ return GetEntry(name, value);
+}
+
+bool Metadata::AddSubMetadata(const std::string &name,
+ std::unique_ptr<Metadata> sub_metadata) {
+ auto sub_ptr = sub_metadatas_.find(name);
+ // Avoid accidentally writing over a sub-metadata with the same name.
+ if (sub_ptr != sub_metadatas_.end()) {
+ return false;
+ }
+ sub_metadatas_[name] = std::move(sub_metadata);
+ return true;
+}
+
+const Metadata *Metadata::GetSubMetadata(const std::string &name) const {
+ auto sub_ptr = sub_metadatas_.find(name);
+ if (sub_ptr == sub_metadatas_.end()) {
+ return nullptr;
+ }
+ return sub_ptr->second.get();
+}
+
+void Metadata::RemoveEntry(const std::string &name) {
+ // Actually just remove "name", no need to check if it exists.
+ auto entry_ptr = entries_.find(name);
+ if (entry_ptr != entries_.end()) {
+ entries_.erase(entry_ptr);
+ }
+}
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/metadata/metadata.h b/extern/draco/dracoenc/src/draco/metadata/metadata.h
new file mode 100644
index 00000000000..b4dd202c991
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/metadata/metadata.h
@@ -0,0 +1,190 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#ifndef DRACO_METADATA_METADATA_H_
+#define DRACO_METADATA_METADATA_H_
+
+#include <cstring>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "draco/core/hash_utils.h"
+
+namespace draco {
+
+// Class for storing a value of an entry in Metadata. Internally it is
+// represented by a buffer of data. It can be accessed by various data types,
+// e.g. int, float, binary data or string.
+class EntryValue {
+ public:
+ template <typename DataTypeT>
+ explicit EntryValue(const DataTypeT &data) {
+ const size_t data_type_size = sizeof(DataTypeT);
+ data_.resize(data_type_size);
+ memcpy(&data_[0], &data, data_type_size);
+ }
+
+ template <typename DataTypeT>
+ explicit EntryValue(const std::vector<DataTypeT> &data) {
+ const size_t total_size = sizeof(DataTypeT) * data.size();
+ data_.resize(total_size);
+ memcpy(&data_[0], &data[0], total_size);
+ }
+
+ EntryValue(const EntryValue &value);
+ explicit EntryValue(const std::string &value);
+
+ template <typename DataTypeT>
+ bool GetValue(DataTypeT *value) const {
+ const size_t data_type_size = sizeof(DataTypeT);
+ if (data_type_size != data_.size()) {
+ return false;
+ }
+ memcpy(value, &data_[0], data_type_size);
+ return true;
+ }
+
+ template <typename DataTypeT>
+ bool GetValue(std::vector<DataTypeT> *value) const {
+ if (data_.empty())
+ return false;
+ const size_t data_type_size = sizeof(DataTypeT);
+ if (data_.size() % data_type_size != 0) {
+ return false;
+ }
+ value->resize(data_.size() / data_type_size);
+ memcpy(&value->at(0), &data_[0], data_.size());
+ return true;
+ }
+
+ const std::vector<uint8_t> &data() const { return data_; }
+
+ private:
+ std::vector<uint8_t> data_;
+
+ friend struct EntryValueHasher;
+};
+
+// Functor for computing a hash from data stored within an EntryValue.
+struct EntryValueHasher {
+ size_t operator()(const EntryValue &ev) const {
+ size_t hash = ev.data_.size();
+ for (int i = 0; i < ev.data_.size(); ++i) {
+ hash = HashCombine(ev.data_[i], hash);
+ }
+ return hash;
+ }
+};
+
+// Class for holding generic metadata. It has a list of entries which consist of
+// an entry name and an entry value. Each Metadata could also have nested
+// metadata.
+class Metadata {
+ public:
+ Metadata() {}
+ Metadata(const Metadata &metadata);
+ // In theory, we support all types of data as long as it could be serialized
+ // to binary data. We provide the following functions for inserting and
+ // accessing entries of common data types. For now, developers need to know
+ // the type of entries they are requesting.
+ void AddEntryInt(const std::string &name, int32_t value);
+ bool GetEntryInt(const std::string &name, int32_t *value) const;
+
+ void AddEntryIntArray(const std::string &name,
+ const std::vector<int32_t> &value);
+ bool GetEntryIntArray(const std::string &name,
+ std::vector<int32_t> *value) const;
+
+ void AddEntryDouble(const std::string &name, double value);
+ bool GetEntryDouble(const std::string &name, double *value) const;
+
+ void AddEntryDoubleArray(const std::string &name,
+ const std::vector<double> &value);
+ bool GetEntryDoubleArray(const std::string &name,
+ std::vector<double> *value) const;
+
+ void AddEntryString(const std::string &name, const std::string &value);
+ bool GetEntryString(const std::string &name, std::string *value) const;
+
+ // Add a blob of data as an entry.
+ void AddEntryBinary(const std::string &name,
+ const std::vector<uint8_t> &value);
+ bool GetEntryBinary(const std::string &name,
+ std::vector<uint8_t> *value) const;
+
+ bool AddSubMetadata(const std::string &name,
+ std::unique_ptr<Metadata> sub_metadata);
+ const Metadata *GetSubMetadata(const std::string &name) const;
+
+ void RemoveEntry(const std::string &name);
+
+ int num_entries() const { return static_cast<int>(entries_.size()); }
+ const std::unordered_map<std::string, EntryValue> &entries() const {
+ return entries_;
+ }
+ const std::unordered_map<std::string, std::unique_ptr<Metadata>>
+ &sub_metadatas() const {
+ return sub_metadatas_;
+ }
+
+ private:
+ // Make this function private to avoid adding undefined data types.
+ template <typename DataTypeT>
+ void AddEntry(const std::string &entry_name, const DataTypeT &entry_value) {
+ const auto itr = entries_.find(entry_name);
+ if (itr != entries_.end())
+ entries_.erase(itr);
+ entries_.insert(std::make_pair(entry_name, EntryValue(entry_value)));
+ }
+
+ // Make this function private to avoid adding undefined data types.
+ template <typename DataTypeT>
+ bool GetEntry(const std::string &entry_name, DataTypeT *entry_value) const {
+ const auto itr = entries_.find(entry_name);
+ if (itr == entries_.end()) {
+ return false;
+ }
+ return itr->second.GetValue(entry_value);
+ }
+
+ std::unordered_map<std::string, EntryValue> entries_;
+ std::unordered_map<std::string, std::unique_ptr<Metadata>> sub_metadatas_;
+
+ friend struct MetadataHasher;
+};
+
+// Functor for computing a hash from data stored within a metadata class.
+struct MetadataHasher {
+ size_t operator()(const Metadata &metadata) const {
+ size_t hash =
+ HashCombine(metadata.entries_.size(), metadata.sub_metadatas_.size());
+ EntryValueHasher entry_value_hasher;
+ for (const auto &entry : metadata.entries_) {
+ hash = HashCombine(entry.first, hash);
+ hash = HashCombine(entry_value_hasher(entry.second), hash);
+ }
+ MetadataHasher metadata_hasher;
+ for (auto &&sub_metadata : metadata.sub_metadatas_) {
+ hash = HashCombine(sub_metadata.first, hash);
+ hash = HashCombine(metadata_hasher(*sub_metadata.second), hash);
+ }
+ return hash;
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_METADATA_METADATA_H_
diff --git a/extern/draco/dracoenc/src/draco/metadata/metadata_decoder.cc b/extern/draco/dracoenc/src/draco/metadata/metadata_decoder.cc
new file mode 100644
index 00000000000..62acc26772a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/metadata/metadata_decoder.cc
@@ -0,0 +1,103 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/metadata/metadata_decoder.h"
+
+#include <string>
+
+#include "draco/core/varint_decoding.h"
+
+namespace draco {
+
+MetadataDecoder::MetadataDecoder() : buffer_(nullptr) {}
+
+bool MetadataDecoder::DecodeMetadata(DecoderBuffer *in_buffer,
+ Metadata *metadata) {
+ if (!metadata)
+ return false;
+ buffer_ = in_buffer;
+ return DecodeMetadata(metadata);
+}
+
+bool MetadataDecoder::DecodeGeometryMetadata(DecoderBuffer *in_buffer,
+ GeometryMetadata *metadata) {
+ if (!metadata)
+ return false;
+ buffer_ = in_buffer;
+ uint32_t num_att_metadata = 0;
+ DecodeVarint(&num_att_metadata, buffer_);
+ // Decode attribute metadata.
+ for (uint32_t i = 0; i < num_att_metadata; ++i) {
+ uint32_t att_unique_id;
+ DecodeVarint(&att_unique_id, buffer_);
+ std::unique_ptr<AttributeMetadata> att_metadata =
+ std::unique_ptr<AttributeMetadata>(new AttributeMetadata());
+ att_metadata->set_att_unique_id(att_unique_id);
+ if (!DecodeMetadata(static_cast<Metadata *>(att_metadata.get())))
+ return false;
+ metadata->AddAttributeMetadata(std::move(att_metadata));
+ }
+ return DecodeMetadata(static_cast<Metadata *>(metadata));
+}
+
+bool MetadataDecoder::DecodeMetadata(Metadata *metadata) {
+ uint32_t num_entries = 0;
+ DecodeVarint(&num_entries, buffer_);
+ for (uint32_t i = 0; i < num_entries; ++i) {
+ if (!DecodeEntry(metadata))
+ return false;
+ }
+ uint32_t num_sub_metadata = 0;
+ DecodeVarint(&num_sub_metadata, buffer_);
+ for (uint32_t i = 0; i < num_sub_metadata; ++i) {
+ std::string sub_metadata_name;
+ if (!DecodeName(&sub_metadata_name))
+ return false;
+ std::unique_ptr<Metadata> sub_metadata =
+ std::unique_ptr<Metadata>(new Metadata());
+ if (!DecodeMetadata(sub_metadata.get()))
+ return false;
+ metadata->AddSubMetadata(sub_metadata_name, std::move(sub_metadata));
+ }
+ return true;
+}
+
+bool MetadataDecoder::DecodeEntry(Metadata *metadata) {
+ std::string entry_name;
+ if (!DecodeName(&entry_name))
+ return false;
+ uint32_t data_size = 0;
+ if (!DecodeVarint(&data_size, buffer_))
+ return false;
+ if (data_size == 0)
+ return false;
+ std::vector<uint8_t> entry_value(data_size);
+ if (!buffer_->Decode(&entry_value[0], data_size))
+ return false;
+ metadata->AddEntryBinary(entry_name, entry_value);
+ return true;
+}
+
+bool MetadataDecoder::DecodeName(std::string *name) {
+ uint8_t name_len = 0;
+ if (!buffer_->Decode(&name_len))
+ return false;
+ name->resize(name_len);
+ if (name_len == 0)
+ return true;
+ if (!buffer_->Decode(&name->at(0), name_len))
+ return false;
+ return true;
+}
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/metadata/metadata_decoder.h b/extern/draco/dracoenc/src/draco/metadata/metadata_decoder.h
new file mode 100644
index 00000000000..b4c4943fbaa
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/metadata/metadata_decoder.h
@@ -0,0 +1,42 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#ifndef DRACO_METADATA_METADATA_DECODER_H_
+#define DRACO_METADATA_METADATA_DECODER_H_
+
+#include "draco/core/decoder_buffer.h"
+#include "draco/metadata/geometry_metadata.h"
+#include "draco/metadata/metadata.h"
+
+namespace draco {
+
+// Class for decoding the metadata.
+class MetadataDecoder {
+ public:
+ MetadataDecoder();
+ bool DecodeMetadata(DecoderBuffer *in_buffer, Metadata *metadata);
+ bool DecodeGeometryMetadata(DecoderBuffer *in_buffer,
+ GeometryMetadata *metadata);
+
+ private:
+ bool DecodeMetadata(Metadata *metadata);
+ bool DecodeEntries(Metadata *metadata);
+ bool DecodeEntry(Metadata *metadata);
+ bool DecodeName(std::string *name);
+
+ DecoderBuffer *buffer_;
+};
+} // namespace draco
+
+#endif // DRACO_METADATA_METADATA_DECODER_H_
diff --git a/extern/draco/dracoenc/src/draco/metadata/metadata_encoder.cc b/extern/draco/dracoenc/src/draco/metadata/metadata_encoder.cc
new file mode 100644
index 00000000000..c98a334b003
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/metadata/metadata_encoder.cc
@@ -0,0 +1,93 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/metadata/metadata_encoder.h"
+
+#include "draco/core/varint_encoding.h"
+
+namespace draco {
+
+bool MetadataEncoder::EncodeMetadata(EncoderBuffer *out_buffer,
+ const Metadata *metadata) {
+ const std::unordered_map<std::string, EntryValue> &entries =
+ metadata->entries();
+ // Encode number of entries.
+ EncodeVarint(static_cast<uint32_t>(metadata->num_entries()), out_buffer);
+ // Encode all entries.
+ for (const auto &entry : entries) {
+ if (!EncodeString(out_buffer, entry.first))
+ return false;
+ const std::vector<uint8_t> &entry_value = entry.second.data();
+ const uint32_t data_size = static_cast<uint32_t>(entry_value.size());
+ EncodeVarint(data_size, out_buffer);
+ out_buffer->Encode(entry_value.data(), data_size);
+ }
+ const std::unordered_map<std::string, std::unique_ptr<Metadata>>
+ &sub_metadatas = metadata->sub_metadatas();
+ // Encode number of sub-metadata
+ EncodeVarint(static_cast<uint32_t>(sub_metadatas.size()), out_buffer);
+ // Encode each sub-metadata
+ for (auto &&sub_metadata_entry : sub_metadatas) {
+ if (!EncodeString(out_buffer, sub_metadata_entry.first))
+ return false;
+ EncodeMetadata(out_buffer, sub_metadata_entry.second.get());
+ }
+
+ return true;
+}
+
+bool MetadataEncoder::EncodeAttributeMetadata(
+ EncoderBuffer *out_buffer, const AttributeMetadata *metadata) {
+ if (!metadata)
+ return false;
+ // Encode attribute id.
+ EncodeVarint(metadata->att_unique_id(), out_buffer);
+ EncodeMetadata(out_buffer, static_cast<const Metadata *>(metadata));
+ return true;
+}
+
+bool MetadataEncoder::EncodeGeometryMetadata(EncoderBuffer *out_buffer,
+ const GeometryMetadata *metadata) {
+ if (!metadata)
+ return false;
+ // Encode number of attribute metadata.
+ const std::vector<std::unique_ptr<AttributeMetadata>> &att_metadatas =
+ metadata->attribute_metadatas();
+ // TODO(draco-eng): Limit the number of attributes.
+ EncodeVarint(static_cast<uint32_t>(att_metadatas.size()), out_buffer);
+ // Encode each attribute metadata
+ for (auto &&att_metadata : att_metadatas) {
+ EncodeAttributeMetadata(out_buffer, att_metadata.get());
+ }
+ // Encode normal metadata part.
+ EncodeMetadata(out_buffer, static_cast<const Metadata *>(metadata));
+
+ return true;
+}
+
+bool MetadataEncoder::EncodeString(EncoderBuffer *out_buffer,
+ const std::string &str) {
+ // We only support string of maximum length 255 which is using one byte to
+ // encode the length.
+ if (str.size() > 255)
+ return false;
+ if (str.empty()) {
+ out_buffer->Encode(static_cast<uint8_t>(0));
+ } else {
+ out_buffer->Encode(static_cast<uint8_t>(str.size()));
+ out_buffer->Encode(str.c_str(), str.size());
+ }
+ return true;
+}
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/metadata/metadata_encoder.h b/extern/draco/dracoenc/src/draco/metadata/metadata_encoder.h
new file mode 100644
index 00000000000..5bce5d5b50e
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/metadata/metadata_encoder.h
@@ -0,0 +1,41 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#ifndef DRACO_METADATA_METADATA_ENCODER_H_
+#define DRACO_METADATA_METADATA_ENCODER_H_
+
+#include "draco/core/encoder_buffer.h"
+#include "draco/metadata/geometry_metadata.h"
+#include "draco/metadata/metadata.h"
+
+namespace draco {
+
+// Class for encoding metadata. It could encode either base Metadata class or
+// a metadata of a geometry, e.g. a point cloud.
+class MetadataEncoder {
+ public:
+ MetadataEncoder() {}
+
+ bool EncodeGeometryMetadata(EncoderBuffer *out_buffer,
+ const GeometryMetadata *metadata);
+ bool EncodeMetadata(EncoderBuffer *out_buffer, const Metadata *metadata);
+
+ private:
+ bool EncodeAttributeMetadata(EncoderBuffer *out_buffer,
+ const AttributeMetadata *metadata);
+ bool EncodeString(EncoderBuffer *out_buffer, const std::string &str);
+};
+} // namespace draco
+
+#endif // DRACO_METADATA_METADATA_ENCODER_H_
diff --git a/extern/draco/dracoenc/src/draco/metadata/metadata_encoder_test.cc b/extern/draco/dracoenc/src/draco/metadata/metadata_encoder_test.cc
new file mode 100644
index 00000000000..fbda6137c39
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/metadata/metadata_encoder_test.cc
@@ -0,0 +1,165 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/metadata/metadata_encoder.h"
+#include "draco/core/decoder_buffer.h"
+#include "draco/core/draco_test_base.h"
+#include "draco/core/encoder_buffer.h"
+#include "draco/metadata/metadata.h"
+#include "draco/metadata/metadata_decoder.h"
+
+namespace {
+
+class MetadataEncoderTest : public ::testing::Test {
+ protected:
+ MetadataEncoderTest() {}
+
+ void TestEncodingMetadata() {
+ ASSERT_TRUE(encoder.EncodeMetadata(&encoder_buffer, &metadata));
+
+ draco::Metadata decoded_metadata;
+ decoder_buffer.Init(encoder_buffer.data(), encoder_buffer.size());
+ ASSERT_TRUE(decoder.DecodeMetadata(&decoder_buffer, &decoded_metadata));
+ CheckMetadatasAreEqual(metadata, decoded_metadata);
+ }
+
+ void TestEncodingGeometryMetadata() {
+ ASSERT_TRUE(
+ encoder.EncodeGeometryMetadata(&encoder_buffer, &geometry_metadata));
+
+ draco::GeometryMetadata decoded_metadata;
+ decoder_buffer.Init(encoder_buffer.data(), encoder_buffer.size());
+ ASSERT_TRUE(
+ decoder.DecodeGeometryMetadata(&decoder_buffer, &decoded_metadata));
+ CheckGeometryMetadatasAreEqual(geometry_metadata, decoded_metadata);
+ }
+
+ void CheckBlobOfDataAreEqual(const std::vector<uint8_t> &data0,
+ const std::vector<uint8_t> &data1) {
+ ASSERT_EQ(data0.size(), data1.size());
+ for (int i = 0; i < data0.size(); ++i)
+ ASSERT_EQ(data0[i], data1[i]);
+ }
+
+ void CheckGeometryMetadatasAreEqual(
+ const draco::GeometryMetadata &metadata0,
+ const draco::GeometryMetadata &metadata1) {
+ ASSERT_EQ(metadata0.attribute_metadatas().size(),
+ metadata1.attribute_metadatas().size());
+ const std::vector<std::unique_ptr<draco::AttributeMetadata>>
+ &att_metadatas0 = metadata0.attribute_metadatas();
+ const std::vector<std::unique_ptr<draco::AttributeMetadata>>
+ &att_metadatas1 = metadata1.attribute_metadatas();
+ // Compare each attribute metadata.
+ for (int i = 0; i < metadata0.attribute_metadatas().size(); ++i) {
+ CheckMetadatasAreEqual(
+ static_cast<const draco::Metadata &>(*att_metadatas0[i]),
+ static_cast<const draco::Metadata &>(*att_metadatas1[i]));
+ }
+ // Compare entries and sub metadata.
+ CheckMetadatasAreEqual(static_cast<const draco::Metadata &>(metadata0),
+ static_cast<const draco::Metadata &>(metadata1));
+ }
+
+ void CheckMetadatasAreEqual(const draco::Metadata &metadata0,
+ const draco::Metadata &metadata1) {
+ ASSERT_EQ(metadata0.num_entries(), metadata1.num_entries());
+ const std::unordered_map<std::string, draco::EntryValue> &entries0 =
+ metadata0.entries();
+ const std::unordered_map<std::string, draco::EntryValue> &entries1 =
+ metadata1.entries();
+ for (const auto &entry : entries0) {
+ const std::string &entry_name = entry.first;
+ const std::vector<uint8_t> &data0 = entry.second.data();
+ const auto entry1_ptr = entries1.find(entry_name);
+ ASSERT_NE(entry1_ptr, entries1.end());
+ const std::vector<uint8_t> &data1 = entry1_ptr->second.data();
+ CheckBlobOfDataAreEqual(data0, data1);
+ }
+ // Check nested metadata.
+ ASSERT_EQ(metadata0.sub_metadatas().size(),
+ metadata1.sub_metadatas().size());
+ const std::unordered_map<std::string, std::unique_ptr<draco::Metadata>>
+ &sub_metadatas0 = metadata0.sub_metadatas();
+ // Encode each sub-metadata
+ for (auto &&sub_metadata_entry0 : sub_metadatas0) {
+ const auto sub_metadata_ptr1 =
+ metadata1.GetSubMetadata(sub_metadata_entry0.first);
+ ASSERT_NE(sub_metadata_ptr1, nullptr);
+ CheckMetadatasAreEqual(*sub_metadata_entry0.second, *sub_metadata_ptr1);
+ }
+ }
+
+ draco::MetadataEncoder encoder;
+ draco::MetadataDecoder decoder;
+ draco::EncoderBuffer encoder_buffer;
+ draco::DecoderBuffer decoder_buffer;
+ draco::Metadata metadata;
+ draco::GeometryMetadata geometry_metadata;
+};
+
+TEST_F(MetadataEncoderTest, TestSingleEntry) {
+ metadata.AddEntryInt("int", 100);
+ ASSERT_EQ(metadata.num_entries(), 1);
+
+ TestEncodingMetadata();
+}
+
+TEST_F(MetadataEncoderTest, TestMultipleEntries) {
+ metadata.AddEntryInt("int", 100);
+ metadata.AddEntryDouble("double", 1.234);
+ const std::string entry_value = "test string entry";
+ metadata.AddEntryString("string", entry_value);
+ ASSERT_EQ(metadata.num_entries(), 3);
+
+ TestEncodingMetadata();
+}
+
+TEST_F(MetadataEncoderTest, TestEncodingArrayEntries) {
+ std::vector<int32_t> int_array({1, 2, 3});
+ metadata.AddEntryIntArray("int_array", int_array);
+ std::vector<double> double_array({0.1, 0.2, 0.3});
+ metadata.AddEntryDoubleArray("double_array", double_array);
+ ASSERT_EQ(metadata.num_entries(), 2);
+
+ TestEncodingMetadata();
+}
+
+TEST_F(MetadataEncoderTest, TestEncodingBinaryEntry) {
+ const std::vector<uint8_t> binarydata({0x1, 0x2, 0x3, 0x4});
+ metadata.AddEntryBinary("binary_data", binarydata);
+
+ TestEncodingMetadata();
+}
+
+TEST_F(MetadataEncoderTest, TestEncodingNestedMetadata) {
+ metadata.AddEntryDouble("double", 1.234);
+ std::unique_ptr<draco::Metadata> sub_metadata =
+ std::unique_ptr<draco::Metadata>(new draco::Metadata());
+ sub_metadata->AddEntryInt("int", 100);
+ metadata.AddSubMetadata("sub0", std::move(sub_metadata));
+
+ TestEncodingMetadata();
+}
+
+TEST_F(MetadataEncoderTest, TestEncodingGeometryMetadata) {
+ std::unique_ptr<draco::AttributeMetadata> att_metadata =
+ std::unique_ptr<draco::AttributeMetadata>(new draco::AttributeMetadata);
+ att_metadata->AddEntryInt("int", 100);
+ att_metadata->AddEntryString("name", "pos");
+ ASSERT_TRUE(geometry_metadata.AddAttributeMetadata(std::move(att_metadata)));
+
+ TestEncodingGeometryMetadata();
+}
+} // namespace
diff --git a/extern/draco/dracoenc/src/draco/metadata/metadata_test.cc b/extern/draco/dracoenc/src/draco/metadata/metadata_test.cc
new file mode 100644
index 00000000000..ba6a764446c
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/metadata/metadata_test.cc
@@ -0,0 +1,156 @@
+// Copyright 2017 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 <memory>
+#include <string>
+
+#include "draco/core/draco_test_base.h"
+#include "draco/metadata/geometry_metadata.h"
+#include "draco/metadata/metadata.h"
+
+namespace {
+
+class MetadataTest : public ::testing::Test {
+ protected:
+ MetadataTest() {}
+
+ draco::Metadata metadata;
+ draco::GeometryMetadata geometry_metadata;
+};
+
+TEST_F(MetadataTest, TestRemoveEntry) {
+ metadata.AddEntryInt("int", 100);
+ metadata.RemoveEntry("int");
+ int32_t int_value = 0;
+ ASSERT_FALSE(metadata.GetEntryInt("int", &int_value));
+}
+
+TEST_F(MetadataTest, TestSingleEntry) {
+ metadata.AddEntryInt("int", 100);
+ int32_t int_value = 0;
+ ASSERT_TRUE(metadata.GetEntryInt("int", &int_value));
+ ASSERT_EQ(int_value, 100);
+
+ metadata.AddEntryDouble("double", 1.234);
+ double double_value = 0.0;
+ ASSERT_TRUE(metadata.GetEntryDouble("double", &double_value));
+ ASSERT_EQ(double_value, 1.234);
+}
+
+TEST_F(MetadataTest, TestWriteOverEntry) {
+ metadata.AddEntryInt("int", 100);
+ metadata.AddEntryInt("int", 200);
+ int32_t int_value = 0;
+ ASSERT_TRUE(metadata.GetEntryInt("int", &int_value));
+ ASSERT_EQ(int_value, 200);
+}
+
+TEST_F(MetadataTest, TestArrayEntry) {
+ std::vector<int32_t> int_array({1, 2, 3});
+ metadata.AddEntryIntArray("int_array", int_array);
+ std::vector<int32_t> return_int_array;
+ ASSERT_TRUE(metadata.GetEntryIntArray("int_array", &return_int_array));
+ ASSERT_EQ(return_int_array.size(), 3);
+ ASSERT_EQ(return_int_array[0], 1);
+ ASSERT_EQ(return_int_array[1], 2);
+ ASSERT_EQ(return_int_array[2], 3);
+
+ std::vector<double> double_array({0.1, 0.2, 0.3});
+ metadata.AddEntryDoubleArray("double_array", double_array);
+ std::vector<double> return_double_array;
+ ASSERT_TRUE(
+ metadata.GetEntryDoubleArray("double_array", &return_double_array));
+ ASSERT_EQ(return_double_array.size(), 3);
+ ASSERT_EQ(return_double_array[0], 0.1);
+ ASSERT_EQ(return_double_array[1], 0.2);
+ ASSERT_EQ(return_double_array[2], 0.3);
+}
+
+TEST_F(MetadataTest, TestStringEntry) {
+ const std::string entry_value = "test string entry";
+ metadata.AddEntryString("string", entry_value);
+ std::string return_value;
+ ASSERT_TRUE(metadata.GetEntryString("string", &return_value));
+ ASSERT_EQ(entry_value.size(), return_value.size());
+ ASSERT_EQ(entry_value, return_value);
+}
+
+TEST_F(MetadataTest, TestBinaryEntry) {
+ const std::vector<uint8_t> binarydata({0x1, 0x2, 0x3, 0x4});
+ metadata.AddEntryBinary("binary_data", binarydata);
+ std::vector<uint8_t> return_binarydata;
+ ASSERT_TRUE(metadata.GetEntryBinary("binary_data", &return_binarydata));
+ ASSERT_EQ(binarydata.size(), return_binarydata.size());
+ for (int i = 0; i < binarydata.size(); ++i) {
+ ASSERT_EQ(binarydata[i], return_binarydata[i]);
+ }
+}
+
+TEST_F(MetadataTest, TestNestedMetadata) {
+ std::unique_ptr<draco::Metadata> sub_metadata =
+ std::unique_ptr<draco::Metadata>(new draco::Metadata());
+ sub_metadata->AddEntryInt("int", 100);
+
+ metadata.AddSubMetadata("sub0", std::move(sub_metadata));
+ const auto sub_metadata_ptr = metadata.GetSubMetadata("sub0");
+ ASSERT_NE(sub_metadata_ptr, nullptr);
+
+ int32_t int_value = 0;
+ ASSERT_TRUE(sub_metadata_ptr->GetEntryInt("int", &int_value));
+ ASSERT_EQ(int_value, 100);
+}
+
+TEST_F(MetadataTest, TestHardCopyMetadata) {
+ metadata.AddEntryInt("int", 100);
+ std::unique_ptr<draco::Metadata> sub_metadata =
+ std::unique_ptr<draco::Metadata>(new draco::Metadata());
+ sub_metadata->AddEntryInt("int", 200);
+ metadata.AddSubMetadata("sub0", std::move(sub_metadata));
+
+ draco::Metadata copied_metadata(metadata);
+
+ int32_t int_value = 0;
+ ASSERT_TRUE(copied_metadata.GetEntryInt("int", &int_value));
+ ASSERT_EQ(int_value, 100);
+
+ const auto sub_metadata_ptr = copied_metadata.GetSubMetadata("sub0");
+ ASSERT_NE(sub_metadata_ptr, nullptr);
+
+ int32_t sub_int_value = 0;
+ ASSERT_TRUE(sub_metadata_ptr->GetEntryInt("int", &sub_int_value));
+ ASSERT_EQ(sub_int_value, 200);
+}
+
+TEST_F(MetadataTest, TestGeometryMetadata) {
+ std::unique_ptr<draco::AttributeMetadata> att_metadata =
+ std::unique_ptr<draco::AttributeMetadata>(new draco::AttributeMetadata());
+ att_metadata->set_att_unique_id(10);
+ att_metadata->AddEntryInt("int", 100);
+ att_metadata->AddEntryString("name", "pos");
+
+ ASSERT_FALSE(geometry_metadata.AddAttributeMetadata(nullptr));
+ ASSERT_TRUE(geometry_metadata.AddAttributeMetadata(std::move(att_metadata)));
+
+ ASSERT_NE(geometry_metadata.GetAttributeMetadataByUniqueId(10), nullptr);
+ ASSERT_EQ(geometry_metadata.GetAttributeMetadataByUniqueId(1), nullptr);
+
+ const draco::AttributeMetadata *requested_att_metadata =
+ geometry_metadata.GetAttributeMetadataByStringEntry("name", "pos");
+ ASSERT_NE(requested_att_metadata, nullptr);
+ ASSERT_EQ(
+ geometry_metadata.GetAttributeMetadataByStringEntry("name", "not_exists"),
+ nullptr);
+}
+
+} // namespace
diff --git a/extern/draco/dracoenc/src/draco/point_cloud/point_cloud.cc b/extern/draco/dracoenc/src/draco/point_cloud/point_cloud.cc
new file mode 100644
index 00000000000..63eee27417e
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/point_cloud/point_cloud.cc
@@ -0,0 +1,255 @@
+// 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 "draco/point_cloud/point_cloud.h"
+
+#include <algorithm>
+#include <unordered_map>
+
+namespace draco {
+
+PointCloud::PointCloud() : num_points_(0) {}
+
+int32_t PointCloud::NumNamedAttributes(GeometryAttribute::Type type) const {
+ if (type == GeometryAttribute::INVALID ||
+ type >= GeometryAttribute::NAMED_ATTRIBUTES_COUNT)
+ return 0;
+ return static_cast<int32_t>(named_attribute_index_[type].size());
+}
+
+int32_t PointCloud::GetNamedAttributeId(GeometryAttribute::Type type) const {
+ return GetNamedAttributeId(type, 0);
+}
+
+int32_t PointCloud::GetNamedAttributeId(GeometryAttribute::Type type,
+ int i) const {
+ if (NumNamedAttributes(type) <= i)
+ return -1;
+ return named_attribute_index_[type][i];
+}
+
+const PointAttribute *PointCloud::GetNamedAttribute(
+ GeometryAttribute::Type type) const {
+ return GetNamedAttribute(type, 0);
+}
+
+const PointAttribute *PointCloud::GetNamedAttribute(
+ GeometryAttribute::Type type, int i) const {
+ const int32_t att_id = GetNamedAttributeId(type, i);
+ if (att_id == -1)
+ return nullptr;
+ return attributes_[att_id].get();
+}
+
+const PointAttribute *PointCloud::GetNamedAttributeByUniqueId(
+ GeometryAttribute::Type type, uint32_t unique_id) const {
+ for (size_t att_id = 0; att_id < named_attribute_index_[type].size();
+ ++att_id) {
+ if (attributes_[named_attribute_index_[type][att_id]]->unique_id() ==
+ unique_id)
+ return attributes_[named_attribute_index_[type][att_id]].get();
+ }
+ return nullptr;
+}
+
+const PointAttribute *PointCloud::GetAttributeByUniqueId(
+ uint32_t unique_id) const {
+ const int32_t att_id = GetAttributeIdByUniqueId(unique_id);
+ if (att_id == -1)
+ return nullptr;
+ return attributes_[att_id].get();
+}
+
+int32_t PointCloud::GetAttributeIdByUniqueId(uint32_t unique_id) const {
+ for (size_t att_id = 0; att_id < attributes_.size(); ++att_id) {
+ if (attributes_[att_id]->unique_id() == unique_id)
+ return static_cast<int32_t>(att_id);
+ }
+ return -1;
+}
+
+int PointCloud::AddAttribute(std::unique_ptr<PointAttribute> pa) {
+ SetAttribute(static_cast<int>(attributes_.size()), std::move(pa));
+ return static_cast<int>(attributes_.size() - 1);
+}
+
+int PointCloud::AddAttribute(
+ const GeometryAttribute &att, bool identity_mapping,
+ AttributeValueIndex::ValueType num_attribute_values) {
+ const GeometryAttribute::Type type = att.attribute_type();
+ if (type == GeometryAttribute::INVALID)
+ return -1;
+ const int32_t att_id =
+ AddAttribute(std::unique_ptr<PointAttribute>(new PointAttribute(att)));
+ // Initialize point cloud specific attribute data.
+ if (!identity_mapping) {
+ // First create mapping between indices.
+ attribute(att_id)->SetExplicitMapping(num_points_);
+ } else {
+ attribute(att_id)->SetIdentityMapping();
+ attribute(att_id)->Resize(num_points_);
+ }
+ if (num_attribute_values > 0) {
+ attribute(att_id)->Reset(num_attribute_values);
+ }
+ return att_id;
+}
+
+void PointCloud::SetAttribute(int att_id, std::unique_ptr<PointAttribute> pa) {
+ DRACO_DCHECK(att_id >= 0);
+ if (static_cast<int>(attributes_.size()) <= att_id) {
+ attributes_.resize(att_id + 1);
+ }
+ if (pa->attribute_type() < GeometryAttribute::NAMED_ATTRIBUTES_COUNT) {
+ named_attribute_index_[pa->attribute_type()].push_back(att_id);
+ }
+ pa->set_unique_id(att_id);
+ attributes_[att_id] = std::move(pa);
+}
+
+void PointCloud::DeleteAttribute(int att_id) {
+ if (att_id < 0 || att_id >= attributes_.size())
+ return; // Attribute does not exist.
+ const GeometryAttribute::Type att_type =
+ attributes_[att_id]->attribute_type();
+ const uint32_t unique_id = attribute(att_id)->unique_id();
+ attributes_.erase(attributes_.begin() + att_id);
+ // Remove metadata if applicable.
+ if (metadata_) {
+ metadata_->DeleteAttributeMetadataByUniqueId(unique_id);
+ }
+
+ // Remove the attribute from the named attribute list if applicable.
+ if (att_type < GeometryAttribute::NAMED_ATTRIBUTES_COUNT) {
+ const auto it = std::find(named_attribute_index_[att_type].begin(),
+ named_attribute_index_[att_type].end(), att_id);
+ if (it != named_attribute_index_[att_type].end())
+ named_attribute_index_[att_type].erase(it);
+ }
+
+ // Update ids of all subsequent named attributes (decrease them by one).
+ for (int i = 0; i < GeometryAttribute::NAMED_ATTRIBUTES_COUNT; ++i) {
+ for (int j = 0; j < named_attribute_index_[i].size(); ++j) {
+ if (named_attribute_index_[i][j] > att_id) {
+ named_attribute_index_[i][j]--;
+ }
+ }
+ }
+}
+
+#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
+void PointCloud::DeduplicatePointIds() {
+ // Hashing function for a single vertex.
+ auto point_hash = [this](PointIndex p) {
+ PointIndex::ValueType hash = 0;
+ for (int32_t i = 0; i < this->num_attributes(); ++i) {
+ const AttributeValueIndex att_id = attribute(i)->mapped_index(p);
+ hash = static_cast<uint32_t>(HashCombine(att_id.value(), hash));
+ }
+ return hash;
+ };
+ // Comparison function between two vertices.
+ auto point_compare = [this](PointIndex p0, PointIndex p1) {
+ for (int32_t i = 0; i < this->num_attributes(); ++i) {
+ const AttributeValueIndex att_id0 = attribute(i)->mapped_index(p0);
+ const AttributeValueIndex att_id1 = attribute(i)->mapped_index(p1);
+ if (att_id0 != att_id1)
+ return false;
+ }
+ return true;
+ };
+
+ std::unordered_map<PointIndex, PointIndex, decltype(point_hash),
+ decltype(point_compare)>
+ unique_point_map(num_points_, point_hash, point_compare);
+ int32_t num_unique_points = 0;
+ IndexTypeVector<PointIndex, PointIndex> index_map(num_points_);
+ std::vector<PointIndex> unique_points;
+ // Go through all vertices and find their duplicates.
+ for (PointIndex i(0); i < num_points_; ++i) {
+ const auto it = unique_point_map.find(i);
+ if (it != unique_point_map.end()) {
+ index_map[i] = it->second;
+ } else {
+ unique_point_map.insert(std::make_pair(i, PointIndex(num_unique_points)));
+ index_map[i] = num_unique_points++;
+ unique_points.push_back(i);
+ }
+ }
+ if (num_unique_points == num_points_)
+ return; // All vertices are already unique.
+
+ ApplyPointIdDeduplication(index_map, unique_points);
+ set_num_points(num_unique_points);
+}
+
+void PointCloud::ApplyPointIdDeduplication(
+ const IndexTypeVector<PointIndex, PointIndex> &id_map,
+ const std::vector<PointIndex> &unique_point_ids) {
+ int32_t num_unique_points = 0;
+ for (PointIndex i : unique_point_ids) {
+ const PointIndex new_point_id = id_map[i];
+ if (new_point_id >= num_unique_points) {
+ // New unique vertex reached. Copy attribute indices to the proper
+ // position.
+ for (int32_t a = 0; a < num_attributes(); ++a) {
+ attribute(a)->SetPointMapEntry(new_point_id,
+ attribute(a)->mapped_index(i));
+ }
+ num_unique_points = new_point_id.value() + 1;
+ }
+ }
+ for (int32_t a = 0; a < num_attributes(); ++a) {
+ attribute(a)->SetExplicitMapping(num_unique_points);
+ }
+}
+
+bool PointCloud::DeduplicateAttributeValues() {
+ // Go over all attributes and create mapping between duplicate entries.
+ if (num_points() == 0)
+ return false; // Unexpected attribute size.
+ // Deduplicate all attributes.
+ for (int32_t att_id = 0; att_id < num_attributes(); ++att_id) {
+ if (!attribute(att_id)->DeduplicateValues(*attribute(att_id)))
+ return false;
+ }
+ return true;
+}
+#endif
+
+// TODO(xiaoxumeng): Consider to cash the BBox.
+BoundingBox PointCloud::ComputeBoundingBox() const {
+ BoundingBox bounding_box =
+ BoundingBox(Vector3f(std::numeric_limits<float>::max(),
+ std::numeric_limits<float>::max(),
+ std::numeric_limits<float>::max()),
+ Vector3f(-std::numeric_limits<float>::max(),
+ -std::numeric_limits<float>::max(),
+ -std::numeric_limits<float>::max()));
+ auto pc_att = GetNamedAttribute(GeometryAttribute::POSITION);
+ // TODO(xiaoxumeng): Make the BoundingBox a template type, it may not be easy
+ // because PointCloud is not a template.
+ // Or simply add some preconditioning here to make sure the position attribute
+ // is valid, because the current code works only if the position attribute is
+ // defined with 3 components of DT_FLOAT32.
+ // Consider using pc_att->ConvertValue<float, 3>(i, &p[0]) (Enforced
+ // transformation from Vector with any dimension to Vector3f)
+ Vector3f p;
+ for (AttributeValueIndex i(0); i < pc_att->size(); ++i) {
+ pc_att->GetValue(i, &p[0]);
+ bounding_box.update_bounding_box(p);
+ }
+ return bounding_box;
+}
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/point_cloud/point_cloud.h b/extern/draco/dracoenc/src/draco/point_cloud/point_cloud.h
new file mode 100644
index 00000000000..0ea057dcf54
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/point_cloud/point_cloud.h
@@ -0,0 +1,232 @@
+// 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_H_
+#define DRACO_POINT_CLOUD_POINT_CLOUD_H_
+
+#include "draco/draco_features.h"
+
+#include "draco/attributes/point_attribute.h"
+#include "draco/core/bounding_box.h"
+#include "draco/core/vector_d.h"
+#include "draco/metadata/geometry_metadata.h"
+
+namespace draco {
+
+// PointCloud is a collection of n-dimensional points that are described by a
+// set of PointAttributes that can represent data such as positions or colors
+// of individual points (see point_attribute.h).
+class PointCloud {
+ public:
+ PointCloud();
+ virtual ~PointCloud() = default;
+
+ // Returns the number of named attributes of a given type.
+ int32_t NumNamedAttributes(GeometryAttribute::Type type) const;
+
+ // Returns attribute id of the first named attribute with a given type or -1
+ // when the attribute is not used by the point cloud.
+ int32_t GetNamedAttributeId(GeometryAttribute::Type type) const;
+
+ // Returns the id of the i-th named attribute of a given type.
+ int32_t GetNamedAttributeId(GeometryAttribute::Type type, int i) const;
+
+ // Returns the first named attribute of a given type or nullptr if the
+ // attribute is not used by the point cloud.
+ const PointAttribute *GetNamedAttribute(GeometryAttribute::Type type) const;
+
+ // Returns the i-th named attribute of a given type.
+ const PointAttribute *GetNamedAttribute(GeometryAttribute::Type type,
+ int i) const;
+
+ // Returns the named attribute of a given unique id.
+ const PointAttribute *GetNamedAttributeByUniqueId(
+ GeometryAttribute::Type type, uint32_t id) const;
+
+ // Returns the attribute of a given unique id.
+ const PointAttribute *GetAttributeByUniqueId(uint32_t id) const;
+ int32_t GetAttributeIdByUniqueId(uint32_t unique_id) const;
+
+ int32_t num_attributes() const {
+ return static_cast<int32_t>(attributes_.size());
+ }
+ const PointAttribute *attribute(int32_t att_id) const {
+ DRACO_DCHECK_LE(0, att_id);
+ DRACO_DCHECK_LT(att_id, static_cast<int32_t>(attributes_.size()));
+ return attributes_[att_id].get();
+ }
+
+ // Returned attribute can be modified, but it's caller's responsibility to
+ // maintain the attribute's consistency with draco::PointCloud.
+ PointAttribute *attribute(int32_t att_id) {
+ DRACO_DCHECK_LE(0, att_id);
+ DRACO_DCHECK_LT(att_id, static_cast<int32_t>(attributes_.size()));
+ return attributes_[att_id].get();
+ }
+
+ // Adds a new attribute to the point cloud.
+ // Returns the attribute id.
+ int AddAttribute(std::unique_ptr<PointAttribute> pa);
+
+ // Creates and adds a new attribute to the point cloud. The attribute has
+ // properties derived from the provided GeometryAttribute |att|.
+ // If |identity_mapping| is set to true, the attribute will use identity
+ // mapping between point indices and attribute value indices (i.e., each
+ // point has a unique attribute value). If |identity_mapping| is false, the
+ // mapping between point indices and attribute value indices is set to
+ // explicit, and it needs to be initialized manually using the
+ // PointAttribute::SetPointMapEntry() method. |num_attribute_values| can be
+ // used to specify the number of attribute values that are going to be
+ // stored in the newly created attribute. Returns attribute id of the newly
+ // created attribute.
+ int AddAttribute(const GeometryAttribute &att, bool identity_mapping,
+ AttributeValueIndex::ValueType num_attribute_values);
+
+ // Assigns an attribute id to a given PointAttribute. If an attribute with
+ // the same attribute id already exists, it is deleted.
+ virtual void SetAttribute(int att_id, std::unique_ptr<PointAttribute> pa);
+
+ // Deletes an attribute with specified attribute id. Note that this changes
+ // attribute ids of all subsequent attributes.
+ virtual void DeleteAttribute(int att_id);
+
+#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
+ // Deduplicates all attribute values (all attribute entries with the same
+ // value are merged into a single entry).
+ virtual bool DeduplicateAttributeValues();
+
+ // Removes duplicate point ids (two point ids are duplicate when all of their
+ // attributes are mapped to the same entry ids).
+ virtual void DeduplicatePointIds();
+#endif
+
+ // Get bounding box.
+ BoundingBox ComputeBoundingBox() const;
+
+ // Add metadata.
+ void AddMetadata(std::unique_ptr<GeometryMetadata> metadata) {
+ metadata_ = std::move(metadata);
+ }
+
+ // Add metadata for an attribute.
+ void AddAttributeMetadata(int32_t att_id,
+ std::unique_ptr<AttributeMetadata> metadata) {
+ if (!metadata_.get()) {
+ metadata_ = std::unique_ptr<GeometryMetadata>(new GeometryMetadata());
+ }
+ const int32_t att_unique_id = attribute(att_id)->unique_id();
+ metadata->set_att_unique_id(att_unique_id);
+ metadata_->AddAttributeMetadata(std::move(metadata));
+ }
+
+ const AttributeMetadata *GetAttributeMetadataByAttributeId(
+ int32_t att_id) const {
+ if (metadata_ == nullptr)
+ return nullptr;
+ const uint32_t unique_id = attribute(att_id)->unique_id();
+ return metadata_->GetAttributeMetadataByUniqueId(unique_id);
+ }
+
+ // Returns the attribute metadata that has the requested metadata entry.
+ const AttributeMetadata *GetAttributeMetadataByStringEntry(
+ const std::string &name, const std::string &value) const {
+ if (metadata_ == nullptr)
+ return nullptr;
+ return metadata_->GetAttributeMetadataByStringEntry(name, value);
+ }
+
+ // Returns the first attribute that has the requested metadata entry.
+ int GetAttributeIdByMetadataEntry(const std::string &name,
+ const std::string &value) const {
+ if (metadata_ == nullptr)
+ return -1;
+ const AttributeMetadata *att_metadata =
+ metadata_->GetAttributeMetadataByStringEntry(name, value);
+ if (!att_metadata)
+ return -1;
+ return GetAttributeIdByUniqueId(att_metadata->att_unique_id());
+ }
+
+ // Get a const pointer of the metadata of the point cloud.
+ const GeometryMetadata *GetMetadata() const { return metadata_.get(); }
+
+ // Get a pointer to the metadata of the point cloud.
+ GeometryMetadata *metadata() { return metadata_.get(); }
+
+ // Returns the number of n-dimensional points stored within the point cloud.
+ PointIndex::ValueType num_points() const { return num_points_; }
+
+ // Sets the number of points. It's the caller's responsibility to ensure the
+ // new number is valid with respect to the PointAttributes stored in the point
+ // cloud.
+ void set_num_points(PointIndex::ValueType num) { num_points_ = num; }
+
+ protected:
+#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
+ // Applies id mapping of deduplicated points (called by DeduplicatePointIds).
+ virtual void ApplyPointIdDeduplication(
+ const IndexTypeVector<PointIndex, PointIndex> &id_map,
+ const std::vector<PointIndex> &unique_point_ids);
+#endif
+
+ private:
+ // Metadata for the point cloud.
+ std::unique_ptr<GeometryMetadata> metadata_;
+
+ // Attributes describing the point cloud.
+ std::vector<std::unique_ptr<PointAttribute>> attributes_;
+
+ // Ids of named attributes of the given type.
+ std::vector<int32_t>
+ named_attribute_index_[GeometryAttribute::NAMED_ATTRIBUTES_COUNT];
+
+ // The number of n-dimensional points. All point attribute values are stored
+ // in corresponding PointAttribute instances in the |attributes_| array.
+ PointIndex::ValueType num_points_;
+
+ friend struct PointCloudHasher;
+};
+
+// Functor for computing a hash from data stored within a point cloud.
+// Note that this can be quite slow. Two point clouds will have the same hash
+// only when all points have the same order and when all attribute values are
+// exactly the same.
+struct PointCloudHasher {
+ size_t operator()(const PointCloud &pc) const {
+ size_t hash = pc.num_points_;
+ 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 < static_cast<int>(pc.named_attribute_index_[i].size());
+ ++j) {
+ hash = HashCombine(pc.named_attribute_index_[i][j], hash);
+ }
+ }
+ // Hash attributes.
+ for (int i = 0; i < static_cast<int>(pc.attributes_.size()); ++i) {
+ PointAttributeHasher att_hasher;
+ hash = HashCombine(att_hasher(*pc.attributes_[i]), hash);
+ }
+ // Hash metadata.
+ GeometryMetadataHasher metadata_hasher;
+ if (pc.metadata_.get()) {
+ hash = HashCombine(metadata_hasher(*pc.metadata_.get()), hash);
+ }
+ return hash;
+ }
+};
+
+} // namespace draco
+
+#endif // DRACO_POINT_CLOUD_POINT_CLOUD_H_
diff --git a/extern/draco/dracoenc/src/draco/point_cloud/point_cloud_builder.cc b/extern/draco/dracoenc/src/draco/point_cloud/point_cloud_builder.cc
new file mode 100644
index 00000000000..455d9a931db
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/point_cloud/point_cloud_builder.cc
@@ -0,0 +1,73 @@
+// 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 "draco/point_cloud/point_cloud_builder.h"
+
+namespace draco {
+
+PointCloudBuilder::PointCloudBuilder() {}
+
+void PointCloudBuilder::Start(PointIndex::ValueType num_points) {
+ point_cloud_ = std::unique_ptr<PointCloud>(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->num_components();
+ 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<const uint8_t *>(attribute_values) + stride * i.value());
+ }
+ }
+}
+
+std::unique_ptr<PointCloud> PointCloudBuilder::Finalize(
+ bool deduplicate_points) {
+#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
+ if (deduplicate_points) {
+ point_cloud_->DeduplicateAttributeValues();
+ point_cloud_->DeduplicatePointIds();
+ }
+#endif
+ return std::move(point_cloud_);
+}
+
+} // namespace draco
diff --git a/extern/draco/dracoenc/src/draco/point_cloud/point_cloud_builder.h b/extern/draco/dracoenc/src/draco/point_cloud/point_cloud_builder.h
new file mode 100644
index 00000000000..cf55a728b15
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/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 "draco/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<PointCloud> pc = builder.Finalize(deduplicate_points);
+
+class PointCloudBuilder {
+ public:
+ PointCloudBuilder();
+
+ // Starts collecting point cloud data.
+ // The behavior 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<PointCloud> Finalize(bool deduplicate_points);
+
+ private:
+ std::unique_ptr<PointCloud> point_cloud_;
+};
+
+} // namespace draco
+
+#endif // DRACO_POINT_CLOUD_POINT_CLOUD_BUILDER_H_
diff --git a/extern/draco/dracoenc/src/draco/point_cloud/point_cloud_builder_test.cc b/extern/draco/dracoenc/src/draco/point_cloud/point_cloud_builder_test.cc
new file mode 100644
index 00000000000..3222a4c8da8
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/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 "draco/point_cloud/point_cloud_builder.h"
+
+#include "draco/core/draco_test_base.h"
+
+namespace draco {
+
+class PointCloudBuilderTest : public ::testing::Test {
+ protected:
+ // Test data.
+ // clang-format off
+ std::vector<float> 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<int16_t> 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<PointCloud> res = builder.Finalize(false);
+ ASSERT_TRUE(res != nullptr);
+ ASSERT_EQ(res->num_points(), 10);
+}
+
+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<PointCloud> res = builder.Finalize(true);
+ ASSERT_TRUE(res != nullptr);
+ ASSERT_EQ(res->num_points(), 9);
+}
+
+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<PointCloud> res = builder.Finalize(false);
+ ASSERT_TRUE(res != nullptr);
+ ASSERT_EQ(res->num_points(), 10);
+ 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<PointCloud> res = builder.Finalize(false);
+ ASSERT_TRUE(res != nullptr);
+ ASSERT_EQ(res->num_points(), 10);
+ 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 (offsetted 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<PointCloud> res = builder.Finalize(false);
+ ASSERT_TRUE(res != nullptr);
+ ASSERT_EQ(res->num_points(), 4);
+ 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/extern/draco/dracoenc/src/draco/point_cloud/point_cloud_test.cc b/extern/draco/dracoenc/src/draco/point_cloud/point_cloud_test.cc
new file mode 100644
index 00000000000..00491b7309a
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/point_cloud/point_cloud_test.cc
@@ -0,0 +1,131 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "draco/core/draco_test_base.h"
+#include "draco/core/draco_test_utils.h"
+
+#include "draco/point_cloud/point_cloud.h"
+
+namespace {
+
+class PointCloudTest : public ::testing::Test {
+ protected:
+ PointCloudTest() {}
+};
+
+TEST_F(PointCloudTest, TestAttributeDeletion) {
+ draco::PointCloud pc;
+ // Test whether we can correctly delete an attribute from a point cloud.
+ // Create some attributes for the point cloud.
+ draco::GeometryAttribute pos_att;
+ pos_att.Init(draco::GeometryAttribute::POSITION, nullptr, 3,
+ draco::DT_FLOAT32, false, 12, 0);
+ draco::GeometryAttribute norm_att;
+ norm_att.Init(draco::GeometryAttribute::NORMAL, nullptr, 3, draco::DT_FLOAT32,
+ false, 12, 0);
+ draco::GeometryAttribute gen_att;
+ gen_att.Init(draco::GeometryAttribute::GENERIC, nullptr, 3, draco::DT_FLOAT32,
+ false, 12, 0);
+
+ // Add one position, two normal and two generic attributes.
+ pc.AddAttribute(pos_att, false, 0);
+ pc.AddAttribute(gen_att, false, 0);
+ pc.AddAttribute(norm_att, false, 0);
+ pc.AddAttribute(gen_att, false, 0);
+ pc.AddAttribute(norm_att, false, 0);
+
+ ASSERT_EQ(pc.num_attributes(), 5);
+ ASSERT_EQ(pc.attribute(0)->attribute_type(),
+ draco::GeometryAttribute::POSITION);
+ ASSERT_EQ(pc.attribute(3)->attribute_type(),
+ draco::GeometryAttribute::GENERIC);
+
+ // Delete generic attribute.
+ pc.DeleteAttribute(1);
+ ASSERT_EQ(pc.num_attributes(), 4);
+ ASSERT_EQ(pc.attribute(1)->attribute_type(),
+ draco::GeometryAttribute::NORMAL);
+ ASSERT_EQ(pc.NumNamedAttributes(draco::GeometryAttribute::NORMAL), 2);
+ ASSERT_EQ(pc.GetNamedAttributeId(draco::GeometryAttribute::NORMAL, 1), 3);
+
+ // Delete the first normal attribute.
+ pc.DeleteAttribute(1);
+ ASSERT_EQ(pc.num_attributes(), 3);
+ ASSERT_EQ(pc.attribute(1)->attribute_type(),
+ draco::GeometryAttribute::GENERIC);
+ ASSERT_EQ(pc.NumNamedAttributes(draco::GeometryAttribute::NORMAL), 1);
+ ASSERT_EQ(pc.GetNamedAttributeId(draco::GeometryAttribute::NORMAL, 0), 2);
+}
+
+TEST_F(PointCloudTest, TestPointCloudWithMetadata) {
+ draco::PointCloud pc;
+ std::unique_ptr<draco::GeometryMetadata> metadata =
+ std::unique_ptr<draco::GeometryMetadata>(new draco::GeometryMetadata());
+
+ // Add a position attribute metadata.
+ draco::GeometryAttribute pos_att;
+ pos_att.Init(draco::GeometryAttribute::POSITION, nullptr, 3,
+ draco::DT_FLOAT32, false, 12, 0);
+ const uint32_t pos_att_id = pc.AddAttribute(pos_att, false, 0);
+ ASSERT_EQ(pos_att_id, 0);
+ std::unique_ptr<draco::AttributeMetadata> pos_metadata =
+ std::unique_ptr<draco::AttributeMetadata>(new draco::AttributeMetadata());
+ pos_metadata->AddEntryString("name", "position");
+ pc.AddAttributeMetadata(pos_att_id, std::move(pos_metadata));
+ const draco::GeometryMetadata *pc_metadata = pc.GetMetadata();
+ ASSERT_NE(pc_metadata, nullptr);
+ // Add a generic material attribute metadata.
+ draco::GeometryAttribute material_att;
+ material_att.Init(draco::GeometryAttribute::GENERIC, nullptr, 3,
+ draco::DT_FLOAT32, false, 12, 0);
+ const uint32_t material_att_id = pc.AddAttribute(material_att, false, 0);
+ ASSERT_EQ(material_att_id, 1);
+ std::unique_ptr<draco::AttributeMetadata> material_metadata =
+ std::unique_ptr<draco::AttributeMetadata>(new draco::AttributeMetadata());
+ material_metadata->AddEntryString("name", "material");
+ // The material attribute has id of 1 now.
+ pc.AddAttributeMetadata(material_att_id, std::move(material_metadata));
+
+ // Test if the attribute metadata is correctly added.
+ const draco::AttributeMetadata *requested_pos_metadata =
+ pc.GetAttributeMetadataByStringEntry("name", "position");
+ ASSERT_NE(requested_pos_metadata, nullptr);
+ const draco::AttributeMetadata *requested_mat_metadata =
+ pc.GetAttributeMetadataByStringEntry("name", "material");
+ ASSERT_NE(requested_mat_metadata, nullptr);
+
+ // Attribute id should be preserved.
+ ASSERT_EQ(
+ pc.GetAttributeIdByUniqueId(requested_pos_metadata->att_unique_id()), 0);
+ ASSERT_EQ(
+ pc.GetAttributeIdByUniqueId(requested_mat_metadata->att_unique_id()), 1);
+
+ // Test deleting attribute with metadata.
+ pc.DeleteAttribute(pos_att_id);
+ ASSERT_EQ(pc.GetAttributeMetadataByStringEntry("name", "position"), nullptr);
+
+ requested_mat_metadata =
+ pc.GetAttributeMetadataByStringEntry("name", "material");
+ // The unique id should not be changed.
+ ASSERT_EQ(requested_mat_metadata->att_unique_id(), 1);
+ // Now position attribute is removed, material attribute should have the
+ // attribute id of 0.
+ ASSERT_EQ(
+ pc.GetAttributeIdByUniqueId(requested_mat_metadata->att_unique_id()), 0);
+ // Should be able to get metadata using the current attribute id.
+ // Attribute id of material attribute is changed from 1 to 0.
+ ASSERT_NE(pc.GetAttributeMetadataByAttributeId(0), nullptr);
+}
+
+} // namespace
diff --git a/extern/draco/dracoenc/src/draco/tools/draco_decoder.cc b/extern/draco/dracoenc/src/draco/tools/draco_decoder.cc
new file mode 100644
index 00000000000..d96575223d7
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/tools/draco_decoder.cc
@@ -0,0 +1,176 @@
+// 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 <cinttypes>
+#include <fstream>
+
+#include "draco/compression/decode.h"
+#include "draco/core/cycle_timer.h"
+#include "draco/io/obj_encoder.h"
+#include "draco/io/parser_utils.h"
+#include "draco/io/ply_encoder.h"
+
+namespace {
+
+struct Options {
+ Options();
+
+ std::string input;
+ std::string output;
+};
+
+Options::Options() {}
+
+void Usage() {
+ printf("Usage: draco_decoder [options] -i input\n");
+ printf("\n");
+ printf("Main options:\n");
+ printf(" -h | -? show help.\n");
+ printf(" -o <output> output file name.\n");
+}
+
+int ReturnError(const draco::Status &status) {
+ printf("Failed to decode the input file %s\n", status.error_msg());
+ return -1;
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ Options options;
+ const int argc_check = argc - 1;
+
+ for (int i = 1; i < argc; ++i) {
+ if (!strcmp("-h", argv[i]) || !strcmp("-?", argv[i])) {
+ Usage();
+ return 0;
+ } else if (!strcmp("-i", argv[i]) && i < argc_check) {
+ options.input = argv[++i];
+ } else if (!strcmp("-o", argv[i]) && i < argc_check) {
+ options.output = argv[++i];
+ }
+ }
+ if (argc < 3 || options.input.empty()) {
+ Usage();
+ return -1;
+ }
+
+ std::ifstream input_file(options.input, std::ios::binary);
+ if (!input_file) {
+ printf("Failed opening the input file.\n");
+ return -1;
+ }
+
+ // Read the file stream into a buffer.
+ std::streampos file_size = 0;
+ input_file.seekg(0, std::ios::end);
+ file_size = input_file.tellg() - file_size;
+ input_file.seekg(0, std::ios::beg);
+ std::vector<char> data(file_size);
+ input_file.read(data.data(), file_size);
+
+ if (data.empty()) {
+ printf("Empty input file.\n");
+ return -1;
+ }
+
+ // Create a draco decoding buffer. Note that no data is copied in this step.
+ draco::DecoderBuffer buffer;
+ buffer.Init(data.data(), data.size());
+
+ draco::CycleTimer timer;
+ // Decode the input data into a geometry.
+ std::unique_ptr<draco::PointCloud> pc;
+ draco::Mesh *mesh = nullptr;
+ auto type_statusor = draco::Decoder::GetEncodedGeometryType(&buffer);
+ if (!type_statusor.ok()) {
+ return ReturnError(type_statusor.status());
+ }
+ const draco::EncodedGeometryType geom_type = type_statusor.value();
+ if (geom_type == draco::TRIANGULAR_MESH) {
+ timer.Start();
+ draco::Decoder decoder;
+ auto statusor = decoder.DecodeMeshFromBuffer(&buffer);
+ if (!statusor.ok()) {
+ return ReturnError(statusor.status());
+ }
+ std::unique_ptr<draco::Mesh> in_mesh = std::move(statusor).value();
+ timer.Stop();
+ if (in_mesh) {
+ mesh = in_mesh.get();
+ pc = std::move(in_mesh);
+ }
+ } else if (geom_type == draco::POINT_CLOUD) {
+ // Failed to decode it as mesh, so let's try to decode it as a point cloud.
+ timer.Start();
+ draco::Decoder decoder;
+ auto statusor = decoder.DecodePointCloudFromBuffer(&buffer);
+ if (!statusor.ok()) {
+ return ReturnError(statusor.status());
+ }
+ pc = std::move(statusor).value();
+ timer.Stop();
+ }
+
+ if (pc == nullptr) {
+ printf("Failed to decode the input file.\n");
+ return -1;
+ }
+
+ if (options.output.empty()) {
+ // Save the output model into a ply file.
+ options.output = options.input + ".ply";
+ }
+
+ // Save the decoded geometry into a file.
+ // TODO(ostava): Currently only .ply and .obj are supported.
+ const std::string extension = draco::parser::ToLower(
+ options.output.size() >= 4
+ ? options.output.substr(options.output.size() - 4)
+ : options.output);
+
+ if (extension == ".obj") {
+ draco::ObjEncoder obj_encoder;
+ if (mesh) {
+ if (!obj_encoder.EncodeToFile(*mesh, options.output)) {
+ printf("Failed to store the decoded mesh as OBJ.\n");
+ return -1;
+ }
+ } else {
+ if (!obj_encoder.EncodeToFile(*pc.get(), options.output)) {
+ printf("Failed to store the decoded point cloud as OBJ.\n");
+ return -1;
+ }
+ }
+ } else if (extension == ".ply") {
+ draco::PlyEncoder ply_encoder;
+ if (mesh) {
+ if (!ply_encoder.EncodeToFile(*mesh, options.output)) {
+ printf("Failed to store the decoded mesh as PLY.\n");
+ return -1;
+ }
+ } else {
+ if (!ply_encoder.EncodeToFile(*pc.get(), options.output)) {
+ printf("Failed to store the decoded point cloud as PLY.\n");
+ return -1;
+ }
+ }
+ } else {
+ printf("Invalid extension of the output file. Use either .ply or .obj\n");
+ return -1;
+ }
+ printf("Decoded geometry saved to %s (%" PRId64 " ms to decode)\n",
+ options.output.c_str(), timer.GetInMs());
+ return 0;
+}
diff --git a/extern/draco/dracoenc/src/draco/tools/draco_encoder.cc b/extern/draco/dracoenc/src/draco/tools/draco_encoder.cc
new file mode 100644
index 00000000000..05458fbbdf6
--- /dev/null
+++ b/extern/draco/dracoenc/src/draco/tools/draco_encoder.cc
@@ -0,0 +1,373 @@
+// 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 <cinttypes>
+#include <cstdlib>
+#include <fstream>
+
+#include "draco/compression/encode.h"
+#include "draco/core/cycle_timer.h"
+#include "draco/io/mesh_io.h"
+#include "draco/io/point_cloud_io.h"
+
+namespace {
+
+struct Options {
+ Options();
+
+ bool is_point_cloud;
+ int pos_quantization_bits;
+ int tex_coords_quantization_bits;
+ bool tex_coords_deleted;
+ int normals_quantization_bits;
+ bool normals_deleted;
+ int generic_quantization_bits;
+ bool generic_deleted;
+ int compression_level;
+ bool use_metadata;
+ std::string input;
+ std::string output;
+};
+
+Options::Options()
+ : is_point_cloud(false),
+ pos_quantization_bits(14),
+ tex_coords_quantization_bits(12),
+ tex_coords_deleted(false),
+ normals_quantization_bits(10),
+ normals_deleted(false),
+ generic_quantization_bits(8),
+ generic_deleted(false),
+ compression_level(7),
+ use_metadata(false) {}
+
+void Usage() {
+ printf("Usage: draco_encoder [options] -i input\n");
+ printf("\n");
+ printf("Main options:\n");
+ printf(" -h | -? show help.\n");
+ printf(" -i <input> input file name.\n");
+ printf(" -o <output> output file name.\n");
+ printf(
+ " -point_cloud forces the input to be encoded as a point "
+ "cloud.\n");
+ printf(
+ " -qp <value> quantization bits for the position "
+ "attribute, default=14.\n");
+ printf(
+ " -qt <value> quantization bits for the texture coordinate "
+ "attribute, default=12.\n");
+ printf(
+ " -qn <value> quantization bits for the normal vector "
+ "attribute, default=10.\n");
+ printf(
+ " -qg <value> quantization bits for any generic attribute, "
+ "default=8.\n");
+ printf(
+ " -cl <value> compression level [0-10], most=10, least=0, "
+ "default=7.\n");
+ printf(
+ " --skip ATTRIBUTE_NAME skip a given attribute (NORMAL, TEX_COORD, "
+ "GENERIC)\n");
+ printf(
+ " --metadata use metadata to encode extra information in "
+ "mesh files.\n");
+ printf(
+ "\nUse negative quantization values to skip the specified attribute\n");
+}
+
+int StringToInt(const std::string &s) {
+ char *end;
+ return strtol(s.c_str(), &end, 10); // NOLINT
+}
+
+void PrintOptions(const draco::PointCloud &pc, const Options &options) {
+ printf("Encoder options:\n");
+ printf(" Compression level = %d\n", options.compression_level);
+ if (options.pos_quantization_bits == 0) {
+ printf(" Positions: No quantization\n");
+ } else {
+ printf(" Positions: Quantization = %d bits\n",
+ options.pos_quantization_bits);
+ }
+
+ if (pc.GetNamedAttributeId(draco::GeometryAttribute::TEX_COORD) >= 0) {
+ if (options.tex_coords_quantization_bits == 0) {
+ printf(" Texture coordinates: No quantization\n");
+ } else {
+ printf(" Texture coordinates: Quantization = %d bits\n",
+ options.tex_coords_quantization_bits);
+ }
+ } else if (options.tex_coords_deleted) {
+ printf(" Texture coordinates: Skipped\n");
+ }
+
+ if (pc.GetNamedAttributeId(draco::GeometryAttribute::NORMAL) >= 0) {
+ if (options.normals_quantization_bits == 0) {
+ printf(" Normals: No quantization\n");
+ } else {
+ printf(" Normals: Quantization = %d bits\n",
+ options.normals_quantization_bits);
+ }
+ } else if (options.normals_deleted) {
+ printf(" Normals: Skipped\n");
+ }
+
+ if (pc.GetNamedAttributeId(draco::GeometryAttribute::GENERIC) >= 0) {
+ if (options.generic_quantization_bits == 0) {
+ printf(" Generic: No quantization\n");
+ } else {
+ printf(" Generic: Quantization = %d bits\n",
+ options.generic_quantization_bits);
+ }
+ } else if (options.generic_deleted) {
+ printf(" Generic: Skipped\n");
+ }
+ printf("\n");
+}
+
+int EncodePointCloudToFile(const draco::PointCloud &pc, const std::string &file,
+ draco::Encoder *encoder) {
+ draco::CycleTimer timer;
+ // Encode the geometry.
+ draco::EncoderBuffer buffer;
+ timer.Start();
+ const draco::Status status = encoder->EncodePointCloudToBuffer(pc, &buffer);
+ if (!status.ok()) {
+ printf("Failed to encode the point cloud.\n");
+ printf("%s\n", status.error_msg());
+ return -1;
+ }
+ timer.Stop();
+ // Save the encoded geometry into a file.
+ std::ofstream out_file(file, std::ios::binary);
+ if (!out_file) {
+ printf("Failed to create the output file.\n");
+ return -1;
+ }
+ out_file.write(buffer.data(), buffer.size());
+ 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;
+}
+
+int EncodeMeshToFile(const draco::Mesh &mesh, const std::string &file,
+ draco::Encoder *encoder) {
+ draco::CycleTimer timer;
+ // Encode the geometry.
+ draco::EncoderBuffer buffer;
+ timer.Start();
+ const draco::Status status = encoder->EncodeMeshToBuffer(mesh, &buffer);
+ if (!status.ok()) {
+ printf("Failed to encode the mesh.\n");
+ printf("%s\n", status.error_msg());
+ return -1;
+ }
+ timer.Stop();
+ // Save the encoded geometry into a file.
+ std::ofstream out_file(file, std::ios::binary);
+ if (!out_file) {
+ printf("Failed to create the output file.\n");
+ return -1;
+ }
+ out_file.write(buffer.data(), buffer.size());
+ 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;
+}
+
+} // anonymous namespace
+
+int main(int argc, char **argv) {
+ Options options;
+ const int argc_check = argc - 1;
+
+ for (int i = 1; i < argc; ++i) {
+ if (!strcmp("-h", argv[i]) || !strcmp("-?", argv[i])) {
+ Usage();
+ return 0;
+ } else if (!strcmp("-i", argv[i]) && i < argc_check) {
+ options.input = argv[++i];
+ } else if (!strcmp("-o", argv[i]) && i < argc_check) {
+ options.output = argv[++i];
+ } else if (!strcmp("-point_cloud", argv[i])) {
+ options.is_point_cloud = true;
+ } else if (!strcmp("-qp", argv[i]) && i < argc_check) {
+ options.pos_quantization_bits = StringToInt(argv[++i]);
+ if (options.pos_quantization_bits > 30) {
+ printf(
+ "Error: The maximum number of quantization bits for the position "
+ "attribute is 30.\n");
+ return -1;
+ }
+ } else if (!strcmp("-qt", argv[i]) && i < argc_check) {
+ options.tex_coords_quantization_bits = StringToInt(argv[++i]);
+ if (options.tex_coords_quantization_bits > 30) {
+ printf(
+ "Error: The maximum number of quantization bits for the texture "
+ "coordinate attribute is 30.\n");
+ return -1;
+ }
+ } else if (!strcmp("-qn", argv[i]) && i < argc_check) {
+ options.normals_quantization_bits = StringToInt(argv[++i]);
+ if (options.normals_quantization_bits > 30) {
+ printf(
+ "Error: The maximum number of quantization bits for the normal "
+ "attribute is 30.\n");
+ return -1;
+ }
+ } else if (!strcmp("-qg", argv[i]) && i < argc_check) {
+ options.generic_quantization_bits = StringToInt(argv[++i]);
+ if (options.generic_quantization_bits > 30) {
+ printf(
+ "Error: The maximum number of quantization bits for generic "
+ "attributes is 30.\n");
+ return -1;
+ }
+ } else if (!strcmp("-cl", argv[i]) && i < argc_check) {
+ options.compression_level = StringToInt(argv[++i]);
+ } else if (!strcmp("--skip", argv[i]) && i < argc_check) {
+ if (!strcmp("NORMAL", argv[i + 1])) {
+ options.normals_quantization_bits = -1;
+ } else if (!strcmp("TEX_COORD", argv[i + 1])) {
+ options.tex_coords_quantization_bits = -1;
+ } else if (!strcmp("GENERIC", argv[i + 1])) {
+ options.generic_quantization_bits = -1;
+ } else {
+ printf("Error: Invalid attribute name after --skip\n");
+ return -1;
+ }
+ ++i;
+ } else if (!strcmp("--metadata", argv[i])) {
+ options.use_metadata = true;
+ }
+ }
+ if (argc < 3 || options.input.empty()) {
+ Usage();
+ return -1;
+ }
+
+ std::unique_ptr<draco::PointCloud> pc;
+ draco::Mesh *mesh = nullptr;
+ if (!options.is_point_cloud) {
+ auto maybe_mesh =
+ draco::ReadMeshFromFile(options.input, options.use_metadata);
+ if (!maybe_mesh.ok()) {
+ printf("Failed loading the input mesh: %s.\n",
+ maybe_mesh.status().error_msg());
+ return -1;
+ }
+ mesh = maybe_mesh.value().get();
+ pc = std::move(maybe_mesh).value();
+ } else {
+ auto maybe_pc = draco::ReadPointCloudFromFile(options.input);
+ if (!maybe_pc.ok()) {
+ printf("Failed loading the input point cloud: %s.\n",
+ maybe_pc.status().error_msg());
+ return -1;
+ }
+ pc = std::move(maybe_pc).value();
+ }
+
+ if (options.pos_quantization_bits < 0) {
+ printf("Error: Position attribute cannot be skipped.\n");
+ return -1;
+ }
+
+ // Delete attributes if needed. This needs to happen before we set any
+ // quantization settings.
+ if (options.tex_coords_quantization_bits < 0) {
+ if (pc->NumNamedAttributes(draco::GeometryAttribute::TEX_COORD) > 0) {
+ options.tex_coords_deleted = true;
+ }
+ while (pc->NumNamedAttributes(draco::GeometryAttribute::TEX_COORD) > 0) {
+ pc->DeleteAttribute(
+ pc->GetNamedAttributeId(draco::GeometryAttribute::TEX_COORD, 0));
+ }
+ }
+ if (options.normals_quantization_bits < 0) {
+ if (pc->NumNamedAttributes(draco::GeometryAttribute::NORMAL) > 0) {
+ options.normals_deleted = true;
+ }
+ while (pc->NumNamedAttributes(draco::GeometryAttribute::NORMAL) > 0) {
+ pc->DeleteAttribute(
+ pc->GetNamedAttributeId(draco::GeometryAttribute::NORMAL, 0));
+ }
+ }
+ if (options.generic_quantization_bits < 0) {
+ if (pc->NumNamedAttributes(draco::GeometryAttribute::GENERIC) > 0) {
+ options.generic_deleted = true;
+ }
+ while (pc->NumNamedAttributes(draco::GeometryAttribute::GENERIC) > 0) {
+ pc->DeleteAttribute(
+ pc->GetNamedAttributeId(draco::GeometryAttribute::GENERIC, 0));
+ }
+ }
+#ifdef DRACO_ATTRIBUTE_DEDUPLICATION_SUPPORTED
+ // If any attribute has been deleted, run deduplication of point indices again
+ // as some points can be possibly combined.
+ if (options.tex_coords_deleted || options.normals_deleted ||
+ options.generic_deleted) {
+ pc->DeduplicatePointIds();
+ }
+#endif
+
+ // Convert compression level to speed (that 0 = slowest, 10 = fastest).
+ const int speed = 10 - options.compression_level;
+
+ draco::Encoder encoder;
+
+ // Setup encoder options.
+ if (options.pos_quantization_bits > 0) {
+ encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION,
+ options.pos_quantization_bits);
+ }
+ if (options.tex_coords_quantization_bits > 0) {
+ encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD,
+ options.tex_coords_quantization_bits);
+ }
+ if (options.normals_quantization_bits > 0) {
+ encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL,
+ options.normals_quantization_bits);
+ }
+ if (options.generic_quantization_bits > 0) {
+ encoder.SetAttributeQuantization(draco::GeometryAttribute::GENERIC,
+ options.generic_quantization_bits);
+ }
+ encoder.SetSpeedOptions(speed, speed);
+
+ if (options.output.empty()) {
+ // Create a default output file by attaching .drc to the input file name.
+ options.output = options.input + ".drc";
+ }
+
+ PrintOptions(*pc.get(), options);
+
+ int ret = -1;
+ const bool input_is_mesh = mesh && mesh->num_faces() > 0;
+ if (input_is_mesh)
+ ret = EncodeMeshToFile(*mesh, options.output, &encoder);
+ else
+ ret = EncodePointCloudToFile(*pc.get(), options.output, &encoder);
+
+ if (ret != -1 && options.compression_level < 10) {
+ printf(
+ "For better compression, increase the compression level up to '-cl 10' "
+ ".\n\n");
+ }
+
+ return ret;
+}
diff --git a/extern/draco/src/draco-compressor.cpp b/extern/draco/src/draco-compressor.cpp
new file mode 100644
index 00000000000..9e63b8d35cb
--- /dev/null
+++ b/extern/draco/src/draco-compressor.cpp
@@ -0,0 +1,346 @@
+/*
+ * 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.
+ */
+
+/**
+ * Implemententation for the Draco exporter from the C++ side.
+ *
+ * The python side uses the CTypes libary to open the DLL, load function
+ * pointers add pass the data to the compressor as raw bytes.
+ *
+ * The compressor intercepts the regular GLTF exporter after data has been
+ * gathered and right before the data is converted to a JSON representation,
+ * which is going to be written out.
+ *
+ * The original uncompressed data is removed and replaces an extension,
+ * pointing to the newly created buffer containing the compressed data.
+ *
+ * @author Jim Eckerlein <eckerlein@ux3d.io>
+ * @date 2019-01-15
+ */
+
+#include <iostream>
+#include <fstream>
+#include <memory>
+#include <sstream>
+
+#include "draco/mesh/mesh.h"
+#include "draco/point_cloud/point_cloud.h"
+#include "draco/core/vector_d.h"
+#include "draco/io/mesh_io.h"
+
+#if defined(_MSC_VER)
+#define DLL_EXPORT(retType) extern "C" __declspec(dllexport) retType __cdecl
+#else
+#define DLL_EXPORT(retType) extern "C" retType
+#endif
+
+const char *logTag = "DRACO-COMPRESSOR";
+
+/**
+ * This tuple is opaquely exposed to Python through a pointer.
+ * It encapsulates the complete current compressor state.
+ *
+ * A single instance is only intended to compress a single primitive.
+ */
+struct DracoCompressor {
+
+ /**
+ * All positions, normals and texture coordinates are appended to this mesh.
+ */
+ draco::Mesh mesh;
+
+ /**
+ * One data buffer per attribute.
+ */
+ std::vector<std::unique_ptr<draco::DataBuffer>> buffers;
+
+ /**
+ * The buffer the mesh is compressed into.
+ */
+ draco::EncoderBuffer encoderBuffer;
+
+ /**
+ * The id Draco assigns to the position attribute.
+ * Required to be reported in the GLTF file.
+ */
+ uint32_t positionAttributeId = (uint32_t) -1;
+
+ /**
+ * The id Draco assigns to the normal attribute.
+ * Required to be reported in the GLTF file.
+ */
+ uint32_t normalAttributeId = (uint32_t) -1;
+
+ /**
+ * The ids Draco assigns to the texture coordinate attributes.
+ * Required to be reported in the GLTF file.
+ */
+ std::vector<uint32_t> texCoordAttributeIds;
+
+ /**
+ * Level of compression [0-10].
+ * Higher values mean slower encoding.
+ */
+ uint32_t compressionLevel = 7;
+
+ uint32_t quantizationBitsPosition = 14;
+ uint32_t quantizationBitsNormal = 10;
+ uint32_t quantizationBitsTexCoord = 12;
+};
+
+draco::GeometryAttribute createAttribute(
+ draco::GeometryAttribute::Type type,
+ draco::DataBuffer &buffer,
+ uint8_t components
+) {
+ draco::GeometryAttribute attribute;
+ attribute.Init(
+ type,
+ &buffer,
+ components,
+ draco::DataType::DT_FLOAT32,
+ false,
+ sizeof(float) * components,
+ 0
+ );
+ return attribute;
+}
+
+DLL_EXPORT(DracoCompressor *) createCompressor() {
+ return new DracoCompressor;
+}
+
+DLL_EXPORT(void) setCompressionLevel(
+ DracoCompressor *compressor,
+ uint32_t compressionLevel
+) {
+ compressor->compressionLevel = compressionLevel;
+}
+
+DLL_EXPORT(void) setPositionQuantizationBits(
+ DracoCompressor *compressor,
+ uint32_t quantizationBitsPosition
+) {
+ compressor->quantizationBitsPosition = quantizationBitsPosition;
+}
+
+DLL_EXPORT(void) setNormalQuantizationBits(
+ DracoCompressor *compressor,
+ uint32_t quantizationBitsNormal
+) {
+ compressor->quantizationBitsNormal = quantizationBitsNormal;
+}
+
+DLL_EXPORT(void) setTexCoordQuantizationBits(
+ DracoCompressor *compressor,
+ uint32_t quantizationBitsTexCoord
+) {
+ compressor->quantizationBitsTexCoord = quantizationBitsTexCoord;
+}
+
+DLL_EXPORT(bool) compress(
+ DracoCompressor *compressor
+) {
+ printf("%s: Compressing primitive:\n", logTag);
+ printf("%s: Compression level [0-10]: %d\n", logTag, compressor->compressionLevel);
+ printf("%s: Position quantization bits: %d\n", logTag, compressor->quantizationBitsPosition);
+ printf("%s: Normal quantization bits: %d\n", logTag, compressor->quantizationBitsNormal);
+ printf("%s: Position quantization bits: %d\n", logTag, compressor->quantizationBitsTexCoord);
+
+ draco::ExpertEncoder encoder(compressor->mesh);
+
+ encoder.SetSpeedOptions(10 - compressor->compressionLevel, 10 - compressor->compressionLevel);
+ encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, compressor->quantizationBitsPosition);
+ encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, compressor->quantizationBitsNormal);
+ encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, compressor->quantizationBitsTexCoord);
+
+ encoder.SetEncodingMethod(draco::MESH_EDGEBREAKER_ENCODING);
+
+ draco::Status result = encoder.EncodeToBuffer(&compressor->encoderBuffer);
+
+ if(!result.ok()) {
+ printf("%s: Could not compress mesh: %s\n", logTag, result.error_msg());
+ return false;
+ }
+ else {
+ return true;
+ }
+}
+
+/**
+ * Returns the size of the compressed data in bytes.
+ */
+DLL_EXPORT(uint64_t) compressedSize(
+ DracoCompressor *compressor
+) {
+ return compressor->encoderBuffer.size();
+}
+
+/**
+ * Copies the compressed mesh into the given byte buffer.
+ * @param[o_data] A Python `bytes` object.
+ *
+ */
+DLL_EXPORT(void) copyToBytes(
+ DracoCompressor *compressor,
+ uint8_t *o_data
+) {
+ memcpy(o_data, compressor->encoderBuffer.data(), compressedSize(compressor));
+}
+
+DLL_EXPORT(uint32_t) getPositionAttributeId(
+ DracoCompressor *compressor
+) {
+ return compressor->positionAttributeId;
+}
+
+DLL_EXPORT(uint32_t) getNormalAttributeId(
+ DracoCompressor *compressor
+) {
+ return compressor->normalAttributeId;
+}
+
+DLL_EXPORT(uint32_t) getTexCoordAttributeIdCount(
+ DracoCompressor *compressor
+) {
+ return (uint32_t) compressor->texCoordAttributeIds.size();
+}
+
+DLL_EXPORT(uint32_t) getTexCoordAttributeId(
+ DracoCompressor *compressor,
+ uint32_t index
+) {
+ return compressor->texCoordAttributeIds[index];
+}
+
+/**
+ * Releases all memory allocated by the compressor.
+ */
+DLL_EXPORT(void) disposeCompressor(
+ DracoCompressor *compressor
+) {
+ delete compressor;
+}
+
+template<class T>
+void setFaces(
+ draco::Mesh &mesh,
+ int numIndices,
+ T *indices
+) {
+ mesh.SetNumFaces((size_t) numIndices / 3);
+
+ for (int i = 0; i < numIndices; i += 3)
+ {
+ const auto a = draco::PointIndex(indices[i]);
+ const auto b = draco::PointIndex(indices[i + 1]);
+ const auto c = draco::PointIndex(indices[i + 2]);
+ mesh.SetFace(draco::FaceIndex((uint32_t) i), {a, b, c});
+ }
+}
+
+DLL_EXPORT(void) setFaces(
+ DracoCompressor *compressor,
+ uint32_t numIndices,
+ uint32_t bytesPerIndex,
+ void *indices
+) {
+ switch (bytesPerIndex)
+ {
+ case 1:
+ {
+ setFaces(compressor->mesh, numIndices, (uint8_t *) indices);
+ break;
+ }
+ case 2:
+ {
+ setFaces(compressor->mesh, numIndices, (uint16_t *) indices);
+ break;
+ }
+ case 4:
+ {
+ setFaces(compressor->mesh, numIndices, (uint32_t *) indices);
+ break;
+ }
+ default:
+ {
+ printf("%s: Unsupported index size %d\n", logTag, bytesPerIndex);
+ break;
+ }
+ }
+}
+
+void addFloatAttribute(
+ DracoCompressor *compressor,
+ draco::GeometryAttribute::Type type,
+ uint32_t count,
+ uint8_t componentCount,
+ float *source
+) {
+ auto buffer = std::make_unique<draco::DataBuffer>();
+
+ const auto attribute = createAttribute(type, *buffer, componentCount);
+
+ const auto id = (const uint32_t) compressor->mesh.AddAttribute(attribute, false, count);
+ compressor->mesh.attribute(id)->SetIdentityMapping();
+
+ switch (type)
+ {
+ case draco::GeometryAttribute::POSITION:
+ compressor->positionAttributeId = id;
+ break;
+ case draco::GeometryAttribute::NORMAL:
+ compressor->normalAttributeId = id;
+ break;
+ case draco::GeometryAttribute::TEX_COORD:
+ compressor->texCoordAttributeIds.push_back(id);
+ break;
+ default:
+ break;
+ }
+
+ for (uint32_t i = 0; i < count; i++)
+ {
+ compressor->mesh.attribute(id)->SetAttributeValue(
+ draco::AttributeValueIndex(i),
+ source + i * componentCount
+ );
+ }
+
+ compressor->buffers.emplace_back(std::move(buffer));
+}
+
+DLL_EXPORT(void) addPositionAttribute(
+ DracoCompressor *compressor,
+ uint32_t count,
+ float *source
+) {
+ addFloatAttribute(compressor, draco::GeometryAttribute::POSITION, count, 3, source);
+}
+
+DLL_EXPORT(void) addNormalAttribute(
+ DracoCompressor *compressor,
+ uint32_t count,
+ float *source
+) {
+ addFloatAttribute(compressor, draco::GeometryAttribute::NORMAL, count, 3, source);
+}
+
+DLL_EXPORT(void) addTexCoordAttribute(
+ DracoCompressor *compressor,
+ uint32_t count,
+ float *source
+) {
+ addFloatAttribute(compressor, draco::GeometryAttribute::TEX_COORD, count, 2, source);
+}
diff --git a/extern/gflags/CMakeLists.txt b/extern/gflags/CMakeLists.txt
index da24e5504a3..13c3a2a03a9 100644
--- a/extern/gflags/CMakeLists.txt
+++ b/extern/gflags/CMakeLists.txt
@@ -40,6 +40,9 @@ set(SRC
src/util.h
)
+set(LIB
+)
+
if(WIN32)
list(APPEND SRC
src/windows_port.cc
@@ -49,4 +52,4 @@ endif()
add_definitions(${GFLAGS_DEFINES})
-blender_add_lib(extern_gflags "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(extern_gflags "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/glew-es/CMakeLists.txt b/extern/glew-es/CMakeLists.txt
index 8f14855bf4f..5bb46d3f653 100644
--- a/extern/glew-es/CMakeLists.txt
+++ b/extern/glew-es/CMakeLists.txt
@@ -42,6 +42,9 @@ set(SRC
include/GL/wglew.h
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(extern_glew_es "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(extern_glew_es "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/glew/CMakeLists.txt b/extern/glew/CMakeLists.txt
index 47d38586d1a..b84b0a30199 100644
--- a/extern/glew/CMakeLists.txt
+++ b/extern/glew/CMakeLists.txt
@@ -41,6 +41,9 @@ set(SRC
include/GL/wglew.h
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(extern_glew "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(extern_glew "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/glog/CMakeLists.txt b/extern/glog/CMakeLists.txt
index 82b415d06d7..1b1abbfc735 100644
--- a/extern/glog/CMakeLists.txt
+++ b/extern/glog/CMakeLists.txt
@@ -53,6 +53,9 @@ set(SRC
src/stacktrace_x86-inl.h
)
+set(LIB
+)
+
if(WIN32)
list(APPEND SRC
src/windows/port.cc
@@ -90,4 +93,4 @@ endif()
add_definitions(${GFLAGS_DEFINES})
add_definitions(${GLOG_DEFINES})
-blender_add_lib(extern_glog "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(extern_glog "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/gtest/CMakeLists.txt b/extern/gtest/CMakeLists.txt
index 22b47e76b12..f33e012b0f4 100644
--- a/extern/gtest/CMakeLists.txt
+++ b/extern/gtest/CMakeLists.txt
@@ -66,4 +66,7 @@ set(SRC
include/gtest/internal/gtest-type-util.h
)
-blender_add_lib(extern_gtest "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(extern_gtest "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/lzma/CMakeLists.txt b/extern/lzma/CMakeLists.txt
index d4fe8ade9ba..8be848f83ce 100644
--- a/extern/lzma/CMakeLists.txt
+++ b/extern/lzma/CMakeLists.txt
@@ -42,4 +42,7 @@ set(SRC
Types.h
)
-blender_add_lib(extern_lzma "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(extern_lzma "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/lzo/CMakeLists.txt b/extern/lzo/CMakeLists.txt
index e4609963d29..7eebc92c46d 100644
--- a/extern/lzo/CMakeLists.txt
+++ b/extern/lzo/CMakeLists.txt
@@ -36,4 +36,7 @@ set(SRC
minilzo/minilzo.h
)
-blender_add_lib(extern_minilzo "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(extern_minilzo "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/rangetree/CMakeLists.txt b/extern/rangetree/CMakeLists.txt
index 77c293e851a..cb0b424a023 100644
--- a/extern/rangetree/CMakeLists.txt
+++ b/extern/rangetree/CMakeLists.txt
@@ -27,4 +27,7 @@ set(SRC
intern/range_tree.c
)
-blender_add_lib(extern_rangetree "${SRC}" "${INC}" "")
+set(LIB
+)
+
+blender_add_lib(extern_rangetree "${SRC}" "${INC}" "" "${LIB}")
diff --git a/extern/sdlew/CMakeLists.txt b/extern/sdlew/CMakeLists.txt
index c0cd9944755..5698d87108b 100644
--- a/extern/sdlew/CMakeLists.txt
+++ b/extern/sdlew/CMakeLists.txt
@@ -32,4 +32,7 @@ set(SRC
src/sdlew.c
)
-blender_add_lib(extern_sdlew "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(extern_sdlew "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/wcwidth/CMakeLists.txt b/extern/wcwidth/CMakeLists.txt
index 29a2977c3eb..384a2c4f58c 100644
--- a/extern/wcwidth/CMakeLists.txt
+++ b/extern/wcwidth/CMakeLists.txt
@@ -32,4 +32,7 @@ set(SRC
wcwidth.h
)
-blender_add_lib(extern_wcwidth "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(extern_wcwidth "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/extern/xdnd/CMakeLists.txt b/extern/xdnd/CMakeLists.txt
index 9a24606d133..54d6648c2a6 100644
--- a/extern/xdnd/CMakeLists.txt
+++ b/extern/xdnd/CMakeLists.txt
@@ -31,8 +31,11 @@ set(SRC
xdnd.h
)
+set(LIB
+)
+
add_definitions(
-DHAVE_SYS_TIME_H
)
-blender_add_lib(extern_xdnd "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(extern_xdnd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/audaspace/CMakeLists.txt b/intern/audaspace/CMakeLists.txt
index e359a631532..6b828b204f1 100644
--- a/intern/audaspace/CMakeLists.txt
+++ b/intern/audaspace/CMakeLists.txt
@@ -38,6 +38,9 @@ endif()
intern/AUD_Set.h
)
+set(LIB
+)
+
if(WITH_PYTHON)
list(APPEND INC_SYS
${PYTHON_INCLUDE_DIRS}
@@ -49,4 +52,4 @@ if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
endif()
-blender_add_lib(bf_intern_audaspace "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_audaspace "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/clog/CMakeLists.txt b/intern/clog/CMakeLists.txt
index 479723c4cd6..3ca9094dae6 100644
--- a/intern/clog/CMakeLists.txt
+++ b/intern/clog/CMakeLists.txt
@@ -32,7 +32,10 @@ set(SRC
CLG_log.h
)
+set(LIB
+)
+
# Disabled for makesdna/makesrna.
add_definitions(-DWITH_CLOG_PTHREADS)
-blender_add_lib(bf_intern_clog "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_clog "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt
index f8720de366f..3f8868d793b 100644
--- a/intern/cycles/blender/CMakeLists.txt
+++ b/intern/cycles/blender/CMakeLists.txt
@@ -38,6 +38,9 @@ set(SRC
blender_util.h
)
+set(LIB
+)
+
set(ADDON_FILES
addon/__init__.py
addon/engine.py
@@ -59,7 +62,7 @@ if(WITH_CYCLES_NETWORK)
add_definitions(-DWITH_NETWORK)
endif()
-blender_add_lib(bf_intern_cycles "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_cycles "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# avoid link failure with clang 3.4 debug
if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT ${CMAKE_C_COMPILER_VERSION} VERSION_LESS '3.4')
diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp
index 03dc21b91c9..eadbe3eda89 100644
--- a/intern/cycles/render/mesh.cpp
+++ b/intern/cycles/render/mesh.cpp
@@ -2125,7 +2125,7 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
mesh->add_undisplaced();
}
- /* Test if we need tesselation. */
+ /* Test if we need tessellation. */
if(mesh->subdivision_type != Mesh::SUBDIVISION_NONE &&
mesh->num_subd_verts == 0 &&
mesh->subd_params)
diff --git a/intern/dualcon/CMakeLists.txt b/intern/dualcon/CMakeLists.txt
index bd8c2eff504..a5c1137bc65 100644
--- a/intern/dualcon/CMakeLists.txt
+++ b/intern/dualcon/CMakeLists.txt
@@ -45,4 +45,7 @@ set(SRC
dualcon.h
)
-blender_add_lib(bf_intern_dualcon "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_intern_dualcon "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/eigen/CMakeLists.txt b/intern/eigen/CMakeLists.txt
index 6cdd7e55f0a..650a1aa63c1 100644
--- a/intern/eigen/CMakeLists.txt
+++ b/intern/eigen/CMakeLists.txt
@@ -40,4 +40,7 @@ set(SRC
intern/svd.h
)
-blender_add_lib(bf_intern_eigen "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_intern_eigen "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/elbeem/CMakeLists.txt b/intern/elbeem/CMakeLists.txt
index 6a9b307aee4..d70f3939a40 100644
--- a/intern/elbeem/CMakeLists.txt
+++ b/intern/elbeem/CMakeLists.txt
@@ -85,6 +85,9 @@ set(SRC
intern/globals.h
)
+set(LIB
+)
+
# elbeem has some harmless UNUSED warnings
remove_strict_flags()
@@ -116,4 +119,4 @@ else()
add_definitions(-DPARALLEL=0)
endif()
-blender_add_lib_nolist(bf_intern_elbeem "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib_nolist(bf_intern_elbeem "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 74d5af9a6c1..eeec5ab1528 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -84,6 +84,9 @@ set(SRC
intern/GHOST_WindowManager.h
)
+set(LIB
+)
+
if(WITH_GHOST_DEBUG)
list(APPEND SRC
intern/GHOST_EventPrinter.cpp
@@ -337,4 +340,4 @@ endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_intern_ghost "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_ghost "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index b847cace57c..8b270c1d972 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -648,7 +648,7 @@ GHOST_IWindow* GHOST_SystemCocoa::createWindow(
/**
* Create a new offscreen context.
- * Never explicitly delete the context, use disposeContext() instead.
+ * Never explicitly delete the context, use #disposeContext() instead.
* \return The new context (or 0 if creation failed).
*/
GHOST_IContext *
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 12d58b0a9cb..35a9cfac817 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -304,8 +304,8 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow(
/**
* Create a new offscreen context.
- * Never explicitly delete the window, use disposeContext() instead.
- * \return The new context (or 0 if creation failed).
+ * Never explicitly delete the window, use #disposeContext() instead.
+ * \return The new context (or 0 if creation failed).
*/
GHOST_IContext *GHOST_SystemWin32::createOffscreenContext()
{
diff --git a/intern/glew-mx/CMakeLists.txt b/intern/glew-mx/CMakeLists.txt
index c24a741771d..f0b0b2d0ee7 100644
--- a/intern/glew-mx/CMakeLists.txt
+++ b/intern/glew-mx/CMakeLists.txt
@@ -34,6 +34,9 @@ set(SRC
intern/symbol-binding.h
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_intern_glew_mx "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_glew_mx "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/guardedalloc/CMakeLists.txt b/intern/guardedalloc/CMakeLists.txt
index 3f07d46529c..72c3395156e 100644
--- a/intern/guardedalloc/CMakeLists.txt
+++ b/intern/guardedalloc/CMakeLists.txt
@@ -40,6 +40,9 @@ set(SRC
../atomic/atomic_ops.h
)
+set(LIB
+)
+
if(WIN32 AND NOT UNIX)
list(APPEND SRC
intern/mmap_win.c
@@ -53,12 +56,12 @@ if(WITH_MEM_JEMALLOC AND NOT ("${JEMALLOC_VERSION}" VERSION_LESS "5.0.0"))
add_definitions(-DWITH_JEMALLOC_CONF)
endif()
-blender_add_lib(bf_intern_guardedalloc "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_guardedalloc "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# Override C++ alloc, optional.
if(WITH_CXX_GUARDEDALLOC)
set(SRC
cpp/mallocn.cpp
)
- blender_add_lib(bf_intern_guardedalloc_cpp "${SRC}" "${INC}" "${INC_SYS}")
+ blender_add_lib(bf_intern_guardedalloc_cpp "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
endif()
diff --git a/intern/iksolver/CMakeLists.txt b/intern/iksolver/CMakeLists.txt
index 4e9cc7c145b..362e6e2bb6b 100644
--- a/intern/iksolver/CMakeLists.txt
+++ b/intern/iksolver/CMakeLists.txt
@@ -42,4 +42,7 @@ set(SRC
intern/IK_QTask.h
)
-blender_add_lib(bf_intern_iksolver "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_intern_iksolver "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/itasc/CMakeLists.txt b/intern/itasc/CMakeLists.txt
index db711d7eb2a..c0f48bcf957 100644
--- a/intern/itasc/CMakeLists.txt
+++ b/intern/itasc/CMakeLists.txt
@@ -353,6 +353,9 @@ set(SRC
${EIGEN3_HEADERS}
)
+set(LIB
+)
+
unset(EIGEN3_HEADERS)
-blender_add_lib(bf_intern_itasc "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_itasc "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/libmv/CMakeLists.txt b/intern/libmv/CMakeLists.txt
index 98edc1492c5..5adfdbea1af 100644
--- a/intern/libmv/CMakeLists.txt
+++ b/intern/libmv/CMakeLists.txt
@@ -192,7 +192,7 @@ if(WITH_LIBMV)
if(WITH_GTESTS)
- blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "" "")
+ blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "" "" "")
BLENDER_SRC_GTEST("libmv_predict_tracks" "./libmv/autotrack/predict_tracks_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_tracks" "./libmv/autotrack/tracks_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
@@ -232,4 +232,4 @@ else()
)
endif()
-blender_add_lib(bf_intern_libmv "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_libmv "${SRC}" "${INC}" "${INC_SYS}" "")
diff --git a/intern/libmv/bundle.sh b/intern/libmv/bundle.sh
index 093095f5f5c..98a1e364634 100755
--- a/intern/libmv/bundle.sh
+++ b/intern/libmv/bundle.sh
@@ -174,7 +174,7 @@ ${third_headers}
if(WITH_GTESTS)
- blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "${INC}" "${INC_SYS}")
+ blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "${INC}" "${INC_SYS}" "")
${tests}
endif()
@@ -184,5 +184,5 @@ else()
)
endif()
-blender_add_lib(bf_intern_libmv "\${SRC}" "\${INC}" "\${INC_SYS}")
+blender_add_lib(bf_intern_libmv "\${SRC}" "\${INC}" "\${INC_SYS}" "")
EOF
diff --git a/intern/locale/CMakeLists.txt b/intern/locale/CMakeLists.txt
index b4d142e3efb..31b9e690b29 100644
--- a/intern/locale/CMakeLists.txt
+++ b/intern/locale/CMakeLists.txt
@@ -31,6 +31,9 @@ set(SRC
boost_locale_wrapper.h
)
+set(LIB
+)
+
if(APPLE)
# Cocoa code to read the locale on OSX
list(APPEND SRC
@@ -54,4 +57,4 @@ if(WITH_INTERNATIONAL)
add_definitions(${BOOST_DEFINITIONS})
endif()
-blender_add_lib(bf_intern_locale "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_locale "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/memutil/CMakeLists.txt b/intern/memutil/CMakeLists.txt
index e75443c5003..dc1cf0e0ebf 100644
--- a/intern/memutil/CMakeLists.txt
+++ b/intern/memutil/CMakeLists.txt
@@ -38,4 +38,7 @@ set(SRC
MEM_RefCountedC-Api.h
)
-blender_add_lib(bf_intern_memutil "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_intern_memutil "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/mikktspace/CMakeLists.txt b/intern/mikktspace/CMakeLists.txt
index f4755916ccc..1dc508a66a4 100644
--- a/intern/mikktspace/CMakeLists.txt
+++ b/intern/mikktspace/CMakeLists.txt
@@ -39,4 +39,7 @@ set(SRC
mikktspace.h
)
-blender_add_lib(bf_intern_mikktspace "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_intern_mikktspace "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/numaapi/CMakeLists.txt b/intern/numaapi/CMakeLists.txt
index 6d8edb5e1eb..cc4fb27bdb7 100644
--- a/intern/numaapi/CMakeLists.txt
+++ b/intern/numaapi/CMakeLists.txt
@@ -34,6 +34,9 @@ set(SRC
source/build_config.h
)
+set(LIB
+)
+
add_definitions(-DWITH_DYNLOAD)
-blender_add_lib(bf_intern_numaapi "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_numaapi "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/opencolorio/CMakeLists.txt b/intern/opencolorio/CMakeLists.txt
index 8a4423b8e67..48d619d8d71 100644
--- a/intern/opencolorio/CMakeLists.txt
+++ b/intern/opencolorio/CMakeLists.txt
@@ -38,6 +38,9 @@ set(SRC
ocio_impl.h
)
+set(LIB
+)
+
if(WITH_OPENCOLORIO)
add_definitions(
-DWITH_OCIO
@@ -69,4 +72,4 @@ if(WITH_OPENCOLORIO)
endif()
-blender_add_lib(bf_intern_opencolorio "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_opencolorio "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/opensubdiv/CMakeLists.txt b/intern/opensubdiv/CMakeLists.txt
index 66ed4d174a0..62ecb314a80 100644
--- a/intern/opensubdiv/CMakeLists.txt
+++ b/intern/opensubdiv/CMakeLists.txt
@@ -35,6 +35,9 @@ set(SRC
opensubdiv_topology_refiner_capi.h
)
+set(LIB
+)
+
if(WITH_OPENSUBDIV)
macro(OPENSUBDIV_DEFINE_COMPONENT component)
if(${${component}})
@@ -122,4 +125,4 @@ else()
)
endif()
-blender_add_lib(bf_intern_opensubdiv "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_opensubdiv "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/openvdb/CMakeLists.txt b/intern/openvdb/CMakeLists.txt
index ddec43f30a3..2d72febd3cf 100644
--- a/intern/openvdb/CMakeLists.txt
+++ b/intern/openvdb/CMakeLists.txt
@@ -30,6 +30,9 @@ set(SRC
openvdb_capi.h
)
+set(LIB
+)
+
if(WITH_OPENVDB)
add_definitions(
-DWITH_OPENVDB
@@ -69,4 +72,4 @@ if(WITH_OPENVDB)
endif()
endif()
-blender_add_lib(bf_intern_openvdb "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_openvdb "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/rigidbody/CMakeLists.txt b/intern/rigidbody/CMakeLists.txt
index 817838a1642..02841c134c0 100644
--- a/intern/rigidbody/CMakeLists.txt
+++ b/intern/rigidbody/CMakeLists.txt
@@ -32,4 +32,7 @@ set(SRC
RBI_api.h
)
-blender_add_lib(bf_intern_rigidbody "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_intern_rigidbody "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/smoke/CMakeLists.txt b/intern/smoke/CMakeLists.txt
index 0e9c4572bb1..232183fe33c 100644
--- a/intern/smoke/CMakeLists.txt
+++ b/intern/smoke/CMakeLists.txt
@@ -77,6 +77,9 @@ set(SRC
intern/tnt/tnt_version.h
)
+set(LIB
+)
+
# quiet -Wundef
add_definitions(-DDDF_DEBUG=0)
@@ -93,4 +96,4 @@ if(WITH_FFTW3)
)
endif()
-blender_add_lib(bf_intern_smoke "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_smoke "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/string/CMakeLists.txt b/intern/string/CMakeLists.txt
index 7a0d45f4cc4..3f436c79a69 100644
--- a/intern/string/CMakeLists.txt
+++ b/intern/string/CMakeLists.txt
@@ -32,4 +32,7 @@ set(SRC
STR_String.h
)
-blender_add_lib(bf_intern_string "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_intern_string "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/intern/utfconv/CMakeLists.txt b/intern/utfconv/CMakeLists.txt
index 118d1710e53..9450461f50b 100644
--- a/intern/utfconv/CMakeLists.txt
+++ b/intern/utfconv/CMakeLists.txt
@@ -30,6 +30,9 @@ set(SRC
utfconv.h
)
+set(LIB
+)
+
# This is odd but leave it for now...
# Why have win32 check here? - this is only used for windows.
# ... because one day we might want to use it on other platforms.
@@ -40,4 +43,4 @@ if(WIN32)
)
endif()
-blender_add_lib(bf_intern_utfconv "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_intern_utfconv "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/readme.rst b/readme.rst
new file mode 100644
index 00000000000..aadc766b22f
--- /dev/null
+++ b/readme.rst
@@ -0,0 +1,41 @@
+
+.. Keep this document short & concise,
+ linking to external resources instead of including content in-line.
+ See 'release/text/readme.html' for the end user read-me.
+
+
+Blender
+=======
+
+Blender is the free and open source 3D creation suite.
+It supports the entirety of the 3D pipeline-modeling, rigging, animation, simulation, rendering, compositing,
+motion tracking and video editing.
+
+.. figure:: https://code.blender.org/wp-content/uploads/2018/12/springrg.jpg
+ :scale: 50 %
+ :align: center
+
+
+Project Pages
+-------------
+
+- `Main Website <http://www.blender.org>`__
+- `Reference Manual <https://docs.blender.org/manual/en/latest/index.html>`__
+- `User Community <https://www.blender.org/community/>`__
+
+Development
+-----------
+
+- `Build Instructions <https://wiki.blender.org/wiki/Building_Blender>`__
+- `Code Review & Bug Tracker <https://developer.blender.org>`__
+- `Developer Forum <https://devtalk.blender.org>`__
+- `Developer Documentation <https://wiki.blender.org>`__
+
+
+License
+-------
+
+Blender as a whole is licensed under the GNU Public License, Version 3.
+Individual files may have a different, but compatible license.
+
+See `blender.org/about/license <https://www.blender.org/about/license>`__ for details.
diff --git a/release/datafiles/preview.blend b/release/datafiles/preview.blend
new file mode 100644
index 00000000000..f8fafb285c6
--- /dev/null
+++ b/release/datafiles/preview.blend
Binary files differ
diff --git a/release/datafiles/preview_cycles.blend b/release/datafiles/preview_cycles.blend
deleted file mode 100644
index 60c0ed5f763..00000000000
--- a/release/datafiles/preview_cycles.blend
+++ /dev/null
Binary files differ
diff --git a/release/scripts/presets/keyconfig/industry_compatible.py b/release/scripts/presets/keyconfig/industry_compatible.py
new file mode 100644
index 00000000000..8ce38d4b40e
--- /dev/null
+++ b/release/scripts/presets/keyconfig/industry_compatible.py
@@ -0,0 +1,91 @@
+import os
+import bpy
+
+# ------------------------------------------------------------------------------
+# Operators needed by this keymap to function
+
+# Selection Modes
+
+class IC_KEYMAP_OT_mesh_select_mode(bpy.types.Operator):
+ bl_idname = "ic_keymap.mesh_select_mode"
+ bl_label = "Switch to Vertex, Edge or Face Mode from any mode"
+ bl_options = {'UNDO'}
+
+ type: bpy.props.EnumProperty(
+ name="Mode",
+ items=(
+ ('VERT', "Vertex", "Switcth to Vertex Mode From any Mode"),
+ ('EDGE', "Edge", "Switcth to Edge Mode From any Mode"),
+ ('FACE', "Face", "Switcth to Face Mode From any Mode"),
+ ),
+ )
+
+ @classmethod
+ def poll(cls, context):
+ return (context.active_object is not None) and (context.object.type == 'MESH')
+
+ def execute(self, context):
+ bpy.ops.object.mode_set(mode='EDIT')
+ bpy.ops.mesh.select_mode(type=self.type)
+
+ return{'FINISHED'}
+
+
+classes = (
+ IC_KEYMAP_OT_mesh_select_mode,
+)
+
+
+# ------------------------------------------------------------------------------
+# Keymap
+
+dirname, filename = os.path.split(__file__)
+idname = os.path.splitext(filename)[0]
+
+def update_fn(_self, _context):
+ load()
+
+
+def keyconfig_data_oskey_from_ctrl(keyconfig_data_src):
+ # TODO, make into more generic event transforming function.
+ keyconfig_data_dst = []
+ for km_name, km_parms, km_items_data_src in keyconfig_data_src:
+ km_items_data_dst = km_items_data_src.copy()
+ items_dst = []
+ km_items_data_dst["items"] = items_dst
+ for item_src in km_items_data_src["items"]:
+ item_op, item_event, item_prop = item_src
+ if "ctrl" in item_event:
+ item_event = item_event.copy()
+ item_event["oskey"] = item_event["ctrl"]
+ del item_event["ctrl"]
+ items_dst.append((item_op, item_event, item_prop))
+ items_dst.append(item_src)
+ keyconfig_data_dst.append((km_name, km_parms, km_items_data_dst))
+ return keyconfig_data_dst
+
+
+industry_compatible = bpy.utils.execfile(os.path.join(dirname, "keymap_data", "industry_compatible_data.py"))
+
+
+def load():
+ from sys import platform
+ from bl_keymap_utils.io import keyconfig_init_from_data
+
+ prefs = bpy.context.preferences
+
+ kc = bpy.context.window_manager.keyconfigs.new(idname)
+ params = industry_compatible.Params(use_mouse_emulate_3_button=prefs.inputs.use_mouse_emulate_3_button)
+ keyconfig_data = industry_compatible.generate_keymaps(params)
+
+ if platform == 'darwin':
+ keyconfig_data = keyconfig_data_oskey_from_ctrl(keyconfig_data)
+
+ keyconfig_init_from_data(kc, keyconfig_data)
+
+if __name__ == "__main__":
+ # XXX, no way to unregister
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ load()
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 9d4fcf7d0e7..4094bb23e50 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -1116,11 +1116,9 @@ def km_view3d(params):
# New pie menus.
items.extend([
("wm.context_toggle", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "ctrl": True},
- {"properties": [("data_path", 'space_data.show_gizmo_tool')]}),
+ {"properties": [("data_path", 'space_data.show_gizmo_context')]}),
op_menu_pie("VIEW3D_MT_pivot_pie", {"type": 'PERIOD', "value": 'PRESS'}),
op_menu_pie("VIEW3D_MT_orientations_pie", {"type": 'COMMA', "value": 'PRESS'}),
- ("wm.context_toggle", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "ctrl": True},
- {"properties": [("data_path", 'space_data.show_gizmo_tool')]}),
op_menu_pie(
"VIEW3D_MT_shading_pie" if not params.use_v3d_shade_ex_pie else
"VIEW3D_MT_shading_ex_pie",
@@ -1177,7 +1175,7 @@ def km_view3d(params):
("wm.context_toggle", {"type": 'COMMA', "value": 'PRESS', "alt": True},
{"properties": [("data_path", 'tool_settings.use_transform_pivot_point_align')]}),
("wm.context_toggle", {"type": 'SPACE', "value": 'PRESS', "ctrl": True},
- {"properties": [("data_path", 'space_data.show_gizmo_tool')]}),
+ {"properties": [("data_path", 'space_data.show_gizmo_context')]}),
("wm.context_set_enum", {"type": 'PERIOD', "value": 'PRESS'},
{"properties": [("data_path", 'tool_settings.transform_pivot_point'), ("value", 'CURSOR')]}),
("wm.context_set_enum", {"type": 'PERIOD', "value": 'PRESS', "ctrl": True},
@@ -3023,6 +3021,8 @@ def km_grease_pencil_stroke_edit_mode(params):
("transform.shear", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True},
{"properties": [("mode", 'GPENCIL_SHRINKFATTEN')]}),
+ ("transform.transform", {"type": 'F', "value": 'PRESS', "shift": True},
+ {"properties": [("mode", 'GPENCIL_OPACITY')]}),
# Proportonal editing
*_template_items_proportional_editing(connected=True),
# Add menu
@@ -4383,6 +4383,15 @@ def km_transform_modal_map(_params):
return keymap
+def km_transform_gizmo(_params):
+ keymap = (
+ "Transform Gizmo Context",
+ {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
+ {"items": _template_items_gizmo_tweak_value()},
+ )
+ return keymap
+
+
def km_backdrop_transform_widget_tweak_modal_map(_params):
keymap = (
"Backdrop Transform Widget Tweak Modal Map",
@@ -6143,6 +6152,7 @@ def generate_keymaps(params=None):
# Gizmos.
km_gizmos(params),
+ km_transform_gizmo(params),
km_backdrop_transform_widget_tweak_modal_map(params),
km_backdrop_crop_widget(params),
km_backdrop_crop_widget_tweak_modal_map(params),
diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
new file mode 100644
index 00000000000..afaab08da13
--- /dev/null
+++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
@@ -0,0 +1,3597 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+
+# ------------------------------------------------------------------------------
+# Configurable Parameters
+
+class Params:
+ __slots__ = (
+ "select_mouse",
+ "select_mouse_value",
+ "select_tweak",
+ "action_mouse",
+ "action_tweak",
+ "tool_mouse",
+ "tool_tweak",
+ "use_mouse_emulate_3_button",
+
+ )
+
+ def __init__(
+ self,
+ *,
+ use_mouse_emulate_3_button=False,
+ ):
+ self.tool_mouse = 'LEFTMOUSE'
+ self.tool_tweak = 'EVT_TWEAK_L'
+ self.action_tweak = 'EVT_TWEAK_R'
+ self.use_mouse_emulate_3_button = use_mouse_emulate_3_button
+
+
+# ------------------------------------------------------------------------------
+# Constants
+
+
+# Physical layout.
+NUMBERS_1 = ('ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE', 'ZERO')
+# Numeric order.
+NUMBERS_0 = ('ZERO', 'ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE')
+
+
+# ------------------------------------------------------------------------------
+# Keymap Item Wrappers
+
+def op_menu(menu, kmi_args):
+ return ("wm.call_menu", kmi_args, {"properties": [("name", menu)]})
+
+
+def op_menu_pie(menu, kmi_args):
+ return ("wm.call_menu_pie", kmi_args, {"properties": [("name", menu)]})
+
+
+def op_panel(menu, kmi_args, kmi_data=()):
+ return ("wm.call_panel", kmi_args, {"properties": [("name", menu), *kmi_data]})
+
+
+def op_tool(tool, kmi_args):
+ return ("wm.tool_set_by_id", kmi_args, {"properties": [("name", tool)]})
+
+
+def op_tool_cycle(tool, kmi_args):
+ return ("wm.tool_set_by_id", kmi_args, {"properties": [("name", tool), ("cycle", True)]})
+
+
+# ------------------------------------------------------------------------------
+# Keymap Templates
+
+
+def _template_items_object_subdivision_set():
+ return [
+ ("object.subdivision_set",
+ {"type": NUMBERS_0[i], "value": 'PRESS', "ctrl": True},
+ {"properties": [("level", i), ("relative", False)]})
+ for i in range(6)
+ ]
+
+
+def _template_items_gizmo_tweak_value():
+ return [
+ ("gizmogroup.gizmo_tweak", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None),
+ ]
+
+
+def _template_items_animation():
+ return [
+ ("screen.frame_offset", {"type": 'LEFT_ARROW', "value": 'PRESS'},
+ {"properties": [("delta", -1)]}),
+ ("screen.frame_offset", {"type": 'RIGHT_ARROW', "value": 'PRESS'},
+ {"properties": [("delta", 1)]}),
+ ("screen.frame_jump", {"type": 'RIGHT_ARROW', "value": 'PRESS', "ctrl": True},
+ {"properties": [("end", True)]}),
+ ("screen.frame_jump", {"type": 'LEFT_ARROW', "value": 'PRESS', "ctrl": True},
+ {"properties": [("end", False)]}),
+
+ ]
+
+
+def _template_items_gizmo_tweak_modal():
+ return [
+ ("CANCEL", {"type": 'ESC', "value": 'PRESS', "any": True}, None),
+ ("CANCEL", {"type": 'RIGHTMOUSE', "value": 'PRESS', "any": True}, None),
+ ("CONFIRM", {"type": 'RET', "value": 'PRESS', "any": True}, None),
+ ("CONFIRM", {"type": 'NUMPAD_ENTER', "value": 'PRESS', "any": True}, None),
+ ("PRECISION_ON", {"type": 'RIGHT_SHIFT', "value": 'PRESS', "any": True}, None),
+ ("PRECISION_OFF", {"type": 'RIGHT_SHIFT', "value": 'RELEASE', "any": True}, None),
+ ("PRECISION_ON", {"type": 'LEFT_SHIFT', "value": 'PRESS', "any": True}, None),
+ ("PRECISION_OFF", {"type": 'LEFT_SHIFT', "value": 'RELEASE', "any": True}, None),
+ ("SNAP_ON", {"type": 'RIGHT_CTRL', "value": 'PRESS', "any": True}, None),
+ ("SNAP_OFF", {"type": 'RIGHT_CTRL', "value": 'RELEASE', "any": True}, None),
+ ("SNAP_ON", {"type": 'LEFT_CTRL', "value": 'PRESS', "any": True}, None),
+ ("SNAP_OFF", {"type": 'LEFT_CTRL', "value": 'RELEASE', "any": True}, None),
+ ]
+
+# Tool System Templates
+
+def _template_items_basic_tools(*, connected=False):
+ return [
+ op_tool("builtin.select_box", {"type": 'Q', "value": 'PRESS'}),
+ op_tool("builtin.move", {"type": 'W', "value": 'PRESS'}),
+ op_tool("builtin.rotate", {"type": 'E', "value": 'PRESS'}),
+ op_tool("builtin.scale", {"type": 'R', "value": 'PRESS'}),
+ op_tool("builtin.scale_cage", {"type": 'R', "value": 'PRESS', "shift": True}),
+ op_tool("builtin.transform", {"type": 'T', "value": 'PRESS'}),
+ op_tool("builtin.measure", {"type": 'M', "value": 'PRESS'}),
+ ]
+
+def _template_items_tool_select(params, operator, cursor_operator):
+ return [(operator, {"type": 'LEFTMOUSE', "value": 'PRESS'}, None)]
+
+
+def _template_items_tool_select_actions(operator, *, type, value):
+ kmi_args = {"type": type, "value": value}
+ return [
+ (operator, kmi_args, None),
+ (operator, {**kmi_args, "shift": True},
+ {"properties": [("mode", 'ADD')]}),
+ (operator, {**kmi_args, "ctrl": True},
+ {"properties": [("mode", 'SUB')]}),
+ (operator, {**kmi_args, "shift": True, "ctrl": True},
+ {"properties": [("mode", 'AND')]}),
+ ]
+
+
+# This could have a more generic name, for now use for circle select.
+def _template_items_tool_select_actions_simple(operator, *, type, value, properties=[]):
+ kmi_args = {"type": type, "value": value}
+ return [
+ # Don't define 'SET' here, take from the tool options.
+ (operator, kmi_args,
+ {"properties": properties}),
+ (operator, {**kmi_args, "shift": True},
+ {"properties": [*properties, ("mode", 'ADD')]}),
+ (operator, {**kmi_args, "ctrl": True},
+ {"properties": [*properties, ("mode", 'SUB')]}),
+ ]
+
+
+# ------------------------------------------------------------------------------
+# Window, Screen, Areas, Regions
+
+def km_window(params):
+ items = []
+ keymap = (
+ "Window",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+
+ ("wm.doc_view_manual_ui_context", {"type": 'F1', "value": 'PRESS'}, None),
+ op_panel("TOPBAR_PT_name", {"type": 'RET', "value": 'PRESS'}, [("keep_open", False)]),
+ ("wm.search_menu", {"type": 'TAB', "value": 'PRESS'}, None),
+
+ # File operations
+ ("wm.read_homefile", {"type": 'N', "value": 'PRESS', "ctrl": True}, None),
+ op_menu("TOPBAR_MT_file_open_recent", {"type": 'O', "value": 'PRESS', "shift": True, "ctrl": True}),
+ ("wm.open_mainfile", {"type": 'O', "value": 'PRESS', "ctrl": True}, None),
+ ("wm.save_mainfile", {"type": 'S', "value": 'PRESS', "ctrl": True}, None),
+ ("wm.save_as_mainfile", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True}, None),
+ ("wm.quit_blender", {"type": 'Q', "value": 'PRESS', "ctrl": True}, None),
+
+ # Quick menu and toolbar
+ op_menu("SCREEN_MT_user_menu", {"type": 'TAB', "value": 'PRESS', "shift": True}),
+ op_menu("SCREEN_MT_user_menu", {"type": 'F3', "value": 'PRESS'}),
+
+ # NDOF settings
+ op_menu("USERPREF_MT_ndof_settings", {"type": 'NDOF_BUTTON_MENU', "value": 'PRESS'}),
+ ("wm.context_scale_float", {"type": 'NDOF_BUTTON_PLUS', "value": 'PRESS'},
+ {"properties": [("data_path", 'preferences.inputs.ndof_sensitivity'), ("value", 1.1)]}),
+ ("wm.context_scale_float", {"type": 'NDOF_BUTTON_MINUS', "value": 'PRESS'},
+ {"properties": [("data_path", 'preferences.inputs.ndof_sensitivity'), ("value", 1.0 / 1.1)]}),
+ ("wm.context_scale_float", {"type": 'NDOF_BUTTON_PLUS', "value": 'PRESS', "shift": True},
+ {"properties": [("data_path", 'preferences.inputs.ndof_sensitivity'), ("value", 1.5)]}),
+ ("wm.context_scale_float", {"type": 'NDOF_BUTTON_MINUS', "value": 'PRESS', "shift": True},
+ {"properties": [("data_path", 'preferences.inputs.ndof_sensitivity'), ("value", 2.0 / 3.0)]}),
+ ("info.reports_display_update", {"type": 'TIMER_REPORT', "value": 'ANY', "any": True}, None),
+ ])
+
+ return keymap
+
+
+def km_screen(params):
+ items = []
+ keymap = (
+ "Screen",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("screen.repeat_last", {"type": 'G', "value": 'PRESS'}, None),
+ # Animation
+ ("screen.userpref_show", {"type": 'COMMA', "value": 'PRESS', "ctrl": True}, None),
+ ("screen.animation_step", {"type": 'TIMER0', "value": 'ANY', "any": True}, None),
+ ("screen.region_blend", {"type": 'TIMERREGION', "value": 'ANY', "any": True}, None),
+ # Full screen and cycling
+ ("screen.space_context_cycle", {"type": 'TAB', "value": 'PRESS', "ctrl": True},
+ {"properties": [("direction", 'NEXT')]}),
+ ("screen.space_context_cycle", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("direction", 'PREV')]}),
+ ("screen.workspace_cycle", {"type": 'PAGE_DOWN', "value": 'PRESS', "ctrl": True},
+ {"properties": [("direction", 'NEXT')]}),
+ ("screen.workspace_cycle", {"type": 'PAGE_UP', "value": 'PRESS', "ctrl": True},
+ {"properties": [("direction", 'PREV')]}),
+ # Files
+ ("file.execute", {"type": 'RET', "value": 'PRESS'}, None),
+ ("file.execute", {"type": 'NUMPAD_ENTER', "value": 'PRESS'}, None),
+ ("file.cancel", {"type": 'ESC', "value": 'PRESS'}, None),
+ # Undo
+ ("ed.undo", {"type": 'Z', "value": 'PRESS', "ctrl": True}, None),
+ ("ed.redo", {"type": 'Z', "value": 'PRESS', "shift": True, "ctrl": True}, None),
+ ("ed.undo_history", {"type": 'Z', "value": 'PRESS', "alt": True, "ctrl": True}, None),
+ # Render
+ ("render.view_cancel", {"type": 'ESC', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_screen_editing(params):
+ items = []
+ keymap = ("Screen Editing",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items})
+
+ items.extend([
+ # Action zones
+ ("screen.actionzone", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("modifier", 0)]}),
+ ("screen.actionzone", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("modifier", 1)]}),
+ ("screen.actionzone", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("modifier", 2)]}),
+ # Screen tools
+ ("screen.area_split", {"type": 'ACTIONZONE_AREA', "value": 'ANY'}, None),
+ ("screen.area_join", {"type": 'ACTIONZONE_AREA', "value": 'ANY'}, None),
+ ("screen.area_dupli", {"type": 'ACTIONZONE_AREA', "value": 'ANY', "shift": True}, None),
+ ("screen.area_swap", {"type": 'ACTIONZONE_AREA', "value": 'ANY', "ctrl": True}, None),
+ ("screen.region_scale", {"type": 'ACTIONZONE_REGION', "value": 'ANY'}, None),
+ ("screen.screen_full_area", {"type": 'ACTIONZONE_FULLSCREEN', "value": 'ANY'},
+ {"properties": [("use_hide_panels", True)]}),
+ # Area move after action zones
+ ("screen.area_move", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("screen.area_options", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None),
+ # Render
+ ("render.render", {"type": 'RET', "value": 'PRESS', "ctrl": True},
+ {"properties": [("use_viewport", True)]}),
+ ("render.render", {"type": 'RET', "value": 'PRESS', "ctrl": True, "alt": True},
+ {"properties": [("animation", True), ("use_viewport", True)]}),
+ ("render.view_cancel", {"type": 'ESC', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_header(_params):
+ items = []
+ keymap = (
+ "Header",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("screen.header_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_view2d(params):
+ items = []
+ keymap = (
+ "View2D",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Scrollbars
+ ("view2d.scroller_activate", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("view2d.scroller_activate", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
+ # Pan/scroll
+ ("view2d.pan", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, None),
+ ("view2d.pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "alt": True}, None),
+ ("view2d.zoom", {"type": 'RIGHTMOUSE', "value": 'PRESS', "alt": True}, None),
+ ("view2d.pan", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
+ ("view2d.scroll_right", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "ctrl": True}, None),
+ ("view2d.scroll_left", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "ctrl": True}, None),
+ ("view2d.scroll_down", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "shift": True}, None),
+ ("view2d.scroll_up", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "shift": True}, None),
+ ("view2d.ndof", {"type": 'NDOF_MOTION', "value": 'ANY'}, None),
+ # Zoom with single step
+ ("view2d.zoom_out", {"type": 'WHEELOUTMOUSE', "value": 'PRESS'}, None),
+ ("view2d.zoom_in", {"type": 'WHEELINMOUSE', "value": 'PRESS'}, None),
+ ("view2d.zoom_out", {"type": 'NUMPAD_MINUS', "value": 'PRESS'}, None),
+ ("view2d.zoom_in", {"type": 'NUMPAD_PLUS', "value": 'PRESS'}, None),
+ ("view2d.zoom", {"type": 'TRACKPADPAN', "value": 'ANY', "ctrl": True}, None),
+ ("view2d.smoothview", {"type": 'TIMER1', "value": 'ANY', "any": True}, None),
+ # Scroll up/down, only when zoom is not available.
+ ("view2d.scroll_down", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS'}, None),
+ ("view2d.scroll_up", {"type": 'WHEELUPMOUSE', "value": 'PRESS'}, None),
+ ("view2d.scroll_right", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS'}, None),
+ ("view2d.scroll_left", {"type": 'WHEELUPMOUSE', "value": 'PRESS'}, None),
+ # Zoom with drag and border
+ ("view2d.zoom", {"type": 'TRACKPADZOOM', "value": 'ANY'}, None),
+ ("view2d.zoom_border", {"type": 'Z', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_view2d_buttons_list(params):
+ items = []
+ keymap = (
+ "View2D Buttons List",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Scrollbars
+ ("view2d.scroller_activate", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("view2d.scroller_activate", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
+ # Pan scroll
+ ("view2d.pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
+ ("view2d.pan", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
+ ("view2d.scroll_down", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS'}, None),
+ ("view2d.scroll_up", {"type": 'WHEELUPMOUSE', "value": 'PRESS'}, None),
+ ("view2d.scroll_down", {"type": 'PAGE_DOWN', "value": 'PRESS'},
+ {"properties": [("page", True)]}),
+ ("view2d.scroll_up", {"type": 'PAGE_UP', "value": 'PRESS'},
+ {"properties": [("page", True)]}),
+ # Zoom
+ ("view2d.zoom", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "ctrl": True}, None),
+ ("view2d.zoom", {"type": 'TRACKPADZOOM', "value": 'ANY'}, None),
+ ("view2d.zoom", {"type": 'TRACKPADPAN', "value": 'ANY', "ctrl": True}, None),
+ ("view2d.zoom_out", {"type": 'NUMPAD_MINUS', "value": 'PRESS'}, None),
+ ("view2d.zoom_in", {"type": 'NUMPAD_PLUS', "value": 'PRESS'}, None),
+ ("view2d.reset", {"type": 'A', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_user_interface(params):
+ items = []
+ keymap = (
+ "User Interface",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Eyedroppers all have the same event, and pass it through until
+ # a suitable eyedropper handles it.
+ ("ui.eyedropper_color", {"type": 'E', "value": 'PRESS'}, None),
+ ("ui.eyedropper_colorramp", {"type": 'E', "value": 'PRESS'}, None),
+ ("ui.eyedropper_colorramp_point", {"type": 'E', "value": 'PRESS', "alt": True}, None),
+ ("ui.eyedropper_id", {"type": 'E', "value": 'PRESS'}, None),
+ ("ui.eyedropper_depth", {"type": 'E', "value": 'PRESS'}, None),
+ # Copy data path
+ ("ui.copy_data_path_button", {"type": 'C', "value": 'PRESS', "shift": True, "ctrl": True}, None),
+ ("ui.copy_data_path_button", {"type": 'C', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True},
+ {"properties": [("full_path", True)]}),
+ # rames and drivers
+ ("anim.keyframe_insert_button", {"type": 'S', "value": 'PRESS'}, None),
+ ("anim.keyframe_delete_button", {"type": 'S', "value": 'PRESS', "alt": True}, None),
+ ("anim.keyframe_clear_button", {"type": 'S', "value": 'PRESS', "shift": True, "alt": True}, None),
+ ("anim.driver_button_add", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ ("anim.driver_button_remove", {"type": 'D', "value": 'PRESS', "ctrl": True, "alt": True}, None),
+ ("anim.keyingset_button_add", {"type": 'K', "value": 'PRESS'}, None),
+ ("anim.keyingset_button_remove", {"type": 'K', "value": 'PRESS', "alt": True}, None),
+ ])
+
+ return keymap
+
+
+# ------------------------------------------------------------------------------
+# Editors
+
+
+def km_property_editor(params):
+ items = []
+ keymap = (
+ "Property Editor",
+ {"space_type": 'PROPERTIES', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("buttons.context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None),
+ ("screen.space_context_cycle", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("direction", 'PREV'), ], },),
+ ("screen.space_context_cycle", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("direction", 'NEXT'), ], },),
+ ])
+
+ return keymap
+
+
+def km_outliner(params):
+ items = []
+ keymap = (
+ "Outliner",
+ {"space_type": 'OUTLINER', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("outliner.highlight_update", {"type": 'MOUSEMOVE', "value": 'ANY', "any": True}, None),
+ ("outliner.item_rename", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
+ ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK'},
+ {"properties": [("extend", False), ("recursive", False)]}),
+ ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
+ {"properties": [("extend", True), ("recursive", False)]}),
+ ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True},
+ {"properties": [("extend", False), ("recursive", True)]}),
+ ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True, "ctrl": True},
+ {"properties": [("extend", True), ("recursive", True)]}),
+ ("outliner.select_box", {"type": 'Q', "value": 'PRESS'}, None),
+ ("outliner.item_openclose", {"type": 'RIGHT_ARROW', "value": 'PRESS'},
+ {"properties": [("all", False)]}),
+ ("outliner.item_openclose", {"type": 'LEFT_ARROW', "value": 'PRESS'},
+ {"properties": [("all", True)]}),
+ ("outliner.item_rename", {"type": 'RET', "value": 'PRESS'}, None),
+ ("outliner.operation", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None),
+ ("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, None),
+ ("outliner.show_hierarchy", {"type": 'A', "value": 'PRESS'}, None),
+ ("outliner.show_active", {"type": 'PERIOD', "value": 'PRESS'}, None),
+ ("outliner.show_active", {"type": 'F', "value": 'PRESS'}, None),
+ ("outliner.scroll_page", {"type": 'PAGE_DOWN', "value": 'PRESS'},
+ {"properties": [("up", False)]}),
+ ("outliner.scroll_page", {"type": 'PAGE_UP', "value": 'PRESS'},
+ {"properties": [("up", True)]}),
+ ("outliner.show_one_level", {"type": 'NUMPAD_PLUS', "value": 'PRESS'}, None),
+ ("outliner.show_one_level", {"type": 'NUMPAD_MINUS', "value": 'PRESS'},
+ {"properties": [("open", False)]}),
+ ("outliner.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("outliner.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("outliner.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("outliner.keyingset_add_selected", {"type": 'K', "value": 'PRESS'}, None),
+ ("outliner.keyingset_remove_selected", {"type": 'K', "value": 'PRESS', "alt": True}, None),
+ ("anim.keyframe_insert", {"type": 'S', "value": 'PRESS'}, None),
+ ("anim.keyframe_delete", {"type": 'S', "value": 'PRESS', "alt": True}, None),
+ ("outliner.drivers_add_selected", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ ("outliner.drivers_delete_selected", {"type": 'D', "value": 'PRESS', "ctrl": True, "alt": True}, None),
+ ("outliner.collection_new", {"type": 'C', "value": 'PRESS'}, None),
+ ("outliner.collection_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("outliner.collection_delete", {"type": 'DEL', "value": 'PRESS'}, None),
+ ("outliner.object_operation", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("type", 'DELETE')]}),
+ ("outliner.object_operation", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("type", 'DELETE')]}),
+ ("object.move_to_collection", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
+ ("object.link_to_collection", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None),
+ ("outliner.collection_exclude_set", {"type": 'E', "value": 'PRESS'}, None),
+ ("outliner.collection_exclude_clear", {"type": 'E', "value": 'PRESS', "alt": True}, None),
+ ("outliner.hide", {"type": 'H', "value": 'PRESS', "ctrl": True}, None),
+ ("outliner.unhide_all", {"type": 'H', "value": 'PRESS', "alt": True}, None),
+ # Copy/paste.
+ ("outliner.id_copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
+ ("outliner.id_paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
+ ])
+
+ return keymap
+
+
+def km_uv_editor(params):
+ items = []
+ keymap = (
+ "UV Editor",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Selection modes.
+ ("uv.select", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("extend", False)]}),
+ ("uv.select_loop", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "shift": True},
+ {"properties": [("extend", True)]}),
+ ("uv.select_loop", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
+ {"properties": [("extend", False)]}),
+ ("uv.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, None),
+ ("uv.select_more", {"type": 'UP_ARROW', "value": 'PRESS'}, None),
+ ("uv.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS'}, None),
+ ("uv.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("uv.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("uv.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("uv.hide", {"type": 'H', "value": 'PRESS', "ctrl": True},
+ {"properties": [("unselected", False)]}),
+ ("uv.hide", {"type": 'H', "value": 'PRESS', "shift": True},
+ {"properties": [("unselected", True)]}),
+ ("uv.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
+ op_menu_pie("IMAGE_MT_uvs_snap_pie", {"type": 'S', "value": 'PRESS', "shift": True}),
+ op_menu("IMAGE_MT_uvs_select_mode", {"type": 'TAB', "value": 'PRESS', "ctrl": True}),
+ ("transform.translate", {"type": 'W', "value": 'PRESS'}, None),
+ ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("transform.rotate", {"type": 'E', "value": 'PRESS'}, None),
+ ("transform.resize", {"type": 'R', "value": 'PRESS'}, None),
+ op_menu("IMAGE_MT_uvs_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ # Tools
+ op_tool("builtin.select_box", {"type": 'Q', "value": 'PRESS'}),
+ op_tool("builtin.transform", {"type": 'T', "value": 'PRESS'}),
+ ])
+
+ return keymap
+
+
+def km_uv_sculpt(params):
+ items = []
+ keymap = (
+ "UV Sculpt",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("wm.context_toggle", {"type": 'Q', "value": 'PRESS'},
+ {"properties": [("data_path", 'tool_settings.use_uv_sculpt')]}),
+ ("sculpt.uv_sculpt_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("mode", 'NORMAL')]}),
+ ("sculpt.uv_sculpt_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'INVERT')]}),
+ ("sculpt.uv_sculpt_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("mode", 'RELAX')]}),
+ ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
+ {"properties": [("scalar", 0.9)]}),
+ ("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
+ {"properties": [("scalar", 1.0 / 0.9)]}),
+ *_template_paint_radial_control("uv_sculpt"),
+ ("brush.uv_sculpt_tool_set", {"type": 'S', "value": 'PRESS'},
+ {"properties": [("tool", 'RELAX')]}),
+ ("brush.uv_sculpt_tool_set", {"type": 'P', "value": 'PRESS'},
+ {"properties": [("tool", 'PINCH')]}),
+ ("brush.uv_sculpt_tool_set", {"type": 'G', "value": 'PRESS'},
+ {"properties": [("tool", 'GRAB')]}),
+ ])
+
+ return keymap
+
+# 3D View: all regions.
+def km_view3d_generic(_params):
+ items = []
+ keymap = (
+ "3D View Generic",
+ {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ])
+
+ return keymap
+
+# 3D View: main region.
+def km_view3d(params):
+ items = []
+ keymap = (
+ "3D View",
+ {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Visibility.
+ ("view3d.localview", {"type": 'I', "value": 'PRESS', "shift": True}, None),
+ # Navigation.
+ ("view3d.rotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, None),
+ ("view3d.move", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "alt": True}, None),
+ ("view3d.zoom", {"type": 'RIGHTMOUSE', "value": 'PRESS', "alt": True}, None),
+ ("view3d.view_selected", {"type": 'F', "value": 'PRESS', "shift": True},
+ {"properties": [("use_all_regions", True)]}),
+ ("view3d.view_selected", {"type": 'F', "value": 'PRESS'},
+ {"properties": [("use_all_regions", False)]}),
+ ("view3d.smoothview", {"type": 'TIMER1', "value": 'ANY', "any": True}, None),
+ # Trackpad
+ ("view3d.rotate", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
+ ("view3d.rotate", {"type": 'MOUSEROTATE', "value": 'ANY'}, None),
+ ("view3d.move", {"type": 'TRACKPADPAN', "value": 'ANY', "shift": True}, None),
+ ("view3d.zoom", {"type": 'TRACKPADZOOM', "value": 'ANY'}, None),
+ ("view3d.zoom", {"type": 'TRACKPADPAN', "value": 'ANY', "ctrl": True}, None),
+ # Numpad
+ ("view3d.zoom", {"type": 'NUMPAD_PLUS', "value": 'PRESS'},
+ {"properties": [("delta", 1)]}),
+ ("view3d.zoom", {"type": 'NUMPAD_MINUS', "value": 'PRESS'},
+ {"properties": [("delta", -1)]}),
+ ("view3d.zoom", {"type": 'EQUAL', "value": 'PRESS', "ctrl": True},
+ {"properties": [("delta", 1)]}),
+ ("view3d.zoom", {"type": 'MINUS', "value": 'PRESS', "ctrl": True},
+ {"properties": [("delta", -1)]}),
+ ("view3d.zoom", {"type": 'WHEELINMOUSE', "value": 'PRESS'},
+ {"properties": [("delta", 1)]}),
+ ("view3d.zoom", {"type": 'WHEELOUTMOUSE', "value": 'PRESS'},
+ {"properties": [("delta", -1)]}),
+ ("view3d.dolly", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "shift": True},
+ {"properties": [("delta", 1)]}),
+ ("view3d.dolly", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "shift": True},
+ {"properties": [("delta", -1)]}),
+ ("view3d.dolly", {"type": 'EQUAL', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("delta", 1)]}),
+ ("view3d.dolly", {"type": 'MINUS', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("delta", -1)]}),
+ ("view3d.view_all", {"type": 'A', "value": 'PRESS'},
+ {"properties": [("center", False)]}),
+ ("view3d.view_all", {"type": 'A', "value": 'PRESS', "shift": True},
+ {"properties": [("use_all_regions", True), ("center", False)]}),
+ # Numpad views.
+ ("view3d.view_camera", {"type": 'NUMPAD_0', "value": 'PRESS'}, None),
+ ("view3d.view_axis", {"type": 'NUMPAD_1', "value": 'PRESS'},
+ {"properties": [("type", 'FRONT')]}),
+ ("view3d.view_orbit", {"type": 'NUMPAD_2', "value": 'PRESS'},
+ {"properties": [("type", 'ORBITDOWN')]}),
+ ("view3d.view_axis", {"type": 'NUMPAD_3', "value": 'PRESS'},
+ {"properties": [("type", 'RIGHT')]}),
+ ("view3d.view_orbit", {"type": 'NUMPAD_4', "value": 'PRESS'},
+ {"properties": [("type", 'ORBITLEFT')]}),
+ ("view3d.view_persportho", {"type": 'NUMPAD_5', "value": 'PRESS'}, None),
+ ("view3d.view_orbit", {"type": 'NUMPAD_6', "value": 'PRESS'},
+ {"properties": [("type", 'ORBITRIGHT')]}),
+ ("view3d.view_axis", {"type": 'NUMPAD_7', "value": 'PRESS'},
+ {"properties": [("type", 'TOP')]}),
+ ("view3d.view_orbit", {"type": 'NUMPAD_8', "value": 'PRESS'},
+ {"properties": [("type", 'ORBITUP')]}),
+ ("view3d.view_axis", {"type": 'NUMPAD_1', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'BACK')]}),
+ ("view3d.view_axis", {"type": 'NUMPAD_3', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'LEFT')]}),
+ ("view3d.view_axis", {"type": 'NUMPAD_7', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'BOTTOM')]}),
+ ("view3d.view_pan", {"type": 'NUMPAD_2', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'PANDOWN')]}),
+ ("view3d.view_pan", {"type": 'NUMPAD_4', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'PANLEFT')]}),
+ ("view3d.view_pan", {"type": 'NUMPAD_6', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'PANRIGHT')]}),
+ ("view3d.view_pan", {"type": 'NUMPAD_8', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'PANUP')]}),
+ ("view3d.view_roll", {"type": 'NUMPAD_4', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'LEFT')]}),
+ ("view3d.view_roll", {"type": 'NUMPAD_6', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'RIGHT')]}),
+ ("view3d.view_orbit", {"type": 'NUMPAD_9', "value": 'PRESS'},
+ {"properties": [("angle", 3.1415927), ("type", 'ORBITRIGHT')]}),
+ ("view3d.view_axis", {"type": 'NUMPAD_1', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'FRONT'), ("align_active", True)]}),
+ ("view3d.view_axis", {"type": 'NUMPAD_3', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'RIGHT'), ("align_active", True)]}),
+ ("view3d.view_axis", {"type": 'NUMPAD_7', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'TOP'), ("align_active", True)]}),
+ ("view3d.view_axis", {"type": 'NUMPAD_1', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("type", 'BACK'), ("align_active", True)]}),
+ ("view3d.view_axis", {"type": 'NUMPAD_3', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("type", 'LEFT'), ("align_active", True)]}),
+ ("view3d.view_axis", {"type": 'NUMPAD_7', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("type", 'BOTTOM'), ("align_active", True)]}),
+ ("view3d.view_center_pick", {"type": 'MIDDLEMOUSE', "value": 'CLICK', "alt": True}, None),
+ # NDOF
+ ("view3d.ndof_orbit_zoom", {"type": 'NDOF_MOTION', "value": 'ANY'}, None),
+ ("view3d.ndof_orbit", {"type": 'NDOF_MOTION', "value": 'ANY', "ctrl": True}, None),
+ ("view3d.ndof_pan", {"type": 'NDOF_MOTION', "value": 'ANY', "shift": True}, None),
+ ("view3d.ndof_all", {"type": 'NDOF_MOTION', "value": 'ANY', "shift": True, "ctrl": True}, None),
+ ("view3d.view_selected", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'},
+ {"properties": [("use_all_regions", False)]}),
+ ("view3d.view_roll", {"type": 'NDOF_BUTTON_ROLL_CCW', "value": 'PRESS'},
+ {"properties": [("type", 'LEFT')]}),
+ ("view3d.view_roll", {"type": 'NDOF_BUTTON_ROLL_CCW', "value": 'PRESS'},
+ {"properties": [("type", 'RIGHT')]}),
+ ("view3d.view_axis", {"type": 'NDOF_BUTTON_FRONT', "value": 'PRESS'},
+ {"properties": [("type", 'FRONT')]}),
+ ("view3d.view_axis", {"type": 'NDOF_BUTTON_BACK', "value": 'PRESS'},
+ {"properties": [("type", 'BACK')]}),
+ ("view3d.view_axis", {"type": 'NDOF_BUTTON_LEFT', "value": 'PRESS'},
+ {"properties": [("type", 'LEFT')]}),
+ ("view3d.view_axis", {"type": 'NDOF_BUTTON_RIGHT', "value": 'PRESS'},
+ {"properties": [("type", 'RIGHT')]}),
+ ("view3d.view_axis", {"type": 'NDOF_BUTTON_TOP', "value": 'PRESS'},
+ {"properties": [("type", 'TOP')]}),
+ ("view3d.view_axis", {"type": 'NDOF_BUTTON_BOTTOM', "value": 'PRESS'},
+ {"properties": [("type", 'BOTTOM')]}),
+ ("view3d.view_axis", {"type": 'NDOF_BUTTON_FRONT', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'FRONT'), ("align_active", True)]}),
+ ("view3d.view_axis", {"type": 'NDOF_BUTTON_RIGHT', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'RIGHT'), ("align_active", True)]}),
+ ("view3d.view_axis", {"type": 'NDOF_BUTTON_TOP', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'TOP'), ("align_active", True)]}),
+ # Selection.
+ *((operator,
+ {"type": 'LEFTMOUSE', "value": 'CLICK', **{m: True for m in mods}},
+ {"properties": [(c, True) for c in props]},
+ ) for operator, props, mods in (
+ ("view3d.select", ("deselect_all",), ()),
+ ("view3d.select", ("toggle",), ("shift",)),
+ ("view3d.select", ("center", "object"), ("ctrl",)),
+ ("view3d.select", ("enumerate",), ("alt",)),
+ ("view3d.select", ("extend", "toggle", "center"), ("shift", "ctrl")),
+ ("view3d.select", ("center", "enumerate"), ("ctrl", "alt")),
+ ("view3d.select", ("toggle", "enumerate"), ("shift", "alt")),
+ ("view3d.select", ("toggle", "center", "enumerate"), ("shift", "ctrl", "alt")),
+ )),
+ ("view3d.zoom_border", {"type": 'Z', "value": 'PRESS'}, None),
+ # Copy/paste.
+ ("view3d.copybuffer", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
+ ("view3d.pastebuffer", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
+ # Menus.
+ op_menu_pie("VIEW3D_MT_snap_pie", {"type": 'V', "value": 'PRESS'}),
+ # Transform.
+ ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ op_menu_pie("VIEW3D_MT_pivot_pie", {"type": 'PERIOD', "value": 'PRESS'}),
+ op_menu_pie("VIEW3D_MT_orientations_pie", {"type": 'COMMA', "value": 'PRESS'}),
+ ("view3d.toggle_xray", {"type": 'X', "value": 'PRESS', "alt": True}, None),
+
+ ])
+
+ return keymap
+
+
+def km_mask_editing(params):
+ items = []
+ keymap = (
+ "Mask Editing",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("mask.new", {"type": 'N', "value": 'PRESS', "alt": True}, None),
+ op_menu_pie("VIEW3D_MT_proportional_editing_falloff_pie", {"type": 'O', "value": 'PRESS', "shift": True}),
+ ("mask.add_vertex_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None),
+ ("mask.add_feather_vertex_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None),
+ ("mask.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("mask.delete", {"type": 'DEL', "value": 'PRESS'}, None),
+ ("mask.select", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("extend", False), ("deselect", False), ("toggle", False)]}),
+ ("mask.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("extend", False), ("deselect", False), ("toggle", True)]}),
+ ("mask.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("mask.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("mask.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ #*_template_items_select_actions(params, "mask.select_all"),
+ ("mask.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
+ ("mask.select_linked_pick", {"type": 'L', "value": 'PRESS'},
+ {"properties": [("deselect", False)]}),
+ ("mask.select_linked_pick", {"type": 'L', "value": 'PRESS', "shift": True},
+ {"properties": [("deselect", True)]}),
+ ("mask.select_box", {"type": 'Q', "value": 'PRESS'}, None),
+ ("mask.select_circle", {"type": 'C', "value": 'PRESS'}, None),
+ ("mask.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True},
+ {"properties": [("mode", 'ADD')]}),
+ ("mask.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True},
+ {"properties": [("mode", 'SUB')]}),
+ ("mask.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True}, None),
+ ("mask.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True}, None),
+ ("mask.hide_view_clear", {"type": 'H', "value": 'PRESS', "alt": True}, None),
+ ("mask.hide_view_set", {"type": 'H', "value": 'PRESS', "ctrl": True},
+ {"properties": [("unselected", False)]}),
+ ("mask.hide_view_set", {"type": 'H', "value": 'PRESS', "shift": True},
+ {"properties": [("unselected", True)]}),
+ ("clip.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("extend", False)]}),
+ ("mask.cyclic_toggle", {"type": 'C', "value": 'PRESS', "alt": True}, None),
+ ("mask.slide_point", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("mask.slide_spline_curvature", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("mask.handle_type_set", {"type": 'V', "value": 'PRESS'}, None),
+ ("mask.parent_set", {"type": 'P', "value": 'PRESS', "ctrl": True}, None),
+ ("mask.parent_clear", {"type": 'P', "value": 'PRESS', "shift": True}, None),
+ ("mask.shape_key_insert", {"type": 'I', "value": 'PRESS'}, None),
+ ("mask.shape_key_clear", {"type": 'I', "value": 'PRESS', "alt": True}, None),
+ ("mask.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ ("mask.copy_splines", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
+ ("mask.paste_splines", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
+ ("transform.translate", {"type": 'W', "value": 'PRESS'}, None),
+ ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("transform.resize", {"type": 'R', "value": 'PRESS'}, None),
+ ("transform.rotate", {"type": 'E', "value": 'PRESS'}, None),
+
+ ])
+
+ return keymap
+
+
+def km_markers(params):
+ items = []
+ keymap = (
+ "Markers",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("anim.change_frame", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("graph.cursor_set", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("marker.add", {"type": 'M', "value": 'PRESS'}, None),
+ ("marker.move", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("marker.duplicate", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+
+ ("marker.select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("marker.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("extend", True)]}),
+ ("marker.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("extend", False), ("camera", True)]}),
+ ("marker.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("extend", True), ("camera", True)]}),
+ ("marker.select_box", {"type": 'Q', "value": 'PRESS'}, None),
+ ("marker.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("marker.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("marker.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("marker.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("marker.delete", {"type": 'DEL', "value": 'PRESS'}, None),
+ ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
+ ("marker.move", {"type": 'W', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_graph_editor_generic(params):
+ items = []
+ keymap = (
+ "Graph Editor Generic",
+ {"space_type": 'GRAPH_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
+ ("graph.hide", {"type": 'H', "value": 'PRESS', "ctrl": True},
+ {"properties": [("unselected", False)]}),
+ ("graph.hide", {"type": 'H', "value": 'PRESS', "shift": True},
+ {"properties": [("unselected", True)]}),
+ ("graph.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
+ ])
+
+ return keymap
+
+
+def km_graph_editor(params):
+ items = []
+ keymap = (
+ "Graph Editor",
+ {"space_type": 'GRAPH_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ *_template_items_animation(),
+ ("graph.cursor_set", {"type": 'RIGHTMOUSE', "value": 'PRESS', "ctrl": True}, None),
+ ("graph.clickselect", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("extend", False), ("column", False), ("curves", False)]}),
+ ("graph.clickselect", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
+ {"properties": [("extend", False), ("column", True), ("curves", False)]}),
+ ("graph.clickselect", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("extend", True), ("column", False), ("curves", False)]}),
+ ("graph.clickselect", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("extend", True), ("column", True), ("curves", False)]}),
+ ("graph.clickselect", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True, "alt": True},
+ {"properties": [("extend", False), ("column", False), ("curves", True)]}),
+ ("graph.clickselect", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True},
+ {"properties": [("extend", True), ("column", False), ("curves", True)]}),
+ ("graph.select_leftright", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'CHECK'), ("extend", False)]}),
+ ("graph.select_leftright", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("mode", 'CHECK'), ("extend", True)]}),
+ ("graph.select_leftright", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
+ {"properties": [("mode", 'LEFT'), ("extend", False)]}),
+ ("graph.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
+ {"properties": [("mode", 'RIGHT'), ("extend", False)]}),
+ ("graph.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("graph.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("graph.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("graph.select_box", {"type": 'Q', "value": 'PRESS'},
+ {"properties": [("axis_range", False), ("include_handles", False)]}),
+ ("graph.select_box", {"type": 'Q', "value": 'PRESS', "alt": True},
+ {"properties": [("axis_range", True), ("include_handles", False)]}),
+ ("graph.select_box", {"type": 'Q', "value": 'PRESS', "ctrl": True},
+ {"properties": [("axis_range", False), ("include_handles", True)]}),
+ ("graph.select_box", {"type": 'Q', "value": 'PRESS', "ctrl": True, "alt": True},
+ {"properties": [("axis_range", True), ("include_handles", True)]}),
+ ("graph.select_more", {"type": 'UP_ARROW', "value": 'PRESS'}, None),
+ ("graph.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS'}, None),
+ ("graph.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, None),
+ op_menu("GRAPH_MT_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}),
+ op_menu("GRAPH_MT_delete", {"type": 'DEL', "value": 'PRESS'}),
+ op_menu("GRAPH_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ ("graph.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ ("graph.keyframe_insert", {"type": 'S', "value": 'PRESS'}, None),
+ ("graph.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
+ ("graph.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
+ ("graph.paste", {"type": 'V', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("flipped", True)]}),
+ ("graph.previewrange_set", {"type": 'P', "value": 'PRESS', "ctrl": True, "alt": True}, None),
+ ("graph.view_all", {"type": 'A', "value": 'PRESS'}, None),
+ ("graph.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
+ ("graph.view_selected", {"type": 'F', "value": 'PRESS'}, None),
+ ("graph.view_frame", {"type": 'NUMPAD_0', "value": 'PRESS'}, None),
+ ("anim.channels_editable_toggle", {"type": 'TAB', "value": 'PRESS'}, None),
+ ("transform.translate", {"type": 'W', "value": 'PRESS'}, None),
+ ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("transform.transform", {"type": 'Y', "value": 'PRESS'},
+ {"properties": [("mode", 'TIME_EXTEND')]}),
+ ("transform.rotate", {"type": 'E', "value": 'PRESS'}, None),
+ ("transform.resize", {"type": 'R', "value": 'PRESS'}, None),
+ ("marker.add", {"type": 'M', "value": 'PRESS'}, None),
+ ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_image_generic(params):
+ items = []
+ keymap = (
+ "Image Generic",
+ {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("image.new", {"type": 'N', "value": 'PRESS', "alt": True}, None),
+ ("image.open", {"type": 'O', "value": 'PRESS', "alt": True}, None),
+ ("image.reload", {"type": 'R', "value": 'PRESS', "alt": True}, None),
+ ("image.read_viewlayers", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
+ ("image.save", {"type": 'S', "value": 'PRESS', "alt": True}, None),
+ ("image.save_as", {"type": 'S', "value": 'PRESS', "shift": True}, None),
+ ])
+
+ return keymap
+
+
+def km_image(params):
+ items = []
+ keymap = (
+ "Image",
+ {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("image.view_all", {"type": 'A', "value": 'PRESS'}, None),
+ ("image.view_selected", {"type": 'F', "value": 'PRESS'}, None),
+ ("image.view_pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
+ ("image.view_pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "shift": True}, None),
+ ("image.view_pan", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
+ ("image.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
+ ("image.view_ndof", {"type": 'NDOF_MOTION', "value": 'ANY'}, None),
+ ("image.view_zoom_in", {"type": 'WHEELINMOUSE', "value": 'PRESS'}, None),
+ ("image.view_zoom_out", {"type": 'WHEELOUTMOUSE', "value": 'PRESS'}, None),
+ ("image.view_zoom_in", {"type": 'NUMPAD_PLUS', "value": 'PRESS'}, None),
+ ("image.view_zoom_out", {"type": 'NUMPAD_MINUS', "value": 'PRESS'}, None),
+ ("image.view_zoom", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "ctrl": True}, None),
+ ("image.view_zoom", {"type": 'TRACKPADZOOM', "value": 'ANY'}, None),
+ ("image.view_zoom", {"type": 'TRACKPADPAN', "value": 'ANY', "ctrl": True}, None),
+ ("image.view_zoom_border", {"type": 'Z', "value": 'PRESS'}, None),
+ ("image.view_zoom_ratio", {"type": 'NUMPAD_8', "value": 'PRESS', "ctrl": True},
+ {"properties": [("ratio", 8.0)]}),
+ ("image.view_zoom_ratio", {"type": 'NUMPAD_4', "value": 'PRESS', "ctrl": True},
+ {"properties": [("ratio", 4.0)]}),
+ ("image.view_zoom_ratio", {"type": 'NUMPAD_2', "value": 'PRESS', "ctrl": True},
+ {"properties": [("ratio", 2.0)]}),
+ ("image.view_zoom_ratio", {"type": 'NUMPAD_8', "value": 'PRESS', "shift": True},
+ {"properties": [("ratio", 8.0)]}),
+ ("image.view_zoom_ratio", {"type": 'NUMPAD_4', "value": 'PRESS', "shift": True},
+ {"properties": [("ratio", 4.0)]}),
+ ("image.view_zoom_ratio", {"type": 'NUMPAD_2', "value": 'PRESS', "shift": True},
+ {"properties": [("ratio", 2.0)]}),
+ ("image.view_zoom_ratio", {"type": 'NUMPAD_1', "value": 'PRESS'},
+ {"properties": [("ratio", 1.0)]}),
+ ("image.view_zoom_ratio", {"type": 'NUMPAD_2', "value": 'PRESS'},
+ {"properties": [("ratio", 0.5)]}),
+ ("image.view_zoom_ratio", {"type": 'NUMPAD_4', "value": 'PRESS'},
+ {"properties": [("ratio", 0.25)]}),
+ ("image.view_zoom_ratio", {"type": 'NUMPAD_8', "value": 'PRESS'},
+ {"properties": [("ratio", 0.125)]}),
+ ("image.change_frame", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("image.sample", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("image.curves_point_set", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("point", 'BLACK_POINT')]}),
+ ("image.curves_point_set", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("point", 'WHITE_POINT')]}),
+ ("object.mode_set", {"type": 'TWO', "value": 'PRESS'},
+ {"properties": [("mode", 'EDIT')]}),
+ ("object.mode_set", {"type": 'ONE', "value": 'PRESS'},
+ {"properties": [("mode", 'OBJECT')]}),
+ op_menu_pie("IMAGE_MT_pivot_pie", {"type": 'PERIOD', "value": 'PRESS'}),
+
+ ])
+
+ return keymap
+
+
+def km_node_generic(_params):
+ items = []
+ keymap = (
+ "Node Generic",
+ {"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ])
+
+ return keymap
+
+
+def km_node_editor(params):
+ items = []
+ keymap = (
+ "Node Editor",
+ {"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ def node_select_ops(select_mouse):
+ return [
+ ("node.select", {"type": select_mouse, "value": 'PRESS'},
+ {"properties": [("extend", False)]}),
+ ("node.select", {"type": select_mouse, "value": 'PRESS', "ctrl": True},
+ {"properties": [("extend", False)]}),
+ ("node.select", {"type": select_mouse, "value": 'PRESS', "alt": True},
+ {"properties": [("extend", False)]}),
+ ("node.select", {"type": select_mouse, "value": 'PRESS', "ctrl": True, "alt": True},
+ {"properties": [("extend", False)]}),
+ ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True},
+ {"properties": [("extend", True)]}),
+ ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("extend", True)]}),
+ ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("extend", True)]}),
+ ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True, "ctrl": True, "alt": True},
+ {"properties": [("extend", True)]}),
+ ]
+
+ # Allow node selection with both for RMB select
+
+ items.extend(node_select_ops('LEFTMOUSE'))
+
+ items.extend([
+ ("node.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ {"properties": [("tweak", True)]}),
+ ("node.select_lasso", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True, "alt": True},
+ {"properties": [("mode", 'ADD')]}),
+ ("node.select_lasso", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True, "ctrl": True, "alt": True},
+ {"properties": [("mode", 'SUB')]}),
+ ("node.select_circle", {"type": 'C', "value": 'PRESS'}, None),
+ ("node.link", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("detach", False)]}),
+ ("node.link", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("detach", True)]}),
+ ("node.resize", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("node.add_reroute", {"type": params.action_tweak, "value": 'ANY', "shift": True}, None),
+ ("node.links_cut", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, None),
+ ("node.select_link_viewer", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None),
+ ("node.backimage_fit", {"type": 'A', "value": 'PRESS', "alt": True}, None),
+ ("node.backimage_sample", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, None),
+ op_menu("NODE_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ ("node.link_make", {"type": 'F', "value": 'PRESS'},
+ {"properties": [("replace", False)]}),
+ ("node.link_make", {"type": 'F', "value": 'PRESS', "shift": True},
+ {"properties": [("replace", True)]}),
+ op_menu("NODE_MT_add", {"type": 'A', "value": 'PRESS', "shift": True}),
+ ("node.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ ("node.parent_set", {"type": 'P', "value": 'PRESS'}, None),
+ ("node.join", {"type": 'J', "value": 'PRESS', "ctrl": True}, None),
+ ("node.hide_toggle", {"type": 'H', "value": 'PRESS', "ctrl": True}, None),
+ ("node.mute_toggle", {"type": 'M', "value": 'PRESS'}, None),
+ ("node.preview_toggle", {"type": 'H', "value": 'PRESS', "shift": True}, None),
+ ("node.hide_socket_toggle", {"type": 'H', "value": 'PRESS', "ctrl": True}, None),
+ ("node.view_all", {"type": 'A', "value": 'PRESS'}, None),
+ ("node.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
+ ("node.view_selected", {"type": 'F', "value": 'PRESS'}, None),
+ ("node.select_box", {"type": 'Q', "value": 'PRESS'},
+ {"properties": [("tweak", False)]}),
+ ("node.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("node.delete", {"type": 'DEL', "value": 'PRESS'}, None),
+ ("node.delete_reconnect", {"type": 'BACK_SPACE', "value": 'PRESS', "ctrl": True}, None),
+ ("node.delete_reconnect", {"type": 'DEL', "value": 'PRESS', "ctrl": True}, None),
+ ("node.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("node.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("node.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("node.select_linked_to", {"type": 'LEFT_BRACKET', "value": 'PRESS', "shift": True}, None),
+ ("node.select_linked_from", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, None),
+ ("node.select_same_type_step", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "shift": True},
+ {"properties": [("prev", False)]}),
+ ("node.select_same_type_step", {"type": 'LEFT_BRACKET', "value": 'PRESS', "shift": True},
+ {"properties": [("prev", True)]}),
+ ("node.find_node", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
+ ("node.group_make", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
+ ("node.group_ungroup", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None),
+ ("node.group_edit", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
+ {"properties": [("exit", False)]}),
+ ("node.group_edit", {"type": 'ESC', "value": 'PRESS'},
+ {"properties": [("exit", True)]}),
+ ("node.clipboard_copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
+ ("node.clipboard_paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
+ ("node.viewer_border", {"type": 'Z', "value": 'PRESS'}, None),
+ ("node.clear_viewer_border", {"type": 'Z', "value": 'PRESS', "alt": True}, None),
+ ("node.translate_attach", {"type": 'W', "value": 'PRESS'}, None),
+ ("node.translate_attach", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("node.translate_attach", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("transform.translate", {"type": 'W', "value": 'PRESS'},
+ {"properties": [("release_confirm", True)]}),
+ ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ {"properties": [("release_confirm", True)]}),
+ ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ {"properties": [("release_confirm", True)]}),
+ ("transform.rotate", {"type": 'E', "value": 'PRESS'}, None),
+ ("transform.resize", {"type": 'R', "value": 'PRESS'}, None),
+ ("node.move_detach_links_release", {"type": params.action_tweak, "value": 'ANY', "alt": True}, None),
+ ("node.move_detach_links", {"type": 'EVT_TWEAK_L', "value": 'ANY', "alt": True}, None),
+ ])
+
+ return keymap
+
+
+def km_info(params):
+ items = []
+ keymap = (
+ "Info",
+ {"space_type": 'INFO', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("info.select_pick", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("info.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("info.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, None),
+ ("info.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, None),
+ ("info.select_box", {"type": 'Q', "value": 'PRESS'}, None),
+ ("info.report_replay", {"type": 'R', "value": 'PRESS'}, None),
+ ("info.report_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("info.report_delete", {"type": 'DEL', "value": 'PRESS'}, None),
+ ("info.report_copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
+ ])
+
+ return keymap
+
+
+def km_file_browser(params):
+ items = []
+ keymap = (
+ "File Browser",
+ {"space_type": 'FILE_BROWSER', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("file.parent", {"type": 'UP_ARROW', "value": 'PRESS', "alt": True}, None),
+ ("file.previous", {"type": 'LEFT_ARROW', "value": 'PRESS', "alt": True}, None),
+ ("file.next", {"type": 'RIGHT_ARROW', "value": 'PRESS', "alt": True}, None),
+ ("file.refresh", {"type": 'R', "value": 'PRESS'}, None),
+ ("file.parent", {"type": 'P', "value": 'PRESS'}, None),
+ ("file.previous", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("file.next", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True}, None),
+ ("wm.context_toggle", {"type": 'H', "value": 'PRESS'},
+ {"properties": [("data_path", 'space_data.params.show_hidden')]}),
+ ("file.directory_new", {"type": 'I', "value": 'PRESS'}, None),
+ ("file.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("file.delete", {"type": 'DEL', "value": 'PRESS'}, None),
+ ("file.smoothscroll", {"type": 'TIMER1', "value": 'ANY', "any": True}, None),
+ ("file.bookmark_toggle", {"type": 'T', "value": 'PRESS'}, None),
+ ("file.bookmark_add", {"type": 'B', "value": 'PRESS', "ctrl": True}, None),
+ ])
+
+ return keymap
+
+
+def km_file_browser_main(params):
+ items = []
+ keymap = (
+ "File Browser Main",
+ {"space_type": 'FILE_BROWSER', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("file.execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
+ {"properties": [("need_active", True)]}),
+ ("file.refresh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
+ ("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None),
+ ("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
+ {"properties": [("extend", True)]}),
+ ("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True, "ctrl": True},
+ {"properties": [("extend", True), ("fill", True)]}),
+ ("file.select", {"type": 'RIGHTMOUSE', "value": 'CLICK'},
+ {"properties": [("open", False)]}),
+ ("file.select", {"type": 'RIGHTMOUSE', "value": 'CLICK', "shift": True},
+ {"properties": [("extend", True), ("open", False)]}),
+ ("file.select", {"type": 'RIGHTMOUSE', "value": 'CLICK', "alt": True},
+ {"properties": [("extend", True), ("fill", True), ("open", False)]}),
+ ("file.select_walk", {"type": 'UP_ARROW', "value": 'PRESS'},
+ {"properties": [("direction", 'UP')]}),
+ ("file.select_walk", {"type": 'UP_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'UP'), ("extend", True)]}),
+ ("file.select_walk", {"type": 'UP_ARROW', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("direction", 'UP'), ("extend", True), ("fill", True)]}),
+ ("file.select_walk", {"type": 'DOWN_ARROW', "value": 'PRESS'},
+ {"properties": [("direction", 'DOWN')]}),
+ ("file.select_walk", {"type": 'DOWN_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'DOWN'), ("extend", True)]}),
+ ("file.select_walk", {"type": 'DOWN_ARROW', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("direction", 'DOWN'), ("extend", True), ("fill", True)]}),
+ ("file.select_walk", {"type": 'LEFT_ARROW', "value": 'PRESS'},
+ {"properties": [("direction", 'LEFT')]}),
+ ("file.select_walk", {"type": 'LEFT_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'LEFT'), ("extend", True)]}),
+ ("file.select_walk", {"type": 'LEFT_ARROW', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("direction", 'LEFT'), ("extend", True), ("fill", True)]}),
+ ("file.select_walk", {"type": 'RIGHT_ARROW', "value": 'PRESS'},
+ {"properties": [("direction", 'RIGHT')]}),
+ ("file.select_walk", {"type": 'RIGHT_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'RIGHT'), ("extend", True)]}),
+ ("file.select_walk", {"type": 'RIGHT_ARROW', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("direction", 'RIGHT'), ("extend", True), ("fill", True)]}),
+ ("file.previous", {"type": 'BUTTON4MOUSE', "value": 'CLICK'}, None),
+ ("file.next", {"type": 'BUTTON5MOUSE', "value": 'CLICK'}, None),
+ ("file.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, None),
+ ("file.select_box", {"type": 'Q', "value": 'PRESS'}, None),
+ ("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True},
+ {"properties": [("mode", 'ADD')]}),
+ ("file.highlight", {"type": 'MOUSEMOVE', "value": 'ANY', "any": True}, None),
+ ("file.filenum", {"type": 'NUMPAD_PLUS', "value": 'PRESS'},
+ {"properties": [("increment", 1)]}),
+ ("file.filenum", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "shift": True},
+ {"properties": [("increment", 10)]}),
+ ("file.filenum", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True},
+ {"properties": [("increment", 100)]}),
+ ("file.filenum", {"type": 'NUMPAD_MINUS', "value": 'PRESS'},
+ {"properties": [("increment", -1)]}),
+ ("file.filenum", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "shift": True},
+ {"properties": [("increment", -10)]}),
+ ("file.filenum", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True},
+ {"properties": [("increment", -100)]}),
+ ])
+
+ return keymap
+
+
+def km_file_browser_buttons(params):
+ items = []
+ keymap = (
+ "File Browser Buttons",
+ {"space_type": 'FILE_BROWSER', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("file.filenum", {"type": 'NUMPAD_PLUS', "value": 'PRESS'},
+ {"properties": [("increment", 1)]}),
+ ("file.filenum", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "shift": True},
+ {"properties": [("increment", 10)]}),
+ ("file.filenum", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True},
+ {"properties": [("increment", 100)]}),
+ ("file.filenum", {"type": 'NUMPAD_MINUS', "value": 'PRESS'},
+ {"properties": [("increment", -1)]}),
+ ("file.filenum", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "shift": True},
+ {"properties": [("increment", -10)]}),
+ ("file.filenum", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True},
+ {"properties": [("increment", -100)]}),
+ ])
+
+ return keymap
+
+
+def km_dopesheet_generic(params):
+ items = []
+ keymap = (
+ "Dopesheet Generic",
+ {"space_type": 'DOPESHEET_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("action.properties", {"type": 'N', "value": 'PRESS'}, None),
+ ("wm.context_set_enum", {"type": 'TAB', "value": 'PRESS', "ctrl": True},
+ {"properties": [("data_path", 'area.type'), ("value", 'GRAPH_EDITOR')]})
+ ])
+
+ return keymap
+
+
+def km_dopesheet(params):
+ items = []
+ keymap = (
+ "Dopesheet",
+ {"space_type": 'DOPESHEET_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ *_template_items_animation(),
+ ("action.clickselect", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("extend", False), ("column", False), ("channel", False)]}),
+ ("action.clickselect", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
+ {"properties": [("extend", False), ("column", True), ("channel", False)]}),
+ ("action.clickselect", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("extend", True), ("column", False), ("channel", False)]}),
+ ("action.clickselect", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("extend", True), ("column", True), ("channel", False)]}),
+ ("action.clickselect", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True, "alt": True},
+ {"properties": [("extend", False), ("column", False), ("channel", True)]}),
+ ("action.clickselect", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True},
+ {"properties": [("extend", True), ("column", False), ("channel", True)]}),
+ ("action.select_leftright", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'CHECK'), ("extend", False)]}),
+ ("action.select_leftright", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("mode", 'CHECK'), ("extend", True)]}),
+ ("action.select_leftright", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
+ {"properties": [("mode", 'LEFT'), ("extend", False)]}),
+ ("action.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
+ {"properties": [("mode", 'RIGHT'), ("extend", False)]}),
+ ("action.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("action.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("action.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("action.select_box", {"type": 'Q', "value": 'PRESS'},
+ {"properties": [("axis_range", False)]}),
+ ("action.select_box", {"type": 'Q', "value": 'PRESS', "alt": True},
+ {"properties": [("axis_range", True)]}),
+ ("action.select_column", {"type": 'K', "value": 'PRESS'},
+ {"properties": [("mode", 'KEYS')]}),
+ ("action.select_column", {"type": 'K', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'CFRA')]}),
+ ("action.select_column", {"type": 'K', "value": 'PRESS', "shift": True},
+ {"properties": [("mode", 'MARKERS_COLUMN')]}),
+ ("action.select_column", {"type": 'K', "value": 'PRESS', "alt": True},
+ {"properties": [("mode", 'MARKERS_BETWEEN')]}),
+ ("action.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True}, None),
+ ("action.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True}, None),
+ ("action.select_linked", {"type": 'L', "value": 'PRESS'}, None),
+ ("action.frame_jump", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
+ op_menu("DOPESHEET_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ op_menu("DOPESHEET_MT_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}),
+ op_menu("DOPESHEET_MT_delete", {"type": 'DEL', "value": 'PRESS'}),
+ ("action.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ ("action.keyframe_insert", {"type": 'S', "value": 'PRESS'}, None),
+ ("action.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
+ ("action.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
+ ("action.paste", {"type": 'V', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("flipped", True)]}),
+ ("action.previewrange_set", {"type": 'P', "value": 'PRESS', "ctrl": True, "alt": True}, None),
+ ("action.view_all", {"type": 'A', "value": 'PRESS'}, None),
+ ("action.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
+ ("action.view_selected", {"type": 'F', "value": 'PRESS'}, None),
+ ("action.view_frame", {"type": 'NUMPAD_0', "value": 'PRESS'}, None),
+ ("anim.channels_editable_toggle", {"type": 'TAB', "value": 'PRESS'}, None),
+ ("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
+ ("transform.transform", {"type": 'G', "value": 'PRESS'},
+ {"properties": [("mode", 'TIME_TRANSLATE')]}),
+ ("transform.transform", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ {"properties": [("mode", 'TIME_TRANSLATE')]}),
+ ("transform.transform", {"type": 'E', "value": 'PRESS'},
+ {"properties": [("mode", 'TIME_EXTEND')]}),
+ ("transform.transform", {"type": 'S', "value": 'PRESS'},
+ {"properties": [("mode", 'TIME_SCALE')]}),
+ ("transform.transform", {"type": 'T', "value": 'PRESS', "shift": True},
+ {"properties": [("mode", 'TIME_SLIDE')]}),
+ op_menu_pie("VIEW3D_MT_proportional_editing_falloff_pie", {"type": 'O', "value": 'PRESS', "shift": True}),
+ ("marker.add", {"type": 'M', "value": 'PRESS'}, None),
+ ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
+ ("anim.start_frame_set", {"type": 'LEFT_ARROW', "value": 'PRESS', "ctrl": True}, None),
+ ("anim.end_frame_set", {"type": 'RIGHT_ARROW', "value": 'PRESS', "ctrl": True}, None),
+ ])
+
+ return keymap
+
+
+def km_nla_generic(params):
+ items = []
+ keymap = (
+ "NLA Generic",
+ {"space_type": 'NLA_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ *_template_items_animation(),
+ ("nla.tweakmode_enter", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
+ ("nla.tweakmode_exit", {"type": 'ESC', "value": 'PRESS'}, None),
+ ("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
+ ])
+
+ return keymap
+
+
+def km_nla_channels(params):
+ items = []
+ keymap = (
+ "NLA Channels",
+ {"space_type": 'NLA_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("nla.channels_click", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("extend", False)]}),
+ ("nla.channels_click", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("extend", True)]}),
+ ("nla.tracks_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("nla.tracks_delete", {"type": 'DEL', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_nla_editor(params):
+ items = []
+ keymap = (
+ "NLA Editor",
+ {"space_type": 'NLA_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("nla.click_select", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("extend", False)]}),
+ ("nla.click_select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("extend", True)]}),
+ ("nla.select_leftright", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'CHECK'), ("extend", False)]}),
+ ("nla.select_leftright", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("mode", 'CHECK'), ("extend", True)]}),
+ ("nla.select_leftright", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
+ {"properties": [("mode", 'LEFT'), ("extend", False)]}),
+ ("nla.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
+ {"properties": [("mode", 'RIGHT'), ("extend", False)]}),
+ ("nla.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("nla.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("nla.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("nla.select_box", {"type": 'Q', "value": 'PRESS'},
+ {"properties": [("axis_range", False)]}),
+ ("nla.select_box", {"type": 'Q', "value": 'PRESS', "alt": True},
+ {"properties": [("axis_range", True)]}),
+ ("nla.view_all", {"type": 'A', "value": 'PRESS'}, None),
+ ("nla.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
+ ("nla.view_selected", {"type": 'F', "value": 'PRESS'}, None),
+ ("nla.view_frame", {"type": 'NUMPAD_0', "value": 'PRESS'}, None),
+ ("nla.meta_add", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
+ ("nla.meta_remove", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None),
+ ("nla.duplicate", {"type": 'D', "value": 'PRESS', "ctrl": True},
+ {"properties": [("linked", False)]}),
+ ("nla.duplicate", {"type": 'D', "value": 'PRESS', "ctrl": True, "alt": True},
+ {"properties": [("linked", True)]}),
+ ("nla.make_single_user", {"type": 'U', "value": 'PRESS'}, None),
+ ("nla.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("nla.delete", {"type": 'DEL', "value": 'PRESS'}, None),
+ #("nla.split", {"type": 'Y', "value": 'PRESS'}, None),
+ ("nla.mute_toggle", {"type": 'M', "value": 'PRESS'}, None),
+ #("nla.swap", {"type": 'F', "value": 'PRESS', "alt": True}, None),
+ ("nla.move_up", {"type": 'PAGE_UP', "value": 'PRESS'}, None),
+ ("nla.move_down", {"type": 'PAGE_DOWN', "value": 'PRESS'}, None),
+ ("transform.transform", {"type": 'W', "value": 'PRESS'},
+ {"properties": [("mode", 'TRANSLATION')]}),
+ ("transform.transform", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
+ {"properties": [("mode", 'TRANSLATION')]}),
+ ("transform.transform", {"type": 'E', "value": 'PRESS'},
+ {"properties": [("mode", 'TIME_EXTEND')]}),
+ ("transform.transform", {"type": 'R', "value": 'PRESS'},
+ {"properties": [("mode", 'TIME_SCALE')]}),
+ ("marker.add", {"type": 'M', "value": 'PRESS'}, None),
+ ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_text_generic(params):
+ items = []
+ keymap = (
+ "Text Generic",
+ {"space_type": 'TEXT_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("text.start_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
+ ("text.jump", {"type": 'J', "value": 'PRESS', "ctrl": True}, None),
+ ("text.find", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
+ ("text.replace", {"type": 'H', "value": 'PRESS', "ctrl": True}, None),
+ ("text.properties", {"type": 'I', "value": 'PRESS', "ctrl": True}, None),
+ ])
+
+ return keymap
+
+
+def km_text(params):
+ items = []
+ keymap = (
+ "Text",
+ {"space_type": 'TEXT_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("text.move", {"type": 'LEFT_ARROW', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'LINE_BEGIN')]}),
+ ("text.move", {"type": 'RIGHT_ARROW', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'LINE_END')]}),
+ ("text.move", {"type": 'UP_ARROW', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'FILE_TOP')]}),
+ ("text.move", {"type": 'DOWN_ARROW', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'FILE_BOTTOM')]}),
+ ("text.move", {"type": 'LEFT_ARROW', "value": 'PRESS', "alt": True},
+ {"properties": [("type", 'PREVIOUS_WORD')]}),
+ ("text.move", {"type": 'RIGHT_ARROW', "value": 'PRESS', "alt": True},
+ {"properties": [("type", 'NEXT_WORD')]}),
+ ("wm.context_cycle_int", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("data_path", 'space_data.font_size'), ("reverse", False)]}),
+ ("wm.context_cycle_int", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("data_path", 'space_data.font_size'), ("reverse", True)]}),
+ ("wm.context_cycle_int", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True},
+ {"properties": [("data_path", 'space_data.font_size'), ("reverse", False)]}),
+ ("wm.context_cycle_int", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True},
+ {"properties": [("data_path", 'space_data.font_size'), ("reverse", True)]}),
+ ("text.new", {"type": 'N', "value": 'PRESS', "ctrl": True}, None),
+ ])
+
+ items.extend([
+ ("text.open", {"type": 'O', "value": 'PRESS', "alt": True}, None),
+ ("text.reload", {"type": 'R', "value": 'PRESS', "alt": True}, None),
+ ("text.save", {"type": 'S', "value": 'PRESS', "alt": True}, None),
+ ("text.save_as", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
+ ("text.run_script", {"type": 'P', "value": 'PRESS', "alt": True}, None),
+ ("text.cut", {"type": 'X', "value": 'PRESS', "ctrl": True}, None),
+ ("text.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
+ ("text.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
+ ("text.cut", {"type": 'DEL', "value": 'PRESS', "shift": True}, None),
+ ("text.copy", {"type": 'INSERT', "value": 'PRESS', "ctrl": True}, None),
+ ("text.paste", {"type": 'INSERT', "value": 'PRESS', "shift": True}, None),
+ ("text.duplicate_line", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ ("text.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, None),
+ ("text.select_line", {"type": 'A', "value": 'PRESS', "shift": True, "ctrl": True}, None),
+ ("text.select_word", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
+ ("text.move_lines", {"type": 'UP_ARROW', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("direction", 'UP')]}),
+ ("text.move_lines", {"type": 'DOWN_ARROW', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("direction", 'DOWN')]}),
+ ("text.indent", {"type": 'TAB', "value": 'PRESS'}, None),
+ ("text.unindent", {"type": 'TAB', "value": 'PRESS', "shift": True}, None),
+ ("text.uncomment", {"type": 'D', "value": 'PRESS', "shift": True, "ctrl": True}, None),
+ ("text.move", {"type": 'HOME', "value": 'PRESS'},
+ {"properties": [("type", 'LINE_BEGIN')]}),
+ ("text.move", {"type": 'END', "value": 'PRESS'},
+ {"properties": [("type", 'LINE_END')]}),
+ ("text.move", {"type": 'E', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'LINE_END')]}),
+ ("text.move", {"type": 'E', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("type", 'LINE_END')]}),
+ ("text.move", {"type": 'LEFT_ARROW', "value": 'PRESS'},
+ {"properties": [("type", 'PREVIOUS_CHARACTER')]}),
+ ("text.move", {"type": 'RIGHT_ARROW', "value": 'PRESS'},
+ {"properties": [("type", 'NEXT_CHARACTER')]}),
+ ("text.move", {"type": 'LEFT_ARROW', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'PREVIOUS_WORD')]}),
+ ("text.move", {"type": 'RIGHT_ARROW', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'NEXT_WORD')]}),
+ ("text.move", {"type": 'UP_ARROW', "value": 'PRESS'},
+ {"properties": [("type", 'PREVIOUS_LINE')]}),
+ ("text.move", {"type": 'DOWN_ARROW', "value": 'PRESS'},
+ {"properties": [("type", 'NEXT_LINE')]}),
+ ("text.move", {"type": 'PAGE_UP', "value": 'PRESS'},
+ {"properties": [("type", 'PREVIOUS_PAGE')]}),
+ ("text.move", {"type": 'PAGE_DOWN', "value": 'PRESS'},
+ {"properties": [("type", 'NEXT_PAGE')]}),
+ ("text.move", {"type": 'HOME', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'FILE_TOP')]}),
+ ("text.move", {"type": 'END', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'FILE_BOTTOM')]}),
+ ("text.move_select", {"type": 'HOME', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'LINE_BEGIN')]}),
+ ("text.move_select", {"type": 'END', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'LINE_END')]}),
+ ("text.move_select", {"type": 'LEFT_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'PREVIOUS_CHARACTER')]}),
+ ("text.move_select", {"type": 'RIGHT_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'NEXT_CHARACTER')]}),
+ ("text.move_select", {"type": 'LEFT_ARROW', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("type", 'PREVIOUS_WORD')]}),
+ ("text.move_select", {"type": 'RIGHT_ARROW', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("type", 'NEXT_WORD')]}),
+ ("text.move_select", {"type": 'UP_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'PREVIOUS_LINE')]}),
+ ("text.move_select", {"type": 'DOWN_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'NEXT_LINE')]}),
+ ("text.move_select", {"type": 'PAGE_UP', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'PREVIOUS_PAGE')]}),
+ ("text.move_select", {"type": 'PAGE_DOWN', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'NEXT_PAGE')]}),
+ ("text.move_select", {"type": 'HOME', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("type", 'FILE_TOP')]}),
+ ("text.move_select", {"type": 'END', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("type", 'FILE_BOTTOM')]}),
+ ("text.delete", {"type": 'DEL', "value": 'PRESS'},
+ {"properties": [("type", 'NEXT_CHARACTER')]}),
+ ("text.delete", {"type": 'BACK_SPACE', "value": 'PRESS'},
+ {"properties": [("type", 'PREVIOUS_CHARACTER')]}),
+ ("text.delete", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'PREVIOUS_CHARACTER')]}),
+ ("text.delete", {"type": 'DEL', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'NEXT_WORD')]}),
+ ("text.delete", {"type": 'BACK_SPACE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'PREVIOUS_WORD')]}),
+ ("text.overwrite_toggle", {"type": 'INSERT', "value": 'PRESS'}, None),
+ ("text.scroll_bar", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("text.scroll_bar", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
+ ("text.scroll", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
+ ("text.scroll", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
+ ("text.selection_set", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("text.cursor_set", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("text.selection_set", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("select", True)]}),
+ ("text.scroll", {"type": 'WHEELUPMOUSE', "value": 'PRESS'},
+ {"properties": [("lines", -1)]}),
+ ("text.scroll", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS'},
+ {"properties": [("lines", 1)]}),
+ ("text.line_break", {"type": 'RET', "value": 'PRESS'}, None),
+ ("text.line_break", {"type": 'NUMPAD_ENTER', "value": 'PRESS'}, None),
+ op_menu("TEXT_MT_toolbox", {"type": 'RIGHTMOUSE', "value": 'PRESS', "any": True}),
+ ("text.autocomplete", {"type": 'SPACE', "value": 'PRESS', "ctrl": True}, None),
+ ("text.line_number", {"type": 'TEXTINPUT', "value": 'ANY', "any": True}, None),
+ ("text.insert", {"type": 'TEXTINPUT', "value": 'ANY', "any": True}, None),
+ ])
+
+ return keymap
+
+
+def km_sequencercommon(_params):
+ items = []
+ keymap = (
+ "SequencerCommon",
+ {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("sequencer.properties", {"type": 'N', "value": 'PRESS'}, None),
+ ("wm.context_toggle", {"type": 'O', "value": 'PRESS', "shift": True},
+ {"properties": [("data_path", 'scene.sequence_editor.show_overlay')]}),
+ #("sequencer.view_toggle", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, None),
+ ])
+
+ return keymap
+
+
+def km_sequencer(params):
+ items = []
+ keymap = (
+ "Sequencer",
+ {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("sequencer.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("sequencer.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("sequencer.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("sequencer.cut", {"type": 'K', "value": 'PRESS'},
+ {"properties": [("type", 'SOFT')]}),
+ ("sequencer.cut", {"type": 'K', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'HARD')]}),
+ ("sequencer.mute", {"type": 'M', "value": 'PRESS'},
+ {"properties": [("unselected", False)]}),
+ ("sequencer.mute", {"type": 'M', "value": 'PRESS', "shift": True},
+ {"properties": [("unselected", True)]}),
+ ("sequencer.unmute", {"type": 'M', "value": 'PRESS', "alt": True},
+ {"properties": [("unselected", False)]}),
+ ("sequencer.unmute", {"type": 'M', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("unselected", True)]}),
+ ("sequencer.lock", {"type": 'L', "value": 'PRESS', "shift": True}, None),
+ ("sequencer.unlock", {"type": 'L', "value": 'PRESS', "shift": True, "alt": True}, None),
+ ("sequencer.reassign_inputs", {"type": 'R', "value": 'PRESS'}, None),
+ ("sequencer.reload", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
+ ("sequencer.reload", {"type": 'R', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("adjust_length", True)]}),
+ ("sequencer.offset_clear", {"type": 'O', "value": 'PRESS', "alt": True}, None),
+ ("sequencer.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ ("sequencer.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("sequencer.delete", {"type": 'DEL', "value": 'PRESS'}, None),
+ ("sequencer.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
+ ("sequencer.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
+ ("sequencer.images_separate", {"type": 'Y', "value": 'PRESS'}, None),
+ ("sequencer.meta_toggle", {"type": 'TAB', "value": 'PRESS'}, None),
+ ("sequencer.meta_make", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
+ ("sequencer.meta_separate", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None),
+ ("sequencer.view_all", {"type": 'A', "value": 'PRESS'}, None),
+ ("sequencer.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
+ ("sequencer.view_selected", {"type": 'F', "value": 'PRESS'}, None),
+ ("sequencer.view_frame", {"type": 'NUMPAD_0', "value": 'PRESS'}, None),
+ ("sequencer.strip_jump", {"type": 'PAGE_UP', "value": 'PRESS'},
+ {"properties": [("next", True), ("center", False)]}),
+ ("sequencer.strip_jump", {"type": 'PAGE_DOWN', "value": 'PRESS'},
+ {"properties": [("next", False), ("center", False)]}),
+ ("sequencer.strip_jump", {"type": 'PAGE_UP', "value": 'PRESS', "alt": True},
+ {"properties": [("next", True), ("center", True)]}),
+ ("sequencer.strip_jump", {"type": 'PAGE_DOWN', "value": 'PRESS', "alt": True},
+ {"properties": [("next", False), ("center", True)]}),
+ ("sequencer.swap", {"type": 'LEFT_ARROW', "value": 'PRESS', "alt": True},
+ {"properties": [("side", 'LEFT')]}),
+ ("sequencer.swap", {"type": 'RIGHT_ARROW', "value": 'PRESS', "alt": True},
+ {"properties": [("side", 'RIGHT')]}),
+ ("sequencer.gap_remove", {"type": 'BACK_SPACE', "value": 'PRESS'},
+ {"properties": [("all", False)]}),
+ ("sequencer.gap_remove", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True},
+ {"properties": [("all", True)]}),
+ ("sequencer.gap_insert", {"type": 'EQUAL', "value": 'PRESS', "shift": True}, None),
+ ("sequencer.snap", {"type": 'S', "value": 'PRESS', "shift": True}, None),
+ ("sequencer.swap_inputs", {"type": 'S', "value": 'PRESS', "alt": True}, None),
+ *(
+ (("sequencer.cut_multicam",
+ {"type": NUMBERS_1[i], "value": 'PRESS'},
+ {"properties": [("camera", i + 1)]})
+ for i in range(10)
+ )
+ ),
+ ("sequencer.select", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("extend", False), ("linked_handle", False), ("left_right", 'NONE'), ("linked_time", False)]}),
+ ("sequencer.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("extend", True), ("linked_handle", False), ("left_right", 'NONE'), ("linked_time", False)]}),
+ ("sequencer.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
+ {"properties": [("extend", False), ("linked_handle", True), ("left_right", 'NONE'), ("linked_time", False)]}),
+ ("sequencer.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("extend", True), ("linked_handle", True), ("left_right", 'NONE'), ("linked_time", False)]}),
+ ("sequencer.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("extend", False), ("linked_handle", False), ("left_right", 'MOUSE'), ("linked_time", True)]}),
+ ("sequencer.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("extend", True), ("linked_handle", False), ("left_right", 'NONE'), ("linked_time", True)]}),
+ ("sequencer.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True}, None),
+ ("sequencer.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True}, None),
+ ("sequencer.select_linked_pick", {"type": 'L', "value": 'PRESS'},
+ {"properties": [("extend", False)]}),
+ ("sequencer.select_linked_pick", {"type": 'L', "value": 'PRESS', "shift": True},
+ {"properties": [("extend", True)]}),
+ ("sequencer.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
+ ("sequencer.select_box", {"type": 'B', "value": 'PRESS'}, None),
+ ("sequencer.select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}, None),
+ op_menu("SEQUENCER_MT_add", {"type": 'A', "value": 'PRESS', "shift": True}),
+ op_menu("SEQUENCER_MT_change", {"type": 'C', "value": 'PRESS'}),
+ ("sequencer.slip", {"type": 'S', "value": 'PRESS'}, None),
+ ("wm.context_set_int", {"type": 'O', "value": 'PRESS'},
+ {"properties": [("data_path", 'scene.sequence_editor.overlay_frame'), ("value", 0)]}),
+ ("transform.seq_slide", {"type": 'G', "value": 'PRESS'}, None),
+ ("transform.seq_slide", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("transform.transform", {"type": 'E', "value": 'PRESS'},
+ {"properties": [("mode", 'TIME_EXTEND')]}),
+ ("marker.add", {"type": 'M', "value": 'PRESS'}, None),
+ ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_sequencerpreview(params):
+ items = []
+ keymap = (
+ "SequencerPreview",
+ {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("sequencer.view_all_preview", {"type": 'A', "value": 'PRESS'}, None),
+ ("sequencer.view_all_preview", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
+ ("sequencer.view_ghost_border", {"type": 'O', "value": 'PRESS'}, None),
+ ("sequencer.view_zoom_ratio", {"type": 'NUMPAD_1', "value": 'PRESS'},
+ {"properties": [("ratio", 1.0)]}),
+ ("sequencer.sample", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_console(params):
+ items = []
+ keymap = (
+ "Console",
+ {"space_type": 'CONSOLE', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("console.move", {"type": 'LEFT_ARROW', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'PREVIOUS_WORD')]}),
+ ("console.move", {"type": 'RIGHT_ARROW', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'NEXT_WORD')]}),
+ ("console.move", {"type": 'HOME', "value": 'PRESS'},
+ {"properties": [("type", 'LINE_BEGIN')]}),
+ ("console.move", {"type": 'END', "value": 'PRESS'},
+ {"properties": [("type", 'LINE_END')]}),
+ ("wm.context_cycle_int", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("data_path", 'space_data.font_size'), ("reverse", False)]}),
+ ("wm.context_cycle_int", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("data_path", 'space_data.font_size'), ("reverse", True)]}),
+ ("wm.context_cycle_int", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True},
+ {"properties": [("data_path", 'space_data.font_size'), ("reverse", False)]}),
+ ("wm.context_cycle_int", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True},
+ {"properties": [("data_path", 'space_data.font_size'), ("reverse", True)]}),
+ ("console.move", {"type": 'LEFT_ARROW', "value": 'PRESS'},
+ {"properties": [("type", 'PREVIOUS_CHARACTER')]}),
+ ("console.move", {"type": 'RIGHT_ARROW', "value": 'PRESS'},
+ {"properties": [("type", 'NEXT_CHARACTER')]}),
+ ("console.history_cycle", {"type": 'UP_ARROW', "value": 'PRESS'},
+ {"properties": [("reverse", True)]}),
+ ("console.history_cycle", {"type": 'DOWN_ARROW', "value": 'PRESS'},
+ {"properties": [("reverse", False)]}),
+ ("console.delete", {"type": 'DEL', "value": 'PRESS'},
+ {"properties": [("type", 'NEXT_CHARACTER')]}),
+ ("console.delete", {"type": 'BACK_SPACE', "value": 'PRESS'},
+ {"properties": [("type", 'PREVIOUS_CHARACTER')]}),
+ ("console.delete", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'PREVIOUS_CHARACTER')]}),
+ ("console.delete", {"type": 'DEL', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'NEXT_WORD')]}),
+ ("console.delete", {"type": 'BACK_SPACE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'PREVIOUS_WORD')]}),
+ ("console.clear_line", {"type": 'RET', "value": 'PRESS', "shift": True}, None),
+ ("console.clear_line", {"type": 'NUMPAD_ENTER', "value": 'PRESS', "shift": True}, None),
+ ("console.execute", {"type": 'RET', "value": 'PRESS'},
+ {"properties": [("interactive", True)]}),
+ ("console.execute", {"type": 'NUMPAD_ENTER', "value": 'PRESS'},
+ {"properties": [("interactive", True)]}),
+ ("console.autocomplete", {"type": 'SPACE', "value": 'PRESS', "ctrl": True}, None),
+ ("console.copy_as_script", {"type": 'C', "value": 'PRESS', "shift": True, "ctrl": True}, None),
+ ("console.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
+ ("console.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
+ ("console.select_set", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("console.select_word", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
+ ("console.insert", {"type": 'TAB', "value": 'PRESS', "ctrl": True},
+ {"properties": [("text", '\t')]}),
+ ("console.indent", {"type": 'TAB', "value": 'PRESS'}, None),
+ ("console.unindent", {"type": 'TAB', "value": 'PRESS', "shift": True}, None),
+ ("console.insert", {"type": 'TEXTINPUT', "value": 'ANY', "any": True}, None),
+ ])
+
+ return keymap
+
+
+def km_clip(params):
+ items = []
+ keymap = (
+ "Clip",
+ {"space_type": 'CLIP_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("clip.open", {"type": 'O', "value": 'PRESS', "alt": True}, None),
+ ("clip.track_markers", {"type": 'LEFT_ARROW', "value": 'PRESS', "alt": True},
+ {"properties": [("backwards", True), ("sequence", False)]}),
+ ("clip.track_markers", {"type": 'RIGHT_ARROW', "value": 'PRESS', "alt": True},
+ {"properties": [("backwards", False), ("sequence", False)]}),
+ ("clip.track_markers", {"type": 'T', "value": 'PRESS', "ctrl": True},
+ {"properties": [("backwards", False), ("sequence", True)]}),
+ ("clip.track_markers", {"type": 'T', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("backwards", True), ("sequence", True)]}),
+ ("wm.context_toggle_enum", {"type": 'TAB', "value": 'PRESS'},
+ {"properties": [("data_path", 'space_data.mode'), ("value_1", 'TRACKING'), ("value_2", 'MASK')]}),
+ ("clip.solve_camera", {"type": 'S', "value": 'PRESS', "shift": True}, None),
+ ("clip.prefetch", {"type": 'P', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_clip_editor(params):
+ items = []
+ keymap = (
+ "Clip Editor",
+ {"space_type": 'CLIP_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("clip.view_pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
+ ("clip.view_pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "shift": True}, None),
+ ("clip.view_pan", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
+ ("clip.view_zoom", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "ctrl": True}, None),
+ ("clip.view_zoom", {"type": 'TRACKPADZOOM', "value": 'ANY'}, None),
+ ("clip.view_zoom", {"type": 'TRACKPADPAN', "value": 'ANY', "ctrl": True}, None),
+ ("clip.view_zoom_in", {"type": 'WHEELINMOUSE', "value": 'PRESS'}, None),
+ ("clip.view_zoom_out", {"type": 'WHEELOUTMOUSE', "value": 'PRESS'}, None),
+ ("clip.view_zoom_in", {"type": 'NUMPAD_PLUS', "value": 'PRESS'}, None),
+ ("clip.view_zoom_out", {"type": 'NUMPAD_MINUS', "value": 'PRESS'}, None),
+ ("clip.view_zoom_ratio", {"type": 'NUMPAD_8', "value": 'PRESS', "ctrl": True},
+ {"properties": [("ratio", 8.0)]}),
+ ("clip.view_zoom_ratio", {"type": 'NUMPAD_4', "value": 'PRESS', "ctrl": True},
+ {"properties": [("ratio", 4.0)]}),
+ ("clip.view_zoom_ratio", {"type": 'NUMPAD_2', "value": 'PRESS', "ctrl": True},
+ {"properties": [("ratio", 2.0)]}),
+ ("clip.view_zoom_ratio", {"type": 'NUMPAD_8', "value": 'PRESS', "shift": True},
+ {"properties": [("ratio", 8.0)]}),
+ ("clip.view_zoom_ratio", {"type": 'NUMPAD_4', "value": 'PRESS', "shift": True},
+ {"properties": [("ratio", 4.0)]}),
+ ("clip.view_zoom_ratio", {"type": 'NUMPAD_2', "value": 'PRESS', "shift": True},
+ {"properties": [("ratio", 2.0)]}),
+ ("clip.view_zoom_ratio", {"type": 'NUMPAD_1', "value": 'PRESS'},
+ {"properties": [("ratio", 1.0)]}),
+ ("clip.view_zoom_ratio", {"type": 'NUMPAD_2', "value": 'PRESS'},
+ {"properties": [("ratio", 0.5)]}),
+ ("clip.view_zoom_ratio", {"type": 'NUMPAD_4', "value": 'PRESS'},
+ {"properties": [("ratio", 0.25)]}),
+ ("clip.view_zoom_ratio", {"type": 'NUMPAD_8', "value": 'PRESS'},
+ {"properties": [("ratio", 0.125)]}),
+ ("clip.view_all", {"type": 'A', "value": 'PRESS'}, None),
+ ("clip.view_selected", {"type": 'F', "value": 'PRESS'}, None),
+ ("clip.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
+ ("clip.view_ndof", {"type": 'NDOF_MOTION', "value": 'ANY'}, None),
+ ("clip.frame_jump", {"type": 'LEFT_ARROW', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("position", 'PATHSTART')]}),
+ ("clip.frame_jump", {"type": 'RIGHT_ARROW', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("position", 'PATHEND')]}),
+ ("clip.frame_jump", {"type": 'LEFT_ARROW', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("position", 'FAILEDPREV')]}),
+ ("clip.frame_jump", {"type": 'RIGHT_ARROW', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("position", 'PATHSTART')]}),
+ ("clip.change_frame", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("clip.select", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("extend", False)]}),
+ ("clip.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("extend", True)]}),
+ ("clip.select_box", {"type": 'Q', "value": 'PRESS'}, None),
+ ("clip.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("clip.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("clip.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ op_menu("CLIP_MT_select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}),
+ ("clip.add_marker_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None),
+ ("clip.delete_marker", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True}, None),
+ ("clip.delete_marker", {"type": 'DEL', "value": 'PRESS', "shift": True}, None),
+ ("clip.slide_marker", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("clip.disable_markers", {"type": 'D', "value": 'PRESS', "shift": True},
+ {"properties": [("action", 'TOGGLE')]}),
+ ("clip.delete_track", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("clip.delete_track", {"type": 'DEL', "value": 'PRESS'}, None),
+ ("clip.lock_tracks", {"type": 'L', "value": 'PRESS', "ctrl": True},
+ {"properties": [("action", 'LOCK')]}),
+ ("clip.lock_tracks", {"type": 'L', "value": 'PRESS', "alt": True},
+ {"properties": [("action", 'UNLOCK')]}),
+ ("clip.hide_tracks", {"type": 'H', "value": 'PRESS', "ctrl": True},
+ {"properties": [("unselected", False)]}),
+ ("clip.hide_tracks", {"type": 'H', "value": 'PRESS', "shift": True},
+ {"properties": [("unselected", True)]}),
+ ("clip.hide_tracks_clear", {"type": 'H', "value": 'PRESS', "alt": True}, None),
+ ("clip.slide_plane_marker", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("clip.keyframe_insert", {"type": 'S', "value": 'PRESS'}, None),
+ ("clip.keyframe_delete", {"type": 'S', "value": 'PRESS', "alt": True}, None),
+ ("clip.join_tracks", {"type": 'J', "value": 'PRESS', "ctrl": True}, None),
+ op_menu("CLIP_MT_tracking_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ ("wm.context_toggle", {"type": 'L', "value": 'PRESS'},
+ {"properties": [("data_path", 'space_data.lock_selection')]}),
+ ("wm.context_toggle", {"type": 'D', "value": 'PRESS', "alt": True},
+ {"properties": [("data_path", 'space_data.show_disabled')]}),
+ ("wm.context_toggle", {"type": 'S', "value": 'PRESS', "alt": True},
+ {"properties": [("data_path", 'space_data.show_marker_search')]}),
+ ("wm.context_toggle", {"type": 'M', "value": 'PRESS'},
+ {"properties": [("data_path", 'space_data.use_mute_footage')]}),
+ ("transform.translate", {"type": 'W', "value": 'PRESS'}, None),
+ ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("transform.resize", {"type": 'R', "value": 'PRESS'}, None),
+ ("transform.rotate", {"type": 'E', "value": 'PRESS'}, None),
+ ("clip.clear_track_path", {"type": 'T', "value": 'PRESS', "alt": True},
+ {"properties": [("action", 'REMAINED'), ("clear_active", False)]}),
+ ("clip.clear_track_path", {"type": 'T', "value": 'PRESS', "shift": True},
+ {"properties": [("action", 'UPTO'), ("clear_active", False)]}),
+ ("clip.clear_track_path", {"type": 'T', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("action", 'ALL'), ("clear_active", False)]}),
+ op_menu_pie("CLIP_MT_pivot_pie", {"type": 'PERIOD', "value": 'PRESS'}),
+ ("clip.copy_tracks", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
+ ("clip.paste_tracks", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
+ ])
+
+ return keymap
+
+
+def km_clip_graph_editor(params):
+ items = []
+ keymap = (
+ "Clip Graph Editor",
+ {"space_type": 'CLIP_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("clip.graph_select", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("extend", False)]}),
+ ("clip.graph_select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("extend", True)]}),
+ ("clip.graph_select_box", {"type": 'Q', "value": 'PRESS'}, None),
+ ("clip.graph_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, None),
+ # ("clip.graph_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'DESELECT')]}),
+ # ("clip.graph_select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("clip.graph_delete_curve", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("clip.graph_delete_curve", {"type": 'DEL', "value": 'PRESS'}, None),
+ ("clip.graph_delete_knot", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True}, None),
+ ("clip.graph_delete_knot", {"type": 'DEL', "value": 'PRESS', "shift": True}, None),
+ ("clip.graph_view_all", {"type": 'A', "value": 'PRESS'}, None),
+ ("clip.graph_view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
+ ("clip.graph_center_current_frame", {"type": 'NUMPAD_0', "value": 'PRESS'}, None),
+ ("wm.context_toggle", {"type": 'L', "value": 'PRESS'},
+ {"properties": [("data_path", 'space_data.lock_time_cursor')]}),
+ ("clip.clear_track_path", {"type": 'T', "value": 'PRESS', "alt": True},
+ {"properties": [("action", 'REMAINED'), ("clear_active", True)]}),
+ ("clip.clear_track_path", {"type": 'T', "value": 'PRESS', "shift": True},
+ {"properties": [("action", 'UPTO'), ("clear_active", True)]}),
+ ("clip.clear_track_path", {"type": 'T', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("action", 'ALL'), ("clear_active", True)]}),
+ ("clip.graph_disable_markers", {"type": 'D', "value": 'PRESS', "shift": True},
+ {"properties": [("action", 'TOGGLE')]}),
+ ("transform.translate", {"type": 'W', "value": 'PRESS'}, None),
+ ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("transform.resize", {"type": 'R', "value": 'PRESS'}, None),
+ ("transform.rotate", {"type": 'E', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_clip_dopesheet_editor(_params):
+ items = []
+ keymap = (
+ "Clip Dopesheet Editor",
+ {"space_type": 'CLIP_EDITOR', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("clip.dopesheet_select_channel", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("extend", True)]}),
+ ("clip.dopesheet_view_all", {"type": 'HOME', "value": 'PRESS'}, None),
+ ("clip.dopesheet_view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+# ------------------------------------------------------------------------------
+# Animation
+
+
+def km_frames(params):
+ items = []
+ keymap = (
+ "Frames",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Frame offsets
+ ("screen.frame_jump", {"type": 'MEDIA_LAST', "value": 'PRESS'},
+ {"properties": [("end", True)]}),
+ ("screen.frame_jump", {"type": 'MEDIA_FIRST', "value": 'PRESS'},
+ {"properties": [("end", False)]}),
+ ("screen.animation_play", {"type": 'SPACE', "value": 'PRESS'}, None),
+ ("screen.animation_cancel", {"type": 'ESC', "value": 'PRESS'}, None),
+ ("screen.animation_play", {"type": 'MEDIA_PLAY', "value": 'PRESS'}, None),
+ ("screen.animation_cancel", {"type": 'MEDIA_STOP', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_animation(params):
+ items = []
+ keymap = (
+ "Animation",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("wm.context_toggle", {"type": 'T', "value": 'PRESS', "ctrl": True},
+ {"properties": [("data_path", 'space_data.show_seconds')]}),
+ ])
+
+ return keymap
+
+
+def km_animation_channels(params):
+ items = []
+ keymap = (
+ "Animation Channels",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+
+ items.extend([
+ # Click select.
+ ("anim.channels_click", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("anim.channels_click", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("extend", True)]}),
+ ("anim.channels_click", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("children_only", True)]}),
+ # Rename.
+ ("anim.channels_rename", {"type": 'RET', "value": 'PRESS'}, None),
+ ("anim.channels_rename", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
+ # Select keys.
+ ("anim.channel_select_keys", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
+ ("anim.channel_select_keys", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "shift": True},
+ {"properties": [("extend", True)]}),
+ # Find (setting the name filter).
+ ("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
+ # Selection.
+ ("anim.channels_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("anim.channels_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("anim.channels_select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("anim.channels_select_box", {"type": 'B', "value": 'PRESS'}, None),
+ ("anim.channels_select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ # Delete.
+ ("anim.channels_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("anim.channels_delete", {"type": 'DEL', "value": 'PRESS'}, None),
+ # Settings.
+ ("anim.channels_setting_toggle", {"type": 'W', "value": 'PRESS', "shift": True}, None),
+ ("anim.channels_setting_enable", {"type": 'W', "value": 'PRESS', "shift": True, "ctrl": True}, None),
+ ("anim.channels_setting_disable", {"type": 'W', "value": 'PRESS', "alt": True}, None),
+ ("anim.channels_editable_toggle", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
+ # Expand/collapse.
+ ("anim.channels_expand", {"type": 'RIGHT_ARROW', "value": 'PRESS'}, None),
+ ("anim.channels_collapse", {"type": 'LEFT_ARROW', "value": 'PRESS'}, None),
+ ("anim.channels_expand", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True},
+ {"properties": [("all", False)]}),
+ ("anim.channels_collapse", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True},
+ {"properties": [("all", False)]}),
+ # Move.
+ ("anim.channels_move", {"type": 'PAGE_UP', "value": 'PRESS'},
+ {"properties": [("direction", 'UP')]}),
+ ("anim.channels_move", {"type": 'PAGE_DOWN', "value": 'PRESS'},
+ {"properties": [("direction", 'DOWN')]}),
+ ("anim.channels_move", {"type": 'PAGE_UP', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'TOP')]}),
+ ("anim.channels_move", {"type": 'PAGE_DOWN', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'BOTTOM')]}),
+ # Group.
+ ("anim.channels_group", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
+ ("anim.channels_ungroup", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None),
+ # Menus.
+ op_menu("DOPESHEET_MT_channel_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ ])
+
+ return keymap
+
+
+# ------------------------------------------------------------------------------
+# Modes
+
+
+def km_grease_pencil(_params):
+ items = []
+ keymap = (
+ "Grease Pencil",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Draw
+ ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "key_modifier": 'D'},
+ {"properties": [("mode", 'DRAW'), ("wait_for_input", False)]}),
+ # Draw - straight lines
+ ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True, "key_modifier": 'D'},
+ {"properties": [("mode", 'DRAW_STRAIGHT'), ("wait_for_input", False)]}),
+ # Draw - poly lines
+ ("gpencil.annotate", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "alt": True, "key_modifier": 'D'},
+ {"properties": [("mode", 'DRAW_POLY'), ("wait_for_input", False)]}),
+ # Erase
+ ("gpencil.annotate", {"type": 'RIGHTMOUSE', "value": 'PRESS', "key_modifier": 'D'},
+ {"properties": [("mode", 'ERASER'), ("wait_for_input", False)]}),
+
+ # Add blank frame (B because it's easy to reach from D).
+ ("gpencil.blank_frame_add", {"type": 'B', "value": 'PRESS', "key_modifier": 'D'}, None),
+ # Delete active frame - for easier video tutorials/review sessions.
+ # This works even when not in edit mode.
+ ("gpencil.active_frames_delete_all", {"type": 'BACK_SPACE', "value": 'PRESS', "key_modifier": 'D'}, None),
+ ("gpencil.active_frames_delete_all", {"type": 'DEL', "value": 'PRESS', "key_modifier": 'D'}, None),
+ ])
+
+ return keymap
+
+
+def _grease_pencil_selection(params):
+ return [
+ # Select all
+ ("gpencil.select_box", {"type": 'A', "value": 'PRESS', "ctrl": True}, None),
+ # Circle select
+ ("gpencil.select_circle", {"type": 'C', "value": 'PRESS'}, None),
+ # Box select
+ ("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None),
+ # Lasso select
+ ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True},
+ {"properties": [("mode", 'ADD')]}),
+ ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True},
+ {"properties": [("mode", 'SUB')]}),
+ ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True},
+ {"properties": [("mode", 'ADD')]}),
+ ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True},
+ {"properties": [("mode", 'SUB')]}),
+ ("gpencil.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("extend", True), ("toggle", True)]}),
+ # Whole stroke select
+ ("gpencil.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
+ {"properties": [("entire_strokes", True)]}),
+ ("gpencil.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("extend", True), ("entire_strokes", True)]}),
+ # Select linked
+ ("gpencil.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, None),
+ ("gpencil.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "ctrl": True}, None),
+ # Select alternate
+ ("gpencil.select_alternate", {"type": 'L', "value": 'PRESS', "shift": True}, None),
+ # Select grouped
+ ("gpencil.select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}, None),
+ # Select more/less
+ ("gpencil.select_more", {"type": 'UP_ARROW', "value": 'PRESS'}, None),
+ ("gpencil.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS'}, None),
+ ]
+
+
+def _grease_pencil_display():
+ return [
+ ("wm.context_toggle", {"type": 'Q', "value": 'PRESS', "shift": True},
+ {"properties": [("data_path", 'space_data.overlay.use_gpencil_edit_lines')]}),
+ ("wm.context_toggle", {"type": 'Q', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("data_path", 'space_data.overlay.use_gpencil_multiedit_line_only')]}),
+ ]
+
+
+def km_grease_pencil_stroke_edit_mode(params):
+ items = []
+ keymap = (
+ "Grease Pencil Stroke Edit Mode",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Interpolation
+ ("gpencil.interpolate", {"type": 'E', "value": 'PRESS', "ctrl": True, "alt": True}, None),
+ ("gpencil.interpolate_sequence", {"type": 'E', "value": 'PRESS', "shift": True, "ctrl": True}, None),
+ # Normal select
+ ("gpencil.select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ # Selection
+ *_grease_pencil_selection(params),
+ # Duplicate and move selected points
+ ("gpencil.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ # Extrude and move selected points
+ ("gpencil.extrude_move", {"type": 'E', "value": 'PRESS'}, None),
+ # Delete
+ op_menu("VIEW3D_MT_edit_gpencil_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}),
+ op_menu("VIEW3D_MT_edit_gpencil_delete", {"type": 'DEL', "value": 'PRESS'}),
+ ("gpencil.dissolve", {"type": 'BACK_SPACE', "value": 'PRESS', "ctrl": True}, None),
+ ("gpencil.dissolve", {"type": 'DEL', "value": 'PRESS', "ctrl": True}, None),
+ ("gpencil.active_frames_delete_all", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True}, None),
+ ("gpencil.active_frames_delete_all", {"type": 'DEL', "value": 'PRESS', "shift": True}, None),
+ # Context menu
+ op_menu("VIEW3D_MT_gpencil_edit_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ # Separate
+ op_menu("GPENCIL_MT_separate", {"type": 'P', "value": 'PRESS'}),
+ # Split and joint strokes
+ ("gpencil.stroke_split", {"type": 'V', "value": 'PRESS'}, None),
+ ("gpencil.stroke_join", {"type": 'J', "value": 'PRESS', "ctrl": True}, None),
+ ("gpencil.stroke_join", {"type": 'J', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("type", 'JOINCOPY')]}),
+ # Copy + paset
+ ("gpencil.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
+ ("gpencil.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
+ # Snap
+ op_menu("GPENCIL_MT_snap", {"type": 'S', "value": 'PRESS', "shift": True}),
+ # Show/hide
+ ("gpencil.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
+ ("gpencil.hide", {"type": 'H', "value": 'PRESS', "ctrl": True},
+ {"properties": [("unselected", False)]}),
+ ("gpencil.hide", {"type": 'H', "value": 'PRESS', "shift": True},
+ {"properties": [("unselected", True)]}),
+ ("gpencil.selection_opacity_toggle", {"type": 'H', "value": 'PRESS', "ctrl": True}, None),
+ # Display
+ *_grease_pencil_display(),
+ # Isolate layer
+ ("gpencil.layer_isolate", {"type": 'NUMPAD_ASTERIX', "value": 'PRESS'}, None),
+ # Move to layer
+ ("gpencil.move_to_layer", {"type": 'G', "value": 'PRESS'}, None),
+ # Transform tools
+ ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ # Vertex group menu
+ op_menu("GPENCIL_MT_gpencil_vertex_group", {"type": 'G', "value": 'PRESS', "ctrl": True}),
+ # Select mode
+ ("gpencil.selectmode_toggle", {"type": 'ONE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 0)]}),
+ ("gpencil.selectmode_toggle", {"type": 'TWO', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 1)]}),
+ ("gpencil.selectmode_toggle", {"type": 'THREE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 2)]}),
+ # Tools
+ *_template_items_basic_tools(),
+ ])
+
+
+ return keymap
+
+
+def km_grease_pencil_stroke_paint_mode(params):
+ items = []
+ keymap = (
+ "Grease Pencil Stroke Paint Mode",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Brush strength
+ ("wm.radial_control", {"type": 'U', "value": 'PRESS', "shift": True},
+ {"properties": [("data_path_primary", 'tool_settings.gpencil_paint.brush.gpencil_settings.pen_strength')]}),
+ # Brush size
+ ("wm.radial_control", {"type": 'S', "value": 'PRESS'},
+ {"properties": [("data_path_primary", 'tool_settings.gpencil_paint.brush.size')]}),
+ # Draw context menu
+ op_panel("VIEW3D_PT_gpencil_draw_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ # Draw delete menu
+ op_menu("GPENCIL_MT_gpencil_draw_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}),
+ op_menu("GPENCIL_MT_gpencil_draw_delete", {"type": 'DEL', "value": 'PRESS'}),
+ ])
+
+ return keymap
+
+
+def km_grease_pencil_stroke_paint_draw_brush(params):
+ items = []
+ keymap = (
+ "Grease Pencil Stroke Paint (Draw brush)",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Draw
+ ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("mode", 'DRAW'), ("wait_for_input", False)]}),
+ ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("mode", 'DRAW'), ("wait_for_input", False)]}),
+ # Draw - straight lines
+ ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
+ {"properties": [("mode", 'DRAW_STRAIGHT'), ("wait_for_input", False)]}),
+ # Draw - poly lines
+ ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("mode", 'DRAW_POLY'), ("wait_for_input", False)]}),
+ # Erase
+ ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'ERASER'), ("wait_for_input", False)]}),
+ # Constrain Guides Speedlines
+ # Freehand
+ ("gpencil.draw", {"type": 'O', "value": 'PRESS'}, None),
+ ("gpencil.draw", {"type": 'J', "value": 'PRESS'}, None),
+ ("gpencil.draw", {"type": 'J', "value": 'PRESS', "alt": True}, None),
+ ("gpencil.draw", {"type": 'J', "value": 'PRESS', "shift": True}, None),
+ ("gpencil.draw", {"type": 'K', "value": 'PRESS'}, None),
+ ("gpencil.draw", {"type": 'K', "value": 'PRESS', "alt": True}, None),
+ ("gpencil.draw", {"type": 'K', "value": 'PRESS', "shift": True}, None),
+ ("gpencil.draw", {"type": 'L', "value": 'PRESS'}, None),
+ ("gpencil.draw", {"type": 'L', "value": 'PRESS', "alt": True}, None),
+ ("gpencil.draw", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
+ ("gpencil.draw", {"type": 'V', "value": 'PRESS'}, None),
+ # Mirror or flip
+ ("gpencil.draw", {"type": 'M', "value": 'PRESS'}, None),
+ # Mode
+ ("gpencil.draw", {"type": 'C', "value": 'PRESS'}, None),
+ # Set reference point
+ ("gpencil.draw", {"type": 'C', "value": 'PRESS', "alt": True}, None),
+ # Tablet Mappings for Drawing ------------------ */
+ # For now, only support direct drawing using the eraser, as most users using a tablet
+ # may still want to use that as their primary pointing device!
+ ("gpencil.draw", {"type": 'ERASER', "value": 'PRESS'},
+ {"properties": [("mode", 'ERASER'), ("wait_for_input", False)]}),
+ # Selected (used by eraser)
+ ])
+
+ return keymap
+
+
+def km_grease_pencil_stroke_paint_erase(params):
+ items = []
+ keymap = (
+ "Grease Pencil Stroke Paint (Erase)",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Erase
+ ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("mode", 'ERASER'), ("wait_for_input", False)]}),
+ ("gpencil.draw", {"type": 'ERASER', "value": 'PRESS'},
+ {"properties": [("mode", 'ERASER'), ("wait_for_input", False)]}),
+ # Box select (used by eraser)
+ ("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None),
+ # Lasso select
+ ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
+ ])
+
+ return keymap
+
+
+def km_grease_pencil_stroke_paint_fill(params):
+ items = []
+ keymap = (
+ "Grease Pencil Stroke Paint (Fill)",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Fill
+ ("gpencil.fill", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("on_back", False)]}),
+ ("gpencil.fill", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("on_back", True)]}),
+ # If press alternate key, the brush now it's for drawing areas
+ ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'DRAW'), ("wait_for_input", False), ("disable_straight", True)]}),
+ # If press alternative key, the brush now it's for drawing lines
+ ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
+ {"properties": [("mode", 'DRAW'), ("wait_for_input", False), ("disable_straight", True), ("disable_fill", True)]}),
+ # Lasso select
+ ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
+ ])
+
+ return keymap
+
+
+def km_grease_pencil_stroke_sculpt_mode(params):
+ items = []
+ keymap = (
+ "Grease Pencil Stroke Sculpt Mode",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items}
+ )
+
+ items.extend([
+ # Selection
+ *_grease_pencil_selection(params),
+ # Painting
+ ("gpencil.sculpt_paint", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("wait_for_input", False)]}),
+ ("gpencil.sculpt_paint", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("wait_for_input", False)]}),
+ ("gpencil.sculpt_paint", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("wait_for_input", False)]}),
+ # Brush strength
+ ("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True},
+ {"properties": [("data_path_primary", 'tool_settings.gpencil_sculpt.brush.strength')]}),
+ # Brush size
+ ("wm.radial_control", {"type": 'F', "value": 'PRESS'},
+ {"properties": [("data_path_primary", 'tool_settings.gpencil_sculpt.brush.size')]}),
+ # Context menu
+ op_panel("VIEW3D_PT_gpencil_sculpt_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ # Display
+ *_grease_pencil_display(),
+ ])
+
+ return keymap
+
+
+def km_grease_pencil_stroke_weight_mode(params):
+ items = []
+ keymap = (
+ "Grease Pencil Stroke Weight Mode",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Selection
+ *_grease_pencil_selection(params),
+ # Painting
+ ("gpencil.sculpt_paint", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("wait_for_input", False)]}),
+ ("gpencil.sculpt_paint", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("wait_for_input", False)]}),
+ # Brush strength
+ ("wm.radial_control", {"type": 'U', "value": 'PRESS', "shift": True},
+ {"properties": [("data_path_primary", 'tool_settings.gpencil_sculpt.weight_brush.strength')]}),
+ # Brush sze
+ ("wm.radial_control", {"type": 'S', "value": 'PRESS'},
+ {"properties": [("data_path_primary", 'tool_settings.gpencil_sculpt.weight_brush.size')]}),
+ # Display
+ *_grease_pencil_display(),
+ ])
+
+ return keymap
+
+
+def km_face_mask(params):
+ items = []
+ keymap = (
+ "Face Mask",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("paint.face_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("paint.face_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("paint.face_select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("paint.face_select_hide", {"type": 'H', "value": 'PRESS', "ctrl": True},
+ {"properties": [("unselected", False)]}),
+ ("paint.face_select_hide", {"type": 'H', "value": 'PRESS', "shift": True},
+ {"properties": [("unselected", True)]}),
+ ("paint.face_select_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
+ ("paint.face_select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
+ ("paint.face_select_linked_pick", {"type": 'L', "value": 'PRESS'},
+ {"properties": [("deselect", False)]}),
+ ("paint.face_select_linked_pick", {"type": 'L', "value": 'PRESS', "shift": True},
+ {"properties": [("deselect", True)]}),
+ ])
+
+ return keymap
+
+
+def km_weight_paint_vertex_selection(params):
+ items = []
+ keymap = (
+ "Weight Paint Vertex Selection",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("paint.vert_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, None),
+ ])
+
+ return keymap
+
+
+def km_pose(params):
+ items = []
+ keymap = (
+ "Pose",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ *_template_items_animation(),
+ ("object.parent_set", {"type": 'P', "value": 'PRESS'}, None),
+ ("pose.hide", {"type": 'H', "value": 'PRESS', "ctrl": True},
+ {"properties": [("unselected", False)]}),
+ ("pose.hide", {"type": 'H', "value": 'PRESS', "shift": True},
+ {"properties": [("unselected", True)]}),
+ ("pose.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
+ ("pose.rot_clear", {"type": 'E', "value": 'PRESS', "alt": True}, None),
+ ("pose.loc_clear", {"type": 'W', "value": 'PRESS', "alt": True}, None),
+ ("pose.scale_clear", {"type": 'R', "value": 'PRESS', "alt": True}, None),
+ ("pose.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
+ ("pose.paste", {"type": 'V', "value": 'PRESS', "ctrl": True},
+ {"properties": [("flipped", False)]}),
+ ("pose.paste", {"type": 'V', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("flipped", True)]}),
+ ("pose.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("pose.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("pose.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("pose.select_parent", {"type": 'UP_ARROW', "value": 'PRESS'}, None),
+ ("pose.select_hierarchy", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
+ {"properties": [("direction", 'PARENT'), ("extend", False)]}),
+ ("pose.select_hierarchy", {"type": 'UP_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'PARENT'), ("extend", True)]}),
+ ("pose.select_hierarchy", {"type": 'DOWN_ARROW', "value": 'PRESS'},
+ {"properties": [("direction", 'CHILD'), ("extend", False)]}),
+ ("pose.select_hierarchy", {"type": 'DOWN_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'CHILD'), ("extend", True)]}),
+ ("pose.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, None),
+ ("pose.bone_layers", {"type": 'G', "value": 'PRESS'}, None),
+ ("anim.keyframe_insert_menu", {"type": 'S', "value": 'PRESS', "shift": True}, None),
+ ("anim.keyframe_insert_by_name", {"type": 'S', "value": 'PRESS'},
+ {"properties": [("type", 'LocRotScale')]}),
+ ("anim.keyframe_insert_by_name", {"type": 'W', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'Location')]}),
+ ("anim.keyframe_insert_by_name", {"type": 'E', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'Rotation')]}),
+ ("anim.keyframe_insert_by_name", {"type": 'R', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'Scaling')]}),
+
+ ("anim.keyframe_delete_v3d", {"type": 'S', "value": 'PRESS', "alt": True}, None),
+ ("anim.keying_set_active_set", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
+ op_menu("VIEW3D_MT_pose_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ # Tools
+ op_tool("builtin.select_box", {"type": 'Q', "value": 'PRESS'}),
+ op_tool("builtin.move", {"type": 'W', "value": 'PRESS'}),
+ op_tool("builtin.rotate", {"type": 'E', "value": 'PRESS'}),
+ op_tool("builtin.scale", {"type": 'R', "value": 'PRESS'}),
+ op_tool("builtin.scale_cage", {"type": 'R', "value": 'PRESS', "shift": True}),
+ op_tool("builtin.transform", {"type": 'T', "value": 'PRESS'}),
+ op_tool("builtin.measure", {"type": 'M', "value": 'PRESS'}),
+ ])
+
+
+ return keymap
+
+
+def km_object_mode(params):
+ items = []
+ keymap = (
+ "Object Mode",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ *_template_items_animation(),
+ ("object.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("object.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("object.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("object.select_more", {"type": 'UP_ARROW', "value": 'PRESS'}, None),
+ ("object.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS'}, None),
+ ("object.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, None),
+ ("object.select_hierarchy", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
+ {"properties": [("direction", 'PARENT'), ("extend", False)]}),
+ ("object.select_hierarchy", {"type": 'LEFT_BRACKET', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'PARENT'), ("extend", True)]}),
+ ("object.select_hierarchy", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
+ {"properties": [("direction", 'CHILD'), ("extend", False)]}),
+ ("object.select_hierarchy", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'CHILD'), ("extend", True)]}),
+ ("object.parent_set", {"type": 'P', "value": 'PRESS'}, None),
+ ("object.parent_clear", {"type": 'P', "value": 'PRESS', "shift": True}, None),
+ ("object.location_clear", {"type": 'W', "value": 'PRESS', "alt": True},
+ {"properties": [("clear_delta", False)]}),
+ ("object.rotation_clear", {"type": 'E', "value": 'PRESS', "alt": True},
+ {"properties": [("clear_delta", False)]}),
+ ("object.scale_clear", {"type": 'R', "value": 'PRESS', "alt": True},
+ {"properties": [("clear_delta", False)]}),
+ ("object.delete", {"type": 'BACK_SPACE', "value": 'PRESS'},
+ {"properties": [("use_global", False), ("confirm", False)]}),
+ ("object.delete", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True},
+ {"properties": [("use_global", True), ("confirm", False)]}),
+ ("object.delete", {"type": 'DEL', "value": 'PRESS'},
+ {"properties": [("use_global", False), ("confirm", False)]}),
+ ("object.delete", {"type": 'DEL', "value": 'PRESS', "shift": True},
+ {"properties": [("use_global", True), ("confirm", False)]}),
+ ("object.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ # Keyframing
+ ("anim.keyframe_insert_menu", {"type": 'S', "value": 'PRESS', "shift": True}, None),
+ ("anim.keyframe_insert_by_name", {"type": 'S', "value": 'PRESS'},
+ {"properties": [("type", 'LocRotScale')]}),
+ ("anim.keyframe_insert_by_name", {"type": 'W', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'Location')]}),
+ ("anim.keyframe_insert_by_name", {"type": 'E', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'Rotation')]}),
+ ("anim.keyframe_insert_by_name", {"type": 'R', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'Scaling')]}),
+ ("anim.keyframe_delete_v3d", {"type": 'S', "value": 'PRESS', "alt": True}, None),
+ ("anim.keying_set_active_set", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
+ op_menu("VIEW3D_MT_object_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ ("object.move_to_collection", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
+ ("object.link_to_collection", {"type": 'G', "value": 'PRESS', "shift": True, "ctrl": True}, None),
+ ("object.hide_view_clear", {"type": 'H', "value": 'PRESS', "alt": True}, None),
+ ("object.hide_view_set", {"type": 'H', "value": 'PRESS', "ctrl": True},
+ {"properties": [("unselected", False)]}),
+ ("object.hide_view_set", {"type": 'H', "value": 'PRESS', "shift": True},
+ {"properties": [("unselected", True)]}),
+
+ *_template_items_basic_tools(),
+ ])
+
+ return keymap
+
+
+def km_paint_curve(params):
+ items = []
+ keymap = (
+ "Paint Curve",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("paintcurve.add_point_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None),
+ ("paintcurve.select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("paintcurve.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("extend", True)]}),
+ ("paintcurve.slide", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("paintcurve.slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("align", True)]}),
+ ("paintcurve.select", {"type": 'A', "value": 'PRESS'},
+ {"properties": [("toggle", True)]}),
+ ("paintcurve.cursor", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("paintcurve.delete_point", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("paintcurve.delete_point", {"type": 'DEL', "value": 'PRESS'}, None),
+ ("paintcurve.draw", {"type": 'RET', "value": 'PRESS'}, None),
+ ("paintcurve.draw", {"type": 'NUMPAD_ENTER', "value": 'PRESS'}, None),
+ ("transform.translate", {"type": 'W', "value": 'PRESS'}, None),
+ ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
+ ("transform.rotate", {"type": 'E', "value": 'PRESS'}, None),
+ ("transform.resize", {"type": 'R', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+def km_curve(params):
+ items = []
+ keymap = (
+ "Curve",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+
+ items.extend([
+ ("curve.handle_type_set", {"type": 'V', "value": 'PRESS'}, None),
+ ("curve.vertex_add", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True}, None),
+ ("curve.select_all", {"type": 'A', "value": 'CLICK', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("curve.select_all", {"type": 'A', "value": 'CLICK', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("curve.select_all", {"type": 'I', "value": 'CLICK', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("curve.select_row", {"type": 'R', "value": 'PRESS', "shift": True}, None),
+ ("curve.select_more", {"type": 'UP_ARROW', "value": 'PRESS'}, None),
+ ("curve.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS'}, None),
+ ("curve.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, None),
+ ("curve.shortest_path_pick", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True}, None),
+ ("curve.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ op_menu("VIEW3D_MT_edit_curve_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}),
+ op_menu("VIEW3D_MT_edit_curve_delete", {"type": 'DEL', "value": 'PRESS'}),
+ ("curve.dissolve_verts", {"type": 'BACK_SPACE', "value": 'PRESS', "ctrl": True}, None),
+ ("curve.dissolve_verts", {"type": 'DEL', "value": 'PRESS', "ctrl": True}, None),
+ ("curve.tilt_clear", {"type": 'T', "value": 'PRESS', "alt": True}, None),
+ ("curve.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
+ ("curve.hide", {"type": 'H', "value": 'PRESS', "ctrl": True},
+ {"properties": [("unselected", False)]}),
+ ("curve.hide", {"type": 'H', "value": 'PRESS', "shift": True},
+ {"properties": [("unselected", True)]}),
+ op_menu("VIEW3D_MT_edit_curve_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ # Tools
+ *_template_items_basic_tools(),
+ op_tool("builtin.tilt", {"type": 'Y', "value": 'PRESS'}),
+ op_tool("builtin.radius", {"type": 'U', "value": 'PRESS'}),
+
+ ])
+
+ return keymap
+
+# Radial control setup helpers, this operator has a lot of properties.
+
+
+def radial_control_properties(paint, prop, secondary_prop, secondary_rotation=False, color=False, zoom=False):
+ brush_path = 'tool_settings.' + paint + '.brush'
+ unified_path = 'tool_settings.unified_paint_settings'
+ rotation = 'mask_texture_slot.angle' if secondary_rotation else 'texture_slot.angle'
+ return {
+ "properties": [
+ ("data_path_primary", brush_path + '.' + prop),
+ ("data_path_secondary", unified_path + '.' + prop if secondary_prop else ''),
+ ("use_secondary", unified_path + '.' + secondary_prop if secondary_prop else ''),
+ ("rotation_path", brush_path + '.' + rotation),
+ ("color_path", brush_path + '.cursor_color_add'),
+ ("fill_color_path", brush_path + '.color' if color else ''),
+ ("fill_color_override_path", unified_path + '.color' if color else ''),
+ ("fill_color_override_test_path", unified_path + '.use_unified_color' if color else ''),
+ ("zoom_path", 'space_data.zoom' if zoom else ''),
+ ("image_id", brush_path + ''),
+ ("secondary_tex", secondary_rotation),
+ ],
+ }
+
+# Radial controls for the paint and sculpt modes.
+
+
+def _template_paint_radial_control(paint, rotation=False, secondary_rotation=False, color=False, zoom=False):
+ items = []
+
+ items.extend([
+ ("wm.radial_control", {"type": 'S', "value": 'PRESS'},
+ radial_control_properties(paint, 'size', 'use_unified_size', secondary_rotation=secondary_rotation, color=color, zoom=zoom)),
+ ("wm.radial_control", {"type": 'U', "value": 'PRESS'},
+ radial_control_properties(paint, 'strength', 'use_unified_strength', secondary_rotation=secondary_rotation, color=color)),
+ ])
+
+ if rotation:
+ items.extend([
+ ("wm.radial_control", {"type": 'F', "value": 'PRESS', "ctrl": True},
+ radial_control_properties(paint, 'texture_slot.angle', None, color=color)),
+ ])
+
+ if secondary_rotation:
+ items.extend([
+ ("wm.radial_control", {"type": 'F', "value": 'PRESS', "ctrl": True, "alt": True},
+ radial_control_properties(paint, 'mask_texture_slot.angle', None, secondary_rotation=secondary_rotation, color=color)),
+ ])
+
+ return items
+
+
+def km_image_paint(params):
+ items = []
+ keymap = (
+ "Image Paint",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("paint.image_paint", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("mode", 'NORMAL')]}),
+ ("paint.image_paint", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'INVERT')]}),
+ ("paint.brush_colors_flip", {"type": 'X', "value": 'PRESS'}, None),
+ ("paint.grab_clone", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None),
+ ("paint.sample_color", {"type": 'C', "value": 'PRESS'}, None),
+ ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
+ {"properties": [("scalar", 0.9)]}),
+ ("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
+ {"properties": [("scalar", 1.0 / 0.9)]}),
+ *_template_paint_radial_control("image_paint", color=True, zoom=True, rotation=True, secondary_rotation=True),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS'},
+ {"properties": [("mode", 'TRANSLATION')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("mode", 'SCALE')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'ROTATION')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "alt": True},
+ {"properties": [("mode", 'TRANSLATION'), ("texmode", 'SECONDARY')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("mode", 'SCALE'), ("texmode", 'SECONDARY')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "ctrl": True, "alt": True},
+ {"properties": [("mode", 'ROTATION'), ("texmode", 'SECONDARY')]}),
+ ("wm.context_toggle", {"type": 'M', "value": 'PRESS'},
+ {"properties": [("data_path", 'image_paint_object.data.use_paint_mask')]}),
+ ("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True},
+ {"properties": [("data_path", 'tool_settings.image_paint.brush.use_smooth_stroke')]}),
+ op_menu("VIEW3D_MT_angle_control", {"type": 'R', "value": 'PRESS'}),
+ ("wm.context_menu_enum", {"type": 'E', "value": 'PRESS'},
+ {"properties": [("data_path", 'tool_settings.image_paint.brush.stroke_method')]}),
+ op_panel("VIEW3D_PT_paint_texture_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ ])
+
+ return keymap
+
+
+def km_vertex_paint(params):
+ items = []
+ keymap = (
+ "Vertex Paint",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("paint.vertex_paint", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("paint.brush_colors_flip", {"type": 'X', "value": 'PRESS'}, None),
+ ("paint.sample_color", {"type": 'C', "value": 'PRESS'}, None),
+ #("paint.vertex_color_set", {"type": 'K', "value": 'PRESS', "shift": True}, None),
+ ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
+ {"properties": [("scalar", 0.9)]}),
+ ("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
+ {"properties": [("scalar", 1.0 / 0.9)]}),
+ *_template_paint_radial_control("vertex_paint", color=True, rotation=True),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS'},
+ {"properties": [("mode", 'TRANSLATION')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("mode", 'SCALE')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'ROTATION')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "alt": True},
+ {"properties": [("mode", 'TRANSLATION'), ("texmode", 'SECONDARY')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("mode", 'SCALE'), ("texmode", 'SECONDARY')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "ctrl": True, "alt": True},
+ {"properties": [("mode", 'ROTATION'), ("texmode", 'SECONDARY')]}),
+ ("wm.context_toggle", {"type": 'M', "value": 'PRESS'},
+ {"properties": [("data_path", 'vertex_paint_object.data.use_paint_mask')]}),
+ ("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True},
+ {"properties": [("data_path", 'tool_settings.vertex_paint.brush.use_smooth_stroke')]}),
+ op_menu("VIEW3D_MT_angle_control", {"type": 'R', "value": 'PRESS'}),
+ ("wm.context_menu_enum", {"type": 'E', "value": 'PRESS'},
+ {"properties": [("data_path", 'tool_settings.vertex_paint.brush.stroke_method')]}),
+ op_panel("VIEW3D_PT_paint_vertex_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ ])
+
+ return keymap
+
+
+def km_weight_paint(params):
+ items = []
+ keymap = (
+ "Weight Paint",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("paint.weight_paint", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("paint.weight_sample", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None),
+ ("paint.weight_sample_group", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, None),
+ #("paint.weight_gradient", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
+ #{"properties": [("type", 'LINEAR')]}),
+ ("paint.weight_gradient", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True, "alt": True},
+ {"properties": [("type", 'RADIAL')]}),
+ #("paint.weight_set", {"type": 'K', "value": 'PRESS', "shift": True}, None),
+ ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
+ {"properties": [("scalar", 0.9)]}),
+ ("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
+ {"properties": [("scalar", 1.0 / 0.9)]}),
+ *_template_paint_radial_control("weight_paint"),
+ ("wm.radial_control", {"type": 'S', "value": 'PRESS', "ctrl": True},
+ radial_control_properties("weight_paint", 'weight', 'use_unified_weight')),
+ ("wm.context_menu_enum", {"type": 'E', "value": 'PRESS'},
+ {"properties": [("data_path", 'tool_settings.vertex_paint.brush.stroke_method')]}),
+ ("wm.context_toggle", {"type": 'M', "value": 'PRESS'},
+ {"properties": [("data_path", 'weight_paint_object.data.use_paint_mask')]}),
+ ("wm.context_toggle", {"type": 'V', "value": 'PRESS'},
+ {"properties": [("data_path", 'weight_paint_object.data.use_paint_mask_vertex')]}),
+ ("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True},
+ {"properties": [("data_path", 'tool_settings.weight_paint.brush.use_smooth_stroke')]}),
+ op_panel("VIEW3D_PT_paint_weight_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ # Bone selection for combined weight paint + pose mode.
+ ("view3d.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None),
+ ])
+
+ return keymap
+
+
+def km_sculpt(params):
+ items = []
+ keymap = (
+ "Sculpt",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Brush strokes
+ ("sculpt.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("mode", 'NORMAL')]}),
+ ("sculpt.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'INVERT')]}),
+ ("sculpt.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("mode", 'SMOOTH')]}),
+ # Partial Visibility Show/hide
+ ("paint.hide_show", {"type": 'H', "value": 'PRESS', "shift": True},
+ {"properties": [("action", 'SHOW'), ("area", 'INSIDE')]}),
+ ("paint.hide_show", {"type": 'H', "value": 'PRESS', "ctrl": True},
+ {"properties": [("action", 'HIDE'), ("area", 'INSIDE')]}),
+ ("paint.hide_show", {"type": 'H', "value": 'PRESS', "alt": True},
+ {"properties": [("action", 'SHOW'), ("area", 'ALL')]}),
+ # Subdivision levels
+ *_template_items_object_subdivision_set(),
+ ("object.subdivision_set", {"type": 'PAGE_UP', "value": 'PRESS'},
+ {"properties": [("level", 1), ("relative", True)]}),
+ ("object.subdivision_set", {"type": 'PAGE_DOWN', "value": 'PRESS'},
+ {"properties": [("level", -1), ("relative", True)]}),
+ # Mask
+ ("paint.mask_flood_fill", {"type": 'M', "value": 'PRESS', "alt": True},
+ {"properties": [("mode", 'VALUE'), ("value", 0.0)]}),
+ ("paint.mask_flood_fill", {"type": 'I', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'INVERT')]}),
+ ("paint.mask_lasso_gesture", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None),
+ ("wm.context_toggle", {"type": 'M', "value": 'PRESS', "ctrl": True},
+ {"properties": [("data_path", 'scene.tool_settings.sculpt.show_mask')]}),
+ # Dynamic topology
+ ("sculpt.dynamic_topology_toggle", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ ("sculpt.set_detail_size", {"type": 'D', "value": 'PRESS', "shift": True}, None),
+ # Brush properties
+ ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
+ {"properties": [("scalar", 0.9)]}),
+ ("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
+ {"properties": [("scalar", 1.0 / 0.9)]}),
+ *_template_paint_radial_control("sculpt", rotation=True),
+ # Stencil
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS'},
+ {"properties": [("mode", 'TRANSLATION')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True},
+ {"properties": [("mode", 'SCALE')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'ROTATION')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "alt": True},
+ {"properties": [("mode", 'TRANSLATION'), ("texmode", 'SECONDARY')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True, "alt": True},
+ {"properties": [("mode", 'SCALE'), ("texmode", 'SECONDARY')]}),
+ ("brush.stencil_control", {"type": 'RIGHTMOUSE', "value": 'PRESS', "ctrl": True, "alt": True},
+ {"properties": [("mode", 'ROTATION'), ("texmode", 'SECONDARY')]}),
+ # Tools
+ ("paint.brush_select", {"type": 'S', "value": 'PRESS'},
+ {"properties": [("sculpt_tool", 'SMOOTH')]}),
+ ("paint.brush_select", {"type": 'P', "value": 'PRESS'},
+ {"properties": [("sculpt_tool", 'PINCH')]}),
+ ("paint.brush_select", {"type": 'I', "value": 'PRESS'},
+ {"properties": [("sculpt_tool", 'INFLATE')]}),
+ ("paint.brush_select", {"type": 'G', "value": 'PRESS'},
+ {"properties": [("sculpt_tool", 'GRAB')]}),
+ ("paint.brush_select", {"type": 'L', "value": 'PRESS'},
+ {"properties": [("sculpt_tool", 'LAYER')]}),
+ ("paint.brush_select", {"type": 'T', "value": 'PRESS', "shift": True},
+ {"properties": [("sculpt_tool", 'FLATTEN')]}),
+ ("paint.brush_select", {"type": 'C', "value": 'PRESS'},
+ {"properties": [("sculpt_tool", 'CLAY')]}),
+ ("paint.brush_select", {"type": 'C', "value": 'PRESS', "shift": True},
+ {"properties": [("sculpt_tool", 'CREASE')]}),
+ ("paint.brush_select", {"type": 'M', "value": 'PRESS'},
+ {"properties": [("sculpt_tool", 'MASK'), ("toggle", True), ("create_missing", True)]}),
+ ("paint.brush_select", {"type": 'R', "value": 'PRESS'},
+ {"properties": [("sculpt_tool", 'ROTATE')]}),
+ ("paint.brush_select", {"type": 'N', "value": 'PRESS'},
+ {"properties": [("sculpt_tool", 'NUDGE')]}),
+ ("paint.brush_select", {"type": 'T', "value": 'PRESS'},
+ {"properties": [("sculpt_tool", 'THUMB')]}),
+ ("paint.brush_select", {"type": 'H', "value": 'PRESS'},
+ {"properties": [("sculpt_tool", 'SNAKE_HOOK')]}),
+ ("paint.brush_select", {"type": 'B', "value": 'PRESS'},
+ {"properties": [("sculpt_tool", 'BLOB')]}),
+ ("paint.brush_select", {"type": 'D', "value": 'PRESS'},
+ {"properties": [("sculpt_tool", 'DRAW')]}),
+
+ # Menus
+ op_panel("VIEW3D_PT_sculpt_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ ])
+
+ return keymap
+
+
+# Mesh edit mode.
+def km_mesh(params):
+ items = []
+ keymap = (
+ "Mesh",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+
+ ("mesh.loop_select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
+ {"properties": [("extend", False), ("deselect", False), ("toggle", False)]}),
+ ("mesh.loop_select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "shift": True},
+ {"properties": [("extend", False), ("deselect", False), ("toggle", True)]}),
+ # Selection
+ ("mesh.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("mesh.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("mesh.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("mesh.select_more", {"type": 'UP_ARROW', "value": 'PRESS'}, None),
+ ("mesh.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS'}, None),
+ ("mesh.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, None),
+ # Hide/reveal.
+ ("mesh.hide", {"type": 'H', "value": 'PRESS', "ctrl": True},
+ {"properties": [("unselected", False)]}),
+ ("mesh.hide", {"type": 'H', "value": 'PRESS', "shift": True},
+ {"properties": [("unselected", True)]}),
+ ("mesh.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
+ # Tools.
+ ("mesh.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ op_menu("VIEW3D_MT_edit_mesh_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}),
+ op_menu("VIEW3D_MT_edit_mesh_delete", {"type": 'DEL', "value": 'PRESS'}),
+ ("mesh.dissolve_mode", {"type": 'BACK_SPACE', "value": 'PRESS', "ctrl": True}, None),
+ ("mesh.dissolve_mode", {"type": 'DEL', "value": 'PRESS', "ctrl": True}, None),
+ # Menus.
+ op_menu("VIEW3D_MT_edit_mesh_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ #Tools
+ *_template_items_basic_tools(),
+ op_tool("builtin.bevel", {"type": 'B', "value": 'PRESS'}),
+ op_tool("builtin.inset_faces", {"type": 'I', "value": 'PRESS'}),
+ op_tool("builtin.extrude_region", {"type": 'E', "value": 'PRESS', "ctrl": True}),
+ op_tool("builtin.knife", {"type": 'K', "value": 'PRESS'}),
+ op_tool("builtin.loop_cut", {"type": 'C', "value": 'PRESS', "alt": True}),
+
+ ])
+
+ return keymap
+
+
+# Armature edit mode
+def km_armature(params):
+ items = []
+ keymap = (
+ "Armature",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ # Hide/reveal.
+ ("armature.hide", {"type": 'H', "value": 'PRESS', "ctrl": True},
+ {"properties": [("unselected", False)]}),
+ ("armature.hide", {"type": 'H', "value": 'PRESS', "shift": True},
+ {"properties": [("unselected", True)]}),
+ ("armature.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
+ # Parenting.
+ ("armature.parent_set", {"type": 'P', "value": 'PRESS'}, None),
+ ("armature.parent_clear", {"type": 'P', "value": 'PRESS', "shift": True}, None),
+ # Selection.
+ ("armature.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("armature.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("armature.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("armature.select_hierarchy", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
+ {"properties": [("direction", 'PARENT'), ("extend", False)]}),
+ ("armature.select_hierarchy", {"type": 'LEFT_BRACKET', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'PARENT'), ("extend", True)]}),
+ ("armature.select_hierarchy", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
+ {"properties": [("direction", 'CHILD'), ("extend", False)]}),
+ ("armature.select_hierarchy", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "shift": True},
+ {"properties": [("direction", 'CHILD'), ("extend", True)]}),
+ ("armature.select_more", {"type": 'UP_ARROW', "value": 'PRESS'}, None),
+ ("armature.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS'}, None),
+ ("armature.select_similar", {"type": 'G', "value": 'PRESS', "shift": True}, None),
+ ("armature.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
+ {"properties": [("deselect", False)]}),
+ # Editing.
+ op_menu("VIEW3D_MT_edit_armature_delete", {"type": 'DEL', "value": 'PRESS'}),
+ op_menu("VIEW3D_MT_edit_armature_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}),
+ ("armature.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ ("armature.dissolve", {"type": 'BACK_SPACE', "value": 'PRESS', "ctrl": True}, None),
+ ("armature.dissolve", {"type": 'DEL', "value": 'PRESS', "ctrl": True}, None),
+ # Menus.
+ op_menu("VIEW3D_MT_armature_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ # Tools.
+ *_template_items_basic_tools(),
+ op_tool("builtin.roll", {"type": 'Y', "value": 'PRESS'}),
+ op_tool("builtin.extrude", {"type": 'E', "value": 'PRESS', "ctrl": True}),
+
+ ])
+
+ return keymap
+
+
+# Metaball edit mode.
+def km_metaball(params):
+ items = []
+ keymap = (
+ "Metaball",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+
+ items.extend([
+ ("mball.reveal_metaelems", {"type": 'H', "value": 'PRESS', "alt": True}, None),
+ ("mball.hide_metaelems", {"type": 'H', "value": 'PRESS', "ctrl": True},
+ {"properties": [("unselected", False)]}),
+ ("mball.hide_metaelems", {"type": 'H', "value": 'PRESS', "shift": True},
+ {"properties": [("unselected", True)]}),
+ ("mball.delete_metaelems", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("mball.delete_metaelems", {"type": 'DEL', "value": 'PRESS'}, None),
+ ("mball.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ ("mball.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("mball.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("mball.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("mball.select_similar", {"type": 'G', "value": 'PRESS', "shift": True}, None),
+ op_menu("VIEW3D_MT_edit_metaball_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ # Tools
+ *_template_items_basic_tools(),
+ ])
+
+ return keymap
+
+
+# Lattice edit mode.
+def km_lattice(params):
+ items = []
+ keymap = (
+ "Lattice",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("lattice.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("lattice.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("lattice.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("lattice.select_more", {"type": 'UP_ARROW', "value": 'PRESS'}, None),
+ ("lattice.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS'}, None),
+ ("object.vertex_parent_set", {"type": 'P', "value": 'PRESS', "ctrl": True}, None),
+ op_menu("VIEW3D_MT_edit_lattice_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+
+ # Tools
+ op_tool("builtin.select_box", {"type": 'Q', "value": 'PRESS'}),
+ op_tool("builtin.move", {"type": 'W', "value": 'PRESS'}),
+ op_tool("builtin.rotate", {"type": 'E', "value": 'PRESS'}),
+ op_tool("builtin.scale", {"type": 'R', "value": 'PRESS'}),
+ op_tool("builtin.scale_cage", {"type": 'R', "value": 'PRESS', "shift": True}),
+ op_tool("builtin.transform", {"type": 'T', "value": 'PRESS'}),
+ op_tool("builtin.measure", {"type": 'M', "value": 'PRESS'}),
+ ])
+
+ return keymap
+
+
+# Particle edit mode.
+def km_particle(params):
+ items = []
+ keymap = (
+ "Particle",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("particle.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
+ ("particle.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}),
+ ("particle.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
+ ("particle.select_more", {"type": 'UP_ARROW', "value": 'PRESS'}, None),
+ ("particle.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS'}, None),
+ ("particle.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
+ {"properties": [("deselect", False)]}),
+ ("particle.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "shift": True},
+ {"properties": [("deselect", True)]}),
+ ("particle.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
+ ("particle.delete", {"type": 'DEL', "value": 'PRESS'}, None),
+ ("particle.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
+ ("particle.hide", {"type": 'H', "value": 'PRESS', "ctrl": True},
+ {"properties": [("unselected", False)]}),
+ ("particle.hide", {"type": 'H', "value": 'PRESS', "shift": True},
+ {"properties": [("unselected", True)]}),
+ ("particle.brush_edit", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("particle.brush_edit", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, None),
+ ("wm.radial_control", {"type": 'S', "value": 'PRESS'},
+ {"properties": [("data_path_primary", 'tool_settings.particle_edit.brush.size')]}),
+ ("wm.radial_control", {"type": 'U', "value": 'PRESS', "shift": True},
+ {"properties": [("data_path_primary", 'tool_settings.particle_edit.brush.strength')]}),
+ op_menu("VIEW3D_MT_particle_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ ])
+
+ return keymap
+
+
+# Text edit mode.
+def km_font(params):
+ items = []
+ keymap = (
+ "Font",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("font.style_toggle", {"type": 'B', "value": 'PRESS', "ctrl": True},
+ {"properties": [("style", 'BOLD')]}),
+ ("font.style_toggle", {"type": 'I', "value": 'PRESS', "ctrl": True},
+ {"properties": [("style", 'ITALIC')]}),
+ ("font.style_toggle", {"type": 'U', "value": 'PRESS', "ctrl": True},
+ {"properties": [("style", 'UNDERLINE')]}),
+ ("font.style_toggle", {"type": 'P', "value": 'PRESS', "ctrl": True},
+ {"properties": [("style", 'SMALL_CAPS')]}),
+ ("font.delete", {"type": 'DEL', "value": 'PRESS'},
+ {"properties": [("type", 'NEXT_OR_SELECTION')]}),
+ ("font.delete", {"type": 'DEL', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'NEXT_WORD')]}),
+ ("font.delete", {"type": 'BACK_SPACE', "value": 'PRESS'},
+ {"properties": [("type", 'PREVIOUS_OR_SELECTION')]}),
+ ("font.delete", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'PREVIOUS_OR_SELECTION')]}),
+ ("font.delete", {"type": 'BACK_SPACE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'PREVIOUS_WORD')]}),
+ ("font.move", {"type": 'HOME', "value": 'PRESS'},
+ {"properties": [("type", 'LINE_BEGIN')]}),
+ ("font.move", {"type": 'END', "value": 'PRESS'},
+ {"properties": [("type", 'LINE_END')]}),
+ ("font.move", {"type": 'LEFT_ARROW', "value": 'PRESS'},
+ {"properties": [("type", 'PREVIOUS_CHARACTER')]}),
+ ("font.move", {"type": 'RIGHT_ARROW', "value": 'PRESS'},
+ {"properties": [("type", 'NEXT_CHARACTER')]}),
+ ("font.move", {"type": 'LEFT_ARROW', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'PREVIOUS_WORD')]}),
+ ("font.move", {"type": 'RIGHT_ARROW', "value": 'PRESS', "ctrl": True},
+ {"properties": [("type", 'NEXT_WORD')]}),
+ ("font.move", {"type": 'UP_ARROW', "value": 'PRESS'},
+ {"properties": [("type", 'PREVIOUS_LINE')]}),
+ ("font.move", {"type": 'DOWN_ARROW', "value": 'PRESS'},
+ {"properties": [("type", 'NEXT_LINE')]}),
+ ("font.move", {"type": 'PAGE_UP', "value": 'PRESS'},
+ {"properties": [("type", 'PREVIOUS_PAGE')]}),
+ ("font.move", {"type": 'PAGE_DOWN', "value": 'PRESS'},
+ {"properties": [("type", 'NEXT_PAGE')]}),
+ ("font.move_select", {"type": 'HOME', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'LINE_BEGIN')]}),
+ ("font.move_select", {"type": 'END', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'LINE_END')]}),
+ ("font.move_select", {"type": 'LEFT_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'PREVIOUS_CHARACTER')]}),
+ ("font.move_select", {"type": 'RIGHT_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'NEXT_CHARACTER')]}),
+ ("font.move_select", {"type": 'LEFT_ARROW', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("type", 'PREVIOUS_WORD')]}),
+ ("font.move_select", {"type": 'RIGHT_ARROW', "value": 'PRESS', "shift": True, "ctrl": True},
+ {"properties": [("type", 'NEXT_WORD')]}),
+ ("font.move_select", {"type": 'UP_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'PREVIOUS_LINE')]}),
+ ("font.move_select", {"type": 'DOWN_ARROW', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'NEXT_LINE')]}),
+ ("font.move_select", {"type": 'PAGE_UP', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'PREVIOUS_PAGE')]}),
+ ("font.move_select", {"type": 'PAGE_DOWN', "value": 'PRESS', "shift": True},
+ {"properties": [("type", 'NEXT_PAGE')]}),
+ ("font.change_spacing", {"type": 'LEFT_ARROW', "value": 'PRESS', "alt": True},
+ {"properties": [("delta", -1)]}),
+ ("font.change_spacing", {"type": 'RIGHT_ARROW', "value": 'PRESS', "alt": True},
+ {"properties": [("delta", 1)]}),
+ ("font.change_character", {"type": 'UP_ARROW', "value": 'PRESS', "alt": True},
+ {"properties": [("delta", 1)]}),
+ ("font.change_character", {"type": 'DOWN_ARROW', "value": 'PRESS', "alt": True},
+ {"properties": [("delta", -1)]}),
+ ("font.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, None),
+ ("font.text_copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
+ ("font.text_cut", {"type": 'X', "value": 'PRESS', "ctrl": True}, None),
+ ("font.text_paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
+ ("font.line_break", {"type": 'RET', "value": 'PRESS'}, None),
+ ("font.text_insert", {"type": 'TEXTINPUT', "value": 'ANY', "any": True}, None),
+ ("font.text_insert", {"type": 'BACK_SPACE', "value": 'PRESS', "alt": True},
+ {"properties": [("accent", True)]}),
+ op_menu("VIEW3D_MT_edit_text_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
+ ])
+
+
+ return keymap
+
+
+def km_object_non_modal(params):
+ items = []
+ keymap = (
+ "Object Non-modal",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ ("object.mode_set",{"type": 'ONE', "value": 'PRESS'},
+ {"properties": [("mode", 'OBJECT')]}),
+ ("ic_keymap.mesh_select_mode",{"type": 'TWO', "value": 'PRESS'},
+ {"properties": [("type", 'VERT')]}),
+ ("ic_keymap.mesh_select_mode",{"type": 'THREE', "value": 'PRESS'},
+ {"properties": [("type", 'EDGE')]}),
+ ("ic_keymap.mesh_select_mode",{"type": 'FOUR', "value": 'PRESS'},
+ {"properties": [("type", 'FACE')]}),
+ ("object.mode_set",{"type": 'TWO', "value": 'PRESS'},
+ {"properties": [("mode", 'EDIT')]}),
+ ("object.mode_set",{"type": 'FIVE', "value": 'PRESS'},
+ {"properties": [("mode", 'SCULPT')]}),
+ ("object.mode_set",{"type": 'SIX', "value": 'PRESS'},
+ {"properties": [("mode", 'VERTEX_PAINT')]}),
+ ("object.mode_set",{"type": 'SEVEN', "value": 'PRESS'},
+ {"properties": [("mode", 'WEIGHT_PAINT')]}),
+ ("object.mode_set",{"type": 'EIGHT', "value": 'PRESS'},
+ {"properties": [("mode", 'TEXTURE_PAINT')]}),
+
+ ("object.mode_set",{"type": 'TWO', "value": 'PRESS'},
+ {"properties": [("mode", 'EDIT_GPENCIL')]}),
+ ("object.mode_set",{"type": 'THREE', "value": 'PRESS'},
+ {"properties": [("mode", 'SCULPT_GPENCIL')]}),
+ ("object.mode_set",{"type": 'FOUR', "value": 'PRESS'},
+ {"properties": [("mode", 'PAINT_GPENCIL')]}),
+ ("object.mode_set",{"type": 'FIVE', "value": 'PRESS'},
+ {"properties": [("mode", 'WEIGHT_GPENCIL')]}),
+
+ ("object.mode_set",{"type": 'THREE', "value": 'PRESS'},
+ {"properties": [("mode", 'POSE')]})
+ ])
+
+ return keymap
+
+
+# ------------------------------------------------------------------------------
+# Modal Maps and Gizmos
+
+
+def km_eyedropper_modal_map(_params):
+ items = []
+ keymap = (
+ "Eyedropper Modal Map",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW', "modal": True},
+ {"items": items},
+ )
+
+ items.extend([
+ ("CANCEL", {"type": 'ESC', "value": 'PRESS', "any": True}, None),
+ ("CANCEL", {"type": 'RIGHTMOUSE', "value": 'PRESS', "any": True}, None),
+ ("SAMPLE_CONFIRM", {"type": 'RET', "value": 'RELEASE', "any": True}, None),
+ ("SAMPLE_CONFIRM", {"type": 'NUMPAD_ENTER', "value": 'RELEASE', "any": True}, None),
+ ("SAMPLE_CONFIRM", {"type": 'LEFTMOUSE', "value": 'RELEASE', "any": True}, None),
+ ("SAMPLE_BEGIN", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None),
+ ("SAMPLE_RESET", {"type": 'SPACE', "value": 'RELEASE', "any": True}, None),
+ ])
+
+ return keymap
+
+
+def km_eyedropper_colorramp_pointsampling_map(_params):
+ items = []
+ keymap = (
+ "Eyedropper ColorRamp PointSampling Map",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW', "modal": True},
+ {"items": items},
+ )
+
+ items.extend([
+ ("CANCEL", {"type": 'ESC', "value": 'PRESS', "any": True}, None),
+ ("CANCEL", {"type": 'BACK_SPACE', "value": 'PRESS', "any": True}, None),
+ ("SAMPLE_CONFIRM", {"type": 'RIGHTMOUSE', "value": 'PRESS', "any": True}, None),
+ ("SAMPLE_CONFIRM", {"type": 'RET', "value": 'RELEASE', "any": True}, None),
+ ("SAMPLE_CONFIRM", {"type": 'NUMPAD_ENTER', "value": 'RELEASE', "any": True}, None),
+ ("SAMPLE_SAMPLE", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None),
+ ("SAMPLE_RESET", {"type": 'SPACE', "value": 'RELEASE', "any": True}, None),
+ ])
+
+ return keymap
+
+
+def km_transform_modal_map(_params):
+ items = []
+ keymap = (
+ "Transform Modal Map",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW', "modal": True},
+ {"items": items},
+ )
+
+ items.extend([
+ ("CONFIRM", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None),
+ ("CONFIRM", {"type": 'RET', "value": 'PRESS', "any": True}, None),
+ ("CONFIRM", {"type": 'NUMPAD_ENTER', "value": 'PRESS', "any": True}, None),
+ ("CANCEL", {"type": 'RIGHTMOUSE', "value": 'PRESS', "any": True}, None),
+ ("CANCEL", {"type": 'ESC', "value": 'PRESS', "any": True}, None),
+ ("AXIS_X", {"type": 'X', "value": 'PRESS'}, None),
+ ("AXIS_Y", {"type": 'Y', "value": 'PRESS'}, None),
+ ("AXIS_Z", {"type": 'Z', "value": 'PRESS'}, None),
+ ("PLANE_X", {"type": 'X', "value": 'PRESS', "shift": True}, None),
+ ("PLANE_Y", {"type": 'Y', "value": 'PRESS', "shift": True}, None),
+ ("PLANE_Z", {"type": 'Z', "value": 'PRESS', "shift": True}, None),
+ ("CONS_OFF", {"type": 'C', "value": 'PRESS'}, None),
+ ("TRANSLATE", {"type": 'G', "value": 'PRESS'}, None),
+ ("ROTATE", {"type": 'R', "value": 'PRESS'}, None),
+ ("RESIZE", {"type": 'S', "value": 'PRESS'}, None),
+ ("SNAP_TOGGLE", {"type": 'TAB', "value": 'PRESS', "shift": True}, None),
+ ("SNAP_INV_ON", {"type": 'LEFT_CTRL', "value": 'PRESS', "any": True}, None),
+ ("SNAP_INV_OFF", {"type": 'LEFT_CTRL', "value": 'RELEASE', "any": True}, None),
+ ("SNAP_INV_ON", {"type": 'RIGHT_CTRL', "value": 'PRESS', "any": True}, None),
+ ("SNAP_INV_OFF", {"type": 'RIGHT_CTRL', "value": 'RELEASE', "any": True}, None),
+ ("ADD_SNAP", {"type": 'A', "value": 'PRESS'}, None),
+ ("REMOVE_SNAP", {"type": 'A', "value": 'PRESS', "alt": True}, None),
+ ("PROPORTIONAL_SIZE_UP", {"type": 'PAGE_UP', "value": 'PRESS'}, None),
+ ("PROPORTIONAL_SIZE_DOWN", {"type": 'PAGE_DOWN', "value": 'PRESS'}, None),
+ ("PROPORTIONAL_SIZE_UP", {"type": 'PAGE_UP', "value": 'PRESS', "shift": True}, None),
+ ("PROPORTIONAL_SIZE_DOWN", {"type": 'PAGE_DOWN', "value": 'PRESS', "shift": True}, None),
+ ("PROPORTIONAL_SIZE_UP", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS'}, None),
+ ("PROPORTIONAL_SIZE_DOWN", {"type": 'WHEELUPMOUSE', "value": 'PRESS'}, None),
+ ("PROPORTIONAL_SIZE_UP", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "shift": True}, None),
+ ("PROPORTIONAL_SIZE_DOWN", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "shift": True}, None),
+ ("PROPORTIONAL_SIZE", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
+ ("EDGESLIDE_EDGE_NEXT", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "alt": True}, None),
+ ("EDGESLIDE_PREV_NEXT", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "alt": True}, None),
+ ("AUTOIK_CHAIN_LEN_UP", {"type": 'PAGE_UP', "value": 'PRESS', "shift": True}, None),
+ ("AUTOIK_CHAIN_LEN_DOWN", {"type": 'PAGE_DOWN', "value": 'PRESS', "shift": True}, None),
+ ("AUTOIK_CHAIN_LEN_UP", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "shift": True}, None),
+ ("AUTOIK_CHAIN_LEN_DOWN", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "shift": True}, None),
+ ("INSERTOFS_TOGGLE_DIR", {"type": 'T', "value": 'PRESS'}, None),
+ ])
+
+ return keymap
+
+
+# ------------------------------------------------------------------------------
+# Tool System Keymaps
+
+
+
+def km_3d_view_tool_move(params):
+ return (
+ "3D View Tool: Move",
+ {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
+ {"items": [
+ ("transform.translate", {"type": params.tool_tweak, "value": 'ANY'},
+ {"properties": [("release_confirm", True)]}),
+ ("object.duplicate_move", {"type": params.tool_tweak, "value": 'ANY', "shift": True}, None),
+ ("mesh.duplicate_move", {"type": params.tool_tweak, "value": 'ANY', "shift": True}, None),
+ ("curve.duplicate_move", {"type": params.tool_tweak, "value": 'ANY', "shift": True}, None),
+ ("armature.duplicate_move", {"type": params.tool_tweak, "value": 'ANY', "shift": True}, None),
+ ("mball.duplicate_move", {"type": params.tool_tweak, "value": 'ANY', "shift": True}, None),
+ ("gpencil.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
+ ]},
+ )
+
+
+# ------------------------------------------------------------------------------
+# Full Configuration
+
+def generate_keymaps(params=None):
+ if params is None:
+ params = Params()
+ return [
+ # Window, screen, area, region.
+ km_window(params),
+ km_screen(params),
+ km_screen_editing(params),
+ km_header(params),
+ km_view2d(params),
+ km_view2d_buttons_list(params),
+ km_user_interface(params),
+ km_property_editor(params),
+
+ # Editors.
+ km_outliner(params),
+ km_uv_editor(params),
+ km_uv_sculpt(params),
+ km_view3d_generic(params),
+ km_view3d(params),
+ km_mask_editing(params),
+ km_markers(params),
+ km_graph_editor_generic(params),
+ km_graph_editor(params),
+ km_image_generic(params),
+ km_image(params),
+ km_node_generic(params),
+ km_node_editor(params),
+ km_info(params),
+ km_file_browser(params),
+ km_file_browser_main(params),
+ km_file_browser_buttons(params),
+ km_dopesheet_generic(params),
+ km_dopesheet(params),
+ km_nla_generic(params),
+ km_nla_channels(params),
+ km_nla_editor(params),
+ km_text_generic(params),
+ km_text(params),
+ km_sequencercommon(params),
+ km_sequencer(params),
+ km_sequencerpreview(params),
+ km_console(params),
+ km_clip(params),
+ km_clip_editor(params),
+ km_clip_graph_editor(params),
+ km_clip_dopesheet_editor(params),
+
+ # Animation.
+ km_frames(params),
+ km_animation(params),
+ km_animation_channels(params),
+
+ # Modes.
+ km_grease_pencil(params),
+ km_grease_pencil_stroke_edit_mode(params),
+ km_grease_pencil_stroke_paint_mode(params),
+ km_grease_pencil_stroke_paint_draw_brush(params),
+ km_grease_pencil_stroke_paint_erase(params),
+ km_grease_pencil_stroke_paint_fill(params),
+ km_grease_pencil_stroke_sculpt_mode(params),
+ km_grease_pencil_stroke_weight_mode(params),
+ km_face_mask(params),
+ km_weight_paint_vertex_selection(params),
+ km_pose(params),
+ km_object_mode(params),
+ km_paint_curve(params),
+ km_curve(params),
+ km_image_paint(params),
+ km_vertex_paint(params),
+ km_weight_paint(params),
+ km_sculpt(params),
+ km_mesh(params),
+ km_armature(params),
+ km_metaball(params),
+ km_lattice(params),
+ km_particle(params),
+ km_font(params),
+ km_object_non_modal(params),
+
+ # Modal maps.
+ km_eyedropper_modal_map(params),
+ km_eyedropper_colorramp_pointsampling_map(params),
+ km_transform_modal_map(params),
+
+ # Tool System.
+ km_3d_view_tool_move(params),
+ ]
diff --git a/release/scripts/startup/bl_app_templates_system/Sculpting/__init__.py b/release/scripts/startup/bl_app_templates_system/Sculpting/__init__.py
index bf32d6672ca..4d1361ed22a 100644
--- a/release/scripts/startup/bl_app_templates_system/Sculpting/__init__.py
+++ b/release/scripts/startup/bl_app_templates_system/Sculpting/__init__.py
@@ -35,8 +35,10 @@ def load_handler(dummy):
bpy.ops.mesh.subdivide(number_cuts=6, smoothness=1.0)
bpy.ops.object.mode_set(mode='SCULPT')
+
def register():
bpy.app.handlers.load_factory_startup_post.append(load_handler)
+
def unregister():
bpy.app.handlers.load_factory_startup_post.remove(load_handler)
diff --git a/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py b/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py
index 4c769c69165..bfad66ec6cd 100644
--- a/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py
+++ b/release/scripts/startup/bl_app_templates_system/Video_Editing/__init__.py
@@ -31,8 +31,10 @@ def load_handler(dummy):
params = space.params
params.use_filter_folder = True
+
def register():
bpy.app.handlers.load_factory_startup_post.append(load_handler)
+
def unregister():
bpy.app.handlers.load_factory_startup_post.remove(load_handler)
diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py
index fbebee2d8a1..9fdaebc7540 100644
--- a/release/scripts/startup/bl_operators/node.py
+++ b/release/scripts/startup/bl_operators/node.py
@@ -192,14 +192,12 @@ class NODE_OT_add_search(NodeAddOperator, Operator):
for index, item in enumerate(nodeitems_utils.node_items_iter(context)):
if isinstance(item, nodeitems_utils.NodeItem):
- nodetype = getattr(bpy.types, item.nodetype, None)
- if nodetype:
- enum_items.append(
- (str(index),
- item.label,
- nodetype.bl_rna.description,
- index,
- ))
+ enum_items.append(
+ (str(index),
+ item.label,
+ "",
+ index,
+ ))
return enum_items
# Look up the item based on index
diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py
index f2124c094a7..c05ee01c72d 100644
--- a/release/scripts/startup/bl_operators/object.py
+++ b/release/scripts/startup/bl_operators/object.py
@@ -324,15 +324,15 @@ class ShapeTransfer(Operator):
('OFFSET',
"Offset",
"Apply the relative positional offset",
- ),
+ ),
('RELATIVE_FACE',
"Relative Face",
"Calculate relative position (using faces)",
- ),
+ ),
('RELATIVE_EDGE',
"Relative Edge",
"Calculate relative position (using edges)",
- ),
+ ),
),
name="Transformation Mode",
description="Relative shape positions to the new shape method",
diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py
index 35807761567..14b96e1d5c1 100644
--- a/release/scripts/startup/bl_operators/object_quick_effects.py
+++ b/release/scripts/startup/bl_operators/object_quick_effects.py
@@ -46,11 +46,13 @@ def object_ensure_material(obj, mat_name):
obj.data.materials.append(mat)
return mat
+
class ObjectModeOperator:
@classmethod
def poll(cls, context):
return context.mode == 'OBJECT'
+
class QuickFur(ObjectModeOperator, Operator):
bl_idname = "object.quick_fur"
bl_label = "Quick Fur"
@@ -507,10 +509,11 @@ class QuickFluid(ObjectModeOperator, Operator):
# and scale with initial velocity
v = 0.5 * self.initial_velocity
obj.location = 0.5 * (max_co + min_co) + Vector((0.0, 0.0, -1.0)) + v
- obj.scale = (0.5 * (max_co - min_co) +
- Vector((1.0, 1.0, 2.0)) +
- Vector((abs(v[0]), abs(v[1]), abs(v[2])))
- )
+ obj.scale = (
+ 0.5 * (max_co - min_co) +
+ Vector((1.0, 1.0, 2.0)) +
+ Vector((abs(v[0]), abs(v[1]), abs(v[2])))
+ )
# setup smoke domain
bpy.ops.object.modifier_add(type='FLUID_SIMULATION')
diff --git a/release/scripts/startup/bl_operators/uvcalc_smart_project.py b/release/scripts/startup/bl_operators/uvcalc_smart_project.py
index 58c915ce029..528513aa301 100644
--- a/release/scripts/startup/bl_operators/uvcalc_smart_project.py
+++ b/release/scripts/startup/bl_operators/uvcalc_smart_project.py
@@ -583,10 +583,10 @@ def packIslands(islandList):
w, h = maxx - minx, maxy - miny
if USER_ISLAND_MARGIN:
- minx -= USER_ISLAND_MARGIN * w/2
- miny -= USER_ISLAND_MARGIN * h/2
- maxx += USER_ISLAND_MARGIN * w/2
- maxy += USER_ISLAND_MARGIN * h/2
+ minx -= USER_ISLAND_MARGIN * w / 2
+ miny -= USER_ISLAND_MARGIN * h / 2
+ maxx += USER_ISLAND_MARGIN * w / 2
+ maxy += USER_ISLAND_MARGIN * h / 2
# recalc width and height
w, h = maxx - minx, maxy - miny
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index c6f79d49d41..de811d42e00 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -1423,7 +1423,6 @@ class WM_OT_sysinfo(Operator):
return {'RUNNING_MODAL'}
-
class WM_OT_operator_cheat_sheet(Operator):
"""List all the Operators in a text-block, useful for scripting"""
bl_idname = "wm.operator_cheat_sheet"
@@ -1484,7 +1483,6 @@ class WM_OT_owner_disable(Operator):
return {'FINISHED'}
-
class WM_OT_tool_set_by_id(Operator):
"""Set the tool by name (for keymaps)"""
bl_idname = "wm.tool_set_by_id"
@@ -1666,7 +1664,7 @@ class WM_MT_splash(Menu):
# Draw setup screen if no preferences have been saved yet.
import os
- userconfig_path = bpy.utils.user_resource('CONFIG');
+ userconfig_path = bpy.utils.user_resource('CONFIG')
userdef_path = os.path.join(userconfig_path, "userpref.blend")
if not os.path.isfile(userdef_path):
diff --git a/release/scripts/startup/bl_ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py
index e80f104b393..4e10ac205bf 100644
--- a/release/scripts/startup/bl_ui/properties_data_armature.py
+++ b/release/scripts/startup/bl_ui/properties_data_armature.py
@@ -26,6 +26,7 @@ from .properties_animviz import (
MotionPathButtonsPanel_display,
)
+
class ArmatureButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py
index 8cae779469a..84fe9fb1f3e 100644
--- a/release/scripts/startup/bl_ui/properties_data_camera.py
+++ b/release/scripts/startup/bl_ui/properties_data_camera.py
@@ -386,6 +386,7 @@ class DATA_PT_camera_display(CameraButtonsPanel, Panel):
col = flow.column()
col.prop(cam, "show_name", text="Name")
+
class DATA_PT_camera_display_composition_guides(CameraButtonsPanel, Panel):
bl_label = "Composition Guides"
bl_parent_id = "DATA_PT_camera_display"
diff --git a/release/scripts/startup/bl_ui/properties_physics_cloth.py b/release/scripts/startup/bl_ui/properties_physics_cloth.py
index 1fc3fcdfdb3..f3d84a7acc0 100644
--- a/release/scripts/startup/bl_ui/properties_physics_cloth.py
+++ b/release/scripts/startup/bl_ui/properties_physics_cloth.py
@@ -332,7 +332,7 @@ class PHYSICS_PT_cloth_property_weights(PhysicButtonsPanel, Panel):
col = flow.column()
col.prop_search(
cloth, "vertex_group_structural_stiffness", ob, "vertex_groups",
- text="Structural Group"
+ text="Structural Group",
)
col.prop(cloth, "tension_stiffness_max", text="Max Tension")
col.prop(cloth, "compression_stiffness_max", text="Max Compression")
@@ -342,7 +342,7 @@ class PHYSICS_PT_cloth_property_weights(PhysicButtonsPanel, Panel):
col = flow.column()
col.prop_search(
cloth, "vertex_group_shear_stiffness", ob, "vertex_groups",
- text="Shear Group"
+ text="Shear Group",
)
col.prop(cloth, "shear_stiffness_max", text="Max Shearing")
diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py
index 965b635418f..11a9b250454 100644
--- a/release/scripts/startup/bl_ui/properties_physics_common.py
+++ b/release/scripts/startup/bl_ui/properties_physics_common.py
@@ -39,13 +39,21 @@ def physics_add(self, layout, md, name, type, typeicon, toggles):
row = layout.row(align=True)
if md:
row.context_pointer_set("modifier", md)
- row.operator("object.modifier_remove", text=name, text_ctxt=i18n_contexts.default, icon='X')
+ row.operator(
+ "object.modifier_remove",
+ text=name,
+ text_ctxt=i18n_contexts.default,
+ icon='X',
+ )
if toggles:
row.prop(md, "show_render", text="")
row.prop(md, "show_viewport", text="")
else:
row.operator(
- "object.modifier_add", text=name, text_ctxt=i18n_contexts.default, icon='BLANK1'
+ "object.modifier_add",
+ text=name,
+ text_ctxt=i18n_contexts.default,
+ icon='BLANK1',
).type = type
diff --git a/release/scripts/startup/bl_ui/space_text.py b/release/scripts/startup/bl_ui/space_text.py
index b62e2d740b3..07184c81e42 100644
--- a/release/scripts/startup/bl_ui/space_text.py
+++ b/release/scripts/startup/bl_ui/space_text.py
@@ -95,7 +95,7 @@ class TEXT_HT_footer(Header):
row.label(
text="Text: External"
if text.library
- else "Text: Internal"
+ else "Text: Internal",
)
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 42953a05ca6..ed31a847933 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -77,7 +77,7 @@ class _template_widget:
props = tool.gizmo_group_properties("VIEW3D_GGT_xform_extrude")
layout.prop(props, "axis_type", expand=True)
- class TRANSFORM_GGT_gizmo:
+ class VIEW3D_GGT_xform_gizmo:
@staticmethod
def draw_settings_with_index(context, layout, index):
scene = context.scene
@@ -121,15 +121,13 @@ class _defs_view3d_generic:
else:
kmi_add = None
kmi_remove = None
- return (
- tip_(
+ return tip_(
"Measure distance and angles.\n"
"\u2022 {} anywhere for new measurement.\n"
"\u2022 Drag ruler segment to measure an angle.\n"
"\u2022 {} to remove the active ruler.\n"
"\u2022 Ctrl while dragging to snap.\n"
"\u2022 Shift while dragging to measure surface thickness."
- )
).format(
kmi_to_string_or_none(kmi_add),
kmi_to_string_or_none(kmi_remove),
@@ -239,13 +237,13 @@ class _defs_transform:
@ToolDef.from_fn
def translate():
def draw_settings(context, layout, tool):
- _template_widget.TRANSFORM_GGT_gizmo.draw_settings_with_index(context, layout, 1)
+ _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 1)
return dict(
idname="builtin.move",
label="Move",
# cursor='SCROLL_XY',
icon="ops.transform.translate",
- widget="TRANSFORM_GGT_gizmo",
+ widget="VIEW3D_GGT_xform_gizmo",
operator="transform.translate",
keymap="3D View Tool: Move",
draw_settings=draw_settings,
@@ -254,13 +252,13 @@ class _defs_transform:
@ToolDef.from_fn
def rotate():
def draw_settings(context, layout, tool):
- _template_widget.TRANSFORM_GGT_gizmo.draw_settings_with_index(context, layout, 2)
+ _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 2)
return dict(
idname="builtin.rotate",
label="Rotate",
# cursor='SCROLL_XY',
icon="ops.transform.rotate",
- widget="TRANSFORM_GGT_gizmo",
+ widget="VIEW3D_GGT_xform_gizmo",
operator="transform.rotate",
keymap="3D View Tool: Rotate",
draw_settings=draw_settings,
@@ -269,13 +267,13 @@ class _defs_transform:
@ToolDef.from_fn
def scale():
def draw_settings(context, layout, tool):
- _template_widget.TRANSFORM_GGT_gizmo.draw_settings_with_index(context, layout, 3)
+ _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 3)
return dict(
idname="builtin.scale",
label="Scale",
# cursor='SCROLL_XY',
icon="ops.transform.resize",
- widget="TRANSFORM_GGT_gizmo",
+ widget="VIEW3D_GGT_xform_gizmo",
operator="transform.resize",
keymap="3D View Tool: Scale",
draw_settings=draw_settings,
@@ -284,7 +282,7 @@ class _defs_transform:
@ToolDef.from_fn
def scale_cage():
def draw_settings(context, layout, tool):
- _template_widget.TRANSFORM_GGT_gizmo.draw_settings_with_index(context, layout, 3)
+ _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 3)
return dict(
idname="builtin.scale_cage",
label="Scale Cage",
@@ -295,31 +293,6 @@ class _defs_transform:
draw_settings=draw_settings,
)
- @ToolDef.from_fn
- def transform():
- def draw_settings(context, layout, tool):
- if not layout.use_property_split:
- layout.label(text="Gizmos:")
- tool_settings = context.tool_settings
- layout.prop(tool_settings, "use_gizmo_mode")
-
- props = tool.gizmo_group_properties("TRANSFORM_GGT_gizmo")
- layout.prop(props, "drag_action")
-
- _template_widget.TRANSFORM_GGT_gizmo.draw_settings_with_index(context, layout, 1)
-
- return dict(
- idname="builtin.transform",
- label="Transform",
- description=(
- "Supports any combination of grab, rotate & scale at once"
- ),
- icon="ops.transform.transform",
- widget="TRANSFORM_GGT_gizmo",
- keymap="3D View Tool: Transform",
- draw_settings=draw_settings,
- )
-
class _defs_view3d_select:
@@ -726,7 +699,7 @@ class _defs_edit_mesh:
props = tool.operator_properties("transform.shear")
layout.label(text="View Axis:")
layout.prop(props, "shear_axis", expand=True)
- _template_widget.TRANSFORM_GGT_gizmo.draw_settings_with_index(context, layout, 2)
+ _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 2)
return dict(
idname="builtin.shear",
label="Shear",
@@ -1692,7 +1665,6 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
# for reuse
_tools_transform = (
- _defs_transform.transform,
_defs_transform.translate,
_defs_transform.rotate,
(
diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py
index 7b9b324066a..7598c6954d5 100644
--- a/release/scripts/startup/bl_ui/space_topbar.py
+++ b/release/scripts/startup/bl_ui/space_topbar.py
@@ -344,7 +344,7 @@ class _draw_left_context_mode:
settings = tool_settings.gpencil_paint
row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
- if brush.gpencil_tool in {'FILL', 'DRAW'}:
+ if context.object and brush.gpencil_tool in {'FILL', 'DRAW'}:
draw_color_selector()
from .properties_paint_common import (
@@ -361,7 +361,7 @@ class _draw_left_context_mode:
sub.active = settings.use_thickness_curve
sub.popover(
panel="TOPBAR_PT_gpencil_primitive",
- text="Thickness Profile"
+ text="Thickness Profile",
)
if brush.gpencil_tool == 'FILL':
@@ -370,7 +370,7 @@ class _draw_left_context_mode:
sub = row.row(align=True)
sub.popover(
panel="TOPBAR_PT_gpencil_fill",
- text="Fill Options"
+ text="Fill Options",
)
@staticmethod
@@ -921,7 +921,7 @@ class TOPBAR_MT_help(Menu):
"wm.url_open", text="Blender Store", icon='URL',
).url = "https://store.blender.org"
layout.operator(
- "wm.url_open", text="Development Fund", icon='URL'
+ "wm.url_open", text="Development Fund", icon='URL',
).url = "https://fund.blender.org"
layout.operator(
"wm.url_open", text="Donate", icon='URL',
@@ -1084,11 +1084,11 @@ class TOPBAR_PT_name(Panel):
row.prop(item, "name", text="")
found = True
elif space_type == 'NODE_EDITOR':
- layout.label(text="Node Name")
+ layout.label(text="Node Label")
item = context.active_node
if item:
row = row_with_icon(layout, 'NODE')
- row.prop(item, "name", text="")
+ row.prop(item, "label", text="")
found = True
else:
if mode == 'POSE' or (mode == 'WEIGHT_PAINT' and context.pose_object):
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 21b423923df..4413e4f06eb 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -113,7 +113,7 @@ class VIEW3D_HT_header(Header):
sub.active = gpd.use_multiedit
sub.popover(
panel="VIEW3D_PT_gpencil_multi_frame",
- text="Multiframe"
+ text="Multiframe",
)
if gpd.use_stroke_edit_mode:
@@ -122,7 +122,7 @@ class VIEW3D_HT_header(Header):
row.popover(
panel="VIEW3D_PT_tools_grease_pencil_interpolate",
- text="Interpolate"
+ text="Interpolate",
)
VIEW3D_MT_editor_menus.draw_collapsible(context, layout)
@@ -132,6 +132,17 @@ class VIEW3D_HT_header(Header):
# Mode & Transform Settings
scene = context.scene
+ # Gizmo popover.
+ row = layout.row(align=True)
+ # FIXME: place-holder icon.
+ row.prop(view, "show_gizmo", text="", toggle=True, icon='EMPTY_DATA')
+ sub = row.row(align=True)
+ sub.active = view.show_gizmo
+ sub.popover(
+ panel="VIEW3D_PT_gizmo_display",
+ text="",
+ )
+
# Orientation
if object_mode in {'OBJECT', 'EDIT', 'EDIT_GPENCIL'} or has_pose_mode:
orient_slot = scene.transform_orientation_slots[0]
@@ -252,7 +263,7 @@ class VIEW3D_HT_header(Header):
sub.active = settings.use_guide
sub.popover(
panel="VIEW3D_PT_gpencil_guide",
- text="Guides"
+ text="Guides",
)
layout.separator_spacer()
@@ -275,7 +286,7 @@ class VIEW3D_HT_header(Header):
row.prop(overlay, "show_overlays", icon='OVERLAY', text="")
sub = row.row(align=True)
sub.active = overlay.show_overlays
- sub.popover(panel="VIEW3D_PT_overlay")
+ sub.popover(panel="VIEW3D_PT_overlay", text="")
row = layout.row()
row.active = (shading.type in {'WIREFRAME', 'SOLID'}) or object_mode in {'EDIT'}
@@ -292,7 +303,7 @@ class VIEW3D_HT_header(Header):
# show the shading popover which shows double-sided option.
# sub.enabled = shading.type != 'RENDERED'
- sub.popover(panel="VIEW3D_PT_shading")
+ sub.popover(panel="VIEW3D_PT_shading", text="")
class VIEW3D_MT_editor_menus(Menu):
@@ -4342,7 +4353,7 @@ class VIEW3D_PT_view3d_lock(Panel):
view, "lock_bone", lock_object.data,
"edit_bones" if lock_object.mode == 'EDIT'
else "bones",
- text=""
+ text="",
)
else:
subcol.prop(view, "lock_cursor", text="Lock to 3D Cursor")
@@ -4507,7 +4518,8 @@ class VIEW3D_PT_shading(Panel):
return context.scene.display.shading
def draw(self, context):
- pass
+ layout = self.layout
+ layout.label(text="Viewport Shading")
class VIEW3D_PT_shading_lighting(Panel):
@@ -4658,26 +4670,28 @@ class VIEW3D_PT_shading_options(Panel):
sub = row.row()
sub.active = shading.show_xray
sub.prop(shading, "xray_alpha", text="X-Ray")
+ #X-ray mode is off when alpha is 1.0
+ xray_active = shading.show_xray and shading.xray_alpha != 1
row = col.row()
row.prop(shading, "show_shadows", text="")
- row.active = not shading.show_xray
+ row.active = not xray_active
sub = row.row(align=True)
sub.active = shading.show_shadows
sub.prop(shading, "shadow_intensity", text="Shadow")
sub.popover(
panel="VIEW3D_PT_shading_options_shadow",
icon='PREFERENCES',
- text=""
+ text="",
)
col = layout.column()
row = col.row()
- row.active = not shading.show_xray
+ row.active = not xray_active
row.prop(shading, "show_cavity")
- if shading.show_cavity and not shading.show_xray:
+ if shading.show_cavity and not xray_active:
row.prop(shading, "cavity_type", text="Type")
if shading.cavity_type in {'WORLD', 'BOTH'}:
@@ -4688,7 +4702,7 @@ class VIEW3D_PT_shading_options(Panel):
sub.popover(
panel="VIEW3D_PT_shading_options_ssao",
icon='PREFERENCES',
- text=""
+ text="",
)
if shading.cavity_type in {'SCREEN', 'BOTH'}:
@@ -4698,7 +4712,7 @@ class VIEW3D_PT_shading_options(Panel):
sub.prop(shading, "curvature_valley_factor", text="Valley")
row = col.row()
- row.active = not shading.show_xray
+ row.active = not xray_active
row.prop(shading, "use_dof", text="Depth Of Field")
if shading.type in {'WIREFRAME', 'SOLID'}:
@@ -4753,41 +4767,56 @@ class VIEW3D_PT_shading_options_ssao(Panel):
col.prop(scene.display, "matcap_ssao_attenuation")
-class VIEW3D_PT_overlay(Panel):
+class VIEW3D_PT_gizmo_display(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
- bl_label = "Overlays"
- bl_ui_units_x = 13
-
- def draw(self, context):
- pass
-
-
-class VIEW3D_PT_overlay_gizmo(Panel):
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'HEADER'
- bl_parent_id = 'VIEW3D_PT_overlay'
bl_label = "Gizmo"
- def draw_header(self, context):
- view = context.space_data
- self.layout.prop(view, "show_gizmo", text="")
-
def draw(self, context):
layout = self.layout
+ scene = context.scene
view = context.space_data
overlay = view.overlay
- display_all = overlay.show_overlays
col = layout.column()
- col.active = display_all
+ col.label(text="Viewport Gizmos")
+
+ col.active = view.show_gizmo
+ colsub = col.column()
+ colsub.prop(view, "show_gizmo_navigate", text="Navigate")
+ colsub.prop(view, "show_gizmo_tool", text="Active Tools")
+ colsub.prop(view, "show_gizmo_context", text="Active Object")
+
+ colsub = col.column()
+ colsub.active = view.show_gizmo_context
+ colsub.label(text="Object Gizmos")
+ colsub.prop(scene.transform_orientation_slots[1], "type", text="")
+ colsub.prop(view, "show_gizmo_object_translate", text="Move")
+ colsub.prop(view, "show_gizmo_object_rotate", text="Rotate")
+ colsub.prop(view, "show_gizmo_object_scale", text="Scale")
+
+ # Match order of object type visibility
+ colsub.label(text="Empty")
+ colsub.prop(view, "show_gizmo_empty_image", text="Image")
+ colsub.prop(view, "show_gizmo_empty_force_field", text="Force Field")
+ colsub.label(text="Light")
+ colsub.prop(view, "show_gizmo_light_size", text="Size")
+ colsub.prop(view, "show_gizmo_light_look_at", text="Look At")
+ colsub.label(text="Camera")
+ colsub.prop(view, "show_gizmo_camera_lens", text="Lens")
+ colsub.prop(view, "show_gizmo_camera_dof_distance", text="Focus Distance")
- row = col.row(align=True)
- row.active = view.show_gizmo
- row.prop(view, "show_gizmo_navigate", text="Navigate", toggle=True)
- row.prop(view, "show_gizmo_context", text="Active Object", toggle=True)
- row.prop(view, "show_gizmo_tool", text="Active Tools", toggle=True)
+
+class VIEW3D_PT_overlay(Panel):
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'HEADER'
+ bl_label = "Overlays"
+ bl_ui_units_x = 13
+
+ def draw(self, context):
+ layout = self.layout
+ layout.label(text="Viewport Overlays")
class VIEW3D_PT_overlay_guides(Panel):
@@ -5862,6 +5891,7 @@ class VIEW3D_PT_sculpt_context_menu(Panel):
if capabilities.has_height:
layout.prop(brush, "height", slider=True, text="Height")
+
class TOPBAR_PT_gpencil_materials(GreasePencilMaterialsPanel, Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
@@ -6040,8 +6070,8 @@ classes = (
VIEW3D_PT_shading_options,
VIEW3D_PT_shading_options_shadow,
VIEW3D_PT_shading_options_ssao,
+ VIEW3D_PT_gizmo_display,
VIEW3D_PT_overlay,
- VIEW3D_PT_overlay_gizmo,
VIEW3D_PT_overlay_guides,
VIEW3D_PT_overlay_object,
VIEW3D_PT_overlay_geometry,
diff --git a/release/scripts/templates_py/ui_previews_dynamic_enum.py b/release/scripts/templates_py/ui_previews_dynamic_enum.py
index 39a3750bfee..37ee28bd8cf 100644
--- a/release/scripts/templates_py/ui_previews_dynamic_enum.py
+++ b/release/scripts/templates_py/ui_previews_dynamic_enum.py
@@ -51,7 +51,11 @@ def enum_previews_from_directory_items(self, context):
for i, name in enumerate(image_paths):
# generates a thumbnail preview for a file.
filepath = os.path.join(directory, name)
- thumb = pcoll.load(filepath, filepath, 'IMAGE')
+ icon = pcoll.get(name)
+ if not icon:
+ thumb = pcoll.load(name, filepath, 'IMAGE')
+ else:
+ thumb = pcoll[name]
enum_items.append((name, name, "", thumb.icon_id, i))
pcoll.my_previews = enum_items
diff --git a/source/blender/alembic/CMakeLists.txt b/source/blender/alembic/CMakeLists.txt
index 813653b74c0..e6badc27110 100644
--- a/source/blender/alembic/CMakeLists.txt
+++ b/source/blender/alembic/CMakeLists.txt
@@ -72,8 +72,11 @@ set(SRC
intern/abc_util.h
)
+set(LIB
+)
+
if(WITH_ALEMBIC_HDF5)
add_definitions(-DWITH_ALEMBIC_HDF5)
endif()
-blender_add_lib(bf_alembic "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_alembic "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc
index 3f2ccab203d..f37dbab3b57 100644
--- a/source/blender/alembic/intern/abc_exporter.cc
+++ b/source/blender/alembic/intern/abc_exporter.cc
@@ -145,18 +145,20 @@ static bool object_type_is_exportable(Scene *scene, Object *ob)
* This ignores selection and layer visibility,
* and assumes that the dupli-object itself (e.g. the group-instantiating empty) is exported.
*/
-static bool export_object(const ExportSettings * const settings, const Base * const ob_base,
+static bool export_object(const ExportSettings * const settings, const Base * const base,
bool is_duplicated)
{
if (!is_duplicated) {
+ View3D *v3d = NULL;
+
/* These two tests only make sense when the object isn't being instanced
* into the scene. When it is, its exportability is determined by
* its dupli-object and the DupliObject::no_draw property. */
- if (settings->selected_only && !object_selected(ob_base)) {
+ if (settings->selected_only && !BASE_SELECTED(v3d, base)) {
return false;
}
// FIXME Sybren: handle these cleanly (maybe just remove code), now using active scene layer instead.
- if (settings->visible_layers_only && (ob_base->flag & BASE_VISIBLE) == 0) {
+ if (settings->visible_layers_only && !BASE_VISIBLE(v3d, base)) {
return false;
}
}
@@ -371,21 +373,21 @@ void AbcExporter::createTransformWritersHierarchy()
/* We do not export transforms for objects of these classes. */
break;
default:
- exploreTransform(base, ob->parent, NULL);
+ exploreTransform(base, ob, ob->parent, NULL);
}
}
}
}
-void AbcExporter::exploreTransform(Base *ob_base, Object *parent, Object *dupliObParent)
+void AbcExporter::exploreTransform(Base *base, Object *object, Object *parent, Object *dupliObParent)
{
/* If an object isn't exported itself, its duplilist shouldn't be
* exported either. */
- if (!export_object(&m_settings, ob_base, dupliObParent != NULL)) {
+ if (!export_object(&m_settings, base, dupliObParent != NULL)) {
return;
}
- Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, ob_base->object);
+ Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object);
if (object_type_is_exportable(m_settings.scene, ob)) {
createTransformWriter(ob, parent, dupliObParent);
}
@@ -393,9 +395,6 @@ void AbcExporter::exploreTransform(Base *ob_base, Object *parent, Object *dupliO
ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob);
if (lb) {
- Base fake_base = *ob_base; // copy flags (like selection state) from the real object.
- fake_base.next = fake_base.prev = NULL;
-
DupliObject *link = static_cast<DupliObject *>(lb->first);
Object *dupli_ob = NULL;
Object *dupli_parent = NULL;
@@ -410,8 +409,7 @@ void AbcExporter::exploreTransform(Base *ob_base, Object *parent, Object *dupliO
dupli_ob = link->ob;
dupli_parent = (dupli_ob->parent) ? dupli_ob->parent : ob;
- fake_base.object = dupli_ob;
- exploreTransform(&fake_base, dupli_parent, ob);
+ exploreTransform(base, dupli_ob, dupli_parent, ob);
}
}
@@ -491,27 +489,24 @@ AbcTransformWriter *AbcExporter::createTransformWriter(Object *ob, Object *paren
void AbcExporter::createShapeWriters()
{
for (Base *base = static_cast<Base *>(m_settings.view_layer->object_bases.first); base; base = base->next) {
- exploreObject(base, NULL);
+ exploreObject(base, base->object, NULL);
}
}
-void AbcExporter::exploreObject(Base *ob_base, Object *dupliObParent)
+void AbcExporter::exploreObject(Base *base, Object *object, Object *dupliObParent)
{
/* If an object isn't exported itself, its duplilist shouldn't be
* exported either. */
- if (!export_object(&m_settings, ob_base, dupliObParent != NULL)) {
+ if (!export_object(&m_settings, base, dupliObParent != NULL)) {
return;
}
- Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, ob_base->object);
+ Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object);
createShapeWriter(ob, dupliObParent);
ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob);
if (lb) {
- Base fake_base = *ob_base; // copy flags (like selection state) from the real object.
- fake_base.next = fake_base.prev = NULL;
-
DupliObject *link = static_cast<DupliObject *>(lb->first);
for (; link; link = link->next) {
@@ -520,8 +515,7 @@ void AbcExporter::exploreObject(Base *ob_base, Object *dupliObParent)
continue;
}
if (link->type == OB_DUPLICOLLECTION) {
- fake_base.object = link->ob;
- exploreObject(&fake_base, ob);
+ exploreObject(base, link->ob, ob);
}
}
diff --git a/source/blender/alembic/intern/abc_exporter.h b/source/blender/alembic/intern/abc_exporter.h
index d9628317036..5e00ccdff07 100644
--- a/source/blender/alembic/intern/abc_exporter.h
+++ b/source/blender/alembic/intern/abc_exporter.h
@@ -116,8 +116,8 @@ private:
void createTransformWritersHierarchy();
AbcTransformWriter *createTransformWriter(Object *ob, Object *parent, Object *dupliObParent);
- void exploreTransform(Base *ob_base, Object *parent, Object *dupliObParent);
- void exploreObject(Base *ob_base, Object *dupliObParent);
+ void exploreTransform(Base *base, Object *object, Object *parent, Object *dupliObParent);
+ void exploreObject(Base *base, Object *object, Object *dupliObParent);
void createShapeWriters();
void createShapeWriter(Object *ob, Object *dupliObParent);
void createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform);
diff --git a/source/blender/alembic/intern/abc_mball.cc b/source/blender/alembic/intern/abc_mball.cc
index d31c251f64d..bb6ab57643f 100644
--- a/source/blender/alembic/intern/abc_mball.cc
+++ b/source/blender/alembic/intern/abc_mball.cc
@@ -44,49 +44,33 @@ AbcMBallWriter::AbcMBallWriter(
AbcTransformWriter *parent,
uint32_t time_sampling,
ExportSettings &settings)
- : AbcObjectWriter(ob, time_sampling, settings, parent)
+ : AbcGenericMeshWriter(ob, parent, time_sampling, settings)
, m_bmain(bmain)
{
m_is_animated = isAnimated();
-
- m_mesh_ob = BKE_object_copy(bmain, ob);
- m_mesh_ob->runtime.curve_cache = (CurveCache *)MEM_callocN(
- sizeof(CurveCache),
- "CurveCache for AbcMBallWriter");
- /* TODO(Sybren): reimplement metaball writing as subclass of AbcGenericMeshWriter. */
- m_mesh_writer = new AbcMeshWriter(m_mesh_ob, parent, time_sampling, settings);
- m_mesh_writer->setIsAnimated(m_is_animated);
}
AbcMBallWriter::~AbcMBallWriter()
-{
- delete m_mesh_writer;
- BKE_object_free(m_mesh_ob);
-}
+{}
bool AbcMBallWriter::isAnimated() const
{
- MetaBall *mb = static_cast<MetaBall *>(m_object->data);
- if (mb->adt != NULL) return true;
-
- /* Any movement of any object in the parent chain
- * could cause the mball to deform. */
- for (Object *ob = m_object; ob != NULL; ob = ob->parent) {
- if (ob->adt != NULL) return true;
- }
- return false;
+ return true;
}
-void AbcMBallWriter::do_write()
+Mesh *AbcMBallWriter::getEvaluatedMesh(Scene * /*scene_eval*/, Object *ob_eval, bool &r_needsfree)
{
- /* We have already stored a sample for this object. */
- if (!m_first_frame && !m_is_animated)
- return;
+ if (ob_eval->runtime.mesh_eval != NULL) {
+ /* Mesh_eval only exists when generative modifiers are in use. */
+ r_needsfree = false;
+ return ob_eval->runtime.mesh_eval;
+ }
+ r_needsfree = true;
+ /* The approach below is copied from BKE_mesh_new_from_object() */
Mesh *tmpmesh = BKE_mesh_add(m_bmain, ((ID *)m_object->data)->name + 2);
BLI_assert(tmpmesh != NULL);
- m_mesh_ob->data = tmpmesh;
/* BKE_mesh_add gives us a user count we don't need */
id_us_min(&tmpmesh->id);
@@ -100,12 +84,14 @@ void AbcMBallWriter::do_write()
BKE_mesh_from_metaball(&disp, tmpmesh);
BKE_displist_free(&disp);
- BKE_mesh_texspace_copy_from_object(tmpmesh, m_mesh_ob);
+ BKE_mesh_texspace_copy_from_object(tmpmesh, m_object);
- m_mesh_writer->write();
+ return tmpmesh;
+}
- BKE_id_free(m_bmain, tmpmesh);
- m_mesh_ob->data = NULL;
+void AbcMBallWriter::freeEvaluatedMesh(struct Mesh *mesh)
+{
+ BKE_id_free(m_bmain, mesh);
}
bool AbcMBallWriter::isBasisBall(Scene *scene, Object *ob)
diff --git a/source/blender/alembic/intern/abc_mball.h b/source/blender/alembic/intern/abc_mball.h
index 629a584e89e..d2bdb8f297a 100644
--- a/source/blender/alembic/intern/abc_mball.h
+++ b/source/blender/alembic/intern/abc_mball.h
@@ -22,24 +22,21 @@
#define __ABC_MBALL_H__
#include "abc_object.h"
+#include "abc_mesh.h"
-class AbcMeshWriter;
struct EvaluationContext;
struct Main;
struct MetaBall;
struct Object;
/* AbcMBallWriter converts the metaballs to meshes at every frame,
- * and defers to a wrapped AbcMeshWriter to perform the writing
+ * and defers to AbcGenericMeshWriter to perform the writing
* to the Alembic file. Only the basis balls are exported, as this
* results in the entire shape as one mesh. */
-class AbcMBallWriter : public AbcObjectWriter {
- AbcMeshWriter *m_mesh_writer;
- Object *m_mesh_ob;
- bool m_is_animated;
+class AbcMBallWriter : public AbcGenericMeshWriter {
Main *m_bmain;
public:
- AbcMBallWriter(
+ explicit AbcMBallWriter(
Main *bmain,
Object *ob,
AbcTransformWriter *parent,
@@ -50,9 +47,12 @@ public:
static bool isBasisBall(Scene *scene, Object *ob);
+protected:
+ Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override;
+ void freeEvaluatedMesh(struct Mesh *mesh) override;
+
private:
- virtual void do_write();
- bool isAnimated() const;
+ bool isAnimated() const override;
};
diff --git a/source/blender/alembic/intern/abc_mesh.cc b/source/blender/alembic/intern/abc_mesh.cc
index 2472fe1945f..97cd6ffc0e3 100644
--- a/source/blender/alembic/intern/abc_mesh.cc
+++ b/source/blender/alembic/intern/abc_mesh.cc
@@ -387,14 +387,19 @@ void AbcGenericMeshWriter::do_write()
writeMesh(mesh);
}
- if (needsfree) BKE_id_free(NULL, mesh);
+ if (needsfree) freeEvaluatedMesh(mesh);
}
catch (...) {
- if (needsfree) BKE_id_free(NULL, mesh);
+ if (needsfree) freeEvaluatedMesh(mesh);
throw;
}
}
+void AbcGenericMeshWriter::freeEvaluatedMesh(struct Mesh *mesh)
+{
+ BKE_id_free(NULL, mesh);
+}
+
void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh)
{
std::vector<Imath::V3f> points, normals;
diff --git a/source/blender/alembic/intern/abc_mesh.h b/source/blender/alembic/intern/abc_mesh.h
index 938c292b686..35ad0f8b297 100644
--- a/source/blender/alembic/intern/abc_mesh.h
+++ b/source/blender/alembic/intern/abc_mesh.h
@@ -61,6 +61,7 @@ protected:
virtual void do_write();
virtual bool isAnimated() const;
virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) = 0;
+ virtual void freeEvaluatedMesh(struct Mesh *mesh);
Mesh *getFinalMesh(bool &r_needsfree);
diff --git a/source/blender/alembic/intern/abc_object.cc b/source/blender/alembic/intern/abc_object.cc
index 20a43a821ed..d8bd92121aa 100644
--- a/source/blender/alembic/intern/abc_object.cc
+++ b/source/blender/alembic/intern/abc_object.cc
@@ -290,12 +290,8 @@ Alembic::AbcGeom::IXform AbcObjectReader::xform()
return IXform(abc_parent, Alembic::AbcGeom::kWrapExisting);
}
- /* Should not happen. */
- std::cerr << "AbcObjectReader::xform(): "
- << "unable to find IXform for Alembic object '"
- << m_iobject.getFullName() << "'\n";
- BLI_assert(false);
-
+ /* This can happen in certain cases. For example, MeshLab exports
+ * point clouds without parent XForm. */
return IXform();
}
@@ -304,6 +300,8 @@ void AbcObjectReader::read_matrix(float r_mat[4][4], const float time,
{
IXform ixform = xform();
if (!ixform) {
+ unit_m4(r_mat);
+ is_constant = true;
return;
}
diff --git a/source/blender/alembic/intern/abc_util.cc b/source/blender/alembic/intern/abc_util.cc
index 8af7fb8b6f6..a4a7256b783 100644
--- a/source/blender/alembic/intern/abc_util.cc
+++ b/source/blender/alembic/intern/abc_util.cc
@@ -86,11 +86,6 @@ std::string get_object_dag_path_name(const Object * const ob, Object *dupli_pare
return name;
}
-bool object_selected(const Base * const ob_base)
-{
- return ob_base->flag & SELECT;
-}
-
Imath::M44d convert_matrix(float mat[4][4])
{
Imath::M44d m;
diff --git a/source/blender/alembic/intern/abc_util.h b/source/blender/alembic/intern/abc_util.h
index a78a787fa14..d21fe7a78ce 100644
--- a/source/blender/alembic/intern/abc_util.h
+++ b/source/blender/alembic/intern/abc_util.h
@@ -52,8 +52,6 @@ std::string get_id_name(const ID * const id);
std::string get_id_name(const Object * const ob);
std::string get_object_dag_path_name(const Object * const ob, Object *dupli_parent);
-bool object_selected(const Base * const ob_base);
-
Imath::M44d convert_matrix(float mat[4][4]);
typedef enum {
diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc
index bf10a9498b5..541db514bb0 100644
--- a/source/blender/alembic/intern/alembic_capi.cc
+++ b/source/blender/alembic/intern/alembic_capi.cc
@@ -292,6 +292,8 @@ static void export_endjob(void *customdata)
{
ExportJobData *data = static_cast<ExportJobData *>(customdata);
+ DEG_graph_free(data->settings.depsgraph);
+
if (data->was_canceled && BLI_exists(data->filename)) {
BLI_delete(data->filename, false, false);
}
diff --git a/source/blender/avi/CMakeLists.txt b/source/blender/avi/CMakeLists.txt
index d5d062bb826..b03c1a433fb 100644
--- a/source/blender/avi/CMakeLists.txt
+++ b/source/blender/avi/CMakeLists.txt
@@ -46,4 +46,7 @@ set(SRC
intern/avi_rgb32.h
)
-blender_add_lib(bf_avi "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_avi "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/blenfont/CMakeLists.txt b/source/blender/blenfont/CMakeLists.txt
index e8a7750c445..25497d1b1c2 100644
--- a/source/blender/blenfont/CMakeLists.txt
+++ b/source/blender/blenfont/CMakeLists.txt
@@ -51,6 +51,9 @@ set(SRC
intern/blf_internal_types.h
)
+set(LIB
+)
+
if(WIN32)
list(APPEND SRC
intern/blf_font_win32_compat.c
@@ -70,4 +73,4 @@ endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_blenfont "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_blenfont "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index 9f81c41bd2d..06904ee36b6 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -74,11 +74,12 @@ static SpinLock ft_lib_mutex;
/* -------------------------------------------------------------------- */
/** \name Glyph Batching
* \{ */
+
/**
* Drawcalls are precious! make them count!
* Since most of the Text elems are not covered by other UI elements, we can
* group some strings together and render them in one drawcall. This behavior
- * is on demand only, between BLF_batch_start() and BLF_batch_end().
+ * is on demand only, between #BLF_batch_draw_begin() and #BLF_batch_draw_end().
*/
static void blf_batch_draw_init(void)
{
diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h
index 09d8cbf933c..d9a1064d740 100644
--- a/source/blender/blenkernel/BKE_armature.h
+++ b/source/blender/blenkernel/BKE_armature.h
@@ -184,7 +184,7 @@ void BKE_pchan_bbone_spline_params_get(
struct bPoseChannel *pchan, const bool rest, struct BBoneSplineParameters *r_param);
void BKE_pchan_bbone_spline_setup(
- struct bPoseChannel *pchan, const bool rest, Mat4 result_array[MAX_BBONE_SUBDIV]);
+ struct bPoseChannel *pchan, const bool rest, const bool for_deform, Mat4 *result_array);
void BKE_pchan_bbone_handles_compute(
const BBoneSplineParameters *param,
@@ -192,13 +192,15 @@ void BKE_pchan_bbone_handles_compute(
float h2[3], float *r_roll2,
bool ease, bool offsets);
int BKE_pchan_bbone_spline_compute(
- struct BBoneSplineParameters *param, Mat4 result_array[MAX_BBONE_SUBDIV]);
+ struct BBoneSplineParameters *param, const bool for_deform, Mat4 *result_array);
void BKE_pchan_bbone_segments_cache_compute(
struct bPoseChannel *pchan);
void BKE_pchan_bbone_segments_cache_copy(
struct bPoseChannel *pchan, struct bPoseChannel *pchan_from);
+void BKE_pchan_bbone_deform_segment_index(const struct bPoseChannel *pchan, float pos, int *r_index, float *r_blend_next);
+
/* like EBONE_VISIBLE */
#define PBONE_VISIBLE(arm, bone) ( \
CHECK_TYPE_INLINE(arm, bArmature *), \
@@ -243,6 +245,8 @@ void BKE_splineik_execute_tree(
struct Depsgraph *depsgraph, struct Scene *scene,
struct Object *ob, struct bPoseChannel *pchan_root, float ctime);
+void BKE_pose_pchan_index_rebuild(struct bPose *pose);
+
void BKE_pose_eval_init(
struct Depsgraph *depsgraph,
struct Scene *scene,
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 4c88410134c..7d9fa2d428f 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -20,21 +20,25 @@
* \ingroup bke
*/
-/* these lines are grep'd, watch out for our not-so-awesome regex
- * and keep comment above the defines.
- * Use STRINGIFY() rather than defining with quotes */
+/**
+ * The lines below use regex from scripts to extract their values,
+ * Keep this in mind when modifying this file and keep this comment above the defines.
+ *
+ * \note Use #STRINGIFY() rather than defining with quotes.
+ */
#define BLENDER_VERSION 280
-#define BLENDER_SUBVERSION 54
-/* Several breakages with 280, e.g. collections vs layers */
+#define BLENDER_SUBVERSION 57
+/** Several breakages with 280, e.g. collections vs layers. */
#define BLENDER_MINVERSION 280
#define BLENDER_MINSUBVERSION 0
-/* used by packaging tools */
-/* can be left blank, otherwise a,b,c... etc with no quotes */
+/** Used by packaging tools. */
+/** Can be left blank, otherwise a,b,c... etc with no quotes. */
#define BLENDER_VERSION_CHAR
-/* alpha/beta/rc/release, docs use this */
+/** alpha/beta/rc/release, docs use this. */
#define BLENDER_VERSION_CYCLE beta
-extern char versionstr[]; /* from blender.c */
+/** Defined in from blender.c */
+extern char versionstr[];
#endif /* __BKE_BLENDER_VERSION_H__ */
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index e94ba27a8ea..a22d30a01fa 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -86,7 +86,6 @@ void BKE_gpencil_frame_delete_laststroke(struct bGPDlayer *gpl, struct bGPDframe
/* materials */
void BKE_gpencil_material_index_remove(struct bGPdata *gpd, int index);
void BKE_gpencil_material_remap(struct bGPdata *gpd, const unsigned int *remap, unsigned int remap_len);
-int BKE_gpencil_get_material_index(struct Object *ob, struct Material *ma);
/* statistics functions */
void BKE_gpencil_stats_update(struct bGPdata *gpd);
@@ -131,23 +130,29 @@ struct bGPDlayer *BKE_gpencil_layer_getactive(struct bGPdata *gpd);
void BKE_gpencil_layer_setactive(struct bGPdata *gpd, struct bGPDlayer *active);
void BKE_gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl);
-struct Material *BKE_gpencil_get_material_from_brush(struct Brush *brush);
-void BKE_gpencil_brush_set_material(struct Brush *brush, struct Material *material);
+/* Brush */
+struct Material *BKE_gpencil_brush_material_get(struct Brush *brush);
+void BKE_gpencil_brush_material_set(struct Brush *brush, struct Material *material);
-struct Material *BKE_gpencil_handle_brush_material(struct Main *bmain, struct Object *ob, struct Brush *brush);
-int BKE_gpencil_handle_material(struct Main *bmain, struct Object *ob, struct Material *material);
+/* Object */
+struct Material *BKE_gpencil_object_material_ensure_active(struct Main *bmain, struct Object *ob);
+struct Material *BKE_gpencil_object_material_ensure_from_brush(struct Main *bmain, struct Object *ob, struct Brush *brush);
+int BKE_gpencil_object_material_ensure(struct Main *bmain, struct Object *ob, struct Material *material);
-struct Material *BKE_gpencil_handle_new_material(struct Main *bmain, struct Object *ob, const char *name, int *r_index);
+struct Material *BKE_gpencil_object_material_new(struct Main *bmain, struct Object *ob, const char *name, int *r_index);
-struct Material *BKE_gpencil_get_material_for_brush(struct Object *ob, struct Brush *brush);
-int BKE_gpencil_get_material_index_for_brush(struct Object *ob, struct Brush *brush);
+int BKE_gpencil_object_material_get_index(struct Object *ob, struct Material *ma);
-struct Material *BKE_gpencil_current_input_toolsettings_material(struct Main *bmain, struct Object *ob, struct ToolSettings *ts);
-struct Material *BKE_gpencil_current_input_brush_material(struct Main *bmain, struct Object *ob, struct Brush *brush);
-struct Material *BKE_gpencil_current_input_material(struct Main *bmain, struct Object *ob);
+struct Material *BKE_gpencil_object_material_get_from_brush(struct Object *ob, struct Brush *brush);
+int BKE_gpencil_object_material_get_index_from_brush(struct Object *ob, struct Brush *brush);
+struct Material *BKE_gpencil_object_material_ensure_from_active_input_toolsettings(
+ struct Main *bmain, struct Object *ob, struct ToolSettings *ts);
+struct Material *BKE_gpencil_object_material_ensure_from_active_input_brush(
+ struct Main *bmain, struct Object *ob, struct Brush *brush);
+struct Material *BKE_gpencil_object_material_ensure_from_active_input_material(
+ struct Main *bmain, struct Object *ob);
-struct Material *BKE_gpencil_material_ensure(struct Main *bmain, struct Object *ob);
/* object boundbox */
bool BKE_gpencil_data_minmax(
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index 9f522c11733..10f57f88e76 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -409,7 +409,7 @@ const char *modifier_path_relbase_from_global(struct Object *ob);
* If the modifier data is already original, return it as-is. */
struct ModifierData *modifier_get_original(struct ModifierData *md);
struct ModifierData *modifier_get_evaluated(
- struct Depsgraph* depsgraph,
+ struct Depsgraph *depsgraph,
struct Object *object,
struct ModifierData *md);
diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h
index 4e4883222cc..2ab2bd3fbe6 100644
--- a/source/blender/blenkernel/BKE_scene.h
+++ b/source/blender/blenkernel/BKE_scene.h
@@ -128,6 +128,7 @@ float BKE_scene_frame_get(const struct Scene *scene);
float BKE_scene_frame_get_from_ctime(const struct Scene *scene, const float frame);
void BKE_scene_frame_set(struct Scene *scene, double cfra);
+struct TransformOrientationSlot *BKE_scene_orientation_slot_get_from_flag(struct Scene *scene, int slot_index);
struct TransformOrientationSlot *BKE_scene_orientation_slot_get(struct Scene *scene, int flag);
void BKE_scene_orientation_slot_set_index(struct TransformOrientationSlot *orient_slot, int orientation);
int BKE_scene_orientation_slot_get_index(const struct TransformOrientationSlot *orient_slot);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 7162d75875d..50aa1c3825e 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -356,6 +356,9 @@ set(SRC
intern/subdiv_inline.h
)
+set(LIB
+)
+
if(WITH_BINRELOC)
list(APPEND INC_SYS
${BINRELOC_INCLUDE_DIRS}
@@ -567,4 +570,4 @@ endif()
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX")
#endif()
-blender_add_lib(bf_blenkernel "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_blenkernel "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index 9ba3a23df05..da4fea26e64 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -825,6 +825,8 @@ void BKE_pose_channels_free_ex(bPose *pose, bool do_id_user)
}
BKE_pose_channels_hash_free(pose);
+
+ MEM_SAFE_FREE(pose->chan_array);
}
void BKE_pose_channels_free(bPose *pose)
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index 6e0767836d7..1b9e6dcdbc4 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -36,6 +36,7 @@
#include "BLI_ghash.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
+#include "BLI_alloca.h"
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
@@ -389,45 +390,60 @@ int bone_autoside_name(char name[MAXBONENAME], int UNUSED(strip_number), short a
/* ************* B-Bone support ******************* */
-/* data has MAX_BBONE_SUBDIV+1 interpolated points, will become desired amount with equal distances */
-static void equalize_bbone_bezier(float *data, int desired)
+/* Compute a set of bezier parameter values that produce approximately equally spaced points. */
+static void equalize_cubic_bezier(const float control[4][3], int temp_segments, int final_segments, float *r_t_points)
{
- float *fp, totdist, ddist, dist, fac1, fac2;
- float pdist[MAX_BBONE_SUBDIV + 1];
- float temp[MAX_BBONE_SUBDIV + 1][4];
- int a, nr;
+ float (*coords)[3] = BLI_array_alloca(coords, temp_segments + 1);
+ float *pdist = BLI_array_alloca(pdist, temp_segments + 1);
+ /* Compute the first pass of bezier point coordinates. */
+ for (int i = 0; i < 3; i++) {
+ BKE_curve_forward_diff_bezier(
+ control[0][i], control[1][i], control[2][i], control[3][i],
+ &coords[0][i], temp_segments, sizeof(*coords)
+ );
+ }
+
+ /* Calculate the length of the polyline at each point. */
pdist[0] = 0.0f;
- for (a = 0, fp = data; a < MAX_BBONE_SUBDIV; a++, fp += 4) {
- copy_qt_qt(temp[a], fp);
- pdist[a + 1] = pdist[a] + len_v3v3(fp, fp + 4);
- }
- /* do last point */
- copy_qt_qt(temp[a], fp);
- totdist = pdist[a];
-
- /* go over distances and calculate new points */
- ddist = totdist / ((float)desired);
- nr = 1;
- for (a = 1, fp = data + 4; a < desired; a++, fp += 4) {
- dist = ((float)a) * ddist;
-
- /* we're looking for location (distance) 'dist' in the array */
- while ((nr < MAX_BBONE_SUBDIV) && (dist >= pdist[nr]))
+
+ for (int i = 0; i < temp_segments; i++)
+ pdist[i + 1] = pdist[i] + len_v3v3(coords[i], coords[i + 1]);
+
+ /* Go over distances and calculate new parameter values. */
+ float dist_step = pdist[temp_segments] / final_segments;
+
+ r_t_points[0] = 0.0f;
+
+ for (int i = 1, nr = 1; i <= final_segments; i++) {
+ float dist = i * dist_step;
+
+ /* We're looking for location (distance) 'dist' in the array. */
+ while ((nr < temp_segments) && (dist >= pdist[nr]))
nr++;
- fac1 = pdist[nr] - pdist[nr - 1];
- fac2 = pdist[nr] - dist;
- fac1 = fac2 / fac1;
- fac2 = 1.0f - fac1;
+ float fac = (pdist[nr] - dist) / (pdist[nr] - pdist[nr - 1]);
- fp[0] = fac1 * temp[nr - 1][0] + fac2 * temp[nr][0];
- fp[1] = fac1 * temp[nr - 1][1] + fac2 * temp[nr][1];
- fp[2] = fac1 * temp[nr - 1][2] + fac2 * temp[nr][2];
- fp[3] = fac1 * temp[nr - 1][3] + fac2 * temp[nr][3];
+ r_t_points[i] = (nr - fac) / temp_segments;
}
- /* set last point, needed for orientation calculus */
- copy_qt_qt(fp, temp[MAX_BBONE_SUBDIV]);
+
+ r_t_points[final_segments] = 1.0f;
+}
+
+/* Evaluate bezier position and tangent at a specific parameter value using the De Casteljau algorithm. */
+static void evaluate_cubic_bezier(const float control[4][3], float t, float r_pos[3], float r_tangent[3])
+{
+ float layer1[3][3];
+ interp_v3_v3v3(layer1[0], control[0], control[1], t);
+ interp_v3_v3v3(layer1[1], control[1], control[2], t);
+ interp_v3_v3v3(layer1[2], control[2], control[3], t);
+
+ float layer2[2][3];
+ interp_v3_v3v3(layer2[0], layer1[0], layer1[1], t);
+ interp_v3_v3v3(layer2[1], layer1[1], layer1[2], t);
+
+ sub_v3_v3v3(r_tangent, layer2[1], layer2[0]);
+ madd_v3_v3v3fl(r_pos, layer2[0], r_tangent, t);
}
/* Get "next" and "prev" bones - these are used for handle calculations. */
@@ -640,13 +656,13 @@ void BKE_pchan_bbone_spline_params_get(struct bPoseChannel *pchan, const bool re
/* Fills the array with the desired amount of bone->segments elements.
* This calculation is done within unit bone space. */
-void BKE_pchan_bbone_spline_setup(bPoseChannel *pchan, const bool rest, Mat4 result_array[MAX_BBONE_SUBDIV])
+void BKE_pchan_bbone_spline_setup(bPoseChannel *pchan, const bool rest, const bool for_deform, Mat4 *result_array)
{
BBoneSplineParameters param;
BKE_pchan_bbone_spline_params_get(pchan, rest, &param);
- pchan->bone->segments = BKE_pchan_bbone_spline_compute(&param, result_array);
+ pchan->bone->segments = BKE_pchan_bbone_spline_compute(&param, for_deform, result_array);
}
/* Computes the bezier handle vectors and rolls coming from custom handles. */
@@ -654,6 +670,7 @@ void BKE_pchan_bbone_handles_compute(const BBoneSplineParameters *param, float h
{
float mat3[3][3];
float length = param->length;
+ float epsilon = 1e-5 * length;
if (param->do_scale) {
length *= param->scale[1];
@@ -669,7 +686,9 @@ void BKE_pchan_bbone_handles_compute(const BBoneSplineParameters *param, float h
h1[1] -= length;
}
- normalize_v3(h1);
+ if (normalize_v3(h1) < epsilon)
+ copy_v3_fl3(h1, 0.0f, -1.0f, 0.0f);
+
negate_v3(h1);
if (!param->prev_bbone) {
@@ -693,7 +712,8 @@ void BKE_pchan_bbone_handles_compute(const BBoneSplineParameters *param, float h
h2[1] -= length;
}
- normalize_v3(h2);
+ if (normalize_v3(h2) < epsilon)
+ copy_v3_fl3(h2, 0.0f, 1.0f, 0.0f);
/* Find the next roll to interpolate as well. */
copy_m3_m4(mat3, param->next_mat);
@@ -749,20 +769,55 @@ void BKE_pchan_bbone_handles_compute(const BBoneSplineParameters *param, float h
}
}
+static void make_bbone_spline_matrix(
+ BBoneSplineParameters *param, float scalemats[2][4][4],
+ float pos[3], float axis[3], float roll, float scalefac,
+ float result[4][4]
+) {
+ float mat3[3][3];
+
+ vec_roll_to_mat3(axis, roll, mat3);
+
+ copy_m4_m3(result, mat3);
+ copy_v3_v3(result[3], pos);
+
+ if (param->do_scale) {
+ /* Correct for scaling when this matrix is used in scaled space. */
+ mul_m4_series(result, scalemats[0], result, scalemats[1]);
+ }
+
+ /* BBone scale... */
+ mul_v3_fl(result[0], scalefac);
+ mul_v3_fl(result[2], scalefac);
+}
+
+/* Fade from first to second derivative when the handle is very short. */
+static void ease_handle_axis(const float deriv1[3], const float deriv2[3], float r_axis[3])
+{
+ const float gap = 0.1f;
+
+ copy_v3_v3(r_axis, deriv1);
+
+ float len1 = len_squared_v3(deriv1), len2 = len_squared_v3(deriv2);
+ float ratio = len1 / len2;
+
+ if (ratio < gap * gap) {
+ madd_v3_v3fl(r_axis, deriv2, gap - sqrtf(ratio));
+ }
+}
+
/* Fills the array with the desired amount of bone->segments elements.
* This calculation is done within unit bone space. */
-int BKE_pchan_bbone_spline_compute(BBoneSplineParameters *param, Mat4 result_array[MAX_BBONE_SUBDIV])
+int BKE_pchan_bbone_spline_compute(BBoneSplineParameters *param, const bool for_deform, Mat4 *result_array)
{
- float scalemat[4][4], iscalemat[4][4];
- float mat3[3][3];
- float h1[3], roll1, h2[3], roll2;
- float data[MAX_BBONE_SUBDIV + 1][4], *fp;
+ float scalemats[2][4][4];
+ float bezt_controls[4][3];
+ float h1[3], roll1, h2[3], roll2, prev[3], cur[3], axis[3];
float length = param->length;
- int a;
if (param->do_scale) {
- size_to_mat4(scalemat, param->scale);
- invert_m4_m4(iscalemat, scalemat);
+ size_to_mat4(scalemats[1], param->scale);
+ invert_m4_m4(scalemats[0], scalemats[1]);
length *= param->scale[1];
}
@@ -772,48 +827,60 @@ int BKE_pchan_bbone_spline_compute(BBoneSplineParameters *param, Mat4 result_arr
/* Make curve. */
CLAMP_MAX(param->segments, MAX_BBONE_SUBDIV);
- BKE_curve_forward_diff_bezier(0.0f, h1[0], h2[0], 0.0f, data[0], MAX_BBONE_SUBDIV, 4 * sizeof(float));
- BKE_curve_forward_diff_bezier(0.0f, h1[1], length + h2[1], length, data[0] + 1, MAX_BBONE_SUBDIV, 4 * sizeof(float));
- BKE_curve_forward_diff_bezier(0.0f, h1[2], h2[2], 0.0f, data[0] + 2, MAX_BBONE_SUBDIV, 4 * sizeof(float));
- BKE_curve_forward_diff_bezier(roll1, roll1 + 0.390464f * (roll2 - roll1), roll2 - 0.390464f * (roll2 - roll1), roll2, data[0] + 3, MAX_BBONE_SUBDIV, 4 * sizeof(float));
+ copy_v3_fl3(bezt_controls[3], 0.0f, length, 0.0f);
+ add_v3_v3v3(bezt_controls[2], bezt_controls[3], h2);
+ copy_v3_v3(bezt_controls[1], h1);
+ zero_v3(bezt_controls[0]);
- equalize_bbone_bezier(data[0], param->segments); /* note: does stride 4! */
+ float bezt_points[MAX_BBONE_SUBDIV + 1];
- /* Make transformation matrices for the segments for drawing. */
- for (a = 0, fp = data[0]; a < param->segments; a++, fp += 4) {
- sub_v3_v3v3(h1, fp + 4, fp);
- vec_roll_to_mat3(h1, fp[3], mat3); /* fp[3] is roll */
+ equalize_cubic_bezier(bezt_controls, MAX_BBONE_SUBDIV, param->segments, bezt_points);
- copy_m4_m3(result_array[a].mat, mat3);
- copy_v3_v3(result_array[a].mat[3], fp);
+ /* Deformation uses N+1 matrices computed at points between the segments. */
+ if (for_deform) {
+ /* Bezier derivatives. */
+ float bezt_deriv1[3][3], bezt_deriv2[2][3];
- if (param->do_scale) {
- /* Correct for scaling when this matrix is used in scaled space. */
- mul_m4_series(result_array[a].mat, iscalemat, result_array[a].mat, scalemat);
+ for (int i = 0; i < 3; i++) {
+ sub_v3_v3v3(bezt_deriv1[i], bezt_controls[i + 1], bezt_controls[i]);
+ }
+ for (int i = 0; i < 2; i++) {
+ sub_v3_v3v3(bezt_deriv2[i], bezt_deriv1[i + 1], bezt_deriv1[i]);
}
- /* BBone scale... */
- {
- const int num_segments = param->segments;
+ /* End points require special handling to fix zero length handles. */
+ ease_handle_axis(bezt_deriv1[0], bezt_deriv2[0], axis);
+ make_bbone_spline_matrix(param, scalemats, bezt_controls[0], axis, roll1, param->scaleIn, result_array[0].mat);
- const float scaleIn = param->scaleIn;
- const float scaleFactorIn = 1.0f + (scaleIn - 1.0f) * ((float)(num_segments - a) / (float)num_segments);
+ for (int a = 1; a < param->segments; a++) {
+ evaluate_cubic_bezier(bezt_controls, bezt_points[a], cur, axis);
- const float scaleOut = param->scaleOut;
- const float scaleFactorOut = 1.0f + (scaleOut - 1.0f) * ((float)(a + 1) / (float)num_segments);
+ float fac = ((float)a) / param->segments;
+ float roll = interpf(roll2, roll1, fac);
+ float scalefac = interpf(param->scaleOut, param->scaleIn, fac);
- const float scalefac = scaleFactorIn * scaleFactorOut;
- float bscalemat[4][4], bscale[3];
+ make_bbone_spline_matrix(param, scalemats, cur, axis, roll, scalefac, result_array[a].mat);
+ }
+
+ negate_v3(bezt_deriv2[1]);
+ ease_handle_axis(bezt_deriv1[2], bezt_deriv2[1], axis);
+ make_bbone_spline_matrix(param, scalemats, bezt_controls[3], axis, roll2, param->scaleOut, result_array[param->segments].mat);
+ }
+ /* Other code (e.g. display) uses matrices for the segments themselves. */
+ else {
+ zero_v3(prev);
+
+ for (int a = 0; a < param->segments; a++) {
+ evaluate_cubic_bezier(bezt_controls, bezt_points[a + 1], cur, axis);
- bscale[0] = scalefac;
- bscale[1] = 1.0f;
- bscale[2] = scalefac;
+ sub_v3_v3v3(axis, cur, prev);
- size_to_mat4(bscalemat, bscale);
+ float fac = (a + 0.5f) / param->segments;
+ float roll = interpf(roll2, roll1, fac);
+ float scalefac = interpf(param->scaleOut, param->scaleIn, fac);
- /* Note: don't multiply by inverse scale mat here, as it causes problems with scaling shearing and breaking segment chains */
- /*mul_m4_series(result_array[a].mat, ibscalemat, result_array[a].mat, bscalemat);*/
- mul_m4_series(result_array[a].mat, result_array[a].mat, bscalemat);
+ make_bbone_spline_matrix(param, scalemats, prev, axis, roll, scalefac, result_array[a].mat);
+ copy_v3_v3(prev, cur);
}
}
@@ -843,10 +910,10 @@ static void allocate_bbone_cache(bPoseChannel *pchan, int segments)
}
runtime->bbone_segments = segments;
- runtime->bbone_rest_mats = MEM_malloc_arrayN(sizeof(Mat4), (uint)segments, "bPoseChannel_Runtime::bbone_rest_mats");
- runtime->bbone_pose_mats = MEM_malloc_arrayN(sizeof(Mat4), (uint)segments, "bPoseChannel_Runtime::bbone_pose_mats");
- runtime->bbone_deform_mats = MEM_malloc_arrayN(sizeof(Mat4), 1 + (uint)segments, "bPoseChannel_Runtime::bbone_deform_mats");
- runtime->bbone_dual_quats = MEM_malloc_arrayN(sizeof(DualQuat), (uint)segments, "bPoseChannel_Runtime::bbone_dual_quats");
+ runtime->bbone_rest_mats = MEM_malloc_arrayN(sizeof(Mat4), 1 + (uint)segments, "bPoseChannel_Runtime::bbone_rest_mats");
+ runtime->bbone_pose_mats = MEM_malloc_arrayN(sizeof(Mat4), 1 + (uint)segments, "bPoseChannel_Runtime::bbone_pose_mats");
+ runtime->bbone_deform_mats = MEM_malloc_arrayN(sizeof(Mat4), 2 + (uint)segments, "bPoseChannel_Runtime::bbone_deform_mats");
+ runtime->bbone_dual_quats = MEM_malloc_arrayN(sizeof(DualQuat), 1 + (uint)segments, "bPoseChannel_Runtime::bbone_dual_quats");
}
}
@@ -869,8 +936,8 @@ void BKE_pchan_bbone_segments_cache_compute(bPoseChannel *pchan)
DualQuat *b_bone_dual_quats = runtime->bbone_dual_quats;
int a;
- BKE_pchan_bbone_spline_setup(pchan, false, b_bone);
- BKE_pchan_bbone_spline_setup(pchan, true, b_bone_rest);
+ BKE_pchan_bbone_spline_setup(pchan, false, true, b_bone);
+ BKE_pchan_bbone_spline_setup(pchan, true, true, b_bone_rest);
/* Compute deform matrices. */
/* first matrix is the inverse arm_mat, to bring points in local bone space
@@ -883,7 +950,7 @@ void BKE_pchan_bbone_segments_cache_compute(bPoseChannel *pchan)
* - transform with b_bone matrix
* - transform back into global space */
- for (a = 0; a < bone->segments; a++) {
+ for (a = 0; a <= bone->segments; a++) {
float tmat[4][4];
invert_m4_m4(tmat, b_bone_rest[a].mat);
@@ -906,44 +973,83 @@ void BKE_pchan_bbone_segments_cache_copy(bPoseChannel *pchan, bPoseChannel *pcha
else {
allocate_bbone_cache(pchan, segments);
- memcpy(runtime->bbone_rest_mats, runtime_from->bbone_rest_mats, sizeof(Mat4) * segments);
- memcpy(runtime->bbone_pose_mats, runtime_from->bbone_pose_mats, sizeof(Mat4) * segments);
- memcpy(runtime->bbone_deform_mats, runtime_from->bbone_deform_mats, sizeof(Mat4) * (1 + segments));
- memcpy(runtime->bbone_dual_quats, runtime_from->bbone_dual_quats, sizeof(DualQuat) * segments);
+ memcpy(runtime->bbone_rest_mats, runtime_from->bbone_rest_mats, sizeof(Mat4) * (1 + segments));
+ memcpy(runtime->bbone_pose_mats, runtime_from->bbone_pose_mats, sizeof(Mat4) * (1 + segments));
+ memcpy(runtime->bbone_deform_mats, runtime_from->bbone_deform_mats, sizeof(Mat4) * (2 + segments));
+ memcpy(runtime->bbone_dual_quats, runtime_from->bbone_dual_quats, sizeof(DualQuat) * (1 + segments));
}
}
-static void b_bone_deform(const bPoseChannel *pchan, float co[3], DualQuat *dq, float defmat[3][3])
+/** Calculate index and blend factor for the two B-Bone segment nodes affecting the point at 0 <= pos <= 1. */
+void BKE_pchan_bbone_deform_segment_index(const bPoseChannel *pchan, float pos, int *r_index, float *r_blend_next)
{
- Bone *bone = pchan->bone;
- const Mat4 *b_bone = pchan->runtime.bbone_deform_mats;
- const float (*mat)[4] = b_bone[0].mat;
- float segment, y;
- int a;
+ int segments = pchan->bone->segments;
- /* need to transform co back to bonespace, only need y */
- y = mat[0][1] * co[0] + mat[1][1] * co[1] + mat[2][1] * co[2] + mat[3][1];
+ CLAMP(pos, 0.0f, 1.0f);
+
+ /* Calculate the indices of the 2 affecting b_bone segments.
+ * Integer part is the first segment's index.
+ * Integer part plus 1 is the second segment's index.
+ * Fractional part is the blend factor. */
+ float pre_blend = pos * (float)segments;
+
+ int index = (int)floorf(pre_blend);
+ float blend = pre_blend - index;
+
+ CLAMP(index, 0, segments);
+ CLAMP(blend, 0.0f, 1.0f);
- /* now calculate which of the b_bones are deforming this */
- segment = bone->length / ((float)bone->segments);
- a = (int)(y / segment);
+ *r_index = index;
+ *r_blend_next = blend;
+}
+
+/* Add the effect of one bone or B-Bone segment to the accumulated result. */
+static void pchan_deform_accumulate(
+ const DualQuat *deform_dq, const float deform_mat[4][4], const float co_in[3], float weight,
+ float co_accum[3], DualQuat *dq_accum, float mat_accum[3][3]
+) {
+ if (weight == 0.0f)
+ return;
- /* note; by clamping it extends deform at endpoints, goes best with
- * straight joints in restpos. */
- CLAMP(a, 0, bone->segments - 1);
+ if (dq_accum) {
+ BLI_assert(!co_accum);
- if (dq) {
- copy_dq_dq(dq, &(pchan->runtime.bbone_dual_quats)[a]);
+ add_weighted_dq_dq(dq_accum, deform_dq, weight);
}
else {
- mul_m4_v3(b_bone[a + 1].mat, co);
+ float tmp[3];
+ mul_v3_m4v3(tmp, deform_mat, co_in);
- if (defmat) {
- copy_m3_m4(defmat, b_bone[a + 1].mat);
+ sub_v3_v3(tmp, co_in);
+ madd_v3_v3fl(co_accum, tmp, weight);
+
+ if (mat_accum) {
+ float tmpmat[3][3];
+ copy_m3_m4(tmpmat, deform_mat);
+
+ madd_m3_m3m3fl(mat_accum, mat_accum, tmpmat, weight);
}
}
}
+static void b_bone_deform(const bPoseChannel *pchan, const float co[3], float weight, float vec[3], DualQuat *dq, float defmat[3][3])
+{
+ const DualQuat *quats = pchan->runtime.bbone_dual_quats;
+ const Mat4 *mats = pchan->runtime.bbone_deform_mats;
+ const float (*mat)[4] = mats[0].mat;
+ float blend, y;
+ int index;
+
+ /* Transform co to bone space and get its y component. */
+ y = mat[0][1] * co[0] + mat[1][1] * co[1] + mat[2][1] * co[2] + mat[3][1];
+
+ /* Calculate the indices of the 2 affecting b_bone segments. */
+ BKE_pchan_bbone_deform_segment_index(pchan, y / pchan->bone->length, &index, &blend);
+
+ pchan_deform_accumulate(&quats[index], mats[index + 1].mat, co, weight * (1.0f - blend), vec, dq, defmat);
+ pchan_deform_accumulate(&quats[index + 1], mats[index + 2].mat, co, weight * blend, vec, dq, defmat);
+}
+
/* using vec with dist to bone b1 - b2 */
float distfactor_to_bone(const float vec[3], const float b1[3], const float b2[3], float rad1, float rad2, float rdist)
{
@@ -996,60 +1102,25 @@ float distfactor_to_bone(const float vec[3], const float b1[3], const float b2[3
}
}
-static void pchan_deform_mat_add(bPoseChannel *pchan, float weight, float bbonemat[3][3], float mat[3][3])
-{
- float wmat[3][3];
-
- if (pchan->bone->segments > 1)
- copy_m3_m3(wmat, bbonemat);
- else
- copy_m3_m4(wmat, pchan->chan_mat);
-
- mul_m3_fl(wmat, weight);
- add_m3_m3m3(mat, mat, wmat);
-}
-
static float dist_bone_deform(bPoseChannel *pchan, const bPoseChanDeform *pdef_info, float vec[3], DualQuat *dq,
float mat[3][3], const float co[3])
{
Bone *bone = pchan->bone;
float fac, contrib = 0.0;
- float cop[3], bbonemat[3][3];
- DualQuat bbonedq;
if (bone == NULL)
return 0.0f;
- copy_v3_v3(cop, co);
-
- fac = distfactor_to_bone(cop, bone->arm_head, bone->arm_tail, bone->rad_head, bone->rad_tail, bone->dist);
+ fac = distfactor_to_bone(co, bone->arm_head, bone->arm_tail, bone->rad_head, bone->rad_tail, bone->dist);
if (fac > 0.0f) {
fac *= bone->weight;
contrib = fac;
if (contrib > 0.0f) {
- if (vec) {
- if (bone->segments > 1 && pchan->runtime.bbone_segments == bone->segments)
- /* applies on cop and bbonemat */
- b_bone_deform(pchan, cop, NULL, (mat) ? bbonemat : NULL);
- else
- mul_m4_v3(pchan->chan_mat, cop);
-
- /* Make this a delta from the base position */
- sub_v3_v3(cop, co);
- madd_v3_v3fl(vec, cop, fac);
-
- if (mat)
- pchan_deform_mat_add(pchan, fac, bbonemat, mat);
- }
- else {
- if (bone->segments > 1 && pchan->runtime.bbone_segments == bone->segments) {
- b_bone_deform(pchan, cop, &bbonedq, NULL);
- add_weighted_dq_dq(dq, &bbonedq, fac);
- }
- else
- add_weighted_dq_dq(dq, pdef_info->dual_quat, fac);
- }
+ if (bone->segments > 1 && pchan->runtime.bbone_segments == bone->segments)
+ b_bone_deform(pchan, co, fac, vec, dq, mat);
+ else
+ pchan_deform_accumulate(pdef_info->dual_quat, pchan->chan_mat, co, fac, vec, dq, mat);
}
}
@@ -1061,36 +1132,14 @@ static void pchan_bone_deform(bPoseChannel *pchan, const bPoseChanDeform *pdef_i
float mat[3][3], const float co[3], float *contrib)
{
Bone *bone = pchan->bone;
- float cop[3], bbonemat[3][3];
- DualQuat bbonedq;
if (!weight)
return;
- copy_v3_v3(cop, co);
-
- if (vec) {
- if (bone->segments > 1 && bone->segments == pchan->runtime.bbone_segments)
- /* applies on cop and bbonemat */
- b_bone_deform(pchan, cop, NULL, (mat) ? bbonemat : NULL);
- else
- mul_m4_v3(pchan->chan_mat, cop);
-
- vec[0] += (cop[0] - co[0]) * weight;
- vec[1] += (cop[1] - co[1]) * weight;
- vec[2] += (cop[2] - co[2]) * weight;
-
- if (mat)
- pchan_deform_mat_add(pchan, weight, bbonemat, mat);
- }
- else {
- if (bone->segments > 1 && bone->segments == pchan->runtime.bbone_segments) {
- b_bone_deform(pchan, cop, &bbonedq, NULL);
- add_weighted_dq_dq(dq, &bbonedq, weight);
- }
- else
- add_weighted_dq_dq(dq, pdef_info->dual_quat, weight);
- }
+ if (bone->segments > 1 && pchan->runtime.bbone_segments == bone->segments)
+ b_bone_deform(pchan, co, weight, vec, dq, mat);
+ else
+ pchan_deform_accumulate(pdef_info->dual_quat, pchan->chan_mat, co, weight, vec, dq, mat);
(*contrib) += weight;
}
@@ -1666,7 +1715,9 @@ void BKE_armature_mat_pose_to_bone_ex(struct Depsgraph *depsgraph, Object *ob, b
BKE_armature_mat_pose_to_bone(&work_pchan, inmat, outmat);
}
-/* same as BKE_object_mat3_to_rot() */
+/**
+ * Same as #BKE_object_mat3_to_rot().
+ */
void BKE_pchan_mat3_to_rot(bPoseChannel *pchan, float mat[3][3], bool use_compat)
{
BLI_ASSERT_UNIT_M3(mat);
@@ -1687,8 +1738,10 @@ void BKE_pchan_mat3_to_rot(bPoseChannel *pchan, float mat[3][3], bool use_compat
}
}
-/* Apply a 4x4 matrix to the pose bone,
- * similar to BKE_object_apply_mat4() */
+/**
+ * Apply a 4x4 matrix to the pose bone,
+ * similar to #BKE_object_apply_mat4().
+ */
void BKE_pchan_apply_mat4(bPoseChannel *pchan, float mat[4][4], bool use_compat)
{
float rot[3][3];
@@ -1696,9 +1749,11 @@ void BKE_pchan_apply_mat4(bPoseChannel *pchan, float mat[4][4], bool use_compat)
BKE_pchan_mat3_to_rot(pchan, rot, use_compat);
}
-/* Remove rest-position effects from pose-transform for obtaining
+/**
+ * Remove rest-position effects from pose-transform for obtaining
* 'visual' transformation of pose-channel.
- * (used by the Visual-Keyframing stuff) */
+ * (used by the Visual-Keyframing stuff).
+ */
void BKE_armature_mat_pose_to_delta(float delta_mat[4][4], float pose_mat[4][4], float arm_mat[4][4])
{
float imat[4][4];
diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c
index 5cd82803fae..6bf120b8f00 100644
--- a/source/blender/blenkernel/intern/armature_update.c
+++ b/source/blender/blenkernel/intern/armature_update.c
@@ -56,10 +56,10 @@ typedef struct tSplineIK_Tree {
int type; /* type of IK that this serves (CONSTRAINT_TYPE_KINEMATIC or ..._SPLINEIK) */
- bool free_points; /* free the point positions array */
short chainlen; /* number of bones in the chain */
+ float totlength; /* total length of bones in the chain */
- float *points; /* parametric positions for the joints along the curve */
+ const float *points; /* parametric positions for the joints along the curve */
bPoseChannel **chain; /* chain of bones to affect using Spline IK (ordered from the tip) */
bPoseChannel *root; /* bone that is the root node of the chain */
@@ -71,15 +71,14 @@ typedef struct tSplineIK_Tree {
/* ----------- */
/* Tag the bones in the chain formed by the given bone for IK */
-static void splineik_init_tree_from_pchan(Scene *scene, Object *UNUSED(ob), bPoseChannel *pchan_tip)
+static void splineik_init_tree_from_pchan(Scene *UNUSED(scene), Object *UNUSED(ob), bPoseChannel *pchan_tip)
{
bPoseChannel *pchan, *pchanRoot = NULL;
bPoseChannel *pchanChain[255];
bConstraint *con = NULL;
bSplineIKConstraint *ikData = NULL;
- float boneLengths[255], *jointPoints;
+ float boneLengths[255];
float totLength = 0.0f;
- bool free_joints = 0;
int segcount = 0;
/* find the SplineIK constraint */
@@ -101,31 +100,6 @@ static void splineik_init_tree_from_pchan(Scene *scene, Object *UNUSED(ob), bPos
if (con == NULL)
return;
- /* make sure that the constraint targets are ok
- * - this is a workaround for a depsgraph bug...
- */
- if (ikData->tar) {
- /* note: when creating constraints that follow path, the curve gets the CU_PATH set now,
- * currently for paths to work it needs to go through the bevlist/displist system (ton)
- */
-
- /* TODO: Make sure this doesn't crash. */
-#if 0
- /* only happens on reload file, but violates depsgraph still... fix! */
- if (ELEM(NULL, ikData->tar->curve_cache, ikData->tar->curve_cache->path, ikData->tar->curve_cache->path->data)) {
- BKE_displist_make_curveTypes(depsgraph, scene, ikData->tar, 0);
-
- /* path building may fail in EditMode after removing verts [#33268]*/
- if (ELEM(NULL, ikData->tar->curve_cache->path, ikData->tar->curve_cache->path->data)) {
- /* BLI_assert(cu->path != NULL); */
- return;
- }
- }
-#else
- (void) scene;
-#endif
- }
-
/* find the root bone and the chain of bones from the root to the tip
* NOTE: this assumes that the bones are connected, but that may not be true... */
for (pchan = pchan_tip; pchan && (segcount < ikData->chainlen); pchan = pchan->parent, segcount++) {
@@ -182,41 +156,6 @@ static void splineik_init_tree_from_pchan(Scene *scene, Object *UNUSED(ob), bPos
/* disallow negative values (happens with float precision) */
CLAMP_MIN(ikData->points[segcount], 0.0f);
- /* apply corrections for sensitivity to scaling on a copy of the bind points,
- * since it's easier to determine the positions of all the joints beforehand this way
- */
- if ((ikData->flag & CONSTRAINT_SPLINEIK_SCALE_LIMITED) && (totLength != 0.0f)) {
- float splineLen, maxScale;
- int i;
-
- /* make a copy of the points array, that we'll store in the tree
- * - although we could just multiply the points on the fly, this approach means that
- * we can introduce per-segment stretchiness later if it is necessary
- */
- jointPoints = MEM_dupallocN(ikData->points);
- free_joints = 1;
-
- /* get the current length of the curve */
- /* NOTE: this is assumed to be correct even after the curve was resized */
- splineLen = ikData->tar->runtime.curve_cache->path->totdist;
-
- /* calculate the scale factor to multiply all the path values by so that the
- * bone chain retains its current length, such that
- * maxScale * splineLen = totLength
- */
- maxScale = totLength / splineLen;
-
- /* apply scaling correction to all of the temporary points */
- /* TODO: this is really not adequate enough on really short chains */
- for (i = 0; i < segcount; i++)
- jointPoints[i] *= maxScale;
- }
- else {
- /* just use the existing points array */
- jointPoints = ikData->points;
- free_joints = 0;
- }
-
/* make a new Spline-IK chain, and store it in the IK chains */
/* TODO: we should check if there is already an IK chain on this, since that would take precedence... */
{
@@ -225,14 +164,14 @@ static void splineik_init_tree_from_pchan(Scene *scene, Object *UNUSED(ob), bPos
tree->type = CONSTRAINT_TYPE_SPLINEIK;
tree->chainlen = segcount;
+ tree->totlength = totLength;
/* copy over the array of links to bones in the chain (from tip to root) */
tree->chain = MEM_mallocN(sizeof(bPoseChannel *) * segcount, "SplineIK Chain");
memcpy(tree->chain, pchanChain, sizeof(bPoseChannel *) * segcount);
/* store reference to joint position array */
- tree->points = jointPoints;
- tree->free_points = free_joints;
+ tree->points = ikData->points;
/* store references to different parts of the chain */
tree->root = pchanRoot;
@@ -261,20 +200,69 @@ static void splineik_init_tree(Scene *scene, Object *ob, float UNUSED(ctime))
/* ----------- */
-/* Evaluate spline IK for a given bone */
-static void splineik_evaluate_bone(
- struct Depsgraph *depsgraph, tSplineIK_Tree *tree, Scene *scene, Object *ob, bPoseChannel *pchan,
- int index, float ctime)
+typedef struct tSplineIk_EvalState {
+ float curve_position; /* Current position along the curve. */
+ float curve_scale; /* Global scale to apply to curve positions. */
+ float locrot_offset[4][4]; /* Bone rotation and location offset inherited from parent. */
+} tSplineIk_EvalState;
+
+/* Prepare data to evaluate spline IK. */
+static bool splineik_evaluate_init(tSplineIK_Tree *tree, tSplineIk_EvalState *state)
{
bSplineIKConstraint *ikData = tree->ikData;
- float poseHead[3], poseTail[3], poseMat[4][4];
+
+ /* Make sure that the constraint targets are ok, to avoid crashes
+ * in case of a depsgraph bug or dependency cycle.
+ */
+ if (ikData->tar == NULL) {
+ return false;
+ }
+
+ CurveCache *cache = ikData->tar->runtime.curve_cache;
+
+ if (ELEM(NULL, cache, cache->path, cache->path->data)) {
+ return false;
+ }
+
+ /* Initialize the evaluation state. */
+ state->curve_position = 0.0f;
+ state->curve_scale = 1.0f;
+ unit_m4(state->locrot_offset);
+
+ /* Apply corrections for sensitivity to scaling. */
+ if ((ikData->flag & CONSTRAINT_SPLINEIK_SCALE_LIMITED) && (tree->totlength != 0.0f)) {
+ /* get the current length of the curve */
+ /* NOTE: this is assumed to be correct even after the curve was resized */
+ float splineLen = cache->path->totdist;
+
+ /* calculate the scale factor to multiply all the path values by so that the
+ * bone chain retains its current length, such that
+ * maxScale * splineLen = totLength
+ */
+ state->curve_scale = tree->totlength / splineLen;
+ }
+
+ return true;
+}
+
+/* Evaluate spline IK for a given bone. */
+static void splineik_evaluate_bone(tSplineIK_Tree *tree, Object *ob, bPoseChannel *pchan, int index, tSplineIk_EvalState *state)
+{
+ bSplineIKConstraint *ikData = tree->ikData;
+ float origHead[3], origTail[3], poseHead[3], poseTail[3], poseMat[4][4];
float splineVec[3], scaleFac, radius = 1.0f;
- /* firstly, calculate the bone matrix the standard way, since this is needed for roll control */
- BKE_pose_where_is_bone(depsgraph, scene, ob, pchan, ctime, 1);
+ mul_v3_m4v3(poseHead, state->locrot_offset, pchan->pose_head);
+ mul_v3_m4v3(poseTail, state->locrot_offset, pchan->pose_tail);
- copy_v3_v3(poseHead, pchan->pose_head);
- copy_v3_v3(poseTail, pchan->pose_tail);
+ copy_v3_v3(origHead, poseHead);
+
+ /* first, adjust the point positions on the curve */
+ float curveLen = tree->points[index] - tree->points[index + 1];
+ float pointStart = state->curve_position;
+ float pointEnd = pointStart + curveLen * state->curve_scale;
+
+ state->curve_position = pointEnd;
/* step 1: determine the positions for the endpoints of the bone */
{
@@ -282,18 +270,18 @@ static void splineik_evaluate_bone(
float tailBlendFac = 1.0f;
/* determine if the bone should still be affected by SplineIK */
- if (tree->points[index + 1] >= 1.0f) {
+ if (pointStart >= 1.0f) {
/* spline doesn't affect the bone anymore, so done... */
pchan->flag |= POSE_DONE;
return;
}
- else if ((tree->points[index] >= 1.0f) && (tree->points[index + 1] < 1.0f)) {
+ else if ((pointEnd >= 1.0f) && (pointStart < 1.0f)) {
/* blending factor depends on the amount of the bone still left on the chain */
- tailBlendFac = (1.0f - tree->points[index + 1]) / (tree->points[index] - tree->points[index + 1]);
+ tailBlendFac = (1.0f - pointStart) / (pointEnd - pointStart);
}
/* tail endpoint */
- if (where_on_path(ikData->tar, tree->points[index], vec, dir, NULL, &rad, NULL)) {
+ if (where_on_path(ikData->tar, pointEnd, vec, dir, NULL, &rad, NULL)) {
/* apply curve's object-mode transforms to the position
* unless the option to allow curve to be positioned elsewhere is activated (i.e. no root)
*/
@@ -309,7 +297,7 @@ static void splineik_evaluate_bone(
}
/* head endpoint */
- if (where_on_path(ikData->tar, tree->points[index + 1], vec, dir, NULL, &rad, NULL)) {
+ if (where_on_path(ikData->tar, pointStart, vec, dir, NULL, &rad, NULL)) {
/* apply curve's object-mode transforms to the position
* unless the option to allow curve to be positioned elsewhere is activated (i.e. no root)
*/
@@ -342,9 +330,7 @@ static void splineik_evaluate_bone(
/* compute the raw rotation matrix from the bone's current matrix by extracting only the
* orientation-relevant axes, and normalizing them
*/
- copy_v3_v3(rmat[0], pchan->pose_mat[0]);
- copy_v3_v3(rmat[1], pchan->pose_mat[1]);
- copy_v3_v3(rmat[2], pchan->pose_mat[2]);
+ mul_m3_m4m4(rmat, state->locrot_offset, pchan->pose_mat);
normalize_m3(rmat);
/* also, normalize the orientation imposed by the bone, now that we've extracted the scale factor */
@@ -375,6 +361,9 @@ static void splineik_evaluate_bone(
mul_m3_m3m3(tmat, dmat, rmat); /* m1, m3, m2 */
normalize_m3(tmat); /* attempt to reduce shearing, though I doubt this'll really help too much now... */
copy_m4_m3(poseMat, tmat);
+
+ /* apply rotation to the accumulated parent transform */
+ mul_m4_m3m4(state->locrot_offset, dmat, state->locrot_offset);
}
/* step 4: set the scaling factors for the axes */
@@ -484,19 +473,18 @@ static void splineik_evaluate_bone(
/* when the 'no-root' option is affected, the chain can retain
* the shape but be moved elsewhere
*/
- copy_v3_v3(poseHead, pchan->pose_head);
+ copy_v3_v3(poseHead, origHead);
}
else if (tree->con->enforce < 1.0f) {
/* when the influence is too low
* - blend the positions for the 'root' bone
* - stick to the parent for any other
*/
- if (pchan->parent) {
- copy_v3_v3(poseHead, pchan->pose_head);
+ if (index < tree->chainlen - 1) {
+ copy_v3_v3(poseHead, origHead);
}
else {
- /* FIXME: this introduces popping artifacts when we reach 0.0 */
- interp_v3_v3v3(poseHead, pchan->pose_head, poseHead, tree->con->enforce);
+ interp_v3_v3v3(poseHead, origHead, poseHead, tree->con->enforce);
}
}
copy_v3_v3(poseMat[3], poseHead);
@@ -505,9 +493,14 @@ static void splineik_evaluate_bone(
copy_m4_m4(pchan->pose_mat, poseMat);
copy_v3_v3(pchan->pose_head, poseHead);
+ mul_v3_mat3_m4v3(origTail, state->locrot_offset, pchan->pose_tail);
+
/* recalculate tail, as it's now outdated after the head gets adjusted above! */
BKE_pose_where_is_bone_tail(pchan);
+ /* update the offset in the accumulated parent transform */
+ sub_v3_v3v3(state->locrot_offset[3], pchan->pose_tail, origTail);
+
/* done! */
pchan->flag |= POSE_DONE;
}
@@ -521,20 +514,28 @@ static void splineik_execute_tree(struct Depsgraph *depsgraph, Scene *scene, Obj
while ((tree = pchan_root->siktree.first) != NULL) {
int i;
- /* walk over each bone in the chain, calculating the effects of spline IK
- * - the chain is traversed in the opposite order to storage order (i.e. parent to children)
- * so that dependencies are correct
- */
+ /* Firstly, calculate the bone matrix the standard way, since this is needed for roll control. */
for (i = tree->chainlen - 1; i >= 0; i--) {
- bPoseChannel *pchan = tree->chain[i];
- splineik_evaluate_bone(depsgraph, tree, scene, ob, pchan, i, ctime);
+ BKE_pose_where_is_bone(depsgraph, scene, ob, tree->chain[i], ctime, 1);
+ }
+
+ /* After that, evaluate the actual Spline IK, unless there are missing dependencies. */
+ tSplineIk_EvalState state;
+
+ if (splineik_evaluate_init(tree, &state)) {
+ /* Walk over each bone in the chain, calculating the effects of spline IK
+ * - the chain is traversed in the opposite order to storage order (i.e. parent to children)
+ * so that dependencies are correct
+ */
+ for (i = tree->chainlen - 1; i >= 0; i--) {
+ bPoseChannel *pchan = tree->chain[i];
+ splineik_evaluate_bone(tree, ob, pchan, i, &state);
+ }
}
/* free the tree info specific to SplineIK trees now */
if (tree->chain)
MEM_freeN(tree->chain);
- if (tree->free_points)
- MEM_freeN(tree->points);
/* free this tree */
BLI_freelinkN(&pchan_root->siktree, tree);
@@ -555,8 +556,9 @@ void BKE_splineik_execute_tree(
/* *************** Depsgraph evaluation callbacks ************ */
-static void pose_pchan_index_create(bPose *pose)
+void BKE_pose_pchan_index_rebuild(bPose *pose)
{
+ MEM_SAFE_FREE(pose->chan_array);
const int num_channels = BLI_listbase_count(&pose->chanbase);
pose->chan_array = MEM_malloc_arrayN(
num_channels, sizeof(bPoseChannel *), "pose->chan_array");
@@ -604,7 +606,8 @@ void BKE_pose_eval_init(struct Depsgraph *depsgraph,
}
}
- pose_pchan_index_create(pose);
+ BLI_assert(pose->chan_array != NULL || BLI_listbase_is_empty(&pose->chanbase));
+
BKE_armature_cached_bbone_deformation_free_data(object);
}
@@ -805,7 +808,6 @@ static void pose_eval_cleanup_common(Object *object)
bPose *pose = object->pose;
BLI_assert(pose != NULL);
BLI_assert(pose->chan_array != NULL || BLI_listbase_is_empty(&pose->chanbase));
- MEM_SAFE_FREE(pose->chan_array);
}
void BKE_pose_eval_done(struct Depsgraph *depsgraph, Object *object)
@@ -838,7 +840,8 @@ void BKE_pose_eval_proxy_init(struct Depsgraph *depsgraph, Object *object)
BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
- pose_pchan_index_create(object->pose);
+ BLI_assert(object->pose->chan_array != NULL || BLI_listbase_is_empty(&object->pose->chanbase));
+
BKE_armature_cached_bbone_deformation_free_data(object);
}
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index 336a4739760..eadf190c363 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -804,7 +804,7 @@ static void collection_missing_parents_remove(Collection *collection)
* This is used for library remapping, where these pointers have been set to NULL.
* Otherwise this should never happen.
*
- * \note caller must ensure BKE_main_collection_sync_remap() is called afterwards!
+ * \note caller must ensure #BKE_main_collection_sync_remap() is called afterwards!
*
* \param collection: may be \a NULL, in which case whole \a bmain database of collections is checked.
*/
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 6d7765d4314..59a374d5e88 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -319,8 +319,7 @@ void BKE_constraint_mat_convertspace(
{
/* local + parent to pose */
if (pchan->bone) {
- copy_m4_m4(diff_mat, pchan->bone->arm_mat);
- mul_m4_m4m4(mat, mat, diff_mat);
+ mul_m4_m4m4(mat, pchan->bone->arm_mat, mat);
}
/* use pose-space as stepping stone for other spaces */
@@ -592,58 +591,25 @@ static void constraint_target_to_mat4(Object *ob, const char *substring, float m
Mat4 *bbone = pchan->runtime.bbone_pose_mats;
float tempmat[4][4];
float loc[3], fac;
+ int index;
/* figure out which segment(s) the headtail value falls in */
- fac = (float)pchan->bone->segments * headtail;
-
- if (fac >= pchan->bone->segments - 1) {
- /* special case: end segment doesn't get created properly... */
- float pt[3], sfac;
- int index;
-
- /* bbone points are in bonespace, so need to move to posespace first */
- index = pchan->bone->segments - 1;
- mul_v3_m4v3(pt, pchan->pose_mat, bbone[index].mat[3]);
-
- /* interpolate between last segment point and the endpoint */
- sfac = fac - (float)(pchan->bone->segments - 1); /* fac is just the "leftover" between penultimate and last points */
- interp_v3_v3v3(loc, pt, pchan->pose_tail, sfac);
- }
- else {
- /* get indices for finding interpolating between points along the bbone */
- float pt_a[3], pt_b[3], pt[3];
- int index_a, index_b;
-
- index_a = floorf(fac);
- CLAMP(index_a, 0, MAX_BBONE_SUBDIV - 1);
-
- index_b = ceilf(fac);
- CLAMP(index_b, 0, MAX_BBONE_SUBDIV - 1);
-
- /* interpolate between these points */
- copy_v3_v3(pt_a, bbone[index_a].mat[3]);
- copy_v3_v3(pt_b, bbone[index_b].mat[3]);
-
- interp_v3_v3v3(pt, pt_a, pt_b, fac - floorf(fac));
-
- /* move the point from bone local space to pose space... */
- mul_v3_m4v3(loc, pchan->pose_mat, pt);
- }
+ BKE_pchan_bbone_deform_segment_index(pchan, headtail, &index, &fac);
/* apply full transformation of the segment if requested */
if (full_bbone) {
- int index = floorf(fac);
- CLAMP(index, 0, pchan->bone->segments - 1);
+ interp_m4_m4m4(tempmat, bbone[index].mat, bbone[index + 1].mat, fac);
- mul_m4_m4m4(tempmat, pchan->pose_mat, bbone[index].mat);
+ mul_m4_m4m4(tempmat, pchan->pose_mat, tempmat);
}
+ /* only interpolate location */
else {
+ interp_v3_v3v3(loc, bbone[index].mat[3], bbone[index + 1].mat[3], fac);
+
copy_m4_m4(tempmat, pchan->pose_mat);
+ mul_v3_m4v3(tempmat[3], pchan->pose_mat, loc);
}
- /* use interpolated distance for subtarget */
- copy_v3_v3(tempmat[3], loc);
-
mul_m4_m4m4(mat, ob->obmat, tempmat);
}
else {
@@ -2155,10 +2121,31 @@ static void armdef_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
}
}
+static void armdef_accumulate_matrix(float obmat[4][4], float iobmat[4][4], float basemat[4][4], float bonemat[4][4], float weight, float r_sum_mat[4][4], DualQuat *r_sum_dq)
+{
+ if (weight == 0.0f)
+ return;
+
+ /* Convert the selected matrix into object space. */
+ float mat[4][4];
+ mul_m4_series(mat, obmat, bonemat, iobmat);
+
+ /* Accumulate the transformation. */
+ if (r_sum_dq != NULL) {
+ DualQuat tmpdq;
+
+ mat4_to_dquat(&tmpdq, basemat, mat);
+ add_weighted_dq_dq(r_sum_dq, &tmpdq, weight);
+ }
+ else {
+ madd_m4_m4m4fl(r_sum_mat, r_sum_mat, mat, weight);
+ }
+}
+
/* Compute and accumulate transformation for a single target bone. */
static void armdef_accumulate_bone(bConstraintTarget *ct, bPoseChannel *pchan, const float wco[3], bool force_envelope, float *r_totweight, float r_sum_mat[4][4], DualQuat *r_sum_dq)
{
- float mat[4][4], iobmat[4][4], basemat[4][4], co[3];
+ float iobmat[4][4], basemat[4][4], co[3];
Bone *bone = pchan->bone;
float weight = ct->weight;
@@ -2172,6 +2159,11 @@ static void armdef_accumulate_bone(bConstraintTarget *ct, bPoseChannel *pchan, c
bone->rad_head, bone->rad_tail, bone->dist);
}
+ /* Compute the quaternion base matrix. */
+ if (r_sum_dq != NULL) {
+ mul_m4_series(basemat, ct->tar->obmat, bone->arm_mat, iobmat);
+ }
+
/* Find the correct bone transform matrix in world space. */
if (bone->segments > 1 && bone->segments == pchan->runtime.bbone_segments) {
Mat4 *b_bone_mats = pchan->runtime.bbone_deform_mats;
@@ -2182,34 +2174,21 @@ static void armdef_accumulate_bone(bConstraintTarget *ct, bPoseChannel *pchan, c
* Need to transform co back to bonespace, only need y. */
float y = iamat[0][1] * co[0] + iamat[1][1] * co[1] + iamat[2][1] * co[2] + iamat[3][1];
- float segment = bone->length / ((float)bone->segments);
- int a = (int)(y / segment);
-
- CLAMP(a, 0, bone->segments - 1);
+ /* Blend the matrix. */
+ int index;
+ float blend;
+ BKE_pchan_bbone_deform_segment_index(pchan, y / bone->length, &index, &blend);
- /* Convert the selected matrix into object space. */
- mul_m4_series(mat, ct->tar->obmat, b_bone_mats[a + 1].mat, iobmat);
+ armdef_accumulate_matrix(ct->tar->obmat, iobmat, basemat, b_bone_mats[index + 1].mat, weight * (1.0f - blend), r_sum_mat, r_sum_dq);
+ armdef_accumulate_matrix(ct->tar->obmat, iobmat, basemat, b_bone_mats[index + 2].mat, weight * blend, r_sum_mat, r_sum_dq);
}
else {
/* Simple bone. This requires DEG_OPCODE_BONE_DONE dependency due to chan_mat. */
- mul_m4_series(mat, ct->tar->obmat, pchan->chan_mat, iobmat);
+ armdef_accumulate_matrix(ct->tar->obmat, iobmat, basemat, pchan->chan_mat, weight, r_sum_mat, r_sum_dq);
}
- /* Accumulate the transformation. */
+ /* Accumulate the weight. */
*r_totweight += weight;
-
- if (r_sum_dq != NULL) {
- DualQuat tmpdq;
-
- mul_m4_series(basemat, ct->tar->obmat, bone->arm_mat, iobmat);
-
- mat4_to_dquat(&tmpdq, basemat, mat);
- add_weighted_dq_dq(r_sum_dq, &tmpdq, weight);
- }
- else {
- mul_m4_fl(mat, weight);
- add_m4_m4m4(r_sum_mat, r_sum_mat, mat);
- }
}
static void armdef_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targets)
@@ -4729,15 +4708,18 @@ const bConstraintTypeInfo *BKE_constraint_typeinfo_get(bConstraint *con)
/* ---------- Data Management ------- */
-/* helper function for BKE_constraint_free_data() - unlinks references */
+/**
+ * Helper function for #BKE_constraint_free_data() - unlinks references.
+ */
static void con_unlink_refs_cb(bConstraint *UNUSED(con), ID **idpoin, bool is_reference, void *UNUSED(userData))
{
if (*idpoin && is_reference)
id_us_min(*idpoin);
}
-/* Free data of a specific constraint if it has any info.
- * be sure to run BIK_clear_data() when freeing an IK constraint,
+/**
+ * Free data of a specific constraint if it has any info.
+ * be sure to run #BIK_clear_data() when freeing an IK constraint,
* unless DAG_relations_tag_update is called.
*/
void BKE_constraint_free_data_ex(bConstraint *con, bool do_id_user)
diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.c
index 5dd5e014cc8..4aa3b702693 100644
--- a/source/blender/blenkernel/intern/crazyspace.c
+++ b/source/blender/blenkernel/intern/crazyspace.c
@@ -305,12 +305,13 @@ int BKE_crazyspace_get_first_deform_matrices_editbmesh(
return numleft;
}
-/* Crazyspace evaluation needs to have an object which has all the fields
+/**
+ * Crazyspace evaluation needs to have an object which has all the fields
* evaluated, but the mesh data being at undeformed state. This way it can
* re-apply modifiers and also have proper pointers to key data blocks.
*
- * Similar to BKE_object_eval_reset(), but does not modify the actual evaluated
- * object. */
+ * Similar to #BKE_object_eval_reset(), but does not modify the actual evaluated object.
+ */
static void crazyspace_init_object_for_eval(
struct Depsgraph *depsgraph,
Object *object,
diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c
index 9b184713f0e..c779f05c0b0 100644
--- a/source/blender/blenkernel/intern/font.c
+++ b/source/blender/blenkernel/intern/font.c
@@ -431,17 +431,6 @@ static void buildchar(Curve *cu, ListBase *nubase, unsigned int character, CharI
vfd = vfont_get_data(which_vfont(cu, info));
if (!vfd) return;
-#if 0
- if (cu->selend < cu->selstart) {
- if ((charidx >= (cu->selend)) && (charidx <= (cu->selstart - 2)))
- sel = 1;
- }
- else {
- if ((charidx >= (cu->selstart - 1)) && (charidx <= (cu->selend - 1)))
- sel = 1;
- }
-#endif
-
/* make a copy at distance ofsx, ofsy with shear */
shear = cu->shear;
si = sinf(rot);
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 2ef8568e308..c204a8128c1 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -985,7 +985,7 @@ void BKE_gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl)
BLI_freelinkN(&gpd->layers, gpl);
}
-Material *BKE_gpencil_get_material_from_brush(Brush *brush)
+Material *BKE_gpencil_brush_material_get(Brush *brush)
{
Material *ma = NULL;
@@ -998,7 +998,7 @@ Material *BKE_gpencil_get_material_from_brush(Brush *brush)
return ma;
}
-void BKE_gpencil_brush_set_material(Brush *brush, Material *ma)
+void BKE_gpencil_brush_material_set(Brush *brush, Material *ma)
{
BLI_assert(brush);
BLI_assert(brush->gpencil_settings);
@@ -1014,13 +1014,13 @@ void BKE_gpencil_brush_set_material(Brush *brush, Material *ma)
}
/* Adds the pinned material to the object if necessary. */
-Material *BKE_gpencil_handle_brush_material(Main *bmain, Object *ob, Brush *brush)
+Material *BKE_gpencil_object_material_ensure_from_brush(Main *bmain, Object *ob, Brush *brush)
{
if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) {
- Material *ma = BKE_gpencil_get_material_from_brush(brush);
+ Material *ma = BKE_gpencil_brush_material_get(brush);
/* check if the material is already on object material slots and add it if missing */
- if (ma && BKE_gpencil_get_material_index(ob, ma) < 0) {
+ if (ma && BKE_gpencil_object_material_get_index(ob, ma) < 0) {
BKE_object_material_slot_add(bmain, ob);
assign_material(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF);
}
@@ -1034,12 +1034,12 @@ Material *BKE_gpencil_handle_brush_material(Main *bmain, Object *ob, Brush *brus
}
/* Assigns the material to object (if not already present) and returns its index (mat_nr). */
-int BKE_gpencil_handle_material(Main *bmain, Object *ob, Material *material)
+int BKE_gpencil_object_material_ensure(Main *bmain, Object *ob, Material *material)
{
if (!material) {
return -1;
}
- int index = BKE_gpencil_get_material_index(ob, material);
+ int index = BKE_gpencil_object_material_get_index(ob, material);
if (index < 0) {
BKE_object_material_slot_add(bmain, ob);
assign_material(bmain, ob, material, ob->totcol, BKE_MAT_ASSIGN_USERPREF);
@@ -1052,7 +1052,7 @@ int BKE_gpencil_handle_material(Main *bmain, Object *ob, Material *material)
*
* \param *r_index: value is set to zero based index of the new material if r_index is not NULL
*/
-Material *BKE_gpencil_handle_new_material(Main *bmain, Object *ob, const char *name, int *r_index)
+Material *BKE_gpencil_object_material_new(Main *bmain, Object *ob, const char *name, int *r_index)
{
Material *ma = BKE_material_add_gpencil(bmain, name);
id_us_min(&ma->id); /* no users yet */
@@ -1067,10 +1067,10 @@ Material *BKE_gpencil_handle_new_material(Main *bmain, Object *ob, const char *n
}
/* Returns the material for a brush with respect to its pinned state. */
-Material *BKE_gpencil_get_material_for_brush(Object *ob, Brush *brush)
+Material *BKE_gpencil_object_material_get_from_brush(Object *ob, Brush *brush)
{
- if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) {
- Material *ma = BKE_gpencil_get_material_from_brush(brush);
+ if ((brush) && (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED)) {
+ Material *ma = BKE_gpencil_brush_material_get(brush);
return ma;
}
else {
@@ -1079,10 +1079,10 @@ Material *BKE_gpencil_get_material_for_brush(Object *ob, Brush *brush)
}
/* Returns the material index for a brush with respect to its pinned state. */
-int BKE_gpencil_get_material_index_for_brush(Object *ob, Brush *brush)
+int BKE_gpencil_object_material_get_index_from_brush(Object *ob, Brush *brush)
{
- if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) {
- return BKE_gpencil_get_material_index(ob, brush->gpencil_settings->material);
+ if ((brush) && (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED)) {
+ return BKE_gpencil_object_material_get_index(ob, brush->gpencil_settings->material);
}
else {
return ob->actcol - 1;
@@ -1090,21 +1090,21 @@ int BKE_gpencil_get_material_index_for_brush(Object *ob, Brush *brush)
}
/* Guaranteed to return a material assigned to object. Returns never NULL. */
-Material *BKE_gpencil_current_input_toolsettings_material(Main *bmain, Object *ob, ToolSettings *ts)
+Material *BKE_gpencil_object_material_ensure_from_active_input_toolsettings(Main *bmain, Object *ob, ToolSettings *ts)
{
if (ts && ts->gp_paint && ts->gp_paint->paint.brush) {
- return BKE_gpencil_current_input_brush_material(bmain, ob, ts->gp_paint->paint.brush);
+ return BKE_gpencil_object_material_ensure_from_active_input_brush(bmain, ob, ts->gp_paint->paint.brush);
}
else {
- return BKE_gpencil_current_input_brush_material(bmain, ob, NULL);
+ return BKE_gpencil_object_material_ensure_from_active_input_brush(bmain, ob, NULL);
}
}
/* Guaranteed to return a material assigned to object. Returns never NULL. */
-Material *BKE_gpencil_current_input_brush_material(Main *bmain, Object *ob, Brush *brush)
+Material *BKE_gpencil_object_material_ensure_from_active_input_brush(Main *bmain, Object *ob, Brush *brush)
{
if (brush) {
- Material *ma = BKE_gpencil_handle_brush_material(bmain, ob, brush);
+ Material *ma = BKE_gpencil_object_material_ensure_from_brush(bmain, ob, brush);
if (ma) {
return ma;
}
@@ -1113,21 +1113,21 @@ Material *BKE_gpencil_current_input_brush_material(Main *bmain, Object *ob, Brus
brush->gpencil_settings->flag &= ~GP_BRUSH_MATERIAL_PINNED;
}
}
- return BKE_gpencil_current_input_material(bmain, ob);
+ return BKE_gpencil_object_material_ensure_from_active_input_material(bmain, ob);
}
/* Guaranteed to return a material assigned to object. Returns never NULL. Only use this for materials unrelated to user input */
-Material *BKE_gpencil_current_input_material(Main *bmain, Object *ob)
+Material *BKE_gpencil_object_material_ensure_from_active_input_material(Main *bmain, Object *ob)
{
Material *ma = give_current_material(ob, ob->actcol);
if (ma) {
return ma;
}
- return BKE_gpencil_handle_new_material(bmain, ob, "Material", NULL);
+ return BKE_gpencil_object_material_new(bmain, ob, "Material", NULL);
}
/* Get active color, and add all default settings if we don't find anything */
-Material *BKE_gpencil_material_ensure(Main *bmain, Object *ob)
+Material *BKE_gpencil_object_material_ensure_active(Main *bmain, Object *ob)
{
Material *ma = NULL;
@@ -1135,7 +1135,7 @@ Material *BKE_gpencil_material_ensure(Main *bmain, Object *ob)
if (ELEM(NULL, bmain, ob))
return NULL;
- ma = BKE_gpencil_current_input_material(bmain, ob);
+ ma = BKE_gpencil_object_material_ensure_from_active_input_material(bmain, ob);
if (ma->gp_style == NULL) {
BKE_material_init_gpencil_settings(ma);
}
@@ -1662,7 +1662,7 @@ void BKE_gpencil_stats_update(bGPdata *gpd)
}
/* get material index (0-based like mat_nr not actcol) */
-int BKE_gpencil_get_material_index(Object *ob, Material *ma)
+int BKE_gpencil_object_material_get_index(Object *ob, Material *ma)
{
short *totcol = give_totcolp(ob);
Material *read_ma = NULL;
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index b00e4cbcdd9..0205f275f59 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -4770,8 +4770,9 @@ bool BKE_image_has_loaded_ibuf(Image *image)
return has_loaded_ibuf;
}
-/* References the result, BKE_image_release_ibuf is to be called to de-reference.
- * Use lock=NULL when calling BKE_image_release_ibuf().
+/**
+ * References the result, #BKE_image_release_ibuf is to be called to de-reference.
+ * Use lock=NULL when calling #BKE_image_release_ibuf().
*/
ImBuf *BKE_image_get_ibuf_with_name(Image *image, const char *name)
{
@@ -4797,8 +4798,9 @@ ImBuf *BKE_image_get_ibuf_with_name(Image *image, const char *name)
return ibuf;
}
-/* References the result, BKE_image_release_ibuf is to be called to de-reference.
- * Use lock=NULL when calling BKE_image_release_ibuf().
+/**
+ * References the result, #BKE_image_release_ibuf is to be called to de-reference.
+ * Use lock=NULL when calling #BKE_image_release_ibuf().
*
* TODO(sergey): This is actually "get first entry from the cache", which is
* not so much predictable. But using first loaded image buffer
diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c
index f85964f76bc..de1c3976b7b 100644
--- a/source/blender/blenkernel/intern/key.c
+++ b/source/blender/blenkernel/intern/key.c
@@ -1497,7 +1497,9 @@ KeyBlock *BKE_keyblock_add_ctime(Key *key, const char *name, const bool do_force
if (!do_force && (key->type != KEY_RELATIVE)) {
KeyBlock *it_kb;
for (it_kb = key->block.first; it_kb; it_kb = it_kb->next) {
- if (it_kb->pos == cpos) {
+ /* Use epsilon to avoid floating point precision issues.
+ * 1e-3 because the position is stored as frame * 1e-2. */
+ if (compare_ff(it_kb->pos, cpos, 1e-3f)) {
return kb;
}
}
diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c
index 453cbe16de0..b7ae8eea18d 100644
--- a/source/blender/blenkernel/intern/library_query.c
+++ b/source/blender/blenkernel/intern/library_query.c
@@ -581,7 +581,7 @@ static void library_foreach_ID_link(
}
data.cb_flag = data_cb_flag;
- CALLBACK_INVOKE(object->gpd, IDWALK_CB_USER);
+ /* Note that ob->gpd is deprecated, so no need to handle it here. */
CALLBACK_INVOKE(object->instance_collection, IDWALK_CB_USER);
if (object->pd) {
@@ -783,7 +783,7 @@ static void library_foreach_ID_link(
CALLBACK_INVOKE(child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER);
}
for (CollectionParent *parent = collection->parents.first; parent; parent = parent->next) {
- CALLBACK_INVOKE(parent->collection, IDWALK_CB_NEVER_SELF);
+ CALLBACK_INVOKE(parent->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_LOOPBACK);
}
break;
}
@@ -1294,7 +1294,7 @@ bool BKE_library_ID_is_indirectly_used(Main *bmain, void *idv)
}
/**
- * Combine \a BKE_library_ID_is_locally_used() and \a BKE_library_ID_is_indirectly_used() in a single call.
+ * Combine #BKE_library_ID_is_locally_used() and #BKE_library_ID_is_indirectly_used() in a single call.
*/
void BKE_library_ID_test_usages(Main *bmain, void *idv, bool *is_used_local, bool *is_used_linked)
{
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index 449c2f86b04..74fb31d0121 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -634,6 +634,7 @@ static Mesh *mesh_new_nomain_from_template_ex(
me_dst->totpoly = polys_len;
me_dst->cd_flag = me_src->cd_flag;
+ me_dst->editflag = me_src->editflag;
CustomData_copy(&me_src->vdata, &me_dst->vdata, mask.vmask, CD_CALLOC, verts_len);
CustomData_copy(&me_src->edata, &me_dst->edata, mask.emask, CD_CALLOC, edges_len);
diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c
index 664698afa38..5be4b8524b1 100644
--- a/source/blender/blenkernel/intern/mesh_convert.c
+++ b/source/blender/blenkernel/intern/mesh_convert.c
@@ -869,26 +869,31 @@ Mesh *BKE_mesh_new_from_object(
Object *object_input = ob;
Object *object_eval = DEG_get_evaluated_object(depsgraph, object_input);
- Object object_for_eval = *object_eval;
+ Object object_for_eval;
if (object_eval == object_input) {
/* Evaluated mesh contains all modifiers applied already.
* The other types of object has them applied, but are stored in other
* data structures than a mesh. So need to apply modifiers again on a
* temporary copy before converting result to mesh. */
- if (object_for_eval.type == OB_MESH) {
+ if (object_input->type == OB_MESH) {
effective_apply_modifiers = false;
}
else {
effective_apply_modifiers = true;
}
+ object_for_eval = *object_eval;
}
else {
if (apply_modifiers) {
+ object_for_eval = *object_eval;
if (object_for_eval.runtime.mesh_orig != NULL) {
object_for_eval.data = object_for_eval.runtime.mesh_orig;
}
}
+ else {
+ object_for_eval = *object_input;
+ }
}
const bool cage = !effective_apply_modifiers;
diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.c
index a5aa198bc4e..295b65190a8 100644
--- a/source/blender/blenkernel/intern/mesh_validate.c
+++ b/source/blender/blenkernel/intern/mesh_validate.c
@@ -1063,10 +1063,13 @@ void BKE_mesh_strip_loose_faces(Mesh *me)
}
}
-/* Works on both loops and polys! */
-/* Note: It won't try to guess which loops of an invalid poly to remove!
- * this is the work of the caller, to mark those loops...
- * See e.g. BKE_mesh_validate_arrays(). */
+/**
+ * Works on both loops and polys!
+ *
+ * \note It won't try to guess which loops of an invalid poly to remove!
+ * this is the work of the caller, to mark those loops...
+ * See e.g. #BKE_mesh_validate_arrays().
+ */
void BKE_mesh_strip_loose_polysloops(Mesh *me)
{
MPoly *p;
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 8f52e579a91..7600c74b777 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -895,7 +895,7 @@ ModifierData *modifier_get_original(ModifierData *md)
}
struct ModifierData *modifier_get_evaluated(
- Depsgraph* depsgraph,
+ Depsgraph *depsgraph,
Object *object,
ModifierData *md)
{
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 9985c8026e7..6e9c8ecb480 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -1429,11 +1429,11 @@ Object *BKE_object_copy(Main *bmain, const Object *ob)
/** Perform deep-copy of object and its 'children' data-blocks (obdata, materials, actions, etc.).
*
- * \param dupflag Controls which sub-data are also duplicated (see \a eDupli_ID_Flags in \a DNA_userdef_types.h).
+ * \param dupflag Controls which sub-data are also duplicated (see #eDupli_ID_Flags in DNA_userdef_types.h).
*
- * \note This function does not do any remapping to new IDs, caller must do it (\a BKE_libblock_relink_to_newid()).
- * \note Caller MUST free \a newid pointers itself (\a BKE_main_id_clear_newpoins()) and call updates of DEG too
- * (\a DAG_relations_tag_update()).
+ * \note This function does not do any remapping to new IDs, caller must do it (\a #BKE_libblock_relink_to_newid()).
+ * \note Caller MUST free \a newid pointers itself (#BKE_main_id_clear_newpoins()) and call updates of DEG too
+ * (#DAG_relations_tag_update()).
*/
Object *BKE_object_duplicate(Main *bmain, const Object *ob, const int dupflag)
{
@@ -3147,10 +3147,12 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph,
object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update);
}
-/* WARNING: "scene" here may not be the scene object actually resides in.
+/**
+ * \warning "scene" here may not be the scene object actually resides in.
* When dealing with background-sets, "scene" is actually the active scene.
* e.g. "scene" <-- set 1 <-- set 2 ("ob" lives here) <-- set 3 <-- ... <-- set n
- * rigid bodies depend on their world so use BKE_object_handle_update_ex() to also pass along the current rigid body world
+ * rigid bodies depend on their world so use #BKE_object_handle_update_ex()
+ * to also pass along the current rigid body world.
*/
void BKE_object_handle_update(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index c03fffc4098..bcca39cff24 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -66,7 +66,7 @@
*
* Some changes done directly in evaluated object require them to be reset
* before being re-evaluated.
- * For example, we need to call this before BKE_mesh_new_from_object(),
+ * For example, we need to call this before #BKE_mesh_new_from_object(),
* in case we removed/added modifiers in the evaluated object.
*/
void BKE_object_eval_reset(Object *ob_eval)
@@ -244,8 +244,10 @@ void BKE_object_handle_data_update(
BKE_object_eval_boundbox(depsgraph, ob);
}
-/* TODO(sergey): Ensure that bounding box is already calculated, and move this
- * into BKE_object_synchronize_to_original(). */
+/**
+ * TODO(sergey): Ensure that bounding box is already calculated, and move this
+ * into #BKE_object_synchronize_to_original().
+ */
void BKE_object_eval_boundbox(Depsgraph *depsgraph, Object *object)
{
if (!DEG_is_active(depsgraph)) {
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index 7dc772576c5..09fc7c64d6a 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -516,7 +516,7 @@ static void pbvh_build(PBVH *bvh, BB *cb, BBC *prim_bbc, int totprim)
* Do a full rebuild with on Mesh data structure.
*
* \note Unlike mpoly/mloop/verts, looptri is **totally owned** by PBVH (which means it may rewrite it if needed,
- * see BKE_pbvh_apply_vertCos().
+ * see #BKE_pbvh_apply_vertCos().
*/
void BKE_pbvh_build_mesh(
PBVH *bvh, const MPoly *mpoly, const MLoop *mloop, MVert *verts,
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 644eb704655..361459c7c18 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -651,7 +651,6 @@ void BKE_scene_init(Scene *sce)
sce->toolsettings->uvcalc_flag = UVCALC_TRANSFORM_CORRECT;
sce->toolsettings->unwrapper = 1;
sce->toolsettings->select_thresh = 0.01f;
- sce->toolsettings->gizmo_flag = SCE_GIZMO_SHOW_TRANSLATE | SCE_GIZMO_SHOW_ROTATE | SCE_GIZMO_SHOW_SCALE;
sce->toolsettings->selectmode = SCE_SELECT_VERTEX;
sce->toolsettings->uv_selectmode = UV_SELECT_VERTEX;
@@ -1347,24 +1346,28 @@ void BKE_scene_frame_set(struct Scene *scene, double cfra)
/** \name Scene Orientation Slots
* \{ */
-TransformOrientationSlot *BKE_scene_orientation_slot_get(Scene *scene, int flag)
+TransformOrientationSlot *BKE_scene_orientation_slot_get(Scene *scene, int slot_index)
{
- BLI_assert(flag && !(flag & ~(SCE_GIZMO_SHOW_TRANSLATE | SCE_GIZMO_SHOW_ROTATE | SCE_GIZMO_SHOW_SCALE)));
- int index = SCE_ORIENT_DEFAULT;
- if (flag & SCE_GIZMO_SHOW_TRANSLATE) {
- index = SCE_ORIENT_TRANSLATE;
+ if ((scene->orientation_slots[slot_index].flag & SELECT) == 0) {
+ slot_index = SCE_ORIENT_DEFAULT;
}
- else if (flag & SCE_GIZMO_SHOW_ROTATE) {
- index = SCE_ORIENT_ROTATE;
+ return &scene->orientation_slots[slot_index];
+}
+
+TransformOrientationSlot *BKE_scene_orientation_slot_get_from_flag(Scene *scene, int flag)
+{
+ BLI_assert(flag && !(flag & ~(V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE | V3D_GIZMO_SHOW_OBJECT_SCALE)));
+ int slot_index = SCE_ORIENT_DEFAULT;
+ if (flag & V3D_GIZMO_SHOW_OBJECT_TRANSLATE) {
+ slot_index = SCE_ORIENT_TRANSLATE;
}
- else if (flag & SCE_GIZMO_SHOW_SCALE) {
- index = SCE_ORIENT_SCALE;
+ else if (flag & V3D_GIZMO_SHOW_OBJECT_ROTATE) {
+ slot_index = SCE_ORIENT_ROTATE;
}
-
- if ((scene->orientation_slots[index].flag & SELECT) == 0) {
- index = SCE_ORIENT_DEFAULT;
+ else if (flag & V3D_GIZMO_SHOW_OBJECT_SCALE) {
+ slot_index = SCE_ORIENT_SCALE;
}
- return &scene->orientation_slots[index];
+ return BKE_scene_orientation_slot_get(scene, slot_index);
}
/**
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index 0d8b2381572..2e0d4719339 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -725,8 +725,9 @@ static void add_bp_springlist(BodyPoint *bp, int springID)
}
}
-/* do this once when sb is build
- * it is O(N^2) so scanning for springs every iteration is too expensive
+/**
+ * Do this once when sb is build it is `O(N^2)`
+ * so scanning for springs every iteration is too expensive.
*/
static void build_bps_springlist(Object *ob)
{
diff --git a/source/blender/blenkernel/intern/tracking_stabilize.c b/source/blender/blenkernel/intern/tracking_stabilize.c
index 5e7f2421428..6a0dbefd340 100644
--- a/source/blender/blenkernel/intern/tracking_stabilize.c
+++ b/source/blender/blenkernel/intern/tracking_stabilize.c
@@ -266,12 +266,13 @@ static StabContext *initialize_stabilization_working_context(MovieClip *clip)
return ctx;
}
-/* Discard all private working data attached to this call context.
- * NOTE: We allocate the record for the per track baseline contribution
- * locally for each call context (i.e. call to
- * BKE_tracking_stabilization_data_get()
- * Thus it is correct to discard all allocations found within the
- * corresponding _local_ GHash
+/**
+ * Discard all private working data attached to this call context.
+ *
+ * \note We allocate the record for the per track baseline contribution
+ * locally for each call context (i.e. call to #BKE_tracking_stabilization_data_get)
+ * Thus it is correct to discard all allocations found within the
+ * corresponding _local_ GHash.
*/
static void discard_stabilization_working_context(StabContext *ctx)
{
diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h
index 8e35b197e14..0b65597665d 100644
--- a/source/blender/blenlib/BLI_math_matrix.h
+++ b/source/blender/blenlib/BLI_math_matrix.h
@@ -59,6 +59,9 @@ void swap_m4m4(float A[4][4], float B[4][4]);
void add_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3]);
void add_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4]);
+void madd_m3_m3m3fl(float R[3][3], const float A[3][3], const float B[3][3], const float f);
+void madd_m4_m4m4fl(float R[4][4], const float A[4][4], const float B[4][4], const float f);
+
void sub_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3]);
void sub_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4]);
@@ -66,7 +69,9 @@ void mul_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3]);
void mul_m4_m3m4(float R[4][4], const float A[3][3], const float B[4][4]);
void mul_m4_m4m3(float R[4][4], const float A[4][4], const float B[3][3]);
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4]);
-void mul_m3_m3m4(float R[3][3], const float A[4][4], const float B[3][3]);
+void mul_m3_m3m4(float R[3][3], const float A[3][3], const float B[4][4]);
+void mul_m3_m4m3(float R[3][3], const float A[4][4], const float B[3][3]);
+void mul_m3_m4m4(float R[3][3], const float A[4][4], const float B[4][4]);
/* special matrix multiplies
* uniq: R <-- AB, R is neither A nor B
diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h
index 831626b164b..09844d75c93 100644
--- a/source/blender/blenlib/BLI_string.h
+++ b/source/blender/blenlib/BLI_string.h
@@ -32,6 +32,7 @@ extern "C" {
#endif
#include "BLI_compiler_attrs.h"
+#include "BLI_utildefines_variadic.h"
char *BLI_strdupn(const char *str, const size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
@@ -117,6 +118,53 @@ int BLI_string_find_split_words(
len += BLI_snprintf_rlen(dst + len, ARRAY_SIZE(dst) - len, format, __VA_ARGS__)
/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Equal to Any Element (STR_ELEM) Macro
+ *
+ * Follows #ELEM macro convention.
+ * \{ */
+
+/* STR_ELEM#(v, ...): is the first arg equal any others? */
+/* Internal helpers. */
+#define _VA_STR_ELEM2(v, a) \
+ (strcmp(v, a) == 0)
+#define _VA_STR_ELEM3(v, a, b) \
+ (_VA_STR_ELEM2(v, a) || ((v) == (b)))
+#define _VA_STR_ELEM4(v, a, b, c) \
+ (_VA_STR_ELEM3(v, a, b) || ((v) == (c)))
+#define _VA_STR_ELEM5(v, a, b, c, d) \
+ (_VA_STR_ELEM4(v, a, b, c) || ((v) == (d)))
+#define _VA_STR_ELEM6(v, a, b, c, d, e) \
+ (_VA_STR_ELEM5(v, a, b, c, d) || ((v) == (e)))
+#define _VA_STR_ELEM7(v, a, b, c, d, e, f) \
+ (_VA_STR_ELEM6(v, a, b, c, d, e) || ((v) == (f)))
+#define _VA_STR_ELEM8(v, a, b, c, d, e, f, g) \
+ (_VA_STR_ELEM7(v, a, b, c, d, e, f) || ((v) == (g)))
+#define _VA_STR_ELEM9(v, a, b, c, d, e, f, g, h) \
+ (_VA_STR_ELEM8(v, a, b, c, d, e, f, g) || ((v) == (h)))
+#define _VA_STR_ELEM10(v, a, b, c, d, e, f, g, h, i) \
+ (_VA_STR_ELEM9(v, a, b, c, d, e, f, g, h) || ((v) == (i)))
+#define _VA_STR_ELEM11(v, a, b, c, d, e, f, g, h, i, j) \
+ (_VA_STR_ELEM10(v, a, b, c, d, e, f, g, h, i) || ((v) == (j)))
+#define _VA_STR_ELEM12(v, a, b, c, d, e, f, g, h, i, j, k) \
+ (_VA_STR_ELEM11(v, a, b, c, d, e, f, g, h, i, j) || ((v) == (k)))
+#define _VA_STR_ELEM13(v, a, b, c, d, e, f, g, h, i, j, k, l) \
+ (_VA_STR_ELEM12(v, a, b, c, d, e, f, g, h, i, j, k) || ((v) == (l)))
+#define _VA_STR_ELEM14(v, a, b, c, d, e, f, g, h, i, j, k, l, m) \
+ (_VA_STR_ELEM13(v, a, b, c, d, e, f, g, h, i, j, k, l) || ((v) == (m)))
+#define _VA_STR_ELEM15(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n) \
+ (_VA_STR_ELEM14(v, a, b, c, d, e, f, g, h, i, j, k, l, m) || ((v) == (n)))
+#define _VA_STR_ELEM16(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) \
+ (_VA_STR_ELEM15(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n) || ((v) == (o)))
+#define _VA_STR_ELEM17(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
+ (_VA_STR_ELEM16(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) || ((v) == (p)))
+
+/* reusable STR_ELEM macro */
+#define STR_ELEM(...) VA_NARGS_CALL_OVERLOAD(_VA_STR_ELEM, __VA_ARGS__)
+
+/** \} */
+
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index 79cde43d399..33ddf9367f4 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -250,6 +250,9 @@ set(SRC
BLI_timeit.hpp
)
+set(LIB
+)
+
if(WITH_MEM_VALGRIND)
add_definitions(-DWITH_MEM_VALGRIND)
endif()
@@ -271,4 +274,4 @@ set_source_files_properties(
PROPERTIES HEADER_FILE_ONLY TRUE
)
-blender_add_lib(bf_blenlib "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_blenlib "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c
index af3a7a4f68a..ae1c6e0ba0b 100644
--- a/source/blender/blenlib/intern/BLI_ghash.c
+++ b/source/blender/blenlib/intern/BLI_ghash.c
@@ -1045,7 +1045,7 @@ void BLI_ghash_flag_clear(GHash *gh, uint flag)
/**
* Create a new GHashIterator. The hash table must not be mutated
* while the iterator is in use, and the iterator will step exactly
- * BLI_ghash_len(gh) times before becoming done.
+ * #BLI_ghash_len(gh) times before becoming done.
*
* \param gh: The GHash to iterate over.
* \return Pointer to a new DynStr.
@@ -1060,7 +1060,7 @@ GHashIterator *BLI_ghashIterator_new(GHash *gh)
/**
* Init an already allocated GHashIterator. The hash table must not
* be mutated while the iterator is in use, and the iterator will
- * step exactly BLI_ghash_len(gh) times before becoming done.
+ * step exactly #BLI_ghash_len(gh) times before becoming done.
*
* \param ghi: The GHashIterator to initialize.
* \param gh: The GHash to iterate over.
diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c
index 5acc84da288..24e3f22172e 100644
--- a/source/blender/blenlib/intern/BLI_mempool.c
+++ b/source/blender/blenlib/intern/BLI_mempool.c
@@ -371,7 +371,7 @@ void *BLI_mempool_calloc(BLI_mempool *pool)
/**
* Free an element from the mempool.
*
- * \note doesnt protect against double frees, don't be stupid!
+ * \note doesn't protect against double frees, take care!
*/
void BLI_mempool_free(BLI_mempool *pool, void *addr)
{
diff --git a/source/blender/blenlib/intern/edgehash.c b/source/blender/blenlib/intern/edgehash.c
index 55c9a189a20..564090f734e 100644
--- a/source/blender/blenlib/intern/edgehash.c
+++ b/source/blender/blenlib/intern/edgehash.c
@@ -314,7 +314,7 @@ void *BLI_edgehash_lookup_default(EdgeHash *eh, uint v0, uint v1, void *default_
* Return value for given edge (\a v0, \a v1), or NULL if
* if key does not exist in hash. (If need exists
* to differentiate between key-value being NULL and
- * lack of key then see BLI_edgehash_lookup_p().
+ * lack of key then see #BLI_edgehash_lookup_p().
*/
void *BLI_edgehash_lookup(EdgeHash *eh, uint v0, uint v1)
{
diff --git a/source/blender/blenlib/intern/listbase.c b/source/blender/blenlib/intern/listbase.c
index 8e6de3ab141..e34f9d0ab10 100644
--- a/source/blender/blenlib/intern/listbase.c
+++ b/source/blender/blenlib/intern/listbase.c
@@ -61,7 +61,7 @@ void BLI_movelisttolist(ListBase *dst, ListBase *src)
}
/**
- * moves the entire contents of \a src at the begining of \a dst.
+ * moves the entire contents of \a src at the beginning of \a dst.
*/
void BLI_movelisttolist_reverse(ListBase *dst, ListBase *src)
{
diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c
index 68c80beb005..3da69e92227 100644
--- a/source/blender/blenlib/intern/math_matrix.c
+++ b/source/blender/blenlib/intern/math_matrix.c
@@ -316,8 +316,32 @@ void mul_m4_m4m3(float m1[4][4], const float m3_[4][4], const float m2_[3][3])
m1[2][2] = m2[2][0] * m3[0][2] + m2[2][1] * m3[1][2] + m2[2][2] * m3[2][2];
}
+/* m1 = m2 * m3, ignore the elements on the 4th row/column of m2 */
+void mul_m3_m3m4(float m1[3][3], const float m3_[3][3], const float m2_[4][4])
+{
+ float m2[4][4], m3[3][3];
+
+ /* copy so it works when m1 is the same pointer as m2 or m3 */
+ /* TODO: avoid copying when matrices are different */
+ copy_m4_m4(m2, m2_);
+ copy_m3_m3(m3, m3_);
+
+ /* m1[i][j] = m2[i][k] * m3[k][j] */
+ m1[0][0] = m2[0][0] * m3[0][0] + m2[0][1] * m3[1][0] + m2[0][2] * m3[2][0];
+ m1[0][1] = m2[0][0] * m3[0][1] + m2[0][1] * m3[1][1] + m2[0][2] * m3[2][1];
+ m1[0][2] = m2[0][0] * m3[0][2] + m2[0][1] * m3[1][2] + m2[0][2] * m3[2][2];
+
+ m1[1][0] = m2[1][0] * m3[0][0] + m2[1][1] * m3[1][0] + m2[1][2] * m3[2][0];
+ m1[1][1] = m2[1][0] * m3[0][1] + m2[1][1] * m3[1][1] + m2[1][2] * m3[2][1];
+ m1[1][2] = m2[1][0] * m3[0][2] + m2[1][1] * m3[1][2] + m2[1][2] * m3[2][2];
+
+ m1[2][0] = m2[2][0] * m3[0][0] + m2[2][1] * m3[1][0] + m2[2][2] * m3[2][0];
+ m1[2][1] = m2[2][0] * m3[0][1] + m2[2][1] * m3[1][1] + m2[2][2] * m3[2][1];
+ m1[2][2] = m2[2][0] * m3[0][2] + m2[2][1] * m3[1][2] + m2[2][2] * m3[2][2];
+}
+
/* m1 = m2 * m3, ignore the elements on the 4th row/column of m3 */
-void mul_m3_m3m4(float m1[3][3], const float m3_[4][4], const float m2_[3][3])
+void mul_m3_m4m3(float m1[3][3], const float m3_[4][4], const float m2_[3][3])
{
float m2[3][3], m3[4][4];
@@ -360,6 +384,18 @@ void mul_m4_m3m4(float m1[4][4], const float m3_[3][3], const float m2_[4][4])
m1[2][2] = m2[2][0] * m3[0][2] + m2[2][1] * m3[1][2] + m2[2][2] * m3[2][2];
}
+void mul_m3_m4m4(float m1[3][3], const float m3[4][4], const float m2[4][4])
+{
+ m1[0][0] = m2[0][0] * m3[0][0] + m2[0][1] * m3[1][0] + m2[0][2] * m3[2][0];
+ m1[0][1] = m2[0][0] * m3[0][1] + m2[0][1] * m3[1][1] + m2[0][2] * m3[2][1];
+ m1[0][2] = m2[0][0] * m3[0][2] + m2[0][1] * m3[1][2] + m2[0][2] * m3[2][2];
+ m1[1][0] = m2[1][0] * m3[0][0] + m2[1][1] * m3[1][0] + m2[1][2] * m3[2][0];
+ m1[1][1] = m2[1][0] * m3[0][1] + m2[1][1] * m3[1][1] + m2[1][2] * m3[2][1];
+ m1[1][2] = m2[1][0] * m3[0][2] + m2[1][1] * m3[1][2] + m2[1][2] * m3[2][2];
+ m1[2][0] = m2[2][0] * m3[0][0] + m2[2][1] * m3[1][0] + m2[2][2] * m3[2][0];
+ m1[2][1] = m2[2][0] * m3[0][1] + m2[2][1] * m3[1][1] + m2[2][2] * m3[2][1];
+ m1[2][2] = m2[2][0] * m3[0][2] + m2[2][1] * m3[1][2] + m2[2][2] * m3[2][2];
+}
/** \name Macro helpers for: mul_m3_series
* \{ */
@@ -813,6 +849,28 @@ void add_m4_m4m4(float m1[4][4], const float m2[4][4], const float m3[4][4])
}
}
+void madd_m3_m3m3fl(float m1[3][3], const float m2[3][3], const float m3[3][3], const float f)
+{
+ int i, j;
+
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 3; j++) {
+ m1[i][j] = m2[i][j] + m3[i][j] * f;
+ }
+ }
+}
+
+void madd_m4_m4m4fl(float m1[4][4], const float m2[4][4], const float m3[4][4], const float f)
+{
+ int i, j;
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 4; j++) {
+ m1[i][j] = m2[i][j] + m3[i][j] * f;
+ }
+ }
+}
+
void sub_m3_m3m3(float m1[3][3], const float m2[3][3], const float m3[3][3])
{
int i, j;
@@ -1811,7 +1869,7 @@ void blend_m4_m4m4(float out[4][4], const float dst[4][4], const float src[4][4]
*
* \note This code is about five times slower as the 'naive' interpolation done by #blend_m3_m3m3
* (it typically remains below 2 usec on an average i74700, while #blend_m3_m3m3 remains below 0.4 usec).
- * However, it gives expected results even with non-uniformaly scaled matrices, see T46418 for an example.
+ * However, it gives expected results even with non-uniformly scaled matrices, see T46418 for an example.
*
* Based on "Matrix Animation and Polar Decomposition", by Ken Shoemake & Tom Duff
*
@@ -2533,20 +2591,20 @@ void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4])
}
/**
- * SpaceTransform struct encapsulates all needed data to convert between two coordinate spaces
+ * #SpaceTransform struct encapsulates all needed data to convert between two coordinate spaces
* (where conversion can be represented by a matrix multiplication).
*
* A SpaceTransform is initialized using:
- * BLI_SPACE_TRANSFORM_SETUP(&data, ob1, ob2)
+ * - #BLI_SPACE_TRANSFORM_SETUP(&data, ob1, ob2)
*
* After that the following calls can be used:
- * BLI_space_transform_apply(&data, co); // converts a coordinate in ob1 space to the corresponding ob2 space
- * BLI_space_transform_invert(&data, co); // converts a coordinate in ob2 space to the corresponding ob1 space
+ * - #BLI_space_transform_apply(&data, co); // converts a coordinate in ob1 space to the corresponding ob2 space.
+ * - #BLI_space_transform_invert(&data, co); // converts a coordinate in ob2 space to the corresponding ob1 space.
*
- * Same concept as BLI_space_transform_apply and BLI_space_transform_invert, but no is normalized after conversion
+ * Same concept as #BLI_space_transform_apply and #BLI_space_transform_invert, but no is normalized after conversion
* (and not translated at all!):
- * BLI_space_transform_apply_normal(&data, no);
- * BLI_space_transform_invert_normal(&data, no);
+ * - #BLI_space_transform_apply_normal(&data, no);
+ * - #BLI_space_transform_invert_normal(&data, no);
*/
/**
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c
index 0bd09f0c268..dc5ee984c25 100644
--- a/source/blender/blenlib/intern/path_util.c
+++ b/source/blender/blenlib/intern/path_util.c
@@ -1626,11 +1626,13 @@ bool BLI_ensure_filename(char *filepath, size_t maxlen, const char *filename)
return false;
}
-/* Converts "/foo/bar.txt" to "/foo/" and "bar.txt"
- * - wont change 'string'
- * - wont create any directories
- * - dosnt use CWD, or deal with relative paths.
- * - Only fill's in *dir and *file when they are non NULL
+/**
+ * Converts `/foo/bar.txt` to "/foo/" and `bar.txt`
+ *
+ * - Wont change \a string.
+ * - Wont create any directories.
+ * - Doesn't use CWD, or deal with relative paths.
+ * - Only fill's in \a dir and \a file when they are non NULL.
* */
void BLI_split_dirfile(const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen)
{
diff --git a/source/blender/blenlib/intern/threads.c b/source/blender/blenlib/intern/threads.c
index df2e50233d1..0758af03193 100644
--- a/source/blender/blenlib/intern/threads.c
+++ b/source/blender/blenlib/intern/threads.c
@@ -839,7 +839,7 @@ void BLI_threaded_malloc_begin(void)
unsigned int level = atomic_fetch_and_add_u(&thread_levels, 1);
if (level == 0) {
MEM_set_lock_callback(BLI_lock_malloc_thread, BLI_unlock_malloc_thread);
- /* There is a little chance that two threads will meed to acces to a
+ /* There is a little chance that two threads will need to access to a
* scheduler which was not yet created from main thread. which could
* cause scheduler created multiple times.
*/
@@ -874,10 +874,10 @@ static bool check_is_threadripper2_alike_topology(void)
return false;
}
if (strstr(cpu_brand, "Threadripper")) {
- /* NOTE: We consinder all Threadrippers having similar topology to
+ /* NOTE: We consider all Thread-rippers having similar topology to
* the second one. This is because we are trying to utilize NUMA node
* 0 as much as possible. This node does exist on earlier versions of
- * threadripper and setting affinity to it should not have negative
+ * thread-ripper and setting affinity to it should not have negative
* effect.
* This allows us to avoid per-model check, making the code more
* reliable for the CPUs which are not yet released.
@@ -893,7 +893,7 @@ static bool check_is_threadripper2_alike_topology(void)
* up their DR slots, making it only two dies connected to a DDR slot
* with actual memory in it. */
if (strstr(cpu_brand, "EPYC")) {
- /* NOTE: Similarly to Threadripper we do not do model check. */
+ /* NOTE: Similarly to Thread-ripper we do not do model check. */
is_threadripper2 = true;
}
MEM_freeN(cpu_brand);
@@ -913,7 +913,7 @@ static void threadripper_put_process_on_fast_node(void)
* However, if scene fits into memory adjacent to a single die we don't
* want OS to re-schedule the process to another die since that will make
* it further away from memory allocated for .blend file. */
- /* NOTE: Even if NUMA is avasilable in the API but is disabled in BIOS on
+ /* NOTE: Even if NUMA is available in the API but is disabled in BIOS on
* this workstation we still process here. If NUMA is disabled it will be a
* single node, so our action is no-visible-changes, but allows to keep
* things simple and unified. */
@@ -942,7 +942,7 @@ static void threadripper_put_thread_on_fast_node(void)
void BLI_thread_put_process_on_fast_node(void)
{
/* Disabled for now since this causes only 16 threads to be used on a
- * threadripper for computations like sculpting and fluid sim. The problem
+ * thread-ripper for computations like sculpting and fluid sim. The problem
* is that all threads created as children from this thread will inherit
* the NUMA node and so will end up on the same node. This can be fixed
* case-by-case by assigning the NUMA node for every child thread, however
diff --git a/source/blender/blenlib/intern/winstuff_dir.c b/source/blender/blenlib/intern/winstuff_dir.c
index b302eaea9de..9c1a51f9be7 100644
--- a/source/blender/blenlib/intern/winstuff_dir.c
+++ b/source/blender/blenlib/intern/winstuff_dir.c
@@ -54,9 +54,10 @@ struct __dirstream {
struct dirent direntry;
};
-/* Note: MinGW (FREE_WINDOWS) has opendir() and _wopendir(), and only the
- * latter accepts a path name of wchar_t type. Rather than messing up with
- * extra #ifdef's here and there, Blender's own implementations of opendir()
+/**
+ * \note MinGW (FREE_WINDOWS) has #opendir() and #_wopendir(), and only the
+ * latter accepts a path name of #wchar_t type. Rather than messing up with
+ * extra #ifdef's here and there, Blender's own implementations of #opendir()
* and related functions are used to properly support paths with non-ASCII
* characters. (kjym3)
*/
diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index b9bec1b97b3..26d1ee14856 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -67,6 +67,9 @@ set(SRC
intern/readfile.h
)
+set(LIB
+)
+
if(WITH_BUILDINFO)
add_definitions(-DWITH_BUILDINFO)
endif()
@@ -92,7 +95,7 @@ if(WITH_ALEMBIC)
add_definitions(-DWITH_ALEMBIC)
endif()
-blender_add_lib(bf_blenloader "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_blenloader "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# needed so writefile.c can use dna_type_offsets.h
add_dependencies(bf_blenloader bf_dna)
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 6c4049ec295..2543271e691 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -1628,7 +1628,7 @@ bool BLO_library_path_explode(const char *path, char *r_dir, char **r_group, cha
*
* \param filepath: The path of the file to extract thumbnail from.
* \return The raw thumbnail
- * (MEM-allocated, as stored in file, use BKE_main_thumbnail_to_imbuf() to convert it to ImBuf image).
+ * (MEM-allocated, as stored in file, use #BKE_main_thumbnail_to_imbuf() to convert it to ImBuf image).
*/
BlendThumbnail *BLO_thumbnail_from_file(const char *filepath)
{
@@ -7170,7 +7170,7 @@ static void direct_link_area(FileData *fd, ScrArea *area)
direct_link_gpencil(fd, v3d->gpd);
}
v3d->localvd = newdataadr(fd, v3d->localvd);
- v3d->properties_storage = NULL;
+ v3d->runtime.properties_storage = NULL;
/* render can be quite heavy, set to solid on load */
if (v3d->shading.type == OB_RENDER) {
@@ -8171,7 +8171,7 @@ static void direct_link_library(FileData *fd, Library *lib, Main *main)
/* Now, since Blender always expect **latest** Main pointer from fd->mainlist to be the active library
* Main pointer, where to add all non-library data-blocks found in file next, we have to switch that
* 'dupli' found Main to latest position in the list!
- * Otherwise, you get weird disappearing linked data on a rather unconsistant basis.
+ * Otherwise, you get weird disappearing linked data on a rather inconsistent basis.
* See also T53977 for reproducible case. */
BLI_remlink(fd->mainlist, newmain);
BLI_addtail(fd->mainlist, newmain);
@@ -8930,7 +8930,7 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const int ta
/* In undo case, most libs and linked data should be kept as is from previous state (see BLO_read_from_memfile).
* However, some needed by the snapshot being read may have been removed in previous one, and would go missing.
- * This leads e.g. to desappearing objects in some undo/redo case, see T34446.
+ * This leads e.g. to disappearing objects in some undo/redo case, see T34446.
* That means we have to carefully check whether current lib or libdata already exits in old main, if it does
* we merely copy it over into new main area, otherwise we have to do a full read of that bhead... */
if (fd->memfile && ELEM(bhead->code, ID_LI, ID_LINK_PLACEHOLDER)) {
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 25ada2fd13e..ce2345b44db 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -564,6 +564,36 @@ static void do_version_collection_propagate_lib_to_children(Collection *collecti
}
}
+/** convert old annotations colors */
+static void do_versions_fix_annotations(bGPdata *gpd)
+{
+ for (const bGPDpalette *palette = gpd->palettes.first; palette; palette = palette->next) {
+ for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ /* fix layers */
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* unlock/unhide layer */
+ gpl->flag &= ~GP_LAYER_LOCKED;
+ gpl->flag &= ~GP_LAYER_HIDE;
+ /* set opacity to 1 */
+ gpl->opacity = 1.0f;
+ /* disable tint */
+ gpl->tintcolor[3] = 0.0f;
+
+ for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ if ((gps->colorname[0] != '\0') &&
+ (STREQ(gps->colorname, palcolor->info)))
+ {
+ /* copy color settings */
+ copy_v4_v4(gpl->color, palcolor->color);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
void do_versions_after_linking_280(Main *bmain)
{
bool use_collection_compat_28 = true;
@@ -657,6 +687,30 @@ void do_versions_after_linking_280(Main *bmain)
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 280, 0)) {
+ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
+ for (SpaceLink *space = sa->spacedata.first; space; space = space->next) {
+ if (space->spacetype == SPACE_IMAGE) {
+ SpaceImage *sima = (SpaceImage *)space;
+ if ((sima) && (sima->gpd)) {
+ sima->gpd->flag |= GP_DATA_ANNOTATIONS;
+ do_versions_fix_annotations(sima->gpd);
+ }
+ }
+ if (space->spacetype == SPACE_CLIP) {
+ SpaceClip *spclip = (SpaceClip *)space;
+ MovieClip *clip = spclip->clip;
+ if ((clip) && (clip->gpd)) {
+ clip->gpd->flag |= GP_DATA_ANNOTATIONS;
+ do_versions_fix_annotations(clip->gpd);
+ }
+ }
+ }
+ }
+ }
+ }
+
/* New workspace design */
if (!MAIN_VERSION_ATLEAST(bmain, 280, 1)) {
do_version_workspaces_after_lib_link(bmain);
@@ -1776,12 +1830,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
- if (scene->toolsettings->gizmo_flag == 0) {
- scene->toolsettings->gizmo_flag = SCE_GIZMO_SHOW_TRANSLATE | SCE_GIZMO_SHOW_ROTATE | SCE_GIZMO_SHOW_SCALE;
- }
- }
-
if (!DNA_struct_elem_find(fd->filesdna, "RigidBodyWorld", "RigidBodyWorld_Shared", "*shared")) {
for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
RigidBodyWorld *rbw = scene->rigidbody_world;
@@ -2995,15 +3043,24 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- {
- /* Versioning code until next subversion bump goes here. */
-
+ if (!MAIN_VERSION_ATLEAST(bmain, 280, 55)) {
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
if (sl->spacetype == SPACE_TEXT) {
ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
ARegion *ar = MEM_callocN(sizeof(ARegion), "footer for text");
+
+ /* Remove multiple footers that were added by mistake. */
+ ARegion *ar_footer, *ar_next;
+ for (ar_footer = regionbase->first; ar_footer; ar_footer = ar_next) {
+ ar_next = ar_footer->next;
+ if (ar_footer->regiontype == RGN_TYPE_FOOTER) {
+ BLI_freelinkN(regionbase, ar_footer);
+ }
+ }
+
+ /* Add footer. */
ARegion *ar_header = NULL;
for (ar_header = regionbase->first; ar_header; ar_header = ar_header->next) {
@@ -3022,4 +3079,40 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 280, 56)) {
+ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ for (ScrArea *area = screen->areabase.first; area; area = area->next) {
+ for (SpaceLink *sl = area->spacedata.first; sl; sl = sl->next) {
+ if (sl->spacetype == SPACE_VIEW3D) {
+ View3D *v3d = (View3D *)sl;
+ v3d->gizmo_show_armature = V3D_GIZMO_SHOW_ARMATURE_BBONE | V3D_GIZMO_SHOW_ARMATURE_ROLL;
+ v3d->gizmo_show_empty = V3D_GIZMO_SHOW_EMPTY_IMAGE | V3D_GIZMO_SHOW_EMPTY_FORCE_FIELD;
+ v3d->gizmo_show_light = V3D_GIZMO_SHOW_LIGHT_SIZE | V3D_GIZMO_SHOW_LIGHT_LOOK_AT;
+ v3d->gizmo_show_camera = V3D_GIZMO_SHOW_CAMERA_LENS | V3D_GIZMO_SHOW_CAMERA_DOF_DIST;
+ }
+ }
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 280, 57)) {
+ /* Enable Show Interpolation in dopesheet by default. */
+ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
+ for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
+ if (sl->spacetype == SPACE_ACTION) {
+ SpaceAction *saction = (SpaceAction *)sl;
+ if ((saction->flag & SACTION_SHOW_EXTREMES) == 0) {
+ saction->flag |= SACTION_SHOW_INTERPOLATION;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ {
+ /* Versioning code until next subversion bump goes here. */
+ }
}
diff --git a/source/blender/blentranslation/CMakeLists.txt b/source/blender/blentranslation/CMakeLists.txt
index fa3f8c47bee..2a397af051d 100644
--- a/source/blender/blentranslation/CMakeLists.txt
+++ b/source/blender/blentranslation/CMakeLists.txt
@@ -39,6 +39,9 @@ set(SRC
BLT_translation.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
@@ -56,7 +59,7 @@ if(WIN32)
endif()
endif()
-blender_add_lib(bf_blentranslation "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_blentranslation "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if(WITH_INTERNATIONAL)
add_subdirectory(msgfmt)
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt
index 631886ac86e..be6c819bc1f 100644
--- a/source/blender/bmesh/CMakeLists.txt
+++ b/source/blender/bmesh/CMakeLists.txt
@@ -163,6 +163,9 @@ set(SRC
bmesh_tools.h
)
+set(LIB
+)
+
if(MSVC AND NOT MSVC_CLANG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX /wd4101")
endif()
@@ -182,4 +185,4 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
-blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/collada/BCAnimationSampler.cpp b/source/blender/collada/BCAnimationSampler.cpp
index afc37191e2e..7e504a0e745 100644
--- a/source/blender/collada/BCAnimationSampler.cpp
+++ b/source/blender/collada/BCAnimationSampler.cpp
@@ -395,9 +395,9 @@ void BCAnimationSampler::generate_transforms(Object *ob, Bone *bone, BCAnimation
}
/**
- * Collect all keyframes from all animation curves related to the object
- * The bc_get... functions check for NULL and correct object type
- * The add_keyframes_from() function checks for NULL
+ * Collect all keyframes from all animation curves related to the object.
+ * The bc_get... functions check for NULL and correct object type.
+ * The #add_keyframes_from() function checks for NULL.
*/
void BCAnimationSampler::initialize_keyframes(BCFrameSet &frameset, Object *ob)
{
diff --git a/source/blender/collada/CMakeLists.txt b/source/blender/collada/CMakeLists.txt
index 41178b542eb..7a02068f42d 100644
--- a/source/blender/collada/CMakeLists.txt
+++ b/source/blender/collada/CMakeLists.txt
@@ -125,6 +125,9 @@ set(SRC
collada_utils.h
)
+set(LIB
+)
+
if(WITH_BUILDINFO)
add_definitions(-DWITH_BUILDINFO)
endif()
@@ -138,4 +141,4 @@ if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive")
endif()
-blender_add_lib(bf_collada "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_collada "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index 306c06c4576..67e7afefcd3 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -539,6 +539,9 @@ set(SRC
operations/COM_MaskOperation.h
)
+set(LIB
+)
+
list(APPEND INC
${CMAKE_CURRENT_BINARY_DIR}/operations
)
@@ -552,4 +555,4 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
-blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt
index 18674a85654..5c8432e1043 100644
--- a/source/blender/depsgraph/CMakeLists.txt
+++ b/source/blender/depsgraph/CMakeLists.txt
@@ -110,6 +110,9 @@ set(SRC
intern/depsgraph_update.h
)
+set(LIB
+)
+
if(WITH_BOOST)
list(APPEND INC_SYS
${BOOST_INCLUDE_DIR}
@@ -117,4 +120,4 @@ if(WITH_BOOST)
add_definitions(-DHAVE_BOOST_FUNCTION_BINDINGS)
endif()
-blender_add_lib(bf_depsgraph "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_depsgraph "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
index 35652ebb53e..9fd4a8dc802 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
@@ -172,7 +172,8 @@ void DepsgraphRelationBuilder::build_ik_pose(Object *object,
BUILD,
"\nStarting IK Build: pchan = %s, target = (%s, %s), "
"segcount = %d\n",
- pchan->name, data->tar->id.name, data->subtarget, data->rootbone);
+ pchan->name, data->tar ? data->tar->id.name : "NULL",
+ data->subtarget, data->rootbone);
bPoseChannel *parchan = pchan;
/* Exclude tip from chain if needed. */
if (!(data->flag & CONSTRAINT_IK_TIP)) {
@@ -260,8 +261,11 @@ void DepsgraphRelationBuilder::build_splineik_pose(Object *object,
RELATION_FLAG_GODMODE);
/* Attach path dependency to solver. */
if (data->tar != NULL) {
- ComponentKey target_key(&data->tar->id, NodeType::GEOMETRY);
- add_relation(target_key, init_ik_key, "Curve.Path -> Spline IK");
+ ComponentKey target_geometry_key(&data->tar->id, NodeType::GEOMETRY);
+ add_relation(target_geometry_key, solver_key, "Curve.Path -> Spline IK");
+ ComponentKey target_transform_key(&data->tar->id, NodeType::TRANSFORM);
+ add_relation(target_transform_key, solver_key, "Curve.Transform -> Spline IK");
+ add_special_eval_flag(&data->tar->id, DAG_EVAL_NEED_CURVE_PATH);
}
pchan->flag |= POSE_DONE;
OperationKey final_transforms_key(
@@ -269,41 +273,27 @@ void DepsgraphRelationBuilder::build_splineik_pose(Object *object,
add_relation(solver_key, final_transforms_key, "Spline IK Result");
root_map->add_bone(pchan->name, rootchan->name);
/* Walk to the chain's root/ */
- int segcount = 0;
+ int segcount = 1;
for (bPoseChannel *parchan = pchan->parent;
- parchan != NULL;
- parchan = parchan->parent)
+ parchan != NULL && segcount < data->chainlen;
+ parchan = parchan->parent, segcount++)
{
/* Make Spline IK solver dependent on this bone's result, since it can
* only run after the standard results of the bone are know. Validate
* links step on the bone will ensure that users of this bone only grab
* the result with IK solver results. */
- if (parchan != pchan) {
- OperationKey parent_key(&object->id,
- NodeType::BONE,
- parchan->name,
- OperationCode::BONE_READY);
- add_relation(parent_key, solver_key, "Spline IK Solver Update");
- OperationKey bone_done_key(&object->id,
- NodeType::BONE,
- parchan->name,
- OperationCode::BONE_DONE);
- add_relation(solver_key, bone_done_key, "IK Chain Result");
- }
+ OperationKey parent_key(&object->id,
+ NodeType::BONE,
+ parchan->name,
+ OperationCode::BONE_READY);
+ add_relation(parent_key, solver_key, "Spline IK Solver Update");
+ OperationKey bone_done_key(&object->id,
+ NodeType::BONE,
+ parchan->name,
+ OperationCode::BONE_DONE);
+ add_relation(solver_key, bone_done_key, "Spline IK Solver Result");
parchan->flag |= POSE_DONE;
- OperationKey final_transforms_key(&object->id,
- NodeType::BONE,
- parchan->name,
- OperationCode::BONE_DONE);
- add_relation(
- solver_key, final_transforms_key, "Spline IK Solver Result");
root_map->add_bone(parchan->name, rootchan->name);
- /* TODO(sergey): This is an arbitrary value, which was just following
- * old code convention. */
- segcount++;
- if ((segcount == data->chainlen) || (segcount > 255)) {
- break;
- }
}
OperationKey pose_done_key(
&object->id, NodeType::EVAL_POSE, OperationCode::POSE_DONE);
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index f6887af63f2..daa599b6972 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -472,8 +472,8 @@ void deg_graph_on_visible_update(Main *bmain, Depsgraph *graph)
{
for (DEG::IDNode *id_node : graph->id_nodes) {
if (!id_node->visible_components_mask) {
- /* ID has no components which affects anything visible. no meed
- * bother with it to tag or anything. */
+ /* ID has no components which affects anything visible.
+ * No need bother with it to tag or anything. */
continue;
}
if (id_node->visible_components_mask ==
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
index 0d60defa9b6..387253ab3fe 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
@@ -697,6 +697,7 @@ void update_id_after_copy(const Depsgraph *depsgraph,
update_pose_orig_pointers(object_orig->pose,
object_cow->pose);
}
+ BKE_pose_pchan_index_rebuild(object_cow->pose);
}
update_particles_after_copy(object_orig, object_cow);
update_modifiers_orig_pointers(object_orig, object_cow);
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 3ac606fc48f..fe9b3d1f629 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -151,6 +151,9 @@ set(SRC
engines/workbench/workbench_private.h
)
+set(LIB
+)
+
data_to_c_simple(engines/eevee/shaders/ambient_occlusion_lib.glsl SRC)
data_to_c_simple(engines/eevee/shaders/default_frag.glsl SRC)
data_to_c_simple(engines/eevee/shaders/default_world_frag.glsl SRC)
@@ -355,4 +358,4 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
-blender_add_lib(bf_draw "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_draw "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index 32ec54b1ca6..94e0231c5fe 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -1551,7 +1551,7 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sld
*/
bool use_volume_material = (gpumat_array[0] && GPU_material_use_domain_volume(gpumat_array[0]));
- if (DRW_state_is_image_render() || ob->dt >= OB_SOLID) {
+ if ((ob->dt >= OB_SOLID) || DRW_state_is_image_render()) {
/* Get per-material split surface */
char *auto_layer_names;
int *auto_layer_is_srgb;
diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
index cf3025302ef..972ada22b50 100644
--- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
+++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
@@ -31,6 +31,7 @@
#include "BKE_library.h"
#include "BKE_gpencil.h"
+#include "BKE_object.h"
#include "gpencil_engine.h"
@@ -38,7 +39,33 @@
#include "DEG_depsgraph.h"
- /* add a gpencil object to cache to defer drawing */
+/* verify if exist a non instanced version of the object */
+static bool gpencil_has_noninstanced_object(Object *ob_instance)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ const ViewLayer *view_layer = draw_ctx->view_layer;
+ Object *ob = NULL;
+ for (Base *base = view_layer->object_bases.first; base; base = base->next) {
+ ob = base->object;
+ if (ob->type != OB_GPENCIL) {
+ continue;
+ }
+ /* object must be visible (invisible objects don't create VBO data) */
+ if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) {
+ continue;
+ }
+ /* is not duplicated and the name is equals */
+ if ((ob->base_flag & BASE_FROM_DUPLI) == 0) {
+ if (STREQ(ob->id.name, ob_instance->id.name)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/* add a gpencil object to cache to defer drawing */
tGPencilObjectCache *gpencil_object_cache_add(
tGPencilObjectCache *cache_array, Object *ob,
int *gp_cache_size, int *gp_cache_used)
@@ -76,7 +103,18 @@ tGPencilObjectCache *gpencil_object_cache_add(
cache_elem->idx = *gp_cache_used;
/* object is duplicated (particle) */
- cache_elem->is_dup_ob = ob->base_flag & BASE_FROM_DUPLI;
+ if (ob->base_flag & BASE_FROM_DUPLI) {
+ /* Check if the original object is not in the viewlayer
+ * and cannot be managed as dupli. This is slower, but required to keep
+ * the particle drawing FPS and display instanced objects in scene
+ * without the original object */
+ bool has_original = gpencil_has_noninstanced_object(ob);
+ cache_elem->is_dup_ob = (has_original) ? ob->base_flag & BASE_FROM_DUPLI : false;
+ }
+ else {
+ cache_elem->is_dup_ob = false;
+ }
+
cache_elem->scale = mat4_to_scale(ob->obmat);
/* save FXs */
diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c
index 475a083ac97..6aefd2a69c5 100644
--- a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c
+++ b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c
@@ -1355,7 +1355,7 @@ void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, T
float obscale = mat4_to_scale(ob->obmat);
/* use the brush material */
- Material *ma = BKE_gpencil_get_material_for_brush(ob, brush);
+ Material *ma = BKE_gpencil_object_material_get_from_brush(ob, brush);
if (ma != NULL) {
gp_style = ma->gp_style;
}
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c
index c5546a55dda..d1a8f40a428 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.c
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.c
@@ -316,7 +316,7 @@ void GPENCIL_cache_init(void *vedata)
if (obact && (obact->type == OB_GPENCIL) && (obact->data)) {
obact_gpd = (bGPdata *)obact->data;
/* use the brush material */
- Material *ma = BKE_gpencil_get_material_for_brush(obact, brush);
+ Material *ma = BKE_gpencil_object_material_get_from_brush(obact, brush);
if (ma != NULL) {
gp_style = ma->gp_style;
}
@@ -405,7 +405,7 @@ void GPENCIL_cache_init(void *vedata)
/* xray mode */
if (v3d) {
- stl->storage->is_xray = (v3d->shading.flag & V3D_XRAY_FLAG(v3d)) ? 1 : 0;
+ stl->storage->is_xray = XRAY_ACTIVE(v3d);
}
else {
stl->storage->is_xray = 0;
@@ -620,7 +620,8 @@ void GPENCIL_cache_populate(void *vedata, Object *ob)
/* enable instance loop */
if (!stl->g_data->do_instances) {
- stl->g_data->do_instances = ob->base_flag & BASE_FROM_DUPLI;
+ tGPencilObjectCache *cache_ob = &stl->g_data->gp_object_cache[stl->g_data->gp_cache_used - 1];
+ stl->g_data->do_instances = cache_ob->is_dup_ob;
}
/* load drawing data */
@@ -642,7 +643,7 @@ void GPENCIL_cache_populate(void *vedata, Object *ob)
bGPdata *gpd_orig = (bGPdata *)DEG_get_original_id(&gpd->id);
if ((draw_ctx->obact == ob) &&
- ((gpd_orig->runtime.ar == NULL) || (gpd_orig->runtime.ar == draw_ctx->ar)))
+ ((gpd_orig->runtime.ar == NULL) || (gpd_orig->runtime.ar == draw_ctx->ar)))
{
DRW_gpencil_populate_buffer_strokes(&e_data, vedata, ts, ob);
}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl
index 1ebb6797f63..aa38ff26a62 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl
@@ -84,6 +84,9 @@ void main(void)
if (sp2.x < -area.x || sp2.x > area.x) return;
if (sp2.y < -area.y || sp2.y > area.y) return;
+ /* culling behind camera */
+ if (P1.w < 0 || P2.w < 0) return;
+
/* determine the direction of each of the 3 segments (previous, current, next) */
vec2 v0 = normalize(sp1 - sp0);
vec2 v1 = normalize(sp2 - sp1);
diff --git a/source/blender/draw/engines/workbench/workbench_deferred.c b/source/blender/draw/engines/workbench/workbench_deferred.c
index f75d385714f..482d7178cb7 100644
--- a/source/blender/draw/engines/workbench/workbench_deferred.c
+++ b/source/blender/draw/engines/workbench/workbench_deferred.c
@@ -897,7 +897,7 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob)
if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) {
return;
}
- if (!DRW_state_is_image_render() && ob->dt < OB_SOLID) {
+ if ((ob->dt < OB_SOLID) && !DRW_state_is_image_render()) {
return;
}
diff --git a/source/blender/draw/engines/workbench/workbench_forward.c b/source/blender/draw/engines/workbench/workbench_forward.c
index 45f1c632a05..fe8a341bf3e 100644
--- a/source/blender/draw/engines/workbench/workbench_forward.c
+++ b/source/blender/draw/engines/workbench/workbench_forward.c
@@ -38,6 +38,7 @@
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
+#include "ED_view3d.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
@@ -307,11 +308,12 @@ void workbench_forward_engine_init(WORKBENCH_Data *vedata)
workbench_private_data_get_light_direction(wpd, light_direction);
if (!e_data.checker_depth_sh) {
- workbench_forward_outline_shaders_ensure(wpd, draw_ctx->sh_cfg);
-
e_data.checker_depth_sh = DRW_shader_create_fullscreen(
datatoc_workbench_checkerboard_depth_frag_glsl, NULL);
}
+
+ workbench_forward_outline_shaders_ensure(wpd, draw_ctx->sh_cfg);
+
workbench_volume_engine_init();
workbench_fxaa_engine_init();
workbench_taa_engine_init(vedata);
@@ -381,7 +383,7 @@ void workbench_forward_engine_init(WORKBENCH_Data *vedata)
/* TODO(campbell): displays but masks geometry,
* only use with wire or solid-without-xray for now. */
- if (((wpd->shading.type != OB_WIRE && XRAY_FLAG(wpd) == 0)) &&
+ if ((wpd->shading.type != OB_WIRE && !XRAY_FLAG_ENABLED(wpd)) &&
(draw_ctx->rv3d && (draw_ctx->rv3d->rflag & RV3D_CLIPPING) && draw_ctx->rv3d->clipbb))
{
psl->background_pass = DRW_pass_create(
@@ -407,7 +409,7 @@ void workbench_forward_engine_init(WORKBENCH_Data *vedata)
noise_offset = fmodf(noise_offset + 1.0f / 8.0f, 1.0f);
}
- if (wpd->shading.flag & XRAY_FLAG(wpd)) {
+ if (XRAY_FLAG_ENABLED(wpd)) {
blend_threshold = 1.0f - XRAY_ALPHA(wpd) * 0.9f;
}
diff --git a/source/blender/draw/engines/workbench/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c
index c551a369b58..663727285a1 100644
--- a/source/blender/draw/engines/workbench/workbench_render.c
+++ b/source/blender/draw/engines/workbench/workbench_render.c
@@ -28,6 +28,8 @@
#include "DRW_render.h"
+#include "ED_view3d.h"
+
#include "GPU_shader.h"
#include "DEG_depsgraph.h"
@@ -135,7 +137,7 @@ void workbench_render(WORKBENCH_Data *data, RenderEngine *engine, RenderLayer *r
return;
}
- const bool deferred = (scene->display.shading.flag & XRAY_FLAG(&scene->display)) == 0;
+ const bool deferred = !XRAY_FLAG_ENABLED(&scene->display);
if (deferred) {
/* Init engine. */
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index 3d4618021e8..ce75cc0f05d 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -598,8 +598,4 @@ typedef struct DRWContextState {
const DRWContextState *DRW_context_state_get(void);
-#define XRAY_ALPHA(v3d) (((v3d)->shading.type == OB_WIRE) ? (v3d)->shading.xray_alpha_wire : (v3d)->shading.xray_alpha)
-#define XRAY_FLAG(v3d) (((v3d)->shading.type == OB_WIRE) ? V3D_SHADING_XRAY_BONE : V3D_SHADING_XRAY)
-#define XRAY_ENABLED(v3d) ((((v3d)->shading.flag & XRAY_FLAG(v3d)) != 0) && (XRAY_ALPHA(v3d) < 1.0f))
-
#endif /* __DRW_RENDER_H__ */
diff --git a/source/blender/draw/intern/draw_armature.c b/source/blender/draw/intern/draw_armature.c
index 76a48260fc2..06013bad7c0 100644
--- a/source/blender/draw/intern/draw_armature.c
+++ b/source/blender/draw/intern/draw_armature.c
@@ -1079,7 +1079,7 @@ static void ebone_spline_preview(EditBone *ebone, float result_array[MAX_BBONE_S
param.curveOutX = ebone->curveOutX;
param.curveOutY = ebone->curveOutY;
- ebone->segments = BKE_pchan_bbone_spline_compute(&param, (Mat4 *)result_array);
+ ebone->segments = BKE_pchan_bbone_spline_compute(&param, false, (Mat4 *)result_array);
}
static void draw_bone_update_disp_matrix_bbone(EditBone *eBone, bPoseChannel *pchan)
@@ -1118,12 +1118,7 @@ static void draw_bone_update_disp_matrix_bbone(EditBone *eBone, bPoseChannel *pc
if (pchan) {
Mat4 *bbones_mat = (Mat4 *)pchan->draw_data->bbone_matrix;
if (bbone_segments > 1) {
- if (bbone_segments == pchan->runtime.bbone_segments) {
- memcpy(bbones_mat, pchan->runtime.bbone_pose_mats, sizeof(Mat4) * bbone_segments);
- }
- else {
- BKE_pchan_bbone_spline_setup(pchan, false, bbones_mat);
- }
+ BKE_pchan_bbone_spline_setup(pchan, false, false, bbones_mat);
for (int i = bbone_segments; i--; bbones_mat++) {
mul_m4_m4m4(bbones_mat->mat, bbones_mat->mat, s);
@@ -1246,8 +1241,8 @@ static void draw_points(
Bone *bone = pchan->bone;
if (is_envelope_draw) {
drw_shgroup_bone_envelope(
- pchan->disp_mat, col_solid_root, col_hint_root, col_wire_root,
- &bone->rad_head, &envelope_ignore, sh_cfg);
+ pchan->disp_mat, col_solid_root, col_hint_root, col_wire_root,
+ &bone->rad_head, &envelope_ignore, sh_cfg);
}
else {
drw_shgroup_bone_point(pchan->disp_mat, col_solid_root, col_hint_root, col_wire_root, sh_cfg);
diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index 0256564ffb5..717fd69149c 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -3808,10 +3808,11 @@ void drw_batch_cache_generate_requested(Object *ob)
const enum eContextObjectMode mode = CTX_data_mode_enum_ex(
draw_ctx->object_edit, draw_ctx->obact, draw_ctx->object_mode);
const bool is_paint_mode = ELEM(mode, CTX_MODE_PAINT_TEXTURE, CTX_MODE_PAINT_VERTEX, CTX_MODE_PAINT_WEIGHT);
+
const bool use_hide = (
(ob->type == OB_MESH) &&
((is_paint_mode && (ob == draw_ctx->obact) &&
- (BKE_paint_select_face_test(ob) || BKE_paint_select_vert_test(ob))) ||
+ DRW_object_use_hide_faces(ob)) ||
((mode == CTX_MODE_EDIT_MESH) && BKE_object_is_in_editmode(ob))));
struct Mesh *mesh_eval = ob->runtime.mesh_eval;
diff --git a/source/blender/draw/intern/draw_cache_impl_displist.c b/source/blender/draw/intern/draw_cache_impl_displist.c
index 29fc5478072..9dbf8af0372 100644
--- a/source/blender/draw/intern/draw_cache_impl_displist.c
+++ b/source/blender/draw/intern/draw_cache_impl_displist.c
@@ -598,9 +598,9 @@ static void set_edge_adjacency_lines_indices(EdgeHash *eh, GPUIndexBufBuilder *e
static void set_edges_adjacency_lines_indices(void *thunk, uint v1, uint v2, uint v3)
{
void **packed = (void **)thunk;
- GPUIndexBufBuilder *elb = (GPUIndexBufBuilder*)packed[0];
+ GPUIndexBufBuilder *elb = (GPUIndexBufBuilder *)packed[0];
EdgeHash *eh = (EdgeHash *)packed[1];
- bool *r_is_manifold = (bool*)packed[2];
+ bool *r_is_manifold = (bool *)packed[2];
set_edge_adjacency_lines_indices(eh, elb, r_is_manifold, v1, v2, v3);
set_edge_adjacency_lines_indices(eh, elb, r_is_manifold, v2, v3, v1);
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 5643d23c7cd..e35a191cad5 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -982,11 +982,13 @@ static MeshRenderData *mesh_render_data_create_ex(
CustomData_free_layers(cd_ldata, CD_MLOOPTANGENT, rdata->loop_len);
if (rdata->cd.layers.uv_len != 0) {
+ int act_uv = rdata->cd.layers.uv_active;
for (int i_src = 0, i_dst = 0; i_src < cd_layers_src.uv_len; i_src++, i_dst++) {
if ((cd_used->uv & (1 << i_src)) == 0) {
+ /* This is a non-used UV slot. Skip. */
i_dst--;
if (rdata->cd.layers.uv_active >= i_src) {
- rdata->cd.layers.uv_active--;
+ act_uv--;
}
}
else {
@@ -1002,6 +1004,10 @@ static MeshRenderData *mesh_render_data_create_ex(
BLI_snprintf(rdata->cd.uuid.auto_mix[i_dst], sizeof(*rdata->cd.uuid.auto_mix), "a%u", hash);
}
}
+ if (rdata->cd.layers.uv_active != -1) {
+ /* Actual active UV slot inside uv layers used for shading. */
+ rdata->cd.layers.uv_active = act_uv;
+ }
}
if (rdata->cd.layers.tangent_len != 0) {
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index cde7b283976..699ac8d5b0b 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -200,7 +200,7 @@ bool DRW_object_is_flat_normal(const Object *ob)
bool DRW_object_use_hide_faces(const struct Object *ob)
{
if (ob->type == OB_MESH) {
- const Mesh *me = DEG_get_original_object((Object *)ob)->data;
+ const Mesh *me = ob->data;
switch (ob->mode) {
case OB_MODE_TEXTURE_PAINT:
@@ -1581,9 +1581,7 @@ void DRW_draw_render_loop_ex(
if (DST.draw_ctx.evil_C) {
/* needed so gizmo isn't obscured */
- if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) &&
- ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0))
- {
+ if ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) {
glDisable(GL_DEPTH_TEST);
DRW_draw_gizmo_3d();
}
@@ -1601,7 +1599,7 @@ void DRW_draw_render_loop_ex(
GPU_depth_test(true);
}
- if ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) {
+ if ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) {
/* Draw 2D after region info so we can draw on top of the camera passepartout overlay.
* 'DRW_draw_region_info' sets the projection in pixel-space. */
GPU_depth_test(false);
diff --git a/source/blender/draw/modes/edit_armature_mode.c b/source/blender/draw/modes/edit_armature_mode.c
index 68f091da797..e2af2f10779 100644
--- a/source/blender/draw/modes/edit_armature_mode.c
+++ b/source/blender/draw/modes/edit_armature_mode.c
@@ -26,6 +26,8 @@
#include "DNA_armature_types.h"
#include "DNA_view3d_types.h"
+#include "ED_view3d.h"
+
#include "draw_common.h"
#include "draw_mode_engines.h"
@@ -112,8 +114,7 @@ static void EDIT_ARMATURE_cache_populate(void *vedata, Object *ob)
const DRWContextState *draw_ctx = DRW_context_state_get();
int ghost = (ob->dtx & OB_DRAWXRAY) ? 1 : 0;
- bool transp = (stl->g_data->transparent_bones || (ob->dt <= OB_WIRE)) ||
- (draw_ctx->v3d->shading.flag & XRAY_FLAG(draw_ctx->v3d)) != 0;
+ bool transp = (stl->g_data->transparent_bones || (ob->dt <= OB_WIRE)) || XRAY_FLAG_ENABLED(draw_ctx->v3d);
DRWArmaturePasses passes = {
.bone_solid = (transp) ? psl->bone_transp[ghost] : psl->bone_solid[ghost],
diff --git a/source/blender/draw/modes/edit_mesh_mode.c b/source/blender/draw/modes/edit_mesh_mode.c
index 71c759d47a5..dedf7eac00a 100644
--- a/source/blender/draw/modes/edit_mesh_mode.c
+++ b/source/blender/draw/modes/edit_mesh_mode.c
@@ -43,6 +43,8 @@
#include "BLI_dynstr.h"
#include "BLI_string_utils.h"
+#include "ED_view3d.h"
+
extern char datatoc_paint_weight_vert_glsl[];
extern char datatoc_paint_weight_frag_glsl[];
@@ -385,7 +387,7 @@ static void EDIT_MESH_cache_init(void *vedata)
stl->g_data->do_faces = true;
stl->g_data->do_edges = true;
- stl->g_data->do_zbufclip = ((v3d)->shading.flag & XRAY_FLAG(v3d)) != 0;
+ stl->g_data->do_zbufclip = XRAY_FLAG_ENABLED(v3d);
stl->g_data->data_mask[0] = 0xFF; /* Face Flag */
stl->g_data->data_mask[1] = 0xFF; /* Edge Flag */
@@ -740,7 +742,7 @@ static void EDIT_MESH_draw_scene(void *vedata)
DRW_draw_pass(psl->normals);
DRW_draw_pass(psl->edit_face_overlay);
- if (v3d->shading.type == OB_SOLID && (v3d->shading.flag & XRAY_FLAG(v3d)) == 0 &&
+ if (v3d->shading.type == OB_SOLID && !XRAY_FLAG_ENABLED(v3d) &&
stl->g_data->ghost_ob == 1 && stl->g_data->edit_ob == 1)
{
/* In the case of single ghost object edit (common case for retopology):
diff --git a/source/blender/draw/modes/object_mode.c b/source/blender/draw/modes/object_mode.c
index 563d1267885..7b6519a6863 100644
--- a/source/blender/draw/modes/object_mode.c
+++ b/source/blender/draw/modes/object_mode.c
@@ -1011,8 +1011,7 @@ static void OBJECT_cache_init(void *vedata)
}
g_data = stl->g_data;
- g_data->xray_enabled = XRAY_ENABLED(draw_ctx->v3d) &&
- (draw_ctx->v3d->shading.type < OB_MATERIAL);
+ g_data->xray_enabled = XRAY_ACTIVE(draw_ctx->v3d);
g_data->xray_enabled_and_not_wire = g_data->xray_enabled && draw_ctx->v3d->shading.type > OB_WIRE;
{
@@ -1841,8 +1840,7 @@ static void camera_view3d_reconstruction(
BLI_assert(BLI_listbase_is_empty(&sgl->camera_path));
const bool is_solid_bundle = (v3d->bundle_drawtype == OB_EMPTY_SPHERE) &&
- ((v3d->shading.type != OB_SOLID) ||
- ((v3d->shading.flag & XRAY_FLAG(v3d)) == 0));
+ ((v3d->shading.type != OB_SOLID) || !XRAY_FLAG_ENABLED(v3d));
MovieTracking *tracking = &clip->tracking;
/* Index must start in 1, to mimic BKE_tracking_track_get_indexed. */
@@ -2988,7 +2986,11 @@ static void OBJECT_cache_populate(void *vedata, Object *ob)
(draw_ctx->v3d->flag & V3D_SELECT_OUTLINE) && ((ob->base_flag & BASE_SELECTED) != 0) &&
((DRW_object_is_renderable(ob) && (ob->dt > OB_WIRE)) || (ob->dt == OB_WIRE)));
const bool show_relations = ((draw_ctx->v3d->flag & V3D_HIDE_HELPLINES) == 0);
- const bool hide_object_extra = (v3d->overlay.flag & V3D_OVERLAY_HIDE_OBJECT_XTRAS) != 0;
+ const bool hide_object_extra = (
+ (v3d->overlay.flag & V3D_OVERLAY_HIDE_OBJECT_XTRAS) != 0 &&
+ /* Show if this is the camera we're looking through
+ * since it's useful for moving the camera. */
+ (((rv3d->persp == RV3D_CAMOB) && ((ID *)v3d->camera == ob->id.orig_id)) == 0));
if (do_outlines) {
if (!BKE_object_is_in_editmode(ob) &&
@@ -3152,8 +3154,7 @@ static void OBJECT_cache_populate(void *vedata, Object *ob)
bArmature *arm = ob->data;
if (arm->edbo == NULL) {
if (DRW_state_is_select() || !DRW_pose_mode_armature(ob, draw_ctx->obact)) {
- bool is_wire = (v3d->shading.type == OB_WIRE) || (ob->dt <= OB_WIRE) ||
- (v3d->shading.flag & XRAY_FLAG(v3d)) != 0;
+ bool is_wire = (v3d->shading.type == OB_WIRE) || (ob->dt <= OB_WIRE) || XRAY_FLAG_ENABLED(v3d);
DRWArmaturePasses passes = {
.bone_solid = (is_wire) ? NULL : sgl->bone_solid,
.bone_outline = sgl->bone_outline,
diff --git a/source/blender/draw/modes/overlay_mode.c b/source/blender/draw/modes/overlay_mode.c
index 52e8f73f971..f2cafd51666 100644
--- a/source/blender/draw/modes/overlay_mode.c
+++ b/source/blender/draw/modes/overlay_mode.c
@@ -34,6 +34,8 @@
#include "GPU_shader.h"
#include "DRW_render.h"
+#include "ED_view3d.h"
+
#include "draw_mode_engines.h"
#ifdef __APPLE__
@@ -421,7 +423,7 @@ static void overlay_cache_finish(void *vedata)
View3D *v3d = ctx->v3d;
/* only in solid mode */
- if (v3d->shading.type == OB_SOLID && (v3d->shading.flag & XRAY_FLAG(v3d)) == 0) {
+ if (v3d->shading.type == OB_SOLID && !XRAY_FLAG_ENABLED(v3d)) {
if (stl->g_data->ghost_stencil_test) {
DRW_pass_state_add(psl->face_wireframe_pass, DRW_STATE_STENCIL_EQUAL);
}
diff --git a/source/blender/draw/modes/pose_mode.c b/source/blender/draw/modes/pose_mode.c
index c4050bf3f1a..c445335b587 100644
--- a/source/blender/draw/modes/pose_mode.c
+++ b/source/blender/draw/modes/pose_mode.c
@@ -27,6 +27,8 @@
#include "DRW_engine.h"
#include "DRW_render.h"
+#include "ED_view3d.h"
+
/* If builtin shaders are needed */
#include "GPU_shader.h"
@@ -34,11 +36,13 @@
#include "draw_mode_engines.h"
/* *********** LISTS *********** */
-/* All lists are per viewport specific datas.
+/**
+ * All lists are per viewport specific datas.
* They are all free when viewport changes engines
- * or is free itself. Use POSE_engine_init() to
- * initialize most of them and POSE_cache_init()
- * for POSE_PassList */
+ * or is free itself. Use #POSE_engine_init() to
+ * initialize most of them and #POSE_cache_init()
+ * for #POSE_PassList
+ */
typedef struct POSE_PassList {
struct DRWPass *bone_solid[2];
@@ -198,8 +202,7 @@ static void POSE_cache_populate(void *vedata, Object *ob)
}
if (DRW_pose_mode_armature(ob, draw_ctx->obact)) {
int ghost = (ob->dtx & OB_DRAWXRAY) ? 1 : 0;
- bool transp = (ppd->transparent_bones || (ob->dt <= OB_WIRE)) ||
- (draw_ctx->v3d->shading.flag & XRAY_FLAG(draw_ctx->v3d)) != 0;
+ bool transp = (ppd->transparent_bones || (ob->dt <= OB_WIRE)) || XRAY_FLAG_ENABLED(draw_ctx->v3d);
DRWArmaturePasses passes = {
.bone_solid = (transp) ? psl->bone_transp[ghost] : psl->bone_solid[ghost],
diff --git a/source/blender/editors/animation/CMakeLists.txt b/source/blender/editors/animation/CMakeLists.txt
index 933b4815175..14936e5bc2a 100644
--- a/source/blender/editors/animation/CMakeLists.txt
+++ b/source/blender/editors/animation/CMakeLists.txt
@@ -53,6 +53,9 @@ set(SRC
anim_intern.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
@@ -63,4 +66,4 @@ endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_animation "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_animation "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index 618ac8f6f13..1bddc6268b5 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -734,7 +734,7 @@ static int acf_object_setting_flag(bAnimContext *UNUSED(ac), eAnimChannel_Settin
switch (setting) {
case ACHANNEL_SETTING_SELECT: /* selected */
- return SELECT;
+ return BASE_SELECTED;
case ACHANNEL_SETTING_EXPAND: /* expanded */
*neg = 1;
@@ -766,7 +766,7 @@ static void *acf_object_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings se
switch (setting) {
case ACHANNEL_SETTING_SELECT: /* selected */
- return GET_ACF_FLAG_PTR(ob->flag, type);
+ return GET_ACF_FLAG_PTR(base->flag, type);
case ACHANNEL_SETTING_EXPAND: /* expanded */
return GET_ACF_FLAG_PTR(ob->nlaflag, type); // xxx
diff --git a/source/blender/editors/animation/anim_intern.h b/source/blender/editors/animation/anim_intern.h
index af1fd0e31c4..4661d19378a 100644
--- a/source/blender/editors/animation/anim_intern.h
+++ b/source/blender/editors/animation/anim_intern.h
@@ -38,6 +38,8 @@ extern ListBase builtin_keyingsets;
*/
void ANIM_OT_keyframe_insert(struct wmOperatorType *ot);
void ANIM_OT_keyframe_delete(struct wmOperatorType *ot);
+void ANIM_OT_keyframe_insert_by_name(struct wmOperatorType *ot);
+void ANIM_OT_keyframe_delete_by_name(struct wmOperatorType *ot);
/* Main Keyframe Management operators:
* These handle keyframes management from various spaces. They will handle the menus
diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c
index b4e60d29a40..2287f2e0347 100644
--- a/source/blender/editors/animation/anim_ops.c
+++ b/source/blender/editors/animation/anim_ops.c
@@ -505,6 +505,8 @@ void ED_operatortypes_anim(void)
WM_operatortype_append(ANIM_OT_keyframe_insert_button);
WM_operatortype_append(ANIM_OT_keyframe_delete_button);
WM_operatortype_append(ANIM_OT_keyframe_clear_button);
+ WM_operatortype_append(ANIM_OT_keyframe_insert_by_name);
+ WM_operatortype_append(ANIM_OT_keyframe_delete_by_name);
WM_operatortype_append(ANIM_OT_driver_button_add);
diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c
index d186a0bb757..c9e672a111d 100644
--- a/source/blender/editors/animation/keyframes_general.c
+++ b/source/blender/editors/animation/keyframes_general.c
@@ -64,9 +64,10 @@
/* **************************************************** */
-/* Only delete the nominated keyframe from provided F-Curve.
+/**
+ * Only delete the nominated keyframe from provided F-Curve.
* Not recommended to be used many times successively. For that
- * there is delete_fcurve_keys().
+ * there is #delete_fcurve_keys().
*/
void delete_fcurve_key(FCurve *fcu, int index, bool do_recalc)
{
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 4cea8ddbf74..bc630040fdd 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -80,6 +80,8 @@
#include "anim_intern.h"
+static KeyingSet *keyingset_get_from_op_with_error(wmOperator *op, PropertyRNA *prop, Scene *scene);
+
/* ************************************************** */
/* Keyframing Setting Wrangling */
@@ -447,13 +449,13 @@ int insert_bezt_fcurve(FCurve *fcu, const BezTriple *bezt, eInsertKeyFlags flag)
}
/**
- * This function is a wrapper for insert_bezt_fcurve_internal(), and should be used when
+ * This function is a wrapper for #insert_bezt_fcurve(), and should be used when
* adding a new keyframe to a curve, when the keyframe doesn't exist anywhere else yet.
* It returns the index at which the keyframe was added.
*
- * \param keyframe_type: The type of keyframe (eBezTriple_KeyframeType)
+ * \param keyframe_type: The type of keyframe (#eBezTriple_KeyframeType).
* \param flag: Optional flags (eInsertKeyFlags) for controlling how keys get added
- * and/or whether updates get done
+ * and/or whether updates get done.
*/
int insert_vert_fcurve(FCurve *fcu, float x, float y, eBezTriple_KeyframeType keyframe_type, eInsertKeyFlags flag)
{
@@ -1587,26 +1589,12 @@ static int insert_key_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Object *obedit = CTX_data_edit_object(C);
bool ob_edit_mode = false;
- KeyingSet *ks = NULL;
- int type = RNA_enum_get(op->ptr, "type");
+
float cfra = (float)CFRA; // XXX for now, don't bother about all the yucky offset crap
short success;
- /* type is the Keying Set the user specified to use when calling the operator:
- * - type == 0: use scene's active Keying Set
- * - type > 0: use a user-defined Keying Set from the active scene
- * - type < 0: use a builtin Keying Set
- */
- if (type == 0)
- type = scene->active_keyingset;
- if (type > 0)
- ks = BLI_findlink(&scene->keyingsets, type - 1);
- else
- ks = BLI_findlink(&builtin_keyingsets, -type - 1);
-
- /* report failures */
+ KeyingSet *ks = keyingset_get_from_op_with_error(op, op->type->prop, scene);
if (ks == NULL) {
- BKE_report(op->reports, RPT_ERROR, "No active keying set");
return OPERATOR_CANCELLED;
}
@@ -1677,6 +1665,36 @@ void ANIM_OT_keyframe_insert(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
+/* Clone of 'ANIM_OT_keyframe_insert' which uses a name for the keying set instead of an enum. */
+void ANIM_OT_keyframe_insert_by_name(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Insert Keyframe (by name)";
+ ot->idname = "ANIM_OT_keyframe_insert_by_name";
+ ot->description = "Alternate access to 'Insert Keyframe' for keymaps to use";
+
+ /* callbacks */
+ ot->exec = insert_key_exec;
+ ot->poll = modify_key_op_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* keyingset to use (idname) */
+ prop = RNA_def_string_file_path(ot->srna, "type", "Type", MAX_ID_NAME - 2, "", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+ ot->prop = prop;
+
+ /* confirm whether a keyframe was added by showing a popup
+ * - by default, this is enabled, since this operator is assumed to be called independently
+ */
+ prop = RNA_def_boolean(ot->srna, "confirm_success", 1, "Confirm Successful Insert",
+ "Show a popup when the keyframes get successfully added");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+}
+
/* Insert Key Operator (With Menu) ------------------------ */
/* This operator checks if a menu should be shown for choosing the KeyingSet to use,
* then calls the menu if necessary before
@@ -1752,22 +1770,36 @@ void ANIM_OT_keyframe_insert_menu(wmOperatorType *ot)
static int delete_key_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- KeyingSet *ks = NULL;
- int type = RNA_enum_get(op->ptr, "type");
float cfra = (float)CFRA; // XXX for now, don't bother about all the yucky offset crap
short success;
- /* type is the Keying Set the user specified to use when calling the operator:
- * - type == 0: use scene's active Keying Set
- * - type > 0: use a user-defined Keying Set from the active scene
- * - type < 0: use a builtin Keying Set
- */
- if (type == 0)
- type = scene->active_keyingset;
- if (type > 0)
- ks = BLI_findlink(&scene->keyingsets, type - 1);
- else
- ks = BLI_findlink(&builtin_keyingsets, -type - 1);
+ KeyingSet *ks = keyingset_get_from_op_with_error(op, op->type->prop, scene);
+ if (ks == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const int prop_type = RNA_property_type(op->type->prop);
+ if (prop_type == PROP_ENUM) {
+ int type = RNA_property_enum_get(op->ptr, op->type->prop);
+ ks = ANIM_keyingset_get_from_enum_type(scene, type);
+ if (ks == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No active keying set");
+ return OPERATOR_CANCELLED;
+ }
+ }
+ else if (prop_type == PROP_STRING) {
+ char type_id[MAX_ID_NAME - 2];
+ RNA_property_string_get(op->ptr, op->type->prop, type_id);
+ ks = ANIM_keyingset_get_from_idname(scene, type_id);
+
+ if (ks == NULL) {
+ BKE_reportf(op->reports, RPT_ERROR, "No active keying set '%s' not found", type_id);
+ return OPERATOR_CANCELLED;
+ }
+ }
+ else {
+ BLI_assert(0);
+ }
/* report failure */
if (ks == NULL) {
@@ -1828,6 +1860,34 @@ void ANIM_OT_keyframe_delete(wmOperatorType *ot)
"Show a popup when the keyframes get successfully removed");
}
+void ANIM_OT_keyframe_delete_by_name(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Delete Keying-Set Keyframe (by name)";
+ ot->idname = "ANIM_OT_keyframe_delete_by_name";
+ ot->description = "Alternate access to 'Delete Keyframe' for keymaps to use";
+
+ /* callbacks */
+ ot->exec = delete_key_exec;
+ ot->poll = modify_key_op_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* keyingset to use (idname) */
+ prop = RNA_def_string_file_path(ot->srna, "type", "Type", MAX_ID_NAME - 2, "", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+ ot->prop = prop;
+
+ /* confirm whether a keyframe was added by showing a popup
+ * - by default, this is enabled, since this operator is assumed to be called independently
+ */
+ RNA_def_boolean(ot->srna, "confirm_success", 1, "Confirm Successful Delete",
+ "Show a popup when the keyframes get successfully removed");
+}
+
/* Delete Key Operator ------------------------ */
/* NOTE: Although this version is simpler than the more generic version for KeyingSets,
* it is more useful for animators working in the 3D view.
@@ -2597,3 +2657,36 @@ bool ED_autokeyframe_pchan(bContext *C, Scene *scene, Object *ob, bPoseChannel *
return false;
}
}
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Utilities
+ * \{ */
+
+/** Use for insert/delete key-frame. */
+static KeyingSet *keyingset_get_from_op_with_error(wmOperator *op, PropertyRNA *prop, Scene *scene)
+{
+ KeyingSet *ks = NULL;
+ const int prop_type = RNA_property_type(prop);
+ if (prop_type == PROP_ENUM) {
+ int type = RNA_property_enum_get(op->ptr, prop);
+ ks = ANIM_keyingset_get_from_enum_type(scene, type);
+ if (ks == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No active keying set");
+ }
+ }
+ else if (prop_type == PROP_STRING) {
+ char type_id[MAX_ID_NAME - 2];
+ RNA_property_string_get(op->ptr, prop, type_id);
+ ks = ANIM_keyingset_get_from_idname(scene, type_id);
+
+ if (ks == NULL) {
+ BKE_reportf(op->reports, RPT_ERROR, "Keying set '%s' not found", type_id);
+ }
+ }
+ else {
+ BLI_assert(0);
+ }
+ return ks;
+}
+
+/** \} */
diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c
index d532e22e7f7..654543c9d5d 100644
--- a/source/blender/editors/animation/keyingsets.c
+++ b/source/blender/editors/animation/keyingsets.c
@@ -784,6 +784,40 @@ const EnumPropertyItem *ANIM_keying_sets_enum_itemf(bContext *C, PointerRNA *UNU
return item;
}
+/**
+ * Get the keying set from enum values generated in #ANIM_keying_sets_enum_itemf.
+ *
+ * Type is the Keying Set the user specified to use when calling the operator:
+ * - type == 0: use scene's active Keying Set
+ * - type > 0: use a user-defined Keying Set from the active scene
+ * - type < 0: use a builtin Keying Set
+ */
+KeyingSet *ANIM_keyingset_get_from_enum_type(Scene *scene, int type)
+{
+ KeyingSet *ks = NULL;
+
+ if (type == 0) {
+ type = scene->active_keyingset;
+ }
+
+ if (type > 0) {
+ ks = BLI_findlink(&scene->keyingsets, type - 1);
+ }
+ else {
+ ks = BLI_findlink(&builtin_keyingsets, -type - 1);
+ }
+ return ks;
+}
+
+KeyingSet *ANIM_keyingset_get_from_idname(Scene *scene, const char *idname)
+{
+ KeyingSet *ks = BLI_findstring(&scene->keyingsets, idname, offsetof(KeyingSet, idname));
+ if (ks == NULL) {
+ ks = BLI_findstring(&builtin_keyingsets, idname, offsetof(KeyingSet, idname));
+ }
+ return ks;
+}
+
/* ******************************************* */
/* KEYFRAME MODIFICATION */
diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt
index 0dd213d3bef..d63c53a6a09 100644
--- a/source/blender/editors/armature/CMakeLists.txt
+++ b/source/blender/editors/armature/CMakeLists.txt
@@ -58,10 +58,13 @@ set(SRC
meshlaplacian.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_armature "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_armature "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c
index 6e93ecc484d..4aab80bcbdd 100644
--- a/source/blender/editors/armature/armature_select.c
+++ b/source/blender/editors/armature/armature_select.c
@@ -419,7 +419,7 @@ static EditBone *get_nearest_editbonepoint(
if (use_cycle) {
static int last_mval[2] = {-100, -100};
- if (vc->v3d->shading.type > OB_WIRE) {
+ if (!XRAY_ACTIVE(vc->v3d)) {
do_nearest = true;
if (len_manhattan_v2v2_int(vc->mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) {
do_nearest = false;
@@ -428,7 +428,7 @@ static EditBone *get_nearest_editbonepoint(
copy_v2_v2_int(last_mval, vc->mval);
}
else {
- if (vc->v3d->shading.type > OB_WIRE) {
+ if (!XRAY_ACTIVE(vc->v3d)) {
do_nearest = true;
}
}
diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c
index 5608479905a..5aec55bd1ed 100644
--- a/source/blender/editors/armature/armature_skinning.c
+++ b/source/blender/editors/armature/armature_skinning.c
@@ -318,7 +318,7 @@ static void add_verts_to_dgroups(
if ((par->pose) && (pchan = BKE_pose_channel_find_name(par->pose, bone->name))) {
if (bone->segments > 1) {
segments = bone->segments;
- BKE_pchan_bbone_spline_setup(pchan, true, bbone_array);
+ BKE_pchan_bbone_spline_setup(pchan, true, false, bbone_array);
bbone = bbone_array;
}
}
diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c
index 6b09912064e..465209eb9c5 100644
--- a/source/blender/editors/armature/armature_utils.c
+++ b/source/blender/editors/armature/armature_utils.c
@@ -547,13 +547,16 @@ EditBone *make_boneList(ListBase *edbo, ListBase *bones, struct Bone *actBone)
return active;
}
-/* This function:
- * - sets local head/tail rest locations using parent bone's arm_mat.
- * - calls BKE_armature_where_is_bone() which uses parent's transform (arm_mat) to define this bone's transform.
- * - fixes (converts) EditBone roll into Bone roll.
- * - calls again BKE_armature_where_is_bone(), since roll fiddling may have changed things for our bone...
- * Note that order is crucial here, we can only handle child if all its parents in chain have already been handled
- * (this is ensured by recursive process). */
+/**
+ * This function:
+ * - Sets local head/tail rest locations using parent bone's arm_mat.
+ * - Calls #BKE_armature_where_is_bone() which uses parent's transform (arm_mat) to define this bone's transform.
+ * - Fixes (converts) EditBone roll into Bone roll.
+ * - Calls again #BKE_armature_where_is_bone(), since roll fiddling may have changed things for our bone...
+ *
+ * \note The order is crucial here, we can only handle child
+ * if all its parents in chain have already been handled (this is ensured by recursive process).
+ */
static void armature_finalize_restpose(ListBase *bonelist, ListBase *editbonelist)
{
Bone *curBone;
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c
index 7ba6db92a47..41df3086fa8 100644
--- a/source/blender/editors/armature/pose_lib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -1118,7 +1118,7 @@ static void poselib_preview_apply(bContext *C, wmOperator *op)
else
RNA_int_set(op->ptr, "pose_index", -2); /* -2 means don't apply any pose */
- /* old optimize trick... this enforces to bypass the depgraph
+ /* old optimize trick... this enforces to bypass the depsgraph
* - note: code copied from transform_generics.c -> recalcData()
*/
// FIXME: shouldn't this use the builtin stuff?
diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c
index 8b0bfadcbe7..e6ed26bdf71 100644
--- a/source/blender/editors/armature/pose_slide.c
+++ b/source/blender/editors/armature/pose_slide.c
@@ -295,7 +295,7 @@ static void pose_slide_refresh(bContext *C, tPoseSlideOp *pso)
/**
* Although this lookup is not ideal, we won't be dealing with a lot of objects at a given time.
- * But if it comes to that we can instead store prev/next frme in the #tPChanFCurveLink.
+ * But if it comes to that we can instead store prev/next frame in the #tPChanFCurveLink.
*/
static bool pose_frame_range_from_object_get(tPoseSlideOp *pso, Object *ob, float *prevFrameF, float *nextFrameF)
{
@@ -704,7 +704,7 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
pose_slide_refresh(C, pso);
}
-/* perform autokeyframing after changes were made + confirmed */
+/* perform auto-key-framing after changes were made + confirmed */
static void pose_slide_autoKeyframe(bContext *C, tPoseSlideOp *pso)
{
/* wrapper around the generic call */
diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c
index 75a3f13ee84..25ddcee52cf 100644
--- a/source/blender/editors/armature/pose_utils.c
+++ b/source/blender/editors/armature/pose_utils.c
@@ -277,7 +277,7 @@ void poseAnim_mapping_reset(ListBase *pfLinks)
}
}
-/* perform autokeyframing after changes were made + confirmed */
+/* perform auto-key-framing after changes were made + confirmed */
void poseAnim_mapping_autoKeyframe(bContext *C, Scene *scene, ListBase *pfLinks, float cframe)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -303,7 +303,7 @@ void poseAnim_mapping_autoKeyframe(bContext *C, Scene *scene, ListBase *pfLinks,
return;
}
- /* insert keyframes as necessary if autokeyframing */
+ /* Insert keyframes as necessary if auto-key-framing. */
KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID);
ListBase dsources = {NULL, NULL};
tPChanFCurveLink *pfl;
diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt
index 9d1b463ec42..726a744dd95 100644
--- a/source/blender/editors/curve/CMakeLists.txt
+++ b/source/blender/editors/curve/CMakeLists.txt
@@ -48,10 +48,13 @@ set(SRC
curve_intern.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_curve "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_curve "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 10729ecaa5e..4aaf0dc2a2f 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -2697,7 +2697,7 @@ static void smooth_single_bezt(
}
/**
- * Same as smooth_single_bezt(), keep in sync
+ * Same as #smooth_single_bezt(), keep in sync.
*/
static void smooth_single_bp(
BPoint *bp,
diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt
index 876af10f884..04bd067b146 100644
--- a/source/blender/editors/datafiles/CMakeLists.txt
+++ b/source/blender/editors/datafiles/CMakeLists.txt
@@ -28,6 +28,9 @@ set(SRC
)
+set(LIB
+)
+
# Order matches "UI_icons.h", final name will be formatted: "icons{size}_{name}.dat"
set(ICON_NAMES
question
@@ -764,7 +767,7 @@ if(WITH_BLENDER)
# blender UI only
# blends
- data_to_c_simple(../../../../release/datafiles/preview_cycles.blend SRC)
+ data_to_c_simple(../../../../release/datafiles/preview.blend SRC)
data_to_c_simple(../../../../release/datafiles/preview_grease_pencil.blend SRC)
# images
@@ -845,4 +848,4 @@ endif()
unset(ICON_NAMES)
-blender_add_lib(bf_editor_datafiles "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_datafiles "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/gizmo_library/CMakeLists.txt b/source/blender/editors/gizmo_library/CMakeLists.txt
index 0e7b2a8be0f..0d2d9446889 100644
--- a/source/blender/editors/gizmo_library/CMakeLists.txt
+++ b/source/blender/editors/gizmo_library/CMakeLists.txt
@@ -58,6 +58,9 @@ set(SRC
gizmo_types/value2d_gizmo.c
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_gizmo_library "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_gizmo_library "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
index f31e53d2a1f..3e3aa13f184 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
@@ -369,7 +369,7 @@ static void gizmo_arrow_exit(bContext *C, wmGizmo *gz, const bool cancel)
const bool is_prop_valid = WM_gizmo_target_property_is_valid(gz_prop);
if (!cancel) {
- /* Assign incase applying the opetration needs an updated offset
+ /* Assign incase applying the operation needs an updated offset
* editmesh bisect needs this. */
if (is_prop_valid) {
const int transform_flag = RNA_enum_get(arrow->gizmo.ptr, "transform");
diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
index 977b7c7a0a5..9579f7db196 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
@@ -472,7 +472,7 @@ static int gizmo_cage3d_modal(
return OPERATOR_RUNNING_MODAL;
}
/* For transform logic to be manageable we operate in -0.5..0.5 2D space,
- * no matter the size of the rectangle, mouse coorts are scaled to unit space.
+ * no matter the size of the rectangle, mouse coords are scaled to unit space.
* The mouse coords have been projected into the matrix so we don't need to worry about axis alignment.
*
* - The cursor offset are multiplied by 'dims'.
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt
index a12dca3ad16..53b527dd1f7 100644
--- a/source/blender/editors/gpencil/CMakeLists.txt
+++ b/source/blender/editors/gpencil/CMakeLists.txt
@@ -50,8 +50,8 @@ set(SRC
gpencil_fill.c
gpencil_interpolate.c
gpencil_merge.c
- gpencil_old.c
gpencil_ops.c
+ gpencil_ops_versioning.c
gpencil_paint.c
gpencil_primitive.c
gpencil_select.c
@@ -61,10 +61,13 @@ set(SRC
gpencil_intern.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_gpencil "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_gpencil "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c
index 43e2b5bd3bf..2a7f9a715ad 100644
--- a/source/blender/editors/gpencil/annotate_draw.c
+++ b/source/blender/editors/gpencil/annotate_draw.c
@@ -1036,9 +1036,8 @@ void ED_annotation_draw_2dimage(const bContext *C)
}
if (ED_screen_animation_playing(wm)) {
- /* don't show onionskins during animation playback/scrub (i.e. it obscures the poses)
- * OpenGL Renders (i.e. final output), or depth buffer (i.e. not real strokes)
- */
+ /* Don't show onion-skins during animation playback/scrub (i.e. it obscures the poses)
+ * OpenGL Renders (i.e. final output), or depth buffer (i.e. not real strokes). */
dflag |= GP_DRAWDATA_NO_ONIONS;
}
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index b9b0c8fe788..5152bcb0a90 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -1423,34 +1423,8 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps
break;
}
case SPACE_NODE:
- {
- p->gpd->runtime.sbuffer_sflag |= GP_STROKE_2DSPACE;
- break;
- }
case SPACE_SEQ:
- {
- p->gpd->runtime.sbuffer_sflag |= GP_STROKE_2DSPACE;
- break;
- }
case SPACE_IMAGE:
- {
- SpaceImage *sima = (SpaceImage *)p->sa->spacedata.first;
-
- /* only set these flags if the image editor doesn't have an image active,
- * otherwise user will be confused by strokes not appearing after they're drawn
- *
- * Admittedly, this is a bit hacky, but it works much nicer from an ergonomic standpoint!
- */
- if (ELEM(NULL, sima, sima->image)) {
- /* make strokes be drawn in screen space */
- p->gpd->runtime.sbuffer_sflag &= ~GP_STROKE_2DSPACE;
- *(p->align_flag) &= ~GP_PROJECT_VIEWSPACE;
- }
- else {
- p->gpd->runtime.sbuffer_sflag |= GP_STROKE_2DSPACE;
- }
- break;
- }
case SPACE_CLIP:
{
p->gpd->runtime.sbuffer_sflag |= GP_STROKE_2DSPACE;
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index cd28043a6fe..de6ecfefb2f 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -63,7 +63,7 @@ static int gpencil_monkey_color(
int idx;
/* create a new one */
- ma = BKE_gpencil_handle_new_material(bmain, ob, pct->name, &idx);
+ ma = BKE_gpencil_object_material_new(bmain, ob, pct->name, &idx);
copy_v4_v4(ma->gp_style->stroke_rgba, pct->line);
copy_v4_v4(ma->gp_style->fill_rgba, pct->fill);
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index c56cb94c58d..1107819a09a 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -63,7 +63,7 @@ static int gp_stroke_material(
int idx;
/* create a new one */
- ma = BKE_gpencil_handle_new_material(bmain, ob, pct->name, &idx);
+ ma = BKE_gpencil_object_material_new(bmain, ob, pct->name, &idx);
copy_v4_v4(ma->gp_style->stroke_rgba, pct->line);
copy_v4_v4(ma->gp_style->fill_rgba, pct->fill);
diff --git a/source/blender/editors/gpencil/gpencil_armature.c b/source/blender/editors/gpencil/gpencil_armature.c
index a482eb837e7..8fff6b207ba 100644
--- a/source/blender/editors/gpencil/gpencil_armature.c
+++ b/source/blender/editors/gpencil/gpencil_armature.c
@@ -314,7 +314,7 @@ static void gpencil_add_verts_to_dgroups(
{
if (bone->segments > 1) {
segments = bone->segments;
- BKE_pchan_bbone_spline_setup(pchan, true, bbone_array);
+ BKE_pchan_bbone_spline_setup(pchan, true, false, bbone_array);
bbone = bbone_array;
}
}
diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c
index 6b646566996..e7e6d7a5f7f 100644
--- a/source/blender/editors/gpencil/gpencil_brush.c
+++ b/source/blender/editors/gpencil/gpencil_brush.c
@@ -811,7 +811,7 @@ static bool gp_brush_randomize_apply(
/* Jitter is applied perpendicular to the mouse movement vector
* - We compute all effects in screenspace (since it's easier)
* and then project these to get the points/distances in
- * viewspace as needed
+ * view-space as needed.
*/
float mvec[2], svec[2];
@@ -1029,8 +1029,8 @@ static void gp_brush_clone_init(bContext *C, tGP_BrushEditData *gso)
data->new_strokes = MEM_callocN(sizeof(bGPDstroke *) * data->totitems, "cloned strokes ptr array");
}
- /* Init colormap for mapping between the pasted stroke's source colour(names)
- * and the final colours that will be used here instead...
+ /* Init colormap for mapping between the pasted stroke's source color (names)
+ * and the final colours that will be used here instead.
*/
data->new_colors = gp_copybuf_validate_colormap(C);
}
@@ -1101,7 +1101,7 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
/* Fix color references */
Material *ma = BLI_ghash_lookup(data->new_colors, &new_stroke->mat_nr);
- gps->mat_nr = BKE_gpencil_get_material_index(ob, ma);
+ gps->mat_nr = BKE_gpencil_object_material_get_index(ob, ma);
if (!ma || gps->mat_nr) {
gps->mat_nr = 0;
}
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index e3d57149d0f..710e8a9bc1c 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -521,7 +521,7 @@ static int gp_layer_duplicate_object_exec(bContext *C, wmOperator *op)
* otherwise add the slot with the material
*/
Material *ma_src = give_current_material(ob_src, gps_src->mat_nr + 1);
- int idx = BKE_gpencil_handle_material(bmain, ob_dst, ma_src);
+ int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src);
/* reasign the stroke material to the right slot in destination object */
gps_dst->mat_nr = idx;
@@ -1379,7 +1379,7 @@ static int gp_stroke_change_color_exec(bContext *C, wmOperator *op)
}
}
/* try to find slot */
- int idx = BKE_gpencil_get_material_index(ob, ma);
+ int idx = BKE_gpencil_object_material_get_index(ob, ma);
if (idx < 0) {
return OPERATOR_CANCELLED;
}
@@ -2054,7 +2054,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op)
for (short i = 0; i < *totcol; i++) {
Material *tmp_ma = give_current_material(ob_src, i + 1);
- BKE_gpencil_handle_material(bmain, ob_dst, tmp_ma);
+ BKE_gpencil_object_material_ensure(bmain, ob_dst, tmp_ma);
}
/* duplicate bGPDlayers */
@@ -2089,7 +2089,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op)
/* reasign material. Look old material and try to find in dst */
ma_src = give_current_material(ob_src, gps->mat_nr + 1);
- gps->mat_nr = BKE_gpencil_handle_material(bmain, ob_dst, ma_src);
+ gps->mat_nr = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src);
bGPDspoint *pt;
int i;
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 99d37f87da7..e1eaedd435c 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -1080,7 +1080,7 @@ GHash *gp_copybuf_validate_colormap(bContext *C)
char *ma_name = BLI_ghashIterator_getValue(&gh_iter);
Material *ma = BLI_ghash_lookup(name_to_ma, ma_name);
- BKE_gpencil_handle_material(bmain, ob, ma);
+ BKE_gpencil_object_material_ensure(bmain, ob, ma);
/* Store this mapping (for use later when pasting) */
if (!BLI_ghash_haskey(new_colors, POINTER_FROM_INT(*key))) {
@@ -1334,7 +1334,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
/* Remap material */
Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr));
- new_stroke->mat_nr = BKE_gpencil_get_material_index(ob, ma);
+ new_stroke->mat_nr = BKE_gpencil_object_material_get_index(ob, ma);
BLI_assert(new_stroke->mat_nr >= 0); /* have to add the material first */
}
}
@@ -3957,7 +3957,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
/* add duplicate materials */
ma = give_current_material(ob, gps->mat_nr + 1); /* XXX same material can be in multiple slots */
- idx = BKE_gpencil_handle_material(bmain, ob_dst, ma);
+ idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
/* selected points mode */
if (mode == GP_SEPARATE_POINT) {
@@ -4029,7 +4029,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
continue;
}
ma = give_current_material(ob, gps->mat_nr + 1);
- gps->mat_nr = BKE_gpencil_handle_material(bmain, ob_dst, ma);
+ gps->mat_nr = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
}
}
}
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 5922872070d..e13dfb38c7d 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -1017,7 +1017,7 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
gps->flag |= GP_STROKE_CYCLIC;
gps->flag |= GP_STROKE_3DSPACE;
- gps->mat_nr = BKE_gpencil_handle_material(tgpf->bmain, tgpf->ob, tgpf->mat);
+ gps->mat_nr = BKE_gpencil_object_material_ensure(tgpf->bmain, tgpf->ob, tgpf->mat);
/* allocate memory for storage points */
gps->totpoints = tgpf->sbuffer_size;
@@ -1222,7 +1222,7 @@ static tGPDfill *gp_session_init_fill(bContext *C, wmOperator *UNUSED(op))
int totcol = tgpf->ob->totcol;
/* get color info */
- Material *ma = BKE_gpencil_current_input_brush_material(bmain, tgpf->ob, brush);
+ Material *ma = BKE_gpencil_object_material_ensure_from_active_input_brush(bmain, tgpf->ob, brush);
tgpf->mat = ma;
diff --git a/source/blender/editors/gpencil/gpencil_old.c b/source/blender/editors/gpencil/gpencil_ops_versioning.c
index 47654b69da4..9fdb5a9b174 100644
--- a/source/blender/editors/gpencil/gpencil_old.c
+++ b/source/blender/editors/gpencil/gpencil_ops_versioning.c
@@ -48,6 +48,9 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "RNA_access.h"
+#include "RNA_define.h"
+
#include "ED_object.h"
#include "ED_gpencil.h"
@@ -94,25 +97,26 @@ static bool gpencil_convert_old_files_poll(bContext *C)
return (int) (scene->gpd != NULL);
}
-static int gpencil_convert_old_files_exec(bContext *C, wmOperator *UNUSED(op))
+static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
+ const bool is_annotation = RNA_boolean_get(op->ptr, "annotation");
+ bGPdata *gpd = scene->gpd;
/* Convert grease pencil scene datablock to GP object */
- if ((scene->gpd) && (view_layer != NULL)) {
+ if ((!is_annotation) && (view_layer != NULL)) {
Object *ob;
ob = BKE_object_add_for_data(bmain, view_layer, OB_GPENCIL, "GP_Scene", &scene->gpd->id, false);
zero_v3(ob->loc);
/* convert grease pencil palettes (version >= 2.78) to materials and weights */
- bGPdata *gpd = scene->gpd;
for (const bGPDpalette *palette = gpd->palettes.first; palette; palette = palette->next) {
for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
/* create material slot */
- Material *ma = BKE_gpencil_handle_new_material(bmain, ob, palcolor->info, NULL);
+ Material *ma = BKE_gpencil_object_material_new(bmain, ob, palcolor->info, NULL);
/* copy color settings */
MaterialGPencilStyle *gp_style = ma->gp_style;
@@ -161,35 +165,32 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *UNUSED(op))
scene->gpd = NULL;
}
-#if 0 /* GPXX */
- /* Handle object-linked grease pencil datablocks */
- for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) {
- if (ob->gpd) {
- if (ob->type == OB_GPENCIL) {
- /* GP Object - remap the links */
- ob->data = ob->gpd;
- ob->gpd = NULL;
- }
- else if (ob->type == OB_EMPTY) {
- /* Empty with GP data - This should be able to be converted
- * to a GP object with little data loss
- */
- ob->data = ob->gpd;
- ob->gpd = NULL;
- ob->type = OB_GPENCIL;
- }
- else {
- /* FIXME: What to do in this case?
- *
- * We cannot create new objects for these, as we don't have a scene & scene layer
- * to put them into from here...
- */
- printf("WARNING: Old Grease Pencil data ('%s') still exists on Object '%s'\n",
- ob->gpd->id.name + 2, ob->id.name + 2);
+ if (is_annotation) {
+ for (const bGPDpalette *palette = gpd->palettes.first; palette; palette = palette->next) {
+ for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ /* fix layers */
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* unlock/unhide layer */
+ gpl->flag &= ~GP_LAYER_LOCKED;
+ gpl->flag &= ~GP_LAYER_HIDE;
+ /* set opacity to 1 */
+ gpl->opacity = 1.0f;
+ /* disable tint */
+ gpl->tintcolor[3] = 0.0f;
+ for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ if ((gps->colorname[0] != '\0') &&
+ (STREQ(gps->colorname, palcolor->info)))
+ {
+ /* copy color settings */
+ copy_v4_v4(gpl->color, palcolor->color);
+ }
+ }
+ }
+ }
}
}
}
-#endif
/* notifiers */
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -200,9 +201,9 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *UNUSED(op))
void GPENCIL_OT_convert_old_files(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Convert 2.7 Grease Pencil File";
+ ot->name = "Convert Grease Pencil";
ot->idname = "GPENCIL_OT_convert_old_files";
- ot->description = "Convert 2.7x grease pencil files to 2.8";
+ ot->description = "Convert 2.7x grease pencil files to 2.80";
/* callbacks */
ot->exec = gpencil_convert_old_files_exec;
@@ -210,4 +211,7 @@ void GPENCIL_OT_convert_old_files(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* props */
+ ot->prop = RNA_def_boolean(ot->srna, "annotation", 0, "Annotation", "Convert to Annotations");
}
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index d544bd8f9b6..daea24ecaa3 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -1223,7 +1223,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
}
/* Save material index */
- gps->mat_nr = BKE_gpencil_get_material_index_for_brush(p->ob, p->brush);
+ gps->mat_nr = BKE_gpencil_object_material_get_index_from_brush(p->ob, p->brush);
/* calculate UVs along the stroke */
ED_gpencil_calc_stroke_uv(obact, gps);
@@ -1832,7 +1832,7 @@ static void gp_init_colors(tGPsdata *p)
MaterialGPencilStyle *gp_style = NULL;
/* use brush material */
- p->material = BKE_gpencil_current_input_brush_material(p->bmain, p->ob, brush);
+ p->material = BKE_gpencil_object_material_ensure_from_active_input_brush(p->bmain, p->ob, brush);
/* assign color information to temp tGPsdata */
gp_style = p->material->gp_style;
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index f6571773706..f92398520fa 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -140,7 +140,7 @@ static void gp_init_colors(tGPDprimitive *p)
MaterialGPencilStyle *gp_style = NULL;
/* use brush material */
- p->mat = BKE_gpencil_current_input_brush_material(p->bmain, p->ob, brush);
+ p->mat = BKE_gpencil_object_material_ensure_from_active_input_brush(p->bmain, p->ob, brush);
/* assign color information to temp data */
gp_style = p->mat->gp_style;
@@ -331,7 +331,7 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
gps->flag |= GP_STROKE_3DSPACE;
- gps->mat_nr = BKE_gpencil_get_material_index(tgpi->ob, tgpi->mat);
+ gps->mat_nr = BKE_gpencil_object_material_get_index(tgpi->ob, tgpi->mat);
/* allocate memory for storage points, but keep empty */
gps->totpoints = 0;
@@ -1111,7 +1111,7 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op)
tgpi->gpd->runtime.tot_cp_points = 0;
/* getcolor info */
- tgpi->mat = BKE_gpencil_current_input_toolsettings_material(bmain, tgpi->ob, ts);
+ tgpi->mat = BKE_gpencil_object_material_ensure_from_active_input_toolsettings(bmain, tgpi->ob, ts);
/* set parameters */
tgpi->type = RNA_enum_get(op->ptr, "type");
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 6601bfe9d2f..f582b52713d 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -216,7 +216,7 @@ bGPdata *ED_gpencil_data_get_active_direct(ID *screen_id, ScrArea *sa, Scene *sc
/**
* Get the active Grease Pencil datablock
* \note This is the original (bmain) copy of the datablock, stored in files.
- * Do not use for reading evaluated copies of GP Objects data
+ * Do not use for reading evaluated copies of GP Objects data
*/
bGPdata *ED_gpencil_data_get_active(const bContext *C)
{
@@ -228,9 +228,9 @@ bGPdata *ED_gpencil_data_get_active(const bContext *C)
* Get the evaluated copy of the active Grease Pencil datablock (where applicable)
* - For the 3D View (i.e. "GP Objects"), this gives the evaluated copy of the GP datablock
* (i.e. a copy of the active GP datablock for the active object, where modifiers have been
- * applied). This is needed to correctly work with "Copy-on-Write"
+ * applied). This is needed to correctly work with "Copy-on-Write".
* - For all other editors (i.e. "GP Annotations"), this just gives the active datablock
- * like for ED_gpencil_data_get_active()
+ * like for #ED_gpencil_data_get_active()
*/
bGPdata *ED_gpencil_data_get_active_evaluated(const bContext *C)
{
@@ -640,13 +640,13 @@ void gp_point_to_xy(
/**
* Convert a Grease Pencil coordinate (i.e. can be 2D or 3D) to screenspace (2D)
*
- * Just like gp_point_to_xy(), except the resulting coordinates are floats not ints.
+ * Just like #gp_point_to_xy(), except the resulting coordinates are floats not ints.
* Use this version to solve "stair-step" artifacts which may arise when roundtripping the calculations.
*
- * \param r_x: [out] The screen-space x-coordinate of the point
- * \param r_y: [out] The screen-space y-coordinate of the point
+ * \param r_x: [out] The screen-space x-coordinate of the point.
+ * \param r_y: [out] The screen-space y-coordinate of the point.
*
- * \warning This assumes that the caller has already checked whether the stroke in question can be drawn
+ * \warning This assumes that the caller has already checked whether the stroke in question can be drawn.
*/
void gp_point_to_xy_fl(
const GP_SpaceConversion *gsc, const bGPDstroke *gps, const bGPDspoint *pt,
@@ -808,9 +808,9 @@ bool gp_point_xy_to_3d(const GP_SpaceConversion *gsc, Scene *scene, const float
* Convert tGPspoint (temporary 2D/screenspace point data used by GP modal operators)
* to 3D coordinates.
*
- * \param point2D: The screenspace 2D point data to convert
- * \param depth: Depth array (via ED_view3d_autodist_depth())
- * \param[out] r_out: The resulting 2D point data
+ * \param point2D: The screenspace 2D point data to convert.
+ * \param depth: Depth array (via #ED_view3d_autodist_depth()).
+ * \param[out] r_out: The resulting 2D point data.
*/
void gp_stroke_convertcoords_tpoint(
Scene *scene, ARegion *ar,
@@ -992,7 +992,7 @@ void ED_gp_project_stroke_to_plane(
/**
* Reproject given point to a plane locked to axis to avoid stroke offset
- * \param[in, out] pt : Point to affect
+ * \param[in,out] pt: Point to affect
*/
void ED_gp_project_point_to_plane(
const Scene *scene, const Object *ob,
@@ -1332,7 +1332,7 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob)
}
/* ensure a color exists and is assigned to object */
- BKE_gpencil_current_input_toolsettings_material(bmain, ob, ts);
+ BKE_gpencil_object_material_ensure_from_active_input_toolsettings(bmain, ob, ts);
/* ensure multiframe falloff curve */
if (ts->gp_sculpt.cur_falloff == NULL) {
@@ -1701,7 +1701,7 @@ static void gp_brush_cursor_draw(bContext *C, int x, int y, void *customdata)
}
/* get current drawing color */
- ma = BKE_gpencil_get_material_for_brush(ob, brush);
+ ma = BKE_gpencil_object_material_get_from_brush(ob, brush);
if (ma) {
gp_style = ma->gp_style;
diff --git a/source/blender/editors/include/ED_datafiles.h b/source/blender/editors/include/ED_datafiles.h
index fa082e71bf6..43d7117a1b2 100644
--- a/source/blender/editors/include/ED_datafiles.h
+++ b/source/blender/editors/include/ED_datafiles.h
@@ -29,8 +29,8 @@
extern int datatoc_startup_blend_size;
extern char datatoc_startup_blend[];
-extern int datatoc_preview_cycles_blend_size;
-extern char datatoc_preview_cycles_blend[];
+extern int datatoc_preview_blend_size;
+extern char datatoc_preview_blend[];
extern int datatoc_preview_grease_pencil_blend_size;
extern char datatoc_preview_grease_pencil_blend[];
diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h
index 78a9f13e64f..5191978c529 100644
--- a/source/blender/editors/include/ED_keyframing.h
+++ b/source/blender/editors/include/ED_keyframing.h
@@ -229,6 +229,10 @@ struct KeyingSet *ANIM_get_keyingset_for_autokeying(struct Scene *scene, const c
/* Dynamically populate an enum of Keying Sets */
const struct EnumPropertyItem *ANIM_keying_sets_enum_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free);
+/* Use to get the keying set from the int value used by enums. */
+KeyingSet *ANIM_keyingset_get_from_enum_type(struct Scene *scene, int type);
+KeyingSet *ANIM_keyingset_get_from_idname(struct Scene *scene, const char *idname);
+
/* Check if KeyingSet can be used in the current context */
bool ANIM_keyingset_context_ok_poll(struct bContext *C, struct KeyingSet *ks);
@@ -373,11 +377,12 @@ bool fcurve_frame_has_keyframe(struct FCurve *fcu, float frame, short filter);
*/
bool fcurve_is_changed(struct PointerRNA ptr, struct PropertyRNA *prop, struct FCurve *fcu, float frame);
-/* Main Keyframe Checking API call:
+/**
+ * Main Keyframe Checking API call:
* Checks whether a keyframe exists for the given ID-block one the given frame.
* - It is recommended to call this method over the other keyframe-checkers directly,
* in case some detail of the implementation changes...
- * - frame: the value of this is quite often result of BKE_scene_frame_get()
+ * - frame: the value of this is quite often result of #BKE_scene_frame_get()
*/
bool id_frame_has_keyframe(struct ID *id, float frame, short filter);
diff --git a/source/blender/editors/include/ED_numinput.h b/source/blender/editors/include/ED_numinput.h
index 5f90996988a..d4cb2110c2c 100644
--- a/source/blender/editors/include/ED_numinput.h
+++ b/source/blender/editors/include/ED_numinput.h
@@ -72,15 +72,18 @@ enum {
struct UnitSettings;
-/*********************** NumInput ********************************/
-
-/* There are important things to note here for code using numinput:
- * * Values passed to applyNumInput() should be valid and are stored as default ones (val_org), if it is not EDITED.
- * * bool returned by applyNumInput should be used to decide whether to apply numinput-specific post-process to data.
- * * *Once applyNumInput has been called*, hasNumInput returns a valid value to decide whether to use numinput
- * as drawstr source or not (i.e. to call outputNumInput).
+/* -------------------------------------------------------------------- */
+/** \name NumInput
+ * \{ */
+
+/**
+ * There are important things to note here for code using numinput:
+ * - Values passed to #applyNumInput() should be valid and are stored as default ones (val_org), if it is not EDITED.
+ * - bool returned by #applyNumInput should be used to decide whether to apply numinput-specific post-process to data.
+ * - Once #applyNumInput has been called, #hasNumInput returns a valid value to decide whether to use numinput
+ * as drawstr source or not (i.e. to call #outputNumInput).
*
- * Those two steps have to be separated (so do not use a common call to hasNumInput() to do both in the same time!).
+ * Those two steps have to be separated (so do not use a common call to #hasNumInput() to do both in the same time!).
*/
void initNumInput(NumInput *n);
@@ -94,4 +97,6 @@ bool handleNumInput(struct bContext *C, NumInput *n, const struct wmEvent *event
bool user_string_to_number(bContext *C, const char *str, const struct UnitSettings *unit, int type, double *r_value);
+/** \} */
+
#endif /* __ED_NUMINPUT_H__ */
diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h
index 1bd8782bb12..18573e92926 100644
--- a/source/blender/editors/include/ED_transform.h
+++ b/source/blender/editors/include/ED_transform.h
@@ -82,6 +82,7 @@ enum TfmMode {
TFM_SEQ_SLIDE,
TFM_BONE_ENVELOPE_DIST,
TFM_NORMAL_ROTATION,
+ TFM_GPENCIL_OPACITY,
};
/* TRANSFORM CONTEXTS */
@@ -154,7 +155,8 @@ void Transform_Properties(struct wmOperatorType *ot, int flags);
/* transform gizmos */
-void TRANSFORM_GGT_gizmo(struct wmGizmoGroupType *gzgt);
+void VIEW3D_GGT_xform_gizmo(struct wmGizmoGroupType *gzgt);
+void VIEW3D_GGT_xform_gizmo_context(struct wmGizmoGroupType *gzgt);
void VIEW3D_GGT_xform_cage(struct wmGizmoGroupType *gzgt);
void VIEW3D_GGT_xform_shear(struct wmGizmoGroupType *gzgt);
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 2a115f49c31..4c03be3bd5a 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -556,8 +556,11 @@ void ED_view3d_operator_properties_viewmat_get(struct wmOperator *op, int *winx,
void ED_view3d_stop_render_preview(struct wmWindowManager *wm, struct ARegion *ar);
void ED_view3d_shade_update(struct Main *bmain, struct View3D *v3d, struct ScrArea *sa);
-#define V3D_XRAY_FLAG(v3d) (((v3d)->shading.type == OB_WIRE) ? V3D_SHADING_XRAY_BONE : V3D_SHADING_XRAY)
-#define V3D_IS_ZBUF(v3d) (((v3d)->shading.flag & V3D_XRAY_FLAG(v3d)) == 0)
+#define XRAY_ALPHA(v3d) (((v3d)->shading.type == OB_WIRE) ? (v3d)->shading.xray_alpha_wire : (v3d)->shading.xray_alpha)
+#define XRAY_FLAG(v3d) (((v3d)->shading.type == OB_WIRE) ? V3D_SHADING_XRAY_BONE : V3D_SHADING_XRAY)
+#define XRAY_FLAG_ENABLED(v3d) (((v3d)->shading.flag & XRAY_FLAG(v3d)) != 0)
+#define XRAY_ENABLED(v3d) (XRAY_FLAG_ENABLED(v3d) && (XRAY_ALPHA(v3d) < 1.0f))
+#define XRAY_ACTIVE(v3d) (XRAY_ENABLED(v3d) && ((v3d)->shading.type < OB_MATERIAL))
/* view3d_draw_legacy.c */
/* Try avoid using these more move out of legacy. */
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 9f189b63ad5..4a3e0983989 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -172,21 +172,21 @@ enum {
/* but->flag - general state flags. */
enum {
- /** warning, the first 6 flags are internal. */
+ /** Warning, the first 6 flags are internal. */
UI_BUT_ICON_SUBMENU = 1 << 6,
UI_BUT_ICON_PREVIEW = 1 << 7,
UI_BUT_NODE_LINK = 1 << 8,
UI_BUT_NODE_ACTIVE = 1 << 9,
UI_BUT_DRAG_LOCK = 1 << 10,
- /** grayed out and uneditable */
+ /** Grayed out and un-editable. */
UI_BUT_DISABLED = 1 << 11,
UI_BUT_ANIMATED = 1 << 13,
UI_BUT_ANIMATED_KEY = 1 << 14,
UI_BUT_DRIVEN = 1 << 15,
UI_BUT_REDALERT = 1 << 16,
- /** grayed out but still editable */
+ /** Grayed out but still editable. */
UI_BUT_INACTIVE = 1 << 17,
UI_BUT_LAST_ACTIVE = 1 << 18,
UI_BUT_UNDO = 1 << 19,
diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt
index 1ecd9e64924..78b0202dc22 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -79,6 +79,9 @@ set(SRC
interface_regions_intern.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
@@ -103,4 +106,4 @@ endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_interface "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_interface "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/interface/interface_align.c b/source/blender/editors/interface/interface_align.c
index 8e7dc792da6..9e1563e6511 100644
--- a/source/blender/editors/interface/interface_align.c
+++ b/source/blender/editors/interface/interface_align.c
@@ -260,7 +260,7 @@ static void block_align_proximity_compute(ButAlign *butal, ButAlign *butal_other
* Here, BUT 3 RIGHT side would not get 'dragged' to align with BUT 1 RIGHT side, since BUT 3 has not RIGHT neighbor.
* So, this function, when called with BUT 1, will 'walk' the whole column in \a side_s1 direction (TOP or DOWN when
* called for RIGHT side), and force buttons like BUT 3 to align as needed, if BUT 1 and BUT 3 were detected as needing
- * top-right corner stitching in \a block_align_proximity_compute() step.
+ * top-right corner stitching in #block_align_proximity_compute() step.
*
* \note To avoid doing this twice, some stitching flags are cleared to break the 'stitching connection'
* between neighbors.
diff --git a/source/blender/editors/interface/interface_region_popover.c b/source/blender/editors/interface/interface_region_popover.c
index 7ca6db2ca21..7396b5406ed 100644
--- a/source/blender/editors/interface/interface_region_popover.c
+++ b/source/blender/editors/interface/interface_region_popover.c
@@ -182,7 +182,7 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v
}
/* Prefer popover from header to be positioned into the editor. */
else if (sa && ar) {
- if (ar->regiontype == RGN_TYPE_HEADER){
+ if (ar->regiontype == RGN_TYPE_HEADER) {
if (ED_area_header_alignment(sa) == RGN_ALIGN_BOTTOM) {
UI_block_direction_set(block, UI_DIR_UP | UI_DIR_CENTER_X);
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 0bb1497a74e..897a07708d0 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -713,8 +713,10 @@ static void template_ID(
but = uiDefIconBut(
block, UI_BTYPE_BUT, 0, ICON_LIBRARY_DATA_DIRECT, 0, 0, UI_UNIT_X, UI_UNIT_Y,
NULL, 0, 0, 0, 0,
- TIP_("Direct linked library data-block, click to make local, "
- "Shift + Click to create a static override"));
+ BKE_override_static_is_enabled() ?
+ TIP_("Direct linked library data-block, click to make local, "
+ "Shift + Click to create a static override") :
+ TIP_("Direct linked library data-block, click to make local"));
if (disabled) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
@@ -2213,7 +2215,7 @@ void uiTemplatePreview(
if (!ui_preview) {
ui_preview = MEM_callocN(sizeof(uiPreview), "uiPreview");
BLI_strncpy(ui_preview->preview_id, preview_id, sizeof(ui_preview->preview_id));
- ui_preview->height = (short)(UI_UNIT_Y * 5.6f);
+ ui_preview->height = (short)(UI_UNIT_Y * 7.6f);
BLI_addtail(&ar->ui_previews, ui_preview);
}
@@ -2255,6 +2257,8 @@ void uiTemplatePreview(
col = uiLayoutColumn(row, true);
uiLayoutSetScaleX(col, 1.5);
uiItemR(col, &material_ptr, "preview_render_type", UI_ITEM_R_EXPAND, "", ICON_NONE);
+ uiItemS(col);
+ uiItemR(col, &material_ptr, "use_preview_world", 0, "", ICON_WORLD);
}
if (pr_texture) {
diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c
index 65abac968e3..915793445db 100644
--- a/source/blender/editors/interface/interface_utils.c
+++ b/source/blender/editors/interface/interface_utils.c
@@ -209,7 +209,7 @@ eAutoPropButsReturn uiDefAutoButsRNA(
col = uiLayoutColumn(split, false);
}
- /* may meed to add more cases here.
+ /* May need to add more cases here.
* don't override enum flag names */
/* name is shown above, empty name for button below */
diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt
index 2235e94180d..1aa6de534c4 100644
--- a/source/blender/editors/io/CMakeLists.txt
+++ b/source/blender/editors/io/CMakeLists.txt
@@ -46,6 +46,9 @@ set(SRC
io_ops.h
)
+set(LIB
+)
+
if(WITH_OPENCOLLADA)
add_definitions(-DWITH_COLLADA)
endif()
@@ -62,4 +65,4 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
-blender_add_lib(bf_editor_io "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_io "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/lattice/CMakeLists.txt b/source/blender/editors/lattice/CMakeLists.txt
index f14de01c102..5a231837d0b 100644
--- a/source/blender/editors/lattice/CMakeLists.txt
+++ b/source/blender/editors/lattice/CMakeLists.txt
@@ -41,4 +41,7 @@ set(SRC
lattice_intern.h
)
-blender_add_lib(bf_editor_lattice "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_editor_lattice "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/lattice/editlattice_tools.c b/source/blender/editors/lattice/editlattice_tools.c
index 0d57b94a493..edb79797c2a 100644
--- a/source/blender/editors/lattice/editlattice_tools.c
+++ b/source/blender/editors/lattice/editlattice_tools.c
@@ -133,9 +133,9 @@ typedef enum eLattice_FlipAxes {
} eLattice_FlipAxes;
/**
- * Flip midpoint value so that relative distances between midpoint and neighbor-pair is maintained
- * ! Assumes that uvw <=> xyz (i.e. axis-aligned index-axes with coordinate-axes)
- * - Helper for lattice_flip_exec()
+ * Flip midpoint value so that relative distances between midpoint and neighbor-pair is maintained.
+ * Assumes that uvw <=> xyz (i.e. axis-aligned index-axes with coordinate-axes).
+ * - Helper for #lattice_flip_exec()
*/
static void lattice_flip_point_value(Lattice *lt, int u, int v, int w, float mid, eLattice_FlipAxes axis)
{
@@ -151,8 +151,8 @@ static void lattice_flip_point_value(Lattice *lt, int u, int v, int w, float mid
}
/**
- * Swap pairs of lattice points along a specified axis
- * - Helper for lattice_flip_exec()
+ * Swap pairs of lattice points along a specified axis.
+ * - Helper for #lattice_flip_exec()
*/
static void lattice_swap_point_pairs(Lattice *lt, int u, int v, int w, float mid, eLattice_FlipAxes axis)
{
diff --git a/source/blender/editors/mask/CMakeLists.txt b/source/blender/editors/mask/CMakeLists.txt
index 63f8dc78d13..a8ed6812897 100644
--- a/source/blender/editors/mask/CMakeLists.txt
+++ b/source/blender/editors/mask/CMakeLists.txt
@@ -47,6 +47,9 @@ set(SRC
mask_intern.h
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_mask "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_mask "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt
index bc8761f24c0..41e247b8d77 100644
--- a/source/blender/editors/mesh/CMakeLists.txt
+++ b/source/blender/editors/mesh/CMakeLists.txt
@@ -72,6 +72,9 @@ set(SRC
mesh_intern.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
@@ -86,4 +89,4 @@ endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_mesh "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_mesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
index 9884ded4f0a..1ff7d835aad 100644
--- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
+++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
@@ -241,7 +241,7 @@ static void gizmo_mesh_spin_init_draw_prepare(
{
Scene *scene = CTX_data_scene(C);
- const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, SCE_GIZMO_SHOW_ROTATE);
+ const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, SCE_ORIENT_ROTATE);
switch (orient_slot->type) {
case V3D_ORIENT_VIEW:
{
diff --git a/source/blender/editors/mesh/editmesh_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c
index 19831e8f380..7decd5f4b2f 100644
--- a/source/blender/editors/mesh/editmesh_polybuild.c
+++ b/source/blender/editors/mesh/editmesh_polybuild.c
@@ -91,9 +91,7 @@ static bool edbm_preselect_or_active(
BMElem **r_ele)
{
ARegion *ar = CTX_wm_region(C);
- const bool show_gizmo = !(
- (v3d->flag2 & V3D_HIDE_OVERLAYS) ||
- (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)));
+ const bool show_gizmo = !((v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)));
wmGizmoMap *gzmap = show_gizmo ? ar->gizmo_map : NULL;
wmGizmoGroup *gzgroup = gzmap ? WM_gizmomap_group_find(gzmap, "VIEW3D_GGT_mesh_preselect_elem") : NULL;
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index 1855eeab944..8b75b511319 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -215,7 +215,7 @@ bool EDBM_backbuf_border_init(
{
uint *buf, *dr, buf_len;
- if (vc->obedit == NULL || !V3D_IS_ZBUF(vc->v3d)) {
+ if (vc->obedit == NULL || XRAY_FLAG_ENABLED(vc->v3d)) {
return false;
}
@@ -292,7 +292,7 @@ bool EDBM_backbuf_border_mask_init(ViewContext *vc, const int mcords[][2], short
return false;
}
}
- else if (!V3D_IS_ZBUF(vc->v3d)) {
+ else if (XRAY_FLAG_ENABLED(vc->v3d)) {
return false;
}
@@ -342,7 +342,7 @@ bool EDBM_backbuf_circle_init(
return false;
}
}
- else if (!V3D_IS_ZBUF(vc->v3d)) {
+ else if (XRAY_FLAG_ENABLED(vc->v3d)) {
return false;
}
@@ -454,8 +454,8 @@ static void findnearestvert__doClosest(void *userData, BMVert *eve, const float
*
* \param r_dist: (in/out), minimal distance to the nearest and at the end, actual distance
* \param use_select_bias:
- * - When true, selected vertice are given a 5 pixel bias to make them further than unselect verts.
- * - When false, unselected vertice are given the bias.
+ * - When true, selected vertices are given a 5 pixel bias to make them further than unselect verts.
+ * - When false, unselected vertices are given the bias.
* \param use_cycle: Cycle over elements within #FIND_NEAR_CYCLE_THRESHOLD_MIN in order of index.
*/
BMVert *EDBM_vert_find_nearest_ex(
@@ -464,7 +464,7 @@ BMVert *EDBM_vert_find_nearest_ex(
{
BMesh *bm = vc->em->bm;
- if (V3D_IS_ZBUF(vc->v3d)) {
+ if (!XRAY_FLAG_ENABLED(vc->v3d)) {
uint dist_px = (uint)ED_view3d_backbuf_sample_size_clamp(vc->ar, *r_dist);
unsigned int index;
BMVert *eve;
@@ -655,7 +655,7 @@ BMEdge *EDBM_edge_find_nearest_ex(
{
BMesh *bm = vc->em->bm;
- if (V3D_IS_ZBUF(vc->v3d)) {
+ if (!XRAY_FLAG_ENABLED(vc->v3d)) {
uint dist_px = (uint)ED_view3d_backbuf_sample_size_clamp(vc->ar, *r_dist);
unsigned int index;
BMEdge *eed;
@@ -825,7 +825,7 @@ BMFace *EDBM_face_find_nearest_ex(
{
BMesh *bm = vc->em->bm;
- if (V3D_IS_ZBUF(vc->v3d)) {
+ if (!XRAY_FLAG_ENABLED(vc->v3d)) {
float dist_test = 0.0f;
unsigned int index;
BMFace *efa;
@@ -1757,7 +1757,7 @@ static bool mouse_mesh_loop(bContext *C, const int mval[2], bool extend, bool de
if (select) {
if (em->selectmode & SCE_SELECT_VERTEX) {
/* Find nearest vert from mouse
- * (initialize to large values incase only one vertex can be projected) */
+ * (initialize to large values in case only one vertex can be projected) */
float v1_co[2], v2_co[2];
float length_1 = FLT_MAX;
float length_2 = FLT_MAX;
diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c
index c9365e8d0fd..67e455375df 100644
--- a/source/blender/editors/mesh/editmesh_select_similar.c
+++ b/source/blender/editors/mesh/editmesh_select_similar.c
@@ -1150,7 +1150,7 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
continue;
}
- /* We map back the names of the vertex groups to their corresponsing indices
+ /* We map back the names of the vertex groups to their corresponding indices
* for this object. This is fast, and keep the logic for each vertex very simple. */
GSetIterator gs_iter;
GSET_ITER(gs_iter, gset) {
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 5d653ffe00b..9fb200951ca 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -4943,7 +4943,7 @@ static int edbm_decimate_exec(bContext *C, wmOperator *op)
}
else {
/**
- * Calculate a new ratio based on faces that could be remoevd during decimation.
+ * Calculate a new ratio based on faces that could be removed during decimation.
* needed so 0..1 has a meaningful range when operating on the selection.
*
* This doesn't have to be totally accurate,
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 3a4f9d461ec..f86a2388f87 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -919,8 +919,10 @@ UvElement *BM_uv_element_get(UvElementMap *map, BMFace *efa, BMLoop *l)
/** \name Data Layer Checks
* \{ */
-/* last_sel, use em->act_face otherwise get the last selected face in the editselections
- * at the moment, last_sel is mainly useful for making sure the space image dosnt flicker */
+/**
+ * last_sel, use em->act_face otherwise get the last selected face in the editselections
+ * at the moment, last_sel is mainly useful for making sure the space image doesn't flicker.
+ */
BMFace *EDBM_uv_active_face_get(BMEditMesh *em, const bool sloppy, const bool selected)
{
BMFace *efa = NULL;
diff --git a/source/blender/editors/mesh/mesh_mirror.c b/source/blender/editors/mesh/mesh_mirror.c
index a1859bf70b0..8bfe51d09fa 100644
--- a/source/blender/editors/mesh/mesh_mirror.c
+++ b/source/blender/editors/mesh/mesh_mirror.c
@@ -267,15 +267,15 @@ void ED_mesh_mirrtopo_init(
}
if ((tot_unique <= tot_unique_prev) && (tot_unique_edges <= tot_unique_edges_prev)) {
- /* Finish searching for unique values when 1 loop dosnt give a
- * higher number of unique values compared to the previous loop */
+ /* Finish searching for unique values when 1 loop dosn't give a
+ * higher number of unique values compared to the previous loop. */
break;
}
else {
tot_unique_prev = tot_unique;
tot_unique_edges_prev = tot_unique_edges;
}
- /* Copy the hash calculated this iter, so we can use them next time */
+ /* Copy the hash calculated this iteration, so we can use them next time */
memcpy(topo_hash_prev, topo_hash, sizeof(MirrTopoHash_t) * totvert);
topo_pass++;
diff --git a/source/blender/editors/metaball/CMakeLists.txt b/source/blender/editors/metaball/CMakeLists.txt
index a0d6fb928ff..11547a0af6f 100644
--- a/source/blender/editors/metaball/CMakeLists.txt
+++ b/source/blender/editors/metaball/CMakeLists.txt
@@ -40,4 +40,7 @@ set(SRC
mball_intern.h
)
-blender_add_lib(bf_editor_metaball "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_editor_metaball "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index f15427e61ac..7a5eee4bfbb 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -68,6 +68,9 @@ set(SRC
object_intern.h
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
if(WITH_PYTHON)
@@ -78,4 +81,4 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
-blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index d069772a0ce..6d3cca5477e 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -151,7 +151,7 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op)
EDBM_mesh_normals_update(em);
BKE_editmesh_tessface_calc(em);
- /* Make sure the evaluated mesh is updates.
+ /* Make sure the evaluated mesh is updated.
*
* Most reliable way is to update the tagged objects, which will ensure
* proper copy-on-write update, but also will make sure all dependent
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index c0e022df629..cd8b7d082ef 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -247,7 +247,7 @@ bool ED_vgroup_parray_alloc(ID *id, MDeformVert ***dvert_arr, int *dvert_tot, co
* For use with tools that use ED_vgroup_parray_alloc with \a use_vert_sel == true.
* This finds the unselected mirror deform verts and copies the weights to them from the selected.
*
- * \note \a dvert_array has mirrored weights filled in, incase cleanup operations are needed on both.
+ * \note \a dvert_array has mirrored weights filled in, in case cleanup operations are needed on both.
*/
void ED_vgroup_parray_mirror_sync(
Object *ob,
diff --git a/source/blender/editors/physics/CMakeLists.txt b/source/blender/editors/physics/CMakeLists.txt
index 88d6f1b015c..1b42c3f2669 100644
--- a/source/blender/editors/physics/CMakeLists.txt
+++ b/source/blender/editors/physics/CMakeLists.txt
@@ -51,6 +51,9 @@ set(SRC
physics_intern.h
)
+set(LIB
+)
+
if(WITH_MOD_FLUID)
add_definitions(-DWITH_MOD_FLUID)
endif()
@@ -68,4 +71,4 @@ endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_physics "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_physics "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index 112d453c44a..bd78677bdb6 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -446,7 +446,7 @@ static void PE_set_view3d_data(bContext *C, PEData *data)
ED_view3d_viewcontext_init(C, &data->vc);
- if (V3D_IS_ZBUF(data->vc.v3d)) {
+ if (!XRAY_ENABLED(data->vc.v3d)) {
if (data->vc.v3d->flag & V3D_INVALID_BACKBUF) {
/* needed or else the draw matrix can be incorrect */
view3d_operator_needs_opengl(C);
@@ -503,7 +503,7 @@ static bool key_test_depth(const PEData *data, const float co[3], const int scre
float depth;
/* nothing to do */
- if (!V3D_IS_ZBUF(v3d))
+ if (XRAY_ENABLED(v3d))
return true;
/* used to calculate here but all callers have the screen_co already, so pass as arg */
@@ -3965,7 +3965,7 @@ static int brush_add(const bContext *C, PEData *data, short number)
}
BLI_assert(mesh);
- /* Calculate positions of new particles to add, based on brush interseciton
+ /* Calculate positions of new particles to add, based on brush intersection
* with object. New particle data is assigned to a corresponding to check
* index element of add_pars array. This means, that add_pars is a sparse
* array.
@@ -3993,7 +3993,7 @@ static int brush_add(const bContext *C, PEData *data, short number)
BLI_task_parallel_range(0, number, &iter_data, brush_add_count_iter, &settings);
/* Convert add_parse to a dense array, where all new particles are in the
- * beginnign of the array.
+ * beginning of the array.
*/
n = iter_data.num_added;
for (int current_iter = 0, new_index = 0; current_iter < number; current_iter++) {
diff --git a/source/blender/editors/render/CMakeLists.txt b/source/blender/editors/render/CMakeLists.txt
index c65cada1cc0..dee56d7f2fa 100644
--- a/source/blender/editors/render/CMakeLists.txt
+++ b/source/blender/editors/render/CMakeLists.txt
@@ -50,6 +50,9 @@ set(SRC
render_intern.h
)
+set(LIB
+)
+
if(WITH_HEADLESS)
add_definitions(-DWITH_HEADLESS)
endif()
@@ -67,4 +70,4 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
-blender_add_lib(bf_editor_render "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_render "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 0b0acbffec9..77b24a1b4d9 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -189,7 +189,7 @@ typedef struct IconPreview {
/* *************************** Preview for buttons *********************** */
-static Main *G_pr_main_cycles = NULL;
+static Main *G_pr_main = NULL;
static Main *G_pr_main_grease_pencil = NULL;
#ifndef WITH_HEADLESS
@@ -218,19 +218,13 @@ void ED_preview_ensure_dbase(void)
static bool base_initialized = false;
BLI_assert(BLI_thread_is_main());
if (!base_initialized) {
- G_pr_main_cycles = load_main_from_memory(datatoc_preview_cycles_blend, datatoc_preview_cycles_blend_size);
+ G_pr_main = load_main_from_memory(datatoc_preview_blend, datatoc_preview_blend_size);
G_pr_main_grease_pencil = load_main_from_memory(datatoc_preview_grease_pencil_blend, datatoc_preview_grease_pencil_blend_size);
base_initialized = true;
}
#endif
}
-static bool check_engine_supports_textures(Scene *scene)
-{
- RenderEngineType *type = RE_engines_find(scene->r.engine);
- return (type->flag & RE_USE_TEXTURE_PREVIEW) != 0;
-}
-
static bool check_engine_supports_preview(Scene *scene)
{
RenderEngineType *type = RE_engines_find(scene->r.engine);
@@ -239,8 +233,8 @@ static bool check_engine_supports_preview(Scene *scene)
void ED_preview_free_dbase(void)
{
- if (G_pr_main_cycles)
- BKE_main_free(G_pr_main_cycles);
+ if (G_pr_main)
+ BKE_main_free(G_pr_main);
if (G_pr_main_grease_pencil)
BKE_main_free(G_pr_main_grease_pencil);
@@ -262,14 +256,16 @@ static const char *preview_collection_name(const char pr_type)
return "Sphere";
case MA_CUBE:
return "Cube";
- case MA_MONKEY:
- return "Monkey";
+ case MA_SHADERBALL:
+ return "Shader Ball";
+ case MA_CLOTH:
+ return "Cloth";
+ case MA_FLUID:
+ return "Fluid";
case MA_SPHERE_A:
- return "World Sphere";
- case MA_TEXTURE:
- return "Texture";
+ return "World Shader Ball";
case MA_LAMP:
- return "Light";
+ return "Lamp";
case MA_SKY:
return "Sky";
case MA_HAIR:
@@ -282,8 +278,9 @@ static const char *preview_collection_name(const char pr_type)
}
}
-static void set_preview_collection(Scene *scene, ViewLayer *view_layer, char pr_type)
+static void set_preview_visibility(Scene *scene, ViewLayer *view_layer, char pr_type, int pr_method)
{
+ /* Set appropriate layer as visibile. */
LayerCollection *lc = view_layer->layer_collections.first;
const char *collection_name = preview_collection_name(pr_type);
@@ -296,6 +293,18 @@ static void set_preview_collection(Scene *scene, ViewLayer *view_layer, char pr_
}
}
+ /* Hide floor for icon renders. */
+ for (Base *base = view_layer->object_bases.first; base; base = base->next) {
+ if (STREQ(base->object->id.name + 2, "Floor")) {
+ if (pr_method == PR_ICON_RENDER) {
+ base->object->restrictflag |= OB_RESTRICT_RENDER;
+ }
+ else {
+ base->object->restrictflag &= ~OB_RESTRICT_RENDER;
+ }
+ }
+ }
+
BKE_layer_collection_sync(scene, view_layer);
}
@@ -389,11 +398,8 @@ static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_ty
sce->r.cfra = scene->r.cfra;
- if (id_type == ID_TE && !check_engine_supports_textures(scene)) {
- /* Force blender internal for texture icons and nodes render,
- * seems commonly used render engines does not support
- * such kind of rendering.
- */
+ if (id_type == ID_TE) {
+ /* Texture is not actually rendered with engine, just set dummy value. */
BLI_strncpy(sce->r.engine, RE_engine_id_BLENDER_EEVEE, sizeof(sce->r.engine));
}
else {
@@ -410,8 +416,8 @@ static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_ty
sp->id_copy = NULL;
BLI_addtail(&pr_main->materials, mat);
- /* use current scene world to light sphere */
- if (mat->pr_type == MA_SPHERE_A && sp->pr_method == PR_BUTS_RENDER) {
+ /* Use current scene world for lighting. */
+ if (mat->pr_flag == MA_PREVIEW_WORLD && sp->pr_method == PR_BUTS_RENDER) {
/* Use current scene world to light sphere. */
sce->world = preview_get_localized_world(sp, scene->world);
}
@@ -419,17 +425,14 @@ static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_ty
/* Use a default world color. Using the current
* scene world can be slow if it has big textures. */
sce->world->use_nodes = false;
- sce->world->horr = 0.5f;
- sce->world->horg = 0.5f;
- sce->world->horb = 0.5f;
+ sce->world->horr = 0.05f;
+ sce->world->horg = 0.05f;
+ sce->world->horb = 0.05f;
}
- if (sp->pr_method == PR_ICON_RENDER) {
- set_preview_collection(sce, view_layer, MA_SPHERE_A);
- }
- else {
- set_preview_collection(sce, view_layer, mat->pr_type);
+ set_preview_visibility(sce, view_layer, mat->pr_type, sp->pr_method);
+ if (sp->pr_method != PR_ICON_RENDER) {
if (mat->nodetree && sp->pr_method == PR_NODE_RENDER) {
/* two previews, they get copied by wmJob */
BKE_node_preview_init_tree(mat->nodetree, sp->sizex, sp->sizey, true);
@@ -470,7 +473,6 @@ static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_ty
sp->id_copy = NULL;
BLI_addtail(&pr_main->textures, tex);
}
- set_preview_collection(sce, view_layer, MA_TEXTURE);
if (tex && tex->nodetree && sp->pr_method == PR_NODE_RENDER) {
/* two previews, they get copied by wmJob */
@@ -490,7 +492,7 @@ static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_ty
BLI_addtail(&pr_main->lights, la);
}
- set_preview_collection(sce, view_layer, MA_LAMP);
+ set_preview_visibility(sce, view_layer, MA_LAMP, sp->pr_method);
if (sce->world) {
/* Only use lighting from the light. */
@@ -524,7 +526,7 @@ static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_ty
BLI_addtail(&pr_main->worlds, wrld);
}
- set_preview_collection(sce, view_layer, MA_SKY);
+ set_preview_visibility(sce, view_layer, MA_SKY, sp->pr_method);
sce->world = wrld;
if (wrld && wrld->nodetree && sp->pr_method == PR_NODE_RENDER) {
@@ -1178,7 +1180,7 @@ static void icon_preview_startjob_all_sizes(void *customdata, short *stop, short
}
if ((ma == NULL) || (ma->gp_style == NULL)) {
- sp->pr_main = G_pr_main_cycles;
+ sp->pr_main = G_pr_main;
}
else {
sp->pr_main = G_pr_main_grease_pencil;
@@ -1359,7 +1361,7 @@ void ED_preview_shader_job(const bContext *C, void *owner, ID *id, ID *parent, M
}
if ((ma == NULL) || (ma->gp_style == NULL)) {
- sp->pr_main = G_pr_main_cycles;
+ sp->pr_main = G_pr_main;
}
else {
sp->pr_main = G_pr_main_grease_pencil;
diff --git a/source/blender/editors/scene/CMakeLists.txt b/source/blender/editors/scene/CMakeLists.txt
index 7f496cad060..0768e0ee571 100644
--- a/source/blender/editors/scene/CMakeLists.txt
+++ b/source/blender/editors/scene/CMakeLists.txt
@@ -34,8 +34,11 @@ set(SRC
scene_edit.c
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
-blender_add_lib(bf_editor_scene "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_scene "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/screen/CMakeLists.txt b/source/blender/editors/screen/CMakeLists.txt
index 810adba3288..a4a0b873c92 100644
--- a/source/blender/editors/screen/CMakeLists.txt
+++ b/source/blender/editors/screen/CMakeLists.txt
@@ -54,10 +54,13 @@ set(SRC
screen_intern.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_screen "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_screen "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index 0150127d6fb..e01f6e04086 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -446,7 +446,7 @@ void ED_area_do_msg_notify_tag_refresh(
/**
* Although there's no general support for minimizing areas, the status-bar can
* be snapped to be only a few pixels high. A few pixels rather than 0 so it
- * can be un-minimized again. We consider it pseudo-minimalized and don't draw
+ * can be un-minimized again. We consider it pseudo-minimized and don't draw
* it then.
*/
static bool area_is_pseudo_minimized(const ScrArea *area)
@@ -1913,7 +1913,7 @@ void ED_area_newspace(bContext *C, ScrArea *sa, int type, const bool skip_ar_exi
/* Sync header alignment. */
if (sync_header_alignment) {
/* Spaces with footer. */
- if (st->spaceid == SPACE_TEXT){
+ if (st->spaceid == SPACE_TEXT) {
for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
if (ar->regiontype == RGN_TYPE_HEADER) {
ar->alignment = header_alignment;
@@ -2573,7 +2573,7 @@ int ED_area_footer_alignment_or_fallback(const ScrArea *area, int fallback)
int ED_area_footer_alignment(const ScrArea *area)
{
return ED_area_footer_alignment_or_fallback(
- area, (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_TOP : RGN_ALIGN_BOTTOM);
+ area, (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_TOP : RGN_ALIGN_BOTTOM);
}
/**
diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c
index 230d6179871..1606a159e38 100644
--- a/source/blender/editors/screen/glutil.c
+++ b/source/blender/editors/screen/glutil.c
@@ -286,6 +286,13 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state,
immAttr2f(texco, (float)(0 + offset_left) / tex_w, (float)(subpart_h - offset_top) / tex_h);
immVertex2f(pos, rast_x + (float)offset_left * xzoom, rast_y + (float)(subpart_h - offset_top) * yzoom * scaleY);
immEnd();
+
+ /* NOTE: Weirdly enough this is only required on macOS. Without this there is some sort of
+ * bleeding of data is happening from tiles which are drawn later on.
+ * This doesn't seem to be too slow, but still would be nice to have fast and nice solution. */
+#ifdef __APPLE__
+ GPU_flush();
+#endif
}
}
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 41ce5455c3b..031eee3c9a4 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -1263,8 +1263,10 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s
for (ar = newa->regionbase.first; ar; ar = ar->next) {
ar->flagfullscreen = ar->flag;
- if (ELEM(ar->regiontype, RGN_TYPE_UI, RGN_TYPE_HEADER, RGN_TYPE_FOOTER,
- RGN_TYPE_TOOLS, RGN_TYPE_NAV_BAR, RGN_TYPE_EXECUTE)) {
+ if (ELEM(ar->regiontype,
+ RGN_TYPE_UI, RGN_TYPE_HEADER, RGN_TYPE_FOOTER,
+ RGN_TYPE_TOOLS, RGN_TYPE_NAV_BAR, RGN_TYPE_EXECUTE))
+ {
ar->flag |= RGN_FLAG_HIDDEN;
}
}
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 09ca0476b10..f475a87df59 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -618,7 +618,7 @@ static ARegion *screen_find_region_type(bContext *C, int type)
*
* functions:
*
- * apply() set actionzone event
+ * apply() set action-zone event
*
* exit() free customdata
*
@@ -826,7 +826,7 @@ static AZone *area_actionzone_refresh_xy(ScrArea *sa, const int xy[2], const boo
return az;
}
-/* Finds an actionzone by position in entire screen so azones can overlap */
+/* Finds an action-zone by position in entire screen so azones can overlap. */
static AZone *screen_actionzone_find_xy(bScreen *sc, const int xy[2])
{
for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
@@ -904,7 +904,7 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (az == NULL || ELEM(az->type, AZONE_REGION_SCROLL))
return OPERATOR_PASS_THROUGH;
- /* ok we do the actionzone */
+ /* ok we do the action-zone */
sad = op->customdata = MEM_callocN(sizeof(sActionzoneData), "sActionzoneData");
sad->sa1 = screen_actionzone_area(sc, az);
sad->az = az;
@@ -1054,7 +1054,7 @@ static void SCREEN_OT_actionzone(wmOperatorType *ot)
*
* functions:
*
- * init() set custom data for operator, based on actionzone event custom data
+ * init() set custom data for operator, based on action-zone event custom data
*
* cancel() cancel the operator
*
@@ -1062,7 +1062,7 @@ static void SCREEN_OT_actionzone(wmOperatorType *ot)
*
* callbacks:
*
- * invoke() gets called on shift+lmb drag in actionzone
+ * invoke() gets called on shift+lmb drag in action-zone
* call init(), add handler
*
* modal() accept modal events while doing it
@@ -2280,7 +2280,6 @@ typedef struct RegionMoveData {
static int area_max_regionsize(ScrArea *sa, ARegion *scalear, AZEdge edge)
{
- ARegion *ar;
int dist;
/* regions in regions. */
@@ -2288,11 +2287,11 @@ static int area_max_regionsize(ScrArea *sa, ARegion *scalear, AZEdge edge)
const int align = scalear->alignment & RGN_ALIGN_ENUM_MASK;
if (ELEM(align, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) {
- ar = scalear->prev;
+ ARegion *ar = scalear->prev;
dist = ar->winy + scalear->winy - U.pixelsize;
}
- else if (ELEM(align, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
- ar = scalear->prev;
+ else /* if (ELEM(align, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) */ {
+ ARegion *ar = scalear->prev;
dist = ar->winx + scalear->winx - U.pixelsize;
}
}
@@ -2306,7 +2305,7 @@ static int area_max_regionsize(ScrArea *sa, ARegion *scalear, AZEdge edge)
/* subtractwidth of regions on opposite side
* prevents dragging regions into other opposite regions */
- for (ar = sa->regionbase.first; ar; ar = ar->next) {
+ for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
if (ar == scalear)
continue;
@@ -3811,8 +3810,8 @@ void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UN
(sa->flag & HEADER_NO_PULLDOWN) ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT,
"SCREEN_OT_header_toggle_menus");
- /* file browser should be fullscreen all the time, topbar should
- * never be. But other regions can be maximized/restored... */
+ /* File browser should be fullscreen all the time, top-bar should
+ * never be. But other regions can be maximized/restored. */
if (!ELEM(sa->spacetype, SPACE_FILE, SPACE_TOPBAR)) {
uiItemS(layout);
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index 704de3f7f9d..d772a1a0541 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -64,10 +64,13 @@ set(SRC
sculpt_intern.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_sculpt_paint "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_sculpt_paint "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 863afcf70aa..4d1dd7eb1d6 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -796,8 +796,8 @@ static void paint_draw_alpha_overlay(
UnifiedPaintSettings *ups, Brush *brush,
ViewContext *vc, int x, int y, float zoom, ePaintMode mode)
{
- /* color means that primary brush texture is colured and
- * secondary is used for alpha/mask control */
+ /* Color means that primary brush texture is colored and
+ * secondary is used for alpha/mask control. */
bool col = ELEM(mode, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D, PAINT_MODE_VERTEX) ? true : false;
eOverlayControlFlags flags = BKE_paint_get_overlay_flags();
gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_BLEND_BIT);
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index 5e3d01d7f6a..0fdd3043d0c 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -83,9 +83,9 @@
#include "paint_intern.h"
-/* this is a static resource for non-globality,
- * Maybe it should be exposed as part of the
- * paint operation, but for now just give a public interface */
+/* This is a static resource for non-global access.
+ * Maybe it should be exposed as part of the paint operation, but for now just give a public interface.
+ */
static ImagePaintPartialRedraw imapaintpartial = {0, 0, 0, 0, 0};
ImagePaintPartialRedraw *get_imapaintpartial(void)
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 1691b70a271..45ae3e9dc11 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -924,8 +924,8 @@ static bool project_bucket_point_occluded(
return false;
}
-/* basic line intersection, could move to math_geom.c, 2 points with a horiz line
- * 1 for an intersection, 2 if the first point is aligned, 3 if the second point is aligned */
+/* Basic line intersection, could move to math_geom.c, 2 points with a horizontal line
+ * 1 for an intersection, 2 if the first point is aligned, 3 if the second point is aligned. */
#define ISECT_TRUE 1
#define ISECT_TRUE_P1 2
#define ISECT_TRUE_P2 3
@@ -1747,7 +1747,7 @@ static float project_paint_uvpixel_mask(
} /* otherwise no mask normal is needed, were within the limit */
}
- /* This only works when the opacity dosnt change while painting, stylus pressure messes with this
+ /* This only works when the opacity doesn't change while painting, stylus pressure messes with this
* so don't use it. */
// if (ps->is_airbrush == 0) mask *= BKE_brush_alpha_get(ps->brush);
@@ -2443,8 +2443,8 @@ static void project_bucket_clip_face(
*tot = 3;
return;
}
- /* handle pathological case here,
- * no need for further intersections below since tringle area is almost zero */
+ /* Handle pathological case here,
+ * no need for further intersections below since triangle area is almost zero. */
if (collinear) {
int flag;
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index ca12854aa7d..85bdba683cd 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -1887,7 +1887,7 @@ static void do_wpaint_brush_draw_task_cb_ex(
/* Test to see if the vertex coordinates are within the spherical brush region. */
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* Note: grids are 1:1 with corners (aka loops).
- * For multires, take the vert whose loop cooresponds to the current grid.
+ * For multires, take the vert whose loop corresponds to the current grid.
* Otherwise, take the current vert. */
const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
@@ -2675,7 +2675,7 @@ static void do_vpaint_brush_draw_task_cb_ex(
/* Test to see if the vertex coordinates are within the spherical brush region. */
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* Note: Grids are 1:1 with corners (aka loops).
- * For grid based pbvh, take the vert whose loop cooresponds to the current grid.
+ * For grid based pbvh, take the vert whose loop corresponds to the current grid.
* Otherwise, take the current vert. */
const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
@@ -2769,7 +2769,7 @@ static void do_vpaint_brush_blur_task_cb_ex(
{
/* Test to see if the vertex coordinates are within the spherical brush region. */
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
- /* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
+ /* For grid based pbvh, take the vert whose loop corresponds to the current grid.
* Otherwise, take the current vert. */
const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
@@ -2888,7 +2888,7 @@ static void do_vpaint_brush_smear_task_cb_ex(
{
/* Test to see if the vertex coordinates are within the spherical brush region. */
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
- /* For grid based pbvh, take the vert whose loop cooresponds to the current grid.
+ /* For grid based pbvh, take the vert whose loop corresponds to the current grid.
* Otherwise, take the current vert. */
const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
index a371d8ec93c..cd366ecff3b 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
@@ -224,8 +224,8 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even
vgroup_weight = BKE_defvert_multipaint_collective_weight(
&me->dvert[v_idx_best], defbase_tot, defbase_sel, defbase_tot_sel, ts->auto_normalize);
- /* if autonormalize is enabled, but weights are not normalized,
- * the value can exceed 1 */
+ /* If auto-normalize is enabled, but weights are not normalized,
+ * the value can exceed 1. */
CLAMP(vgroup_weight, 0.0f, 1.0f);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 24edd9bec86..ad2cfb5f9fc 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -1599,7 +1599,7 @@ static void bmesh_neighbor_average(float avg[3], BMVert *v)
}
/* For bmesh: average only the four most aligned (parallel and perpendicular) edges
- * relative to a direction. Naturally converges to a quad-like tesselation. */
+ * relative to a direction. Naturally converges to a quad-like tessellation. */
static void bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert *v)
{
/* Logic for 3 or more is identical. */
diff --git a/source/blender/editors/sound/CMakeLists.txt b/source/blender/editors/sound/CMakeLists.txt
index 67ec2359c5e..41650f07101 100644
--- a/source/blender/editors/sound/CMakeLists.txt
+++ b/source/blender/editors/sound/CMakeLists.txt
@@ -35,6 +35,9 @@ set(SRC
sound_intern.h
)
+set(LIB
+)
+
if(WITH_AUDASPACE)
add_definitions(-DWITH_AUDASPACE)
@@ -51,4 +54,4 @@ if(WITH_CODEC_SNDFILE)
add_definitions(-DWITH_SNDFILE)
endif()
-blender_add_lib(bf_editor_sound "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_sound "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_action/CMakeLists.txt b/source/blender/editors/space_action/CMakeLists.txt
index 92cf40248ff..aff4e75abb7 100644
--- a/source/blender/editors/space_action/CMakeLists.txt
+++ b/source/blender/editors/space_action/CMakeLists.txt
@@ -44,6 +44,9 @@ set(SRC
action_intern.h
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_space_action "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_action "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c
index 8cfb77e58d7..0db1fd6eec3 100644
--- a/source/blender/editors/space_action/action_draw.c
+++ b/source/blender/editors/space_action/action_draw.c
@@ -164,7 +164,7 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar)
bDopeSheet *ads = &saction->ads;
AnimData *adt = NULL;
- float act_start, act_end, y;
+ float y;
unsigned char col1[4], col2[4];
unsigned char col1a[4], col2a[4];
@@ -183,16 +183,6 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar)
UI_GetThemeColor4ubv(TH_DOPESHEET_CHANNELOB, col1b);
UI_GetThemeColor4ubv(TH_DOPESHEET_CHANNELSUBOB, col2b);
- /* set view-mapping rect (only used for x-axis), for NLA-scaling mapping with less calculation */
-
- /* if in NLA there's a strip active, map the view */
- if (ac->datatype == ANIMCONT_ACTION) {
- /* adt = ANIM_nla_mapping_get(ac, NULL); */ /* UNUSED */
-
- /* start and end of action itself */
- calc_action_range(ac->data, &act_start, &act_end, 0);
- }
-
/* build list of channels to draw */
int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
size_t items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
@@ -286,9 +276,6 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar)
/* draw region twice: firstly backdrop, then the current range */
immRectf(pos, v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF(ac));
-
- if (ac->datatype == ANIMCONT_ACTION)
- immRectf(pos, act_start, (float)y - ACHANNEL_HEIGHT_HALF(ac), act_end, (float)y + ACHANNEL_HEIGHT_HALF(ac));
}
else if (ac->datatype == ANIMCONT_GPENCIL) {
unsigned char *color;
@@ -348,6 +335,12 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar)
*/
y = (float)(-ACHANNEL_HEIGHT(ac));
+ int action_flag = saction->flag;
+
+ if (saction->mode == SACTCONT_TIMELINE) {
+ action_flag &= ~(SACTION_SHOW_INTERPOLATION | SACTION_SHOW_EXTREMES);
+ }
+
for (ale = anim_data.first; ale; ale = ale->next) {
const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac));
const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac));
@@ -363,28 +356,28 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar)
/* draw 'keyframes' for each specific datatype */
switch (ale->datatype) {
case ALE_ALL:
- draw_summary_channel(v2d, ale->data, y, ac->yscale_fac, saction->flag);
+ draw_summary_channel(v2d, ale->data, y, ac->yscale_fac, action_flag);
break;
case ALE_SCE:
- draw_scene_channel(v2d, ads, ale->key_data, y, ac->yscale_fac, saction->flag);
+ draw_scene_channel(v2d, ads, ale->key_data, y, ac->yscale_fac, action_flag);
break;
case ALE_OB:
- draw_object_channel(v2d, ads, ale->key_data, y, ac->yscale_fac, saction->flag);
+ draw_object_channel(v2d, ads, ale->key_data, y, ac->yscale_fac, action_flag);
break;
case ALE_ACT:
- draw_action_channel(v2d, adt, ale->key_data, y, ac->yscale_fac, saction->flag);
+ draw_action_channel(v2d, adt, ale->key_data, y, ac->yscale_fac, action_flag);
break;
case ALE_GROUP:
- draw_agroup_channel(v2d, adt, ale->data, y, ac->yscale_fac, saction->flag);
+ draw_agroup_channel(v2d, adt, ale->data, y, ac->yscale_fac, action_flag);
break;
case ALE_FCURVE:
- draw_fcurve_channel(v2d, adt, ale->key_data, y, ac->yscale_fac, saction->flag);
+ draw_fcurve_channel(v2d, adt, ale->key_data, y, ac->yscale_fac, action_flag);
break;
case ALE_GPFRAME:
- draw_gpl_channel(v2d, ads, ale->data, y, ac->yscale_fac, saction->flag);
+ draw_gpl_channel(v2d, ads, ale->data, y, ac->yscale_fac, action_flag);
break;
case ALE_MASKLAY:
- draw_masklay_channel(v2d, ads, ale->data, y, ac->yscale_fac, saction->flag);
+ draw_masklay_channel(v2d, ads, ale->data, y, ac->yscale_fac, action_flag);
break;
}
}
diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c
index 3dffa6afb55..f2550550bc1 100644
--- a/source/blender/editors/space_action/space_action.c
+++ b/source/blender/editors/space_action/space_action.c
@@ -95,6 +95,7 @@ static SpaceLink *action_new(const ScrArea *sa, const Scene *scene)
saction->autosnap = SACTSNAP_FRAME;
saction->mode = SACTCONT_DOPESHEET;
saction->mode_prev = SACTCONT_DOPESHEET;
+ saction->flag = SACTION_SHOW_INTERPOLATION;
saction->ads.filterflag |= ADS_FILTER_SUMMARY;
diff --git a/source/blender/editors/space_api/CMakeLists.txt b/source/blender/editors/space_api/CMakeLists.txt
index 34436ae9b4d..8f744269de8 100644
--- a/source/blender/editors/space_api/CMakeLists.txt
+++ b/source/blender/editors/space_api/CMakeLists.txt
@@ -34,4 +34,7 @@ set(SRC
spacetypes.c
)
-blender_add_lib(bf_editor_space_api "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_editor_space_api "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index 90919fa793d..5ec2041c1dc 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -41,6 +41,9 @@ set(SRC
buttons_intern.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
@@ -51,4 +54,4 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
-blender_add_lib(bf_editor_space_buttons "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_buttons "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_clip/CMakeLists.txt b/source/blender/editors/space_clip/CMakeLists.txt
index a40a1bf6d25..d770a85709a 100644
--- a/source/blender/editors/space_clip/CMakeLists.txt
+++ b/source/blender/editors/space_clip/CMakeLists.txt
@@ -63,10 +63,13 @@ set(SRC
tracking_ops_intern.h
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
-blender_add_lib(bf_editor_space_clip "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_clip "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c
index 79a159bac2e..7cfc5257792 100644
--- a/source/blender/editors/space_clip/clip_editor.c
+++ b/source/blender/editors/space_clip/clip_editor.c
@@ -578,7 +578,7 @@ void ED_clip_point_stable_pos(SpaceClip *sc, ARegion *ar, float x, float y, floa
}
/**
- * \brief the reverse of ED_clip_point_stable_pos(), gets the marker region coords.
+ * \brief the reverse of #ED_clip_point_stable_pos(), gets the marker region coords.
* better name here? view_to_track / track_to_view or so?
*/
void ED_clip_point_stable_pos__reverse(SpaceClip *sc, ARegion *ar, const float co[2], float r_co[2])
diff --git a/source/blender/editors/space_console/CMakeLists.txt b/source/blender/editors/space_console/CMakeLists.txt
index bed1b7dce96..d68e7745bdf 100644
--- a/source/blender/editors/space_console/CMakeLists.txt
+++ b/source/blender/editors/space_console/CMakeLists.txt
@@ -40,10 +40,13 @@ set(SRC
console_intern.h
)
+set(LIB
+)
+
if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_space_console "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_console "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt
index 07ba1cc7a64..a92ed62cae2 100644
--- a/source/blender/editors/space_file/CMakeLists.txt
+++ b/source/blender/editors/space_file/CMakeLists.txt
@@ -52,6 +52,9 @@ set(SRC
fsmenu.h
)
+set(LIB
+)
+
if(WITH_HEADLESS)
add_definitions(-DWITH_HEADLESS)
endif()
@@ -94,4 +97,4 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
-blender_add_lib(bf_editor_space_file "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_file "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 5195fa818d6..3ab08b5ac71 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -240,14 +240,21 @@ short ED_fileselect_set_params(SpaceFile *sfile)
}
if (params->display == FILE_DEFAULTDISPLAY) {
- if (U.uiflag & USER_SHOW_THUMBNAILS) {
- if (params->filter & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT))
- params->display = FILE_IMGDISPLAY;
- else
+ if(params->display_previous == FILE_DEFAULTDISPLAY){
+ if (U.uiflag & USER_SHOW_THUMBNAILS) {
+ if (params->filter & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT)) {
+ params->display = FILE_IMGDISPLAY;
+ }
+ else {
+ params->display = FILE_SHORTDISPLAY;
+ }
+ }
+ else {
params->display = FILE_SHORTDISPLAY;
+ }
}
else {
- params->display = FILE_SHORTDISPLAY;
+ params->display = params->display_previous;
}
}
@@ -265,6 +272,7 @@ short ED_fileselect_set_params(SpaceFile *sfile)
params->flag |= FILE_HIDE_DOT;
params->flag &= ~FILE_DIRSEL_ONLY;
params->display = FILE_SHORTDISPLAY;
+ params->display_previous = FILE_DEFAULTDISPLAY;
params->sort = FILE_SORT_ALPHA;
params->filter = 0;
params->filter_glob[0] = '\0';
@@ -554,7 +562,6 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
(int)layout->column_widths[COLUMN_DATE] + column_space +
(int)layout->column_widths[COLUMN_TIME] + column_space +
(int)layout->column_widths[COLUMN_SIZE] + column_space;
-
}
layout->tile_w = maxlen;
if (layout->rows > 0)
@@ -566,6 +573,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
layout->width = sfile->layout->columns * (layout->tile_w + 2 * layout->tile_border_x) + layout->tile_border_x * 2;
layout->flag = FILE_LAYOUT_HOR;
}
+ params->display_previous = params->display;
layout->dirty = false;
}
diff --git a/source/blender/editors/space_graph/CMakeLists.txt b/source/blender/editors/space_graph/CMakeLists.txt
index 20e2ebc0936..53c228cd9c9 100644
--- a/source/blender/editors/space_graph/CMakeLists.txt
+++ b/source/blender/editors/space_graph/CMakeLists.txt
@@ -45,6 +45,9 @@ set(SRC
graph_intern.h
)
+set(LIB
+)
+
if(WITH_AUDASPACE)
add_definitions(-DWITH_AUDASPACE)
@@ -59,4 +62,4 @@ endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_space_graph "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_graph "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt
index 77abfebc662..99ede338dee 100644
--- a/source/blender/editors/space_image/CMakeLists.txt
+++ b/source/blender/editors/space_image/CMakeLists.txt
@@ -47,6 +47,9 @@ set(SRC
image_intern.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
@@ -73,4 +76,4 @@ endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_space_image "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_image "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_info/CMakeLists.txt b/source/blender/editors/space_info/CMakeLists.txt
index f54e4921e6b..665d130e072 100644
--- a/source/blender/editors/space_info/CMakeLists.txt
+++ b/source/blender/editors/space_info/CMakeLists.txt
@@ -49,10 +49,13 @@ set(SRC
textview.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_space_info "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_info "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_nla/CMakeLists.txt b/source/blender/editors/space_nla/CMakeLists.txt
index eb0d92e2e69..35c29ad4492 100644
--- a/source/blender/editors/space_nla/CMakeLists.txt
+++ b/source/blender/editors/space_nla/CMakeLists.txt
@@ -45,10 +45,13 @@ set(SRC
nla_intern.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_space_nla "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_nla "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c
index 049342b0244..b06b7120faf 100644
--- a/source/blender/editors/space_nla/nla_buttons.c
+++ b/source/blender/editors/space_nla/nla_buttons.c
@@ -360,7 +360,7 @@ static void nla_panel_properties(const bContext *C, Panel *pa)
row = uiLayoutRow(layout, true);
uiItemR(row, &strip_ptr, "blend_type", 0, NULL, ICON_NONE);
- /* blend in/out + autoblending
+ /* Blend in/out + auto-blending:
* - blend in/out can only be set when autoblending is off
*/
column = uiLayoutColumn(layout, true);
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
index a92a2ec66ca..1d21619fbd1 100644
--- a/source/blender/editors/space_nla/nla_edit.c
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -1269,8 +1269,8 @@ static void nlaedit_split_strip_actclip(Main *bmain, AnimData *adt, NlaTrack *nl
nstrip = BKE_nlastrip_copy(bmain, strip, true, 0);
BLI_insertlinkafter(&nlt->strips, strip, nstrip);
- /* set the endpoint of the first strip and the start of the new strip
- * to the splitframe values calculated above
+ /* Set the endpoint of the first strip and the start of the new strip
+ * to the split-frame values calculated above.
*/
strip->end = splitframe;
nstrip->start = splitframe;
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt
index 0950e738e3e..2c1ec0cc834 100644
--- a/source/blender/editors/space_node/CMakeLists.txt
+++ b/source/blender/editors/space_node/CMakeLists.txt
@@ -57,6 +57,9 @@ set(SRC
node_intern.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
@@ -67,4 +70,4 @@ endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_space_node "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_node "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c
index 55a3da6f77c..afb0c833aa8 100644
--- a/source/blender/editors/space_node/node_relationships.c
+++ b/source/blender/editors/space_node/node_relationships.c
@@ -1543,7 +1543,7 @@ void ED_node_link_intersect_test(ScrArea *sa, int test)
/* check if the node rect intersetcts the line from this point to next one */
if (BLI_rctf_isect_segment(&select->totr, coord_array[i], coord_array[i + 1])) {
/* store the shortest distance to the upper left edge
- * of all intersetctions found so far */
+ * of all intersections found so far */
const float node_xy[] = {select->totr.xmin, select->totr.ymax};
/* to be precise coord_array should be clipped by select->totr,
diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt
index 207a5c194bd..ae2d52533fe 100644
--- a/source/blender/editors/space_outliner/CMakeLists.txt
+++ b/source/blender/editors/space_outliner/CMakeLists.txt
@@ -49,10 +49,13 @@ set(SRC
outliner_intern.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_space_outliner "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_outliner "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index d6344eb9005..600f6b1fbe9 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -1750,7 +1750,7 @@ static void outliner_draw_tree_element(
}
else if (te->idcode == ID_OB) {
Object *ob = (Object *)tselem->id;
- Base *base = (Base *)te->directdata;
+ Base *base = BKE_view_layer_base_find(view_layer, ob);
const bool is_selected = (base != NULL) && ((base->flag & BASE_SELECTED) != 0);
if (ob == obact || is_selected) {
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index 72e9236636e..a4e512d6846 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -331,8 +331,6 @@ static void outliner_add_object_contents(SpaceOutliner *soops, TreeElement *te,
outliner_add_element(soops, &te->subtree, ob->proxy, te, TSE_PROXY, 0);
}
- outliner_add_element(soops, &te->subtree, ob->gpd, te, 0, 0);
-
outliner_add_element(soops, &te->subtree, ob->data, te, 0, 0);
if (ob->pose) {
diff --git a/source/blender/editors/space_script/CMakeLists.txt b/source/blender/editors/space_script/CMakeLists.txt
index 5d950a3558d..19b3e42b37a 100644
--- a/source/blender/editors/space_script/CMakeLists.txt
+++ b/source/blender/editors/space_script/CMakeLists.txt
@@ -39,6 +39,9 @@ set(SRC
script_intern.h
)
+set(LIB
+)
+
if(WITH_PYTHON)
list(APPEND INC
../../python
@@ -48,4 +51,4 @@ endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_space_script "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_script "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt
index 7d76fa1af5c..bf70473610d 100644
--- a/source/blender/editors/space_sequencer/CMakeLists.txt
+++ b/source/blender/editors/space_sequencer/CMakeLists.txt
@@ -50,6 +50,9 @@ set(SRC
sequencer_intern.h
)
+set(LIB
+)
+
if(WITH_AUDASPACE)
add_definitions(-DWITH_AUDASPACE)
@@ -64,4 +67,4 @@ endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_space_sequencer "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_sequencer "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index b7d3bec326a..9e5a2624226 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -2455,7 +2455,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op)
Sequence *seq_next;
/* remove seq so overlap tests don't conflict,
- * see seq_free_sequence below for the real free'ing */
+ * see seq_free_sequence below for the real freeing. */
BLI_remlink(ed->seqbasep, seq);
/* if (seq->ipo) id_us_min(&seq->ipo->id); */
/* XXX, remove fcurve and assign to split image strips */
@@ -2584,8 +2584,8 @@ static int sequencer_meta_toggle_exec(bContext *C, wmOperator *UNUSED(op))
BKE_sequence_calc(scene, seq);
}
- /* 2.73+, keeping endpoings is important!
- * moving them around means you can't usefully use metas in a complex edit */
+ /* 2.73+, keeping endpoints is important!
+ * moving them around means you can't usefully use metas in a complex edit. */
#if 1
BKE_sequence_tx_set_final_left(ms->parseq, ms->disp_range[0]);
BKE_sequence_tx_set_final_right(ms->parseq, ms->disp_range[1]);
@@ -3354,7 +3354,7 @@ static int sequencer_copy_exec(bContext *C, wmOperator *op)
}
/* Replace datablock pointers with copies, to keep things working in case
- * datablocks get deleted or another .blend file is openeded. */
+ * datablocks get deleted or another .blend file is opened. */
BKE_sequencer_base_clipboard_pointers_store(bmain, &seqbase_clipboard);
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/space_statusbar/CMakeLists.txt b/source/blender/editors/space_statusbar/CMakeLists.txt
index 8440d410c31..3b1372b419e 100644
--- a/source/blender/editors/space_statusbar/CMakeLists.txt
+++ b/source/blender/editors/space_statusbar/CMakeLists.txt
@@ -37,6 +37,9 @@ set(SRC
space_statusbar.c
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_space_statusbar "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_statusbar "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_text/CMakeLists.txt b/source/blender/editors/space_text/CMakeLists.txt
index 378f2e5d84f..b542732fc54 100644
--- a/source/blender/editors/space_text/CMakeLists.txt
+++ b/source/blender/editors/space_text/CMakeLists.txt
@@ -51,6 +51,9 @@ set(SRC
text_intern.h
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
if(WITH_PYTHON)
@@ -64,4 +67,4 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
-blender_add_lib(bf_editor_space_text "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_text "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c
index 87bbefdd365..1b49e637350 100644
--- a/source/blender/editors/space_text/text_draw.c
+++ b/source/blender/editors/space_text/text_draw.c
@@ -154,14 +154,19 @@ static void format_draw_color(const TextDrawContext *tdc, char formatchar)
/************************** draw text *****************************/
-/* Notes on word-wrap
+/**
+ * Notes on word-wrap
* --
- * All word-wrap functions follow the algorithm below to maintain consistency.
- * line The line to wrap (tabs converted to spaces)
- * view_width The maximum number of characters displayable in the region
- * This equals region_width/font_width for the region
- * wrap_chars Characters that allow wrapping. This equals [' ', '\t', '-']
+ * All word-wrap functions follow the algorithm below to maintain consistency:
+ * - line:
+ * The line to wrap (tabs converted to spaces)
+ * - view_width:
+ * The maximum number of characters displayable in the region
+ * This equals region_width/font_width for the region
+ * - wrap_chars:
+ * Characters that allow wrapping. This equals [' ', '\t', '-']
*
+ * \code{.py}
* def wrap(line, view_width, wrap_chars):
* draw_start = 0
* draw_end = view_width
@@ -175,6 +180,7 @@ static void format_draw_color(const TextDrawContext *tdc, char formatchar)
* draw_end = pos+1
* pos += 1
* print line[draw_start:]
+ * \encode
*/
int wrap_width(const SpaceText *st, ARegion *ar)
diff --git a/source/blender/editors/space_text/text_format_lua.c b/source/blender/editors/space_text/text_format_lua.c
index 7118b8b748a..4b25c5d39a7 100644
--- a/source/blender/editors/space_text/text_format_lua.c
+++ b/source/blender/editors/space_text/text_format_lua.c
@@ -30,9 +30,10 @@
/* *** Lua Keywords (for format_line) *** */
-/* Checks the specified source string for a Lua keyword (minus boolean & 'nil').
+/**
+ * Checks the specified source string for a Lua keyword (minus boolean & 'nil').
* This name must start at the beginning of the source string and must be
- * followed by a non-identifier (see text_check_identifier(char)) or null char.
+ * followed by a non-identifier (see #text_check_identifier(char)) or null char.
*
* If a keyword is found, the length of the matching word is returned.
* Otherwise, -1 is returned.
@@ -40,7 +41,6 @@
* See:
* http://www.lua.org/manual/5.1/manual.html#2.1
*/
-
static int txtfmt_lua_find_keyword(const char *string)
{
int i, len;
@@ -71,9 +71,10 @@ static int txtfmt_lua_find_keyword(const char *string)
return i;
}
-/* Checks the specified source string for a Lua special name/function. This
+/**
+ * Checks the specified source string for a Lua special name/function. This
* name must start at the beginning of the source string and must be followed
- * by a non-identifier (see text_check_identifier(char)) or null character.
+ * by a non-identifier (see *text_check_identifier(char)) or null character.
*
* If a special name is found, the length of the matching name is returned.
* Otherwise, -1 is returned.
@@ -81,7 +82,6 @@ static int txtfmt_lua_find_keyword(const char *string)
* See:
* http://www.lua.org/manual/5.1/manual.html#5.1
*/
-
static int txtfmt_lua_find_specialvar(const char *string)
{
int i, len;
diff --git a/source/blender/editors/space_text/text_format_pov.c b/source/blender/editors/space_text/text_format_pov.c
index da10c8626b5..2435d2f21da 100644
--- a/source/blender/editors/space_text/text_format_pov.c
+++ b/source/blender/editors/space_text/text_format_pov.c
@@ -30,9 +30,10 @@
/* *** POV Keywords (for format_line) *** */
-/* Checks the specified source string for a POV keyword (minus boolean & 'nil').
+/**
+ * Checks the specified source string for a POV keyword (minus boolean & 'nil').
* This name must start at the beginning of the source string and must be
- * followed by a non-identifier (see text_check_identifier(char)) or null char.
+ * followed by a non-identifier (see #text_check_identifier(char)) or null char.
*
* If a keyword is found, the length of the matching word is returned.
* Otherwise, -1 is returned.
@@ -40,7 +41,6 @@
* See:
* http://www.povray.org/documentation/view/3.7.0/212/
*/
-
static int txtfmt_pov_find_keyword(const char *string)
{
int i, len;
@@ -467,9 +467,10 @@ static int txtfmt_pov_find_reserved_builtins(const char *string)
}
-/* Checks the specified source string for a POV modifiers. This
+/**
+ * Checks the specified source string for a POV modifiers. This
* name must start at the beginning of the source string and must be followed
- * by a non-identifier (see text_check_identifier(char)) or null character.
+ * by a non-identifier (see #text_check_identifier(char)) or null character.
*
* If a special name is found, the length of the matching name is returned.
* Otherwise, -1 is returned.
@@ -477,7 +478,6 @@ static int txtfmt_pov_find_reserved_builtins(const char *string)
* See:
* http://www.povray.org/documentation/view/3.7.0/212/
*/
-
static int txtfmt_pov_find_specialvar(const char *string)
{
int i, len;
diff --git a/source/blender/editors/space_text/text_format_pov_ini.c b/source/blender/editors/space_text/text_format_pov_ini.c
index e41fd94ef49..da69a35f0b0 100644
--- a/source/blender/editors/space_text/text_format_pov_ini.c
+++ b/source/blender/editors/space_text/text_format_pov_ini.c
@@ -30,9 +30,10 @@
/* *** POV INI Keywords (for format_line) *** */
-/* Checks the specified source string for a POV INI keyword (minus boolean & 'nil').
+/**
+ * Checks the specified source string for a POV INI keyword (minus boolean & 'nil').
* This name must start at the beginning of the source string and must be
- * followed by a non-identifier (see text_check_identifier(char)) or null char.
+ * followed by a non-identifier (see #text_check_identifier(char)) or null char.
*
* If a keyword is found, the length of the matching word is returned.
* Otherwise, -1 is returned.
@@ -40,7 +41,6 @@
* See:
* http://www.povray.org/documentation/view/3.7.0/212/
*/
-
static int txtfmt_ini_find_keyword(const char *string)
{
int i, len;
diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c
index 85c3c4220c0..87b3835ce1e 100644
--- a/source/blender/editors/space_text/text_format_py.c
+++ b/source/blender/editors/space_text/text_format_py.c
@@ -30,9 +30,10 @@
/* *** Local Functions (for format_line) *** */
-/* Checks the specified source string for a Python built-in function name. This
+/**
+ * Checks the specified source string for a Python built-in function name. This
* name must start at the beginning of the source string and must be followed by
- * a non-identifier (see text_check_identifier(char)) or null character.
+ * a non-identifier (see #text_check_identifier(char)) or null character.
*
* If a built-in function is found, the length of the matching name is returned.
* Otherwise, -1 is returned.
@@ -40,7 +41,6 @@
* See:
* http://docs.python.org/py3k/reference/lexical_analysis.html#keywords
*/
-
static int txtfmt_py_find_builtinfunc(const char *string)
{
int i, len;
diff --git a/source/blender/editors/space_topbar/CMakeLists.txt b/source/blender/editors/space_topbar/CMakeLists.txt
index 418a0eb3ad8..15352044d4a 100644
--- a/source/blender/editors/space_topbar/CMakeLists.txt
+++ b/source/blender/editors/space_topbar/CMakeLists.txt
@@ -37,6 +37,9 @@ set(SRC
space_topbar.c
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_space_topbar "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_topbar "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_userpref/CMakeLists.txt b/source/blender/editors/space_userpref/CMakeLists.txt
index ec0c76c3d5b..d26766358ca 100644
--- a/source/blender/editors/space_userpref/CMakeLists.txt
+++ b/source/blender/editors/space_userpref/CMakeLists.txt
@@ -36,4 +36,7 @@ set(SRC
userpref_intern.h
)
-blender_add_lib(bf_editor_space_userpref "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_editor_space_userpref "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt
index 83e65246797..e3f5245fc59 100644
--- a/source/blender/editors/space_view3d/CMakeLists.txt
+++ b/source/blender/editors/space_view3d/CMakeLists.txt
@@ -72,6 +72,9 @@ set(SRC
view3d_intern.h
)
+set(LIB
+)
+
if(WITH_PYTHON)
blender_include_dirs(../../python)
add_definitions(-DWITH_PYTHON)
@@ -91,4 +94,4 @@ if(WITH_MOD_SMOKE)
add_definitions(-DWITH_SMOKE)
endif()
-blender_add_lib(bf_editor_space_view3d "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_space_view3d "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index d46c7a292cb..42860b62ae5 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -415,8 +415,8 @@ static void view3d_free(SpaceLink *sl)
MEM_freeN(vd->localvd);
}
- if (vd->properties_storage) {
- MEM_freeN(vd->properties_storage);
+ if (vd->runtime.properties_storage) {
+ MEM_freeN(vd->runtime.properties_storage);
}
if (vd->fx_settings.ssao) {
@@ -443,7 +443,7 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl)
if (v3dn->localvd) {
v3dn->localvd = NULL;
- v3dn->properties_storage = NULL;
+ v3dn->runtime.properties_storage = NULL;
}
if (v3dn->shading.type == OB_RENDER) {
@@ -452,7 +452,7 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl)
/* copy or clear inside new stuff */
- v3dn->properties_storage = NULL;
+ v3dn->runtime.properties_storage = NULL;
if (v3dn->fx_settings.dof) {
v3dn->fx_settings.dof = MEM_dupallocN(v3do->fx_settings.dof);
}
@@ -697,6 +697,7 @@ static void view3d_widgets(void)
wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(
&(const struct wmGizmoMapType_Params){SPACE_VIEW3D, RGN_TYPE_WINDOW});
+ WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_xform_gizmo_context);
WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_light_spot);
WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_light_area);
WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_light_target);
@@ -706,7 +707,7 @@ static void view3d_widgets(void)
WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_empty_image);
WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_armature_spline);
- WM_gizmogrouptype_append(TRANSFORM_GGT_gizmo);
+ WM_gizmogrouptype_append(VIEW3D_GGT_xform_gizmo);
WM_gizmogrouptype_append(VIEW3D_GGT_xform_cage);
WM_gizmogrouptype_append(VIEW3D_GGT_xform_shear);
WM_gizmogrouptype_append(VIEW3D_GGT_xform_extrude);
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index 3baf297e580..ca30e3d3b01 100644
--- a/source/blender/editors/space_view3d/view3d_buttons.c
+++ b/source/blender/editors/space_view3d/view3d_buttons.c
@@ -196,10 +196,10 @@ static void apply_scale_factor_clamp(float *val, const int tot, const float ve_m
static TransformProperties *v3d_transform_props_ensure(View3D *v3d)
{
- if (v3d->properties_storage == NULL) {
- v3d->properties_storage = MEM_callocN(sizeof(TransformProperties), "TransformProperties");
+ if (v3d->runtime.properties_storage == NULL) {
+ v3d->runtime.properties_storage = MEM_callocN(sizeof(TransformProperties), "TransformProperties");
}
- return v3d->properties_storage;
+ return v3d->runtime.properties_storage;
}
/* is used for both read and write... */
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index 522d081fdd6..3399bda1cfa 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -1324,7 +1324,6 @@ void view3d_draw_region_info(const bContext *C, ARegion *ar)
BLF_batch_draw_begin();
if ((U.uiflag & USER_SHOW_GIZMO_AXIS) ||
- (v3d->flag2 & V3D_HIDE_OVERLAYS) ||
/* No need to display gizmo and this info. */
(v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_NAVIGATE)))
{
diff --git a/source/blender/editors/space_view3d/view3d_draw_legacy.c b/source/blender/editors/space_view3d/view3d_draw_legacy.c
index 454315afa57..f7fcd5cb11f 100644
--- a/source/blender/editors/space_view3d/view3d_draw_legacy.c
+++ b/source/blender/editors/space_view3d/view3d_draw_legacy.c
@@ -81,6 +81,7 @@
#include "ED_space_api.h"
#include "ED_screen_types.h"
#include "ED_transform.h"
+#include "ED_view3d.h"
#include "UI_interface.h"
#include "UI_interface_icons.h"
@@ -174,12 +175,12 @@ static void validate_object_select_id(
/* do nothing */
}
else if ((obact_eval && (obact_eval->mode & OB_MODE_PARTICLE_EDIT)) &&
- V3D_IS_ZBUF(v3d))
+ !XRAY_ENABLED(v3d))
{
/* do nothing */
}
else if ((obedit && (obedit->mode & OB_MODE_EDIT)) &&
- V3D_IS_ZBUF(v3d))
+ !XRAY_FLAG_ENABLED(v3d))
{
/* do nothing */
}
@@ -654,7 +655,7 @@ static void view3d_draw_bgpic(Scene *scene, Depsgraph *depsgraph,
float zoomx = (x2 - x1) / ibuf->x;
float zoomy = (y2 - y1) / ibuf->y;
- /* for some reason; zoomlevels down refuses to use GL_ALPHA_SCALE */
+ /* For some reason; zoom-levels down refuses to use GL_ALPHA_SCALE. */
if (zoomx < 1.0f || zoomy < 1.0f) {
float tzoom = min_ff(zoomx, zoomy);
int mip = 0;
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 19008ebd122..e74703baf2d 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -4003,6 +4003,13 @@ void VIEW3D_OT_view_camera(wmOperatorType *ot)
* Rotate (orbit) in incremental steps. For interactive orbit see #VIEW3D_OT_rotate.
* \{ */
+enum {
+ V3D_VIEW_STEPLEFT = 1,
+ V3D_VIEW_STEPRIGHT,
+ V3D_VIEW_STEPDOWN,
+ V3D_VIEW_STEPUP,
+};
+
static const EnumPropertyItem prop_view_orbit_items[] = {
{V3D_VIEW_STEPLEFT, "ORBITLEFT", 0, "Orbit Left", "Orbit the view around to the Left"},
{V3D_VIEW_STEPRIGHT, "ORBITRIGHT", 0, "Orbit Right", "Orbit the view around to the Right"},
@@ -4366,6 +4373,13 @@ void VIEW3D_OT_view_roll(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
+enum {
+ V3D_VIEW_PANLEFT = 1,
+ V3D_VIEW_PANRIGHT,
+ V3D_VIEW_PANDOWN,
+ V3D_VIEW_PANUP,
+};
+
static const EnumPropertyItem prop_view_pan_items[] = {
{V3D_VIEW_PANLEFT, "PANLEFT", 0, "Pan Left", "Pan the view to the Left"},
{V3D_VIEW_PANRIGHT, "PANRIGHT", 0, "Pan Right", "Pan the view to the Right"},
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_armature.c b/source/blender/editors/space_view3d/view3d_gizmo_armature.c
index a2b93dcd309..64617b03c5e 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_armature.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_armature.c
@@ -125,9 +125,7 @@ static void gizmo_bbone_offset_set(
static bool WIDGETGROUP_armature_spline_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
{
View3D *v3d = CTX_wm_view3d(C);
- if ((v3d->flag2 & V3D_HIDE_OVERLAYS) ||
- (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)))
- {
+ if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)) {
return false;
}
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_camera.c b/source/blender/editors/space_view3d/view3d_gizmo_camera.c
index 2aa10dc3040..590597145a0 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_camera.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_camera.c
@@ -61,8 +61,11 @@ struct CameraWidgetGroup {
static bool WIDGETGROUP_camera_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
{
View3D *v3d = CTX_wm_view3d(C);
- if ((v3d->flag2 & V3D_HIDE_OVERLAYS) ||
- (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)))
+ if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)) {
+ return false;
+ }
+ if ((v3d->gizmo_show_camera & V3D_GIZMO_SHOW_CAMERA_LENS) == 0 &&
+ (v3d->gizmo_show_camera & V3D_GIZMO_SHOW_CAMERA_DOF_DIST) == 0)
{
return false;
}
@@ -135,6 +138,7 @@ static void WIDGETGROUP_camera_refresh(const bContext *C, wmGizmoGroup *gzgroup)
}
struct CameraWidgetGroup *cagzgroup = gzgroup->customdata;
+ View3D *v3d = CTX_wm_view3d(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = OBACT(view_layer);
Camera *ca = ob->data;
@@ -145,7 +149,9 @@ static void WIDGETGROUP_camera_refresh(const bContext *C, wmGizmoGroup *gzgroup)
negate_v3_v3(dir, ob->obmat[2]);
- if (ca->flag & CAM_SHOWLIMITS) {
+ if ((ca->flag & CAM_SHOWLIMITS) &&
+ (v3d->gizmo_show_camera & V3D_GIZMO_SHOW_CAMERA_DOF_DIST))
+ {
WM_gizmo_set_matrix_location(cagzgroup->dop_dist, ob->obmat[3]);
WM_gizmo_set_matrix_rotation_from_yz_axis(cagzgroup->dop_dist, ob->obmat[1], dir);
WM_gizmo_set_scale(cagzgroup->dop_dist, ca->drawsize);
@@ -234,6 +240,11 @@ static void WIDGETGROUP_camera_refresh(const bContext *C, wmGizmoGroup *gzgroup)
WM_gizmo_target_property_def_rna_ptr(widget, gz_prop_type, &camera_ptr, prop, -1);
}
+ /* This could be handled more elegently (split into two gizmo groups). */
+ if ((v3d->gizmo_show_camera & V3D_GIZMO_SHOW_CAMERA_LENS) == 0) {
+ WM_gizmo_set_flag(cagzgroup->focal_len, WM_GIZMO_HIDDEN, true);
+ WM_gizmo_set_flag(cagzgroup->ortho_scale, WM_GIZMO_HIDDEN, true);
+ }
}
static void WIDGETGROUP_camera_message_subscribe(
@@ -374,9 +385,7 @@ static bool WIDGETGROUP_camera_view_poll(const bContext *C, wmGizmoGroupType *UN
}
View3D *v3d = CTX_wm_view3d(C);
- if ((v3d->flag2 & V3D_HIDE_OVERLAYS) ||
- (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)))
- {
+ if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)) {
return false;
}
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_empty.c b/source/blender/editors/space_view3d/view3d_gizmo_empty.c
index 7d6ec3b782f..8e78fe8a18e 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_empty.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_empty.c
@@ -106,9 +106,10 @@ static bool WIDGETGROUP_empty_image_poll(const bContext *C, wmGizmoGroupType *UN
View3D *v3d = CTX_wm_view3d(C);
RegionView3D *rv3d = CTX_wm_region_view3d(C);
- if ((v3d->flag2 & V3D_HIDE_OVERLAYS) ||
- (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)))
- {
+ if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)) {
+ return false;
+ }
+ if ((v3d->gizmo_show_empty & V3D_GIZMO_SHOW_EMPTY_IMAGE) == 0) {
return false;
}
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_forcefield.c b/source/blender/editors/space_view3d/view3d_gizmo_forcefield.c
index 750a8101a15..34cf38b3466 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_forcefield.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_forcefield.c
@@ -49,9 +49,10 @@ static bool WIDGETGROUP_forcefield_poll(const bContext *C, wmGizmoGroupType *UNU
{
View3D *v3d = CTX_wm_view3d(C);
- if ((v3d->flag2 & V3D_HIDE_OVERLAYS) ||
- (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)))
- {
+ if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)) {
+ return false;
+ }
+ if ((v3d->gizmo_show_empty & V3D_GIZMO_SHOW_EMPTY_FORCE_FIELD) == 0) {
return false;
}
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_light.c b/source/blender/editors/space_view3d/view3d_gizmo_light.c
index 90f1b60e21a..5fd5336f4ca 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_light.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_light.c
@@ -51,9 +51,10 @@
static bool WIDGETGROUP_light_spot_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
{
View3D *v3d = CTX_wm_view3d(C);
- if ((v3d->flag2 & V3D_HIDE_OVERLAYS) ||
- (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)))
- {
+ if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)) {
+ return false;
+ }
+ if ((v3d->gizmo_show_light & V3D_GIZMO_SHOW_LIGHT_SIZE) == 0) {
return false;
}
@@ -161,7 +162,10 @@ static void gizmo_area_light_prop_matrix_set(
static bool WIDGETGROUP_light_area_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
{
View3D *v3d = CTX_wm_view3d(C);
- if (v3d->flag2 & V3D_HIDE_OVERLAYS) {
+ if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)) {
+ return false;
+ }
+ if ((v3d->gizmo_show_light & V3D_GIZMO_SHOW_LIGHT_SIZE) == 0) {
return false;
}
@@ -244,7 +248,10 @@ void VIEW3D_GGT_light_area(wmGizmoGroupType *gzgt)
static bool WIDGETGROUP_light_target_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
{
View3D *v3d = CTX_wm_view3d(C);
- if (v3d->flag2 & V3D_HIDE_OVERLAYS) {
+ if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_CONTEXT)) {
+ return false;
+ }
+ if ((v3d->gizmo_show_light & V3D_GIZMO_SHOW_LIGHT_LOOK_AT) == 0) {
return false;
}
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate.c
index acf6aa7e188..a6fd195b04d 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_navigate.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate.c
@@ -121,13 +121,11 @@ static bool WIDGETGROUP_navigate_poll(const bContext *C, wmGizmoGroupType *UNUSE
{
View3D *v3d = CTX_wm_view3d(C);
if (((U.uiflag & USER_SHOW_GIZMO_AXIS) == 0) ||
- (v3d->flag2 & V3D_HIDE_OVERLAYS) ||
(v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_NAVIGATE)))
{
return false;
}
return true;
-
}
static void WIDGETGROUP_navigate_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
index 09757e13297..f8f92d8bef8 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
@@ -1042,9 +1042,7 @@ static int view3d_ruler_add_invoke(bContext *C, wmOperator *op, const wmEvent *e
View3D *v3d = CTX_wm_view3d(C);
RegionView3D *rv3d = ar->regiondata;
- if ((v3d->flag2 & V3D_HIDE_OVERLAYS) ||
- (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)))
- {
+ if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)) {
BKE_report(op->reports, RPT_WARNING, "Gizmos hidden in this view");
return OPERATOR_CANCELLED;
}
@@ -1107,9 +1105,7 @@ static int view3d_ruler_remove_invoke(bContext *C, wmOperator *op, const wmEvent
ARegion *ar = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
- if ((v3d->flag2 & V3D_HIDE_OVERLAYS) ||
- (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)))
- {
+ if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)) {
BKE_report(op->reports, RPT_WARNING, "Gizmos hidden in this view");
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/space_view3d/view3d_iterators.c b/source/blender/editors/space_view3d/view3d_iterators.c
index 0054680281b..daa1cd38164 100644
--- a/source/blender/editors/space_view3d/view3d_iterators.c
+++ b/source/blender/editors/space_view3d/view3d_iterators.c
@@ -79,10 +79,12 @@ typedef struct foreachScreenFace_userData {
} foreachScreenFace_userData;
-/* Note! - foreach funcs should be called while drawing or directly after
- * if not, ED_view3d_init_mats_rv3d() can be used for selection tools
+/**
+ * \note foreach funcs should be called while drawing or directly after
+ * if not, #ED_view3d_init_mats_rv3d() can be used for selection tools
* but would not give correct results with dupli's for eg. which don't
- * use the object matrix in the usual way */
+ * use the object matrix in the usual way.
+ */
/* ------------------------------------------------------------------------ */
diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c
index 760cbb3f3ef..e5f8ac147de 100644
--- a/source/blender/editors/space_view3d/view3d_project.c
+++ b/source/blender/editors/space_view3d/view3d_project.c
@@ -582,7 +582,7 @@ bool ED_view3d_win_to_3d_on_plane_int(
/**
* Calculate a 3d difference vector from 2d window offset.
- * note that ED_view3d_calc_zfac() must be called first to determine
+ * note that #ED_view3d_calc_zfac() must be called first to determine
* the depth used to calculate the delta.
* \param ar: The region (used for the window width and height).
* \param mval: The area relative 2d difference (such as event->mval[0] - other_x).
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index ea44f19c4fe..3ff6a7146c6 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -943,7 +943,7 @@ static void do_lasso_select_meshobject__doSelectVert(void *userData, MVert *mv,
}
static bool do_lasso_select_paintvert(ViewContext *vc, const int mcords[][2], short moves, const eSelectOp sel_op)
{
- const bool use_zbuf = V3D_IS_ZBUF(vc->v3d);
+ const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
Object *ob = vc->obact;
Mesh *me = ob->data;
rcti rect;
@@ -1472,7 +1472,7 @@ static int mixed_bones_object_selectbuffer_extended(
/* define if we use solid nearest select or not */
if (use_cycle) {
- if (v3d->shading.type > OB_WIRE) {
+ if (!XRAY_ACTIVE(v3d)) {
do_nearest = true;
if (len_manhattan_v2v2_int(mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) {
do_nearest = false;
@@ -1481,7 +1481,7 @@ static int mixed_bones_object_selectbuffer_extended(
copy_v2_v2_int(last_mval, mval);
}
else {
- if (v3d->shading.type > OB_WIRE) {
+ if (!XRAY_ACTIVE(v3d)) {
do_nearest = true;
}
}
@@ -1630,7 +1630,7 @@ Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2])
ED_view3d_viewcontext_init(C, &vc);
- const bool do_nearest = (vc.v3d->shading.type > OB_WIRE);
+ const bool do_nearest = !XRAY_ACTIVE(vc.v3d);
const int hits = mixed_bones_object_selectbuffer(
&vc, buffer, mval, VIEW3D_SELECT_FILTER_NOP, do_nearest);
@@ -1996,7 +1996,7 @@ static bool ed_wpaint_vertex_select_pick(
bool extend, bool deselect, bool toggle, Object *obact)
{
View3D *v3d = CTX_wm_view3d(C);
- const bool use_zbuf = V3D_IS_ZBUF(v3d);
+ const bool use_zbuf = !XRAY_ENABLED(v3d);
Mesh *me = obact->data; /* already checked for NULL */
uint index = 0;
@@ -2246,7 +2246,7 @@ static void do_paintvert_box_select__doSelectVert(void *userData, MVert *mv, con
static bool do_paintvert_box_select(
ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
{
- const bool use_zbuf = V3D_IS_ZBUF(vc->v3d);
+ const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
Mesh *me;
MVert *mvert;
unsigned int *rt;
@@ -3096,7 +3096,7 @@ static void paint_vertsel_circle_select_doSelectVert(void *userData, MVert *mv,
static bool paint_vertsel_circle_select(ViewContext *vc, const eSelectOp sel_op, const int mval[2], float rad)
{
BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB));
- const bool use_zbuf = V3D_IS_ZBUF(vc->v3d);
+ const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
Object *ob = vc->obact;
Mesh *me = ob->data;
bool bbsel;
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index 21716f28b85..4e5dddf4742 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -1049,7 +1049,7 @@ int view3d_opengl_select(
* the object & bone view locking takes 'rect' into account, see: T51629. */
ED_view3d_draw_setup_view(vc->win, depsgraph, scene, ar, v3d, vc->rv3d->viewmat, NULL, &rect);
- if (v3d->shading.type > OB_WIRE) {
+ if (!XRAY_ACTIVE(v3d)) {
GPU_depth_test(true);
}
@@ -1058,7 +1058,7 @@ int view3d_opengl_select(
}
/* If in xray mode, we select the wires in priority. */
- if ((v3d->shading.flag & V3D_XRAY_FLAG(v3d)) && use_nearest) {
+ if (XRAY_ACTIVE(v3d) && use_nearest) {
/* We need to call "GPU_select_*" API's inside DRW_draw_select_loop
* because the OpenGL context created & destroyed inside this function. */
struct DrawSelectLoopUserData drw_select_loop_user_data = {
@@ -1092,7 +1092,8 @@ int view3d_opengl_select(
.rect = &rect,
.gpu_select_mode = gpu_select_mode,
};
- draw_surface = (v3d->shading.type > OB_WIRE) || ((v3d->shading.flag & V3D_XRAY_FLAG(v3d)) == 0);
+ /* If are not in wireframe mode, we need to use the mesh surfaces to check for hits */
+ draw_surface = (v3d->shading.type > OB_WIRE) || !XRAY_ENABLED(v3d);
DRW_draw_select_loop(
depsgraph, ar, v3d,
use_obedit_skip, draw_surface, use_nearest, &rect,
@@ -1104,7 +1105,7 @@ int view3d_opengl_select(
G.f &= ~G_FLAG_PICKSEL;
ED_view3d_draw_setup_view(vc->win, depsgraph, scene, ar, v3d, vc->rv3d->viewmat, NULL, NULL);
- if (v3d->shading.type > OB_WIRE) {
+ if (!XRAY_ACTIVE(v3d)) {
GPU_depth_test(false);
}
diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt
index 088128ba152..7d3690df826 100644
--- a/source/blender/editors/transform/CMakeLists.txt
+++ b/source/blender/editors/transform/CMakeLists.txt
@@ -54,10 +54,13 @@ set(SRC
transform.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_transform "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_transform "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index e785268b80a..4135a1b320d 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -205,6 +205,9 @@ static void applyAlign(TransInfo *t, const int mval[2]);
static void initSeqSlide(TransInfo *t);
static void applySeqSlide(TransInfo *t, const int mval[2]);
+
+static void initGPOpacity(TransInfo *t);
+static void applyGPOpacity(TransInfo *t, const int mval[2]);
/* end transform callbacks */
@@ -2035,7 +2038,7 @@ static void drawTransformPixel(const struct bContext *C, ARegion *ar, void *arg)
ViewLayer *view_layer = t->view_layer;
Object *ob = OBACT(view_layer);
- /* draw autokeyframing hint in the corner
+ /* draw auto-key-framing hint in the corner
* - only draw if enabled (advanced users may be distracted/annoyed),
* for objects that will be autokeyframed (no point otherwise),
* AND only for the active region (as showing all is too overwhelming)
@@ -2613,6 +2616,9 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
case TFM_NORMAL_ROTATION:
initNormalRotation(t);
break;
+ case TFM_GPENCIL_OPACITY:
+ initGPOpacity(t);
+ break;
}
if (t->state == TRANS_CANCEL) {
@@ -5525,6 +5531,84 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
}
/** \} */
+/* -------------------------------------------------------------------- */
+/* Transform (GPencil Opacity) */
+
+/** \name Transform GPencil Strokes Opacity
+ * \{ */
+
+static void initGPOpacity(TransInfo *t)
+{
+ t->mode = TFM_GPENCIL_OPACITY;
+ t->transform = applyGPOpacity;
+
+ initMouseInputMode(t, &t->mouse, INPUT_SPRING);
+
+ t->idx_max = 0;
+ t->num.idx_max = 0;
+ t->snap[0] = 0.0f;
+ t->snap[1] = 0.1f;
+ t->snap[2] = t->snap[1] * 0.1f;
+
+ copy_v3_fl(t->num.val_inc, t->snap[1]);
+ t->num.unit_sys = t->scene->unit.system;
+ t->num.unit_type[0] = B_UNIT_NONE;
+
+ t->flag |= T_NO_ZERO;
+#ifdef USE_NUM_NO_ZERO
+ t->num.val_flag[0] |= NUM_NO_ZERO;
+#endif
+
+ t->flag |= T_NO_CONSTRAINT;
+}
+
+static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2]))
+{
+ float ratio;
+ int i;
+ char str[UI_MAX_DRAW_STR];
+
+ ratio = t->values[0];
+
+ snapGridIncrement(t, &ratio);
+
+ applyNumInput(&t->num, &ratio);
+
+ t->values[0] = ratio;
+
+ /* header print for NumInput */
+ if (hasNumInput(&t->num)) {
+ char c[NUM_STR_REP_LEN];
+
+ outputNumInput(&(t->num), c, &t->scene->unit);
+ BLI_snprintf(str, sizeof(str), IFACE_("Opacity: %s"), c);
+ }
+ else {
+ BLI_snprintf(str, sizeof(str), IFACE_("Opacity: %3f"), ratio);
+ }
+
+ FOREACH_TRANS_DATA_CONTAINER(t, tc) {
+ TransData *td = tc->data;
+ for (i = 0; i < tc->data_len; i++, td++) {
+ if (td->flag & TD_NOACTION)
+ break;
+
+ if (td->flag & TD_SKIP)
+ continue;
+
+ if (td->val) {
+ *td->val = td->ival * ratio;
+ /* apply PET */
+ *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
+ CLAMP(*td->val, 0.0f, 1.0f);
+ }
+ }
+ }
+
+ ED_area_status_text(t->sa, str);
+}
+/** \} */
+
/* -------------------------------------------------------------------- */
/* Transform (Push/Pull) */
@@ -7094,7 +7178,7 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, TransDataContainer *t
if (t->spacetype == SPACE_VIEW3D) {
v3d = t->sa ? t->sa->spacedata.first : NULL;
rv3d = t->ar ? t->ar->regiondata : NULL;
- use_occlude_geometry = (v3d && TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->dt > OB_WIRE && v3d->shading.type > OB_WIRE);
+ use_occlude_geometry = (v3d && TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->dt > OB_WIRE && !XRAY_ENABLED(v3d));
}
calcEdgeSlide_mval_range(t, tc, sld, sv_table, loop_nr, mval, use_occlude_geometry, true);
@@ -7289,7 +7373,7 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, TransDataContainer *t
if (t->spacetype == SPACE_VIEW3D) {
v3d = t->sa ? t->sa->spacedata.first : NULL;
rv3d = t->ar ? t->ar->regiondata : NULL;
- use_occlude_geometry = (v3d && TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->dt > OB_WIRE && v3d->shading.type > OB_WIRE);
+ use_occlude_geometry = (v3d && TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->dt > OB_WIRE && !XRAY_ENABLED(v3d));
}
calcEdgeSlide_mval_range(t, tc, sld, sv_table, loop_nr, mval, use_occlude_geometry, false);
diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c
index 4bed5a3f05c..2e8a921c7d8 100644
--- a/source/blender/editors/transform/transform_constraints.c
+++ b/source/blender/editors/transform/transform_constraints.c
@@ -1005,7 +1005,7 @@ static void setNearestAxis3d(TransInfo *t)
mvec[1] = (float)(t->mval[1] - t->con.imval[1]);
mvec[2] = 0.0f;
- /* we need to correct axis length for the current zoomlevel of view,
+ /* We need to correct axis length for the current zoom-level of view,
* this to prevent projected values to be clipped behind the camera
* and to overflow the short integers.
* The formula used is a bit stupid, just a simplification of the subtraction
diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c
index 0fc36858e13..a3ee7dd5635 100644
--- a/source/blender/editors/transform/transform_conversions.c
+++ b/source/blender/editors/transform/transform_conversions.c
@@ -2292,7 +2292,7 @@ static bool bmesh_test_dist_add(
}
/**
- * \param mtx: Measure disatnce in this space.
+ * \param mtx: Measure distance in this space.
* \param dists: Store the closest connected distance to selected vertices.
* \param index: Optionally store the original index we're measuring the distance to (can be NULL).
*/
@@ -5806,17 +5806,17 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob)
if (t->mode == TFM_DUMMY)
skip_invert = true;
- /* NOTE: This is not really following copy-on-write design and we shoud not
+ /* NOTE: This is not really following copy-on-write design and we should not
* be re-evaluating the evaluated object. But as the comment above mentioned
* this is part of a hack.
- * More proper solution would be to make a shallwe copy of the object and
+ * More proper solution would be to make a shallow copy of the object and
* evaluate that, and access matrix of that evaluated copy of the object.
* Might be more tricky than it sounds, if some logic later on accesses the
* object matrix via td->ob->obmat. */
Object *object_eval = DEG_get_evaluated_object(t->depsgraph, ob);
if (skip_invert == false && constinv == false) {
object_eval->transflag |= OB_NO_CONSTRAINTS; /* BKE_object_where_is_calc checks this */
- /* It is possiblre to have transform data initialization prior to a
+ /* It is possible to have transform data initialization prior to a
* complete dependency graph evaluated. Happens, for example, when
* changing transformation mode. */
BKE_object_tfm_copy(object_eval, ob);
@@ -5829,7 +5829,7 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob)
/* Copy newly evaluated fields to the original object, similar to how
* active dependency graph will do it. */
copy_m4_m4(ob->obmat, object_eval->obmat);
- /* Only copy negative scale flag, this is the only flag which is modifed by
+ /* Only copy negative scale flag, this is the only flag which is modified by
* the BKE_object_where_is_calc(). The rest of the flags we need to keep,
* otherwise we might loose dupli flags (see T61787). */
ob->transflag &= ~OB_NEG_SCALE;
@@ -6465,8 +6465,8 @@ static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
TransData *td;
int i;
- /* rather then adjusting the selection (which the user would notice)
- * tag all mirrored verts, then automerge those */
+ /* Rather then adjusting the selection (which the user would notice)
+ * tag all mirrored verts, then auto-merge those. */
BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false);
for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
@@ -6528,8 +6528,8 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
EdgeSlideData *sld = tc->custom.mode.data;
- /* free temporary faces to avoid automerging and deleting
- * during cleanup - psy-fi */
+ /* Free temporary faces to avoid auto-merging and deleting
+ * during cleanup - psy-fi. */
freeEdgeSlideTempFaces(sld);
}
}
@@ -8357,8 +8357,8 @@ static void createTransGPencil(bContext *C, TransInfo *t)
curvemapping_initialize(ts->gp_sculpt.cur_falloff);
}
- /* First Pass: Count the number of datapoints required for the strokes,
- * (and additional info about the configuration - e.g. 2D/3D?)
+ /* First Pass: Count the number of data-points required for the strokes,
+ * (and additional info about the configuration - e.g. 2D/3D?).
*/
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
/* only editable and visible layers are considered */
@@ -8567,8 +8567,14 @@ static void createTransGPencil(bContext *C, TransInfo *t)
* but never for scale or mirror
*/
if ((t->mode != TFM_RESIZE) && (t->mode != TFM_MIRROR)) {
- td->val = &pt->pressure;
- td->ival = pt->pressure;
+ if (t->mode != TFM_GPENCIL_OPACITY) {
+ td->val = &pt->pressure;
+ td->ival = pt->pressure;
+ }
+ else {
+ td->val = &pt->strength;
+ td->ival = pt->strength;
+ }
}
/* screenspace needs special matrices... */
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index 93bfb7261a8..0f7992e0b4a 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -945,7 +945,7 @@ static void recalcData_objects(TransInfo *t)
/* if animtimer is running, and the object already has animation data,
* check if the auto-record feature means that we should record 'samples'
- * (i.e. uneditable animation values)
+ * (i.e. un-editable animation values)
*
* context is needed for keying set poll() functions.
*/
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index d1e91cd2a73..77480e1a8ce 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -40,6 +40,7 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BLI_string.h"
#include "BKE_action.h"
#include "BKE_context.h"
@@ -273,9 +274,9 @@ static bool gizmo_is_axis_visible(
}
}
- if ((axis_type == MAN_AXES_TRANSLATE && !(twtype & SCE_GIZMO_SHOW_TRANSLATE)) ||
- (axis_type == MAN_AXES_ROTATE && !(twtype & SCE_GIZMO_SHOW_ROTATE)) ||
- (axis_type == MAN_AXES_SCALE && !(twtype & SCE_GIZMO_SHOW_SCALE)))
+ if ((axis_type == MAN_AXES_TRANSLATE && !(twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE)) ||
+ (axis_type == MAN_AXES_ROTATE && !(twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE)) ||
+ (axis_type == MAN_AXES_SCALE && !(twtype & V3D_GIZMO_SHOW_OBJECT_SCALE)))
{
return false;
}
@@ -305,34 +306,34 @@ static bool gizmo_is_axis_visible(
case MAN_AXIS_SCALE_Z:
return (rv3d->twdrawflag & MAN_SCALE_Z);
case MAN_AXIS_SCALE_C:
- return (rv3d->twdrawflag & MAN_SCALE_C && (twtype & SCE_GIZMO_SHOW_TRANSLATE) == 0);
+ return (rv3d->twdrawflag & MAN_SCALE_C && (twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE) == 0);
case MAN_AXIS_TRANS_XY:
return (rv3d->twdrawflag & MAN_TRANS_X &&
rv3d->twdrawflag & MAN_TRANS_Y &&
- (twtype & SCE_GIZMO_SHOW_ROTATE) == 0);
+ (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
case MAN_AXIS_TRANS_YZ:
return (rv3d->twdrawflag & MAN_TRANS_Y &&
rv3d->twdrawflag & MAN_TRANS_Z &&
- (twtype & SCE_GIZMO_SHOW_ROTATE) == 0);
+ (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
case MAN_AXIS_TRANS_ZX:
return (rv3d->twdrawflag & MAN_TRANS_Z &&
rv3d->twdrawflag & MAN_TRANS_X &&
- (twtype & SCE_GIZMO_SHOW_ROTATE) == 0);
+ (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
case MAN_AXIS_SCALE_XY:
return (rv3d->twdrawflag & MAN_SCALE_X &&
rv3d->twdrawflag & MAN_SCALE_Y &&
- (twtype & SCE_GIZMO_SHOW_TRANSLATE) == 0 &&
- (twtype & SCE_GIZMO_SHOW_ROTATE) == 0);
+ (twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE) == 0 &&
+ (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
case MAN_AXIS_SCALE_YZ:
return (rv3d->twdrawflag & MAN_SCALE_Y &&
rv3d->twdrawflag & MAN_SCALE_Z &&
- (twtype & SCE_GIZMO_SHOW_TRANSLATE) == 0 &&
- (twtype & SCE_GIZMO_SHOW_ROTATE) == 0);
+ (twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE) == 0 &&
+ (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
case MAN_AXIS_SCALE_ZX:
return (rv3d->twdrawflag & MAN_SCALE_Z &&
rv3d->twdrawflag & MAN_SCALE_X &&
- (twtype & SCE_GIZMO_SHOW_TRANSLATE) == 0 &&
- (twtype & SCE_GIZMO_SHOW_ROTATE) == 0);
+ (twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE) == 0 &&
+ (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
}
return false;
}
@@ -1184,15 +1185,15 @@ static void gizmo_line_range(const int twtype, const short axis_type, float *r_s
switch (axis_type) {
case MAN_AXES_TRANSLATE:
- if (twtype & SCE_GIZMO_SHOW_SCALE) {
+ if (twtype & V3D_GIZMO_SHOW_OBJECT_SCALE) {
*r_start = *r_len - ofs + 0.075f;
}
- if (twtype & SCE_GIZMO_SHOW_ROTATE) {
+ if (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) {
*r_len += ofs;
}
break;
case MAN_AXES_SCALE:
- if (twtype & (SCE_GIZMO_SHOW_TRANSLATE | SCE_GIZMO_SHOW_ROTATE)) {
+ if (twtype & (V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE)) {
*r_len -= ofs + 0.025f;
}
break;
@@ -1203,7 +1204,7 @@ static void gizmo_line_range(const int twtype, const short axis_type, float *r_s
static void gizmo_xform_message_subscribe(
wmGizmoGroup *gzgroup, struct wmMsgBus *mbus,
- Scene *scene, bScreen *UNUSED(screen), ScrArea *UNUSED(sa), ARegion *ar, const void *type_fn)
+ Scene *scene, bScreen *screen, ScrArea *sa, ARegion *ar, const void *type_fn)
{
/* Subscribe to view properties */
wmMsgSubscribeValue msg_sub_value_gz_tag_refresh = {
@@ -1213,18 +1214,18 @@ static void gizmo_xform_message_subscribe(
};
int orient_flag = 0;
- if (type_fn == TRANSFORM_GGT_gizmo) {
+ if (type_fn == VIEW3D_GGT_xform_gizmo) {
GizmoGroup *ggd = gzgroup->customdata;
orient_flag = ggd->twtype_init;
}
else if (type_fn == VIEW3D_GGT_xform_cage) {
- orient_flag = SCE_GIZMO_SHOW_SCALE;
+ orient_flag = V3D_GIZMO_SHOW_OBJECT_SCALE;
/* pass */
}
else if (type_fn == VIEW3D_GGT_xform_shear) {
- orient_flag = SCE_GIZMO_SHOW_ROTATE;
+ orient_flag = V3D_GIZMO_SHOW_OBJECT_ROTATE;
}
- TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, orient_flag);
+ TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(scene, orient_flag);
PointerRNA orient_ref_ptr;
RNA_pointer_create(&scene->id, &RNA_TransformOrientationSlot, orient_slot, &orient_ref_ptr);
const ToolSettings *ts = scene->toolsettings;
@@ -1267,18 +1268,35 @@ static void gizmo_xform_message_subscribe(
PointerRNA toolsettings_ptr;
RNA_pointer_create(&scene->id, &RNA_ToolSettings, scene->toolsettings, &toolsettings_ptr);
- if (type_fn == TRANSFORM_GGT_gizmo) {
- GizmoGroup *ggd = gzgroup->customdata;
+ if (type_fn == VIEW3D_GGT_xform_gizmo) {
extern PropertyRNA rna_ToolSettings_transform_pivot_point;
- extern PropertyRNA rna_ToolSettings_use_gizmo_mode;
const PropertyRNA *props[] = {
&rna_ToolSettings_transform_pivot_point,
- ggd->use_twtype_refresh ? &rna_ToolSettings_use_gizmo_mode : NULL,
};
for (int i = 0; i < ARRAY_SIZE(props); i++) {
WM_msg_subscribe_rna(mbus, &toolsettings_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
}
}
+
+ PointerRNA view3d_ptr;
+ RNA_pointer_create(&screen->id, &RNA_SpaceView3D, sa->spacedata.first, &view3d_ptr);
+
+ if (type_fn == VIEW3D_GGT_xform_gizmo) {
+ GizmoGroup *ggd = gzgroup->customdata;
+ if (ggd->use_twtype_refresh) {
+ extern PropertyRNA rna_SpaceView3D_show_gizmo_object_translate;
+ extern PropertyRNA rna_SpaceView3D_show_gizmo_object_rotate;
+ extern PropertyRNA rna_SpaceView3D_show_gizmo_object_scale;
+ const PropertyRNA *props[] = {
+ &rna_SpaceView3D_show_gizmo_object_translate,
+ &rna_SpaceView3D_show_gizmo_object_rotate,
+ &rna_SpaceView3D_show_gizmo_object_scale,
+ };
+ for (int i = 0; i < ARRAY_SIZE(props); i++) {
+ WM_msg_subscribe_rna(mbus, &view3d_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
+ }
+ }
+ }
else if (type_fn == VIEW3D_GGT_xform_cage) {
/* pass */
}
@@ -1513,7 +1531,7 @@ static void gizmogroup_init_properties_from_twtype(wmGizmoGroup *gzgroup)
case MAN_AXIS_SCALE_Z:
if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
int draw_options = 0;
- if ((ggd->twtype & (SCE_GIZMO_SHOW_ROTATE | SCE_GIZMO_SHOW_SCALE)) == 0) {
+ if ((ggd->twtype & (V3D_GIZMO_SHOW_OBJECT_ROTATE | V3D_GIZMO_SHOW_OBJECT_SCALE)) == 0) {
draw_options |= ED_GIZMO_ARROW_DRAW_FLAG_STEM;
}
RNA_enum_set(axis->ptr, "draw_options", draw_options);
@@ -1616,23 +1634,23 @@ static void WIDGETGROUP_gizmo_setup(const bContext *C, wmGizmoGroup *gzgroup)
gzgroup->customdata = ggd;
{
- ggd->twtype = 0;
ScrArea *sa = CTX_wm_area(C);
const bToolRef *tref = sa->runtime.tool;
- if (tref == NULL || STREQ(tref->idname, "builtin.transform")) {
- /* Setup all gizmos, they can be toggled via 'ToolSettings.gizmo_flag' */
- ggd->twtype = SCE_GIZMO_SHOW_TRANSLATE | SCE_GIZMO_SHOW_ROTATE | SCE_GIZMO_SHOW_SCALE;
- ggd->use_twtype_refresh = true;
+ ggd->twtype = 0;
+ if (tref && STREQ(tref->idname, "builtin.move")) {
+ ggd->twtype |= V3D_GIZMO_SHOW_OBJECT_TRANSLATE;
}
- else if (STREQ(tref->idname, "builtin.move")) {
- ggd->twtype |= SCE_GIZMO_SHOW_TRANSLATE;
+ else if (tref && STREQ(tref->idname, "builtin.rotate")) {
+ ggd->twtype |= V3D_GIZMO_SHOW_OBJECT_ROTATE;
}
- else if (STREQ(tref->idname, "builtin.rotate")) {
- ggd->twtype |= SCE_GIZMO_SHOW_ROTATE;
+ else if (tref && STREQ(tref->idname, "builtin.scale")) {
+ ggd->twtype |= V3D_GIZMO_SHOW_OBJECT_SCALE;
}
- else if (STREQ(tref->idname, "builtin.scale")) {
- ggd->twtype |= SCE_GIZMO_SHOW_SCALE;
+ else {
+ /* Setup all gizmos, they can be toggled via 'ToolSettings.gizmo_flag' */
+ ggd->twtype = V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE | V3D_GIZMO_SHOW_OBJECT_SCALE;
+ ggd->use_twtype_refresh = true;
}
BLI_assert(ggd->twtype != 0);
ggd->twtype_init = ggd->twtype;
@@ -1646,19 +1664,21 @@ static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup)
{
GizmoGroup *ggd = gzgroup->customdata;
Scene *scene = CTX_data_scene(C);
+ ScrArea *sa = CTX_wm_area(C);
+ View3D *v3d = sa->spacedata.first;
ARegion *ar = CTX_wm_region(C);
RegionView3D *rv3d = ar->regiondata;
struct TransformBounds tbounds;
if (ggd->use_twtype_refresh) {
- ggd->twtype = scene->toolsettings->gizmo_flag & ggd->twtype_init;
+ ggd->twtype = v3d->gizmo_show_object & ggd->twtype_init;
if (ggd->twtype != ggd->twtype_prev) {
ggd->twtype_prev = ggd->twtype;
gizmogroup_init_properties_from_twtype(gzgroup);
}
}
- const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, ggd->twtype_init);
+ const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(scene, ggd->twtype_init);
/* skip, we don't draw anything anyway */
if ((ggd->all_hidden =
@@ -1700,7 +1720,7 @@ static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup)
RNA_float_set(axis->ptr, "length", len);
if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
- if (ggd->twtype & SCE_GIZMO_SHOW_ROTATE) {
+ if (ggd->twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) {
/* Avoid rotate and translate arrows overlap. */
start_co[2] += 0.215f;
}
@@ -1738,7 +1758,7 @@ static void WIDGETGROUP_gizmo_message_subscribe(
bScreen *screen = CTX_wm_screen(C);
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
- gizmo_xform_message_subscribe(gzgroup, mbus, scene, screen, sa, ar, TRANSFORM_GGT_gizmo);
+ gizmo_xform_message_subscribe(gzgroup, mbus, scene, screen, sa, ar, VIEW3D_GGT_xform_gizmo);
}
static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
@@ -1796,7 +1816,7 @@ static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgr
if (!equals_m3m3(viewinv_m3, ggd->prev.viewinv_m3)) {
{
Scene *scene = CTX_data_scene(C);
- const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, ggd->twtype_init);
+ const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(scene, ggd->twtype_init);
switch (orient_slot->type) {
case V3D_ORIENT_VIEW:
{
@@ -1822,7 +1842,7 @@ static void WIDGETGROUP_gizmo_invoke_prepare(
wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, 0);
PointerRNA *ptr = &gzop->ptr;
PropertyRNA *prop_orient_type = RNA_struct_find_property(ptr, "orient_type");
- const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, ggd->twtype_init);
+ const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(scene, ggd->twtype_init);
if (orient_slot == &scene->orientation_slots[SCE_ORIENT_DEFAULT]) {
RNA_property_unset(ptr, prop_orient_type);
}
@@ -1871,46 +1891,95 @@ static void WIDGETGROUP_gizmo_invoke_prepare(
}
}
-static bool WIDGETGROUP_gizmo_poll(const struct bContext *C, struct wmGizmoGroupType *gzgt)
+static bool WIDGETGROUP_gizmo_poll_generic(View3D *v3d)
+{
+ if (v3d->gizmo_flag & V3D_GIZMO_HIDE) {
+ return false;
+ }
+ if (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) {
+ return false;
+ }
+ return true;
+}
+
+static bool WIDGETGROUP_gizmo_poll_context(const struct bContext *C, struct wmGizmoGroupType *UNUSED(gzgt))
+{
+ ScrArea *sa = CTX_wm_area(C);
+ View3D *v3d = sa->spacedata.first;
+ if (!WIDGETGROUP_gizmo_poll_generic(v3d)) {
+ return false;
+ }
+
+ const bToolRef *tref = sa->runtime.tool;
+ if (v3d->gizmo_flag & V3D_GIZMO_HIDE_CONTEXT) {
+ return false;
+ }
+ if ((v3d->gizmo_show_object & (V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE | V3D_GIZMO_SHOW_OBJECT_SCALE)) == 0) {
+ return false;
+ }
+
+ /* Don't show if the tool has a gizmo. */
+ if (tref && tref->runtime && tref->runtime->gizmo_group[0]) {
+ return false;
+ }
+ return true;
+}
+
+static bool WIDGETGROUP_gizmo_poll_tool(const struct bContext *C, struct wmGizmoGroupType *gzgt)
{
if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) {
return false;
}
- View3D *v3d = CTX_wm_view3d(C);
- if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)) {
+
+ return true;
+ ScrArea *sa = CTX_wm_area(C);
+ View3D *v3d = sa->spacedata.first;
+ if (!WIDGETGROUP_gizmo_poll_generic(v3d)) {
return false;
}
- if (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) {
+
+ if (v3d->gizmo_flag & V3D_GIZMO_HIDE_TOOL) {
return false;
}
+
return true;
}
-void TRANSFORM_GGT_gizmo(wmGizmoGroupType *gzgt)
+/* Expose as multiple gizmos so tools use one, persistant context another.
+ * Needed because they use different options which isn't so simple to dynamically update. */
+
+void VIEW3D_GGT_xform_gizmo(wmGizmoGroupType *gzgt)
{
gzgt->name = "Transform Gizmo";
- gzgt->idname = "TRANSFORM_GGT_gizmo";
+ gzgt->idname = "VIEW3D_GGT_xform_gizmo";
- gzgt->flag |= WM_GIZMOGROUPTYPE_3D;
+ gzgt->flag = WM_GIZMOGROUPTYPE_3D;
gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
- gzgt->poll = WIDGETGROUP_gizmo_poll;
+ gzgt->poll = WIDGETGROUP_gizmo_poll_tool;
gzgt->setup = WIDGETGROUP_gizmo_setup;
gzgt->refresh = WIDGETGROUP_gizmo_refresh;
gzgt->message_subscribe = WIDGETGROUP_gizmo_message_subscribe;
gzgt->draw_prepare = WIDGETGROUP_gizmo_draw_prepare;
gzgt->invoke_prepare = WIDGETGROUP_gizmo_invoke_prepare;
+}
- static const EnumPropertyItem rna_enum_gizmo_items[] = {
- {SCE_GIZMO_SHOW_TRANSLATE, "TRANSLATE", 0, "Move", ""},
- {SCE_GIZMO_SHOW_ROTATE, "ROTATE", 0, "Rotate", ""},
- {SCE_GIZMO_SHOW_SCALE, "SCALE", 0, "Scale", ""},
- {0, "NONE", 0, "None", ""},
- {0, NULL, 0, NULL, NULL},
- };
- RNA_def_enum(gzgt->srna, "drag_action", rna_enum_gizmo_items, SCE_GIZMO_SHOW_TRANSLATE, "Drag Action", "");
+/** Only poll, flag & gzmap_params differ. */
+void VIEW3D_GGT_xform_gizmo_context(wmGizmoGroupType *gzgt)
+{
+ gzgt->name = "Transform Gizmo Context";
+ gzgt->idname = "VIEW3D_GGT_xform_gizmo_context";
+
+ gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_PERSISTENT;
+
+ gzgt->poll = WIDGETGROUP_gizmo_poll_context;
+ gzgt->setup = WIDGETGROUP_gizmo_setup;
+ gzgt->refresh = WIDGETGROUP_gizmo_refresh;
+ gzgt->message_subscribe = WIDGETGROUP_gizmo_message_subscribe;
+ gzgt->draw_prepare = WIDGETGROUP_gizmo_draw_prepare;
+ gzgt->invoke_prepare = WIDGETGROUP_gizmo_invoke_prepare;
}
/** \} */
@@ -1997,7 +2066,7 @@ static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmGizmoGroup *gzgr
struct TransformBounds tbounds;
- const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, SCE_GIZMO_SHOW_SCALE);
+ const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, SCE_ORIENT_SCALE);
if ((ED_transform_calc_gizmo_stats(
C, &(struct TransformCalcParams) {
@@ -2079,7 +2148,7 @@ static void WIDGETGROUP_xform_cage_draw_prepare(const bContext *C, wmGizmoGroup
RegionView3D *rv3d = CTX_wm_region_view3d(C);
{
Scene *scene = CTX_data_scene(C);
- const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, SCE_GIZMO_SHOW_SCALE);
+ const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, SCE_ORIENT_SCALE);
switch (orient_slot->type) {
case V3D_ORIENT_VIEW:
{
@@ -2177,7 +2246,7 @@ static void WIDGETGROUP_xform_shear_refresh(const bContext *C, wmGizmoGroup *gzg
struct XFormShearWidgetGroup *xgzgroup = gzgroup->customdata;
struct TransformBounds tbounds;
- const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, SCE_GIZMO_SHOW_ROTATE);
+ const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, SCE_ORIENT_ROTATE);
if (ED_transform_calc_gizmo_stats(
C, &(struct TransformCalcParams) {
@@ -2240,7 +2309,7 @@ static void WIDGETGROUP_xform_shear_draw_prepare(const bContext *C, wmGizmoGroup
{
Scene *scene = CTX_data_scene(C);
/* Shear is like rotate, use the rotate setting. */
- const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, SCE_GIZMO_SHOW_ROTATE);
+ const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, SCE_ORIENT_ROTATE);
switch (orient_slot->type) {
case V3D_ORIENT_VIEW:
{
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index 648e616a27c..7140938c99a 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -155,6 +155,7 @@ const EnumPropertyItem rna_enum_transform_mode_types[] =
{TFM_ALIGN, "ALIGN", 0, "Align", ""},
{TFM_EDGE_SLIDE, "EDGESLIDE", 0, "Edge Slide", ""},
{TFM_SEQ_SLIDE, "SEQSLIDE", 0, "Sequence Slide", ""},
+ {TFM_GPENCIL_OPACITY, "GPENCIL_OPACITY", 0, "GPencil_Opacity", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -1148,58 +1149,6 @@ static void TRANSFORM_OT_transform(struct wmOperatorType *ot)
P_GPENCIL_EDIT | P_CENTER);
}
-static int transform_from_gizmo_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
-{
- bToolRef *tref = WM_toolsystem_ref_from_context(C);
- if (tref) {
- ARegion *ar = CTX_wm_region(C);
- wmGizmoMap *gzmap = ar->gizmo_map;
- wmGizmoGroup *gzgroup = gzmap ? WM_gizmomap_group_find(gzmap, "TRANSFORM_GGT_gizmo") : NULL;
- if (gzgroup != NULL) {
- PointerRNA gzg_ptr;
- WM_toolsystem_ref_properties_ensure_from_gizmo_group(tref, gzgroup->type, &gzg_ptr);
- const int drag_action = RNA_enum_get(&gzg_ptr, "drag_action");
- const char *op_id = NULL;
- switch (drag_action) {
- case SCE_GIZMO_SHOW_TRANSLATE:
- op_id = "TRANSFORM_OT_translate";
- break;
- case SCE_GIZMO_SHOW_ROTATE:
- op_id = "TRANSFORM_OT_rotate";
- break;
- case SCE_GIZMO_SHOW_SCALE:
- op_id = "TRANSFORM_OT_resize";
- break;
- default:
- break;
- }
- if (op_id) {
- wmOperatorType *ot = WM_operatortype_find(op_id, true);
- PointerRNA op_ptr;
- WM_operator_properties_create_ptr(&op_ptr, ot);
- RNA_boolean_set(&op_ptr, "release_confirm", true);
- WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_ptr);
- WM_operator_properties_free(&op_ptr);
- return OPERATOR_FINISHED;
- }
- }
- }
- return OPERATOR_PASS_THROUGH;
-}
-
-/* Use with 'TRANSFORM_GGT_gizmo'. */
-static void TRANSFORM_OT_from_gizmo(struct wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Transform From Gizmo";
- ot->description = "Transform selected items by mode type";
- ot->idname = "TRANSFORM_OT_from_gizmo";
- ot->flag = 0;
-
- /* api callbacks */
- ot->invoke = transform_from_gizmo_invoke;
-}
-
void transform_operatortypes(void)
{
TransformModeItem *tmode;
@@ -1213,8 +1162,6 @@ void transform_operatortypes(void)
WM_operatortype_append(TRANSFORM_OT_select_orientation);
WM_operatortype_append(TRANSFORM_OT_create_orientation);
WM_operatortype_append(TRANSFORM_OT_delete_orientation);
-
- WM_operatortype_append(TRANSFORM_OT_from_gizmo);
}
void ED_keymap_transform(wmKeyConfig *keyconf)
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index ea3657b8729..f2692f57a35 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -673,21 +673,22 @@ static bool raycastObj(
{
bool retval = false;
- if (use_occlusion_test) {
- if (use_obedit && sctx->use_v3d &&
- !V3D_IS_ZBUF(sctx->v3d_data.v3d))
- {
- /* Use of occlude geometry in editing mode disabled. */
- return false;
- }
- }
-
switch (ob->type) {
case OB_MESH:
{
- if (ob->dt == OB_BOUNDBOX || ob->dt == OB_WIRE) {
- /* Do not hit objects that are in wire or bounding box display mode */
- return false;
+ if (use_occlusion_test) {
+ if (use_obedit && sctx->use_v3d &&
+ XRAY_ENABLED(sctx->v3d_data.v3d))
+ {
+ /* Use of occlude geometry in editing mode disabled. */
+ return false;
+ }
+
+ if (ELEM(ob->dt, OB_BOUNDBOX, OB_WIRE)) {
+ /* Do not hit objects that are in wire or bounding box
+ * display mode. */
+ return false;
+ }
}
Mesh *me = ob->data;
@@ -2540,8 +2541,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
const RegionView3D *rv3d = ar->regiondata;
bool use_occlusion_test =
- params->use_occlusion_test &&
- !(sctx->v3d_data.v3d->shading.flag & V3D_XRAY_FLAG(sctx->v3d_data.v3d));
+ params->use_occlusion_test && !XRAY_ENABLED(sctx->v3d_data.v3d);
if (snap_to_flag & SCE_SNAP_MODE_FACE || use_occlusion_test) {
float ray_start[3], ray_normal[3];
diff --git a/source/blender/editors/undo/CMakeLists.txt b/source/blender/editors/undo/CMakeLists.txt
index 6ccba01c465..3a6aded0c08 100644
--- a/source/blender/editors/undo/CMakeLists.txt
+++ b/source/blender/editors/undo/CMakeLists.txt
@@ -36,8 +36,11 @@ set(SRC
undo_intern.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
-blender_add_lib(bf_editor_undo "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_undo "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index 55298e5c4ab..728dacc1649 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -99,6 +99,9 @@ set(SRC
../include/UI_view2d.h
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
if(WITH_INTERNATIONAL)
@@ -112,4 +115,4 @@ if(WITH_PYTHON)
)
endif()
-blender_add_lib(bf_editor_util "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_util "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt
index c54c3851ee7..45cd9b09de3 100644
--- a/source/blender/editors/uvedit/CMakeLists.txt
+++ b/source/blender/editors/uvedit/CMakeLists.txt
@@ -47,10 +47,13 @@ set(SRC
uvedit_parametrizer.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_editor_uvedit "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_editor_uvedit "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index a4247ec370d..1bcf60077ff 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -2827,10 +2827,12 @@ static void UV_OT_select_linked_pick(wmOperatorType *ot)
/** \name Select Split Operator
* \{ */
-/* note: this is based on similar use case to MESH_OT_split(), which has a similar effect
+/**
+ * \note This is based on similar use case to #MESH_OT_split(), which has a similar effect
* but in this case they are not joined to begin with (only having the behavior of being joined)
- * so its best to call this uv_select_split() instead of just split(), but assigned to the same key
- * as MESH_OT_split - Campbell */
+ * so its best to call this #uv_select_split() instead of just split(), but assigned to the same key
+ * as #MESH_OT_split - Campbell.
+ */
static int uv_select_split_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
diff --git a/source/blender/freestyle/CMakeLists.txt b/source/blender/freestyle/CMakeLists.txt
index edab13d4286..9dfeb065902 100644
--- a/source/blender/freestyle/CMakeLists.txt
+++ b/source/blender/freestyle/CMakeLists.txt
@@ -547,6 +547,9 @@ set(SRC
intern/winged_edge/WingedEdgeBuilder.h
)
+set(LIB
+)
+
set(INC
.
../blenkernel
@@ -581,4 +584,4 @@ if(WIN32)
)
endif()
-blender_add_lib(bf_freestyle "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_freestyle "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt
index e1aad91050a..f7cfe073d4a 100644
--- a/source/blender/gpencil_modifiers/CMakeLists.txt
+++ b/source/blender/gpencil_modifiers/CMakeLists.txt
@@ -63,10 +63,13 @@ set(SRC
MOD_gpencil_modifiertypes.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_gpencil_modifiers "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_gpencil_modifiers "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
index cc6d316bd3a..c66580cd5d4 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
@@ -188,7 +188,7 @@ void gpencil_apply_modifier_material(
DEG_id_tag_update(&newmat->id, ID_RECALC_COPY_ON_WRITE);
}
/* reasign color index */
- int idx = BKE_gpencil_get_material_index(ob, newmat);
+ int idx = BKE_gpencil_object_material_get_index(ob, newmat);
gps->mat_nr = idx - 1;
}
else {
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index b76edf4068a..430eefe6fc7 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -123,6 +123,9 @@ set(SRC
intern/gpu_vertex_format_private.h
)
+set(LIB
+)
+
data_to_c_simple(shaders/gpu_shader_depth_only_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_uniform_color_frag.glsl SRC)
data_to_c_simple(shaders/gpu_shader_checker_frag.glsl SRC)
@@ -255,4 +258,4 @@ if(WITH_IMAGE_DDS)
add_definitions(-DWITH_DDS)
endif()
-blender_add_lib(bf_gpu "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_gpu "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h
index e822c33ab4a..77573b80a1b 100644
--- a/source/blender/gpu/GPU_framebuffer.h
+++ b/source/blender/gpu/GPU_framebuffer.h
@@ -96,7 +96,7 @@ void GPU_framebuffer_texture_detach_slot(
* })
* \encode
*
- * \note Unspecified attachements (i.e: those beyond the last
+ * \note Unspecified attachments (i.e: those beyond the last
* GPU_ATTACHMENT_* in GPU_framebuffer_ensure_config list) are left unchanged.
*
* \note Make sure that the dimensions of your textures matches
diff --git a/source/blender/gpu/GPU_immediate.h b/source/blender/gpu/GPU_immediate.h
index 57912c01991..b7d2901daeb 100644
--- a/source/blender/gpu/GPU_immediate.h
+++ b/source/blender/gpu/GPU_immediate.h
@@ -124,8 +124,10 @@ void immUniformColor3ubv(const unsigned char rgb[3]);
void immUniformColor3ubvAlpha(const unsigned char rgb[3], unsigned char a);
void immUniformColor4ubv(const unsigned char rgba[4]);
-/* Extend immBindProgram to use Blender’s library of built-in shader programs.
- * Use immUnbindProgram() when done. */
+/**
+ * Extend #immBindProgram to use Blender’s library of built-in shader programs.
+ * Use #immUnbindProgram() when done.
+ */
void immBindBuiltinProgram(eGPUBuiltinShader shader_id);
/* Extend immUniformColor to take Blender's themes */
diff --git a/source/blender/gpu/GPU_legacy_stubs.h b/source/blender/gpu/GPU_legacy_stubs.h
index b290e4b093c..9d45c051f39 100644
--- a/source/blender/gpu/GPU_legacy_stubs.h
+++ b/source/blender/gpu/GPU_legacy_stubs.h
@@ -40,7 +40,7 @@
#include "BLI_utildefines.h"
/**
- * Empty function, use for breakpoint when a depreacated
+ * Empty function, use for breakpoint when a deprecated
* OpenGL function is called.
*/
static void gl_deprecated(void)
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index 0b69b0c6cd8..552ae8f4cd7 100644
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ b/source/blender/gpu/intern/gpu_codegen.c
@@ -1349,7 +1349,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType
name = outnode->name;
input = outnode->inputs.first;
- if ((STREQ(name, "set_value") || STREQ(name, "set_rgb") || STREQ(name, "set_rgba")) &&
+ if ((STR_ELEM(name, "set_value", "set_rgb", "set_rgba")) &&
(input->type == type))
{
input = MEM_dupallocN(outnode->inputs.first);
diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c
index 6d546c1b5c5..53a69b0b9d7 100644
--- a/source/blender/gpu/intern/gpu_immediate_util.c
+++ b/source/blender/gpu/intern/gpu_immediate_util.c
@@ -144,7 +144,7 @@ void immRecti_complete(int x1, int y1, int x2, int y2, const float color[4])
* Pack color into 3 bytes
*
* This define converts a numerical value to the equivalent 24-bit
- * color, while not being endian-sensitive. On little-endians, this
+ * color, while not being endian-sensitive. On little-endian, this
* is the same as doing a 'naive' indexing, on big-endian, it is not!
*
* \note BGR format (i.e. 0xBBGGRR)...
diff --git a/source/blender/gpu/intern/gpu_uniformbuffer.c b/source/blender/gpu/intern/gpu_uniformbuffer.c
index 47539724a26..a5559b2558a 100644
--- a/source/blender/gpu/intern/gpu_uniformbuffer.c
+++ b/source/blender/gpu/intern/gpu_uniformbuffer.c
@@ -107,7 +107,7 @@ GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_
* Create dynamic UBO from parameters
* Return NULL if failed to create or if \param inputs: is empty.
*
- * \param inputs: ListBase of BLI_genericNodeN(GPUInput)
+ * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput).
*/
GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_out[256])
{
diff --git a/source/blender/ikplugin/CMakeLists.txt b/source/blender/ikplugin/CMakeLists.txt
index 648486072f0..9bbc3a62b99 100644
--- a/source/blender/ikplugin/CMakeLists.txt
+++ b/source/blender/ikplugin/CMakeLists.txt
@@ -39,6 +39,9 @@ set(SRC
intern/ikplugin_api.h
)
+set(LIB
+)
+
if(WITH_IK_SOLVER)
add_definitions(-DWITH_IK_SOLVER)
list(APPEND INC
@@ -64,4 +67,4 @@ if(WITH_IK_ITASC)
)
endif()
-blender_add_lib(bf_ikplugin "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_ikplugin "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt
index dd020ffe1f2..87002e5efc0 100644
--- a/source/blender/imbuf/CMakeLists.txt
+++ b/source/blender/imbuf/CMakeLists.txt
@@ -83,6 +83,9 @@ set(SRC
../../../intern/ffmpeg/ffmpeg_compat.h
)
+set(LIB
+)
+
if(WITH_IMAGE_OPENEXR)
add_definitions(-DWITH_OPENEXR)
else()
@@ -172,4 +175,4 @@ set_source_files_properties(
PROPERTIES HEADER_FILE_ONLY TRUE
)
-blender_add_lib(bf_imbuf "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_imbuf "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/imbuf/intern/cineon/CMakeLists.txt b/source/blender/imbuf/intern/cineon/CMakeLists.txt
index 2a592aba860..2f6ca2793e3 100644
--- a/source/blender/imbuf/intern/cineon/CMakeLists.txt
+++ b/source/blender/imbuf/intern/cineon/CMakeLists.txt
@@ -45,8 +45,11 @@ set(SRC
logmemfile.c
)
+set(LIB
+)
+
if(WITH_IMAGE_CINEON)
add_definitions(-DWITH_CINEON)
endif()
-blender_add_lib(bf_imbuf_cineon "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_imbuf_cineon "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/imbuf/intern/dds/CMakeLists.txt b/source/blender/imbuf/intern/dds/CMakeLists.txt
index 6728dbc758d..c68608f4cff 100644
--- a/source/blender/imbuf/intern/dds/CMakeLists.txt
+++ b/source/blender/imbuf/intern/dds/CMakeLists.txt
@@ -54,8 +54,11 @@ set(SRC
dds_api.cpp
)
+set(LIB
+)
+
if(WITH_IMAGE_DDS)
add_definitions(-DWITH_DDS)
endif()
-blender_add_lib(bf_imbuf_dds "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_imbuf_dds "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/imbuf/intern/oiio/CMakeLists.txt b/source/blender/imbuf/intern/oiio/CMakeLists.txt
index 73438436abc..3311d3fd9b4 100644
--- a/source/blender/imbuf/intern/oiio/CMakeLists.txt
+++ b/source/blender/imbuf/intern/oiio/CMakeLists.txt
@@ -39,6 +39,9 @@ set(SRC
openimageio_api.cpp
)
+set(LIB
+)
+
if(WITH_OPENIMAGEIO)
list(APPEND INC_SYS
${OPENIMAGEIO_INCLUDE_DIRS}
@@ -52,4 +55,4 @@ if(WITH_OPENIMAGEIO)
add_definitions(-DWITH_OPENIMAGEIO)
endif()
-blender_add_lib(bf_imbuf_openimageio "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_imbuf_openimageio "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/imbuf/intern/openexr/CMakeLists.txt b/source/blender/imbuf/intern/openexr/CMakeLists.txt
index 18f68d5b365..c9ede9ff07f 100644
--- a/source/blender/imbuf/intern/openexr/CMakeLists.txt
+++ b/source/blender/imbuf/intern/openexr/CMakeLists.txt
@@ -40,6 +40,9 @@ set(SRC
openexr_api.cpp
)
+set(LIB
+)
+
if(WITH_IMAGE_OPENEXR)
list(APPEND INC_SYS
${OPENEXR_INCLUDE_DIRS}
@@ -47,4 +50,4 @@ if(WITH_IMAGE_OPENEXR)
add_definitions(-DWITH_OPENEXR)
endif()
-blender_add_lib(bf_imbuf_openexr "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_imbuf_openexr "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h
index f17b93fed62..b6fed5fd941 100644
--- a/source/blender/makesdna/DNA_anim_types.h
+++ b/source/blender/makesdna/DNA_anim_types.h
@@ -1109,7 +1109,7 @@ typedef enum eAnimData_Flag {
/* Base Struct for Anim ------------------------------------- */
/**
- * Used for BKE_animdata_from_id()
+ * Used for #BKE_animdata_from_id()
* All ID-datablocks which have their own 'local' AnimData
* should have the same arrangement in their structs.
*/
diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h
index 718ca4f3b7d..3db8bf92f56 100644
--- a/source/blender/makesdna/DNA_material_types.h
+++ b/source/blender/makesdna/DNA_material_types.h
@@ -147,11 +147,14 @@ typedef struct Material {
float gloss_mir DNA_DEPRECATED;
float roughness;
float metallic;
- char _pad0[2];
- /** For buttons and render. */
- char pr_type, use_nodes;
+ /** Nodes */
+ char use_nodes;
+
+ /** Preview render. */
+ char pr_type;
short pr_texture;
+ short pr_flag;
/** Index for render passes. */
short index;
@@ -279,13 +282,18 @@ typedef struct Material {
#define MA_FLAT 0
#define MA_SPHERE 1
#define MA_CUBE 2
-#define MA_MONKEY 3
-#define MA_SPHERE_A 4
+#define MA_SHADERBALL 3
+#define MA_SPHERE_A 4 /* Used for icon renders only. */
#define MA_TEXTURE 5
#define MA_LAMP 6
#define MA_SKY 7
#define MA_HAIR 10
#define MA_ATMOS 11
+#define MA_CLOTH 12
+#define MA_FLUID 13
+
+/* pr_flag */
+#define MA_PREVIEW_WORLD (1 << 0)
/* blend_method */
enum {
diff --git a/source/blender/makesdna/DNA_object_force_types.h b/source/blender/makesdna/DNA_object_force_types.h
index 4b276a0aa85..edd5d305683 100644
--- a/source/blender/makesdna/DNA_object_force_types.h
+++ b/source/blender/makesdna/DNA_object_force_types.h
@@ -185,11 +185,12 @@ typedef struct EffectorWeights {
/* EffectorWeights->flag */
#define EFF_WEIGHT_DO_HAIR 1
-/* Point cache file data types:
- * - used as (1 << flag) so poke jahka if you reach the limit of 15
- * - to add new data types update:
- * - BKE_ptcache_data_size()
- * - ptcache_file_init_pointers()
+/**
+ * Point cache file data types:
+ * - Used as `(1 << flag)` so poke jahka if you reach the limit of 15.
+ * - To add new data types update:
+ * - #BKE_ptcache_data_size()
+ * - #ptcache_file_pointers_init()
*/
#define BPHYS_DATA_INDEX 0
#define BPHYS_DATA_LOCATION 1
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 9f2118d9cc0..c8684f42963 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -202,7 +202,7 @@ typedef struct Object {
void *data;
/** Grease Pencil data. */
- struct bGPdata *gpd;
+ struct bGPdata *gpd DNA_DEPRECATED; // XXX deprecated... replaced by gpencil object, keep for readfile
/** Settings for visualization of object-transform animation. */
bAnimVizSettings avs;
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 75dda608975..49ebc9b9bac 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1466,8 +1466,7 @@ typedef struct ToolSettings {
char edge_mode;
char edge_mode_live_unwrap;
- /* SCE_GIZMO_SHOW_* */
- char gizmo_flag;
+ char _pad1[1];
/* Transform */
char transform_pivot_point;
@@ -2272,13 +2271,6 @@ enum {
#define EDGE_MODE_TAG_BEVEL 4
#define EDGE_MODE_TAG_FREESTYLE 5
-/* ToolSettings.gizmo_flag */
-enum {
- SCE_GIZMO_SHOW_TRANSLATE = (1 << 0),
- SCE_GIZMO_SHOW_ROTATE = (1 << 1),
- SCE_GIZMO_SHOW_SCALE = (1 << 2),
-};
-
/* ToolSettings.gpencil_flags */
typedef enum eGPencil_Flags {
/* When creating new frames, the last frame gets used as the basis for the new one */
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 21e945a084a..2b1a6adb344 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -669,7 +669,9 @@ typedef struct FileSelectParams {
short sort;
/** Display mode flag. */
short display;
+ short display_previous;
/** Filter when (flags & FILE_FILTER) is true. */
+ char _pad2[2];
int filter;
/** Max number of levels in dirtree to show at once, 0 to disable recursion. */
diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h
index 075ea50c13b..53519de42a4 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -144,7 +144,7 @@ typedef struct View3DCursor {
char _pad[6];
} View3DCursor;
-/* 3D Viewport Shading settings */
+/** 3D Viewport Shading settings. */
typedef struct View3DShading {
/** Shading type (VIEW3D_SHADE_SOLID, ..). */
char type;
@@ -188,42 +188,47 @@ typedef struct View3DShading {
} View3DShading;
-/* 3D Viewport Overlay settings */
+/** 3D Viewport Overlay settings. */
typedef struct View3DOverlay {
int flag;
- /* Edit mode settings */
+ /** Edit mode settings. */
int edit_flag;
float normals_length;
float backwire_opacity;
- /* Paint mode settings */
+ /** Paint mode settings. */
int paint_flag;
- /* Weight paint mode settings */
+ /** Weight paint mode settings. */
int wpaint_flag;
char _pad2[4];
- /* Alpha for texture, weight, vertex paint overlay */
+ /** Alpha for texture, weight, vertex paint overlay. */
float texture_paint_mode_opacity;
float vertex_paint_mode_opacity;
float weight_paint_mode_opacity;
- /* Armature edit/pose mode settings */
+ /** Armature edit/pose mode settings. */
int _pad3;
float xray_alpha_bone;
- /* Other settings */
+ /** Other settings. */
float wireframe_threshold;
- /* grease pencil settings */
+ /** Grease pencil settings. */
float gpencil_paper_opacity;
float gpencil_grid_opacity;
float gpencil_fade_layer;
} View3DOverlay;
-/* 3D ViewPort Struct */
+typedef struct View3D_Runtime {
+ /** Nkey panel stores stuff here. */
+ void *properties_storage;
+} View3D_Runtime;
+
+/** 3D ViewPort Struct. */
typedef struct View3D {
struct SpaceLink *next, *prev;
/** Storage of regions for inactive spaces. */
@@ -240,7 +245,10 @@ typedef struct View3D {
float bundle_size;
/** Display style for bundle. */
char bundle_drawtype;
- char _pad3[2];
+
+ char drawtype DNA_DEPRECATED;
+
+ char _pad3[1];
/** Multiview current eye - for internal use. */
char multiview_eye;
@@ -261,7 +269,7 @@ typedef struct View3D {
char ob_centre_bone[64];
unsigned short local_view_uuid;
- short _pad6;
+ char _pad6[2];
int layact DNA_DEPRECATED;
/** Optional bool for 3d cursor to define center. */
@@ -275,34 +283,36 @@ typedef struct View3D {
float clip_start, clip_end;
float ofs[3] DNA_DEPRECATED;
- char _pad[4];
+ char _pad[1];
+
+ /** Transform gizmo info. */
+ /** #V3D_GIZMO_SHOW_* */
+ char gizmo_flag;
+
+ char gizmo_show_object;
+ char gizmo_show_armature;
+ char gizmo_show_empty;
+ char gizmo_show_light;
+ char gizmo_show_camera;
- /** Icon id. */
- short matcap_icon;
+ char gridflag;
short gridlines;
/** Number of subdivisions in the grid between each highlighted grid line. */
short gridsubdiv;
- char gridflag;
- /** Transform gizmo info. */
- char gizmo_flag;
-
- /* actually only used to define the opacity of the grease pencil vertex in edit mode */
+ /** Actually only used to define the opacity of the grease pencil vertex in edit mode. */
float vertex_opacity;
/* note, 'fx_settings.dof' is currently _not_ allocated,
* instead set (temporarily) from camera */
struct GPUFXSettings fx_settings;
- /** Nkey panel stores stuff here (runtime only!). */
- void *properties_storage;
-
/* XXX deprecated? */
/** Grease-Pencil Data (annotation layers). */
struct bGPdata *gpd DNA_DEPRECATED;
- /* Stereoscopy settings */
+ /** Stereoscopy settings. */
short stereo3d_flag;
char stereo3d_camera;
char _pad4;
@@ -310,21 +320,21 @@ typedef struct View3D {
float stereo3d_volume_alpha;
float stereo3d_convergence_alpha;
- /* Display settings */
- short drawtype DNA_DEPRECATED;
- char _pad5[6];
-
+ /** Display settings. */
View3DShading shading;
View3DOverlay overlay;
+
+ /** Runtime evaluation data (keep last). */
+ View3D_Runtime runtime;
} View3D;
-/* View3D->stereo_flag (short) */
+/** #View3D.stereo3d_flag */
#define V3D_S3D_DISPCAMERAS (1 << 0)
#define V3D_S3D_DISPPLANE (1 << 1)
#define V3D_S3D_DISPVOLUME (1 << 2)
-/* View3D->flag (short) */
+/** #View3D.flag */
#define V3D_FLAG_UNUSED_0 (1 << 0) /* cleared */
#define V3D_FLAG_UNUSED_1 (1 << 1) /* cleared */
#define V3D_HIDE_HELPLINES (1 << 2)
@@ -336,12 +346,12 @@ typedef struct View3D {
#define V3D_GLOBAL_STATS (1 << 13)
#define V3D_DRAW_CENTERS (1 << 15)
-/* RegionView3d->persp */
+/** #RegionView3D.persp */
#define RV3D_ORTHO 0
#define RV3D_PERSP 1
#define RV3D_CAMOB 2
-/* RegionView3d->rflag */
+/** #RegionView3D.rflag */
#define RV3D_CLIPPING (1 << 2)
#define RV3D_NAVIGATING (1 << 3)
#define RV3D_GPULIGHT_UPDATE (1 << 4)
@@ -352,14 +362,14 @@ typedef struct View3D {
*/
#define RV3D_ZOFFSET_DISABLED 64
-/* RegionView3d->viewlock */
+/** #RegionView3D.viewlock */
#define RV3D_LOCKED (1 << 0)
#define RV3D_BOXVIEW (1 << 1)
#define RV3D_BOXCLIP (1 << 2)
-/* RegionView3d->viewlock_quad */
+/** #RegionView3D.viewlock_quad */
#define RV3D_VIEWLOCK_INIT (1 << 7)
-/* RegionView3d->view */
+/** #RegionView3D.view */
#define RV3D_VIEW_USER 0
#define RV3D_VIEW_FRONT 1
#define RV3D_VIEW_BACK 2
@@ -372,7 +382,7 @@ typedef struct View3D {
#define RV3D_VIEW_IS_AXIS(view) \
(((view) >= RV3D_VIEW_FRONT) && ((view) <= RV3D_VIEW_BOTTOM))
-/* View3d->flag2 (int) */
+/** #View3D.flag2 (int) */
#define V3D_HIDE_OVERLAYS (1 << 2)
#define V3D_FLAG2_UNUSED_3 (1 << 3) /* cleared */
#define V3D_SHOW_ANNOTATION (1 << 4)
@@ -388,7 +398,7 @@ typedef struct View3D {
#define V3D_FLAG2_UNUSED_14 (1 << 14) /* cleared */
#define V3D_FLAG2_UNUSED_15 (1 << 15) /* cleared */
-/* View3d->gp_flag (short) */
+/** #View3D.gp_flag (short) */
#define V3D_GP_SHOW_PAPER (1 << 0) /* Activate paper to cover all viewport */
#define V3D_GP_SHOW_GRID (1 << 1) /* Activate paper grid */
#define V3D_GP_SHOW_EDIT_LINES (1 << 2)
@@ -396,14 +406,14 @@ typedef struct View3D {
#define V3D_GP_SHOW_ONION_SKIN (1 << 4) /* main switch at view level */
#define V3D_GP_FADE_NOACTIVE_LAYERS (1 << 5) /* fade layers not active */
-/* View3DShading->light */
+/** #View3DShading.light */
enum {
V3D_LIGHTING_FLAT = 0,
V3D_LIGHTING_STUDIO = 1,
V3D_LIGHTING_MATCAP = 2,
};
-/* View3DShading->flag */
+/** #View3DShading.flag */
enum {
V3D_SHADING_OBJECT_OUTLINE = (1 << 0),
V3D_SHADING_XRAY = (1 << 1),
@@ -419,7 +429,7 @@ enum {
V3D_SHADING_DEPTH_OF_FIELD = (1 << 11),
};
-/* View3DShading->color_type */
+/** #View3DShading.color_type */
enum {
V3D_SHADING_MATERIAL_COLOR = 0,
V3D_SHADING_RANDOM_COLOR = 1,
@@ -428,21 +438,21 @@ enum {
V3D_SHADING_OBJECT_COLOR = 4,
};
-/* View3DShading->background_type */
+/** #View3DShading.background_type */
enum {
V3D_SHADING_BACKGROUND_THEME = 0,
V3D_SHADING_BACKGROUND_WORLD = 1,
V3D_SHADING_BACKGROUND_VIEWPORT = 2,
};
-/* View3DShading->cavity_type */
+/** #View3DShading.cavity_type */
enum {
V3D_SHADING_CAVITY_SSAO = 0,
V3D_SHADING_CAVITY_CURVATURE = 1,
V3D_SHADING_CAVITY_BOTH = 2,
};
-/* View3DOverlay->flag */
+/** #View3DOverlay.flag */
enum {
V3D_OVERLAY_FACE_ORIENTATION = (1 << 0),
V3D_OVERLAY_HIDE_CURSOR = (1 << 1),
@@ -457,7 +467,7 @@ enum {
V3D_OVERLAY_HIDE_OBJECT_ORIGINS = (1 << 10),
};
-/* View3DOverlay->edit_flag */
+/** #View3DOverlay.edit_flag */
enum {
V3D_OVERLAY_EDIT_VERT_NORMALS = (1 << 0),
V3D_OVERLAY_EDIT_LOOP_NORMALS = (1 << 1),
@@ -490,17 +500,17 @@ enum {
V3D_OVERLAY_EDIT_CU_NORMALS = (1 << 21),
};
-/* View3DOverlay->paint_flag */
+/** #View3DOverlay.paint_flag */
enum {
V3D_OVERLAY_PAINT_WIRE = (1 << 0),
};
-/* View3DOverlay->wpaint_flag */
+/** #View3DOverlay.wpaint_flag */
enum {
V3D_OVERLAY_WPAINT_CONTOURS = (1 << 0),
};
-/* View3D->around */
+/** #View3D.around */
enum {
/* center of the bounding box */
V3D_AROUND_CENTER_BOUNDS = 0,
@@ -514,17 +524,7 @@ enum {
V3D_AROUND_ACTIVE = 4,
};
-/*View3D types (only used in tools, not actually saved)*/
-#define V3D_VIEW_STEPLEFT 1
-#define V3D_VIEW_STEPRIGHT 2
-#define V3D_VIEW_STEPDOWN 3
-#define V3D_VIEW_STEPUP 4
-#define V3D_VIEW_PANLEFT 5
-#define V3D_VIEW_PANRIGHT 6
-#define V3D_VIEW_PANDOWN 7
-#define V3D_VIEW_PANUP 8
-
-/* View3d->gridflag */
+/** #View3d.gridflag */
#define V3D_SHOW_FLOOR (1 << 0)
#define V3D_SHOW_X (1 << 1)
#define V3D_SHOW_Y (1 << 2)
@@ -543,7 +543,7 @@ enum {
V3D_ORIENT_CUSTOM_MATRIX = (V3D_ORIENT_CUSTOM - 1),
};
-/* View3d.mpr_flag (also) */
+/** #View3d.gizmo_flag */
enum {
/** All gizmos. */
V3D_GIZMO_HIDE = (1 << 0),
@@ -552,10 +552,41 @@ enum {
V3D_GIZMO_HIDE_TOOL = (1 << 3),
};
+/** #View3d.gizmo_show_object */
+enum {
+ V3D_GIZMO_SHOW_OBJECT_TRANSLATE = (1 << 0),
+ V3D_GIZMO_SHOW_OBJECT_ROTATE = (1 << 1),
+ V3D_GIZMO_SHOW_OBJECT_SCALE = (1 << 2),
+};
+/** #View3d.gizmo_show_armature */
+enum {
+ /** Currently unused (WIP gizmo). */
+ V3D_GIZMO_SHOW_ARMATURE_BBONE = (1 << 0),
+ /** Not yet implemented. */
+ V3D_GIZMO_SHOW_ARMATURE_ROLL = (1 << 1),
+};
+/** #View3d.gizmo_show_empty */
+enum {
+ V3D_GIZMO_SHOW_EMPTY_IMAGE = (1 << 0),
+ V3D_GIZMO_SHOW_EMPTY_FORCE_FIELD = (1 << 1),
+};
+/** #View3d.gizmo_show_light */
+enum {
+ /** Use for both spot & area size. */
+ V3D_GIZMO_SHOW_LIGHT_SIZE = (1 << 0),
+ V3D_GIZMO_SHOW_LIGHT_LOOK_AT = (1 << 1),
+};
+/** #View3d.gizmo_show_camera */
+enum {
+ /** Also used for ortho size. */
+ V3D_GIZMO_SHOW_CAMERA_LENS = (1 << 0),
+ V3D_GIZMO_SHOW_CAMERA_DOF_DIST = (1 << 2),
+};
+
#define RV3D_CAMZOOM_MIN -30
#define RV3D_CAMZOOM_MAX 600
-/* #BKE_screen_view3d_zoom_to_fac() values above */
+/** #BKE_screen_view3d_zoom_to_fac() values above */
#define RV3D_CAMZOOM_MIN_FACTOR 0.1657359312880714853f
#define RV3D_CAMZOOM_MAX_FACTOR 44.9852813742385702928f
diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt
index 09f95d50f17..94caab26e29 100644
--- a/source/blender/makesdna/intern/CMakeLists.txt
+++ b/source/blender/makesdna/intern/CMakeLists.txt
@@ -85,8 +85,8 @@ set(INC_SYS
)
set(SRC
- dna_utils.c
dna_genfile.c
+ dna_utils.c
${CMAKE_CURRENT_BINARY_DIR}/dna.c
${CMAKE_CURRENT_BINARY_DIR}/dna_verify.c
${SRC_DNA_INC}
@@ -94,14 +94,17 @@ set(SRC
dna_utils.h
)
+set(LIB
+)
+
set_source_files_properties(
${CMAKE_CURRENT_BINARY_DIR}/dna.c
- ${CMAKE_CURRENT_BINARY_DIR}/dna_verify.c
${CMAKE_CURRENT_BINARY_DIR}/dna_type_offsets.h
+ ${CMAKE_CURRENT_BINARY_DIR}/dna_verify.c
PROPERTIES GENERATED TRUE
)
-blender_add_lib(bf_dna "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_dna "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# -----------------------------------------------------------------------------
@@ -125,4 +128,7 @@ set(SRC
../../blenlib/intern/listbase.c
)
-blender_add_lib(bf_dna_blenlib "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_dna_blenlib "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c
index 38e1d0986fd..863aebd86e8 100644
--- a/source/blender/makesdna/intern/dna_genfile.c
+++ b/source/blender/makesdna/intern/dna_genfile.c
@@ -38,6 +38,7 @@
#include "BLI_utildefines.h"
#include "BLI_endian_switch.h"
#include "BLI_memarena.h"
+#include "BLI_string.h"
#ifdef WITH_DNA_GHASH
# include "BLI_ghash.h"
@@ -718,17 +719,17 @@ const char *DNA_struct_get_compareflags(const SDNA *oldsdna, const SDNA *newsdna
*/
static eSDNA_Type sdna_type_nr(const char *dna_type)
{
- if ((strcmp(dna_type, "char") == 0) || (strcmp(dna_type, "const char") == 0)) { return SDNA_TYPE_CHAR; }
- else if ((strcmp(dna_type, "uchar") == 0) || (strcmp(dna_type, "unsigned char") == 0)) { return SDNA_TYPE_UCHAR; }
- else if ( strcmp(dna_type, "short") == 0) { return SDNA_TYPE_SHORT; }
- else if ((strcmp(dna_type, "ushort") == 0) || (strcmp(dna_type, "unsigned short") == 0)) { return SDNA_TYPE_USHORT; }
- else if ( strcmp(dna_type, "int") == 0) { return SDNA_TYPE_INT; }
- else if ( strcmp(dna_type, "float") == 0) { return SDNA_TYPE_FLOAT; }
- else if ( strcmp(dna_type, "double") == 0) { return SDNA_TYPE_DOUBLE; }
- else if ( strcmp(dna_type, "int64_t") == 0) { return SDNA_TYPE_INT64; }
- else if ( strcmp(dna_type, "uint64_t") == 0) { return SDNA_TYPE_UINT64; }
+ if (STR_ELEM(dna_type, "char", "const char")) { return SDNA_TYPE_CHAR; }
+ else if (STR_ELEM(dna_type, "uchar", "unsigned char")) { return SDNA_TYPE_UCHAR; }
+ else if (STR_ELEM(dna_type, "short")) { return SDNA_TYPE_SHORT; }
+ else if (STR_ELEM(dna_type, "ushort", "unsigned short")) { return SDNA_TYPE_USHORT; }
+ else if (STR_ELEM(dna_type, "int")) { return SDNA_TYPE_INT; }
+ else if (STR_ELEM(dna_type, "float")) { return SDNA_TYPE_FLOAT; }
+ else if (STR_ELEM(dna_type, "double")) { return SDNA_TYPE_DOUBLE; }
+ else if (STR_ELEM(dna_type, "int64_t")) { return SDNA_TYPE_INT64; }
+ else if (STR_ELEM(dna_type, "uint64_t")) { return SDNA_TYPE_UINT64; }
/* invalid! */
- else { return -1; }
+ else { return -1; }
}
/**
@@ -921,7 +922,7 @@ static bool elem_exists(
*
* Passing olddata=NULL doesn't work reliably for existence checks; it will
* return NULL both when the field is found at offset 0 and when it is not
- * found at all. For field existence checks, use elem_exists() instead.
+ * found at all. For field existence checks, use #elem_exists() instead.
*
* \param sdna: Old SDNA
* \param type: Current field type name
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 601eef1372e..cc5abadd0e8 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -466,7 +466,7 @@ static int add_name(const char *str)
/*
* Put )(void) at the end? Maybe )(). Should check this with
* old sdna. Actually, sometimes )(), sometimes )(void...)
- * Alas.. such is the nature of braindamage :(
+ * Alas.. such is the nature of brain-damage :(
*
* Sorted it out: always do )(), except for headdraw and
* windraw, part of ScrArea. This is important, because some
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 4aee3458a92..8521e06c328 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -372,6 +372,9 @@ set(SRC
rna_mesh_utils.h
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_rna "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_rna "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 1d391988cbd..b1c5ef66958 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -2063,7 +2063,7 @@ bool RNA_property_animated(PointerRNA *ptr, PropertyRNA *prop)
}
/** \note Does not take into account editable status, this has to be checked separately
- * (using RNA_property_edtiable_flag() usually). */
+ * (using #RNA_property_editable_flag() usually). */
bool RNA_property_overridable_get(PointerRNA *ptr, PropertyRNA *prop)
{
if (prop->magic == RNA_MAGIC) {
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index a5f43a580c5..ce5b18331c6 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -713,10 +713,10 @@ static void rna_BrushGpencilSettings_use_material_pin_update(bContext *C, Pointe
if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) {
Material *material = give_current_material(ob, ob->actcol);
- BKE_gpencil_brush_set_material(brush, material);
+ BKE_gpencil_brush_material_set(brush, material);
}
else {
- BKE_gpencil_brush_set_material(brush, NULL);
+ BKE_gpencil_brush_material_set(brush, NULL);
}
/* number of material users changed */
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 4c6c7740ff8..bd7f75f86b5 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -115,8 +115,8 @@ static const EnumPropertyItem target_space_pchan_items[] = {
"The transformation of the target is only evaluated in the Pose Space, "
"the target armature object transformation is ignored"},
{CONSTRAINT_SPACE_PARLOCAL, "LOCAL_WITH_PARENT", 0, "Local With Parent",
- "The transformation of the target bone is evaluated relative its local "
- "coordinate system, with the parent transformation added"},
+ "The transformation of the target bone is evaluated relative to its rest pose "
+ "local coordinate system, thus including the parent-induced transformation"},
{CONSTRAINT_SPACE_LOCAL, "LOCAL", 0, "Local Space",
"The transformation of the target is evaluated relative to its local "
"coordinate system"},
@@ -129,8 +129,8 @@ static const EnumPropertyItem owner_space_pchan_items[] = {
{CONSTRAINT_SPACE_POSE, "POSE", 0, "Pose Space",
"The constraint is applied in Pose Space, the object transformation is ignored"},
{CONSTRAINT_SPACE_PARLOCAL, "LOCAL_WITH_PARENT", 0, "Local With Parent",
- "The constraint is applied relative to the local coordinate system of the object, "
- "with the parent transformation added"},
+ "The constraint is applied relative to the rest pose local coordinate system "
+ "of the bone, thus including the parent-induced transformation"},
{CONSTRAINT_SPACE_LOCAL, "LOCAL", 0, "Local Space",
"The constraint is applied relative to the local coordinate system of the object"},
{0, NULL, 0, NULL, NULL},
diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c
index 5445db78582..8e5f486fea1 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -658,9 +658,10 @@ void RNA_def_material(BlenderRNA *brna)
{MA_FLAT, "FLAT", ICON_MATPLANE, "Flat", "Flat XY plane"},
{MA_SPHERE, "SPHERE", ICON_MATSPHERE, "Sphere", "Sphere"},
{MA_CUBE, "CUBE", ICON_MATCUBE, "Cube", "Cube"},
- {MA_MONKEY, "MONKEY", ICON_MONKEY, "Monkey", "Monkey"},
{MA_HAIR, "HAIR", ICON_HAIR, "Hair", "Hair strands"},
- {MA_SPHERE_A, "SPHERE_A", ICON_MAT_SPHERE_SKY, "World Sphere", "Large sphere with sky"},
+ {MA_SHADERBALL, "SHADERBALL", ICON_MATSHADERBALL, "Shader Ball", "Shader Ball"},
+ {MA_CLOTH, "CLOTH", ICON_MATCLOTH, "Cloth", "Cloth"},
+ {MA_FLUID, "FLUID", ICON_MATFLUID, "Fluid", "Fluid"},
{0, NULL, 0, NULL, NULL},
};
@@ -731,7 +732,12 @@ void RNA_def_material(BlenderRNA *brna)
prop = RNA_def_property(srna, "preview_render_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "pr_type");
RNA_def_property_enum_items(prop, preview_type_items);
- RNA_def_property_ui_text(prop, "Preview render type", "Type of preview render");
+ RNA_def_property_ui_text(prop, "Preview Render Type", "Type of preview render");
+ RNA_def_property_update(prop, 0, "rna_Material_update_previews");
+
+ prop = RNA_def_property(srna, "use_preview_world", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "pr_flag", MA_PREVIEW_WORLD);
+ RNA_def_property_ui_text(prop, "Preview World", "Use the current world background to light the preview render");
RNA_def_property_update(prop, 0, "rna_Material_update_previews");
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_UNSIGNED);
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index 2d0afcd66f1..c8feff4c0e8 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -2699,17 +2699,6 @@ static void rna_def_object(BlenderRNA *brna)
"Make the object draw in front of others");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
- /* Grease Pencil */
-#if 1 /* FIXME: Remove this code when all Open-Movie assets have been fixed */
- prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE);
- RNA_def_property_pointer_sdna(prop, NULL, "gpd");
- RNA_def_property_struct_type(prop, "GreasePencil");
- RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_GPencil_datablocks_obdata_poll"); /* XXX */
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
- RNA_def_property_ui_text(prop, "Grease Pencil Data", "Grease Pencil data-block (deprecated)");
- RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
-#endif
-
/* pose */
prop = RNA_def_property(srna, "pose_library", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "poselib");
diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c
index 5e90f1feddf..68b8d1b2a5f 100644
--- a/source/blender/makesrna/intern/rna_object_api.c
+++ b/source/blender/makesrna/intern/rna_object_api.c
@@ -49,7 +49,7 @@ static const EnumPropertyItem space_items[] = {
{CONSTRAINT_SPACE_POSE, "POSE", 0, "Pose Space",
"The pose space of a bone (its armature's object space)"},
{CONSTRAINT_SPACE_PARLOCAL, "LOCAL_WITH_PARENT", 0, "Local With Parent",
- "The local space of a bone's parent bone"},
+ "The rest pose local space of a bone (thus matrix includes parent transforms)"},
{CONSTRAINT_SPACE_LOCAL, "LOCAL", 0, "Local Space",
"The local space of an object/bone"},
{0, NULL, 0, NULL, NULL},
diff --git a/source/blender/makesrna/intern/rna_pose_api.c b/source/blender/makesrna/intern/rna_pose_api.c
index 2658cd552bc..4366b18c049 100644
--- a/source/blender/makesrna/intern/rna_pose_api.c
+++ b/source/blender/makesrna/intern/rna_pose_api.c
@@ -63,7 +63,7 @@ static void rna_PoseBone_bbone_segment_matrix(bPoseChannel *pchan, ReportList *r
BKE_reportf(reports, RPT_ERROR, "Bone '%s' has out of date B-Bone segment data!", pchan->name);
return;
}
- if (index < 0 || index >= pchan->runtime.bbone_segments) {
+ if (index < 0 || index > pchan->runtime.bbone_segments) {
BKE_reportf(reports, RPT_ERROR, "Invalid index %d for B-Bone segments of '%s'!", index, pchan->name);
return;
}
@@ -115,13 +115,13 @@ void RNA_api_pose_channel(StructRNA *srna)
/* B-Bone segment matrices */
func = RNA_def_function(srna, "bbone_segment_matrix", "rna_PoseBone_bbone_segment_matrix");
- RNA_def_function_ui_description(func, "Retrieve the matrix of the B-Bone segment if available");
+ RNA_def_function_ui_description(func, "Retrieve the matrix of the joint between B-Bone segments if available");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
parm = RNA_def_property(func, "matrix_return", PROP_FLOAT, PROP_MATRIX);
RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4);
RNA_def_property_ui_text(parm, "", "The resulting matrix in bone local space");
RNA_def_function_output(func, parm);
- parm = RNA_def_int(func, "index", 0, 0, INT_MAX, "", "Index of the segment", 0, 10000);
+ parm = RNA_def_int(func, "index", 0, 0, INT_MAX, "", "Index of the segment endpoint", 0, 10000);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_boolean(func, "rest", false, "", "Return the rest pose matrix");
diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c
index 660e2a76511..46eb0a8d7bc 100644
--- a/source/blender/makesrna/intern/rna_render.c
+++ b/source/blender/makesrna/intern/rna_render.c
@@ -744,10 +744,6 @@ static void rna_def_render_engine(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_PREVIEW);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
- prop = RNA_def_property(srna, "bl_use_texture_preview", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_TEXTURE_PREVIEW);
- RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
-
prop = RNA_def_property(srna, "bl_use_postprocess", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "type->flag", RE_USE_POSTPROCESS);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 28ee85373bd..a46be8522dd 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -417,13 +417,6 @@ const EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
-static const EnumPropertyItem rna_enum_gizmo_items[] = {
- {SCE_GIZMO_SHOW_TRANSLATE, "TRANSLATE", 0, "Move", ""},
- {SCE_GIZMO_SHOW_ROTATE, "ROTATE", 0, "Rotate", ""},
- {SCE_GIZMO_SHOW_SCALE, "SCALE", 0, "Scale", ""},
- {0, NULL, 0, NULL, NULL},
-};
-
#ifndef RNA_RUNTIME
static const EnumPropertyItem rna_enum_gpencil_interpolation_mode_items[] = {
/* interpolation */
@@ -581,20 +574,12 @@ static void rna_GPencilInterpolateSettings_type_set(PointerRNA *ptr, int value)
}
}
-static void rna_ToolSettings_gizmo_flag_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
-{
- ToolSettings *ts = scene->toolsettings;
- if ((ts->gizmo_flag & (SCE_GIZMO_SHOW_TRANSLATE | SCE_GIZMO_SHOW_ROTATE | SCE_GIZMO_SHOW_SCALE)) == 0) {
- ts->gizmo_flag |= SCE_GIZMO_SHOW_TRANSLATE;
- }
-}
static void rna_SpaceImageEditor_uv_sculpt_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr))
{
ED_space_image_uv_sculpt_update(bmain, bmain->wm.first, scene);
}
-
/* Read-only Iterator of all the scene objects. */
static void rna_Scene_objects_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
@@ -2678,13 +2663,6 @@ static void rna_def_tool_settings(BlenderRNA *brna)
"Scale is affected by snapping settings");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */
- prop = RNA_def_property(srna, "use_gizmo_mode", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_sdna(prop, NULL, "gizmo_flag");
- RNA_def_property_enum_items(prop, rna_enum_gizmo_items);
- RNA_def_property_flag(prop, PROP_ENUM_FLAG);
- RNA_def_property_ui_text(prop, "Gizmo Mode", "");
- RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ToolSettings_gizmo_flag_update");
-
/* Grease Pencil */
prop = RNA_def_property(srna, "use_gpencil_draw_additive", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "gpencil_flags", GP_TOOL_FLAG_RETAIN_LAST);
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 97b997c4604..fda1d71d9db 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -663,8 +663,9 @@ static void rna_3DViewShading_type_update(Main *bmain, Scene *scene, PointerRNA
View3DShading *shading = ptr->data;
if (shading->type == OB_MATERIAL ||
- (shading->type == OB_RENDER && (strcmp(scene->r.engine, RE_engine_id_BLENDER_EEVEE) == 0 ||
- strcmp(scene->r.engine, RE_engine_id_CYCLES)))) {
+ (shading->type == OB_RENDER &&
+ STR_ELEM(scene->r.engine, RE_engine_id_BLENDER_EEVEE, RE_engine_id_CYCLES)))
+ {
/* When switching from workbench to render or material mode the geometry of any
* active sculpt session needs to be recalculated. */
for (Object *ob = bmain->objects.first; ob ; ob = ob->id.next) {
@@ -1330,10 +1331,6 @@ static const EnumPropertyItem *rna_SpaceProperties_context_itemf(
RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_OBJECT);
}
- if (sbuts->pathflag & (1 << BCONTEXT_CONSTRAINT)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_CONSTRAINT);
- }
-
if (sbuts->pathflag & (1 << BCONTEXT_MODIFIER)) {
RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_MODIFIER);
}
@@ -1342,6 +1339,18 @@ static const EnumPropertyItem *rna_SpaceProperties_context_itemf(
RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_SHADERFX);
}
+ if (sbuts->pathflag & (1 << BCONTEXT_PARTICLE)) {
+ RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_PARTICLE);
+ }
+
+ if (sbuts->pathflag & (1 << BCONTEXT_PHYSICS)) {
+ RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_PHYSICS);
+ }
+
+ if (sbuts->pathflag & (1 << BCONTEXT_CONSTRAINT)) {
+ RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_CONSTRAINT);
+ }
+
if (sbuts->pathflag & (1 << BCONTEXT_DATA)) {
RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_DATA);
(item + totitem - 1)->icon = sbuts->dataicon;
@@ -1359,16 +1368,12 @@ static const EnumPropertyItem *rna_SpaceProperties_context_itemf(
RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_MATERIAL);
}
- if (sbuts->pathflag & (1 << BCONTEXT_TEXTURE)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_TEXTURE);
- }
-
- if (sbuts->pathflag & (1 << BCONTEXT_PARTICLE)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_PARTICLE);
+ if (totitem) {
+ RNA_enum_item_add_separator(&item, &totitem);
}
- if (sbuts->pathflag & (1 << BCONTEXT_PHYSICS)) {
- RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_PHYSICS);
+ if (sbuts->pathflag & (1 << BCONTEXT_TEXTURE)) {
+ RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_TEXTURE);
}
RNA_enum_item_end(&item, &totitem);
@@ -3197,6 +3202,59 @@ static void rna_def_space_view3d(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Tool Gizmo", "Active tool gizmo");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+ /* Per object type gizmo display flags. */
+
+ prop = RNA_def_property(srna, "show_gizmo_object_translate", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "gizmo_show_object", V3D_GIZMO_SHOW_OBJECT_TRANSLATE);
+ RNA_def_property_ui_text(prop, "Show Object Location", "Gizmo to adjust location");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ prop = RNA_def_property(srna, "show_gizmo_object_rotate", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "gizmo_show_object", V3D_GIZMO_SHOW_OBJECT_ROTATE);
+ RNA_def_property_ui_text(prop, "Show Object Rotation", "Gizmo to adjust rotation");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ prop = RNA_def_property(srna, "show_gizmo_object_scale", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "gizmo_show_object", V3D_GIZMO_SHOW_OBJECT_SCALE);
+ RNA_def_property_ui_text(prop, "Show Object Scale", "Gizmo to adjust scale");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ /* Empty Object Data. */
+ prop = RNA_def_property(srna, "show_gizmo_empty_image", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "gizmo_show_empty", V3D_GIZMO_SHOW_EMPTY_IMAGE);
+ RNA_def_property_ui_text(prop, "Show Empty Image", "Gizmo to adjust image size and position");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ prop = RNA_def_property(srna, "show_gizmo_empty_force_field", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "gizmo_show_empty", V3D_GIZMO_SHOW_EMPTY_FORCE_FIELD);
+ RNA_def_property_ui_text(prop, "Show Empty Force Field", "Gizmo to adjust the force field");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ /* Light Object Data. */
+ prop = RNA_def_property(srna, "show_gizmo_light_size", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "gizmo_show_light", V3D_GIZMO_SHOW_LIGHT_SIZE);
+ RNA_def_property_ui_text(prop, "Show Light Size", "Gizmo to adjust spot and area size");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ prop = RNA_def_property(srna, "show_gizmo_light_look_at", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "gizmo_show_light", V3D_GIZMO_SHOW_LIGHT_LOOK_AT);
+ RNA_def_property_ui_text(prop, "Show Light Look-At", "Gizmo to adjust spot and area size");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ /* Camera Object Data. */
+ prop = RNA_def_property(srna, "show_gizmo_camera_lens", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "gizmo_show_camera", V3D_GIZMO_SHOW_CAMERA_LENS);
+ RNA_def_property_ui_text(prop, "Show Camera Lens", "Gizmo to adjust camera lens & ortho size");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ prop = RNA_def_property(srna, "show_gizmo_camera_dof_distance", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "gizmo_show_camera", V3D_GIZMO_SHOW_CAMERA_DOF_DIST);
+ RNA_def_property_ui_text(
+ prop, "Show Camera Focus Distance", "Gizmo to adjust camera focus distance "
+ "(depends on limits display)");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+
prop = RNA_def_property(srna, "use_local_camera", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "scenelock", 1);
RNA_def_property_boolean_funcs(prop, NULL, "rna_SpaceView3D_use_local_camera_set");
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index a2de1712960..da41134f4ab 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -4056,7 +4056,7 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
};
static const EnumPropertyItem material_link_items[] = {
- {0, "OBDATA", 0, "ObData", "Toggle whether the material is linked to object data or the object block"},
+ {0, "OBDATA", 0, "Object Data", "Toggle whether the material is linked to object data or the object block"},
{USER_MAT_ON_OB, "OBJECT", 0, "Object",
"Toggle whether the material is linked to object data or the object block"},
{0, NULL, 0, NULL, NULL},
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index e9b596c3890..8f87388f76a 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -111,6 +111,9 @@ set(SRC
intern/MOD_weightvg_util.h
)
+set(LIB
+)
+
if(WITH_ALEMBIC)
add_definitions(-DWITH_ALEMBIC)
list(APPEND INC
@@ -144,4 +147,4 @@ endif()
# So we can have special tricks in modifier system.
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_modifiers "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_modifiers "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index cc2bfbadf80..f58c43cf44f 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -258,6 +258,9 @@ set(SRC
intern/node_util.h
)
+set(LIB
+)
+
if(WITH_PYTHON)
list(APPEND INC
../python
@@ -289,4 +292,4 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
-blender_add_lib(bf_nodes "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_nodes "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index a2978970951..1c3b792f20b 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -28,225 +28,224 @@
/* WARNING! If you edit those strings, please do the same in relevant nodes files (under blender/nodes/...)! */
/* Tree type Node ID RNA def function Enum name Struct name UI Name UI Description */
-DefNode( Node, NODE_FRAME, def_frame, "FRAME", Frame, "Frame", "" );
-DefNode( Node, NODE_GROUP, def_group, "GROUP", Group, "Group", "" );
-DefNode( Node, NODE_GROUP_INPUT, def_group_input, "GROUP_INPUT", GroupInput, "Group Input", "" );
-DefNode( Node, NODE_GROUP_OUTPUT, def_group_output, "GROUP_OUTPUT", GroupOutput, "Group Output", "" );
-DefNode( Node, NODE_REROUTE, 0, "REROUTE", Reroute, "Reroute", "" );
+DefNode(Node, NODE_FRAME, def_frame, "FRAME", Frame, "Frame", "" )
+DefNode(Node, NODE_GROUP, def_group, "GROUP", Group, "Group", "" )
+DefNode(Node, NODE_GROUP_INPUT, def_group_input, "GROUP_INPUT", GroupInput, "Group Input", "" )
+DefNode(Node, NODE_GROUP_OUTPUT, def_group_output, "GROUP_OUTPUT", GroupOutput, "Group Output", "" )
+DefNode(Node, NODE_REROUTE, 0, "REROUTE", Reroute, "Reroute", "" )
-DefNode( ShaderNode, SH_NODE_RGB, 0, "RGB", RGB, "RGB", "" );
-DefNode( ShaderNode, SH_NODE_VALUE, 0, "VALUE", Value, "Value", "" );
-DefNode( ShaderNode, SH_NODE_MIX_RGB, def_mix_rgb, "MIX_RGB", MixRGB, "MixRGB", "" );
-DefNode( ShaderNode, SH_NODE_VALTORGB, def_colorramp, "VALTORGB", ValToRGB, "ColorRamp", "" );
-DefNode( ShaderNode, SH_NODE_RGBTOBW, 0, "RGBTOBW", RGBToBW, "RGB to BW", "" );
-DefNode( ShaderNode, SH_NODE_SHADERTORGB, 0, "SHADERTORGB", ShaderToRGB, "Shader to RGB", "" );
-DefNode( ShaderNode, SH_NODE_NORMAL, 0, "NORMAL", Normal, "Normal", "" );
-DefNode( ShaderNode, SH_NODE_GAMMA, 0, "GAMMA", Gamma, "Gamma", "" );
-DefNode( ShaderNode, SH_NODE_BRIGHTCONTRAST, 0, "BRIGHTCONTRAST", BrightContrast, "Bright Contrast", "" );
-DefNode( ShaderNode, SH_NODE_MAPPING, def_sh_mapping, "MAPPING", Mapping, "Mapping", "" );
-DefNode( ShaderNode, SH_NODE_CURVE_VEC, def_vector_curve, "CURVE_VEC", VectorCurve, "Vector Curves", "" );
-DefNode( ShaderNode, SH_NODE_CURVE_RGB, def_rgb_curve, "CURVE_RGB", RGBCurve, "RGB Curves", "" );
-DefNode( ShaderNode, SH_NODE_CAMERA, 0, "CAMERA", CameraData, "Camera Data", "" );
-DefNode( ShaderNode, SH_NODE_MATH, def_math, "MATH", Math, "Math", "" );
-DefNode( ShaderNode, SH_NODE_VECT_MATH, def_vector_math, "VECT_MATH", VectorMath, "Vector Math", "" );
-DefNode( ShaderNode, SH_NODE_SQUEEZE, 0, "SQUEEZE", Squeeze, "Squeeze Value", "" );
-DefNode( ShaderNode, SH_NODE_INVERT, 0, "INVERT", Invert, "Invert", "" );
-DefNode( ShaderNode, SH_NODE_SEPRGB, 0, "SEPRGB", SeparateRGB, "Separate RGB", "" );
-DefNode( ShaderNode, SH_NODE_COMBRGB, 0, "COMBRGB", CombineRGB, "Combine RGB", "" );
-DefNode( ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue/Saturation", "" );
+DefNode(ShaderNode, SH_NODE_RGB, 0, "RGB", RGB, "RGB", "" )
+DefNode(ShaderNode, SH_NODE_VALUE, 0, "VALUE", Value, "Value", "" )
+DefNode(ShaderNode, SH_NODE_MIX_RGB, def_mix_rgb, "MIX_RGB", MixRGB, "MixRGB", "" )
+DefNode(ShaderNode, SH_NODE_VALTORGB, def_colorramp, "VALTORGB", ValToRGB, "ColorRamp", "" )
+DefNode(ShaderNode, SH_NODE_RGBTOBW, 0, "RGBTOBW", RGBToBW, "RGB to BW", "" )
+DefNode(ShaderNode, SH_NODE_SHADERTORGB, 0, "SHADERTORGB", ShaderToRGB, "Shader to RGB", "" )
+DefNode(ShaderNode, SH_NODE_NORMAL, 0, "NORMAL", Normal, "Normal", "" )
+DefNode(ShaderNode, SH_NODE_GAMMA, 0, "GAMMA", Gamma, "Gamma", "" )
+DefNode(ShaderNode, SH_NODE_BRIGHTCONTRAST, 0, "BRIGHTCONTRAST", BrightContrast, "Bright Contrast", "" )
+DefNode(ShaderNode, SH_NODE_MAPPING, def_sh_mapping, "MAPPING", Mapping, "Mapping", "" )
+DefNode(ShaderNode, SH_NODE_CURVE_VEC, def_vector_curve, "CURVE_VEC", VectorCurve, "Vector Curves", "" )
+DefNode(ShaderNode, SH_NODE_CURVE_RGB, def_rgb_curve, "CURVE_RGB", RGBCurve, "RGB Curves", "" )
+DefNode(ShaderNode, SH_NODE_CAMERA, 0, "CAMERA", CameraData, "Camera Data", "" )
+DefNode(ShaderNode, SH_NODE_MATH, def_math, "MATH", Math, "Math", "" )
+DefNode(ShaderNode, SH_NODE_VECT_MATH, def_vector_math, "VECT_MATH", VectorMath, "Vector Math", "" )
+DefNode(ShaderNode, SH_NODE_SQUEEZE, 0, "SQUEEZE", Squeeze, "Squeeze Value", "" )
+DefNode(ShaderNode, SH_NODE_INVERT, 0, "INVERT", Invert, "Invert", "" )
+DefNode(ShaderNode, SH_NODE_SEPRGB, 0, "SEPRGB", SeparateRGB, "Separate RGB", "" )
+DefNode(ShaderNode, SH_NODE_COMBRGB, 0, "COMBRGB", CombineRGB, "Combine RGB", "" )
+DefNode(ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue/Saturation", "" )
-DefNode( ShaderNode, SH_NODE_OUTPUT_MATERIAL, def_sh_output, "OUTPUT_MATERIAL", OutputMaterial, "Material Output", "" );
-DefNode( ShaderNode, SH_NODE_EEVEE_SPECULAR, 0, "EEVEE_SPECULAR", EeveeSpecular, "Specular", "" );
-DefNode( ShaderNode, SH_NODE_OUTPUT_LIGHT, def_sh_output, "OUTPUT_LIGHT", OutputLight, "Light Output", "" );
-DefNode( ShaderNode, SH_NODE_OUTPUT_WORLD, def_sh_output, "OUTPUT_WORLD", OutputWorld, "World Output", "" );
-DefNode( ShaderNode, SH_NODE_OUTPUT_LINESTYLE, def_sh_output_linestyle,"OUTPUT_LINESTYLE", OutputLineStyle, "Line Style Output", "" );
-DefNode( ShaderNode, SH_NODE_FRESNEL, 0, "FRESNEL", Fresnel, "Fresnel", "" );
-DefNode( ShaderNode, SH_NODE_LAYER_WEIGHT, 0, "LAYER_WEIGHT", LayerWeight, "Layer Weight", "" );
-DefNode( ShaderNode, SH_NODE_MIX_SHADER, 0, "MIX_SHADER", MixShader, "Mix Shader", "" );
-DefNode( ShaderNode, SH_NODE_ADD_SHADER, 0, "ADD_SHADER", AddShader, "Add Shader", "" );
-DefNode( ShaderNode, SH_NODE_ATTRIBUTE, def_sh_attribute, "ATTRIBUTE", Attribute, "Attribute", "" );
-DefNode( ShaderNode, SH_NODE_AMBIENT_OCCLUSION, def_sh_ambient_occlusion,"AMBIENT_OCCLUSION", AmbientOcclusion, "Ambient Occlusion", "" );
-DefNode( ShaderNode, SH_NODE_BACKGROUND, 0, "BACKGROUND", Background, "Background", "" );
-DefNode( ShaderNode, SH_NODE_HOLDOUT, 0, "HOLDOUT", Holdout, "Holdout", "" );
-DefNode( ShaderNode, SH_NODE_BSDF_ANISOTROPIC, def_anisotropic, "BSDF_ANISOTROPIC", BsdfAnisotropic, "Anisotropic BSDF", "" );
-DefNode( ShaderNode, SH_NODE_BSDF_DIFFUSE, 0, "BSDF_DIFFUSE", BsdfDiffuse, "Diffuse BSDF", "" );
-DefNode( ShaderNode, SH_NODE_BSDF_PRINCIPLED, def_principled, "BSDF_PRINCIPLED", BsdfPrincipled, "Principled BSDF", "" );
-DefNode( ShaderNode, SH_NODE_BSDF_GLOSSY, def_glossy, "BSDF_GLOSSY", BsdfGlossy, "Glossy BSDF", "" );
-DefNode( ShaderNode, SH_NODE_BSDF_GLASS, def_glass, "BSDF_GLASS", BsdfGlass, "Glass BSDF", "" );
-DefNode( ShaderNode, SH_NODE_BSDF_REFRACTION, def_refraction, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "" );
-DefNode( ShaderNode, SH_NODE_BSDF_TRANSLUCENT, 0, "BSDF_TRANSLUCENT", BsdfTranslucent, "Translucent BSDF", "" );
-DefNode( ShaderNode, SH_NODE_BSDF_TRANSPARENT, 0, "BSDF_TRANSPARENT", BsdfTransparent, "Transparent BSDF", "" );
-DefNode( ShaderNode, SH_NODE_BSDF_VELVET, 0, "BSDF_VELVET", BsdfVelvet, "Velvet BSDF", "" );
-DefNode( ShaderNode, SH_NODE_BSDF_TOON, def_toon, "BSDF_TOON", BsdfToon, "Toon BSDF", "" );
-DefNode( ShaderNode, SH_NODE_BSDF_HAIR, def_hair, "BSDF_HAIR", BsdfHair, "Hair BSDF", "" );
-DefNode( ShaderNode, SH_NODE_BSDF_HAIR_PRINCIPLED, def_hair_principled, "BSDF_HAIR_PRINCIPLED", BsdfHairPrincipled, "Principled Hair BSDF", "");
-DefNode( ShaderNode, SH_NODE_SUBSURFACE_SCATTERING, def_sh_subsurface, "SUBSURFACE_SCATTERING",SubsurfaceScattering,"Subsurface Scattering","");
-DefNode( ShaderNode, SH_NODE_VOLUME_ABSORPTION, 0, "VOLUME_ABSORPTION", VolumeAbsorption, "Volume Absorption", "" );
-DefNode( ShaderNode, SH_NODE_VOLUME_SCATTER, 0, "VOLUME_SCATTER", VolumeScatter, "Volume Scatter", "" );
-DefNode( ShaderNode, SH_NODE_VOLUME_PRINCIPLED, 0, "PRINCIPLED_VOLUME", VolumePrincipled, "Principled Volume", "" );
-DefNode( ShaderNode, SH_NODE_EMISSION, 0, "EMISSION", Emission, "Emission", "" );
-DefNode( ShaderNode, SH_NODE_NEW_GEOMETRY, 0, "NEW_GEOMETRY", NewGeometry, "Geometry", "" );
-DefNode( ShaderNode, SH_NODE_LIGHT_PATH, 0, "LIGHT_PATH", LightPath, "Light Path", "" );
-DefNode( ShaderNode, SH_NODE_LIGHT_FALLOFF, 0, "LIGHT_FALLOFF", LightFalloff, "Light Falloff", "" );
-DefNode( ShaderNode, SH_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "" );
-DefNode( ShaderNode, SH_NODE_PARTICLE_INFO, 0, "PARTICLE_INFO", ParticleInfo, "Particle Info", "" );
-DefNode( ShaderNode, SH_NODE_HAIR_INFO, 0, "HAIR_INFO", HairInfo, "Hair Info", "" );
-DefNode( ShaderNode, SH_NODE_WIREFRAME, def_sh_tex_wireframe, "WIREFRAME", Wireframe, "Wireframe", "" );
-DefNode( ShaderNode, SH_NODE_WAVELENGTH, 0, "WAVELENGTH", Wavelength, "Wavelength", "" );
-DefNode( ShaderNode, SH_NODE_BLACKBODY, 0, "BLACKBODY", Blackbody, "Blackbody", "" );
-DefNode( ShaderNode, SH_NODE_BUMP, def_sh_bump, "BUMP", Bump, "Bump", "" );
-DefNode( ShaderNode, SH_NODE_NORMAL_MAP, def_sh_normal_map, "NORMAL_MAP", NormalMap, "Normal Map", "" );
-DefNode( ShaderNode, SH_NODE_TANGENT, def_sh_tangent, "TANGENT", Tangent, "Tangent", "" );
-DefNode( ShaderNode, SH_NODE_SCRIPT, def_sh_script, "SCRIPT", Script, "Script", "" );
-DefNode( ShaderNode, SH_NODE_TEX_IMAGE, def_sh_tex_image, "TEX_IMAGE", TexImage, "Image Texture", "" );
-DefNode( ShaderNode, SH_NODE_TEX_ENVIRONMENT, def_sh_tex_environment, "TEX_ENVIRONMENT", TexEnvironment, "Environment Texture","" );
-DefNode( ShaderNode, SH_NODE_TEX_SKY, def_sh_tex_sky, "TEX_SKY", TexSky, "Sky Texture", "" );
-DefNode( ShaderNode, SH_NODE_TEX_GRADIENT, def_sh_tex_gradient, "TEX_GRADIENT", TexGradient, "Gradient Texture", "" );
-DefNode( ShaderNode, SH_NODE_TEX_NOISE, def_sh_tex_noise, "TEX_NOISE", TexNoise, "Noise Texture", "" );
-DefNode( ShaderNode, SH_NODE_TEX_MAGIC, def_sh_tex_magic, "TEX_MAGIC", TexMagic, "Magic Texture", "" );
-DefNode( ShaderNode, SH_NODE_TEX_WAVE, def_sh_tex_wave, "TEX_WAVE", TexWave, "Wave Texture", "" );
-DefNode( ShaderNode, SH_NODE_TEX_MUSGRAVE, def_sh_tex_musgrave, "TEX_MUSGRAVE", TexMusgrave, "Musgrave Texture", "" );
-DefNode( ShaderNode, SH_NODE_TEX_VORONOI, def_sh_tex_voronoi, "TEX_VORONOI", TexVoronoi, "Voronoi Texture", "" );
-DefNode( ShaderNode, SH_NODE_TEX_CHECKER, def_sh_tex_checker, "TEX_CHECKER", TexChecker, "Checker Texture", "" );
-DefNode( ShaderNode, SH_NODE_TEX_BRICK, def_sh_tex_brick, "TEX_BRICK", TexBrick, "Brick Texture", "" );
-DefNode( ShaderNode, SH_NODE_TEX_POINTDENSITY, def_sh_tex_pointdensity,"TEX_POINTDENSITY", TexPointDensity, "Point Density", "" );
-DefNode( ShaderNode, SH_NODE_TEX_COORD, def_sh_tex_coord, "TEX_COORD", TexCoord, "Texture Coordinate","" );
-DefNode( ShaderNode, SH_NODE_VECT_TRANSFORM, def_sh_vect_transform, "VECT_TRANSFORM", VectorTransform, "Vector Transform", "" );
-DefNode( ShaderNode, SH_NODE_SEPHSV, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" );
-DefNode( ShaderNode, SH_NODE_COMBHSV, 0, "COMBHSV", CombineHSV, "Combine HSV", "" );
-DefNode( ShaderNode, SH_NODE_UVMAP, def_sh_uvmap, "UVMAP", UVMap, "UV Map", "" );
-DefNode( ShaderNode, SH_NODE_UVALONGSTROKE, def_sh_uvalongstroke, "UVALONGSTROKE", UVAlongStroke, "UV Along Stroke", "" );
-DefNode( ShaderNode, SH_NODE_SEPXYZ, 0, "SEPXYZ", SeparateXYZ, "Separate XYZ", "" );
-DefNode( ShaderNode, SH_NODE_COMBXYZ, 0, "COMBXYZ", CombineXYZ, "Combine XYZ", "" );
-DefNode( ShaderNode, SH_NODE_BEVEL, def_sh_bevel, "BEVEL", Bevel, "Bevel", "" );
-DefNode( ShaderNode, SH_NODE_DISPLACEMENT, def_sh_displacement, "DISPLACEMENT", Displacement, "Displacement", "" );
-DefNode( ShaderNode, SH_NODE_VECTOR_DISPLACEMENT,def_sh_vector_displacement,"VECTOR_DISPLACEMENT",VectorDisplacement,"Vector Displacement","" );
-DefNode( ShaderNode, SH_NODE_TEX_IES, def_sh_tex_ies, "TEX_IES", TexIES, "IES Texture", "" );
+DefNode(ShaderNode, SH_NODE_OUTPUT_MATERIAL, def_sh_output, "OUTPUT_MATERIAL", OutputMaterial, "Material Output", "" )
+DefNode(ShaderNode, SH_NODE_EEVEE_SPECULAR, 0, "EEVEE_SPECULAR", EeveeSpecular, "Specular", "" )
+DefNode(ShaderNode, SH_NODE_OUTPUT_LIGHT, def_sh_output, "OUTPUT_LIGHT", OutputLight, "Light Output", "" )
+DefNode(ShaderNode, SH_NODE_OUTPUT_WORLD, def_sh_output, "OUTPUT_WORLD", OutputWorld, "World Output", "" )
+DefNode(ShaderNode, SH_NODE_OUTPUT_LINESTYLE, def_sh_output_linestyle,"OUTPUT_LINESTYLE", OutputLineStyle, "Line Style Output", "" )
+DefNode(ShaderNode, SH_NODE_FRESNEL, 0, "FRESNEL", Fresnel, "Fresnel", "" )
+DefNode(ShaderNode, SH_NODE_LAYER_WEIGHT, 0, "LAYER_WEIGHT", LayerWeight, "Layer Weight", "" )
+DefNode(ShaderNode, SH_NODE_MIX_SHADER, 0, "MIX_SHADER", MixShader, "Mix Shader", "" )
+DefNode(ShaderNode, SH_NODE_ADD_SHADER, 0, "ADD_SHADER", AddShader, "Add Shader", "" )
+DefNode(ShaderNode, SH_NODE_ATTRIBUTE, def_sh_attribute, "ATTRIBUTE", Attribute, "Attribute", "" )
+DefNode(ShaderNode, SH_NODE_AMBIENT_OCCLUSION, def_sh_ambient_occlusion,"AMBIENT_OCCLUSION", AmbientOcclusion, "Ambient Occlusion", "" )
+DefNode(ShaderNode, SH_NODE_BACKGROUND, 0, "BACKGROUND", Background, "Background", "" )
+DefNode(ShaderNode, SH_NODE_HOLDOUT, 0, "HOLDOUT", Holdout, "Holdout", "" )
+DefNode(ShaderNode, SH_NODE_BSDF_ANISOTROPIC, def_anisotropic, "BSDF_ANISOTROPIC", BsdfAnisotropic, "Anisotropic BSDF", "" )
+DefNode(ShaderNode, SH_NODE_BSDF_DIFFUSE, 0, "BSDF_DIFFUSE", BsdfDiffuse, "Diffuse BSDF", "" )
+DefNode(ShaderNode, SH_NODE_BSDF_PRINCIPLED, def_principled, "BSDF_PRINCIPLED", BsdfPrincipled, "Principled BSDF", "" )
+DefNode(ShaderNode, SH_NODE_BSDF_GLOSSY, def_glossy, "BSDF_GLOSSY", BsdfGlossy, "Glossy BSDF", "" )
+DefNode(ShaderNode, SH_NODE_BSDF_GLASS, def_glass, "BSDF_GLASS", BsdfGlass, "Glass BSDF", "" )
+DefNode(ShaderNode, SH_NODE_BSDF_REFRACTION, def_refraction, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "" )
+DefNode(ShaderNode, SH_NODE_BSDF_TRANSLUCENT, 0, "BSDF_TRANSLUCENT", BsdfTranslucent, "Translucent BSDF", "" )
+DefNode(ShaderNode, SH_NODE_BSDF_TRANSPARENT, 0, "BSDF_TRANSPARENT", BsdfTransparent, "Transparent BSDF", "" )
+DefNode(ShaderNode, SH_NODE_BSDF_VELVET, 0, "BSDF_VELVET", BsdfVelvet, "Velvet BSDF", "" )
+DefNode(ShaderNode, SH_NODE_BSDF_TOON, def_toon, "BSDF_TOON", BsdfToon, "Toon BSDF", "" )
+DefNode(ShaderNode, SH_NODE_BSDF_HAIR, def_hair, "BSDF_HAIR", BsdfHair, "Hair BSDF", "" )
+DefNode(ShaderNode, SH_NODE_BSDF_HAIR_PRINCIPLED, def_hair_principled, "BSDF_HAIR_PRINCIPLED", BsdfHairPrincipled, "Principled Hair BSDF", "")
+DefNode(ShaderNode, SH_NODE_SUBSURFACE_SCATTERING, def_sh_subsurface, "SUBSURFACE_SCATTERING",SubsurfaceScattering,"Subsurface Scattering","")
+DefNode(ShaderNode, SH_NODE_VOLUME_ABSORPTION, 0, "VOLUME_ABSORPTION", VolumeAbsorption, "Volume Absorption", "" )
+DefNode(ShaderNode, SH_NODE_VOLUME_SCATTER, 0, "VOLUME_SCATTER", VolumeScatter, "Volume Scatter", "" )
+DefNode(ShaderNode, SH_NODE_VOLUME_PRINCIPLED, 0, "PRINCIPLED_VOLUME", VolumePrincipled, "Principled Volume", "" )
+DefNode(ShaderNode, SH_NODE_EMISSION, 0, "EMISSION", Emission, "Emission", "" )
+DefNode(ShaderNode, SH_NODE_NEW_GEOMETRY, 0, "NEW_GEOMETRY", NewGeometry, "Geometry", "" )
+DefNode(ShaderNode, SH_NODE_LIGHT_PATH, 0, "LIGHT_PATH", LightPath, "Light Path", "" )
+DefNode(ShaderNode, SH_NODE_LIGHT_FALLOFF, 0, "LIGHT_FALLOFF", LightFalloff, "Light Falloff", "" )
+DefNode(ShaderNode, SH_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "" )
+DefNode(ShaderNode, SH_NODE_PARTICLE_INFO, 0, "PARTICLE_INFO", ParticleInfo, "Particle Info", "" )
+DefNode(ShaderNode, SH_NODE_HAIR_INFO, 0, "HAIR_INFO", HairInfo, "Hair Info", "" )
+DefNode(ShaderNode, SH_NODE_WIREFRAME, def_sh_tex_wireframe, "WIREFRAME", Wireframe, "Wireframe", "" )
+DefNode(ShaderNode, SH_NODE_WAVELENGTH, 0, "WAVELENGTH", Wavelength, "Wavelength", "" )
+DefNode(ShaderNode, SH_NODE_BLACKBODY, 0, "BLACKBODY", Blackbody, "Blackbody", "" )
+DefNode(ShaderNode, SH_NODE_BUMP, def_sh_bump, "BUMP", Bump, "Bump", "" )
+DefNode(ShaderNode, SH_NODE_NORMAL_MAP, def_sh_normal_map, "NORMAL_MAP", NormalMap, "Normal Map", "" )
+DefNode(ShaderNode, SH_NODE_TANGENT, def_sh_tangent, "TANGENT", Tangent, "Tangent", "" )
+DefNode(ShaderNode, SH_NODE_SCRIPT, def_sh_script, "SCRIPT", Script, "Script", "" )
+DefNode(ShaderNode, SH_NODE_TEX_IMAGE, def_sh_tex_image, "TEX_IMAGE", TexImage, "Image Texture", "" )
+DefNode(ShaderNode, SH_NODE_TEX_ENVIRONMENT, def_sh_tex_environment, "TEX_ENVIRONMENT", TexEnvironment, "Environment Texture","" )
+DefNode(ShaderNode, SH_NODE_TEX_SKY, def_sh_tex_sky, "TEX_SKY", TexSky, "Sky Texture", "" )
+DefNode(ShaderNode, SH_NODE_TEX_GRADIENT, def_sh_tex_gradient, "TEX_GRADIENT", TexGradient, "Gradient Texture", "" )
+DefNode(ShaderNode, SH_NODE_TEX_NOISE, def_sh_tex_noise, "TEX_NOISE", TexNoise, "Noise Texture", "" )
+DefNode(ShaderNode, SH_NODE_TEX_MAGIC, def_sh_tex_magic, "TEX_MAGIC", TexMagic, "Magic Texture", "" )
+DefNode(ShaderNode, SH_NODE_TEX_WAVE, def_sh_tex_wave, "TEX_WAVE", TexWave, "Wave Texture", "" )
+DefNode(ShaderNode, SH_NODE_TEX_MUSGRAVE, def_sh_tex_musgrave, "TEX_MUSGRAVE", TexMusgrave, "Musgrave Texture", "" )
+DefNode(ShaderNode, SH_NODE_TEX_VORONOI, def_sh_tex_voronoi, "TEX_VORONOI", TexVoronoi, "Voronoi Texture", "" )
+DefNode(ShaderNode, SH_NODE_TEX_CHECKER, def_sh_tex_checker, "TEX_CHECKER", TexChecker, "Checker Texture", "" )
+DefNode(ShaderNode, SH_NODE_TEX_BRICK, def_sh_tex_brick, "TEX_BRICK", TexBrick, "Brick Texture", "" )
+DefNode(ShaderNode, SH_NODE_TEX_POINTDENSITY, def_sh_tex_pointdensity,"TEX_POINTDENSITY", TexPointDensity, "Point Density", "" )
+DefNode(ShaderNode, SH_NODE_TEX_COORD, def_sh_tex_coord, "TEX_COORD", TexCoord, "Texture Coordinate","" )
+DefNode(ShaderNode, SH_NODE_VECT_TRANSFORM, def_sh_vect_transform, "VECT_TRANSFORM", VectorTransform, "Vector Transform", "" )
+DefNode(ShaderNode, SH_NODE_SEPHSV, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" )
+DefNode(ShaderNode, SH_NODE_COMBHSV, 0, "COMBHSV", CombineHSV, "Combine HSV", "" )
+DefNode(ShaderNode, SH_NODE_UVMAP, def_sh_uvmap, "UVMAP", UVMap, "UV Map", "" )
+DefNode(ShaderNode, SH_NODE_UVALONGSTROKE, def_sh_uvalongstroke, "UVALONGSTROKE", UVAlongStroke, "UV Along Stroke", "" )
+DefNode(ShaderNode, SH_NODE_SEPXYZ, 0, "SEPXYZ", SeparateXYZ, "Separate XYZ", "" )
+DefNode(ShaderNode, SH_NODE_COMBXYZ, 0, "COMBXYZ", CombineXYZ, "Combine XYZ", "" )
+DefNode(ShaderNode, SH_NODE_BEVEL, def_sh_bevel, "BEVEL", Bevel, "Bevel", "" )
+DefNode(ShaderNode, SH_NODE_DISPLACEMENT, def_sh_displacement, "DISPLACEMENT", Displacement, "Displacement", "" )
+DefNode(ShaderNode, SH_NODE_VECTOR_DISPLACEMENT,def_sh_vector_displacement,"VECTOR_DISPLACEMENT",VectorDisplacement,"Vector Displacement","" )
+DefNode(ShaderNode, SH_NODE_TEX_IES, def_sh_tex_ies, "TEX_IES", TexIES, "IES Texture", "" )
-DefNode( CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" );
-DefNode( CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" );
-DefNode( CompositorNode, CMP_NODE_VALUE, 0, "VALUE", Value, "Value", "" );
-DefNode( CompositorNode, CMP_NODE_MIX_RGB, def_mix_rgb, "MIX_RGB", MixRGB, "Mix", "" );
-DefNode( CompositorNode, CMP_NODE_VALTORGB, def_colorramp, "VALTORGB", ValToRGB, "ColorRamp", "" );
-DefNode( CompositorNode, CMP_NODE_RGBTOBW, 0, "RGBTOBW", RGBToBW, "RGB to BW", "" );
-DefNode( CompositorNode, CMP_NODE_NORMAL, 0, "NORMAL", Normal, "Normal", "" );
-DefNode( CompositorNode, CMP_NODE_CURVE_VEC, def_vector_curve, "CURVE_VEC", CurveVec, "Vector Curves", "" );
-DefNode( CompositorNode, CMP_NODE_CURVE_RGB, def_rgb_curve, "CURVE_RGB", CurveRGB, "RGB Curves", "" );
-DefNode( CompositorNode, CMP_NODE_ALPHAOVER, def_cmp_alpha_over, "ALPHAOVER", AlphaOver, "Alpha Over", "" );
-DefNode( CompositorNode, CMP_NODE_BLUR, def_cmp_blur, "BLUR", Blur, "Blur", "" );
-DefNode( CompositorNode, CMP_NODE_FILTER, def_cmp_filter, "FILTER", Filter, "Filter", "" );
-DefNode( CompositorNode, CMP_NODE_MAP_VALUE, def_cmp_map_value, "MAP_VALUE", MapValue, "Map Value", "" );
-DefNode( CompositorNode, CMP_NODE_MAP_RANGE, def_cmp_map_range, "MAP_RANGE", MapRange, "Map Range", "" );
-DefNode( CompositorNode, CMP_NODE_TIME, def_time, "TIME", Time, "Time", "" );
-DefNode( CompositorNode, CMP_NODE_VECBLUR, def_cmp_vector_blur, "VECBLUR", VecBlur, "Vector Blur", "" );
-DefNode( CompositorNode, CMP_NODE_SEPRGBA, 0, "SEPRGBA", SepRGBA, "Separate RGBA", "" );
-DefNode( CompositorNode, CMP_NODE_SEPHSVA, 0, "SEPHSVA", SepHSVA, "Separate HSVA", "" );
-DefNode( CompositorNode, CMP_NODE_SETALPHA, 0, "SETALPHA", SetAlpha, "Set Alpha", "" );
-DefNode( CompositorNode, CMP_NODE_HUE_SAT, 0, "HUE_SAT", HueSat, "Hue Saturation Value","" );
-DefNode( CompositorNode, CMP_NODE_IMAGE, def_cmp_image, "IMAGE", Image, "Image", "" );
-DefNode( CompositorNode, CMP_NODE_R_LAYERS, def_cmp_render_layers, "R_LAYERS", RLayers, "Render Layers", "" );
-DefNode( CompositorNode, CMP_NODE_COMPOSITE, def_cmp_composite, "COMPOSITE", Composite, "Composite", "" );
+DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" )
+DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" )
+DefNode(CompositorNode, CMP_NODE_VALUE, 0, "VALUE", Value, "Value", "" )
+DefNode(CompositorNode, CMP_NODE_MIX_RGB, def_mix_rgb, "MIX_RGB", MixRGB, "Mix", "" )
+DefNode(CompositorNode, CMP_NODE_VALTORGB, def_colorramp, "VALTORGB", ValToRGB, "ColorRamp", "" )
+DefNode(CompositorNode, CMP_NODE_RGBTOBW, 0, "RGBTOBW", RGBToBW, "RGB to BW", "" )
+DefNode(CompositorNode, CMP_NODE_NORMAL, 0, "NORMAL", Normal, "Normal", "" )
+DefNode(CompositorNode, CMP_NODE_CURVE_VEC, def_vector_curve, "CURVE_VEC", CurveVec, "Vector Curves", "" )
+DefNode(CompositorNode, CMP_NODE_CURVE_RGB, def_rgb_curve, "CURVE_RGB", CurveRGB, "RGB Curves", "" )
+DefNode(CompositorNode, CMP_NODE_ALPHAOVER, def_cmp_alpha_over, "ALPHAOVER", AlphaOver, "Alpha Over", "" )
+DefNode(CompositorNode, CMP_NODE_BLUR, def_cmp_blur, "BLUR", Blur, "Blur", "" )
+DefNode(CompositorNode, CMP_NODE_FILTER, def_cmp_filter, "FILTER", Filter, "Filter", "" )
+DefNode(CompositorNode, CMP_NODE_MAP_VALUE, def_cmp_map_value, "MAP_VALUE", MapValue, "Map Value", "" )
+DefNode(CompositorNode, CMP_NODE_MAP_RANGE, def_cmp_map_range, "MAP_RANGE", MapRange, "Map Range", "" )
+DefNode(CompositorNode, CMP_NODE_TIME, def_time, "TIME", Time, "Time", "" )
+DefNode(CompositorNode, CMP_NODE_VECBLUR, def_cmp_vector_blur, "VECBLUR", VecBlur, "Vector Blur", "" )
+DefNode(CompositorNode, CMP_NODE_SEPRGBA, 0, "SEPRGBA", SepRGBA, "Separate RGBA", "" )
+DefNode(CompositorNode, CMP_NODE_SEPHSVA, 0, "SEPHSVA", SepHSVA, "Separate HSVA", "" )
+DefNode(CompositorNode, CMP_NODE_SETALPHA, 0, "SETALPHA", SetAlpha, "Set Alpha", "" )
+DefNode(CompositorNode, CMP_NODE_HUE_SAT, 0, "HUE_SAT", HueSat, "Hue Saturation Value","" )
+DefNode(CompositorNode, CMP_NODE_IMAGE, def_cmp_image, "IMAGE", Image, "Image", "" )
+DefNode(CompositorNode, CMP_NODE_R_LAYERS, def_cmp_render_layers, "R_LAYERS", RLayers, "Render Layers", "" )
+DefNode(CompositorNode, CMP_NODE_COMPOSITE, def_cmp_composite, "COMPOSITE", Composite, "Composite", "" )
/* NB: OutputFile node has special rna setup function called in rna_nodetree.c */
-DefNode( CompositorNode, CMP_NODE_OUTPUT_FILE, 0, "OUTPUT_FILE", OutputFile, "File Output", "" );
-DefNode( CompositorNode, CMP_NODE_TEXTURE, def_texture, "TEXTURE", Texture, "Texture", "" );
-DefNode( CompositorNode, CMP_NODE_TRANSLATE, def_cmp_translate, "TRANSLATE", Translate, "Translate", "" );
-DefNode( CompositorNode, CMP_NODE_ZCOMBINE, def_cmp_zcombine, "ZCOMBINE", Zcombine, "Z Combine", "" );
-DefNode( CompositorNode, CMP_NODE_COMBRGBA, 0, "COMBRGBA", CombRGBA, "Combine RGBA", "" );
-DefNode( CompositorNode, CMP_NODE_DILATEERODE, def_cmp_dilate_erode, "DILATEERODE", DilateErode, "Dilate/Erode", "" );
-DefNode( CompositorNode, CMP_NODE_INPAINT, def_cmp_inpaint, "INPAINT", Inpaint, "Inpaint", "" );
-DefNode( CompositorNode, CMP_NODE_DESPECKLE, def_cmp_despeckle, "DESPECKLE", Despeckle, "Despeckle", "" );
-DefNode( CompositorNode, CMP_NODE_ROTATE, def_cmp_rotate, "ROTATE", Rotate, "Rotate", "" );
-DefNode( CompositorNode, CMP_NODE_SCALE, def_cmp_scale, "SCALE", Scale, "Scale", "" );
-DefNode( CompositorNode, CMP_NODE_SEPYCCA, def_cmp_ycc, "SEPYCCA", SepYCCA, "Separate YCbCrA", "" );
-DefNode( CompositorNode, CMP_NODE_COMBYCCA, def_cmp_ycc, "COMBYCCA", CombYCCA, "Combine YCbCrA", "" );
-DefNode( CompositorNode, CMP_NODE_SEPYUVA, 0, "SEPYUVA", SepYUVA, "Separate YUVA", "" );
-DefNode( CompositorNode, CMP_NODE_COMBYUVA, 0, "COMBYUVA", CombYUVA, "Combine YUVA", "" );
-DefNode( CompositorNode, CMP_NODE_DIFF_MATTE, def_cmp_diff_matte, "DIFF_MATTE", DiffMatte, "Difference Key", "" );
-DefNode( CompositorNode, CMP_NODE_COLOR_SPILL, def_cmp_color_spill, "COLOR_SPILL", ColorSpill, "Color Spill", "" );
-DefNode( CompositorNode, CMP_NODE_CHROMA_MATTE, def_cmp_chroma_matte, "CHROMA_MATTE", ChromaMatte, "Chroma Key", "" );
-DefNode( CompositorNode, CMP_NODE_CHANNEL_MATTE, def_cmp_channel_matte, "CHANNEL_MATTE", ChannelMatte, "Channel Key", "" );
-DefNode( CompositorNode, CMP_NODE_FLIP, def_cmp_flip, "FLIP", Flip, "Flip", "" );
-DefNode( CompositorNode, CMP_NODE_SPLITVIEWER, def_cmp_splitviewer, "SPLITVIEWER", SplitViewer, "Split Viewer", "" );
-DefNode( CompositorNode, CMP_NODE_MAP_UV, def_cmp_map_uv, "MAP_UV", MapUV, "Map UV", "" );
-DefNode( CompositorNode, CMP_NODE_ID_MASK, def_cmp_id_mask, "ID_MASK", IDMask, "ID Mask", "" );
-DefNode( CompositorNode, CMP_NODE_DOUBLEEDGEMASK, def_cmp_double_edge_mask,"DOUBLEEDGEMASK", DoubleEdgeMask, "Double Edge Mask", "" );
-DefNode( CompositorNode, CMP_NODE_DEFOCUS, def_cmp_defocus, "DEFOCUS", Defocus, "Defocus", "" );
-DefNode( CompositorNode, CMP_NODE_DISPLACE, 0, "DISPLACE", Displace, "Displace", "" );
-DefNode( CompositorNode, CMP_NODE_COMBHSVA, 0, "COMBHSVA", CombHSVA, "Combine HSVA", "" );
-DefNode( CompositorNode, CMP_NODE_MATH, def_math, "MATH", Math, "Math", "" );
-DefNode( CompositorNode, CMP_NODE_LUMA_MATTE, def_cmp_luma_matte, "LUMA_MATTE", LumaMatte, "Luminance Key", "" );
-DefNode( CompositorNode, CMP_NODE_BRIGHTCONTRAST, def_cmp_brightcontrast, "BRIGHTCONTRAST", BrightContrast, "Bright/Contrast", "" );
-DefNode( CompositorNode, CMP_NODE_GAMMA, 0, "GAMMA", Gamma, "Gamma", "" );
-DefNode( CompositorNode, CMP_NODE_INVERT, def_cmp_invert, "INVERT", Invert, "Invert", "" );
-DefNode( CompositorNode, CMP_NODE_NORMALIZE, 0, "NORMALIZE", Normalize, "Normalize", "" );
-DefNode( CompositorNode, CMP_NODE_CROP, def_cmp_crop, "CROP", Crop, "Crop", "" );
-DefNode( CompositorNode, CMP_NODE_DBLUR, def_cmp_dblur, "DBLUR", DBlur, "Directional Blur", "" );
-DefNode( CompositorNode, CMP_NODE_BILATERALBLUR, def_cmp_bilateral_blur, "BILATERALBLUR", Bilateralblur, "Bilateral Blur", "" );
-DefNode( CompositorNode, CMP_NODE_PREMULKEY, def_cmp_premul_key, "PREMULKEY", PremulKey, "Alpha Convert", "" );
-DefNode( CompositorNode, CMP_NODE_GLARE, def_cmp_glare, "GLARE", Glare, "Glare", "" );
-DefNode( CompositorNode, CMP_NODE_TONEMAP, def_cmp_tonemap, "TONEMAP", Tonemap, "Tonemap", "" );
-DefNode( CompositorNode, CMP_NODE_LENSDIST, def_cmp_lensdist, "LENSDIST", Lensdist, "Lens Distortion", "" );
-DefNode( CompositorNode, CMP_NODE_VIEW_LEVELS, def_cmp_levels, "LEVELS", Levels, "Levels", "" );
-DefNode( CompositorNode, CMP_NODE_COLOR_MATTE, def_cmp_color_matte, "COLOR_MATTE", ColorMatte, "Color Key", "" );
-DefNode( CompositorNode, CMP_NODE_DIST_MATTE, def_cmp_distance_matte, "DISTANCE_MATTE", DistanceMatte, "Distance Key", "" );
-DefNode( CompositorNode, CMP_NODE_COLORBALANCE, def_cmp_colorbalance, "COLORBALANCE", ColorBalance, "Color Balance", "" );
-DefNode( CompositorNode, CMP_NODE_HUECORRECT, def_cmp_huecorrect, "HUECORRECT", HueCorrect, "Hue Correct", "" );
-DefNode( CompositorNode, CMP_NODE_MOVIECLIP, def_cmp_movieclip, "MOVIECLIP", MovieClip, "Movie Clip", "" );
-DefNode( CompositorNode, CMP_NODE_TRANSFORM, dev_cmd_transform, "TRANSFORM", Transform, "Transform", "" );
-DefNode( CompositorNode, CMP_NODE_STABILIZE2D, def_cmp_stabilize2d, "STABILIZE2D", Stabilize, "Stabilize 2D", "" );
-DefNode( CompositorNode, CMP_NODE_MOVIEDISTORTION,def_cmp_moviedistortion,"MOVIEDISTORTION",MovieDistortion, "Movie Distortion", "" );
-DefNode( CompositorNode, CMP_NODE_MASK_BOX, def_cmp_boxmask, "BOXMASK", BoxMask, "Box Mask", "" );
-DefNode( CompositorNode, CMP_NODE_MASK_ELLIPSE, def_cmp_ellipsemask, "ELLIPSEMASK", EllipseMask, "Ellipse Mask", "" );
-DefNode( CompositorNode, CMP_NODE_BOKEHIMAGE, def_cmp_bokehimage, "BOKEHIMAGE", BokehImage, "Bokeh Image", "" );
-DefNode( CompositorNode, CMP_NODE_BOKEHBLUR, def_cmp_bokehblur, "BOKEHBLUR", BokehBlur, "Bokeh Blur", "" );
-DefNode( CompositorNode, CMP_NODE_SWITCH, def_cmp_switch, "SWITCH", Switch, "Switch", "" );
-DefNode( CompositorNode, CMP_NODE_SWITCH_VIEW, def_cmp_switch_view, "VIEWSWITCH", SwitchView, "View Switch", "" );
-DefNode( CompositorNode, CMP_NODE_COLORCORRECTION,def_cmp_colorcorrection,"COLORCORRECTION",ColorCorrection, "Color Correction", "" );
-DefNode( CompositorNode, CMP_NODE_MASK, def_cmp_mask, "MASK", Mask, "Mask", "" );
-DefNode( CompositorNode, CMP_NODE_KEYINGSCREEN, def_cmp_keyingscreen, "KEYINGSCREEN", KeyingScreen, "Keying Screen", "" );
-DefNode( CompositorNode, CMP_NODE_KEYING, def_cmp_keying, "KEYING", Keying, "Keying", "" );
-DefNode( CompositorNode, CMP_NODE_TRACKPOS, def_cmp_trackpos, "TRACKPOS", TrackPos, "Track Position", "" );
-DefNode( CompositorNode, CMP_NODE_PIXELATE, 0, "PIXELATE", Pixelate, "Pixelate", "" );
-DefNode( CompositorNode, CMP_NODE_PLANETRACKDEFORM,def_cmp_planetrackdeform,"PLANETRACKDEFORM",PlaneTrackDeform,"Plane Track Deform","" );
-DefNode( CompositorNode, CMP_NODE_CORNERPIN, 0, "CORNERPIN", CornerPin, "Corner Pin", "" );
-DefNode( CompositorNode, CMP_NODE_SUNBEAMS, def_cmp_sunbeams, "SUNBEAMS", SunBeams, "Sun Beams", "" );
-DefNode( CompositorNode, CMP_NODE_CRYPTOMATTE, def_cmp_cryptomatte, "CRYPTOMATTE", Cryptomatte, "Cryptomatte", "" );
+DefNode(CompositorNode, CMP_NODE_OUTPUT_FILE, 0, "OUTPUT_FILE", OutputFile, "File Output", "" )
+DefNode(CompositorNode, CMP_NODE_TEXTURE, def_texture, "TEXTURE", Texture, "Texture", "" )
+DefNode(CompositorNode, CMP_NODE_TRANSLATE, def_cmp_translate, "TRANSLATE", Translate, "Translate", "" )
+DefNode(CompositorNode, CMP_NODE_ZCOMBINE, def_cmp_zcombine, "ZCOMBINE", Zcombine, "Z Combine", "" )
+DefNode(CompositorNode, CMP_NODE_COMBRGBA, 0, "COMBRGBA", CombRGBA, "Combine RGBA", "" )
+DefNode(CompositorNode, CMP_NODE_DILATEERODE, def_cmp_dilate_erode, "DILATEERODE", DilateErode, "Dilate/Erode", "" )
+DefNode(CompositorNode, CMP_NODE_INPAINT, def_cmp_inpaint, "INPAINT", Inpaint, "Inpaint", "" )
+DefNode(CompositorNode, CMP_NODE_DESPECKLE, def_cmp_despeckle, "DESPECKLE", Despeckle, "Despeckle", "" )
+DefNode(CompositorNode, CMP_NODE_ROTATE, def_cmp_rotate, "ROTATE", Rotate, "Rotate", "" )
+DefNode(CompositorNode, CMP_NODE_SCALE, def_cmp_scale, "SCALE", Scale, "Scale", "" )
+DefNode(CompositorNode, CMP_NODE_SEPYCCA, def_cmp_ycc, "SEPYCCA", SepYCCA, "Separate YCbCrA", "" )
+DefNode(CompositorNode, CMP_NODE_COMBYCCA, def_cmp_ycc, "COMBYCCA", CombYCCA, "Combine YCbCrA", "" )
+DefNode(CompositorNode, CMP_NODE_SEPYUVA, 0, "SEPYUVA", SepYUVA, "Separate YUVA", "" )
+DefNode(CompositorNode, CMP_NODE_COMBYUVA, 0, "COMBYUVA", CombYUVA, "Combine YUVA", "" )
+DefNode(CompositorNode, CMP_NODE_DIFF_MATTE, def_cmp_diff_matte, "DIFF_MATTE", DiffMatte, "Difference Key", "" )
+DefNode(CompositorNode, CMP_NODE_COLOR_SPILL, def_cmp_color_spill, "COLOR_SPILL", ColorSpill, "Color Spill", "" )
+DefNode(CompositorNode, CMP_NODE_CHROMA_MATTE, def_cmp_chroma_matte, "CHROMA_MATTE", ChromaMatte, "Chroma Key", "" )
+DefNode(CompositorNode, CMP_NODE_CHANNEL_MATTE, def_cmp_channel_matte, "CHANNEL_MATTE", ChannelMatte, "Channel Key", "" )
+DefNode(CompositorNode, CMP_NODE_FLIP, def_cmp_flip, "FLIP", Flip, "Flip", "" )
+DefNode(CompositorNode, CMP_NODE_SPLITVIEWER, def_cmp_splitviewer, "SPLITVIEWER", SplitViewer, "Split Viewer", "" )
+DefNode(CompositorNode, CMP_NODE_MAP_UV, def_cmp_map_uv, "MAP_UV", MapUV, "Map UV", "" )
+DefNode(CompositorNode, CMP_NODE_ID_MASK, def_cmp_id_mask, "ID_MASK", IDMask, "ID Mask", "" )
+DefNode(CompositorNode, CMP_NODE_DOUBLEEDGEMASK, def_cmp_double_edge_mask,"DOUBLEEDGEMASK", DoubleEdgeMask, "Double Edge Mask", "" )
+DefNode(CompositorNode, CMP_NODE_DEFOCUS, def_cmp_defocus, "DEFOCUS", Defocus, "Defocus", "" )
+DefNode(CompositorNode, CMP_NODE_DISPLACE, 0, "DISPLACE", Displace, "Displace", "" )
+DefNode(CompositorNode, CMP_NODE_COMBHSVA, 0, "COMBHSVA", CombHSVA, "Combine HSVA", "" )
+DefNode(CompositorNode, CMP_NODE_MATH, def_math, "MATH", Math, "Math", "" )
+DefNode(CompositorNode, CMP_NODE_LUMA_MATTE, def_cmp_luma_matte, "LUMA_MATTE", LumaMatte, "Luminance Key", "" )
+DefNode(CompositorNode, CMP_NODE_BRIGHTCONTRAST, def_cmp_brightcontrast, "BRIGHTCONTRAST", BrightContrast, "Bright/Contrast", "" )
+DefNode(CompositorNode, CMP_NODE_GAMMA, 0, "GAMMA", Gamma, "Gamma", "" )
+DefNode(CompositorNode, CMP_NODE_INVERT, def_cmp_invert, "INVERT", Invert, "Invert", "" )
+DefNode(CompositorNode, CMP_NODE_NORMALIZE, 0, "NORMALIZE", Normalize, "Normalize", "" )
+DefNode(CompositorNode, CMP_NODE_CROP, def_cmp_crop, "CROP", Crop, "Crop", "" )
+DefNode(CompositorNode, CMP_NODE_DBLUR, def_cmp_dblur, "DBLUR", DBlur, "Directional Blur", "" )
+DefNode(CompositorNode, CMP_NODE_BILATERALBLUR, def_cmp_bilateral_blur, "BILATERALBLUR", Bilateralblur, "Bilateral Blur", "" )
+DefNode(CompositorNode, CMP_NODE_PREMULKEY, def_cmp_premul_key, "PREMULKEY", PremulKey, "Alpha Convert", "" )
+DefNode(CompositorNode, CMP_NODE_GLARE, def_cmp_glare, "GLARE", Glare, "Glare", "" )
+DefNode(CompositorNode, CMP_NODE_TONEMAP, def_cmp_tonemap, "TONEMAP", Tonemap, "Tonemap", "" )
+DefNode(CompositorNode, CMP_NODE_LENSDIST, def_cmp_lensdist, "LENSDIST", Lensdist, "Lens Distortion", "" )
+DefNode(CompositorNode, CMP_NODE_VIEW_LEVELS, def_cmp_levels, "LEVELS", Levels, "Levels", "" )
+DefNode(CompositorNode, CMP_NODE_COLOR_MATTE, def_cmp_color_matte, "COLOR_MATTE", ColorMatte, "Color Key", "" )
+DefNode(CompositorNode, CMP_NODE_DIST_MATTE, def_cmp_distance_matte, "DISTANCE_MATTE", DistanceMatte, "Distance Key", "" )
+DefNode(CompositorNode, CMP_NODE_COLORBALANCE, def_cmp_colorbalance, "COLORBALANCE", ColorBalance, "Color Balance", "" )
+DefNode(CompositorNode, CMP_NODE_HUECORRECT, def_cmp_huecorrect, "HUECORRECT", HueCorrect, "Hue Correct", "" )
+DefNode(CompositorNode, CMP_NODE_MOVIECLIP, def_cmp_movieclip, "MOVIECLIP", MovieClip, "Movie Clip", "" )
+DefNode(CompositorNode, CMP_NODE_TRANSFORM, dev_cmd_transform, "TRANSFORM", Transform, "Transform", "" )
+DefNode(CompositorNode, CMP_NODE_STABILIZE2D, def_cmp_stabilize2d, "STABILIZE2D", Stabilize, "Stabilize 2D", "" )
+DefNode(CompositorNode, CMP_NODE_MOVIEDISTORTION,def_cmp_moviedistortion,"MOVIEDISTORTION",MovieDistortion, "Movie Distortion", "" )
+DefNode(CompositorNode, CMP_NODE_MASK_BOX, def_cmp_boxmask, "BOXMASK", BoxMask, "Box Mask", "" )
+DefNode(CompositorNode, CMP_NODE_MASK_ELLIPSE, def_cmp_ellipsemask, "ELLIPSEMASK", EllipseMask, "Ellipse Mask", "" )
+DefNode(CompositorNode, CMP_NODE_BOKEHIMAGE, def_cmp_bokehimage, "BOKEHIMAGE", BokehImage, "Bokeh Image", "" )
+DefNode(CompositorNode, CMP_NODE_BOKEHBLUR, def_cmp_bokehblur, "BOKEHBLUR", BokehBlur, "Bokeh Blur", "" )
+DefNode(CompositorNode, CMP_NODE_SWITCH, def_cmp_switch, "SWITCH", Switch, "Switch", "" )
+DefNode(CompositorNode, CMP_NODE_SWITCH_VIEW, def_cmp_switch_view, "VIEWSWITCH", SwitchView, "View Switch", "" )
+DefNode(CompositorNode, CMP_NODE_COLORCORRECTION,def_cmp_colorcorrection,"COLORCORRECTION",ColorCorrection, "Color Correction", "" )
+DefNode(CompositorNode, CMP_NODE_MASK, def_cmp_mask, "MASK", Mask, "Mask", "" )
+DefNode(CompositorNode, CMP_NODE_KEYINGSCREEN, def_cmp_keyingscreen, "KEYINGSCREEN", KeyingScreen, "Keying Screen", "" )
+DefNode(CompositorNode, CMP_NODE_KEYING, def_cmp_keying, "KEYING", Keying, "Keying", "" )
+DefNode(CompositorNode, CMP_NODE_TRACKPOS, def_cmp_trackpos, "TRACKPOS", TrackPos, "Track Position", "" )
+DefNode(CompositorNode, CMP_NODE_PIXELATE, 0, "PIXELATE", Pixelate, "Pixelate", "" )
+DefNode(CompositorNode, CMP_NODE_PLANETRACKDEFORM,def_cmp_planetrackdeform,"PLANETRACKDEFORM",PlaneTrackDeform,"Plane Track Deform","" )
+DefNode(CompositorNode, CMP_NODE_CORNERPIN, 0, "CORNERPIN", CornerPin, "Corner Pin", "" )
+DefNode(CompositorNode, CMP_NODE_SUNBEAMS, def_cmp_sunbeams, "SUNBEAMS", SunBeams, "Sun Beams", "" )
+DefNode(CompositorNode, CMP_NODE_CRYPTOMATTE, def_cmp_cryptomatte, "CRYPTOMATTE", Cryptomatte, "Cryptomatte", "" )
-DefNode( TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" );
-DefNode( TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" );
-DefNode( TextureNode, TEX_NODE_TEXTURE, def_texture, "TEXTURE", Texture, "Texture", "" );
-DefNode( TextureNode, TEX_NODE_BRICKS, def_tex_bricks, "BRICKS", Bricks, "Bricks", "" );
-DefNode( TextureNode, TEX_NODE_MATH, def_math, "MATH", Math, "Math", "" );
-DefNode( TextureNode, TEX_NODE_MIX_RGB, def_mix_rgb, "MIX_RGB", MixRGB, "Mix RGB", "" );
-DefNode( TextureNode, TEX_NODE_RGBTOBW, 0, "RGBTOBW", RGBToBW, "RGB to BW", "" );
-DefNode( TextureNode, TEX_NODE_VALTORGB, def_colorramp, "VALTORGB", ValToRGB, "ColorRamp", "" );
-DefNode( TextureNode, TEX_NODE_IMAGE, def_tex_image, "IMAGE", Image, "Image", "" );
-DefNode( TextureNode, TEX_NODE_CURVE_RGB, def_rgb_curve, "CURVE_RGB", CurveRGB, "RGB Curves", "" );
-DefNode( TextureNode, TEX_NODE_INVERT, 0, "INVERT", Invert, "Invert", "" );
-DefNode( TextureNode, TEX_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue/Saturation", "" );
-DefNode( TextureNode, TEX_NODE_CURVE_TIME, def_time, "CURVE_TIME", CurveTime, "Curve Time", "" );
-DefNode( TextureNode, TEX_NODE_ROTATE, 0, "ROTATE", Rotate, "Rotate", "" );
-DefNode( TextureNode, TEX_NODE_VIEWER, 0, "VIEWER", Viewer, "Viewer", "" );
-DefNode( TextureNode, TEX_NODE_TRANSLATE, 0, "TRANSLATE", Translate, "Translate", "" );
-DefNode( TextureNode, TEX_NODE_COORD, 0, "COORD", Coordinates, "Coordinates", "" );
-DefNode( TextureNode, TEX_NODE_DISTANCE, 0, "DISTANCE", Distance, "Distance", "" );
-DefNode( TextureNode, TEX_NODE_COMPOSE, 0, "COMPOSE", Compose, "Combine RGBA", "" );
-DefNode( TextureNode, TEX_NODE_DECOMPOSE, 0, "DECOMPOSE", Decompose, "Separate RGBA", "" );
-DefNode( TextureNode, TEX_NODE_VALTONOR, 0, "VALTONOR", ValToNor, "Value to Normal", "" );
-DefNode( TextureNode, TEX_NODE_SCALE, 0, "SCALE", Scale, "Scale", "" );
-DefNode( TextureNode, TEX_NODE_AT, 0, "AT", At, "At", "" );
+DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" )
+DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" )
+DefNode(TextureNode, TEX_NODE_TEXTURE, def_texture, "TEXTURE", Texture, "Texture", "" )
+DefNode(TextureNode, TEX_NODE_BRICKS, def_tex_bricks, "BRICKS", Bricks, "Bricks", "" )
+DefNode(TextureNode, TEX_NODE_MATH, def_math, "MATH", Math, "Math", "" )
+DefNode(TextureNode, TEX_NODE_MIX_RGB, def_mix_rgb, "MIX_RGB", MixRGB, "Mix RGB", "" )
+DefNode(TextureNode, TEX_NODE_RGBTOBW, 0, "RGBTOBW", RGBToBW, "RGB to BW", "" )
+DefNode(TextureNode, TEX_NODE_VALTORGB, def_colorramp, "VALTORGB", ValToRGB, "ColorRamp", "" )
+DefNode(TextureNode, TEX_NODE_IMAGE, def_tex_image, "IMAGE", Image, "Image", "" )
+DefNode(TextureNode, TEX_NODE_CURVE_RGB, def_rgb_curve, "CURVE_RGB", CurveRGB, "RGB Curves", "" )
+DefNode(TextureNode, TEX_NODE_INVERT, 0, "INVERT", Invert, "Invert", "" )
+DefNode(TextureNode, TEX_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue/Saturation", "" )
+DefNode(TextureNode, TEX_NODE_CURVE_TIME, def_time, "CURVE_TIME", CurveTime, "Curve Time", "" )
+DefNode(TextureNode, TEX_NODE_ROTATE, 0, "ROTATE", Rotate, "Rotate", "" )
+DefNode(TextureNode, TEX_NODE_VIEWER, 0, "VIEWER", Viewer, "Viewer", "" )
+DefNode(TextureNode, TEX_NODE_TRANSLATE, 0, "TRANSLATE", Translate, "Translate", "" )
+DefNode(TextureNode, TEX_NODE_COORD, 0, "COORD", Coordinates, "Coordinates", "" )
+DefNode(TextureNode, TEX_NODE_DISTANCE, 0, "DISTANCE", Distance, "Distance", "" )
+DefNode(TextureNode, TEX_NODE_COMPOSE, 0, "COMPOSE", Compose, "Combine RGBA", "" )
+DefNode(TextureNode, TEX_NODE_DECOMPOSE, 0, "DECOMPOSE", Decompose, "Separate RGBA", "" )
+DefNode(TextureNode, TEX_NODE_VALTONOR, 0, "VALTONOR", ValToNor, "Value to Normal", "" )
+DefNode(TextureNode, TEX_NODE_SCALE, 0, "SCALE", Scale, "Scale", "" )
+DefNode(TextureNode, TEX_NODE_AT, 0, "AT", At, "At", "" )
/* procedural textures */
-DefNode( TextureNode, TEX_NODE_PROC+TEX_VORONOI, 0, "TEX_VORONOI", TexVoronoi, "Voronoi", "" );
-DefNode( TextureNode, TEX_NODE_PROC+TEX_BLEND, 0, "TEX_BLEND", TexBlend, "Blend", "" );
-DefNode( TextureNode, TEX_NODE_PROC+TEX_MAGIC, 0, "TEX_MAGIC", TexMagic, "Magic", "" );
-DefNode( TextureNode, TEX_NODE_PROC+TEX_MARBLE, 0, "TEX_MARBLE", TexMarble, "Marble", "" );
-DefNode( TextureNode, TEX_NODE_PROC+TEX_CLOUDS, 0, "TEX_CLOUDS", TexClouds, "Clouds", "" );
-DefNode( TextureNode, TEX_NODE_PROC+TEX_WOOD, 0, "TEX_WOOD", TexWood, "Wood", "" );
-DefNode( TextureNode, TEX_NODE_PROC+TEX_MUSGRAVE, 0, "TEX_MUSGRAVE", TexMusgrave, "Musgrave", "" );
-DefNode( TextureNode, TEX_NODE_PROC+TEX_NOISE, 0, "TEX_NOISE", TexNoise, "Noise", "" );
-DefNode( TextureNode, TEX_NODE_PROC+TEX_STUCCI, 0, "TEX_STUCCI", TexStucci, "Stucci", "" );
-DefNode( TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DISTNOISE", TexDistNoise, "Distorted Noise", "" );
-
+DefNode(TextureNode, TEX_NODE_PROC+TEX_VORONOI, 0, "TEX_VORONOI", TexVoronoi, "Voronoi", "" )
+DefNode(TextureNode, TEX_NODE_PROC+TEX_BLEND, 0, "TEX_BLEND", TexBlend, "Blend", "" )
+DefNode(TextureNode, TEX_NODE_PROC+TEX_MAGIC, 0, "TEX_MAGIC", TexMagic, "Magic", "" )
+DefNode(TextureNode, TEX_NODE_PROC+TEX_MARBLE, 0, "TEX_MARBLE", TexMarble, "Marble", "" )
+DefNode(TextureNode, TEX_NODE_PROC+TEX_CLOUDS, 0, "TEX_CLOUDS", TexClouds, "Clouds", "" )
+DefNode(TextureNode, TEX_NODE_PROC+TEX_WOOD, 0, "TEX_WOOD", TexWood, "Wood", "" )
+DefNode(TextureNode, TEX_NODE_PROC+TEX_MUSGRAVE, 0, "TEX_MUSGRAVE", TexMusgrave, "Musgrave", "" )
+DefNode(TextureNode, TEX_NODE_PROC+TEX_NOISE, 0, "TEX_NOISE", TexNoise, "Noise", "" )
+DefNode(TextureNode, TEX_NODE_PROC+TEX_STUCCI, 0, "TEX_STUCCI", TexStucci, "Stucci", "" )
+DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DISTNOISE", TexDistNoise, "Distorted Noise", "" )
/* undefine macros */
#undef DefNode
diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.c b/source/blender/nodes/composite/nodes/node_composite_scale.c
index a98872ea43b..4bd9b5e5db7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_scale.c
+++ b/source/blender/nodes/composite/nodes/node_composite_scale.c
@@ -44,7 +44,7 @@ static void node_composite_update_scale(bNodeTree *UNUSED(ntree), bNode *node)
/* Only show X/Y scale factor inputs for modes using them! */
for (sock = node->inputs.first; sock; sock = sock->next) {
- if (STREQ(sock->name, "X") || STREQ(sock->name, "Y")) {
+ if (STR_ELEM(sock->name, "X", "Y")) {
if (use_xy_scale) {
sock->flag &= ~SOCK_UNAVAIL;
}
diff --git a/source/blender/physics/CMakeLists.txt b/source/blender/physics/CMakeLists.txt
index 800cc6bbdc3..0a991bf2929 100644
--- a/source/blender/physics/CMakeLists.txt
+++ b/source/blender/physics/CMakeLists.txt
@@ -45,4 +45,7 @@ set(SRC
BPH_mass_spring.h
)
-blender_add_lib(bf_physics "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_physics "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/physics/intern/implicit_blender.c b/source/blender/physics/intern/implicit_blender.c
index cc8caca9588..83f586c4078 100644
--- a/source/blender/physics/intern/implicit_blender.c
+++ b/source/blender/physics/intern/implicit_blender.c
@@ -493,18 +493,6 @@ BLI_INLINE void madd_m3_m3fl(float r[3][3], float m[3][3], float f)
r[2][2] += m[2][2] * f;
}
-BLI_INLINE void madd_m3_m3m3fl(float r[3][3], float a[3][3], float b[3][3], float f)
-{
- r[0][0] = a[0][0] + b[0][0] * f;
- r[0][1] = a[0][1] + b[0][1] * f;
- r[0][2] = a[0][2] + b[0][2] * f;
- r[1][0] = a[1][0] + b[1][0] * f;
- r[1][1] = a[1][1] + b[1][1] * f;
- r[1][2] = a[1][2] + b[1][2] * f;
- r[2][0] = a[2][0] + b[2][0] * f;
- r[2][1] = a[2][1] + b[2][1] * f;
- r[2][2] = a[2][2] + b[2][2] * f;
-}
/////////////////////////////////////////////////////////////////
///////////////////////////
diff --git a/source/blender/python/bmesh/CMakeLists.txt b/source/blender/python/bmesh/CMakeLists.txt
index c9524634f7e..8e92b43eb46 100644
--- a/source/blender/python/bmesh/CMakeLists.txt
+++ b/source/blender/python/bmesh/CMakeLists.txt
@@ -51,8 +51,11 @@ set(SRC
bmesh_py_utils.h
)
+set(LIB
+)
+
if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
-blender_add_lib(bf_python_bmesh "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_python_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/python/generic/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt
index c7b361247ef..3ebd90e5b42 100644
--- a/source/blender/python/generic/CMakeLists.txt
+++ b/source/blender/python/generic/CMakeLists.txt
@@ -50,6 +50,9 @@ set(SRC
python_utildefines.h
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_python_ext "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_python_ext "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt
index 3c3a9b7e577..aeef81c5fd7 100644
--- a/source/blender/python/gpu/CMakeLists.txt
+++ b/source/blender/python/gpu/CMakeLists.txt
@@ -54,6 +54,9 @@ set(SRC
gpu_py_vertex_format.h
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_python_gpu "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_python_gpu "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 89e5e368ced..d517d6a9529 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -117,6 +117,9 @@ set(SRC
../BPY_extern_clog.h
)
+set(LIB
+)
+
# only to check if buildinfo is available
if(WITH_BUILDINFO)
add_definitions(-DBUILD_DATE)
@@ -298,4 +301,4 @@ endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_python "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_python "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index f695464ec42..d9052aafae8 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -4136,7 +4136,7 @@ static PyObject *pyrna_struct_getattro(BPy_StructRNA *self, PyObject *pyname)
}
else if (name[0] == '_') { /* rna can't start with a "_", so for __dict__ and similar we can skip using rna lookups */
/* annoying exception, maybe we need to have different types for this... */
- if ((STREQ(name, "__getitem__") || STREQ(name, "__setitem__")) && !RNA_struct_idprops_check(self->ptr.type)) {
+ if (STR_ELEM(name, "__getitem__", "__setitem__") && !RNA_struct_idprops_check(self->ptr.type)) {
PyErr_SetString(PyExc_AttributeError, "bpy_struct: no __getitem__ support for this type");
ret = NULL;
}
diff --git a/source/blender/python/mathutils/CMakeLists.txt b/source/blender/python/mathutils/CMakeLists.txt
index 4a8d69f72fd..b973875977a 100644
--- a/source/blender/python/mathutils/CMakeLists.txt
+++ b/source/blender/python/mathutils/CMakeLists.txt
@@ -55,5 +55,8 @@ set(SRC
mathutils_noise.h
)
+set(LIB
+)
+
-blender_add_lib(bf_python_mathutils "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_python_mathutils "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt
index fc2c0b324a9..96c8b9c860b 100644
--- a/source/blender/render/CMakeLists.txt
+++ b/source/blender/render/CMakeLists.txt
@@ -69,6 +69,9 @@ set(SRC
intern/include/zbuf.h
)
+set(LIB
+)
+
if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
list(APPEND INC
@@ -103,4 +106,4 @@ if(APPLE)
endif()
endif()
-blender_add_lib_nolist(bf_render "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib_nolist(bf_render "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/render/extern/include/RE_engine.h b/source/blender/render/extern/include/RE_engine.h
index 780894f419d..d22ab6d782d 100644
--- a/source/blender/render/extern/include/RE_engine.h
+++ b/source/blender/render/extern/include/RE_engine.h
@@ -58,7 +58,6 @@ struct bNodeTree;
#define RE_USE_SHADING_NODES 16
#define RE_USE_EXCLUDE_LAYERS 32
#define RE_USE_SAVE_BUFFERS 64
-#define RE_USE_TEXTURE_PREVIEW 128
#define RE_USE_SHADING_NODES_CUSTOM 256
#define RE_USE_SPHERICAL_STEREO 512
diff --git a/source/blender/render/intern/source/multires_bake.c b/source/blender/render/intern/source/multires_bake.c
index 632c501c54c..8a8e4bbbb2a 100644
--- a/source/blender/render/intern/source/multires_bake.c
+++ b/source/blender/render/intern/source/multires_bake.c
@@ -802,11 +802,14 @@ static void free_normal_data(void *bake_data)
MEM_freeN(normal_data);
}
-/* MultiresBake callback for normals' baking
- * general idea:
- * - find coord and normal of point with specified UV in hi-res mesh
- * - multiply it by tangmat
- * - vector in color space would be norm(vec) /2 + (0.5, 0.5, 0.5) */
+/**
+ * MultiresBake callback for normals' baking.
+ *
+ * General idea:
+ * - Find coord and normal of point with specified UV in hi-res mesh.
+ * - Multiply it by tangmat.
+ * - Vector in color space would be `norm(vec) / 2 + (0.5, 0.5, 0.5)`.
+ */
static void apply_tangmat_callback(DerivedMesh *lores_dm, DerivedMesh *hires_dm, void *UNUSED(thread_data),
void *bake_data, ImBuf *ibuf, const int tri_index, const int lvl,
const float st[2], float tangmat[3][3], const int x, const int y)
diff --git a/source/blender/shader_fx/CMakeLists.txt b/source/blender/shader_fx/CMakeLists.txt
index ddd21449d40..e8f2a038dce 100644
--- a/source/blender/shader_fx/CMakeLists.txt
+++ b/source/blender/shader_fx/CMakeLists.txt
@@ -57,10 +57,13 @@ set(SRC
FX_shader_types.h
)
+set(LIB
+)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
add_definitions(${GL_DEFINITIONS})
-blender_add_lib(bf_shader_fx "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib(bf_shader_fx "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 10f79691924..3f4f18005ef 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -106,6 +106,9 @@ set(SRC
message_bus/wm_message_bus.h
)
+set(LIB
+)
+
if(WITH_AUDASPACE)
add_definitions(-DWITH_AUDASPACE)
@@ -166,4 +169,4 @@ if(WITH_COMPOSITOR)
add_definitions(-DWITH_COMPOSITOR)
endif()
-blender_add_lib_nolist(bf_windowmanager "${SRC}" "${INC}" "${INC_SYS}")
+blender_add_lib_nolist(bf_windowmanager "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c
index 7346fa31300..1a4141c0178 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c
@@ -644,10 +644,12 @@ void WM_gizmo_properties_create(PointerRNA *ptr, const char *gtstring)
{
const wmGizmoType *gzt = WM_gizmotype_find(gtstring, false);
- if (gzt)
+ if (gzt) {
WM_gizmo_properties_create_ptr(ptr, (wmGizmoType *)gzt);
- else
+ }
+ else {
RNA_pointer_create(NULL, &RNA_GizmoProperties, NULL, ptr);
+ }
}
/* similar to the function above except its uses ID properties
@@ -674,10 +676,12 @@ void WM_gizmo_properties_sanitize(PointerRNA *ptr, const bool no_context)
{
switch (RNA_property_type(prop)) {
case PROP_ENUM:
- if (no_context)
+ if (no_context) {
RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
- else
+ }
+ else {
RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT);
+ }
break;
case PROP_POINTER:
{
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
index 42dd5e8f293..ae264480751 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
@@ -145,18 +145,18 @@ int WM_gizmo_cmp_temp_fl(const void *gz_a_ptr, const void *gz_b_ptr)
{
const wmGizmo *gz_a = gz_a_ptr;
const wmGizmo *gz_b = gz_b_ptr;
- if (gz_a->temp.f < gz_b->temp.f) return -1;
- else if (gz_a->temp.f > gz_b->temp.f) return 1;
- else return 0;
+ if (gz_a->temp.f < gz_b->temp.f) { return -1; }
+ else if (gz_a->temp.f > gz_b->temp.f) { return 1; }
+ else { return 0; }
}
int WM_gizmo_cmp_temp_fl_reverse(const void *gz_a_ptr, const void *gz_b_ptr)
{
const wmGizmo *gz_a = gz_a_ptr;
const wmGizmo *gz_b = gz_b_ptr;
- if (gz_a->temp.f < gz_b->temp.f) return 1;
- else if (gz_a->temp.f > gz_b->temp.f) return -1;
- else return 0;
+ if (gz_a->temp.f < gz_b->temp.f) { return 1; }
+ else if (gz_a->temp.f > gz_b->temp.f) { return -1; }
+ else { return 0; }
}
wmGizmo *wm_gizmogroup_find_intersected_gizmo(
@@ -636,8 +636,9 @@ static wmKeyMap *gizmogroup_tweak_modal_keymap(wmKeyConfig *keyconf, const char
keymap = WM_modalkeymap_get(keyconf, name);
/* this function is called for each spacetype, only needs to add map once */
- if (keymap && keymap->modal_items)
+ if (keymap && keymap->modal_items) {
return NULL;
+ }
keymap = WM_modalkeymap_add(keyconf, name, modal_items);
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
index df9f38824b0..112943a7dba 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
@@ -345,8 +345,9 @@ static void gizmomap_prepare_drawing(
wmGizmoMap *gzmap, const bContext *C, ListBase *draw_gizmos,
const eWM_GizmoFlagMapDrawStep drawstep)
{
- if (!gzmap || BLI_listbase_is_empty(&gzmap->groups))
+ if (!gzmap || BLI_listbase_is_empty(&gzmap->groups)) {
return;
+ }
gzmap->is_init = false;
@@ -844,8 +845,9 @@ bool WM_gizmomap_select_all(bContext *C, wmGizmoMap *gzmap, const int action)
break;
}
- if (changed)
+ if (changed) {
WM_event_add_mousemove(C);
+ }
return changed;
}
@@ -874,12 +876,15 @@ void wm_gizmomap_handler_context_op(bContext *C, wmEventHandler_Op *handler)
else {
ARegion *ar;
CTX_wm_area_set(C, sa);
- for (ar = sa->regionbase.first; ar; ar = ar->next)
- if (ar == handler->context.region)
+ for (ar = sa->regionbase.first; ar; ar = ar->next) {
+ if (ar == handler->context.region) {
break;
+ }
+ }
/* XXX no warning print here, after full-area and back regions are remade */
- if (ar)
+ if (ar) {
CTX_wm_region_set(C, ar);
+ }
}
}
}
@@ -1198,11 +1203,13 @@ void WM_gizmoconfig_update_tag_remove(
*/
void WM_gizmoconfig_update(struct Main *bmain)
{
- if (G.background)
+ if (G.background) {
return;
+ }
- if (wm_gzmap_type_update_flag == 0)
+ if (wm_gzmap_type_update_flag == 0) {
return;
+ }
if (wm_gzmap_type_update_flag & WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE) {
for (wmGizmoMapType *gzmap_type = gizmomaptypes.first;
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index 2c6dcd56471..a05dce9d59a 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -219,9 +219,11 @@ void WM_operator_handlers_clear(wmWindowManager *wm, wmOperatorType *ot)
void WM_keyconfig_reload(bContext *C)
{
if (CTX_py_init_get(C) && !G.background) {
+#ifdef WITH_PYTHON
BPY_execute_string(
C, (const char *[]){"bpy", NULL},
"bpy.utils.keyconfig_init()");
+#endif
}
}
@@ -354,8 +356,9 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
wmOperator *op;
wmKeyConfig *keyconf;
- if (wm->autosavetimer)
+ if (wm->autosavetimer) {
wm_autosave_timer_ended(wm);
+ }
while ((win = BLI_pophead(&wm->windows))) {
/* prevent draw clear to use screen */
@@ -388,7 +391,9 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
wm->undo_stack = NULL;
}
- if (C && CTX_wm_manager(C) == wm) CTX_wm_manager_set(C, NULL);
+ if (C && CTX_wm_manager(C) == wm) {
+ CTX_wm_manager_set(C, NULL);
+ }
}
void wm_close_and_free_all(bContext *C, ListBase *wmlist)
diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c
index de293373870..a04b3d1c843 100644
--- a/source/blender/windowmanager/intern/wm_cursors.c
+++ b/source/blender/windowmanager/intern/wm_cursors.c
@@ -114,19 +114,22 @@ void WM_cursor_set(wmWindow *win, int curs)
#ifdef _WIN32
/* the default win32 cross cursor is barely visible,
* only 1 pixel thick, use another one instead */
- if (curs == CURSOR_EDIT)
+ if (curs == CURSOR_EDIT) {
curs = BC_CROSSCURSOR;
+ }
#else
/* in case of large cursor, also use custom cursor because
* large cursors don't work for system cursors */
- if (U.curssize && curs == CURSOR_EDIT)
+ if (U.curssize && curs == CURSOR_EDIT) {
curs = BC_CROSSCURSOR;
+ }
#endif
GHOST_SetCursorVisibility(win->ghostwin, 1);
- if (curs == CURSOR_STD && win->modalcursor)
+ if (curs == CURSOR_STD && win->modalcursor) {
curs = win->modalcursor;
+ }
win->cursor = curs;
@@ -135,7 +138,9 @@ void WM_cursor_set(wmWindow *win, int curs)
GHOST_SetCursorShape(win->ghostwin, convert_cursor(curs));
}
else {
- if ((curs < SYSCURSOR) || (curs >= BC_NUMCURSORS)) return;
+ if ((curs < SYSCURSOR) || (curs >= BC_NUMCURSORS)) {
+ return;
+ }
if (curs == SYSCURSOR) { /* System default Cursor */
GHOST_SetCursorShape(win->ghostwin, convert_cursor(CURSOR_STD));
@@ -168,8 +173,9 @@ bool WM_cursor_set_from_tool(struct wmWindow *win, const ScrArea *sa, const AReg
void WM_cursor_modal_set(wmWindow *win, int val)
{
- if (win->lastcursor == 0)
+ if (win->lastcursor == 0) {
win->lastcursor = win->cursor;
+ }
win->modalcursor = val;
WM_cursor_set(win, val);
}
@@ -177,8 +183,9 @@ void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
{
win->modalcursor = 0;
- if (win->lastcursor)
+ if (win->lastcursor) {
WM_cursor_set(win, win->lastcursor);
+ }
win->lastcursor = 0;
}
@@ -304,8 +311,9 @@ void WM_cursor_time(wmWindow *win, int nr)
unsigned char bitmap[16][2] = {{0}};
int i, idx;
- if (win->lastcursor == 0)
+ if (win->lastcursor == 0) {
win->lastcursor = win->cursor;
+ }
memset(&mask, 0xFF, sizeof(mask));
@@ -315,8 +323,9 @@ void WM_cursor_time(wmWindow *win, int nr)
int x = idx % 2;
int y = idx / 2;
- for (i = 0; i < 8; i++)
+ for (i = 0; i < 8; i++) {
bitmap[i + y * 8][x] = digit[i];
+ }
nr /= 10;
}
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index 11e803c4b82..92ce9957351 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -76,10 +76,13 @@ ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid)
{
wmDropBoxMap *dm;
- for (dm = dropboxes.first; dm; dm = dm->next)
- if (dm->spaceid == spaceid && dm->regionid == regionid)
- if (STREQLEN(idname, dm->idname, KMAP_MAX_NAME))
+ for (dm = dropboxes.first; dm; dm = dm->next) {
+ if (dm->spaceid == spaceid && dm->regionid == regionid) {
+ if (STREQLEN(idname, dm->idname, KMAP_MAX_NAME)) {
return &dm->dropboxes;
+ }
+ }
+ }
dm = MEM_callocN(sizeof(struct wmDropBoxMap), "dropmap list");
BLI_strncpy(dm->idname, idname, KMAP_MAX_NAME);
@@ -222,13 +225,19 @@ static const char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *e
const char *name;
name = dropbox_active(C, &win->handlers, drag, event);
- if (name) return name;
+ if (name) {
+ return name;
+ }
name = dropbox_active(C, &sa->handlers, drag, event);
- if (name) return name;
+ if (name) {
+ return name;
+ }
name = dropbox_active(C, &ar->handlers, drag, event);
- if (name) return name;
+ if (name) {
+ return name;
+ }
return NULL;
}
@@ -241,8 +250,9 @@ static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *e
const int winsize_y = WM_window_pixels_y(win);
/* for multiwin drags, we only do this if mouse inside */
- if (event->x < 0 || event->y < 0 || event->x > winsize_x || event->y > winsize_y)
+ if (event->x < 0 || event->y < 0 || event->x > winsize_x || event->y > winsize_y) {
return;
+ }
drag->opname[0] = 0;
@@ -361,14 +371,18 @@ static const char *wm_drag_name(wmDrag *drag)
static void drag_rect_minmax(rcti *rect, int x1, int y1, int x2, int y2)
{
- if (rect->xmin > x1)
+ if (rect->xmin > x1) {
rect->xmin = x1;
- if (rect->xmax < x2)
+ }
+ if (rect->xmax < x2) {
rect->xmax = x2;
- if (rect->ymin > y1)
+ }
+ if (rect->ymin > y1) {
rect->ymin = y1;
- if (rect->ymax < y2)
+ }
+ if (rect->ymax < y2) {
rect->ymax = y2;
+ }
}
/* called in wm_draw.c */
@@ -400,8 +414,9 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
x = cursorx - drag->sx / 2;
y = cursory - drag->sy / 2;
- if (rect)
+ if (rect) {
drag_rect_minmax(rect, x, y, x + drag->sx, y + drag->sy);
+ }
else {
float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
@@ -413,10 +428,12 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
x = cursorx - 2 * padding;
y = cursory - 2 * UI_DPI_FAC;
- if (rect)
+ if (rect) {
drag_rect_minmax(rect, x, y, x + iconsize, y + iconsize);
- else
+ }
+ else {
UI_icon_draw_aspect(x, y, drag->icon, 1.0f / UI_DPI_FAC, 0.8, text_col);
+ }
}
/* item name */
@@ -442,10 +459,12 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
if (drag->imb) {
x = cursorx - drag->sx / 2;
- if (cursory + drag->sy / 2 + padding + iconsize < winsize_y)
+ if (cursory + drag->sy / 2 + padding + iconsize < winsize_y) {
y = cursory + drag->sy / 2 + padding;
- else
+ }
+ else {
y = cursory - drag->sy / 2 - padding - iconsize - padding - iconsize;
+ }
}
else {
x = cursorx - 2 * padding;
@@ -462,8 +481,9 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
int w = UI_fontstyle_string_width(fstyle, wm_drag_name(drag));
drag_rect_minmax(rect, x, y, x + w, y + iconsize);
}
- else
+ else {
wm_drop_operator_draw(drag->opname, x, y);
+ }
}
}
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index 4f299201bdf..08ece1fd5c5 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -189,8 +189,9 @@ static bool wm_draw_region_stereo_set(Main *bmain, ScrArea *sa, ARegion *ar, eSt
static void wm_area_mark_invalid_backbuf(ScrArea *sa)
{
- if (sa->spacetype == SPACE_VIEW3D)
+ if (sa->spacetype == SPACE_VIEW3D) {
((View3D *)sa->spacedata.first)->flag |= V3D_INVALID_BACKBUF;
+ }
}
static void wm_region_test_render_do_draw(const Scene *scene, struct Depsgraph *depsgraph,
@@ -207,10 +208,12 @@ static void wm_region_test_render_do_draw(const Scene *scene, struct Depsgraph *
rcti border_rect;
/* do partial redraw when possible */
- if (ED_view3d_calc_render_border(scene, depsgraph, v3d, ar, &border_rect))
+ if (ED_view3d_calc_render_border(scene, depsgraph, v3d, ar, &border_rect)) {
ED_region_tag_redraw_partial(ar, &border_rect);
- else
+ }
+ else {
ED_region_tag_redraw(ar);
+ }
engine->flag &= ~RE_ENGINE_DO_DRAW;
}
@@ -702,8 +705,9 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
}
/* always draw, not only when screen tagged */
- if (win->gesture.first)
+ if (win->gesture.first) {
wm_gesture_draw(win);
+ }
/* needs pixel coords in screen */
if (wm->drags.first) {
@@ -798,32 +802,40 @@ static bool wm_draw_update_test_window(wmWindow *win)
screen->do_draw_paintcursor = true;
ar->do_draw_overlay = false;
}
- if (ar->visible && ar->do_draw)
+ if (ar->visible && ar->do_draw) {
do_draw = true;
+ }
}
ED_screen_areas_iter(win, screen, sa) {
for (ar = sa->regionbase.first; ar; ar = ar->next) {
wm_region_test_render_do_draw(scene, depsgraph, sa, ar);
- if (ar->visible && ar->do_draw)
+ if (ar->visible && ar->do_draw) {
do_draw = true;
+ }
}
}
- if (do_draw)
+ if (do_draw) {
return true;
+ }
- if (screen->do_refresh)
+ if (screen->do_refresh) {
return true;
- if (screen->do_draw)
+ }
+ if (screen->do_draw) {
return true;
- if (screen->do_draw_gesture)
+ }
+ if (screen->do_draw_gesture) {
return true;
- if (screen->do_draw_paintcursor)
+ }
+ if (screen->do_draw_paintcursor) {
return true;
- if (screen->do_draw_drag)
+ }
+ if (screen->do_draw_drag) {
return true;
+ }
return false;
}
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 940e7fa8e96..5d22eb79601 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -185,9 +185,11 @@ static bool wm_test_duplicate_notifier(wmWindowManager *wm, unsigned int type, v
{
wmNotifier *note;
- for (note = wm->queue.first; note; note = note->next)
- if ((note->category | note->data | note->subtype | note->action) == type && note->reference == reference)
+ for (note = wm->queue.first; note; note = note->next) {
+ if ((note->category | note->data | note->subtype | note->action) == type && note->reference == reference) {
return 1;
+ }
+ }
return 0;
}
@@ -198,8 +200,9 @@ void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference
wmWindowManager *wm = CTX_wm_manager(C);
wmNotifier *note;
- if (wm_test_duplicate_notifier(wm, type, reference))
+ if (wm_test_duplicate_notifier(wm, type, reference)) {
return;
+ }
note = MEM_callocN(sizeof(wmNotifier), "notifier");
@@ -222,8 +225,9 @@ void WM_main_add_notifier(unsigned int type, void *reference)
wmWindowManager *wm = bmain->wm.first;
wmNotifier *note;
- if (!wm || wm_test_duplicate_notifier(wm, type, reference))
+ if (!wm || wm_test_duplicate_notifier(wm, type, reference)) {
return;
+ }
note = MEM_callocN(sizeof(wmNotifier), "notifier");
@@ -376,8 +380,9 @@ void wm_event_do_notifiers(bContext *C)
wmNotifier *note, *next;
wmWindow *win;
- if (wm == NULL)
+ if (wm == NULL) {
return;
+ }
BLI_timer_execute();
@@ -398,8 +403,9 @@ void wm_event_do_notifiers(bContext *C)
wm->file_saved = 1;
wm_window_title(wm, win);
}
- else if (note->data == ND_DATACHANGED)
+ else if (note->data == ND_DATACHANGED) {
wm_window_title(wm, win);
+ }
}
if (note->window == win) {
if (note->category == NC_SCREEN) {
@@ -409,15 +415,17 @@ void wm_event_do_notifiers(bContext *C)
UI_popup_handlers_remove_all(C, &win->modalhandlers);
WM_window_set_active_workspace(C, win, ref_ws);
- if (G.debug & G_DEBUG_EVENTS)
+ if (G.debug & G_DEBUG_EVENTS) {
printf("%s: Workspace set %p\n", __func__, note->reference);
+ }
}
else if (note->data == ND_WORKSPACE_DELETE) {
WorkSpace *workspace = note->reference;
ED_workspace_delete(workspace, CTX_data_main(C), C, wm); // XXX hrms, think this over!
- if (G.debug & G_DEBUG_EVENTS)
+ if (G.debug & G_DEBUG_EVENTS) {
printf("%s: Workspace delete %p\n", __func__, workspace);
+ }
}
else if (note->data == ND_LAYOUTBROWSE) {
bScreen *ref_screen = BKE_workspace_layout_screen_get(note->reference);
@@ -427,16 +435,18 @@ void wm_event_do_notifiers(bContext *C)
ED_screen_change(C, ref_screen); /* XXX hrms, think this over! */
- if (G.debug & G_DEBUG_EVENTS)
+ if (G.debug & G_DEBUG_EVENTS) {
printf("%s: screen set %p\n", __func__, note->reference);
+ }
}
else if (note->data == ND_LAYOUTDELETE) {
WorkSpace *workspace = WM_window_get_active_workspace(win);
WorkSpaceLayout *layout = note->reference;
ED_workspace_layout_delete(workspace, layout, C); // XXX hrms, think this over!
- if (G.debug & G_DEBUG_EVENTS)
+ if (G.debug & G_DEBUG_EVENTS) {
printf("%s: screen delete %p\n", __func__, note->reference);
+ }
}
}
}
@@ -445,8 +455,9 @@ void wm_event_do_notifiers(bContext *C)
(note->window == NULL && (note->reference == NULL || note->reference == scene)))
{
if (note->category == NC_SCENE) {
- if (note->data == ND_FRAME)
+ if (note->data == ND_FRAME) {
do_anim = true;
+ }
}
}
if (ELEM(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_WM)) {
@@ -565,10 +576,12 @@ static int wm_handler_ui_call(bContext *C, wmEventHandler_UI *handler, const wmE
/* UI is quite aggressive with swallowing events, like scrollwheel */
/* I realize this is not extremely nice code... when UI gets keymaps it can be maybe smarter */
if (do_wheel_ui == false) {
- if (is_wheel)
+ if (is_wheel) {
return WM_HANDLER_CONTINUE;
- else if (wm_event_always_pass(event) == 0)
+ }
+ else if (wm_event_always_pass(event) == 0) {
do_wheel_ui = true;
+ }
}
/* we set context to where ui handler came from */
@@ -597,12 +610,14 @@ static int wm_handler_ui_call(bContext *C, wmEventHandler_UI *handler, const wmE
CTX_wm_menu_set(C, NULL);
}
- if (retval == WM_UI_HANDLER_BREAK)
+ if (retval == WM_UI_HANDLER_BREAK) {
return WM_HANDLER_BREAK;
+ }
/* event not handled in UI, if wheel then we temporarily disable it */
- if (is_wheel)
+ if (is_wheel) {
do_wheel_ui = false;
+ }
return WM_HANDLER_CONTINUE;
}
@@ -612,8 +627,9 @@ static void wm_handler_ui_cancel(bContext *C)
wmWindow *win = CTX_wm_window(C);
ARegion *ar = CTX_wm_region(C);
- if (!ar)
+ if (!ar) {
return;
+ }
LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, &ar->handlers) {
if (handler_base->type == WM_HANDLER_TYPE_UI) {
@@ -636,15 +652,18 @@ bool WM_operator_poll(bContext *C, wmOperatorType *ot)
for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
wmOperatorType *ot_macro = WM_operatortype_find(otmacro->idname, 0);
- if (0 == WM_operator_poll(C, ot_macro))
+ if (0 == WM_operator_poll(C, ot_macro)) {
return 0;
+ }
}
/* python needs operator type, so we added exception for it */
- if (ot->pyop_poll)
+ if (ot->pyop_poll) {
return ot->pyop_poll(C, ot);
- else if (ot->poll)
+ }
+ else if (ot->poll) {
return ot->poll(C);
+ }
return 1;
}
@@ -836,8 +855,9 @@ static void wm_operator_reports(bContext *C, wmOperator *op, int retval, bool ca
ScrArea *area_prev = CTX_wm_area(C);
ARegion *ar_prev = CTX_wm_region(C);
- if (win_prev == NULL)
+ if (win_prev == NULL) {
CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
+ }
UI_popup_menu_reports(C, op->reports);
@@ -959,11 +979,13 @@ static int wm_operator_exec(
CTX_wm_operator_poll_msg_set(C, NULL);
- if (op == NULL || op->type == NULL)
+ if (op == NULL || op->type == NULL) {
return retval;
+ }
- if (0 == WM_operator_poll(C, op->type))
+ if (0 == WM_operator_poll(C, op->type)) {
return retval;
+ }
if (op->type->exec) {
if (op->type->flag & OPTYPE_UNDO) {
@@ -987,8 +1009,9 @@ static int wm_operator_exec(
/* XXX Disabled the repeat check to address part 2 of #31840.
* Carefully checked all calls to wm_operator_exec and WM_operator_repeat, don't see any reason
* why this was needed, but worth to note it in case something turns bad. (mont29) */
- if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED) /* && repeat == 0 */)
+ if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED) /* && repeat == 0 */) {
wm_operator_reports(C, op, retval, false);
+ }
if (retval & OPERATOR_FINISHED) {
wm_operator_finished(C, op, repeat, store && wm->op_undo_depth == 0);
@@ -1009,8 +1032,9 @@ static int wm_operator_exec_notest(bContext *C, wmOperator *op)
{
int retval = OPERATOR_CANCELLED;
- if (op == NULL || op->type == NULL || op->type->exec == NULL)
+ if (op == NULL || op->type == NULL || op->type->exec == NULL) {
return retval;
+ }
retval = op->type->exec(C, op);
OPERATOR_RETVAL_CHECK(retval);
@@ -1060,7 +1084,7 @@ int WM_operator_repeat_interactive(bContext *C, wmOperator *op)
* \return true if #WM_operator_repeat can run
* simple check for now but may become more involved.
* To be sure the operator can run call `WM_operator_poll(C, op->type)` also, since this call
- * checks if WM_operator_repeat() can run at all, not that it WILL run at any time.
+ * checks if #WM_operator_repeat() can run at all, not that it WILL run at any time.
*/
bool WM_operator_repeat_check(const bContext *UNUSED(C), wmOperator *op)
{
@@ -1146,8 +1170,9 @@ static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot,
RNA_STRUCT_BEGIN (properties, prop)
{
- if (otmacro == NULL)
+ if (otmacro == NULL) {
break;
+ }
/* skip invalid properties */
if (STREQ(RNA_property_identifier(prop), otmacro->idname)) {
@@ -1175,8 +1200,9 @@ static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot,
}
}
- if (root)
+ if (root) {
motherop = NULL;
+ }
}
WM_operator_properties_sanitize(op->ptr, 0);
@@ -1311,8 +1337,9 @@ static int wm_operator_invoke(
int retval = OPERATOR_PASS_THROUGH;
/* this is done because complicated setup is done to call this function that is better not duplicated */
- if (poll_only)
+ if (poll_only) {
return WM_operator_poll(C, ot);
+ }
if (WM_operator_poll(C, ot)) {
wmWindowManager *wm = CTX_wm_manager(C);
@@ -1337,24 +1364,28 @@ static int wm_operator_invoke(
if (op->type->invoke && event) {
wm_region_mouse_co(C, event);
- if (op->type->flag & OPTYPE_UNDO)
+ if (op->type->flag & OPTYPE_UNDO) {
wm->op_undo_depth++;
+ }
retval = op->type->invoke(C, op, event);
OPERATOR_RETVAL_CHECK(retval);
- if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
+ if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
wm->op_undo_depth--;
+ }
}
else if (op->type->exec) {
- if (op->type->flag & OPTYPE_UNDO)
+ if (op->type->flag & OPTYPE_UNDO) {
wm->op_undo_depth++;
+ }
retval = op->type->exec(C, op);
OPERATOR_RETVAL_CHECK(retval);
- if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
+ if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
wm->op_undo_depth--;
+ }
}
else {
/* debug, important to leave a while, should never happen */
@@ -1543,8 +1574,9 @@ static int wm_operator_call_internal(
ar1 = BKE_area_find_region_type(area, type);
}
- if (ar1)
+ if (ar1) {
CTX_wm_region_set(C, ar1);
+ }
}
retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
@@ -1659,11 +1691,15 @@ int WM_operator_call_py(
* we could have some more obvious way of doing this like passing a flag.
*/
wmWindowManager *wm = CTX_wm_manager(C);
- if (!is_undo && wm) wm->op_undo_depth++;
+ if (!is_undo && wm) {
+ wm->op_undo_depth++;
+ }
retval = wm_operator_call_internal(C, ot, properties, reports, context, false, NULL);
- if (!is_undo && wm && (wm == CTX_wm_manager(C))) wm->op_undo_depth--;
+ if (!is_undo && wm && (wm == CTX_wm_manager(C))) {
+ wm->op_undo_depth--;
+ }
return retval;
}
@@ -1684,8 +1720,9 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const
bScreen *screen = CTX_wm_screen(C);
if (screen && handler->op) {
- if (handler->context.area == NULL)
+ if (handler->context.area == NULL) {
CTX_wm_area_set(C, NULL);
+ }
else {
ScrArea *sa = NULL;
@@ -1727,8 +1764,9 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const
}
/* XXX no warning print here, after full-area and back regions are remade */
- if (ar)
+ if (ar) {
CTX_wm_region_set(C, ar);
+ }
}
}
}
@@ -1948,8 +1986,9 @@ static wmKeyMapItem *wm_eventmatch_modal_keymap_items(const wmKeyMap *keymap, wm
static void wm_event_modalkeymap(const bContext *C, wmOperator *op, wmEvent *event, bool *dbl_click_disabled)
{
/* support for modal keymap in macros */
- if (op->opm)
+ if (op->opm) {
op = op->opm;
+ }
if (op->type->modalkeymap) {
wmKeyMap *keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
@@ -2014,8 +2053,9 @@ static void wm_event_modalmap_end(wmEvent *event, bool dbl_click_disabled)
event->val = event->prevval;
event->prevval = 0;
}
- else if (dbl_click_disabled)
+ else if (dbl_click_disabled) {
event->val = KM_DBL_CLICK;
+ }
}
@@ -2050,8 +2090,9 @@ static int wm_handler_operator_call(
wm_region_mouse_co(C, event);
wm_event_modalkeymap(C, op, event, &dbl_click_disabled);
- if (ot->flag & OPTYPE_UNDO)
+ if (ot->flag & OPTYPE_UNDO) {
wm->op_undo_depth++;
+ }
/* warning, after this call all context data and 'event' may be freed. see check below */
retval = ot->modal(C, op, event);
@@ -2064,8 +2105,9 @@ static int wm_handler_operator_call(
wm_event_modalmap_end(event, dbl_click_disabled);
- if (ot->flag & OPTYPE_UNDO)
+ if (ot->flag & OPTYPE_UNDO) {
wm->op_undo_depth--;
+ }
if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
wm_operator_reports(C, op, retval, false);
@@ -2176,15 +2218,18 @@ static int wm_handler_operator_call(
/* Finished and pass through flag as handled */
/* Finished and pass through flag as handled */
- if (retval == (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH))
+ if (retval == (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH)) {
return WM_HANDLER_HANDLED;
+ }
/* Modal unhandled, break */
- if (retval == (OPERATOR_PASS_THROUGH | OPERATOR_RUNNING_MODAL))
+ if (retval == (OPERATOR_PASS_THROUGH | OPERATOR_RUNNING_MODAL)) {
return (WM_HANDLER_BREAK | WM_HANDLER_MODAL);
+ }
- if (retval & OPERATOR_PASS_THROUGH)
+ if (retval & OPERATOR_PASS_THROUGH) {
return WM_HANDLER_CONTINUE;
+ }
return WM_HANDLER_BREAK;
}
@@ -2269,21 +2314,25 @@ static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHand
if (val == EVT_FILESELECT_EXEC) {
int retval;
- if (handler->op->type->flag & OPTYPE_UNDO)
+ if (handler->op->type->flag & OPTYPE_UNDO) {
wm->op_undo_depth++;
+ }
retval = handler->op->type->exec(C, handler->op);
/* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
- if (handler->op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
+ if (handler->op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
wm->op_undo_depth--;
+ }
/* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
if (CTX_wm_manager(C) == wm && wm->op_undo_depth == 0) {
- if (handler->op->type->flag & OPTYPE_UNDO)
+ if (handler->op->type->flag & OPTYPE_UNDO) {
ED_undo_push_op(C, handler->op);
- else if (handler->op->type->flag & OPTYPE_UNDO_GROUPED)
+ }
+ else if (handler->op->type->flag & OPTYPE_UNDO_GROUPED) {
ED_undo_grouped_push_op(C, handler->op);
+ }
}
if (handler->op->reports->list.first) {
@@ -2295,8 +2344,9 @@ static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHand
ScrArea *area_prev = CTX_wm_area(C);
ARegion *ar_prev = CTX_wm_region(C);
- if (win_prev == NULL)
+ if (win_prev == NULL) {
CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
+ }
BKE_report_print_level_set(handler->op->reports, RPT_WARNING);
UI_popup_menu_reports(C, handler->op->reports);
@@ -2326,13 +2376,15 @@ static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHand
}
else {
if (handler->op->type->cancel) {
- if (handler->op->type->flag & OPTYPE_UNDO)
+ if (handler->op->type->flag & OPTYPE_UNDO) {
wm->op_undo_depth++;
+ }
handler->op->type->cancel(C, handler->op);
- if (handler->op->type->flag & OPTYPE_UNDO)
+ if (handler->op->type->flag & OPTYPE_UNDO) {
wm->op_undo_depth--;
+ }
}
WM_operator_free(handler->op);
@@ -2355,10 +2407,12 @@ static int wm_handler_fileselect_call(
{
int action = WM_HANDLER_CONTINUE;
- if (event->type != EVT_FILESELECT)
+ if (event->type != EVT_FILESELECT) {
return action;
- if (handler->op != (wmOperator *)event->customdata)
+ }
+ if (handler->op != (wmOperator *)event->customdata) {
return action;
+ }
return wm_handler_fileselect_do(C, handlers, handler, event->val);
}
@@ -2370,20 +2424,26 @@ static bool handler_boundbox_test(wmEventHandler *handler, const wmEvent *event)
rcti rect = *handler->bblocal;
BLI_rcti_translate(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
- if (BLI_rcti_isect_pt_v(&rect, &event->x))
+ if (BLI_rcti_isect_pt_v(&rect, &event->x)) {
return 1;
- else if (event->type == MOUSEMOVE && BLI_rcti_isect_pt_v(&rect, &event->prevx))
+ }
+ else if (event->type == MOUSEMOVE && BLI_rcti_isect_pt_v(&rect, &event->prevx)) {
return 1;
- else
+ }
+ else {
return 0;
+ }
}
else {
- if (BLI_rcti_isect_pt_v(handler->bbwin, &event->x))
+ if (BLI_rcti_isect_pt_v(handler->bbwin, &event->x)) {
return 1;
- else if (event->type == MOUSEMOVE && BLI_rcti_isect_pt_v(handler->bbwin, &event->prevx))
+ }
+ else if (event->type == MOUSEMOVE && BLI_rcti_isect_pt_v(handler->bbwin, &event->prevx)) {
return 1;
- else
+ }
+ else {
return 0;
+ }
}
}
return 1;
@@ -2525,8 +2585,9 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
event->custom = 0;
/* XXX fileread case */
- if (CTX_wm_window(C) == NULL)
+ if (CTX_wm_window(C) == NULL) {
return action;
+ }
/* escape from drag loop, got freed */
break;
@@ -2682,10 +2743,12 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
}
if (action & WM_HANDLER_BREAK) {
- if (always_pass)
+ if (always_pass) {
action &= ~WM_HANDLER_BREAK;
- else
+ }
+ else {
break;
+ }
}
}
@@ -2706,8 +2769,9 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
}
}
- if (action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL))
+ if (action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL)) {
wm_cursor_arrow_move(CTX_wm_window(C), event);
+ }
#undef PRINT
@@ -2720,8 +2784,9 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
int action = wm_handlers_do_intern(C, event, handlers);
/* fileread case */
- if (CTX_wm_window(C) == NULL)
+ if (CTX_wm_window(C) == NULL) {
return action;
+ }
if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
@@ -2839,10 +2904,12 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
static int wm_event_inside_i(wmEvent *event, rcti *rect)
{
- if (wm_event_always_pass(event))
+ if (wm_event_always_pass(event)) {
return 1;
- if (BLI_rcti_isect_pt_v(rect, &event->x))
+ }
+ if (BLI_rcti_isect_pt_v(rect, &event->x)) {
return 1;
+ }
return 0;
}
@@ -2853,8 +2920,9 @@ static ScrArea *area_event_inside(bContext *C, const int xy[2])
if (screen) {
ED_screen_areas_iter(win, screen, sa) {
- if (BLI_rcti_isect_pt_v(&sa->totrct, xy))
+ if (BLI_rcti_isect_pt_v(&sa->totrct, xy)) {
return sa;
+ }
}
}
return NULL;
@@ -2866,10 +2934,13 @@ static ARegion *region_event_inside(bContext *C, const int xy[2])
ScrArea *area = CTX_wm_area(C);
ARegion *ar;
- if (screen && area)
- for (ar = area->regionbase.first; ar; ar = ar->next)
- if (BLI_rcti_isect_pt_v(&ar->winrct, xy))
+ if (screen && area) {
+ for (ar = area->regionbase.first; ar; ar = ar->next) {
+ if (BLI_rcti_isect_pt_v(&ar->winrct, xy)) {
return ar;
+ }
+ }
+ }
return NULL;
}
@@ -2894,8 +2965,9 @@ static void wm_paintcursor_test(bContext *C, const wmEvent *event)
if (wm->paintcursors.first) {
ARegion *ar = CTX_wm_region(C);
- if (ar)
+ if (ar) {
wm_paintcursor_tag(C, wm->paintcursors.first, ar);
+ }
/* if previous position was not in current region, we have to set a temp new context */
if (ar == NULL || !BLI_rcti_isect_pt_v(&ar->winrct, &event->prevx)) {
@@ -2933,8 +3005,9 @@ static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *even
/* create customdata, first free existing */
if (event->customdata) {
- if (event->customdatafree)
+ if (event->customdatafree) {
MEM_freeN(event->customdata);
+ }
}
event->custom = EVT_DATA_DRAGDROP;
@@ -2986,8 +3059,9 @@ void wm_event_do_handlers(bContext *C)
BLI_assert(WM_window_get_active_screen(win));
BLI_assert(WM_window_get_active_workspace(win));
- if (screen == NULL)
+ if (screen == NULL) {
wm_event_free_all(win);
+ }
else {
Scene *scene = WM_window_get_active_scene(win);
@@ -3071,8 +3145,9 @@ void wm_event_do_handlers(bContext *C)
action |= wm_handlers_do(C, event, &win->modalhandlers);
/* fileread case */
- if (CTX_wm_window(C) == NULL)
+ if (CTX_wm_window(C) == NULL) {
return;
+ }
/* check for a tooltip */
if (screen == WM_window_get_active_screen(win)) {
@@ -3141,11 +3216,13 @@ void wm_event_do_handlers(bContext *C)
action |= wm_handlers_do(C, event, &ar->handlers);
/* fileread case (python), [#29489] */
- if (CTX_wm_window(C) == NULL)
+ if (CTX_wm_window(C) == NULL) {
return;
+ }
- if (action & WM_HANDLER_BREAK)
+ if (action & WM_HANDLER_BREAK) {
break;
+ }
}
}
}
@@ -3172,8 +3249,9 @@ void wm_event_do_handlers(bContext *C)
action |= wm_handlers_do(C, event, &win->handlers);
/* fileread case */
- if (CTX_wm_window(C) == NULL)
+ if (CTX_wm_window(C) == NULL) {
return;
+ }
}
}
@@ -3652,8 +3730,9 @@ bool WM_event_is_modal_tweak_exit(const wmEvent *event, int tweak_event)
* some items (i.e. markers) being tweaked may end up getting
* dropped all over
*/
- if (event->val != KM_RELEASE)
+ if (event->val != KM_RELEASE) {
return 1;
+ }
}
return 0;
@@ -3935,8 +4014,9 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi
{
int mx = event->x, my = event->y;
- if (wm->windows.first == wm->windows.last)
+ if (wm->windows.first == wm->windows.last) {
return NULL;
+ }
/* in order to use window size and mouse position (pixels), we have to use a WM function */
@@ -4004,8 +4084,9 @@ static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event)
/* some painting operators want accurate mouse events, they can
* handle in between mouse move moves, others can happily ignore
* them for better performance */
- if (event_last && event_last->type == MOUSEMOVE)
+ if (event_last && event_last->type == MOUSEMOVE) {
event_last->type = INBETWEEN_MOUSEMOVE;
+ }
wmEvent *event_new = wm_event_add(win, event);
if (event_last == NULL) {
@@ -4110,20 +4191,27 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
/* get value and type from ghost */
event.val = (type == GHOST_kEventButtonDown) ? KM_PRESS : KM_RELEASE;
- if (bd->button == GHOST_kButtonMaskLeft)
+ if (bd->button == GHOST_kButtonMaskLeft) {
event.type = LEFTMOUSE;
- else if (bd->button == GHOST_kButtonMaskRight)
+ }
+ else if (bd->button == GHOST_kButtonMaskRight) {
event.type = RIGHTMOUSE;
- else if (bd->button == GHOST_kButtonMaskButton4)
+ }
+ else if (bd->button == GHOST_kButtonMaskButton4) {
event.type = BUTTON4MOUSE;
- else if (bd->button == GHOST_kButtonMaskButton5)
+ }
+ else if (bd->button == GHOST_kButtonMaskButton5) {
event.type = BUTTON5MOUSE;
- else if (bd->button == GHOST_kButtonMaskButton6)
+ }
+ else if (bd->button == GHOST_kButtonMaskButton6) {
event.type = BUTTON6MOUSE;
- else if (bd->button == GHOST_kButtonMaskButton7)
+ }
+ else if (bd->button == GHOST_kButtonMaskButton7) {
event.type = BUTTON7MOUSE;
- else
+ }
+ else {
event.type = MIDDLEMOUSE;
+ }
wm_eventemulation(&event, false);
@@ -4206,10 +4294,12 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
event.utf8_buf[0] = '\0';
}
else {
- if (event.ascii < 32 && event.ascii > 0)
+ if (event.ascii < 32 && event.ascii > 0) {
event.ascii = '\0';
- if (event.utf8_buf[0] < 32 && event.utf8_buf[0] > 0)
+ }
+ if (event.utf8_buf[0] < 32 && event.utf8_buf[0] > 0) {
event.utf8_buf[0] = '\0';
+ }
}
if (event.utf8_buf[0]) {
@@ -4226,39 +4316,58 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
case LEFTSHIFTKEY:
case RIGHTSHIFTKEY:
if (event.val == KM_PRESS) {
- if (evt->ctrl || evt->alt || evt->oskey) keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND);
- else keymodifier = KM_MOD_FIRST;
+ if (evt->ctrl || evt->alt || evt->oskey) {
+ keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND);
+ }
+ else {
+ keymodifier = KM_MOD_FIRST;
+ }
}
event.shift = evt->shift = keymodifier;
break;
case LEFTCTRLKEY:
case RIGHTCTRLKEY:
if (event.val == KM_PRESS) {
- if (evt->shift || evt->alt || evt->oskey) keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND);
- else keymodifier = KM_MOD_FIRST;
+ if (evt->shift || evt->alt || evt->oskey) {
+ keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND);
+ }
+ else {
+ keymodifier = KM_MOD_FIRST;
+ }
}
event.ctrl = evt->ctrl = keymodifier;
break;
case LEFTALTKEY:
case RIGHTALTKEY:
if (event.val == KM_PRESS) {
- if (evt->ctrl || evt->shift || evt->oskey) keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND);
- else keymodifier = KM_MOD_FIRST;
+ if (evt->ctrl || evt->shift || evt->oskey) {
+ keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND);
+ }
+ else {
+ keymodifier = KM_MOD_FIRST;
+ }
}
event.alt = evt->alt = keymodifier;
break;
case OSKEY:
if (event.val == KM_PRESS) {
- if (evt->ctrl || evt->alt || evt->shift) keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND);
- else keymodifier = KM_MOD_FIRST;
+ if (evt->ctrl || evt->alt || evt->shift) {
+ keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND);
+ }
+ else {
+ keymodifier = KM_MOD_FIRST;
+ }
}
event.oskey = evt->oskey = keymodifier;
break;
default:
- if (event.val == KM_PRESS && event.keymodifier == 0)
- evt->keymodifier = event.type; /* only set in eventstate, for next event */
- else if (event.val == KM_RELEASE && event.keymodifier == event.type)
+ if (event.val == KM_PRESS && event.keymodifier == 0) {
+ /* Only set in eventstate, for next event. */
+ evt->keymodifier = event.type;
+ }
+ else if (event.val == KM_RELEASE && event.keymodifier == event.type) {
event.keymodifier = evt->keymodifier = 0;
+ }
break;
}
@@ -4271,8 +4380,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
/* this case happens on holding a key pressed, it should not generate
* press events events with the same key as modifier */
- if (event.keymodifier == event.type)
+ if (event.keymodifier == event.type) {
event.keymodifier = 0;
+ }
/* this case happens with an external numpad, and also when using 'dead keys' (to compose complex latin
* characters e.g.), it's not really clear why.
@@ -4308,10 +4418,12 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
{
GHOST_TEventWheelData *wheelData = customdata;
- if (wheelData->z > 0)
+ if (wheelData->z > 0) {
event.type = WHEELUPMOUSE;
- else
+ }
+ else {
event.type = WHEELDOWNMOUSE;
+ }
event.val = KM_PRESS;
wm_event_add(win, &event);
@@ -4488,8 +4600,9 @@ float WM_event_tablet_data(const wmEvent *event, int *pen_flip, float tilt[2])
int erasor = 0;
float pressure = 1;
- if (tilt)
+ if (tilt) {
zero_v2(tilt);
+ }
if (event->tablet_data) {
const wmTabletData *wmtab = event->tablet_data;
@@ -4504,8 +4617,9 @@ float WM_event_tablet_data(const wmEvent *event, int *pen_flip, float tilt[2])
}
}
- if (pen_flip)
+ if (pen_flip) {
(*pen_flip) = erasor;
+ }
return pressure;
}
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 0b990be3eec..abea0571a2c 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -194,8 +194,11 @@ static void wm_window_substitute_old(wmWindowManager *oldwm, wmWindowManager *wm
wm->windrawable = win;
}
- if (!G.background) /* file loading in background mode still calls this */
- GHOST_SetWindowUserData(win->ghostwin, win); /* pointer back */
+ /* File loading in background mode still calls this. */
+ if (!G.background) {
+ /* Pointer back. */
+ GHOST_SetWindowUserData(win->ghostwin, win);
+ }
oldwin->ghostwin = NULL;
oldwin->gpuctx = NULL;
@@ -405,7 +408,11 @@ static int wm_read_exotic(const char *name)
retval = BKE_READ_EXOTIC_OK_BLEND;
}
else {
-#if 0 /* historic stuff - no longer used */
+ /* We may want to support loading other file formats
+ * from their header bytes or file extension.
+ * This used to be supported in the code below and may be added
+ * back at some point. */
+#if 0
WM_cursor_wait(true);
if (is_foo_format(name)) {
@@ -892,8 +899,9 @@ void wm_homefile_read(
NULL) != BKE_BLENDFILE_READ_FAIL;
}
if (BLI_listbase_is_empty(&U.themes)) {
- if (G.debug & G_DEBUG)
+ if (G.debug & G_DEBUG) {
printf("\nNote: No (valid) '%s' found, fall back to built-in default.\n\n", filepath_startup);
+ }
success = false;
}
if (success) {
@@ -1024,7 +1032,9 @@ void wm_history_file_read(void)
int num;
const char * const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL);
- if (!cfgdir) return;
+ if (!cfgdir) {
+ return;
+ }
BLI_make_file_string("/", name, cfgdir, BLENDER_HISTORY_FILE);
@@ -1078,8 +1088,9 @@ static void wm_history_file_write(void)
/* will be NULL in background mode */
user_config_dir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL);
- if (!user_config_dir)
+ if (!user_config_dir) {
return;
+ }
BLI_make_file_string("/", name, user_config_dir, BLENDER_HISTORY_FILE);
@@ -1157,8 +1168,9 @@ static ImBuf *blend_file_thumb(const bContext *C, Scene *scene, bScreen *screen,
}
/* scene can be NULL if running a script at startup and calling the save operator */
- if (G.background || scene == NULL)
+ if (G.background || scene == NULL) {
return NULL;
+ }
if ((scene->camera == NULL) && (screen != NULL)) {
sa = BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0);
@@ -1389,8 +1401,9 @@ void WM_autosave_init(wmWindowManager *wm)
{
wm_autosave_timer_ended(wm);
- if (U.flag & USER_AUTOSAVE)
+ if (U.flag & USER_AUTOSAVE) {
wm->autosavetimer = WM_event_add_timer(wm, NULL, TIMERAUTOSAVE, U.savetime * 60.0);
+ }
}
void wm_autosave_timer(const bContext *C, wmWindowManager *wm, wmTimer *UNUSED(wt))
@@ -1457,8 +1470,12 @@ void wm_autosave_delete(void)
BLI_make_file_string("/", str, BKE_tempdir_base(), BLENDER_QUIT_FILE);
/* if global undo; remove tempsave, otherwise rename */
- if (U.uiflag & USER_GLOBALUNDO) BLI_delete(filename, false, false);
- else BLI_rename(filename, str);
+ if (U.uiflag & USER_GLOBALUNDO) {
+ BLI_delete(filename, false, false);
+ }
+ else {
+ BLI_rename(filename, str);
+ }
}
}
@@ -1543,8 +1560,9 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op)
BLI_callback_exec(bmain, NULL, BLI_CB_EVT_SAVE_PRE);
/* check current window and close it if temp */
- if (win && WM_window_is_temp_screen(win))
+ if (win && WM_window_is_temp_screen(win)) {
wm_window_close(C, wm, win);
+ }
/* update keymaps in user preferences */
WM_keyconfig_update(wm);
@@ -1913,15 +1931,19 @@ static int wm_open_mainfile_exec(bContext *C, wmOperator *op)
wm_open_init_load_ui(op, false);
wm_open_init_use_scripts(op, false);
- if (RNA_boolean_get(op->ptr, "load_ui"))
+ if (RNA_boolean_get(op->ptr, "load_ui")) {
G.fileflags &= ~G_FILE_NO_UI;
- else
+ }
+ else {
G.fileflags |= G_FILE_NO_UI;
+ }
- if (RNA_boolean_get(op->ptr, "use_scripts"))
+ if (RNA_boolean_get(op->ptr, "use_scripts")) {
G.f |= G_FLAG_SCRIPT_AUTOEXEC;
- else
+ }
+ else {
G.f &= ~G_FLAG_SCRIPT_AUTOEXEC;
+ }
success = wm_file_read_opwrap(C, filepath, op->reports, !(G.f & G_FLAG_SCRIPT_AUTOEXEC));
@@ -1948,7 +1970,9 @@ static bool wm_open_mainfile_check(bContext *UNUSED(C), wmOperator *op)
/* get the dir */
lslash = (char *)BLI_last_slash(path);
- if (lslash) *(lslash + 1) = '\0';
+ if (lslash) {
+ *(lslash + 1) = '\0';
+ }
if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
if (BKE_autoexec_match(path) == true) {
@@ -2021,10 +2045,12 @@ static int wm_revert_mainfile_exec(bContext *C, wmOperator *op)
wm_open_init_use_scripts(op, false);
- if (RNA_boolean_get(op->ptr, "use_scripts"))
+ if (RNA_boolean_get(op->ptr, "use_scripts")) {
G.f |= G_FLAG_SCRIPT_AUTOEXEC;
- else
+ }
+ else {
G.f &= ~G_FLAG_SCRIPT_AUTOEXEC;
+ }
BLI_strncpy(filepath, BKE_main_blendfile_path(bmain), sizeof(filepath));
success = wm_file_read_opwrap(C, filepath, op->reports, !(G.f & G_FLAG_SCRIPT_AUTOEXEC));
@@ -2310,8 +2336,9 @@ static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *U
int ret;
/* cancel if no active window */
- if (CTX_wm_window(C) == NULL)
+ if (CTX_wm_window(C) == NULL) {
return OPERATOR_CANCELLED;
+ }
save_set_compress(op);
save_set_filepath(C, op);
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index 3b27d2199b6..56dfe4a327d 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -85,8 +85,9 @@ static bool wm_link_append_poll(bContext *C)
* but which totally confuses edit mode (i.e. it becoming not so obvious
* to leave from edit mode and invalid tools in toolbar might be displayed)
* so disable link/append when in edit mode (sergey) */
- if (CTX_data_edit_object(C))
+ if (CTX_data_edit_object(C)) {
return 0;
+ }
return 1;
}
@@ -117,16 +118,21 @@ static short wm_link_append_flag(wmOperator *op)
PropertyRNA *prop;
short flag = 0;
- if (RNA_boolean_get(op->ptr, "autoselect"))
+ if (RNA_boolean_get(op->ptr, "autoselect")) {
flag |= FILE_AUTOSELECT;
- if (RNA_boolean_get(op->ptr, "active_collection"))
+ }
+ if (RNA_boolean_get(op->ptr, "active_collection")) {
flag |= FILE_ACTIVE_COLLECTION;
- if ((prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop))
+ }
+ if ((prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop)) {
flag |= FILE_RELPATH;
- if (RNA_boolean_get(op->ptr, "link"))
+ }
+ if (RNA_boolean_get(op->ptr, "link")) {
flag |= FILE_LINK;
- if (RNA_boolean_get(op->ptr, "instance_collections"))
+ }
+ if (RNA_boolean_get(op->ptr, "instance_collections")) {
flag |= FILE_GROUP_INSTANCE;
+ }
return flag;
}
diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c
index 013ee029200..7e95f36b2db 100644
--- a/source/blender/windowmanager/intern/wm_gesture.c
+++ b/source/blender/windowmanager/intern/wm_gesture.c
@@ -98,8 +98,9 @@ void WM_gesture_end(bContext *C, wmGesture *gesture)
{
wmWindow *win = CTX_wm_window(C);
- if (win->tweak == gesture)
+ if (win->tweak == gesture) {
win->tweak = NULL;
+ }
BLI_remlink(&win->gesture, gesture);
MEM_freeN(gesture->customdata);
if (gesture->userdata && gesture->userdata_free) {
@@ -112,8 +113,9 @@ void WM_gestures_remove(bContext *C)
{
wmWindow *win = CTX_wm_window(C);
- while (win->gesture.first)
+ while (win->gesture.first) {
WM_gesture_end(C, win->gesture.first);
+ }
}
bool WM_gesture_is_modal_first(const wmGesture *gesture)
@@ -136,13 +138,27 @@ int wm_gesture_evaluate(wmGesture *gesture)
int theta = round_fl_to_int(4.0f * atan2f((float)dy, (float)dx) / (float)M_PI);
int val = EVT_GESTURE_W;
- if (theta == 0) val = EVT_GESTURE_E;
- else if (theta == 1) val = EVT_GESTURE_NE;
- else if (theta == 2) val = EVT_GESTURE_N;
- else if (theta == 3) val = EVT_GESTURE_NW;
- else if (theta == -1) val = EVT_GESTURE_SE;
- else if (theta == -2) val = EVT_GESTURE_S;
- else if (theta == -3) val = EVT_GESTURE_SW;
+ if (theta == 0) {
+ val = EVT_GESTURE_E;
+ }
+ else if (theta == 1) {
+ val = EVT_GESTURE_NE;
+ }
+ else if (theta == 2) {
+ val = EVT_GESTURE_N;
+ }
+ else if (theta == 3) {
+ val = EVT_GESTURE_NW;
+ }
+ else if (theta == -1) {
+ val = EVT_GESTURE_SE;
+ }
+ else if (theta == -2) {
+ val = EVT_GESTURE_S;
+ }
+ else if (theta == -3) {
+ val = EVT_GESTURE_SW;
+ }
#if 0
/* debug */
@@ -424,12 +440,14 @@ void wm_gesture_draw(wmWindow *win)
/* all in subwindow space */
wmViewport(&gt->winrct);
- if (gt->type == WM_GESTURE_RECT)
+ if (gt->type == WM_GESTURE_RECT) {
wm_gesture_draw_rect(gt);
+ }
// else if (gt->type == WM_GESTURE_TWEAK)
// wm_gesture_draw_line(gt);
- else if (gt->type == WM_GESTURE_CIRCLE)
+ else if (gt->type == WM_GESTURE_CIRCLE) {
wm_gesture_draw_circle(gt);
+ }
else if (gt->type == WM_GESTURE_CROSS_RECT) {
if (gt->is_active) {
wm_gesture_draw_rect(gt);
@@ -438,12 +456,15 @@ void wm_gesture_draw(wmWindow *win)
wm_gesture_draw_cross(win, gt);
}
}
- else if (gt->type == WM_GESTURE_LINES)
+ else if (gt->type == WM_GESTURE_LINES) {
wm_gesture_draw_lasso(gt, false);
- else if (gt->type == WM_GESTURE_LASSO)
+ }
+ else if (gt->type == WM_GESTURE_LASSO) {
wm_gesture_draw_lasso(gt, true);
- else if (gt->type == WM_GESTURE_STRAIGHTLINE)
+ }
+ else if (gt->type == WM_GESTURE_STRAIGHTLINE) {
wm_gesture_draw_line(gt);
+ }
}
}
@@ -451,6 +472,7 @@ void wm_gesture_tag_redraw(bContext *C)
{
bScreen *screen = CTX_wm_screen(C);
- if (screen)
+ if (screen) {
screen->do_draw_gesture = true;
+ }
}
diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c
index 030cf15adfb..98e17f77a17 100644
--- a/source/blender/windowmanager/intern/wm_gesture_ops.c
+++ b/source/blender/windowmanager/intern/wm_gesture_ops.c
@@ -136,9 +136,9 @@ static bool gesture_box_apply_rect(wmOperator *op)
wmGesture *gesture = op->customdata;
rcti *rect = gesture->customdata;
- if (rect->xmin == rect->xmax || rect->ymin == rect->ymax)
+ if (rect->xmin == rect->xmax || rect->ymin == rect->ymax) {
return 0;
-
+ }
/* operator arguments and storage. */
RNA_int_set(op->ptr, "xmin", min_ii(rect->xmin, rect->xmax));
@@ -353,11 +353,15 @@ int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
switch (event->val) {
case GESTURE_MODAL_CIRCLE_SIZE:
fac = 0.3f * (event->y - event->prevy);
- if (fac > 0)
+ if (fac > 0) {
rect->xmax += ceil(fac);
- else
+ }
+ else {
rect->xmax += floor(fac);
- if (rect->xmax < 1) rect->xmax = 1;
+ }
+ if (rect->xmax < 1) {
+ rect->xmax = 1;
+ }
is_circle_size = true;
break;
case GESTURE_MODAL_CIRCLE_ADD:
@@ -366,7 +370,9 @@ int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
break;
case GESTURE_MODAL_CIRCLE_SUB:
rect->xmax -= 2 + rect->xmax / 10;
- if (rect->xmax < 1) rect->xmax = 1;
+ if (rect->xmax < 1) {
+ rect->xmax = 1;
+ }
is_circle_size = true;
break;
case GESTURE_MODAL_SELECT:
@@ -478,12 +484,15 @@ static void gesture_tweak_modal(bContext *C, const wmEvent *event)
/* We want to get coord from start of drag, not from point where it becomes a tweak event, see T40549 */
tevent.x = rect->xmin + gesture->winrct.xmin;
tevent.y = rect->ymin + gesture->winrct.ymin;
- if (gesture->event_type == LEFTMOUSE)
+ if (gesture->event_type == LEFTMOUSE) {
tevent.type = EVT_TWEAK_L;
- else if (gesture->event_type == RIGHTMOUSE)
+ }
+ else if (gesture->event_type == RIGHTMOUSE) {
tevent.type = EVT_TWEAK_R;
- else
+ }
+ else {
tevent.type = EVT_TWEAK_M;
+ }
tevent.val = val;
/* mouse coords! */
@@ -535,8 +544,9 @@ void wm_tweakevent_test(bContext *C, const wmEvent *event, int action)
if ((action & WM_HANDLER_BREAK)) {
WM_gesture_end(C, win->tweak);
}
- else
+ else {
gesture_tweak_modal(C, event);
+ }
}
}
@@ -764,8 +774,9 @@ static bool gesture_straightline_apply(bContext *C, wmOperator *op)
wmGesture *gesture = op->customdata;
rcti *rect = gesture->customdata;
- if (rect->xmin == rect->xmax && rect->ymin == rect->ymax)
+ if (rect->xmin == rect->xmax && rect->ymin == rect->ymax) {
return 0;
+ }
/* operator arguments and storage. */
RNA_int_set(op->ptr, "xstart", rect->xmin);
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 136d4e00d91..f39cb7d7cb2 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -292,8 +292,9 @@ void WM_init(bContext *C, int argc, const char **argv)
(void)argv; /* unused */
#endif
- if (!G.background && !wm_start_with_console)
+ if (!G.background && !wm_start_with_console) {
GHOST_toggleConsole(3);
+ }
clear_matcopybuf();
ED_render_clear_mtex_copybuf();
@@ -362,8 +363,9 @@ static void free_openrecent(void)
{
struct RecentFile *recent;
- for (recent = G.recent_files.first; recent; recent = recent->next)
+ for (recent = G.recent_files.first; recent; recent = recent->next) {
MEM_freeN(recent->filepath);
+ }
BLI_freelistN(&(G.recent_files));
}
@@ -401,7 +403,7 @@ static int wm_exit_handler(bContext *C, const wmEvent *event, void *userdata)
}
/**
- * Cause a delayed WM_exit() call to avoid leaking memory when trying to exit from within operators.
+ * Cause a delayed #WM_exit() call to avoid leaking memory when trying to exit from within operators.
*/
void wm_exit_schedule_delayed(const bContext *C)
{
@@ -490,8 +492,10 @@ void WM_exit_ext(bContext *C, const bool do_python)
ED_preview_free_dbase(); /* frees a Main dbase, before BKE_blender_free! */
- if (C && wm)
- wm_free_reports(C); /* before BKE_blender_free! - since the ListBases get freed there */
+ if (C && wm) {
+ /* Before BKE_blender_free! - since the ListBases get freed there. */
+ wm_free_reports(C);
+ }
BKE_sequencer_free_clipboard(); /* sequencer.c */
BKE_tracking_clipboard_free();
diff --git a/source/blender/windowmanager/intern/wm_jobs.c b/source/blender/windowmanager/intern/wm_jobs.c
index 3d70fd03845..a1ebc29802f 100644
--- a/source/blender/windowmanager/intern/wm_jobs.c
+++ b/source/blender/windowmanager/intern/wm_jobs.c
@@ -146,19 +146,25 @@ static wmJob *wm_job_find(wmWindowManager *wm, void *owner, const int job_type)
wmJob *wm_job;
if (owner && job_type) {
- for (wm_job = wm->jobs.first; wm_job; wm_job = wm_job->next)
- if (wm_job->owner == owner && wm_job->job_type == job_type)
+ for (wm_job = wm->jobs.first; wm_job; wm_job = wm_job->next) {
+ if (wm_job->owner == owner && wm_job->job_type == job_type) {
return wm_job;
+ }
+ }
}
else if (owner) {
- for (wm_job = wm->jobs.first; wm_job; wm_job = wm_job->next)
- if (wm_job->owner == owner)
+ for (wm_job = wm->jobs.first; wm_job; wm_job = wm_job->next) {
+ if (wm_job->owner == owner) {
return wm_job;
+ }
+ }
}
else if (job_type) {
- for (wm_job = wm->jobs.first; wm_job; wm_job = wm_job->next)
- if (wm_job->job_type == job_type)
+ for (wm_job = wm->jobs.first; wm_job; wm_job = wm_job->next) {
+ if (wm_job->job_type == job_type) {
return wm_job;
+ }
+ }
}
return NULL;
@@ -220,8 +226,9 @@ float WM_jobs_progress(wmWindowManager *wm, void *owner)
{
wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
- if (wm_job && wm_job->flag & WM_JOB_PROGRESS)
+ if (wm_job && wm_job->flag & WM_JOB_PROGRESS) {
return wm_job->progress;
+ }
return 0.0;
}
@@ -246,14 +253,16 @@ static void wm_jobs_update_progress_bars(wmWindowManager *wm)
wmWindow *win;
float progress = total_progress / (float)jobs_progress;
- for (win = wm->windows.first; win; win = win->next)
+ for (win = wm->windows.first; win; win = win->next) {
WM_progress_set(win, progress);
+ }
}
else {
wmWindow *win;
- for (win = wm->windows.first; win; win = win->next)
+ for (win = wm->windows.first; win; win = win->next) {
WM_progress_clear(win);
+ }
}
}
@@ -263,8 +272,9 @@ double WM_jobs_starttime(wmWindowManager *wm, void *owner)
{
wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
- if (wm_job && wm_job->flag & WM_JOB_PROGRESS)
+ if (wm_job && wm_job->flag & WM_JOB_PROGRESS) {
return wm_job->start_time;
+ }
return 0;
}
@@ -273,8 +283,9 @@ char *WM_jobs_name(wmWindowManager *wm, void *owner)
{
wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
- if (wm_job)
+ if (wm_job) {
return wm_job->name;
+ }
return NULL;
}
@@ -283,8 +294,9 @@ void *WM_jobs_customdata(wmWindowManager *wm, void *owner)
{
wmJob *wm_job = wm_job_find(wm, owner, WM_JOB_TYPE_ANY);
- if (wm_job)
+ if (wm_job) {
return WM_jobs_customdata_get(wm_job);
+ }
return NULL;
}
@@ -293,8 +305,9 @@ void *WM_jobs_customdata_from_type(wmWindowManager *wm, int job_type)
{
wmJob *wm_job = wm_job_find(wm, NULL, job_type);
- if (wm_job)
+ if (wm_job) {
return WM_jobs_customdata_get(wm_job);
+ }
return NULL;
}
@@ -323,8 +336,9 @@ void *WM_jobs_customdata_get(wmJob *wm_job)
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void (*free)(void *))
{
/* pending job? just free */
- if (wm_job->customdata)
+ if (wm_job->customdata) {
wm_job->free(wm_job->customdata);
+ }
wm_job->customdata = customdata;
wm_job->free = free;
@@ -385,14 +399,18 @@ static void wm_jobs_test_suspend_stop(wmWindowManager *wm, wmJob *test)
}
/* if new job is not render, then check for same startjob */
- if (0 == (test->flag & WM_JOB_EXCL_RENDER))
- if (wm_job->startjob != test->startjob)
+ if (0 == (test->flag & WM_JOB_EXCL_RENDER)) {
+ if (wm_job->startjob != test->startjob) {
continue;
+ }
+ }
/* if new job is render, any render job should be stopped */
- if (test->flag & WM_JOB_EXCL_RENDER)
- if (0 == (wm_job->flag & WM_JOB_EXCL_RENDER))
+ if (test->flag & WM_JOB_EXCL_RENDER) {
+ if (0 == (wm_job->flag & WM_JOB_EXCL_RENDER)) {
continue;
+ }
+ }
suspend = true;
@@ -434,8 +452,9 @@ void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
wm_job->customdata = NULL;
wm_job->running = true;
- if (wm_job->initjob)
+ if (wm_job->initjob) {
wm_job->initjob(wm_job->run_customdata);
+ }
wm_job->stop = false;
wm_job->ready = false;
@@ -448,8 +467,9 @@ void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
}
/* restarted job has timer already */
- if (wm_job->wt == NULL)
+ if (wm_job->wt == NULL) {
wm_job->wt = WM_event_add_timer(wm, wm_job->win, TIMERJOBS, wm_job->timestep);
+ }
wm_job->start_time = PIL_check_seconds_timer();
}
@@ -480,16 +500,20 @@ static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *wm_job)
BLI_threadpool_end(&wm_job->threads);
WM_job_main_thread_lock_acquire(wm_job);
- if (wm_job->endjob)
+ if (wm_job->endjob) {
wm_job->endjob(wm_job->run_customdata);
+ }
}
- if (wm_job->wt)
+ if (wm_job->wt) {
WM_event_remove_timer(wm, wm_job->win, wm_job->wt);
- if (wm_job->customdata)
+ }
+ if (wm_job->customdata) {
wm_job->free(wm_job->customdata);
- if (wm_job->run_customdata)
+ }
+ if (wm_job->run_customdata) {
wm_job->run_free(wm_job->run_customdata);
+ }
/* remove wm_job */
wm_job_free(wm, wm_job);
@@ -505,8 +529,9 @@ void WM_jobs_kill_all(wmWindowManager *wm)
{
wmJob *wm_job;
- while ((wm_job = wm->jobs.first))
+ while ((wm_job = wm->jobs.first)) {
wm_jobs_kill_job(wm, wm_job);
+ }
}
@@ -518,8 +543,9 @@ void WM_jobs_kill_all_except(wmWindowManager *wm, void *owner)
for (wm_job = wm->jobs.first; wm_job; wm_job = next_job) {
next_job = wm_job->next;
- if (wm_job->owner != owner)
+ if (wm_job->owner != owner) {
wm_jobs_kill_job(wm, wm_job);
+ }
}
}
@@ -531,9 +557,11 @@ void WM_jobs_kill_type(struct wmWindowManager *wm, void *owner, int job_type)
for (wm_job = wm->jobs.first; wm_job; wm_job = next_job) {
next_job = wm_job->next;
- if (!owner || wm_job->owner == owner)
- if (job_type == WM_JOB_TYPE_ANY || wm_job->job_type == job_type)
+ if (!owner || wm_job->owner == owner) {
+ if (job_type == WM_JOB_TYPE_ANY || wm_job->job_type == job_type) {
wm_jobs_kill_job(wm, wm_job);
+ }
+ }
}
}
@@ -601,19 +629,23 @@ void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt)
/* always call note and update when ready */
if (wm_job->do_update || wm_job->ready) {
- if (wm_job->update)
+ if (wm_job->update) {
wm_job->update(wm_job->run_customdata);
- if (wm_job->note)
+ }
+ if (wm_job->note) {
WM_event_add_notifier(C, wm_job->note, NULL);
+ }
- if (wm_job->flag & WM_JOB_PROGRESS)
+ if (wm_job->flag & WM_JOB_PROGRESS) {
WM_event_add_notifier(C, NC_WM | ND_JOB, NULL);
+ }
wm_job->do_update = false;
}
if (wm_job->ready) {
- if (wm_job->endjob)
+ if (wm_job->endjob) {
wm_job->endjob(wm_job->run_customdata);
+ }
/* free own data */
wm_job->run_free(wm_job->run_customdata);
@@ -634,8 +666,9 @@ void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt)
BLI_threadpool_end(&wm_job->threads);
WM_job_main_thread_lock_acquire(wm_job);
- if (wm_job->endnote)
+ if (wm_job->endnote) {
WM_event_add_notifier(C, wm_job->endnote, NULL);
+ }
WM_event_add_notifier(C, NC_WM | ND_JOB, NULL);
diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c
index 16e4913a206..ba9accb5dee 100644
--- a/source/blender/windowmanager/intern/wm_keymap.c
+++ b/source/blender/windowmanager/intern/wm_keymap.c
@@ -152,10 +152,12 @@ static void wm_keyconfig_properties_update_ot(ListBase *km_lb)
}
for (kmdi = km->diff_items.first; kmdi; kmdi = kmdi->next) {
- if (kmdi->add_item)
+ if (kmdi->add_item) {
wm_keymap_item_properties_update_ot(kmdi->add_item);
- if (kmdi->remove_item)
+ }
+ if (kmdi->remove_item) {
wm_keymap_item_properties_update_ot(kmdi->remove_item);
+ }
}
}
}
@@ -232,10 +234,12 @@ static wmKeyMapDiffItem *wm_keymap_diff_item_copy(wmKeyMapDiffItem *kmdi)
wmKeyMapDiffItem *kmdin = MEM_dupallocN(kmdi);
kmdin->next = kmdin->prev = NULL;
- if (kmdi->add_item)
+ if (kmdi->add_item) {
kmdin->add_item = wm_keymap_item_copy(kmdi->add_item);
- if (kmdi->remove_item)
+ }
+ if (kmdi->remove_item) {
kmdin->remove_item = wm_keymap_item_copy(kmdi->remove_item);
+ }
return kmdin;
}
@@ -331,8 +335,9 @@ static wmKeyConfig *WM_keyconfig_active(wmWindowManager *wm)
/* first try from preset */
keyconf = BLI_findstring(&wm->keyconfigs, U.keyconfigstr, offsetof(wmKeyConfig, idname));
- if (keyconf)
+ if (keyconf) {
return keyconf;
+ }
/* otherwise use default */
return wm->defaultconf;
@@ -401,11 +406,13 @@ void WM_keymap_clear(wmKeyMap *keymap)
wmKeyMapItem *kmi;
wmKeyMapDiffItem *kmdi;
- for (kmdi = keymap->diff_items.first; kmdi; kmdi = kmdi->next)
+ for (kmdi = keymap->diff_items.first; kmdi; kmdi = kmdi->next) {
wm_keymap_diff_item_free(kmdi);
+ }
- for (kmi = keymap->items.first; kmi; kmi = kmi->next)
+ for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
wm_keymap_item_free(kmi);
+ }
BLI_freelistN(&keymap->diff_items);
BLI_freelistN(&keymap->items);
@@ -437,6 +444,12 @@ bool WM_keymap_poll(bContext *C, wmKeyMap *keymap)
}
}
+ if (UNLIKELY(BLI_listbase_is_empty(&keymap->items))) {
+ /* Empty key-maps may be missing more there may be a typo in the name.
+ * Warn early to avoid loosing time investigating each case. */
+ CLOG_WARN(WM_LOG_KEYMAPS, "empty keymap '%s'", keymap->idname);
+ }
+
if (keymap->poll != NULL) {
return keymap->poll(C);
}
@@ -476,9 +489,11 @@ wmKeyMapItem *WM_keymap_verify_item(wmKeyMap *keymap, const char *idname, int ty
{
wmKeyMapItem *kmi;
- for (kmi = keymap->items.first; kmi; kmi = kmi->next)
- if (STREQLEN(kmi->idname, idname, OP_MAX_TYPENAME))
+ for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
+ if (STREQLEN(kmi->idname, idname, OP_MAX_TYPENAME)) {
break;
+ }
+ }
if (kmi == NULL) {
kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry");
@@ -563,9 +578,11 @@ static wmKeyMapItem *wm_keymap_find_item_equals(wmKeyMap *km, wmKeyMapItem *need
{
wmKeyMapItem *kmi;
- for (kmi = km->items.first; kmi; kmi = kmi->next)
- if (wm_keymap_item_equals(kmi, needle))
+ for (kmi = km->items.first; kmi; kmi = kmi->next) {
+ if (wm_keymap_item_equals(kmi, needle)) {
return kmi;
+ }
+ }
return NULL;
}
@@ -574,9 +591,11 @@ static wmKeyMapItem *wm_keymap_find_item_equals_result(wmKeyMap *km, wmKeyMapIte
{
wmKeyMapItem *kmi;
- for (kmi = km->items.first; kmi; kmi = kmi->next)
- if (wm_keymap_item_equals_result(kmi, needle))
+ for (kmi = km->items.first; kmi; kmi = kmi->next) {
+ if (wm_keymap_item_equals_result(kmi, needle)) {
return kmi;
+ }
+ }
return NULL;
}
@@ -607,8 +626,9 @@ static void wm_keymap_diff(wmKeyMap *diff_km, wmKeyMap *from_km, wmKeyMap *to_km
if (to_kmi) {
orig_kmi = WM_keymap_item_find_id(orig_km, kmi->id);
- if (!orig_kmi && addon_km)
+ if (!orig_kmi && addon_km) {
orig_kmi = wm_keymap_find_item_equals(addon_km, kmi);
+ }
if (orig_kmi) {
orig_kmi->flag &= ~KMI_EXPANDED;
@@ -637,8 +657,9 @@ static void wm_keymap_patch(wmKeyMap *km, wmKeyMap *diff_km)
kmi_remove = NULL;
if (kmdi->remove_item) {
kmi_remove = wm_keymap_find_item_equals(km, kmdi->remove_item);
- if (!kmi_remove)
+ if (!kmi_remove) {
kmi_remove = wm_keymap_find_item_equals_result(km, kmdi->remove_item);
+ }
}
/* add item */
@@ -711,32 +732,39 @@ static wmKeyMap *wm_keymap_patch_update(ListBase *lb, wmKeyMap *defaultmap, wmKe
/* try to find corresponding id's for items */
for (kmi = km->items.first; kmi; kmi = kmi->next) {
orig_kmi = wm_keymap_find_item_equals(defaultmap, kmi);
- if (!orig_kmi)
+ if (!orig_kmi) {
orig_kmi = wm_keymap_find_item_equals_result(defaultmap, kmi);
+ }
- if (orig_kmi)
+ if (orig_kmi) {
kmi->id = orig_kmi->id;
- else
+ }
+ else {
kmi->id = -(km->kmi_id++);
+ }
}
km->flag |= KEYMAP_UPDATE; /* update again to create diff */
}
- else
+ else {
km = wm_keymap_copy(defaultmap);
+ }
/* add addon keymap items */
- if (addonmap)
+ if (addonmap) {
wm_keymap_addon_add(km, addonmap);
+ }
/* tag as being user edited */
- if (usermap)
+ if (usermap) {
km->flag |= KEYMAP_USER_MODIFIED;
+ }
km->flag |= KEYMAP_USER | expanded;
/* apply user changes of diff keymap */
- if (usermap && (usermap->flag & KEYMAP_DIFF))
+ if (usermap && (usermap->flag & KEYMAP_DIFF)) {
wm_keymap_patch(km, usermap);
+ }
/* add to list */
BLI_addtail(lb, km);
@@ -766,8 +794,9 @@ static void wm_keymap_diff_update(ListBase *lb, wmKeyMap *defaultmap, wmKeyMap *
/* create diff keymap */
diffmap = wm_keymap_new(km->idname, km->spaceid, km->regionid);
diffmap->flag |= KEYMAP_DIFF;
- if (defaultmap->flag & KEYMAP_MODAL)
+ if (defaultmap->flag & KEYMAP_MODAL) {
diffmap->flag |= KEYMAP_MODAL;
+ }
wm_keymap_diff(diffmap, defaultmap, km, origmap, addonmap);
/* add to list if not empty */
@@ -796,10 +825,13 @@ wmKeyMap *WM_keymap_list_find(ListBase *lb, const char *idname, int spaceid, int
{
wmKeyMap *km;
- for (km = lb->first; km; km = km->next)
- if (km->spaceid == spaceid && km->regionid == regionid)
- if (STREQLEN(idname, km->idname, KMAP_MAX_NAME))
+ for (km = lb->first; km; km = km->next) {
+ if (km->spaceid == spaceid && km->regionid == regionid) {
+ if (STREQLEN(idname, km->idname, KMAP_MAX_NAME)) {
return km;
+ }
+ }
+ }
return NULL;
}
@@ -808,10 +840,13 @@ wmKeyMap *WM_keymap_list_find_spaceid_or_empty(ListBase *lb, const char *idname,
{
wmKeyMap *km;
- for (km = lb->first; km; km = km->next)
- if (ELEM(km->spaceid, spaceid, SPACE_EMPTY) && km->regionid == regionid)
- if (STREQLEN(idname, km->idname, KMAP_MAX_NAME))
+ for (km = lb->first; km; km = km->next) {
+ if (ELEM(km->spaceid, spaceid, SPACE_EMPTY) && km->regionid == regionid) {
+ if (STREQLEN(idname, km->idname, KMAP_MAX_NAME)) {
return km;
+ }
+ }
+ }
return NULL;
}
@@ -876,10 +911,13 @@ wmKeyMap *WM_modalkeymap_get(wmKeyConfig *keyconf, const char *idname)
{
wmKeyMap *km;
- for (km = keyconf->keymaps.first; km; km = km->next)
- if (km->flag & KEYMAP_MODAL)
- if (STREQLEN(idname, km->idname, KMAP_MAX_NAME))
+ for (km = keyconf->keymaps.first; km; km = km->next) {
+ if (km->flag & KEYMAP_MODAL) {
+ if (STREQLEN(idname, km->idname, KMAP_MAX_NAME)) {
break;
+ }
+ }
+ }
return km;
}
@@ -966,8 +1004,9 @@ static void wm_user_modal_keymap_set_items(wmWindowManager *wm, wmKeyMap *km)
defaultkm = WM_keymap_list_find(&wm->defaultconf->keymaps, km->idname, 0, 0);
- if (!defaultkm)
+ if (!defaultkm) {
return;
+ }
km->modal_items = defaultkm->modal_items;
km->poll = defaultkm->poll;
@@ -976,8 +1015,9 @@ static void wm_user_modal_keymap_set_items(wmWindowManager *wm, wmKeyMap *km)
if (km->modal_items) {
for (kmi = km->items.first; kmi; kmi = kmi->next) {
if (kmi->propvalue_str[0]) {
- if (RNA_enum_value_from_id(km->modal_items, kmi->propvalue_str, &propvalue))
+ if (RNA_enum_value_from_id(km->modal_items, kmi->propvalue_str, &propvalue)) {
kmi->propvalue = propvalue;
+ }
kmi->propvalue_str[0] = '\0';
}
}
@@ -1245,7 +1285,9 @@ static wmKeyMapItem *wm_keymap_item_find_handlers(
}
}
/* ensure un-initialized keymap is never used */
- if (r_keymap) *r_keymap = NULL;
+ if (r_keymap) {
+ *r_keymap = NULL;
+ }
return NULL;
}
@@ -1270,36 +1312,44 @@ static wmKeyMapItem *wm_keymap_item_find_props(
}
}
- if (sa && found == NULL)
+ if (sa && found == NULL) {
found = wm_keymap_item_find_handlers(C, &sa->handlers, opname, opcontext, properties, is_strict, params, r_keymap);
+ }
if (found == NULL) {
if (ELEM(opcontext, WM_OP_EXEC_REGION_WIN, WM_OP_INVOKE_REGION_WIN)) {
if (sa) {
- if (!(ar && ar->regiontype == RGN_TYPE_WINDOW))
+ if (!(ar && ar->regiontype == RGN_TYPE_WINDOW)) {
ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
+ }
- if (ar)
+ if (ar) {
found = wm_keymap_item_find_handlers(C, &ar->handlers, opname, opcontext, properties, is_strict, params, r_keymap);
+ }
}
}
else if (ELEM(opcontext, WM_OP_EXEC_REGION_CHANNELS, WM_OP_INVOKE_REGION_CHANNELS)) {
- if (!(ar && ar->regiontype == RGN_TYPE_CHANNELS))
+ if (!(ar && ar->regiontype == RGN_TYPE_CHANNELS)) {
ar = BKE_area_find_region_type(sa, RGN_TYPE_CHANNELS);
+ }
- if (ar)
+ if (ar) {
found = wm_keymap_item_find_handlers(C, &ar->handlers, opname, opcontext, properties, is_strict, params, r_keymap);
+ }
}
else if (ELEM(opcontext, WM_OP_EXEC_REGION_PREVIEW, WM_OP_INVOKE_REGION_PREVIEW)) {
- if (!(ar && ar->regiontype == RGN_TYPE_PREVIEW))
+ if (!(ar && ar->regiontype == RGN_TYPE_PREVIEW)) {
ar = BKE_area_find_region_type(sa, RGN_TYPE_PREVIEW);
+ }
- if (ar)
+ if (ar) {
found = wm_keymap_item_find_handlers(C, &ar->handlers, opname, opcontext, properties, is_strict, params, r_keymap);
+ }
}
else {
- if (ar)
+ if (ar) {
found = wm_keymap_item_find_handlers(C, &ar->handlers, opname, opcontext, properties, is_strict, params, r_keymap);
+ }
}
}
@@ -1466,40 +1516,50 @@ bool WM_keymap_item_compare(wmKeyMapItem *k1, wmKeyMapItem *k2)
{
int k1type, k2type;
- if (k1->flag & KMI_INACTIVE || k2->flag & KMI_INACTIVE)
+ if (k1->flag & KMI_INACTIVE || k2->flag & KMI_INACTIVE) {
return 0;
+ }
/* take event mapping into account */
k1type = WM_userdef_event_map(k1->type);
k2type = WM_userdef_event_map(k2->type);
- if (k1type != KM_ANY && k2type != KM_ANY && k1type != k2type)
+ if (k1type != KM_ANY && k2type != KM_ANY && k1type != k2type) {
return 0;
+ }
if (k1->val != KM_ANY && k2->val != KM_ANY) {
/* take click, press, release conflict into account */
- if (k1->val == KM_CLICK && ELEM(k2->val, KM_PRESS, KM_RELEASE, KM_CLICK) == 0)
+ if (k1->val == KM_CLICK && ELEM(k2->val, KM_PRESS, KM_RELEASE, KM_CLICK) == 0) {
return 0;
- if (k2->val == KM_CLICK && ELEM(k1->val, KM_PRESS, KM_RELEASE, KM_CLICK) == 0)
+ }
+ if (k2->val == KM_CLICK && ELEM(k1->val, KM_PRESS, KM_RELEASE, KM_CLICK) == 0) {
return 0;
- if (k1->val != k2->val)
+ }
+ if (k1->val != k2->val) {
return 0;
+ }
}
- if (k1->shift != KM_ANY && k2->shift != KM_ANY && k1->shift != k2->shift)
+ if (k1->shift != KM_ANY && k2->shift != KM_ANY && k1->shift != k2->shift) {
return 0;
+ }
- if (k1->ctrl != KM_ANY && k2->ctrl != KM_ANY && k1->ctrl != k2->ctrl)
+ if (k1->ctrl != KM_ANY && k2->ctrl != KM_ANY && k1->ctrl != k2->ctrl) {
return 0;
+ }
- if (k1->alt != KM_ANY && k2->alt != KM_ANY && k1->alt != k2->alt)
+ if (k1->alt != KM_ANY && k2->alt != KM_ANY && k1->alt != k2->alt) {
return 0;
+ }
- if (k1->oskey != KM_ANY && k2->oskey != KM_ANY && k1->oskey != k2->oskey)
+ if (k1->oskey != KM_ANY && k2->oskey != KM_ANY && k1->oskey != k2->oskey) {
return 0;
+ }
- if (k1->keymodifier != k2->keymodifier)
+ if (k1->keymodifier != k2->keymodifier) {
return 0;
+ }
return 1;
}
@@ -1524,10 +1584,12 @@ void WM_keyconfig_update_tag(wmKeyMap *km, wmKeyMapItem *kmi)
/* quick tag to do delayed keymap updates */
wm_keymap_update_flag |= WM_KEYMAP_UPDATE_RECONFIGURE;
- if (km)
+ if (km) {
km->flag |= KEYMAP_UPDATE;
- if (kmi)
+ }
+ if (kmi) {
kmi->flag |= KMI_UPDATE;
+ }
}
void WM_keyconfig_update_operatortype(void)
@@ -1557,8 +1619,9 @@ static wmKeyMap *wm_keymap_preset(wmWindowManager *wm, wmKeyMap *km)
wmKeyMap *keymap;
keymap = WM_keymap_list_find(&keyconf->keymaps, km->idname, km->spaceid, km->regionid);
- if (!keymap && wm->defaultconf)
+ if (!keymap && wm->defaultconf) {
keymap = WM_keymap_list_find(&wm->defaultconf->keymaps, km->idname, km->spaceid, km->regionid);
+ }
return keymap;
}
@@ -1570,11 +1633,13 @@ void WM_keyconfig_update(wmWindowManager *wm)
wmKeyMapDiffItem *kmdi;
bool compat_update = false;
- if (G.background)
+ if (G.background) {
return;
+ }
- if (wm_keymap_update_flag == 0)
+ if (wm_keymap_update_flag == 0) {
return;
+ }
if (wm_keymap_update_flag & WM_KEYMAP_UPDATE_OPERATORTYPE) {
/* an operatortype has been removed, this wont happen often
@@ -1603,22 +1668,25 @@ void WM_keyconfig_update(wmWindowManager *wm)
}
- if (wm_keymap_update_flag == 0)
+ if (wm_keymap_update_flag == 0) {
return;
-
+ }
/* update operator properties for non-modal user keymaps */
for (km = U.user_keymaps.first; km; km = km->next) {
if ((km->flag & KEYMAP_MODAL) == 0) {
for (kmdi = km->diff_items.first; kmdi; kmdi = kmdi->next) {
- if (kmdi->add_item)
+ if (kmdi->add_item) {
wm_keymap_item_properties_set(kmdi->add_item);
- if (kmdi->remove_item)
+ }
+ if (kmdi->remove_item) {
wm_keymap_item_properties_set(kmdi->remove_item);
+ }
}
- for (kmi = km->items.first; kmi; kmi = kmi->next)
+ for (kmi = km->items.first; kmi; kmi = kmi->next) {
wm_keymap_item_properties_set(kmi);
+ }
}
}
@@ -1631,8 +1699,9 @@ void WM_keyconfig_update(wmWindowManager *wm)
addonmap = WM_keymap_list_find(&wm->addonconf->keymaps, km->idname, km->spaceid, km->regionid);
/* diff */
- if (defaultmap)
+ if (defaultmap) {
wm_keymap_diff_update(&U.user_keymaps, defaultmap, addonmap, km);
+ }
}
}
@@ -1678,14 +1747,16 @@ wmKeyMap *WM_keymap_active(wmWindowManager *wm, wmKeyMap *keymap)
{
wmKeyMap *km;
- if (!keymap)
+ if (!keymap) {
return NULL;
+ }
/* first user defined keymaps */
km = WM_keymap_list_find(&wm->userconf->keymaps, keymap->idname, keymap->spaceid, keymap->regionid);
- if (km)
+ if (km) {
return km;
+ }
return keymap;
}
@@ -1699,8 +1770,9 @@ void WM_keymap_restore_item_to_default(bContext *C, wmKeyMap *keymap, wmKeyMapIt
wmKeyMap *defaultmap, *addonmap;
wmKeyMapItem *orig;
- if (!keymap)
+ if (!keymap) {
return;
+ }
/* construct default keymap from preset + addons */
defaultmap = wm_keymap_preset(wm, keymap);
diff --git a/source/blender/windowmanager/intern/wm_keymap_utils.c b/source/blender/windowmanager/intern/wm_keymap_utils.c
index 1c3e09dac29..7a5c0d4d2b8 100644
--- a/source/blender/windowmanager/intern/wm_keymap_utils.c
+++ b/source/blender/windowmanager/intern/wm_keymap_utils.c
@@ -207,10 +207,12 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
}
else if (STRPREFIX(opname, "OBJECT_OT")) {
/* exception, this needs to work outside object mode too */
- if (STRPREFIX(opname, "OBJECT_OT_mode_set"))
+ if (STRPREFIX(opname, "OBJECT_OT_mode_set")) {
km = WM_keymap_find_all(C, "Object Non-modal", 0, 0);
- else
+ }
+ else {
km = WM_keymap_find_all(C, "Object Mode", 0, 0);
+ }
}
/* Object mode related */
else if (STRPREFIX(opname, "GROUP_OT") ||
diff --git a/source/blender/windowmanager/intern/wm_menu_type.c b/source/blender/windowmanager/intern/wm_menu_type.c
index 76bd36885fd..f466ac44939 100644
--- a/source/blender/windowmanager/intern/wm_menu_type.c
+++ b/source/blender/windowmanager/intern/wm_menu_type.c
@@ -45,12 +45,14 @@ MenuType *WM_menutype_find(const char *idname, bool quiet)
if (idname[0]) {
mt = BLI_ghash_lookup(menutypes_hash, idname);
- if (mt)
+ if (mt) {
return mt;
+ }
}
- if (!quiet)
+ if (!quiet) {
printf("search for unknown menutype %s\n", idname);
+ }
return NULL;
}
diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c
index 820123621f7..0e4d8a7bf71 100644
--- a/source/blender/windowmanager/intern/wm_operator_props.c
+++ b/source/blender/windowmanager/intern/wm_operator_props.c
@@ -64,17 +64,21 @@ void WM_operator_properties_filesel(
{0, NULL, 0, NULL, NULL},
};
- if (flag & WM_FILESEL_FILEPATH)
+ if (flag & WM_FILESEL_FILEPATH) {
RNA_def_string_file_path(ot->srna, "filepath", NULL, FILE_MAX, "File Path", "Path to file");
+ }
- if (flag & WM_FILESEL_DIRECTORY)
+ if (flag & WM_FILESEL_DIRECTORY) {
RNA_def_string_dir_path(ot->srna, "directory", NULL, FILE_MAX, "Directory", "Directory of the file");
+ }
- if (flag & WM_FILESEL_FILENAME)
+ if (flag & WM_FILESEL_FILENAME) {
RNA_def_string_file_name(ot->srna, "filename", NULL, FILE_MAX, "File Name", "Name of the file");
+ }
- if (flag & WM_FILESEL_FILES)
+ if (flag & WM_FILESEL_FILES) {
RNA_def_collection_runtime(ot->srna, "files", &RNA_OperatorFileListElement, "Files", "");
+ }
if (action == FILE_SAVE) {
/* note, this is only used to check if we should highlight the filename area red when the
@@ -116,8 +120,9 @@ void WM_operator_properties_filesel(
FILE_LOADLIB, FILE_SPECIAL);
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
- if (flag & WM_FILESEL_RELPATH)
+ if (flag & WM_FILESEL_RELPATH) {
RNA_def_boolean(ot->srna, "relative_path", true, "Relative Path", "Select the file relative to the blend file");
+ }
if ((filter & FILE_TYPE_IMAGE) || (filter & FILE_TYPE_MOVIE)) {
prop = RNA_def_boolean(ot->srna, "show_multiview", 0, "Enable Multi-View", "");
diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c
index 76586ee2a0b..606fb822e67 100644
--- a/source/blender/windowmanager/intern/wm_operator_type.c
+++ b/source/blender/windowmanager/intern/wm_operator_type.c
@@ -158,8 +158,9 @@ void WM_operatortype_remove_ptr(wmOperatorType *ot)
MEM_freeN(ot->last_properties);
}
- if (ot->macro.first)
+ if (ot->macro.first) {
wm_operatortype_free_macro(ot);
+ }
BLI_ghash_remove(global_ops_hash, ot->idname, NULL, NULL);
@@ -172,8 +173,9 @@ bool WM_operatortype_remove(const char *idname)
{
wmOperatorType *ot = WM_operatortype_find(idname, 0);
- if (ot == NULL)
+ if (ot == NULL) {
return false;
+ }
WM_operatortype_remove_ptr(ot);
@@ -194,11 +196,14 @@ static void operatortype_ghash_free_cb(wmOperatorType *ot)
MEM_freeN(ot->last_properties);
}
- if (ot->macro.first)
+ if (ot->macro.first) {
wm_operatortype_free_macro(ot);
+ }
- if (ot->ext.srna) /* python operator, allocs own string */
+ if (ot->ext.srna) {
+ /* python operator, allocs own string */
MEM_freeN((void *)ot->idname);
+ }
MEM_freeN(ot);
}
@@ -354,10 +359,12 @@ static int wm_macro_invoke_internal(bContext *C, wmOperator *op, const wmEvent *
/* start from operator received as argument */
for (; opm; opm = opm->next) {
- if (opm->type->invoke)
+ if (opm->type->invoke) {
retval = opm->type->invoke(C, opm, event);
- else if (opm->type->exec)
+ }
+ else if (opm->type->exec) {
retval = opm->type->exec(C, opm);
+ }
OPERATOR_RETVAL_CHECK(retval);
@@ -480,8 +487,10 @@ wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *nam
ot->cancel = wm_macro_cancel;
ot->poll = NULL;
- if (!ot->description) /* XXX All ops should have a description but for now allow them not to. */
+ if (!ot->description) {
+ /* XXX All ops should have a description but for now allow them not to. */
ot->description = UNDOCUMENTED_OPERATOR_TIP;
+ }
RNA_def_struct_ui_text(ot->srna, ot->name, ot->description);
RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname);
@@ -509,8 +518,9 @@ void WM_operatortype_append_macro_ptr(void (*opfunc)(wmOperatorType *, void *),
ot->cancel = wm_macro_cancel;
ot->poll = NULL;
- if (!ot->description)
+ if (!ot->description) {
ot->description = UNDOCUMENTED_OPERATOR_TIP;
+ }
/* Set the default i18n context now, so that opfunc can redefine it if needed! */
RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 6e5f83eba47..1411b8af1fe 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -157,8 +157,9 @@ void WM_operator_bl_idname(char *to, const char *from)
BLI_strncpy(to, from, OP_MAX_TYPENAME);
}
}
- else
+ else {
to[0] = 0;
+ }
}
/**
@@ -550,10 +551,12 @@ void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
{
wmOperatorType *ot = WM_operatortype_find(opstring, false);
- if (ot)
+ if (ot) {
WM_operator_properties_create_ptr(ptr, ot);
- else
+ }
+ else {
RNA_pointer_create(NULL, &RNA_OperatorProperties, NULL, ptr);
+ }
}
/* similar to the function above except its uses ID properties
@@ -580,10 +583,12 @@ void WM_operator_properties_sanitize(PointerRNA *ptr, const bool no_context)
{
switch (RNA_property_type(prop)) {
case PROP_ENUM:
- if (no_context)
+ if (no_context) {
RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
- else
+ }
+ else {
RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT);
+ }
break;
case PROP_POINTER:
{
@@ -834,10 +839,12 @@ int WM_operator_confirm_message_ex(bContext *C, wmOperator *op,
uiLayout *layout;
IDProperty *properties = op->ptr->data;
- if (properties && properties->len)
+ if (properties && properties->len) {
properties = IDP_CopyProperty(op->ptr->data);
- else
+ }
+ else {
properties = NULL;
+ }
pup = UI_popup_menu_begin(C, title, icon);
layout = UI_popup_menu_layout(pup);
@@ -900,7 +907,9 @@ bool WM_operator_filesel_ensure_ext_imtype(wmOperator *op, const struct ImageFor
/* op->poll */
bool WM_operator_winactive(bContext *C)
{
- if (CTX_wm_window(C) == NULL) return 0;
+ if (CTX_wm_window(C) == NULL) {
+ return 0;
+ }
return 1;
}
@@ -919,9 +928,11 @@ wmOperator *WM_operator_last_redo(const bContext *C)
wmOperator *op;
/* only for operators that are registered and did an undo push */
- for (op = wm->operators.last; op; op = op->prev)
- if ((op->type->flag & OPTYPE_REGISTER) && (op->type->flag & OPTYPE_UNDO))
+ for (op = wm->operators.last; op; op = op->prev) {
+ if ((op->type->flag & OPTYPE_REGISTER) && (op->type->flag & OPTYPE_UNDO)) {
break;
+ }
+ }
return op;
}
@@ -1020,8 +1031,9 @@ static void wm_block_redo_cancel_cb(bContext *C, void *arg_op)
wmOperator *op = arg_op;
/* if operator never got executed, free it */
- if (op != WM_operator_last_redo(C))
+ if (op != WM_operator_last_redo(C)) {
WM_operator_free(op);
+ }
}
static uiBlock *wm_block_create_redo(bContext *C, ARegion *ar, void *arg_op)
@@ -1046,17 +1058,20 @@ static uiBlock *wm_block_create_redo(bContext *C, ARegion *ar, void *arg_op)
UI_block_func_handle_set(block, wm_block_redo_cb, arg_op);
layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, width, UI_UNIT_Y, 0, style);
- if (op == WM_operator_last_redo(C))
- if (!WM_operator_check_ui_enabled(C, op->type->name))
+ if (op == WM_operator_last_redo(C)) {
+ if (!WM_operator_check_ui_enabled(C, op->type->name)) {
uiLayoutSetEnabled(layout, false);
+ }
+ }
if (op->type->flag & OPTYPE_MACRO) {
for (op = op->macro.first; op; op = op->next) {
uiTemplateOperatorPropertyButs(
C, layout, op, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN,
UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
- if (op->next)
+ if (op->next) {
uiItemS(layout);
+ }
}
}
else {
@@ -1211,8 +1226,9 @@ static void wm_operator_ui_popup_ok(struct bContext *C, void *arg, int retval)
wmOpPopUp *data = arg;
wmOperator *op = data->op;
- if (op && retval > 0)
+ if (op && retval > 0) {
WM_operator_call_ex(C, op, true);
+ }
MEM_freeN(data);
}
@@ -1251,13 +1267,15 @@ static int wm_operator_props_popup_ex(bContext *C, wmOperator *op,
/* if we don't have global undo, we can't do undo push for automatic redo,
* so we require manual OK clicking in this popup */
- if (!do_redo || !(U.uiflag & USER_GLOBALUNDO))
+ if (!do_redo || !(U.uiflag & USER_GLOBALUNDO)) {
return WM_operator_props_dialog_popup(C, op, 300, 20);
+ }
UI_popup_block_ex(C, wm_block_create_redo, NULL, wm_block_redo_cancel_cb, op, op);
- if (do_call)
+ if (do_call) {
wm_block_redo_cb(C, op, 0);
+ }
return OPERATOR_RUNNING_MODAL;
}
@@ -1754,10 +1772,12 @@ static bool wm_operator_winactive_normal(bContext *C)
wmWindow *win = CTX_wm_window(C);
bScreen *screen;
- if (win == NULL)
+ if (win == NULL) {
return 0;
- if (!((screen = WM_window_get_active_screen(win)) && (screen->state == SCREENNORMAL)))
+ }
+ if (!((screen = WM_window_get_active_screen(win)) && (screen->state == SCREENNORMAL))) {
return 0;
+ }
return 1;
}
@@ -2090,8 +2110,9 @@ static void radial_control_paint_tex(RadialControl *rc, float radius, float alph
immEnd();
/* undo rotation */
- if (rc->rot_prop)
+ if (rc->rot_prop) {
GPU_matrix_pop();
+ }
}
else {
/* flat color if no texture available */
@@ -2173,8 +2194,9 @@ static void radial_control_paint_cursor(bContext *UNUSED(C), int x, int y, void
radial_control_paint_tex(rc, tex_radius, alpha);
/* set line color */
- if (rc->col_prop)
+ if (rc->col_prop) {
RNA_property_float_get_array(&rc->col_ptr, rc->col_prop, col);
+ }
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -2205,8 +2227,9 @@ static void radial_control_paint_cursor(bContext *UNUSED(C), int x, int y, void
/* draw circles on top */
imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, r1, 40);
imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, r2, 40);
- if (rmin > 0.0f)
+ if (rmin > 0.0f) {
imm_draw_circle_wire_2d(pos, 0.0, 0.0f, rmin, 40);
+ }
immUnbindProgram();
BLF_size(fontid, 1.5 * fstyle_points * U.pixelsize, U.dpi);
@@ -2253,23 +2276,28 @@ static int radial_control_get_path(
}
/* get an rna string path from the operator's properties */
- if (!(str = RNA_string_get_alloc(op->ptr, name, NULL, 0)))
+ if (!(str = RNA_string_get_alloc(op->ptr, name, NULL, 0))) {
return 1;
+ }
if (str[0] == '\0') {
- if (r_prop) *r_prop = NULL;
+ if (r_prop) {
+ *r_prop = NULL;
+ }
MEM_freeN(str);
return 1;
}
- if (!r_prop)
+ if (!r_prop) {
r_prop = &unused_prop;
+ }
/* get rna from path */
if (!RNA_path_resolve(ctx_ptr, str, r_ptr, r_prop)) {
MEM_freeN(str);
- if (flags & RC_PROP_ALLOW_MISSING)
+ if (flags & RC_PROP_ALLOW_MISSING) {
return 1;
+ }
else {
BKE_reportf(op->reports, RPT_ERROR, "Could not resolve path '%s'", name);
return 0;
@@ -2331,18 +2359,22 @@ static int radial_control_get_properties(bContext *C, wmOperator *op)
}
}
- if (!radial_control_get_path(&ctx_ptr, op, data_path, &rc->ptr, &rc->prop, 0, 0))
+ if (!radial_control_get_path(&ctx_ptr, op, data_path, &rc->ptr, &rc->prop, 0, 0)) {
return 0;
+ }
/* data path is required */
- if (!rc->prop)
+ if (!rc->prop) {
return 0;
+ }
- if (!radial_control_get_path(&ctx_ptr, op, "rotation_path", &rc->rot_ptr, &rc->rot_prop, 0, RC_PROP_REQUIRE_FLOAT))
- return 0;
- if (!radial_control_get_path(&ctx_ptr, op, "color_path", &rc->col_ptr, &rc->col_prop, 3, RC_PROP_REQUIRE_FLOAT))
+ if (!radial_control_get_path(&ctx_ptr, op, "rotation_path", &rc->rot_ptr, &rc->rot_prop, 0, RC_PROP_REQUIRE_FLOAT)) {
return 0;
+ }
+ if (!radial_control_get_path(&ctx_ptr, op, "color_path", &rc->col_ptr, &rc->col_prop, 3, RC_PROP_REQUIRE_FLOAT)) {
+ return 0;
+ }
if (!radial_control_get_path(
&ctx_ptr, op, "fill_color_path", &rc->fill_col_ptr, &rc->fill_col_prop, 3, RC_PROP_REQUIRE_FLOAT))
@@ -2373,8 +2405,9 @@ static int radial_control_get_properties(bContext *C, wmOperator *op)
return 0;
}
- if (!radial_control_get_path(&ctx_ptr, op, "image_id", &rc->image_id_ptr, NULL, 0, 0))
+ if (!radial_control_get_path(&ctx_ptr, op, "image_id", &rc->image_id_ptr, NULL, 0, 0)) {
return 0;
+ }
else if (rc->image_id_ptr.data) {
/* extra check, pointer must be to an ID */
if (!RNA_struct_is_ID(rc->image_id_ptr.type)) {
@@ -2394,8 +2427,9 @@ static int radial_control_invoke(bContext *C, wmOperator *op, const wmEvent *eve
RadialControl *rc;
- if (!(op->customdata = rc = MEM_callocN(sizeof(RadialControl), "RadialControl")))
+ if (!(op->customdata = rc = MEM_callocN(sizeof(RadialControl), "RadialControl"))) {
return OPERATOR_CANCELLED;
+ }
if (!radial_control_get_properties(C, op)) {
MEM_freeN(rc);
@@ -2534,8 +2568,9 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even
if (rc->subtype == PROP_ANGLE) {
numValue = DEG2RADF(numValue);
numValue = fmod(numValue, 2.0f * (float)M_PI);
- if (numValue < 0.0f)
+ if (numValue < 0.0f) {
numValue += 2.0f * (float)M_PI;
+ }
}
CLAMP(numValue, rc->min_value, rc->max_value);
@@ -2619,22 +2654,31 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even
case PROP_DISTANCE:
case PROP_PIXEL:
new_value = dist;
- if (snap) new_value = ((int)new_value + 5) / 10 * 10;
+ if (snap) {
+ new_value = ((int)new_value + 5) / 10 * 10;
+ }
break;
case PROP_PERCENTAGE:
new_value = ((dist - WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE) / WM_RADIAL_CONTROL_DISPLAY_WIDTH) * 100.0f;
- if (snap) new_value = ((int)(new_value + 2.5f)) / 5 * 5;
+ if (snap) {
+ new_value = ((int)(new_value + 2.5f)) / 5 * 5;
+ }
break;
case PROP_FACTOR:
new_value = (WM_RADIAL_CONTROL_DISPLAY_SIZE - dist) / WM_RADIAL_CONTROL_DISPLAY_WIDTH;
- if (snap) new_value = ((int)ceil(new_value * 10.f) * 10.0f) / 100.f;
+ if (snap) {
+ new_value = ((int)ceil(new_value * 10.f) * 10.0f) / 100.f;
+ }
break;
case PROP_ANGLE:
new_value = atan2f(delta[1], delta[0]) + (float)M_PI + angle_precision;
new_value = fmod(new_value, 2.0f * (float)M_PI);
- if (new_value < 0.0f)
+ if (new_value < 0.0f) {
new_value += 2.0f * (float)M_PI;
- if (snap) new_value = DEG2RADF(((int)RAD2DEGF(new_value) + 5) / 10 * 10);
+ }
+ if (snap) {
+ new_value = DEG2RADF(((int)RAD2DEGF(new_value) + 5) / 10 * 10);
+ }
break;
default:
new_value = dist; /* dummy value, should this ever happen? - campbell */
@@ -2685,8 +2729,9 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even
if (rc->subtype == PROP_ANGLE) {
numValue = DEG2RADF(numValue);
numValue = fmod(numValue, 2.0f * (float)M_PI);
- if (numValue < 0.0f)
+ if (numValue < 0.0f) {
numValue += 2.0f * (float)M_PI;
+ }
}
CLAMP(numValue, rc->min_value, rc->max_value);
@@ -2703,8 +2748,9 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even
ED_region_tag_redraw(CTX_wm_region(C));
radial_control_update_header(op, C);
- if (ret != OPERATOR_RUNNING_MODAL)
+ if (ret != OPERATOR_RUNNING_MODAL) {
radial_control_cancel(C, op);
+ }
return ret;
}
@@ -2766,8 +2812,9 @@ static void redraw_timer_window_swap(bContext *C)
ScrArea *sa;
CTX_wm_menu_set(C, NULL);
- for (sa = CTX_wm_screen(C)->areabase.first; sa; sa = sa->next)
+ for (sa = CTX_wm_screen(C)->areabase.first; sa; sa = sa->next) {
ED_area_tag_redraw(sa);
+ }
wm_draw_update(C);
CTX_wm_window_set(C, win); /* XXX context manipulation warning! */
@@ -2853,8 +2900,9 @@ static void redraw_timer_step(
while (tot--) {
/* todo, ability to escape! */
scene->r.cfra++;
- if (scene->r.cfra > scene->r.efra)
+ if (scene->r.cfra > scene->r.efra) {
scene->r.cfra = scene->r.sfra;
+ }
BKE_scene_graph_update_for_newframe(depsgraph, bmain);
redraw_timer_window_swap(C);
@@ -3058,8 +3106,9 @@ static int previews_clear_exec(bContext *C, wmOperator *op)
for (i = 0; lb[i]; i++) {
ID *id = lb[i]->first;
-
- if (!id) continue;
+ if (!id) {
+ continue;
+ }
// printf("%s: %d, %d, %d -> %d\n", id->name, GS(id->name), BKE_idcode_to_idfilter(GS(id->name)),
// id_filters, BKE_idcode_to_idfilter(GS(id->name)) & id_filters);
@@ -3226,7 +3275,9 @@ static void gesture_circle_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Gesture Circle");
/* this function is called for each spacetype, only needs to add map once */
- if (keymap && keymap->modal_items) return;
+ if (keymap && keymap->modal_items) {
+ return;
+ }
keymap = WM_modalkeymap_add(keyconf, "View3D Gesture Circle", modal_items);
@@ -3254,7 +3305,9 @@ static void gesture_straightline_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Gesture Straight Line");
/* this function is called for each spacetype, only needs to add map once */
- if (keymap && keymap->modal_items) return;
+ if (keymap && keymap->modal_items) {
+ return;
+ }
keymap = WM_modalkeymap_add(keyconf, "Gesture Straight Line", modal_items);
@@ -3279,7 +3332,9 @@ static void gesture_box_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Gesture Box");
/* this function is called for each spacetype, only needs to add map once */
- if (keymap && keymap->modal_items) return;
+ if (keymap && keymap->modal_items) {
+ return;
+ }
keymap = WM_modalkeymap_add(keyconf, "Gesture Box", modal_items);
@@ -3327,7 +3382,9 @@ static void gesture_zoom_border_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Gesture Zoom Border");
/* this function is called for each spacetype, only needs to add map once */
- if (keymap && keymap->modal_items) return;
+ if (keymap && keymap->modal_items) {
+ return;
+ }
keymap = WM_modalkeymap_add(keyconf, "Gesture Zoom Border", modal_items);
diff --git a/source/blender/windowmanager/intern/wm_panel_type.c b/source/blender/windowmanager/intern/wm_panel_type.c
index 5c0fd4ca520..f00129c139b 100644
--- a/source/blender/windowmanager/intern/wm_panel_type.c
+++ b/source/blender/windowmanager/intern/wm_panel_type.c
@@ -45,12 +45,14 @@ PanelType *WM_paneltype_find(const char *idname, bool quiet)
if (idname[0]) {
pt = BLI_ghash_lookup(g_paneltypes_hash, idname);
- if (pt)
+ if (pt) {
return pt;
+ }
}
- if (!quiet)
+ if (!quiet) {
printf("search for unknown paneltype %s\n", idname);
+ }
return NULL;
}
diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c
index 0a1ec29c01c..12931c2f5ac 100644
--- a/source/blender/windowmanager/intern/wm_playanim.c
+++ b/source/blender/windowmanager/intern/wm_playanim.c
@@ -291,8 +291,9 @@ static void playanim_toscreen(PlayState *ps, PlayAnimPict *picture, struct ImBuf
IMB_rect_from_float(ibuf);
imb_freerectfloatImBuf(ibuf);
}
- if (ibuf->rect == NULL)
+ if (ibuf->rect == NULL) {
return;
+ }
GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
@@ -673,13 +674,19 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
key_data = (GHOST_TEventKeyData *)GHOST_GetEventData(evt);
switch (key_data->key) {
case GHOST_kKeyA:
- if (val) ps->noskip = !ps->noskip;
+ if (val) {
+ ps->noskip = !ps->noskip;
+ }
break;
case GHOST_kKeyI:
- if (val) ps->indicator = !ps->indicator;
+ if (val) {
+ ps->indicator = !ps->indicator;
+ }
break;
case GHOST_kKeyP:
- if (val) ps->pingpong = !ps->pingpong;
+ if (val) {
+ ps->pingpong = !ps->pingpong;
+ }
break;
case GHOST_kKeyF:
{
@@ -811,9 +818,10 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
case GHOST_kKeyNumpadSlash:
if (val) {
if (g_WS.qual & WS_QUAL_SHIFT) {
- if (ps->curframe_ibuf)
+ if (ps->curframe_ibuf) {
printf(" Name: %s | Speed: %.2f frames/s\n",
ps->curframe_ibuf->name, ps->fstep / swaptime);
+ }
}
else {
swaptime = ps->fstep / 5.0;
@@ -849,11 +857,13 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
i++;
picture = picture->next;
}
- if (playback_handle)
+ if (playback_handle) {
AUD_Handle_stop(playback_handle);
+ }
playback_handle = AUD_Device_play(audio_device, source, 1);
- if (playback_handle)
+ if (playback_handle) {
AUD_Handle_setPosition(playback_handle, i / fps_movie);
+ }
update_sound_fps();
}
#endif
@@ -883,11 +893,13 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
i++;
picture = picture->next;
}
- if (playback_handle)
+ if (playback_handle) {
AUD_Handle_stop(playback_handle);
+ }
playback_handle = AUD_Device_play(audio_device, source, 1);
- if (playback_handle)
+ if (playback_handle) {
AUD_Handle_setPosition(playback_handle, i / fps_movie);
+ }
update_sound_fps();
}
#endif
@@ -915,7 +927,9 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
case GHOST_kKeyPlus:
case GHOST_kKeyNumpadPlus:
{
- if (val == 0) break;
+ if (val == 0) {
+ break;
+ }
if (g_WS.qual & WS_QUAL_CTRL) {
playanim_window_zoom(ps, 0.1f);
}
@@ -930,7 +944,9 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
case GHOST_kKeyMinus:
case GHOST_kKeyNumpadMinus:
{
- if (val == 0) break;
+ if (val == 0) {
+ break;
+ }
if (g_WS.qual & WS_QUAL_CTRL) {
playanim_window_zoom(ps, -0.1f);
}
@@ -969,24 +985,29 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
tag_change_frame(ps, cx);
}
}
- else
+ else {
g_WS.qual &= ~WS_QUAL_LMOUSE;
+ }
}
else if (bd->button == GHOST_kButtonMaskMiddle) {
if (type == GHOST_kEventButtonDown) {
- if (inside_window)
+ if (inside_window) {
g_WS.qual |= WS_QUAL_MMOUSE;
+ }
}
- else
+ else {
g_WS.qual &= ~WS_QUAL_MMOUSE;
+ }
}
else if (bd->button == GHOST_kButtonMaskRight) {
if (type == GHOST_kEventButtonDown) {
- if (inside_window)
+ if (inside_window) {
g_WS.qual |= WS_QUAL_RMOUSE;
+ }
}
- else
+ else {
g_WS.qual &= ~WS_QUAL_RMOUSE;
+ }
}
break;
}
@@ -1099,7 +1120,9 @@ static void playanim_window_zoom(PlayState *ps, const float zoom_offset)
int sizex, sizey;
/* int ofsx, ofsy; */ /* UNUSED */
- if (ps->zoom + zoom_offset > 0.0f) ps->zoom += zoom_offset;
+ if (ps->zoom + zoom_offset > 0.0f) {
+ ps->zoom += zoom_offset;
+ }
// playanim_window_get_position(&ofsx, &ofsy);
playanim_window_get_size(&sizex, &sizey);
@@ -1280,8 +1303,12 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.win_x = ps.ibufx;
ps.win_y = ps.ibufy;
- if (maxwinx % ibuf->x) maxwinx = ibuf->x * (1 + (maxwinx / ibuf->x));
- if (maxwiny % ibuf->y) maxwiny = ibuf->y * (1 + (maxwiny / ibuf->y));
+ if (maxwinx % ibuf->x) {
+ maxwinx = ibuf->x * (1 + (maxwinx / ibuf->x));
+ }
+ if (maxwiny % ibuf->y) {
+ maxwiny = ibuf->y * (1 + (maxwiny / ibuf->y));
+ }
glClearColor(0.1, 0.1, 0.1, 0.0);
@@ -1335,8 +1362,9 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
#define USE_IMB_CACHE
while (ps.go) {
- if (ps.pingpong)
+ if (ps.pingpong) {
ps.direction = -ps.direction;
+ }
if (ps.direction == 1) {
ps.picture = picsbase.first;
@@ -1357,11 +1385,14 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.picture = ps.picture->prev;
}
}
- if (ptottime > 0.0) ptottime = 0.0;
+ if (ptottime > 0.0) {
+ ptottime = 0.0;
+ }
#ifdef WITH_AUDASPACE
- if (playback_handle)
+ if (playback_handle) {
AUD_Handle_stop(playback_handle);
+ }
playback_handle = AUD_Device_play(audio_device, source, 1);
update_sound_fps();
#endif
@@ -1369,7 +1400,9 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
while (ps.picture) {
int hasevent;
#ifndef USE_IMB_CACHE
- if (ibuf != NULL && ibuf->ftype == 0) IMB_freeImBuf(ibuf);
+ if (ibuf != NULL && ibuf->ftype == 0) {
+ IMB_freeImBuf(ibuf);
+ }
#endif
if (ps.picture->ibuf) {
ibuf = ps.picture->ibuf;
@@ -1428,7 +1461,9 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
GHOST_SetTitle(g_WS.ghost_window, ps.picture->name);
#endif
- while (pupdate_time()) PIL_sleep_ms(1);
+ while (pupdate_time()) {
+ PIL_sleep_ms(1);
+ }
ptottime -= swaptime;
playanim_toscreen(&ps, ps.picture, ibuf, ps.fontid, ps.fstep);
} /* else delete */
@@ -1484,7 +1519,9 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
}
}
- if (ps.wait2 || ptottime < swaptime || ps.turbo || ps.noskip) break;
+ if (ps.wait2 || ptottime < swaptime || ps.turbo || ps.noskip) {
+ break;
+ }
ptottime -= swaptime;
}
if (ps.picture == NULL && ps.sstep) {
@@ -1518,7 +1555,9 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
/* cleanup */
#ifndef USE_IMB_CACHE
- if (ibuf) IMB_freeImBuf(ibuf);
+ if (ibuf) {
+ IMB_freeImBuf(ibuf);
+ }
#endif
BLI_freelistN(&picsbase);
diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c
index 68cbc9b59a7..6691a7f453b 100644
--- a/source/blender/windowmanager/intern/wm_stereo.c
+++ b/source/blender/windowmanager/intern/wm_stereo.c
@@ -162,12 +162,14 @@ void wm_stereo3d_draw_sidebyside(wmWindow *win, int view)
int soffx = WM_window_pixels_x(win) * 0.5f;
if (view == STEREO_LEFT_ID) {
- if (!cross_eyed)
+ if (!cross_eyed) {
soffx = 0;
+ }
}
else { //RIGHT_LEFT_ID
- if (cross_eyed)
+ if (cross_eyed) {
soffx = 0;
+ }
}
const int sizex = WM_window_pixels_x(win);
@@ -286,8 +288,9 @@ bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check)
*/
void wm_stereo3d_mouse_offset_apply(wmWindow *win, int *r_mouse_xy)
{
- if (!WM_stereo3d_enabled(win, false))
+ if (!WM_stereo3d_enabled(win, false)) {
return;
+ }
if (win->stereo3d_format->display_mode == S3D_DISPLAY_SIDEBYSIDE) {
const int half_x = win->sizex / 2;
@@ -339,19 +342,23 @@ static bool wm_stereo3d_set_properties(bContext *UNUSED(C), wmOperator *op)
prop = RNA_struct_find_property(op->ptr, "use_interlace_swap");
if (RNA_property_is_set(op->ptr, prop)) {
- if (RNA_property_boolean_get(op->ptr, prop))
+ if (RNA_property_boolean_get(op->ptr, prop)) {
s3d->flag |= S3D_INTERLACE_SWAP;
- else
+ }
+ else {
s3d->flag &= ~S3D_INTERLACE_SWAP;
+ }
is_set = true;
}
prop = RNA_struct_find_property(op->ptr, "use_sidebyside_crosseyed");
if (RNA_property_is_set(op->ptr, prop)) {
- if (RNA_property_boolean_get(op->ptr, prop))
+ if (RNA_property_boolean_get(op->ptr, prop)) {
s3d->flag |= S3D_SIDEBYSIDE_CROSSEYED;
- else
+ }
+ else {
s3d->flag &= ~S3D_SIDEBYSIDE_CROSSEYED;
+ }
is_set = true;
}
@@ -379,8 +386,9 @@ int wm_stereo3d_set_exec(bContext *C, wmOperator *op)
Stereo3dData *s3dd;
bool ok = true;
- if (G.background)
+ if (G.background) {
return OPERATOR_CANCELLED;
+ }
if (op->customdata == NULL) {
/* no invoke means we need to set the operator properties here */
@@ -460,10 +468,12 @@ int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev
{
wm_stereo3d_set_init(C, op);
- if (wm_stereo3d_set_properties(C, op))
+ if (wm_stereo3d_set_properties(C, op)) {
return wm_stereo3d_set_exec(C, op);
- else
+ }
+ else {
return WM_operator_props_dialog_popup(C, op, 250, 100);
+ }
}
void wm_stereo3d_set_draw(bContext *UNUSED(C), wmOperator *op)
diff --git a/source/blender/windowmanager/intern/wm_subwindow.c b/source/blender/windowmanager/intern/wm_subwindow.c
index bf4288e549b..6f856e16047 100644
--- a/source/blender/windowmanager/intern/wm_subwindow.c
+++ b/source/blender/windowmanager/intern/wm_subwindow.c
@@ -101,8 +101,12 @@ void wmWindowViewport(wmWindow *win)
void wmOrtho2(float x1, float x2, float y1, float y2)
{
/* prevent opengl from generating errors */
- if (x1 == x2) x2 += 1.0f;
- if (y1 == y2) y2 += 1.0f;
+ if (x2 == x1) {
+ x2 += 1.0f;
+ }
+ if (y2 == y1) {
+ y2 += 1.0f;
+ }
GPU_matrix_ortho_set(x1, x2, y1, y2, -100, 100);
}
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index a4c953fd958..a1dc822dc97 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -179,8 +179,12 @@ static void wm_window_check_position(rcti *rect)
rect->ymin -= d;
}
- if (rect->xmin < 0) rect->xmin = 0;
- if (rect->ymin < 0) rect->ymin = 0;
+ if (rect->xmin < 0) {
+ rect->xmin = 0;
+ }
+ if (rect->ymin < 0) {
+ rect->ymin = 0;
+ }
}
static void wm_ghostwindow_destroy(wmWindowManager *wm, wmWindow *win)
@@ -220,8 +224,9 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
WM_event_remove_handlers(C, &win->handlers);
WM_event_remove_handlers(C, &win->modalhandlers);
- if (CTX_wm_window(C) == win)
+ if (CTX_wm_window(C) == win) {
CTX_wm_window_set(C, NULL);
+ }
}
BKE_screen_area_map_free(&win->global_areas);
@@ -229,18 +234,22 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
/* end running jobs, a job end also removes its timer */
for (wt = wm->timers.first; wt; wt = wtnext) {
wtnext = wt->next;
- if (wt->win == win && wt->event_type == TIMERJOBS)
+ if (wt->win == win && wt->event_type == TIMERJOBS) {
wm_jobs_timer_ended(wm, wt);
+ }
}
/* timer removing, need to call this api function */
for (wt = wm->timers.first; wt; wt = wtnext) {
wtnext = wt->next;
- if (wt->win == win)
+ if (wt->win == win) {
WM_event_remove_timer(wm, win, wt);
+ }
}
- if (win->eventstate) MEM_freeN(win->eventstate);
+ if (win->eventstate) {
+ MEM_freeN(win->eventstate);
+ }
if (win->cursor_keymap_status) {
MEM_freeN(win->cursor_keymap_status);
@@ -261,10 +270,11 @@ static int find_free_winid(wmWindowManager *wm)
wmWindow *win;
int id = 1;
- for (win = wm->windows.first; win; win = win->next)
- if (id <= win->winid)
+ for (win = wm->windows.first; win; win = win->next) {
+ if (id <= win->winid) {
id = win->winid + 1;
-
+ }
+ }
return id;
}
@@ -560,8 +570,9 @@ void wm_window_title(wmWindowManager *wm, wmWindow *win)
G_MAIN->recovered ? " (Recovered)" : "");
GHOST_SetTitle(win->ghostwin, str);
}
- else
+ else {
GHOST_SetTitle(win->ghostwin, "Blender");
+ }
/* Informs GHOST of unsaved changes, to set window modified visual indicator (MAC OS X)
* and to give hint of unsaved changes for a user warning mechanism
@@ -639,8 +650,9 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
int scr_w, scr_h, posy;
/* a new window is created when pageflip mode is required for a window */
- if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP)
+ if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
glSettings.flags |= GHOST_glStereoVisual;
+ }
if (G.debug & G_DEBUG_GPU) {
glSettings.flags |= GHOST_glDebugContext;
@@ -897,9 +909,11 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i
wm_window_check_position(&rect);
/* test if we have a temp screen already */
- for (win = CTX_wm_manager(C)->windows.first; win; win = win->next)
- if (WM_window_is_temp_screen(win))
+ for (win = CTX_wm_manager(C)->windows.first; win; win = win->next) {
+ if (WM_window_is_temp_screen(win)) {
break;
+ }
+ }
/* add new window? */
if (win == NULL) {
@@ -973,16 +987,21 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i
ED_drivers_editor_init(C, sa);
}
- if (sa->spacetype == SPACE_IMAGE)
+ if (sa->spacetype == SPACE_IMAGE) {
title = IFACE_("Blender Render");
- else if (ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF))
+ }
+ else if (ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF)) {
title = IFACE_("Blender Preferences");
- else if (sa->spacetype == SPACE_FILE)
+ }
+ else if (sa->spacetype == SPACE_FILE) {
title = IFACE_("Blender File View");
- else if (sa->spacetype == SPACE_GRAPH)
+ }
+ else if (sa->spacetype == SPACE_GRAPH) {
title = IFACE_("Blender Drivers Editor");
- else
+ }
+ else {
title = "Blender";
+ }
if (win->ghostwin) {
GHOST_SetTitle(win->ghostwin, title);
@@ -1034,14 +1053,17 @@ int wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *UNUSED(op))
wmWindow *window = CTX_wm_window(C);
GHOST_TWindowState state;
- if (G.background)
+ if (G.background) {
return OPERATOR_CANCELLED;
+ }
state = GHOST_GetWindowState(window->ghostwin);
- if (state != GHOST_kWindowStateFullScreen)
+ if (state != GHOST_kWindowStateFullScreen) {
GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateFullScreen);
- else
+ }
+ else {
GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateNormal);
+ }
return OPERATOR_FINISHED;
@@ -1116,8 +1138,9 @@ static int query_qual(modifierKeyType qual)
}
GHOST_GetModifierKeyState(g_system, left, &val);
- if (!val)
+ if (!val) {
GHOST_GetModifierKeyState(g_system, right, &val);
+ }
return val;
}
@@ -1170,9 +1193,9 @@ void wm_window_reset_drawable(void)
BLI_assert(GPU_framebuffer_active_get() == NULL);
wmWindowManager *wm = G_MAIN->wm.first;
- if (wm == NULL)
+ if (wm == NULL) {
return;
-
+ }
wmWindow *win = wm->windrawable;
if (win && win->ghostwin) {
@@ -1616,12 +1639,15 @@ static int wm_window_timer(const bContext *C)
wt->ltime = time;
wt->ntime = wt->stime + wt->timestep * ceil(wt->duration / wt->timestep);
- if (wt->event_type == TIMERJOBS)
+ if (wt->event_type == TIMERJOBS) {
wm_jobs_timer(C, wm, wt);
- else if (wt->event_type == TIMERAUTOSAVE)
+ }
+ else if (wt->event_type == TIMERAUTOSAVE) {
wm_autosave_timer(C, wm, wt);
- else if (wt->event_type == TIMERNOTIFIER)
+ }
+ else if (wt->event_type == TIMERNOTIFIER) {
WM_main_add_notifier(POINTER_AS_UINT(wt->customdata), NULL);
+ }
else if (win) {
wmEvent event;
wm_event_init_from_window(win, &event);
@@ -1649,14 +1675,15 @@ void wm_window_process_events(const bContext *C)
hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
- if (hasevent)
+ if (hasevent) {
GHOST_DispatchEvents(g_system);
-
+ }
hasevent |= wm_window_timer(C);
/* no event, we sleep 5 milliseconds */
- if (hasevent == 0)
+ if (hasevent == 0) {
PIL_sleep_ms(5);
+ }
}
/* **************** init ********************** */
@@ -1690,9 +1717,9 @@ void wm_ghost_init(bContext *C)
void wm_ghost_exit(void)
{
- if (g_system)
+ if (g_system) {
GHOST_DisposeSystem(g_system);
-
+ }
g_system = NULL;
}
@@ -1703,12 +1730,15 @@ void WM_event_timer_sleep(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *t
{
wmTimer *wt;
- for (wt = wm->timers.first; wt; wt = wt->next)
- if (wt == timer)
+ for (wt = wm->timers.first; wt; wt = wt->next) {
+ if (wt == timer) {
break;
+ }
+ }
- if (wt)
+ if (wt) {
wt->sleep = do_sleep;
+ }
}
wmTimer *WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
@@ -1750,14 +1780,17 @@ void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *
wmTimer *wt;
/* extra security check */
- for (wt = wm->timers.first; wt; wt = wt->next)
- if (wt == timer)
+ for (wt = wm->timers.first; wt; wt = wt->next) {
+ if (wt == timer) {
break;
+ }
+ }
if (wt) {
wmWindow *win;
- if (wm->reports.reporttimer == wt)
+ if (wm->reports.reporttimer == wt) {
wm->reports.reporttimer = NULL;
+ }
BLI_remlink(&wm->timers, wt);
if (wt->customdata != NULL && (wt->flags & WM_TIMER_NO_FREE_CUSTOM_DATA) == 0) {
@@ -1861,10 +1894,12 @@ void WM_clipboard_text_set(const char *buf, bool selection)
int newlen = 0;
for (p = buf; *p; p++) {
- if (*p == '\n')
+ if (*p == '\n') {
newlen += 2;
- else
+ }
+ else {
newlen++;
+ }
}
newbuf = MEM_callocN(newlen + 1, "WM_clipboard_text_set");
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index d8b579580f9..5d1dc92d1e1 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -38,6 +38,9 @@ blender_include_dirs(
../blender/windowmanager
)
+set(LIB
+)
+
add_definitions(${GL_DEFINITIONS})
blender_include_dirs("${GLEW_INCLUDE_PATH}")
@@ -634,6 +637,13 @@ if(UNIX AND NOT APPLE)
endif()
endif()
+
+ if(WITH_DRACO)
+ install(
+ PROGRAMS $<TARGET_FILE:extern_draco>
+ DESTINATION ${TARGETDIR_VER}/python/lib/python${PYTHON_VERSION}/site-packages
+ )
+ endif()
elseif(WIN32)
set(BLENDER_TEXT_FILES_DESTINATION ".")
@@ -833,6 +843,12 @@ elseif(WIN32)
DESTINATION "."
)
+ if(WITH_DRACO)
+ install(
+ PROGRAMS $<TARGET_FILE:extern_draco>
+ DESTINATION ${TARGETDIR_VER}/python/lib/site-packages
+ )
+ endif()
elseif(APPLE)
# handy install macro to exclude files, we use \$ escape for the "to"
@@ -937,6 +953,12 @@ elseif(APPLE)
unset(_py_inc_suffix)
endif()
+ if(WITH_DRACO)
+ install(
+ PROGRAMS $<TARGET_FILE:extern_draco>
+ DESTINATION ${TARGETDIR_VER}/python/lib/python${PYTHON_VERSION}/site-packages
+ )
+ endif()
endif()
# -----------------------------------------------------------------------------
@@ -1004,11 +1026,19 @@ install(
add_dependencies(blender makesdna)
-setup_blender_sorted_libs()
-target_link_libraries(blender ${BLENDER_SORTED_LIBS})
+# Use for testing 'BLENDER_SORTED_LIBS' removal.
+if(NOT (DEFINED WITHOUT_SORTED_LIBS AND WITHOUT_SORTED_LIBS))
+ setup_blender_sorted_libs()
+ target_link_libraries(blender ${BLENDER_SORTED_LIBS})
+else()
+ target_link_libraries(blender ${LIB})
+endif()
+
setup_liblinks(blender)
+unset(LIB)
+
# -----------------------------------------------------------------------------
# Setup launcher
diff --git a/source/creator/creator.c b/source/creator/creator.c
index 6d07f2a9c42..90ab3acbeb7 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -271,9 +271,7 @@ int main(
{
int i;
for (i = 0; i < argc; i++) {
- if (STREQ(argv[i], "--debug") || STREQ(argv[i], "-d") ||
- STREQ(argv[i], "--debug-memory") || STREQ(argv[i], "--debug-all"))
- {
+ if (STR_ELEM(argv[i], "-d", "--debug", "--debug-memory", "--debug-all")) {
printf("Switching to fully guarded memory allocator.\n");
MEM_use_guarded_allocator();
break;
diff --git a/tests/gtests/testing/CMakeLists.txt b/tests/gtests/testing/CMakeLists.txt
index 22d2b960730..b868f4da8f4 100644
--- a/tests/gtests/testing/CMakeLists.txt
+++ b/tests/gtests/testing/CMakeLists.txt
@@ -35,4 +35,7 @@ set(SRC
testing.h
)
-blender_add_lib(bf_testing_main "${SRC}" "${INC}" "${INC_SYS}")
+set(LIB
+)
+
+blender_add_lib(bf_testing_main "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")