Add support for parsing 'st' primvar from USD.

PiperOrigin-RevId: 786277487
Change-Id: Id99e6bf6a656b623950c363597da3930ee4c00e3
This commit is contained in:
Sam Haves
2025-07-23 07:46:53 -07:00
committed by Copybara-Service
parent 4f43a7d08e
commit 8383bd439c
+63 -3
View File
@@ -264,10 +264,13 @@ bool MaybeParseGeomPrimitive(const pxr::UsdPrim& prim, T* element,
return true;
}
mjsMesh* ParseUsdMesh(mjSpec* spec, const pxr::UsdPrim& prim, mjsGeom* geom, pxr::UsdGeomXformCache& xform_cache) {
mjsMesh* ParseUsdMesh(mjSpec* spec, const pxr::UsdPrim& prim, mjsGeom* geom,
pxr::UsdGeomXformCache& xform_cache) {
if (!prim.IsA<pxr::UsdGeomMesh>()) {
return nullptr;
}
mjsMesh* mesh = mjs_addMesh(spec, nullptr);
geom->type = mjGEOM_MESH;
pxr::UsdGeomMesh usd_mesh(prim);
std::vector<float> uservert;
@@ -276,6 +279,15 @@ mjsMesh* ParseUsdMesh(mjSpec* spec, const pxr::UsdPrim& prim, mjsGeom* geom, pxr
pxr::VtVec3fArray points;
usd_mesh.GetPointsAttr().Get(&points);
pxr::VtVec2fArray uvs;
pxr::VtIntArray uv_indices;
std::vector<float> texcoord;
std::vector<int> userfacetexcoord;
pxr::UsdGeomPrimvarsAPI primvarsAPI(prim);
pxr::UsdGeomPrimvar st_primvar =
primvarsAPI.FindPrimvarWithInheritance(pxr::TfToken("st"));
uservert.reserve(points.size() * 3);
for (const auto& pt : points) {
uservert.push_back(pt[0]);
@@ -283,6 +295,40 @@ mjsMesh* ParseUsdMesh(mjSpec* spec, const pxr::UsdPrim& prim, mjsGeom* geom, pxr
uservert.push_back(pt[2]);
}
bool has_uvs = st_primvar.HasAuthoredValue();
bool face_varying_uvs = has_uvs && st_primvar.GetInterpolation() ==
pxr::UsdGeomTokens->faceVarying;
bool st_indexed = st_primvar.IsIndexed();
if (has_uvs) {
pxr::VtVec2fArray uvs;
st_primvar.Get(&uvs);
if (st_indexed) {
st_primvar.GetIndices(&uv_indices);
}
// If the UVs do not vary per point per face, then we need to compute the
// effective uv array taking indexing into account as we do not support UV
// indexing in mujoco.
if (!face_varying_uvs && st_primvar.IsIndexed()) {
texcoord.reserve(uv_indices.size() * 2);
for (const auto& idx : uv_indices) {
auto uv = uvs[idx];
texcoord.push_back(uv[0]);
// USD origin is bottom left, MuJoCo is top left.
texcoord.push_back(1.0f - uv[1]);
}
} else {
texcoord.reserve(uvs.size() * 2);
for (const auto& uv : uvs) {
texcoord.push_back(uv[0]);
// USD origin is bottom left, MuJoCo is top left.
texcoord.push_back(1.0f - uv[1]);
}
}
mjs_setFloat(mesh->usertexcoord, texcoord.data(), texcoord.size());
}
pxr::VtIntArray indices;
usd_mesh.GetFaceVertexIndicesAttr().Get(&indices);
pxr::VtIntArray counts;
@@ -298,13 +344,22 @@ mjsMesh* ParseUsdMesh(mjSpec* spec, const pxr::UsdPrim& prim, mjsGeom* geom, pxr
userface.push_back(indices[vtx_idx]);
userface.push_back(indices[vtx_idx + k]);
userface.push_back(indices[vtx_idx + k + 1]);
// If the UVs vary per face, then we need to compute the effective
// index array after we've created the triangle fan for non triangular
// faces.
if (face_varying_uvs) {
userfacetexcoord.push_back(st_indexed ? uv_indices[vtx_idx] : vtx_idx);
userfacetexcoord.push_back(st_indexed ? uv_indices[vtx_idx + k]
: vtx_idx + k);
userfacetexcoord.push_back(st_indexed ? uv_indices[vtx_idx + k + 1]
: vtx_idx + k + 1);
}
k++;
}
vtx_idx += count;
}
mjsMesh* mesh = mjs_addMesh(spec, nullptr);
auto world_xform = xform_cache.GetLocalToWorldTransform(prim);
auto scale = GetScale(world_xform);
mesh->scale[0] = scale[0];
@@ -316,6 +371,11 @@ mjsMesh* ParseUsdMesh(mjSpec* spec, const pxr::UsdPrim& prim, mjsGeom* geom, pxr
mjs_setFloat(mesh->uservert, uservert.data(), uservert.size());
mjs_setInt(mesh->userface, userface.data(), userface.size());
if (face_varying_uvs) {
mjs_setInt(mesh->userfacetexcoord, userfacetexcoord.data(),
userfacetexcoord.size());
}
mjs_setString(geom->meshname, mesh_name.c_str());
return mesh;
}