diff --git a/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc b/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc index 6c3b17d..c6d2304 100644 --- a/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc +++ b/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.cc @@ -16,6 +16,36 @@ namespace draco { +PredictionSchemeMethod SelectPredictionMethod( + int att_id, const PointCloudEncoder *encoder) { + if (encoder->options()->GetSpeed() >= 10) { + return PREDICTION_NONE; // No prediction when the fastest speed is + // requested. + } + 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; + } + } + // Handle other attribute types. + if (encoder->options()->GetSpeed() >= 8) { + return PREDICTION_DIFFERENCE; + } + if (encoder->options()->GetSpeed() >= 2) { + // Parallelogram prediction is used for speeds 2 - 7. + return MESH_PREDICTION_PARALLELOGRAM; + } + // Multi-parallelogram is used for speeds 0, 1. + return MESH_PREDICTION_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) { diff --git a/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h b/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h index ebbf9ca..975cbd7 100644 --- a/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h +++ b/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h @@ -23,6 +23,11 @@ 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); + // Creates a prediction scheme for a given encoder and given prediction method. // The prediction schemes are automatically initialized with encoder specific // data if needed. @@ -34,23 +39,9 @@ CreatePredictionSchemeForEncoder(PredictionSchemeMethod method, int att_id, const TransformT &transform) { const PointAttribute *const att = encoder->point_cloud()->attribute(att_id); if (method == PREDICTION_UNDEFINED) { - if (encoder->options()->GetSpeed() >= 10) { - return nullptr; // No prediction when the fastest speed is requested. - } - if (encoder->GetGeometryType() == TRIANGULAR_MESH) { - // Use speed setting to select the best encoding method. - if (encoder->options()->GetSpeed() >= 8) { - method = PREDICTION_DIFFERENCE; - } else if (encoder->options()->GetSpeed() >= 5) { - method = MESH_PREDICTION_PARALLELOGRAM; - } else { - if (att->attribute_type() == GeometryAttribute::TEX_COORD) { - method = MESH_PREDICTION_TEX_COORDS; - } else { - method = MESH_PREDICTION_MULTI_PARALLELOGRAM; - } - } - } + 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 diff --git a/compression/attributes/sequential_attribute_decoder.cc b/compression/attributes/sequential_attribute_decoder.cc index ecd416c..59a79d0 100644 --- a/compression/attributes/sequential_attribute_decoder.cc +++ b/compression/attributes/sequential_attribute_decoder.cc @@ -36,7 +36,8 @@ bool SequentialAttributeDecoder::InitializeStandalone( bool SequentialAttributeDecoder::Decode( const std::vector &point_ids, DecoderBuffer *in_buffer) { - attribute_->Reset(point_ids.size()); + if (!attribute_->Reset(point_ids.size())) + return false; if (!DecodeValues(point_ids, in_buffer)) return false; return true; diff --git a/compression/attributes/sequential_integer_attribute_encoder.cc b/compression/attributes/sequential_integer_attribute_encoder.cc index 1f03620..9eecd56 100644 --- a/compression/attributes/sequential_integer_attribute_encoder.cc +++ b/compression/attributes/sequential_integer_attribute_encoder.cc @@ -97,9 +97,8 @@ bool SequentialIntegerAttributeEncoder::EncodeValues( reinterpret_cast(values_.data())); } - if (encoder() == nullptr || - encoder()->options()->GetGlobalBool("use_built_in_attribute_compression", - true)) { + if (encoder() == nullptr || encoder()->options()->GetGlobalBool( + "use_built_in_attribute_compression", true)) { out_buffer->Encode(static_cast(1)); EncodeSymbols(reinterpret_cast(values_.data()), point_ids.size() * num_components, num_components, diff --git a/core/adaptive_rans_coding.cc b/core/adaptive_rans_coding.cc index 694bfae..e1f24a5 100644 --- a/core/adaptive_rans_coding.cc +++ b/core/adaptive_rans_coding.cc @@ -83,7 +83,7 @@ bool AdaptiveRAnsBitDecoder::StartDecoding(DecoderBuffer *source_buffer) { if (!source_buffer->Decode(&size_in_bytes)) return false; if (size_in_bytes > source_buffer->remaining_size()) - return false; + return false; if (ans_read_init(&ans_decoder_, reinterpret_cast( const_cast(source_buffer->data_head())), diff --git a/core/data_buffer.cc b/core/data_buffer.cc index a37a597..1812bf8 100644 --- a/core/data_buffer.cc +++ b/core/data_buffer.cc @@ -18,7 +18,10 @@ namespace draco { DataBuffer::DataBuffer() {} -void DataBuffer::Update(const void *data, int64_t size) { +bool DataBuffer::Update(const void *data, int64_t size) { + if (size < 0) + return false; + if (data == nullptr) { // If no data is provided, just resize the buffer. data_.resize(size); @@ -27,19 +30,25 @@ void DataBuffer::Update(const void *data, int64_t size) { data_.assign(byte_data, byte_data + size); } descriptor_.buffer_update_count++; + return true; } -void DataBuffer::Update(const void *data, int64_t size, int64_t 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(data_.size())) data_.resize(size + offset); const uint8_t *const byte_data = static_cast(data); std::copy(byte_data, byte_data + size, data_.data() + offset); } descriptor_.buffer_update_count++; + return true; } void DataBuffer::WriteDataToStream(std::ostream &stream) { diff --git a/core/data_buffer.h b/core/data_buffer.h index 1a5c1ea..329060d 100644 --- a/core/data_buffer.h +++ b/core/data_buffer.h @@ -36,10 +36,10 @@ struct DataBufferDescriptor { class DataBuffer { public: DataBuffer(); - void Update(const void *data, int64_t size); + bool Update(const void *data, int64_t size); // TODO(zhafang): The two update functions should be combined. I will // leave for now in case it breaks any geometry compression tools. - void Update(const void *data, int64_t size, int64_t offset); + bool Update(const void *data, int64_t size, int64_t offset); void WriteDataToStream(std::ostream &stream); // Reads data from the buffer. Potentially unsafe, called needs to ensure // the accessed memory is valid. diff --git a/core/draco_index_type_vector.h b/core/draco_index_type_vector.h index d5804eb..bb9321c 100644 --- a/core/draco_index_type_vector.h +++ b/core/draco_index_type_vector.h @@ -30,6 +30,9 @@ namespace draco { template class IndexTypeVector { public: + typedef typename std::vector::const_reference const_reference; + typedef typename std::vector::reference reference; + IndexTypeVector() {} explicit IndexTypeVector(size_t size) : vector_(size) {} IndexTypeVector(size_t size, const ValueTypeT &val) : vector_(size, val) {} @@ -45,16 +48,16 @@ class IndexTypeVector { void push_back(const ValueTypeT &val) { vector_.push_back(val); } void push_back(ValueTypeT &&val) { vector_.push_back(std::move(val)); } - inline ValueTypeT &operator[](const IndexTypeT &index) { + inline reference operator[](const IndexTypeT &index) { return vector_[index.value()]; } - inline const ValueTypeT &operator[](const IndexTypeT &index) const { + inline const_reference operator[](const IndexTypeT &index) const { return vector_[index.value()]; } - inline ValueTypeT &at(const IndexTypeT &index) { + inline reference at(const IndexTypeT &index) { return vector_[index.value()]; } - inline const ValueTypeT &at(const IndexTypeT &index) const { + inline const_reference at(const IndexTypeT &index) const { return vector_[index.value()]; } const ValueTypeT *data() const { return vector_.data(); } diff --git a/core/vector_d.h b/core/vector_d.h index 8676ff1..e0fcf53 100644 --- a/core/vector_d.h +++ b/core/vector_d.h @@ -24,41 +24,43 @@ namespace draco { // D-dimensional vector class with basic operations. template class VectorD { - typedef VectorD Self; - public: + typedef VectorD 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 further down, which is not ideal. + // to the constructors further down, which is not ideal. // TODO(hemmer): fix constructor below and remove others. // template // explicit VectorD(Args... args) : v_({args...}) {} VectorD(const CoeffT &c0, const CoeffT &c1) : v_({{c0, c1}}) { - CHECK(dimension_t == 2); + CHECK_EQ(dimension_t, 2); v_[0] = c0; v_[1] = c1; } VectorD(const CoeffT &c0, const CoeffT &c1, const CoeffT &c2) : v_({{c0, c1, c2}}) { - CHECK(dimension_t == 3); + CHECK_EQ(dimension_t, 3); } VectorD(const CoeffT &c0, const CoeffT &c1, const CoeffT &c2, const CoeffT &c3) : v_({{c0, c1, c2, c3}}) { - CHECK(dimension_t == 4); + CHECK_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}}) { - CHECK(dimension_t == 5); + CHECK_EQ(dimension_t, 5); } VectorD(const Self &o) { @@ -160,6 +162,25 @@ VectorD operator*(const CoeffT &o, return v * o; } +// Calculates the squared distance between two points. +template +CoeffT SquaredDistance(const VectorD v1, + const VectorD v2) { + CoeffT difference; + CoeffT squared_distance = 0; + // Check each index seperately 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; +} + typedef VectorD Vector2f; typedef VectorD Vector3f; typedef VectorD Vector4f; @@ -172,4 +193,15 @@ typedef VectorD Vector5ui; } // namespace draco +template +inline ::std::basic_ostream &operator<<( + ::std::basic_ostream &out, + const draco::VectorD &vec) { + for (int i = 0; i < dimension_t - 1; ++i) { + out << vec[i] << " "; + } + out << vec[dimension_t - 1]; + return out; +} + #endif // DRACO_CORE_VECTOR_D_H_ diff --git a/core/vector_d_test.cc b/core/vector_d_test.cc index 2079a7d..4ed6102 100644 --- a/core/vector_d_test.cc +++ b/core/vector_d_test.cc @@ -18,10 +18,26 @@ namespace { -using draco::Vector3f; +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; class VectorDTest : public ::testing::Test { protected: + template + void TestSquaredDistance(const draco::VectorD v1, + const draco::VectorD v2, + const CoeffT result) { + CoeffT squared_distance = SquaredDistance(v1, v2); + ASSERT_EQ(squared_distance, result); + squared_distance = SquaredDistance(v2, v1); + ASSERT_EQ(squared_distance, result); + } }; TEST_F(VectorDTest, TestOperators) { @@ -69,4 +85,54 @@ TEST_F(VectorDTest, TestOperators) { ASSERT_EQ(v.Dot(v), 14); } +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); +} + } // namespace diff --git a/io/obj_decoder.cc b/io/obj_decoder.cc index 3943558..8ebfe3f 100644 --- a/io/obj_decoder.cc +++ b/io/obj_decoder.cc @@ -311,22 +311,38 @@ bool ObjDecoder::ParseFace(bool *error) { ->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])); + ->SetPointMapEntry( + vert_id, AttributeValueIndex(num_positions_ + indices[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])); + ->SetPointMapEntry( + vert_id, AttributeValueIndex(num_tex_coords_ + indices[1])); + } else if (tex_att_id_ >= 0) { + // 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 (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])); + out_point_cloud_->attribute(norm_att_id_) + ->SetPointMapEntry(vert_id, + AttributeValueIndex(num_normals_ + indices[2])); + } else if (norm_att_id_ >= 0) { + // 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)); } + if (material_att_id_ >= 0) { out_point_cloud_->attribute(material_att_id_) ->SetPointMapEntry(vert_id, AttributeValueIndex(last_material_id_)); diff --git a/io/obj_decoder_test.cc b/io/obj_decoder_test.cc index 45f0d89..2d0740b 100644 --- a/io/obj_decoder_test.cc +++ b/io/obj_decoder_test.cc @@ -48,5 +48,10 @@ TEST_F(ObjDecoderTest, ExtraVertexOBJ) { test_decoding(file_name); } +TEST_F(ObjDecoderTest, ParialAttributesOBJ) { + const std::string file_name = "cube_att_partial.obj"; + test_decoding(file_name); +} + } // namespace draco diff --git a/io/obj_encoder.cc b/io/obj_encoder.cc index 6ed7c2e..ad4920f 100644 --- a/io/obj_encoder.cc +++ b/io/obj_encoder.cc @@ -95,7 +95,7 @@ bool ObjEncoder::EncodePositions() { return false; buffer()->Encode("v ", 2); EncodeFloatList(&value[0], 3); - buffer()->Encode("\r\n", 2); + buffer()->Encode("\n", 1); } pos_att_ = att; return true; @@ -112,7 +112,7 @@ bool ObjEncoder::EncodeTextureCoordinates() { return false; buffer()->Encode("vt ", 3); EncodeFloatList(&value[0], 2); - buffer()->Encode("\r\n", 2); + buffer()->Encode("\n", 1); } tex_coord_att_ = att; return true; @@ -129,7 +129,7 @@ bool ObjEncoder::EncodeNormals() { return false; buffer()->Encode("vn ", 3); EncodeFloatList(&value[0], 3); - buffer()->Encode("\r\n", 2); + buffer()->Encode("\n", 1); } normal_att_ = att; return true; @@ -142,7 +142,7 @@ bool ObjEncoder::EncodeFaces() { if (!EncodeFaceCorner(i, j)) return false; } - buffer()->Encode("\r\n", 2); + buffer()->Encode("\n", 1); } return true; } diff --git a/io/ply_decoder.cc b/io/ply_decoder.cc index 3525d2a..6c6ee91 100644 --- a/io/ply_decoder.cc +++ b/io/ply_decoder.cc @@ -132,6 +132,11 @@ bool PlyDecoder::DecodeVertexData(const PlyElement *vertex_element) { // Decode vertex positions. { // TODO(ostava): For now assume the position types are float32. + if (x_prop->data_type() != DT_FLOAT32 || + y_prop->data_type() != DT_FLOAT32 || + z_prop->data_type() != DT_FLOAT32) { + return false; + } PlyPropertyReader x_reader(x_prop); PlyPropertyReader y_reader(y_prop); PlyPropertyReader z_reader(z_prop); @@ -149,6 +154,33 @@ bool PlyDecoder::DecodeVertexData(const PlyElement *vertex_element) { } } + // 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 x_reader(n_x_prop); + PlyPropertyReader y_reader(n_y_prop); + PlyPropertyReader 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 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"); diff --git a/io/ply_decoder_test.cc b/io/ply_decoder_test.cc index cbd95ca..f4eb3c5 100644 --- a/io/ply_decoder_test.cc +++ b/io/ply_decoder_test.cc @@ -32,10 +32,13 @@ class PlyDecoderTest : public ::testing::Test { } void test_decoding_method(const std::string &file_name, int num_faces, - uint32_t num_points) { - const std::unique_ptr mesh(DecodePly(file_name)); + uint32_t num_points, + std::unique_ptr *out_mesh) { + std::unique_ptr mesh(DecodePly(file_name)); ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name; ASSERT_EQ(mesh->num_faces(), num_faces); + if (out_mesh) + *out_mesh = std::move(mesh); const std::unique_ptr pc(DecodePly(file_name)); ASSERT_NE(pc, nullptr) << "Failed to load test model " << file_name; @@ -45,7 +48,18 @@ class PlyDecoderTest : public ::testing::Test { TEST_F(PlyDecoderTest, TestPlyDecoding) { const std::string file_name = "test_pos_color.ply"; - test_decoding_method(file_name, 224, 114); + test_decoding_method(file_name, 224, 114, nullptr); +} + +TEST_F(PlyDecoderTest, TestPlyNormals) { + const std::string file_name = "cube_att.ply"; + std::unique_ptr mesh; + test_decoding_method(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. } } // namespace draco diff --git a/javascript/example/DRACOLoader.js b/javascript/example/DRACOLoader.js index 0f0a0b9..f0c9b9c 100644 --- a/javascript/example/DRACOLoader.js +++ b/javascript/example/DRACOLoader.js @@ -75,7 +75,8 @@ THREE.DRACOLoader.prototype = { /* * Example on how to retrieve mesh and attributes. */ - let numFaces, numPoints, numVertexCoordinates, numTextureCoordinates, numAttributes; + let numFaces, numPoints; + let numVertexCoordinates, numTextureCoordinates, numAttributes; // For output basic geometry information. let geometryInfoStr; if (geometryType == DracoModule.TRIANGULAR_MESH) { @@ -137,9 +138,11 @@ THREE.DRACOLoader.prototype = { let textCoordAttributeData; if (texCoordAttId != -1) { geometryInfoStr += "\nLoaded texture coordinate attribute.\n"; - const texCoordAttribute = wrapper.GetAttribute(dracoGeometry, texCoordAttId); + const texCoordAttribute = wrapper.GetAttribute(dracoGeometry, + texCoordAttId); textCoordAttributeData = new DracoModule.DracoFloat32Array(); - wrapper.GetAttributeFloatForAllPoints(dracoGeometry, texCoordAttribute, + wrapper.GetAttributeFloatForAllPoints(dracoGeometry, + texCoordAttribute, textCoordAttributeData); } @@ -191,7 +194,7 @@ THREE.DRACOLoader.prototype = { DracoModule.destroy(norAttributeData); if (texCoordAttId != -1) DracoModule.destroy(textCoordAttributeData); - + // For mesh, we need to generate the faces. if (geometryType == DracoModule.TRIANGULAR_MESH) { const numIndices = numFaces * 3; diff --git a/point_cloud/point_attribute.cc b/point_cloud/point_attribute.cc index 5af8944..bcffc18 100644 --- a/point_cloud/point_attribute.cc +++ b/point_cloud/point_attribute.cc @@ -32,15 +32,17 @@ PointAttribute::PointAttribute(const GeometryAttribute &att) num_unique_entries_(0), identity_mapping_(false) {} -void PointAttribute::Reset(size_t num_attribute_values) { +bool PointAttribute::Reset(size_t num_attribute_values) { if (attribute_buffer_ == nullptr) { attribute_buffer_ = std::unique_ptr(new DataBuffer()); } const int64_t entry_size = DataTypeLength(data_type()) * components_count(); - attribute_buffer_->Update(nullptr, num_attribute_values * entry_size); + 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_ = num_attribute_values; + return true; } AttributeValueIndex::ValueType PointAttribute::DeduplicateValues( diff --git a/point_cloud/point_attribute.h b/point_cloud/point_attribute.h index 856a962..d603509 100644 --- a/point_cloud/point_attribute.h +++ b/point_cloud/point_attribute.h @@ -39,7 +39,7 @@ class PointAttribute : public GeometryAttribute { PointAttribute &operator=(PointAttribute &&attribute) = default; // Prepares the attribute storage for the specified number of entries. - void Reset(size_t num_attribute_values); + bool Reset(size_t num_attribute_values); size_t size() const { return num_unique_entries_; } AttributeValueIndex mapped_index(PointIndex point_index) const { diff --git a/testdata/cube_att.ply b/testdata/cube_att.ply new file mode 100644 index 0000000..ea280ef --- /dev/null +++ b/testdata/cube_att.ply @@ -0,0 +1,50 @@ +ply +format ascii 1.0 +comment VCGLIB generated +element vertex 24 +property float x +property float y +property float z +property float nx +property float ny +property float nz +element face 12 +property list uchar int vertex_indices +property list uchar float texcoord +end_header +0 0 0 -1 0 0 +0 0 1 0 0 1 +0 1 0 0 1 0 +0 1 1 0 1 0 +1 0 0 0 -1 0 +1 0 1 0 -1 0 +1 1 0 1 0 0 +1 1 1 1 0 0 +0 0 0 0 -1 0 +0 0 0 0 0 -1 +1 1 0 0 1 0 +1 1 0 0 0 -1 +1 0 0 1 0 0 +1 0 0 0 0 -1 +0 1 0 -1 0 0 +0 1 0 0 0 -1 +0 1 1 0 0 1 +0 1 1 -1 0 0 +0 0 1 0 -1 0 +0 0 1 -1 0 0 +1 1 1 0 0 1 +1 1 1 0 1 0 +1 0 1 0 0 1 +1 0 1 1 0 0 +3 9 11 13 6 1 1 0 0 0 1 +3 9 15 11 6 1 1 1 0 0 0 +3 0 17 14 6 0 1 1 0 0 0 +3 0 19 17 6 0 1 1 1 1 0 +3 2 21 10 6 0 0 1 1 1 0 +3 2 3 21 6 0 0 0 1 1 1 +3 12 6 7 6 1 1 1 0 0 0 +3 12 7 23 6 1 1 0 0 0 1 +3 8 4 5 6 0 1 1 1 1 0 +3 8 5 18 6 0 1 1 0 0 0 +3 1 22 20 6 0 1 1 1 1 0 +3 1 20 16 6 0 1 1 0 0 0 diff --git a/testdata/cube_att_partial.obj b/testdata/cube_att_partial.obj new file mode 100644 index 0000000..4534459 --- /dev/null +++ b/testdata/cube_att_partial.obj @@ -0,0 +1,35 @@ +# Attributes are only partially specified on the faces. + +v 0.0 0.0 0.0 +v 0.0 0.0 1.0 +v 0.0 1.0 0.0 +v 0.0 1.0 1.0 +v 1.0 0.0 0.0 +v 1.0 0.0 1.0 +v 1.0 1.0 0.0 +v 1.0 1.0 1.0 + +vn 0.0 0.0 1.0 +vn 0.0 0.0 -1.0 +vn 0.0 1.0 0.0 +vn 0.0 -1.0 0.0 +vn 1.0 0.0 0.0 +vn -1.0 0.0 0.0 + +vt 0.0 0.0 +vt 0.0 1.0 +vt 1.0 0.0 +vt 1.0 1.0 + +f 1/4 7/1 5/2 +f 1/4/2 3/3/2 7/1/2 +f 1/2 4/3 3/1 +f 1//6 2//6 4//6 +f 3/1/3 8/4/3 7/3/3 +f 3/1/3 4/2/3 8/4/3 +f 5//5 7//5 8//5 +f 5/4/5 8/1/5 6/2/5 +f 1/2/4 5/4/4 6/3/4 +f 1/2/4 6/3/4 2/1/4 +f 2 6 8 +f 2 8 4