diff --git a/src/draco/io/ply_decoder.cc b/src/draco/io/ply_decoder.cc index b78c0569..52c911f2 100644 --- a/src/draco/io/ply_decoder.cc +++ b/src/draco/io/ply_decoder.cc @@ -70,8 +70,11 @@ Status PlyDecoder::DecodeInternal() { PlyReader ply_reader; DRACO_RETURN_IF_ERROR(ply_reader.Read(buffer())); // First, decode the connectivity data. - if (out_mesh_) - DRACO_RETURN_IF_ERROR(DecodeFaceData(ply_reader.GetElementByName("face"))); + if (out_mesh_) { + const PlyElement *face_element = ply_reader.GetElementByName("face"); + DRACO_RETURN_IF_ERROR(DecodeFaceData(face_element)); + DRACO_RETURN_IF_ERROR(DecodeTexCoordData(face_element)); + } // Decode all attributes. DRACO_RETURN_IF_ERROR( DecodeVertexData(ply_reader.GetElementByName("vertex"))); @@ -136,6 +139,65 @@ Status PlyDecoder::DecodeFaceData(const PlyElement *face_element) { return OkStatus(); } +Status PlyDecoder::DecodeTexCoordData(const PlyElement *face_element) { + if (face_element == nullptr) { + return Status(Status::INVALID_PARAMETER, "face_element is null"); + } + const PlyProperty *texture_coordinates = + face_element->GetPropertyByName("texcoord"); + if (texture_coordinates == nullptr || !texture_coordinates->is_list()) { + return OkStatus(); + } + + // Allocate attribute for texture coordinates. + // There is one pair of texture coordinates per triangle corner. + const int num_corners = out_mesh_->num_faces() * 3; + + + std::unique_ptr attr(new PointAttribute()); + attr->Init(GeometryAttribute::TEX_COORD, 2, DT_FLOAT32, false, num_corners); + + PlyPropertyReader uv_reader(texture_coordinates); + AttributeValueIndex corner_index(0); + + auto pushTexcoordPair = [uv_reader, &corner_index, &attr] + (uint64_t offset, uint64_t index) -> void { + float uv_value[2]; + uv_value[0] = uv_reader.ReadValue(static_cast(offset + index * 2)); + uv_value[1] = uv_reader.ReadValue(static_cast(offset + index * 2 + 1)); + attr->SetAttributeValue(corner_index, uv_value); + corner_index++; + }; + + const int64_t num_polygons = face_element->num_entries(); + for (int i = 0; i < num_polygons; ++i) { + const int64_t uv_list_offset = texture_coordinates->GetListEntryOffset(i); + const int64_t uv_list_size = texture_coordinates->GetListEntryNumValues(i); + if (uv_list_size < 6 || uv_list_size % 2 != 0) { + continue; // Need at least three pairs of UV coordinates per polygon. + } + + // Triangulate polygon assuming the polygon is convex. + const int64_t num_triangles = uv_list_size / 2 - 2; + + for (int64_t ti = 0; ti < num_triangles; ++ti) { + pushTexcoordPair(uv_list_offset, 0); + for (int64_t c = 1; c < 3; ++c) { + pushTexcoordPair(uv_list_offset, ti + c); + } + } + } + + IndexTypeVector corner_map(num_corners); + for (CornerIndex ci(0); ci < num_corners; ++ci) { + corner_map[ci] = AttributeValueIndex(ci.value()); + } + // I don't think it works to have this as the only point-mapped attribute + // while the rest of the attributes are identity-mapped. + out_mesh_->AddAttributeWithConnectivity(std::move(attr), corner_map); + return OkStatus(); +} + template bool PlyDecoder::ReadPropertiesToAttribute( const std::vector &properties, diff --git a/src/draco/io/ply_decoder.h b/src/draco/io/ply_decoder.h index db1e480c..ff98f39a 100644 --- a/src/draco/io/ply_decoder.h +++ b/src/draco/io/ply_decoder.h @@ -49,6 +49,8 @@ class PlyDecoder { private: Status DecodeFaceData(const PlyElement *face_element); + Status DecodeTexCoordData(const PlyElement *face_element); + Status DecodeVertexData(const PlyElement *vertex_element); template diff --git a/src/draco/io/ply_decoder_test.cc b/src/draco/io/ply_decoder_test.cc index 1dd70d5c..63b085a2 100644 --- a/src/draco/io/ply_decoder_test.cc +++ b/src/draco/io/ply_decoder_test.cc @@ -77,6 +77,21 @@ TEST_F(PlyDecoderTest, TestPlyNormals) { ASSERT_EQ(att->size(), 6); // 6 unique normal values. } +TEST_F(PlyDecoderTest, TestPlyTexCoords) { + const std::string file_name = "cube_att.ply"; + std::unique_ptr mesh; + test_decoding(file_name, 12, 3 * 8, &mesh); + ASSERT_NE(mesh, nullptr); + const int att_id = mesh->GetNamedAttributeId(GeometryAttribute::TEX_COORD); + ASSERT_GE(att_id, 0); + const PointAttribute *const att = mesh->attribute(att_id); + ASSERT_EQ(att->size(), 4); // 4 unique texture coordinate values. + float vertex_0_tex_coord[2]; + att->GetValue(AttributeValueIndex(0), vertex_0_tex_coord); + ASSERT_EQ(vertex_0_tex_coord[0], 1); + ASSERT_EQ(vertex_0_tex_coord[1], 1); +} + TEST_F(PlyDecoderTest, TestPlyDecodingAll) { // test if we can read all ply that are currently in test folder. test_decoding("bun_zipper.ply"); diff --git a/src/draco/mesh/mesh.cc b/src/draco/mesh/mesh.cc index b287ecb4..0628d1de 100644 --- a/src/draco/mesh/mesh.cc +++ b/src/draco/mesh/mesh.cc @@ -68,6 +68,7 @@ void Mesh::Copy(const Mesh &src) { // Copy structural metadata. structural_metadata_.Copy(src.structural_metadata_); } +#endif namespace { // A helper struct that augments a point index with an attribute value index. @@ -182,6 +183,7 @@ int32_t Mesh::AddAttributeWithConnectivity( return PointCloud::AddAttribute(std::move(att)); } +#ifdef DRACO_TRANSCODER_SUPPORTED int32_t Mesh::AddPerVertexAttribute(std::unique_ptr att) { const PointAttribute *const pos_att = GetNamedAttribute(GeometryAttribute::POSITION); diff --git a/src/draco/mesh/mesh.h b/src/draco/mesh/mesh.h index 652c2c01..f705b1f8 100644 --- a/src/draco/mesh/mesh.h +++ b/src/draco/mesh/mesh.h @@ -96,7 +96,6 @@ class Mesh : public PointCloud { } } -#ifdef DRACO_TRANSCODER_SUPPORTED // Adds a point attribute |att| to the mesh and returns the index of the // newly inserted attribute. Attribute connectivity data is specified in // |corner_to_value| array that contains mapping between face corners and @@ -108,6 +107,7 @@ class Mesh : public PointCloud { std::unique_ptr att, const IndexTypeVector &corner_to_value); +#ifdef DRACO_TRANSCODER_SUPPORTED // Adds a point attribute |att| to the mesh and returns the index of the // newly inserted attribute. The inserted attribute must have the same // connectivity as the position attribute of the mesh (that is, the attribute