Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
28 changes: 25 additions & 3 deletions genesis/options/textures.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,34 @@ def __init__(self, **data):

elif self.image_array is not None:
if not isinstance(self.image_array, np.ndarray):
gs.raise_exception("`image_array` needs to be an numpy array.")
gs.raise_exception("`image_array` needs to be a numpy array.")
arr = self.image_array
if arr.dtype != np.uint8:
if np.issubdtype(arr.dtype, np.floating):
if arr.max() <= 1.0:
arr = (arr * 255.0).round()
arr = np.clip(arr, 0.0, 255.0).astype(np.uint8)
elif arr.dtype == np.bool_:
arr = arr.astype(np.uint8) * 255
elif np.issubdtype(arr.dtype, np.integer):
arr = np.clip(arr, 0, 255).astype(np.uint8)
else:
gs.raise_exception(
f"Unsupported image dtype {arr.dtype}. "
"Only uint8, integer, floating-point, or bool types are supported."
)
self.image_array = arr

# calculate channel
if self.image_array is None:
if isinstance(self.resolution, (tuple, list)):
H, W = self.resolution
else:
H = W = self.resolution

# Default to 3-channel RGB
white = np.array([255, 255, 255], dtype=np.uint8)
self.image_array = np.full((H, W, 3), white, dtype=np.uint8)
self._mean_color = np.array([1.0, 1.0, 1.0], dtype=np.float16)
self._channel = 3
else:
Expand All @@ -163,8 +187,6 @@ def __init__(self, **data):
if self.encoding not in ["srgb", "linear"]:
gs.raise_exception(f"Invalid image encoding: {self.encoding}.")

assert self.image_array is None or self.image_array.dtype == np.uint8

def check_dim(self, dim):
if self.image_array is not None:
if self._channel > dim:
Expand Down
80 changes: 75 additions & 5 deletions tests/test_mesh.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import os
from pathlib import Path
import numpy as np
import pytest
import trimesh
import numpy as np
import os
from huggingface_hub import snapshot_download

import genesis as gs
import genesis.utils.mesh as mu
import genesis.utils.gltf as gltf_utils
import genesis.utils.mesh as mu
import genesis.utils.usda as usda_utils

from .utils import get_hf_assets, assert_allclose, assert_array_equal
from .utils import assert_allclose, assert_array_equal, get_hf_assets

VERTICES_TOL = 1e-05 # Transformation loses a little precision in vertices
NORMALS_TOL = 1e-02 # Conversion from .usd to .glb loses a little precision in normals
Expand Down Expand Up @@ -292,3 +292,73 @@ def test_usd_parse(usd_filename):
check_gs_textures(
gs_glb_material.emissive_texture, gs_usd_material.emissive_texture, 0.0, material_name, "emissive"
)


@pytest.mark.required
def test_urdf_with_existing_glb(tmp_path, show_viewer):
assets = Path(gs.utils.get_assets_dir())
glb_path = assets / "usd" / "sneaker_airforce.glb"
urdf_path = tmp_path / "model.urdf"
urdf_path.write_text(
f"""<robot name="shoe">
<link name="base">
<visual>
<geometry><mesh filename="{glb_path}"/></geometry>
</visual>
</link>
</robot>
"""
)
scene = gs.Scene(
show_viewer=show_viewer,
show_FPS=False,
)
scene.build()
scene.step()


@pytest.mark.required
@pytest.mark.parametrize(
"n_channels, float_type",
[
(1, np.float32), # grayscale → H×W
(2, np.float64), # L+A → H×W×2
],
)
def test_urdf_with_float_texture_glb(tmp_path, show_viewer, n_channels, float_type):
vertices = np.array(
[[-0.5, -0.5, 0.0], [0.5, -0.5, 0.0], [0.5, 0.5, 0.0], [-0.5, 0.5, 0.0]],
dtype=np.float32,
)
faces = np.array([[0, 1, 2], [0, 2, 3]], dtype=np.uint32)

mesh = trimesh.Trimesh(vertices, faces, process=False)

H = W = 16
if n_channels == 1:
img = np.random.rand(H, W).astype(float_type)
else:
img = np.random.rand(H, W, n_channels).astype(float_type)

mesh.visual = trimesh.visual.texture.TextureVisuals(
uv=np.array([[0, 0], [1, 0], [1, 1], [0, 1]], dtype=np.float32),
material=trimesh.visual.material.SimpleMaterial(image=img),
)

glb_path = tmp_path / f"tex_{n_channels}c.glb"
urdf_path = tmp_path / f"tex_{n_channels}c.urdf"
trimesh.Scene([mesh]).export(glb_path)

urdf_path.write_text(
f"""<robot name="tex{n_channels}c">
<link name="base">
<visual>
<geometry><mesh filename="{glb_path}"/></geometry>
</visual>
</link>
</robot>
"""
)
scene = gs.Scene(show_viewer=show_viewer, show_FPS=False)
scene.build()
scene.step()