Skip to content
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

Decode texture coordinates #39

Merged
merged 4 commits into from
Mar 27, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
decode texture coordinates
inspired by PR#17 which has diverged
denisri committed Mar 3, 2023
commit 51819d12d868d7322da9c632cd6d5777558bd734
4,814 changes: 2,469 additions & 2,345 deletions src/DracoPy.cpp

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions src/DracoPy.h
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ namespace DracoFunctions {
successful,
not_draco_encoded,
no_position_attribute,
no_tex_coord_attribute,
no_normal_coord_attribute,
failed_during_decoding
};
@@ -45,6 +46,7 @@ namespace DracoFunctions {
struct MeshObject : PointCloudObject {
std::vector<float> normals;
std::vector<unsigned int> faces;
std::vector<float> tex_coord;
};

struct EncodedObject {
@@ -146,6 +148,24 @@ namespace DracoFunctions {
meshObject.colors_set = false;
}

const int tex_att_id = mesh->GetNamedAttributeId(draco::GeometryAttribute::TEX_COORD);
if (tex_att_id >= 0) {
const auto *const tex_att = mesh->attribute(tex_att_id);
const int tex_channel = tex_att->num_components();
meshObject.tex_coord.reserve(tex_channel * mesh->num_points());
float* tex_val = new float[tex_channel];
for (draco::PointIndex v(0); v < mesh->num_points(); ++v) {
if (!tex_att->ConvertValue<float>(tex_att->mapped_index(v), tex_channel, tex_val)) {
break; // it already failed
} else {
for (int i = 0; i < tex_channel; ++i) {
meshObject.tex_coord.push_back(tex_val[i]);
}
}
}
delete [] tex_val;
}

const draco::GeometryMetadata *metadata = mesh->GetMetadata();
meshObject.encoding_options_set = false;
if (metadata) {
2 changes: 1 addition & 1 deletion src/DracoPy.pxd
Original file line number Diff line number Diff line change
@@ -33,8 +33,8 @@ cdef extern from "DracoPy.h" namespace "DracoFunctions":
vector[float] points
vector[unsigned int] faces

# TODO: add support for normals, which are not currently supported.
vector[float] normals
vector[float] tex_coord

# Encoding options
bool encoding_options_set
11 changes: 10 additions & 1 deletion src/DracoPy.pyx
Original file line number Diff line number Diff line change
@@ -67,6 +67,15 @@ class DracoMesh(DracoPointCloud):
N = len(normals_) // 3
return np.array(normals_).reshape((N,3))

@property
def tex_coord(self):
tex_coord_ = self.data_struct['tex_coord']
if len(tex_coord_) == 0:
return np.array([], dtype=tex_coord_.dtype)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This array will have the wrong shape. It needs to be reshaped.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting point (well, two points actually).

  • Which shape is actually expected for texture corrdinates ?
    Using it this way, for a mesh with a texure with 2 coordinates per vertex, I actually get an array of shape (NV, 2) (where NV is the number of vertices). This is consistent with points or normals arrays which are (NV, 3) for instance. But here I get "flipped" coordinates (both coordinates are switched) compared to the initial non-draco-encoded mesh, but as I don't know Draco at all I am not sure whether it is expected or not, and why. Is it what you were pointing out ? And thus it is not really a "reshape", but I need to do: tex_corrd[:, -1::-1] to get back the expected coordinates. Should I include this operation in the tex_coord property here (and is there a reason for this ?) ?
  • in the situation where there are no texture coords, here I return an empty array of shape (0, ). Maybe it's not the best and I shoudl return None instead, or an array of shape (0, 0) ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to return the tex coordinates in the same order as the original model.

For the second point, I think it makes sense to return None if the model has no texture attribute, but if it is labeled as having one, then (0,0) would be preferred.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK it now returns None when there are no texture coords. And testing again I don't find the coordinates inversions I had when testing at first. So I must have done something wrong, but it seems to work as expected now (otherwise there is something I don't understand)

N = len(self.data_struct['points']) // 3
NC = len(tex_coord_) // N
return np.array(tex_coord_).reshape((N, NC))

class EncodingOptions(object):
def __init__(self, quantization_bits, quantization_range, quantization_origin):
self.quantization_bits = quantization_bits
@@ -254,4 +263,4 @@ def decode_buffer_to_mesh(buffer) -> Union[DracoMesh, DracoPointCloud]:

def decode_buffer_to_point_cloud(buffer) -> Union[DracoMesh, DracoPointCloud]:
"""Provided for backwards compatibility. Use decode."""
return cast(decode(buffer), DracoPointCloud)
return cast(decode(buffer), DracoPointCloud)