Skip to content

Commit 69028c3

Browse files
authored
[MISC] Track visual overwrite by surface color as mesh metadata. (#2325)
1 parent 160bb61 commit 69028c3

File tree

3 files changed

+55
-15
lines changed

3 files changed

+55
-15
lines changed

genesis/engine/mesh.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,12 @@ def __init__(
6262
self._metadata: dict[str, Any] = metadata or {}
6363
self._color = np.array([1.0, 1.0, 1.0, 1.0], dtype=gs.np_float)
6464

65-
# By default, all meshes are considered zup, unless the "FileMorph.file_meshes_are_zup" option was set to False.
65+
# By default, all meshes are considered zup, unless the "FileMorph.file_meshes_are_zup" option was set to False
6666
self._metadata.setdefault("imported_as_zup", True)
6767

68+
# By default, all meshes are considered having their original visual
69+
self._metadata.setdefault("is_visual_overwritten", False)
70+
6871
if not is_mesh_zup:
6972
if self._metadata["imported_as_zup"]:
7073
self._mesh.apply_transform(mu.Y_UP_TRANSFORM.T)
@@ -245,6 +248,7 @@ def from_trimesh(
245248
# Visual may not have uv, e.g. ColorVisuals
246249
uvs = None
247250

251+
metadata = metadata or {}
248252
must_update_surface = True
249253
roughness_factor = None
250254
color_image = None
@@ -287,14 +291,14 @@ def from_trimesh(
287291
else:
288292
# TODO: support vertex/face colors in luisa
289293
color_factor = tuple(np.array(visual.main_color, dtype=np.float32) / 255.0)
294+
elif surface.color is not None:
295+
color_factor = surface.color
296+
metadata["is_visual_overwritten"] = True
290297
elif (isinstance(visual, trimesh.visual.color.ColorVisuals) and visual.defined) or (
291298
isinstance(visual, trimesh.visual.color.VertexColor) and visual.vertex_colors.size > 0
292299
):
293-
# Color is already vertex-based. No need to create a new texture to keep the original one, unless a color
294-
# overwrite has been specified as surface-level.
295-
must_update_surface = surface.color is not None
296-
elif surface.color is not None:
297-
color_factor = surface.color
300+
# Color is already vertex-based. It is not only necessary to create a new visual.
301+
must_update_surface = False
298302
else:
299303
# use white color as default
300304
color_factor = (1.0, 1.0, 1.0, 1.0)
@@ -336,14 +340,20 @@ def from_attrs(
336340
if surface is None:
337341
surface = gs.surfaces.Default()
338342

343+
metadata = metadata or {}
344+
metadata["is_visual_overwritten"] = metadata.get("is_visual_overwritten", False) or (surface.color is not None)
345+
visual = mu.surface_uvs_to_trimesh_visual(surface, uvs, len(verts))
346+
347+
tmesh = trimesh.Trimesh(
348+
vertices=verts,
349+
faces=faces,
350+
vertex_normals=normals,
351+
visual=visual,
352+
process=False,
353+
)
354+
339355
return cls(
340-
mesh=trimesh.Trimesh(
341-
vertices=verts,
342-
faces=faces,
343-
vertex_normals=normals,
344-
visual=mu.surface_uvs_to_trimesh_visual(surface, uvs, len(verts)),
345-
process=False,
346-
),
356+
mesh=tmesh,
347357
surface=surface,
348358
uvs=uvs,
349359
scale=scale,
@@ -402,6 +412,7 @@ def update_trimesh_visual(self):
402412
Update the trimesh obj's visual attributes using its surface and uvs.
403413
"""
404414
self._mesh.visual = mu.surface_uvs_to_trimesh_visual(self.surface, self.uvs, len(self.verts))
415+
self._metadata["is_visual_overwritten"] = True
405416

406417
def apply_transform(self, T):
407418
"""

genesis/utils/urdf.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ def parse_urdf(morph, surface):
117117
scale *= geometry.scale
118118

119119
# Overwrite surface color by original color specified in URDF file only if necessary
120+
is_urdf_material = False
120121
if geom_is_col:
121122
geom_surface = gs.surfaces.Collision()
122123
elif (
@@ -125,6 +126,7 @@ def parse_urdf(morph, surface):
125126
and geom_prop.material.color is not None
126127
and (morph.prioritize_urdf_material or surface.color is None)
127128
):
129+
is_urdf_material = True
128130
geom_surface = surface.copy()
129131
geom_surface.color = geom_prop.material.color
130132
else:
@@ -145,6 +147,9 @@ def parse_urdf(morph, surface):
145147
is_mesh_zup=morph.file_meshes_are_zup,
146148
metadata={"mesh_path": mesh_path},
147149
)
150+
if is_urdf_material:
151+
# Material color defined in URDF are not considered as visual overwrite
152+
mesh.metadata["is_visual_overwritten"] = False
148153

149154
g_info = {"mesh" if geom_is_col else "vmesh": mesh}
150155
link_g_infos_.append(g_info)

tests/test_rigid_physics.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2711,11 +2711,12 @@ def test_urdf_capsule(tmp_path, show_viewer, tol):
27112711
@pytest.mark.required
27122712
@pytest.mark.required
27132713
@pytest.mark.parametrize("overwrite", [False, True])
2714-
def test_urdf_color_overwrite(overwrite):
2715-
scene = gs.Scene()
2714+
def test_urdf_color_overwrite(overwrite, show_viewer):
2715+
scene = gs.Scene(show_viewer=show_viewer)
27162716
box = scene.add_entity(
27172717
gs.morphs.URDF(
27182718
file="genesis/assets/urdf/blue_box/model.urdf",
2719+
convexify=False,
27192720
),
27202721
surface=gs.surfaces.Default(
27212722
color=(1.0, 0.0, 0.0, 1.0) if overwrite else None,
@@ -2724,26 +2725,49 @@ def test_urdf_color_overwrite(overwrite):
27242725
axis = scene.add_entity(
27252726
gs.morphs.Mesh(
27262727
file="meshes/axis.obj",
2728+
convexify=False,
27272729
),
27282730
surface=gs.surfaces.Default(
27292731
color=(1.0, 0.0, 0.0, 1.0) if overwrite else None,
27302732
),
27312733
)
2734+
asset_path = get_hf_dataset(pattern="work_table.glb")
2735+
table = scene.add_entity(
2736+
gs.morphs.Mesh(
2737+
file=f"{asset_path}/work_table.glb",
2738+
convexify=False,
2739+
),
2740+
surface=gs.surfaces.Default(
2741+
color=(1.0, 0.0, 0.0, 1.0) if overwrite else None,
2742+
),
2743+
)
2744+
if show_viewer:
2745+
scene.build()
27322746
for vgeom in box.vgeoms:
2747+
assert vgeom.vmesh.metadata["is_visual_overwritten"] == overwrite
27332748
visual = vgeom.vmesh.trimesh.visual
27342749
assert visual.defined
27352750
color = np.unique(visual.vertex_colors, axis=0)
27362751
assert_array_equal(color, (255, 0, 0, 255) if overwrite else (0, 0, 255, 255))
27372752
for vgeom in axis.vgeoms:
2753+
assert vgeom.vmesh.metadata["is_visual_overwritten"] == overwrite
27382754
visual = vgeom.vmesh.trimesh.visual
27392755
assert visual.defined
27402756
color = np.unique(visual.vertex_colors, axis=0)
27412757
if overwrite:
27422758
assert_array_equal(color, (255, 0, 0, 255))
27432759
else:
27442760
assert_array_equal(color, [[0, 0, 178, 255], [0, 178, 0, 255], [178, 0, 0, 255], [255, 255, 255, 255]])
2761+
for vgeom in table.vgeoms:
2762+
assert vgeom.vmesh.metadata["is_visual_overwritten"] == overwrite
2763+
visual = vgeom.vmesh.trimesh.visual
2764+
assert visual.defined
2765+
if overwrite:
2766+
color = np.unique(visual.vertex_colors, axis=0)
2767+
assert_array_equal(color, (255, 0, 0, 255))
27452768
for entity in scene.entities:
27462769
for geom in entity.geoms:
2770+
assert geom.mesh.metadata["is_visual_overwritten"]
27472771
visual = geom.mesh.trimesh.visual
27482772
assert visual.defined
27492773
color = np.unique(visual.vertex_colors, axis=0)

0 commit comments

Comments
 (0)