-
Notifications
You must be signed in to change notification settings - Fork 976
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for per-face texture coordinates in the PLY decoder. #1028
base: main
Are you sure you want to change the base?
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<PointAttribute> attr(new PointAttribute()); | ||
attr->Init(GeometryAttribute::TEX_COORD, 2, DT_FLOAT32, false, num_corners); | ||
|
||
PlyPropertyReader<float> 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<int>(offset + index * 2)); | ||
uv_value[1] = uv_reader.ReadValue(static_cast<int>(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; | ||
pushTexcoordPair(uv_list_offset, 0); | ||
for (int64_t ti = 0; ti < num_triangles; ++ti) { | ||
for (int64_t c = 1; c < 3; ++c) { | ||
pushTexcoordPair(uv_list_offset, ti + c); | ||
} | ||
} | ||
} | ||
|
||
IndexTypeVector<CornerIndex, AttributeValueIndex> 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_->set_num_points(num_corners); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the number of points should be recomputed automatically when the function is called. I think this may actually cause some issues in the subsequent function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've made this change, but it crashes because the I've tried setting the number of points here to the number of corners, and the number of vertices. Nothing works; the program either crashes or fails non-deterministically at various points. |
||
out_mesh_->AddAttributeWithConnectivity(std::move(attr), corner_map); | ||
return OkStatus(); | ||
} | ||
|
||
template <typename DataTypeT> | ||
bool PlyDecoder::ReadPropertiesToAttribute( | ||
const std::vector<const PlyProperty *> &properties, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this would add one value for each corner, not to mention the corner index seems to be wrong.
Basically you would have to add value for each corner where the corner index is (3 * triangle_index + c) where c={0, 1, 2}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, fixed. This moves through vertices in the same order as in
DecodeFaceData
. When triangulating a polygon with more than 3 sides, the first vertex is repeated in each triangle.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that the first vertex is repeated but even so, each triangle of the polygon will have a distinct corner attached to the "first" vertex. E.g. if you have a quad there will be two corners attached to it. In the current implementation only the corner from the first triangle is going to be processed which is wrong because it messes up the way how Draco maps corners to triangles + it leaves some corners with uninitialized values. Again, as an example for a quad, we would expect to have two triangles with corners: (0, 1, 2), (3, 4, 5) where they would be mapped to values (0, 1, 2), (0, 2, 3).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to make sure, are you looking at the change from the latest commit? eee2335
For a quad with corners (0, 1, 2, 3), this should push 6 pairs of UVs in the order (0, 1, 2, 0, 2, 3). Is that not correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry about that. Not sure what I was looking at, you are right.
One other potential issue I can see is that, if I understand it correctly, the DecodeTexCoordData() is called before we decode any vertex data. I think this may cause some issues because AddAttributeWithConnectivity assumes that we have some valid points in the mesh already. Can you try to move this decoding after DecodeVertexData() to see if it helps?