Merge pull request #1901 from abhihjoshi:usd-fixes

PiperOrigin-RevId: 671815757
Change-Id: I8a08385b63401c743253545d89389ab8aaac91bc
This commit is contained in:
Copybara-Service
2024-09-06 10:31:09 -07:00
10 changed files with 199 additions and 88 deletions
+1
View File
@@ -142,6 +142,7 @@ jobs:
fi fi
source venv/bin/activate source venv/bin/activate
python -m pip install --upgrade --require-hashes -r "${repo}/python/build_requirements.txt" python -m pip install --upgrade --require-hashes -r "${repo}/python/build_requirements.txt"
python -m pip install --upgrade --require-hashes -r "${repo}/python/build_requirements_usd.txt"
- name: Configure MuJoCo - name: Configure MuJoCo
run: > run: >
mkdir build && mkdir build &&
+41
View File
@@ -0,0 +1,41 @@
# usd-core is not available for Linux aarch64, so leave it at the end incase it
# fails.
pillow==10.2.0 \
--hash=sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311 \
--hash=sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5 \
--hash=sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56 \
--hash=sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac \
--hash=sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa \
--hash=sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9 \
--hash=sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67 \
--hash=sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e \
--hash=sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588 \
--hash=sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364 \
--hash=sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4 \
--hash=sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72 \
--hash=sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483 \
--hash=sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e \
--hash=sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8 \
--hash=sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef \
--hash=sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e \
--hash=sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39 \
--hash=sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e \
--hash=sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67 \
--hash=sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9
usd-core==24.8 \
--hash=sha256:39fe8e266875e1105886cab870df4bcbe2d40a84696177bc574c22b96d843bf6 \
--hash=sha256:8b38b347dce9336d00dacd4c0e5813bcb43c61e165c653d754e3ee3dc4b2b715 \
--hash=sha256:d1dfe295ccbc57cac39e6dee2d7ce831d7a6a504b8a17dc974e631e3521f83a6 \
--hash=sha256:d3e06fd8c953c4de3d24591cdb9e8e65ca28a876519378d1e4a5d9bca419d7c1 \
--hash=sha256:7c18be89012d03f57d5445101695461c678f370d565259893c1cbcb71a6124e6 \
--hash=sha256:0a503029fa895c9d2307bde2b15748478a0bcb4e8c4ebfa90c7c613fb8137f9d \
--hash=sha256:a3e1d9b34d3936716dbce4ee22c135acffab0f58ba9da09d47dc74992cfc5259 \
--hash=sha256:c8efa7784fd11c3e97dec752d93913a234e976d1ebdf3ba8a23af606c86affb0 \
--hash=sha256:db4951f221323dea83795015f27105a375f28ddabe15ee237acc0728b8dd0212 \
--hash=sha256:0de3647478e7165d82d73e4b5eb30a7f83df167cc9b6cec3d19fd75fe6d01302 \
--hash=sha256:ffdba837680171a468d7ec29edc243fcd47d8d1e044fa810c4037b920d31ef60 \
--hash=sha256:20c45a3099662d32c1a059f3a08adc7829ef7ce40977c33e28835c6bb34aa321 \
--hash=sha256:6ecef8b1592a37c03c3cc3255f302cb2f5e67d4b9f691084b4674ed92032243c \
--hash=sha256:ba067c7476e509b49fa1cec3f19821ab8b10c9b43eeb6cae82676bb498297710 \
--hash=sha256:f3cafd0ddf647a1a8b2335ba109019628252d775c0b3d0f18407211b7801fe53
+33 -15
View File
@@ -5,7 +5,7 @@
""" """
endTimeCode = 1 endTimeCode = 1
startTimeCode = 0 startTimeCode = 0
timeCodesPerSecond = 24 timeCodesPerSecond = 60
upAxis = "Z" upAxis = "Z"
) )
@@ -13,48 +13,66 @@ def Xform "World"
{ {
def Xform "Light_Xform_0" def Xform "Light_Xform_0"
{ {
double3 xformOp:translate double3 xformOp:translate.timeSamples = {
0: (0, -3.674234628677368, 3.674234628677368),
}
uniform token[] xformOpOrder = ["xformOp:translate"] uniform token[] xformOpOrder = ["xformOp:translate"]
def SphereLight "Light_0" def SphereLight "Light_0"
{ {
color3f inputs:color = (0.4, 0.4, 0.4)
float inputs:intensity = 10000
bool inputs:normalize = 1 bool inputs:normalize = 1
float inputs:radius = 0.3 float inputs:radius = 0.3
bool treatAsPoint = 0 bool treatAsPoint = 0
} }
} }
def Xform "CubeMesh_Xform_white_box" def Xform "Camera_Xform_closeup"
{ {
matrix4d xformOp:transform.timeSamples = { matrix4d xformOp:transform.timeSamples = {
0: ( (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 1) ), 0: ( (1, 0, -0, 0), (0, 0.009999499656260014, 0.9999499917030334, 0), (0, -0.9999499917030334, 0.009999499656260014, 0), (0, -6, 0, 1) ),
} }
uniform token[] xformOpOrder = ["xformOp:transform"] uniform token[] xformOpOrder = ["xformOp:transform"]
def Mesh "CubeMesh_white_box" ( def Camera "Camera_closeup"
{
float2 clippingRange = (0.0001, 1000000)
float focalLength = 12
float focusDistance = 400
float horizontalAperture = 12
}
}
def Xform "Mesh_Xform_white_box_id0_geom"
{
token visibility.timeSamples = {
0: "inherited",
1: "invisible",
}
float3 xformOp:scale
matrix4d xformOp:transform.timeSamples = {
0: ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1) ),
}
uniform token[] xformOpOrder = ["xformOp:transform", "xformOp:scale"]
def Mesh "Mesh_white_box_id0_geom" (
apiSchemas = ["MaterialBindingAPI"] apiSchemas = ["MaterialBindingAPI"]
) )
{ {
int[] faceVertexCounts = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] int[] faceVertexCounts = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
int[] faceVertexIndices = [4, 7, 5, 4, 6, 7, 0, 2, 4, 2, 6, 4, 0, 1, 2, 1, 3, 2, 1, 5, 7, 1, 7, 3, 2, 3, 7, 2, 7, 6, 0, 4, 1, 1, 4, 5] int[] faceVertexIndices = [4, 7, 5, 4, 6, 7, 0, 2, 4, 2, 6, 4, 0, 1, 2, 1, 3, 2, 1, 5, 7, 1, 7, 3, 2, 3, 7, 2, 7, 6, 0, 4, 1, 1, 4, 5]
rel material:binding = </World/_materials/Material_white_box> rel material:binding = </World/_materials/Material_white_box_id0_geom>
point3f[] points = [(-1, -1, -1), (1, -1, -1), (-1, -1, 1), (1, -1, 1), (-1, 1, -1), (1, 1, -1), (-1, 1, 1), (1, 1, 1)] point3f[] points = [(-1, -1, -1), (1, -1, -1), (-1, -1, 1), (1, -1, 1), (-1, 1, -1), (1, 1, -1), (-1, 1, 1), (1, 1, 1)]
texCoord2f[] primvars:UVMap = [] (
interpolation = "faceVarying"
)
int[] primvars:UVMap:indices = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
uniform token subdivisionScheme = "none" uniform token subdivisionScheme = "none"
token visibility.timeSamples = {
0: "inherited",
}
} }
} }
def "_materials" def "_materials"
{ {
def Material "Material_white_box" def Material "Material_white_box_id0_geom"
{ {
token outputs:surface.connect = </World/_materials/Material_white_box/Principled_BSDF.outputs:surface> token outputs:surface.connect = </World/_materials/Material_white_box_id0_geom/Principled_BSDF.outputs:surface>
def Shader "Principled_BSDF" def Shader "Principled_BSDF"
{ {
+4 -1
View File
@@ -79,7 +79,10 @@ if __name__ == '__main__':
) )
parser.add_argument( parser.add_argument(
'--camera_names', type=str, nargs='+', help='cameras to include in usd' '--camera_names',
type=str,
nargs='+',
help='cameras to include in usd'
) )
parser.add_argument( parser.add_argument(
+58 -28
View File
@@ -15,7 +15,7 @@
"""USD exporter.""" """USD exporter."""
import os import os
from typing import List, Optional from typing import List, Optional, Union
import mujoco import mujoco
import mujoco.usd.camera as camera_module import mujoco.usd.camera as camera_module
@@ -25,8 +25,6 @@ import mujoco.usd.shapes as shapes_module
import numpy as np import numpy as np
from PIL import Image as im from PIL import Image as im
from PIL import ImageOps from PIL import ImageOps
import scipy
import termcolor
# TODO: b/288149332 - Remove once USD Python Binding works well with pytype. # TODO: b/288149332 - Remove once USD Python Binding works well with pytype.
# pytype: disable=module-attr # pytype: disable=module-attr
@@ -115,7 +113,7 @@ class USDExporter:
self.usd_cameras = [] self.usd_cameras = []
# initializing rendering requirements # initializing rendering requirements
self.renderer = mujoco.Renderer(model, height, width, max_geom) self._scene = mujoco.MjvScene(model=model, maxgeom=max_geom)
self._initialize_usd_stage() self._initialize_usd_stage()
self._scene_option = mujoco.MjvOption() # using default scene option self._scene_option = mujoco.MjvOption() # using default scene option
@@ -133,7 +131,7 @@ class USDExporter:
@property @property
def scene(self): def scene(self):
"""Returns the scene.""" """Returns the scene."""
return self.renderer.scene return self._scene
def _initialize_usd_stage(self): def _initialize_usd_stage(self):
"""Initializes a USD stage to represent the mujoco scene.""" """Initializes a USD stage to represent the mujoco scene."""
@@ -165,13 +163,49 @@ class USDExporter:
os.makedirs(self.assets_directory) os.makedirs(self.assets_directory)
if self.verbose: if self.verbose:
print( print(f"Writing output frames and assets to {self.output_directory_path}")
termcolor.colored(
"Writing output frames and assets to" def _update_scene(
f" {self.output_directory_path}", self,
"green", data: mujoco.MjData,
) camera: Union[int, str] = -1,
scene_option: Optional[mujoco.MjvOption] = None,
):
"""Updates the scene."""
camera_id = camera
if isinstance(camera_id, str):
camera_id = mujoco.mj_name2id(
self.model, mujoco.mjtObj.mjOBJ_CAMERA.value, camera_id
) )
if camera_id == -1:
raise ValueError(f"The camera '{camera}' does not exist.")
if camera_id < -1 or camera_id >= self.model.ncam:
raise ValueError(
f"The camera id {camera_id} is out of range [-1, {self.model.ncam})."
)
# Render camera.
camera = mujoco.MjvCamera()
camera.fixedcamid = camera_id
# Defaults to mjCAMERA_FREE, otherwise mjCAMERA_FIXED refers to a
# camera explicitly defined in the model.
if camera_id == -1:
camera.type = mujoco.mjtCamera.mjCAMERA_FREE
mujoco.mjv_defaultFreeCamera(self.model, camera)
else:
camera.type = mujoco.mjtCamera.mjCAMERA_FIXED
scene_option = scene_option or self._scene_option
mujoco.mjv_updateScene(
self.model,
data,
scene_option,
None,
camera,
mujoco.mjtCatBit.mjCAT_ALL.value,
self._scene,
)
def update_scene( def update_scene(
self, self,
@@ -190,11 +224,10 @@ class USDExporter:
scene_option = scene_option or self._scene_option scene_option = scene_option or self._scene_option
# update the mujoco renderer # update the mujoco renderer
self.renderer.update_scene(data, scene_option=scene_option) self._update_scene(data, scene_option=scene_option)
if self.updates == 0: if self.updates == 0:
self._initialize_usd_stage() self._initialize_usd_stage()
self._load_lights() self._load_lights()
self._load_cameras() self._load_cameras()
@@ -237,11 +270,8 @@ class USDExporter:
if self.verbose: if self.verbose:
print( print(
termcolor.colored( f"Completed writing {self.model.ntex} textures to"
f"Completed writing {self.model.ntex} textures to" f" {self.assets_directory}",
f" {self.assets_directory}",
"green",
)
) )
def _load_geom(self, geom: mujoco.MjvGeom): def _load_geom(self, geom: mujoco.MjvGeom):
@@ -383,8 +413,10 @@ class USDExporter:
camera = self.usd_cameras[i] camera = self.usd_cameras[i]
camera_name = self.camera_names[i] camera_name = self.camera_names[i]
self.renderer.update_scene( self._update_scene(
data, scene_option=scene_option, camera=camera_name data,
camera=camera_name,
scene_option=scene_option,
) )
avg_camera = mujoco.mjv_averageCamera( avg_camera = mujoco.mjv_averageCamera(
@@ -450,9 +482,11 @@ class USDExporter:
new_camera = camera_module.USDCamera( new_camera = camera_module.USDCamera(
stage=self.stage, obj_name=obj_name) stage=self.stage, obj_name=obj_name)
r = scipy.spatial.transform.Rotation.from_euler( rotation = np.zeros(9)
"xyz", rotation_xyz, degrees=True) quat = np.zeros(4)
new_camera.update(cam_pos=np.array(pos), cam_mat=r.as_matrix(), frame=0) mujoco.mju_euler2Quat(quat, rotation_xyz, "xyz")
mujoco.mju_quat2Mat(rotation, quat)
new_camera.update(cam_pos=np.array(pos), cam_mat=rotation, frame=0)
def save_scene(self, filetype: str = "usd"): def save_scene(self, filetype: str = "usd"):
"""Saves the scene to a USD file.""" """Saves the scene to a USD file."""
@@ -468,11 +502,7 @@ class USDExporter:
f"frames/frame_{self.frame_count}.{filetype}" f"frames/frame_{self.frame_count}.{filetype}"
) )
if self.verbose: if self.verbose:
print( print(f"Completed writing frame_{self.frame_count}.{filetype}")
termcolor.colored(
f"Completed writing frame_{self.frame_count}.{filetype}", "green"
)
)
def _get_geom_name(self, geom): def _get_geom_name(self, geom):
"""Adding id as part of name for USD file.""" """Adding id as part of name for USD file."""
+23 -22
View File
@@ -14,30 +14,23 @@
# ============================================================================== # ==============================================================================
"""Tests for the MuJoCo USD Exporter.""" """Tests for the MuJoCo USD Exporter."""
import logging
import os import os
import tempfile
from absl.testing import absltest from absl.testing import absltest
from etils import epath from etils import epath
import mujoco import mujoco
from mujoco.usd import exporter as exporter_module # pylint: disable=g-import-not-at-top
# Open3D and USD are not fully supported on all MuJoCo architectures.
execute_test = True
try:
from mujoco.usd import exporter as exporter_module # pylint: disable=g-import-not-at-top
except ImportError:
logging.warning('Skipping test due to missing import')
execute_test = False
class ExporterTest(absltest.TestCase): class ExporterTest(absltest.TestCase):
def test_usd_export(self): def test_usd_export(self):
if not execute_test:
return
output_dir = os.getenv('TEST_UNDECLARED_OUTPUTS_DIR') output_dir_root = os.getenv(
"TEST_UNDECLARED_OUTPUTS_DIR", tempfile.gettempdir()
)
output_dir_name = "usd_test"
xml = """ xml = """
<mujoco> <mujoco>
<worldbody> <worldbody>
@@ -50,19 +43,27 @@ class ExporterTest(absltest.TestCase):
data = mujoco.MjData(model) data = mujoco.MjData(model)
exporter = exporter_module.USDExporter( exporter = exporter_module.USDExporter(
model, model,
output_directory_name='mujoco_usdpkg', output_directory_name=output_dir_name,
output_directory_root=output_dir, output_directory_root=output_dir_root,
camera_names=["closeup"],
) )
mujoco.mj_step(model, data)
exporter.update_scene(data) exporter.update_scene(data)
exporter.save_scene('export.usda') exporter.save_scene("usda")
with open(os.path.join( with open(
output_dir, 'mujoco_usdpkg/frames', 'frame_1.export.usda'), 'r') as f: os.path.join(
output_dir_root, f"{output_dir_name}/frames", "frame_1.usda"
),
"r",
encoding="utf-8",
) as f:
golden_path = os.path.join( golden_path = os.path.join(
epath.resource_path('mujoco'), 'testdata', 'usd_golden.usda') epath.resource_path("mujoco"), "testdata", "usd_golden.usda"
with open(golden_path, 'r') as golden_file: )
self.assertEqual(f.read(), golden_file.read()) with open(golden_path, "r", encoding="utf-8") as golden_file:
self.assertEqual(f.readlines(), golden_file.readlines())
if __name__ == '__main__': if __name__ == "__main__":
absltest.main() absltest.main()
+20 -14
View File
@@ -16,7 +16,7 @@
import abc import abc
import collections import collections
from typing import Any, Dict, List, Optional, Tuple from typing import Any, Dict, Optional, Sequence, Tuple
import mujoco import mujoco
import mujoco.usd.shapes as shapes_module import mujoco.usd.shapes as shapes_module
@@ -55,7 +55,7 @@ class USDObject(abc.ABC):
geom: mujoco.MjvGeom, geom: mujoco.MjvGeom,
obj_name: str, obj_name: str,
rgba: np.ndarray = np.array([1, 1, 1, 1]), rgba: np.ndarray = np.array([1, 1, 1, 1]),
geom_textures: List[Optional[Tuple[str, mujoco.mjtTexture]]] = None geom_textures: Sequence[Optional[Tuple[str, mujoco.mjtTexture]]] = ()
): ):
self.stage = stage self.stage = stage
self.model = model self.model = model
@@ -120,7 +120,7 @@ class USDObject(abc.ABC):
# setting the image texture attributes # setting the image texture attributes
image_shader.CreateIdAttr("UsdUVTexture") image_shader.CreateIdAttr("UsdUVTexture")
image_shader.CreateInput("file", Sdf.ValueTypeNames.Asset).Set( image_shader.CreateInput("file", Sdf.ValueTypeNames.Asset).Set(
self.geom_textures[mujoco.mjtTextureRole.mjTEXROLE_RGB][0] self.geom_textures[mujoco.mjtTextureRole.mjTEXROLE_RGB.value][0]
) )
image_shader.CreateInput("sourceColorSpace", Sdf.ValueTypeNames.Token).Set( image_shader.CreateInput("sourceColorSpace", Sdf.ValueTypeNames.Token).Set(
"sRGB" "sRGB"
@@ -225,7 +225,7 @@ class USDMesh(USDObject):
obj_name: str, obj_name: str,
dataid: int, dataid: int,
rgba: np.ndarray = np.array([1, 1, 1, 1]), rgba: np.ndarray = np.array([1, 1, 1, 1]),
geom_textures: List[Optional[Tuple[str, mujoco.mjtTexture]]] = None geom_textures: Sequence[Optional[Tuple[str, mujoco.mjtTexture]]] = ()
): ):
super().__init__(stage, model, geom, obj_name, rgba, geom_textures) super().__init__(stage, model, geom, obj_name, rgba, geom_textures)
@@ -245,7 +245,7 @@ class USDMesh(USDObject):
if ( if (
geom.matid != -1 geom.matid != -1
and self.geom_textures[mujoco.mjtTextureRole.mjTEXROLE_RGB] and self.geom_textures[mujoco.mjtTextureRole.mjTEXROLE_RGB.value]
): ):
# setting mesh uv properties # setting mesh uv properties
mesh_texcoord, mesh_facetexcoord = self._get_uv_geometry() mesh_texcoord, mesh_facetexcoord = self._get_uv_geometry()
@@ -324,7 +324,7 @@ class USDPrimitiveMesh(USDObject):
geom: mujoco.MjvGeom, geom: mujoco.MjvGeom,
obj_name: str, obj_name: str,
rgba: np.ndarray = np.array([1, 1, 1, 1]), rgba: np.ndarray = np.array([1, 1, 1, 1]),
geom_textures: List[Optional[Tuple[str, mujoco.mjtTexture]]] = None geom_textures: Sequence[Optional[Tuple[str, mujoco.mjtTexture]]] = ()
): ):
super().__init__(stage, model, geom, obj_name, rgba, geom_textures) super().__init__(stage, model, geom, obj_name, rgba, geom_textures)
@@ -345,7 +345,7 @@ class USDPrimitiveMesh(USDObject):
if ( if (
geom.matid != -1 geom.matid != -1
and self.geom_textures[mujoco.mjtTextureRole.mjTEXROLE_RGB] and self.geom_textures[mujoco.mjtTextureRole.mjTEXROLE_RGB.value]
): ):
# setting mesh uv properties # setting mesh uv properties
mesh_texcoord, _ = self._get_uv_geometry() mesh_texcoord, _ = self._get_uv_geometry()
@@ -366,7 +366,7 @@ class USDPrimitiveMesh(USDObject):
"""Generates the mesh for the primitive USD object.""" """Generates the mesh for the primitive USD object."""
tex_role = mujoco.mjtTextureRole tex_role = mujoco.mjtTextureRole
geom_rgb_texture = ( geom_rgb_texture = (
self.geom_textures[tex_role.mjTEXROLE_RGB] self.geom_textures[tex_role.mjTEXROLE_RGB.value]
if self.geom_textures if self.geom_textures
else None else None
) )
@@ -381,7 +381,7 @@ class USDPrimitiveMesh(USDObject):
mesh_texcoord = np.array(self.prim_mesh.triangle_uvs) mesh_texcoord = np.array(self.prim_mesh.triangle_uvs)
mesh_facetexcoord = np.asarray(self.prim_mesh.triangles) mesh_facetexcoord = np.asarray(self.prim_mesh.triangles)
tex_role = mujoco.mjtTextureRole tex_role = mujoco.mjtTextureRole
geom_rgb_texture = self.geom_textures[tex_role.mjTEXROLE_RGB][1] geom_rgb_texture = self.geom_textures[tex_role.mjTEXROLE_RGB.value][1]
if geom_rgb_texture == mujoco.mjtTexture.mjTEXTURE_2D: if geom_rgb_texture == mujoco.mjtTexture.mjTEXTURE_2D:
s_scale, t_scale = self.model.mat_texrepeat[self.geom.matid] s_scale, t_scale = self.model.mat_texrepeat[self.geom.matid]
@@ -392,8 +392,14 @@ class USDPrimitiveMesh(USDObject):
if self.geom.size[1] > 0: if self.geom.size[1] > 0:
t_scale *= self.geom.size[1] t_scale *= self.geom.size[1]
mesh_texcoord[:, 0] *= s_scale / (self.geom.size[0] * 2) s_size, t_size = self.geom.size[:2]
mesh_texcoord[:, 1] *= t_scale / (self.geom.size[1] * 2) if self.geom.type == mujoco.mjtGeom.mjGEOM_PLANE:
s_size = s_size if s_size > 0 else 1
t_size = t_size if t_size > 0 else 1
if self.model.mat_texuniform[self.geom.matid]:
mesh_texcoord[:, 0] *= s_scale / (s_size * 2)
mesh_texcoord[:, 1] *= t_scale / (t_size * 2)
return mesh_texcoord, mesh_facetexcoord.flatten() return mesh_texcoord, mesh_facetexcoord.flatten()
@@ -418,7 +424,7 @@ class USDTendon(USDObject):
geom: mujoco.MjvGeom, geom: mujoco.MjvGeom,
obj_name: str, obj_name: str,
rgba: np.ndarray = np.array([1, 1, 1, 1]), rgba: np.ndarray = np.array([1, 1, 1, 1]),
geom_textures: List[Optional[Tuple[str, mujoco.mjtTexture]]] = None geom_textures: Sequence[Optional[Tuple[str, mujoco.mjtTexture]]] = ()
): ):
super().__init__(stage, model, geom, obj_name, rgba, geom_textures) super().__init__(stage, model, geom, obj_name, rgba, geom_textures)
@@ -452,7 +458,7 @@ class USDTendon(USDObject):
) )
tex_role = mujoco.mjtTextureRole tex_role = mujoco.mjtTextureRole
if geom.matid != -1 and self.geom_textures[tex_role.mjTEXROLE_RGB]: if geom.matid != -1 and self.geom_textures[tex_role.mjTEXROLE_RGB.value]:
# setting uv properties for each of the parts in the tendon # setting uv properties for each of the parts in the tendon
part_uv_geometries = self._get_uv_geometry() part_uv_geometries = self._get_uv_geometry()
for name, part_uv_geometry in part_uv_geometries.items(): for name, part_uv_geometry in part_uv_geometries.items():
@@ -479,7 +485,7 @@ class USDTendon(USDObject):
"""Generates the tendon mesh using primitives.""" """Generates the tendon mesh using primitives."""
mesh_parts = {} mesh_parts = {}
geom_rgb_texture = ( geom_rgb_texture = (
self.geom_textures[mujoco.mjtTextureRole.mjTEXROLE_RGB] self.geom_textures[mujoco.mjtTextureRole.mjTEXROLE_RGB.value]
if self.geom_textures if self.geom_textures
else None else None
) )
+11 -8
View File
@@ -26,7 +26,7 @@ def get_triangle_uvs(
texture_type: Optional[mujoco.mjtTexture] texture_type: Optional[mujoco.mjtTexture]
): ):
"""Returns UV coordinates for a given mesh.""" """Returns UV coordinates for a given mesh."""
if not texture_type: if texture_type is None:
return None return None
triangle_uvs = [] triangle_uvs = []
@@ -110,7 +110,7 @@ class TriangleMesh:
height: float, height: float,
depth: float, depth: float,
texture_type: Optional[mujoco.mjtTexture] texture_type: Optional[mujoco.mjtTexture]
) -> TriangleMesh: ) -> "TriangleMesh":
"""Creates a box.""" """Creates a box."""
vertices = np.array([[0.0, 0.0, 0.0], vertices = np.array([[0.0, 0.0, 0.0],
[width, 0.0, 0.0], [width, 0.0, 0.0],
@@ -144,7 +144,7 @@ class TriangleMesh:
radius: float, radius: float,
texture_type: Optional[mujoco.mjtTexture], texture_type: Optional[mujoco.mjtTexture],
resolution: int resolution: int
) -> TriangleMesh: ) -> "TriangleMesh":
"""Creates a sphere.""" """Creates a sphere."""
vertices = [] vertices = []
triangles = [] triangles = []
@@ -178,7 +178,7 @@ class TriangleMesh:
radius: float, radius: float,
texture_type: Optional[mujoco.mjtTexture], texture_type: Optional[mujoco.mjtTexture],
resolution: int, resolution: int,
) -> TriangleMesh: ) -> "TriangleMesh":
"""Creates a hemisphere.""" """Creates a hemisphere."""
vertices = [] vertices = []
triangles = [] triangles = []
@@ -218,7 +218,7 @@ class TriangleMesh:
height: float, height: float,
texture_type: Optional[mujoco.mjtTexture], texture_type: Optional[mujoco.mjtTexture],
resolution: int resolution: int
) -> TriangleMesh: ) -> "TriangleMesh":
"""Creates a cylinder.""" """Creates a cylinder."""
vertices = [] vertices = []
triangles = [] triangles = []
@@ -254,14 +254,14 @@ class TriangleMesh:
return TriangleMesh(vertices, triangles, triangle_uvs) return TriangleMesh(vertices, triangles, triangle_uvs)
def translate(self, translation: np.array): def translate(self, translation: np.ndarray) -> None:
self.vertices = self.vertices + translation self.vertices = self.vertices + translation
def rotate(self, rotation: np.array, center: Tuple[float, ...]): def rotate(self, rotation: np.ndarray, center: Tuple[float, ...]) -> None:
translated_point = self.vertices - center translated_point = self.vertices - center
self.vertices = np.dot(translated_point, rotation) + center self.vertices = np.dot(translated_point, rotation) + center
def scale(self, scale: np.array): def scale(self, scale: np.ndarray) -> None:
self.vertices = self.vertices * scale self.vertices = self.vertices * scale
def get_center(self): def get_center(self):
@@ -434,4 +434,7 @@ def mesh_factory(
else: else:
mesh += prim_mesh mesh += prim_mesh
if mesh is None:
raise ValueError("Mesh not found.")
return mesh_config["name"], mesh return mesh_config["name"], mesh
+1
View File
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# ============================================================================== # ==============================================================================
"""Utility functions for USD exporter."""
import numpy as np import numpy as np
+7
View File
@@ -58,4 +58,11 @@ mujoco = [
"include/mujoco/*.h", "include/mujoco/*.h",
"testdata/*.xml", "testdata/*.xml",
"testdata/*.msh", "testdata/*.msh",
"testdata/*.usda",
]
[project.optional-dependencies]
usd = [
"usd-core",
"pillow"
] ]