Skip to content

Commit e93d8b5

Browse files
committed
Add support for per-face texture coordinates in the PLY decoder.
1 parent 9f856ab commit e93d8b5

File tree

3 files changed

+93
-6
lines changed

3 files changed

+93
-6
lines changed

src/draco/io/ply_decoder.cc

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,20 @@ Status PlyDecoder::DecodeFromBuffer(DecoderBuffer *buffer,
6969
Status PlyDecoder::DecodeInternal() {
7070
PlyReader ply_reader;
7171
DRACO_RETURN_IF_ERROR(ply_reader.Read(buffer()));
72+
73+
const PlyElement *vertex_element = ply_reader.GetElementByName("vertex");
74+
if (vertex_element == nullptr) {
75+
return Status(Status::DRACO_ERROR, "no vertex element");
76+
}
77+
7278
// First, decode the connectivity data.
73-
if (out_mesh_)
74-
DRACO_RETURN_IF_ERROR(DecodeFaceData(ply_reader.GetElementByName("face")));
79+
if (out_mesh_) {
80+
const int num_vertices = vertex_element->num_entries();
81+
DRACO_RETURN_IF_ERROR(DecodeFaceData(ply_reader.GetElementByName("face"),
82+
num_vertices));
83+
}
7584
// Decode all attributes.
76-
DRACO_RETURN_IF_ERROR(
77-
DecodeVertexData(ply_reader.GetElementByName("vertex")));
85+
DRACO_RETURN_IF_ERROR(DecodeVertexData(vertex_element));
7886
// In case there are no faces this is just a point cloud which does
7987
// not require deduplication.
8088
if (out_mesh_ && out_mesh_->num_faces() != 0) {
@@ -91,7 +99,7 @@ Status PlyDecoder::DecodeInternal() {
9199
return OkStatus();
92100
}
93101

94-
Status PlyDecoder::DecodeFaceData(const PlyElement *face_element) {
102+
Status PlyDecoder::DecodeFaceData(const PlyElement *face_element, const int num_vertices) {
95103
// We accept point clouds now.
96104
if (face_element == nullptr) {
97105
return Status(Status::INVALID_PARAMETER, "face_element is null");
@@ -133,6 +141,65 @@ Status PlyDecoder::DecodeFaceData(const PlyElement *face_element) {
133141
}
134142
}
135143
out_mesh_->SetNumFaces(face_index.value());
144+
145+
DecodeFaceTexCoordData(face_element, vertex_indices, num_vertices);
146+
147+
return OkStatus();
148+
}
149+
150+
// Decodes per-face texture coordinate data into the TEX_COORD attribute.
151+
// When texture coordinates are stored on faces, the same vertex may have
152+
// multiple sets of texture coordinates from different faces. This function
153+
// rewrites the texture coordinates every time the same vertex is encountered
154+
// on a new face, effectively choosing the last coordinate pair encountered
155+
// for each vertex.
156+
Status PlyDecoder::DecodeFaceTexCoordData(
157+
const PlyElement *face_element,
158+
const PlyProperty *vertex_indices,
159+
const int num_vertices) {
160+
if (face_element == nullptr) {
161+
return Status(Status::INVALID_PARAMETER, "face_element is null");
162+
}
163+
if (vertex_indices == nullptr) {
164+
return Status(Status::INVALID_PARAMETER, "vertex_indices is null");
165+
}
166+
167+
const PlyProperty *texture_coordinates =
168+
face_element->GetPropertyByName("texcoord");
169+
if (!texture_coordinates) {
170+
return OkStatus(); // No texture coordinates to decode.
171+
}
172+
173+
// Allocate attribute for texture coordinates.
174+
GeometryAttribute uv_attr;
175+
uv_attr.Init(GeometryAttribute::TEX_COORD, nullptr, 2, DT_FLOAT32, false, sizeof(float) * 2, 0);
176+
const int uv_att_id = out_point_cloud_->AddAttribute(uv_attr, true, num_vertices);
177+
178+
const int64_t num_polygons = face_element->num_entries();
179+
PlyPropertyReader<float> uv_reader(texture_coordinates);
180+
PlyPropertyReader<PointIndex::ValueType> vertex_index_reader(vertex_indices);
181+
182+
for (int64_t face_index = 0; face_index < num_polygons; ++face_index) {
183+
const int64_t vertex_list_offset = vertex_indices->GetListEntryOffset(face_index);
184+
const int64_t vertex_list_size = vertex_indices->GetListEntryNumValues(face_index);
185+
186+
const int64_t uv_list_offset = texture_coordinates->GetListEntryOffset(face_index);
187+
const int64_t uv_list_size = texture_coordinates->GetListEntryNumValues(face_index);
188+
189+
if (uv_list_size < 2 * vertex_list_size) {
190+
continue; // Skip invalid uv list. Must have two texture coords per vertex.
191+
}
192+
193+
for (int64_t i = 0; i < vertex_list_size; ++i) {
194+
uint32_t vertex_index = vertex_index_reader.ReadValue(static_cast<int>(vertex_list_offset + i));
195+
float uv_value[2];
196+
uv_value[0] = uv_reader.ReadValue(static_cast<int>(uv_list_offset + i * 2));
197+
uv_value[1] = uv_reader.ReadValue(static_cast<int>(uv_list_offset + i * 2 + 1));
198+
out_point_cloud_->attribute(uv_att_id)->SetAttributeValue(
199+
AttributeValueIndex(vertex_index), uv_value);
200+
}
201+
}
202+
136203
return OkStatus();
137204
}
138205

src/draco/io/ply_decoder.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ class PlyDecoder {
4848
DecoderBuffer *buffer() { return &buffer_; }
4949

5050
private:
51-
Status DecodeFaceData(const PlyElement *face_element);
51+
Status DecodeFaceData(const PlyElement *face_element,
52+
const int num_vertices);
53+
Status DecodeFaceTexCoordData(
54+
const PlyElement *face_element,
55+
const PlyProperty *vertex_indices,
56+
const int num_vertices);
5257
Status DecodeVertexData(const PlyElement *vertex_element);
5358

5459
template <typename DataTypeT>

src/draco/io/ply_decoder_test.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,21 @@ TEST_F(PlyDecoderTest, TestPlyNormals) {
7777
ASSERT_EQ(att->size(), 6); // 6 unique normal values.
7878
}
7979

80+
TEST_F(PlyDecoderTest, TestPlyTexCoords) {
81+
const std::string file_name = "cube_att.ply";
82+
std::unique_ptr<Mesh> mesh;
83+
test_decoding(file_name, 12, 3 * 8, &mesh);
84+
ASSERT_NE(mesh, nullptr);
85+
const int att_id = mesh->GetNamedAttributeId(GeometryAttribute::TEX_COORD);
86+
ASSERT_GE(att_id, 0);
87+
const PointAttribute *const att = mesh->attribute(att_id);
88+
ASSERT_EQ(att->size(), 4); // 4 unique texture coordinate values.
89+
float vertex_0_tex_coord[2];
90+
att->GetValue(AttributeValueIndex(0), vertex_0_tex_coord);
91+
ASSERT_EQ(vertex_0_tex_coord[0], 0);
92+
ASSERT_EQ(vertex_0_tex_coord[1], 1);
93+
}
94+
8095
TEST_F(PlyDecoderTest, TestPlyDecodingAll) {
8196
// test if we can read all ply that are currently in test folder.
8297
test_decoding("bun_zipper.ply");

0 commit comments

Comments
 (0)