Skip to content

Conversation

@alanray-tech
Copy link

@alanray-tech alanray-tech commented Dec 3, 2025

Description

This PR adds comprehensive USD (Universal Scene Description) import functionality to Genesis, enabling users to directly import USD stages containing articulated structures and rigid bodies into Genesis simulation scenes.

The implementation includes:

  • USD Parser System: A modular parser architecture with separate parsers for materials, articulations, and rigid bodies
  • Articulation Support: Full parsing of USD articulations with support for multiple joint types (revolute, prismatic, spherical, fixed)
  • Rigid Body Support: Parsing of standalone rigid bodies with collision and visual geometry
  • Material Parsing (Placeholder, need further development): Automatic extraction and conversion of USD rendering materials
  • Scene Integration: New Scene.add_stage() method for easy USD file import
  • New Morph Types: USDArticulation and USDRigidBody morph classes for USD-based entities

Key components:

  • UsdParser: Main parser entrance with import_from_stage() method
  • UsdArticulationParser: Extracts and parses articulation structures from USD stages
  • UsdRigidBodyParser: Extracts and parses rigid body prims from USD stages
  • UsdRenderingMaterialParser: Parses USD materials and shaders
  • UsdParserContext: Context manager for tracking parsed entities and materials
  • UsdParserUtils: Utility functions for transform computation, mesh conversion, and coordinate system transformations

Motivation and Context

USD is a widely-used format in the robotics and simulation community, particularly in tools like NVIDIA Isaac Sim, Omniverse, and other industry-standard platforms. Adding USD import capability allows Genesis users to:

  1. Import assets directly from USD files without manual conversion
  2. Leverage existing USD asset libraries and pipelines
  3. Work seamlessly with USD-based workflows and tools
  4. Import complex articulated structures (robots, mechanisms) with proper joint definitions
  5. Import rigid bodies with correct collision and visual geometry

This feature significantly improves Genesis's interoperability with the broader robotics and simulation ecosystem.

How Has This Been / Can This Be Tested?

The feature can be tested using the provided example script:

import genesis as gs
gs.init(backend=gs.cpu)

scene = gs.Scene(
    viewer_options=gs.options.ViewerOptions(
        camera_pos=(3.5, 0.0, 2.5),
        camera_lookat=(0.0, 0.0, 0.5),
        camera_fov=40,
        enable_interaction=True,
    ),
    rigid_options=gs.options.RigidOptions(
        gravity=(0, 0, -9.8),
        enable_collision=True,
        enable_joint_limit=True,
        max_collision_pairs=10000,
    ),
    show_viewer=True,
)

# Load a USD stage
entities = scene.add_stage("path/to/your/file.usd")

# Build and run simulation
scene.build()
while scene.viewer.is_alive():
    scene.step()

Testing Checklist:

  1. Test with USD files containing both articulations and rigid bodies
  2. Verify joint types are correctly parsed (revolute, prismatic, fixed)
  3. Verify visual and collision meshes are correctly extracted
  4. Verify materials are correctly applied (Placeholder, need further development)
  5. Verify transforms and coordinate systems are correctly converted

Test Files:

  • Example script: examples/usd/import_stage.py
  • Test with USD files that have:
    • ArticulationRootAPI prims
    • RigidBodyAPI prims
    • CollisionAPI prims
    • Visual and collision geometry
    • Various joint types

Screenshots (if appropriate):

20251202-115133.mp4

Checklist:

  • I read the CONTRIBUTING document.

  • I followed the Submitting Code Changes section of CONTRIBUTING document.

  • I tagged the title correctly (including BUG FIX/FEATURE/MISC/BREAKING)

  • I updated the documentation accordingly or no change is needed.

  • I tested my changes and added instructions on how to test it for reviewers.

  • I have added tests to cover my changes.

  • All new and existing tests passed.

@alanray-tech alanray-tech changed the title Usd Articulation/RigidBody Support Feature [Feature] Usd Stage Import Support: Articulation & RigidBody Dec 3, 2025
@YilingQiao
Copy link
Collaborator

can you add a unit test? The assets can be put into the huggingface repo. An example is like this

asset_path = get_hf_dataset(pattern="linear_deformable.urdf")

Comment on lines +8 to +15
from pxr import Usd, UsdGeom, Gf
from typing import List, Tuple
import genesis as gs
import numpy as np
import trimesh
from collections import deque
from .. import geom as gu
import scipy.linalg
Copy link
Collaborator

Choose a reason for hiding this comment

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

You should first import standards libraries, skip line, then ubiquitous third-party libraries (torch, numpy, tqdm...etc), skip line, then random third-party libraries, skip line, then our own custom libraries, skip line, then relative genesis imports. For each group, make sure it is sorted alphabetically.


def usd_quat_to_numpy(usd_quat: Gf.Quatf) -> np.ndarray:
"""
Convert a USD Gf.Quatf to a numpy array (w, x, y, z format).
Copy link
Collaborator

Choose a reason for hiding this comment

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

(w, x, y, z) format

Comment on lines +58 to +59
R, S = scipy.linalg.polar(trans_matrix[:3, :3], side="right")
return R, S
Copy link
Collaborator

Choose a reason for hiding this comment

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

Avoid temporary if not necessary.

Moreover, scipy should NOT be considered part of our dependencies. Avoid relying on it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

If you need more helpers, add them to gs.utils.geom.

"""

# Compute Genesis transform relative to ref_prim (Q^i_j)
Q_rel, S = compute_gs_related_transform(usd_mesh.GetPrim(), ref_prim)
Copy link
Collaborator

Choose a reason for hiding this comment

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

"Genesis transform" is weird naming. Could you stick on mathematically accurate description? Same for compute_gs_related_transform: gs_related is not a good name.

points = np.array(points_attr.Get())
# Apply only scaling to every point
points = points @ S
face_vertex_counts = np.array(face_vertex_counts_attr.Get())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Prefer using np.asarray / np.asanyarray when passing generic objects.

Comment on lines +98 to +119
has_non_tri_quads = False
for i, count in enumerate(face_vertex_counts):
face_vertex_counts[i] = count
if count == 3:
# Triangle - use directly
faces.append(face_vertex_indices[offset : offset + count])
elif count == 4:
# Quad - split into two triangles
quad = face_vertex_indices[offset : offset + count]
faces.append([quad[0], quad[1], quad[2]])
faces.append([quad[0], quad[2], quad[3]])
elif count > 4:
# Polygon with more than 4 vertices - triangulate using triangle fan
# Use the first vertex as the fan center and connect to each pair of consecutive vertices
polygon = face_vertex_indices[offset : offset + count]
for j in range(1, count - 1):
faces.append([polygon[0], polygon[j], polygon[j + 1]])
has_non_tri_quads = True
else:
# Invalid face (count < 3)
gs.logger.warning(f"Invalid face vertex count {count} in USD mesh {usd_mesh.GetPath()}. Skipping face.")
offset += count
Copy link
Collaborator

Choose a reason for hiding this comment

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

This for-loop does not look efficient at all. It is fast enough even for complex mesh?

Comment on lines +131 to +146
def apply_transform_to_pos(trans_matrix: np.ndarray, pos: np.ndarray) -> np.ndarray:
"""
Apply a transformation matrix to a position.
Parameters
----------
trans_matrix : np.ndarray, shape (4, 4) or (3, 3)
pos : np.ndarray, shape (3,)
The position to apply the transformation to.
Returns
-------
np.ndarray, shape (3,)
The transformed position.
"""
return trans_matrix[:3, :3] @ pos + trans_matrix[:3, 3]
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we already have a helper function for this in gs.utils.geom. Why reimplement this?

Copy link
Collaborator

Choose a reason for hiding this comment

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

transform_by_T

Comment on lines +149 to +165
def extract_quat_from_transform(trans_matrix: np.ndarray) -> np.ndarray:
"""
Extract quaternion from a 4x4 transformation matrix.
Parameters
----------
trans_matrix : np.ndarray, shape (4, 4) or (3, 3)
The transformation matrix.
Returns
-------
np.ndarray, shape (4,)
Quaternion as numpy array [w, x, y, z].
"""
R, _ = extract_rotation_and_scale(trans_matrix)
quat = gu.R_to_quat(R)
return quat
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same. Just call gu.R_to_quat. It is already scale-insensitive.

Comment on lines +186 to +187
t = imageable.ComputeLocalToWorldTransform(Usd.TimeCode.Default()).GetTranspose()
return np.array(t)
Copy link
Collaborator

Choose a reason for hiding this comment

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

return np.asarray(imageable.ComputeLocalToWorldTransform(Usd.TimeCode.Default()).GetTranspose())

Comment on lines +211 to +212
prim_to_ref_prim_transform = world_to_ref_prim @ prim_world_transform
return prim_to_ref_prim_transform
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remove temporary.

return np.array(t)


def compute_usd_related_transform(prim: Usd.Prim, ref_prim: Usd.Prim | None) -> np.ndarray:
Copy link
Collaborator

Choose a reason for hiding this comment

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

usd_related is not a good function name. Use a mathematical description.

return Q_i_j, S_prim


def convert_usd_joint_axis_to_gs(usd_local_joint_axis: np.ndarray, usd_link_prim: Usd.Prim | None) -> np.ndarray:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same. This function names are confusing. We cannot tell straight away what they are computing, which is problematic. Even the docstring does not explain what is the difference between "USD link local space" and "Genesis link local space".

Comment on lines +343 to +357
def compute_joint_axis_scaling_factor(gs_local_joint_axis: np.ndarray) -> float:
"""
Compute the scaling factor for a joint axis.
Parameters
----------
gs_local_joint_axis : np.ndarray, shape (3,)
The joint axis in Genesis link local space.
Returns
-------
float
The scaling factor.
"""
return np.linalg.norm(gs_local_joint_axis)
Copy link
Collaborator

@duburcqa duburcqa Jan 7, 2026

Choose a reason for hiding this comment

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

I'm not convinced adding a function abstraction of top of a simple norm is helpful but ok.

Comment on lines +15 to +20
class UsdGeometryAdapter:
"""
A adapter to convert USD geometry to Genesis geometry info.
Receive: UsdGeom.Mesh, UsdGeom.Plane, UsdGeom.Sphere, UsdGeom.Capsule, UsdGeom.Cube
Return: Genesis geometry info
"""
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need this? It is confusing.

Comment on lines +47 to +61
elif self._prim.IsA(UsdGeom.Plane):
r = self._create_gs_plane_geo_info()
g_info.update(r)
elif self._prim.IsA(UsdGeom.Sphere):
r = self._create_gs_sphere_geo_info()
g_info.update(r)
elif self._prim.IsA(UsdGeom.Capsule):
r = self._create_gs_capsule_geo_info()
g_info.update(r)
elif self._prim.IsA(UsdGeom.Cube):
r = self._create_gs_cube_geo_info()
g_info.update(r)
elif self._prim.IsA(UsdGeom.Cylinder):
r = self._create_gs_cylinder_geo_info()
g_info.update(r)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why are you prefixing all your function with _gs_? Of course it is creating Genesis objects, what else it could be? We only have a single simulator.

Comment on lines +140 to +142
gs.logger.warning(
f"Size of uvs mismatch for mesh {mesh_prim.GetPath()} in usd file {self._get_usd_file_path()}."
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not an exception? Is this kind of issue very common? Because we need to have a good motivation to be fault tolerant.


# Triangulate faces
if len(face_vertex_counts) == 0:
triangles = np.array([], dtype=np.int32).reshape(0, 3)
Copy link
Collaborator

Choose a reason for hiding this comment

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

triangles = np.zeros((0, 3), dtype=np.int32)

triangles = np.array(triangles, dtype=np.int32)
else:
triangles = face_vertex_indices.reshape(-1, 3)
# Get UV name from material (needed for UV extraction)
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is this comment?

Comment on lines +358 to +361
gs.logger.warning(
f"Sphere: {self._prim.GetPath()} scale is not uniform: {S}, take the mean of the three components"
)
radius *= np.mean(S_diag)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't like this kind of fallback. If you cannot parse a file properly, just raise an exception. Hiding failure is rarely desirable.

Comment on lines +386 to +388
# Get capsule dimensions (defaults)
radius = radius_attr.Get() if radius_attr and radius_attr.HasValue() else 0.5
height = height_attr.Get() if height_attr and height_attr.HasValue() else 1.0
Copy link
Collaborator

Choose a reason for hiding this comment

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

From where are these default coming from? Could you are a URL for reference?

Comment on lines +480 to +482
# Get cylinder dimensions (defaults)
radius = radius_attr.Get() if radius_attr and radius_attr.HasValue() else 0.5
height = height_attr.Get() if height_attr and height_attr.HasValue() else 1.0
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same. Add reference for default values.

Comment on lines +520 to +561
class BatchedUsdGeometryAdapater:
"""
A adapter to convert USD geometry to Genesis geometry info.
Receive: List[UsdGeom.Mesh], List[UsdGeom.Plane], List[UsdGeom.Sphere], List[UsdGeom.Capsule], List[UsdGeom.Cube]
Return: List[Dict]
"""

def __init__(
self, ctx: UsdParserContext, start_prim: Usd.Prim, ref_prim: Usd.Prim, mesh_type: Literal["mesh", "vmesh"]
):
self._ctx: UsdParserContext = ctx
self._start_prim: Usd.Prim = start_prim
self._ref_prim: Usd.Prim = ref_prim
self._mesh_type: Literal["mesh", "vmesh"] = mesh_type
self._geometries: List[Usd.Prim] = self._find_all_geometries()

def _find_all_geometries(self) -> List[Usd.Prim]:
"""Find all geometries under the start prim."""
geometries: List[Usd.Prim] = []

# consider the start prim itself
for geom_type in UsdGeometryAdapter.SupportUsdGeoms:
if self._start_prim.IsA(geom_type):
geometries.append(self._start_prim)
break

# consider the children of the start prim
for prim in bfs_iterator(self._start_prim):
for geom_type in UsdGeometryAdapter.SupportUsdGeoms:
if prim.IsA(geom_type):
geometries.append(prim)
break
return geometries

def create_gs_geo_infos(self) -> List[Dict]:
"""Create geometry info for all geometries."""
g_infos: List[Dict] = []
for geometry in self._geometries:
g_info = UsdGeometryAdapter(self._ctx, geometry, self._ref_prim, self._mesh_type).create_gs_geo_info()
assert g_info is not None, f"Geometry: {geometry.GetPath()} create gs geo info failed"
g_infos.append(g_info)
return g_infos
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is this a class instead of a function?

g_infos: List[Dict] = []
for geometry in self._geometries:
g_info = UsdGeometryAdapter(self._ctx, geometry, self._ref_prim, self._mesh_type).create_gs_geo_info()
assert g_info is not None, f"Geometry: {geometry.GetPath()} create gs geo info failed"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Prefer raising exception, unless it mainly serves as documentation and should never happen in practice.

import genesis as gs
import numpy as np
import re
from scipy.spatial.transform import Rotation as R
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do not use scipy.

Comment on lines +10 to +28
from pxr import Usd, UsdGeom, UsdPhysics, UsdShade, Sdf
from typing import List, Dict, Tuple, Literal
import genesis as gs
import numpy as np
import re
from scipy.spatial.transform import Rotation as R
from .usd_parser_context import UsdParserContext
from .usd_parser_utils import (
bfs_iterator,
compute_gs_related_transform,
extract_quat_from_transform,
compute_gs_global_transform,
convert_usd_joint_axis_to_gs,
usd_quat_to_numpy,
convert_usd_joint_pos_to_gs,
)
from .usd_geo_adapter import BatchedUsdGeometryAdapater, UsdGeometryAdapter
from .. import geom as gu
from .. import urdf as urdf_utils
Copy link
Collaborator

Choose a reason for hiding this comment

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

Fix order of imports.


class UsdArticulationParser:
"""
A parser to extract articulation information from a USD stage.
Copy link
Collaborator

Choose a reason for hiding this comment

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

"articulation information" is not clear. Be more explicit.

f"Provided prim {articulation_root_prim.GetPath()} is not an Articulation Root. Now we only support articulation parsing from ArticulationRootAPI."
)

gs.logger.info(f"Parsing USD articulation from {articulation_root_prim.GetPath()}.")
Copy link
Collaborator

Choose a reason for hiding this comment

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

.debug

self._root: Usd.Prim = articulation_root_prim
if not articulation_root_prim.HasAPI(UsdPhysics.ArticulationRootAPI):
gs.raise_exception(
f"Provided prim {articulation_root_prim.GetPath()} is not an Articulation Root. Now we only support articulation parsing from ArticulationRootAPI."
Copy link
Collaborator

Choose a reason for hiding this comment

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

Line is too long.

Comment on lines +31 to +190
class UsdArticulationParser:
"""
A parser to extract articulation information from a USD stage.
The Parser is agnostic to genesis structures, it only focuses on USD articulation structure.
"""

def __init__(self, stage: Usd.Stage, articulation_root_prim: Usd.Prim):
"""
Initialize the articulation parser.
Parameters
----------
stage : Usd.Stage
The USD stage.
articulation_root_prim : Usd.Prim
The root prim of the articulation (must have ArticulationRootAPI).
"""
self._stage: Usd.Stage = stage
self._root: Usd.Prim = articulation_root_prim
if not articulation_root_prim.HasAPI(UsdPhysics.ArticulationRootAPI):
gs.raise_exception(
f"Provided prim {articulation_root_prim.GetPath()} is not an Articulation Root. Now we only support articulation parsing from ArticulationRootAPI."
)

gs.logger.info(f"Parsing USD articulation from {articulation_root_prim.GetPath()}.")

self.joints: List[UsdPhysics.Joint] = []
self.fixed_joints: List[UsdPhysics.FixedJoint] = []
self.revolute_joints: List[UsdPhysics.RevoluteJoint] = []
self.prismatic_joints: List[UsdPhysics.PrismaticJoint] = []
self.spherical_joints: List[UsdPhysics.SphericalJoint] = []
self._collect_joints()

self.links: List[Usd.Prim] = []
self._collect_links()

# ==================== Static Methods: Finding Articulation Roots ====================

@staticmethod
def find_all_articulation_roots(stage: Usd.Stage, context: UsdParserContext = None) -> List[Usd.Prim]:
"""
Find all prims with ArticulationRootAPI in the stage.
Parameters
----------
stage : Usd.Stage
The USD stage.
context : UsdParserContext, optional
If provided, articulation roots will be added to the context.
Returns
-------
List[Usd.Prim]
List of articulation root prims.
"""
articulation_roots = []
for prim in bfs_iterator(stage.GetPseudoRoot()):
if prim.HasAPI(UsdPhysics.ArticulationRootAPI):
articulation_roots.append(prim)
if context:
context.add_articulation_root(prim)
return articulation_roots

# ==================== Collection Methods: Joints and Links ====================

def _collect_joints(self):
"""Collect all joints in the articulation."""
for child in bfs_iterator(self._root):
if child.IsA(UsdPhysics.Joint):
joint_api = UsdPhysics.Joint(child)
self.joints.append(joint_api)
if child.IsA(UsdPhysics.RevoluteJoint):
revolute_joint_api = UsdPhysics.RevoluteJoint(child)
self.revolute_joints.append(revolute_joint_api)
elif child.IsA(UsdPhysics.FixedJoint):
fixed_joint_api = UsdPhysics.FixedJoint(child)
self.fixed_joints.append(fixed_joint_api)
elif child.IsA(UsdPhysics.PrismaticJoint):
prismatic_joint_api = UsdPhysics.PrismaticJoint(child)
self.prismatic_joints.append(prismatic_joint_api)
elif child.IsA(UsdPhysics.SphericalJoint):
spherical_joint_api = UsdPhysics.SphericalJoint(child)
self.spherical_joints.append(spherical_joint_api)

def _collect_links(self):
"""Collect all links connected by joints in the articulation."""
# Now we have joints collected, we can find links connected by these joints
paths = set()
for joint in self.joints:
body0_targets = joint.GetBody0Rel().GetTargets()
body1_targets = joint.GetBody1Rel().GetTargets()
for target_path in body0_targets + body1_targets:
# Check target is valid
if self._stage.GetPrimAtPath(target_path):
paths.add(target_path)
else:
gs.raise_exception(f"Joint {joint.GetPath()} has invalid target body reference {target_path}.")
for path in paths:
prim = self._stage.GetPrimAtPath(path)
self.links.append(prim)

# ==================== Geometry Collection Methods ====================

visual_pattern = re.compile(r"^(visual|Visual).*")
collision_pattern = re.compile(r"^(collision|Collision).*")
all_pattern = re.compile(r"^.*")

@staticmethod
def create_gs_geo_infos(
context: UsdParserContext, link: Usd.Prim, pattern, mesh_type: Literal["mesh", "vmesh"]
) -> List[Dict]:
# if the link itself is a geometry
geo_infos: List[Dict] = []
link_geo_adapter = UsdGeometryAdapter(context, link, link, mesh_type)
link_geo_info = link_geo_adapter.create_gs_geo_info()
if link_geo_info is not None:
geo_infos.append(link_geo_info)

# - Link
# - Visuals
# - Collisions
search_roots: list[Usd.Prim] = []
for child in link.GetChildren():
if pattern.match(child.GetName()):
search_roots.append(child)

for search_root in search_roots:
adapter = BatchedUsdGeometryAdapater(context, search_root, link, mesh_type)
geo_infos.extend(adapter.create_gs_geo_infos())

return geo_infos

@staticmethod
def get_visual_geometries(link: Usd.Prim, context: UsdParserContext) -> List[Dict]:
if context.vis_mode == "visual":
vis_geo_infos = UsdArticulationParser.create_gs_geo_infos(
context, link, UsdArticulationParser.visual_pattern, "vmesh"
)
if len(vis_geo_infos) == 0:
# if no visual geometries found, use any pattern to find visual geometries
gs.logger.info(
f"No visual geometries found, using any pattern to find visual geometries in {link.GetPath()}"
)
vis_geo_infos = UsdArticulationParser.create_gs_geo_infos(
context, link, UsdArticulationParser.all_pattern, "vmesh"
)
elif context.vis_mode == "collision":
vis_geo_infos = UsdArticulationParser.create_gs_geo_infos(
context, link, UsdArticulationParser.collision_pattern, "vmesh"
)
else:
gs.raise_exception(f"Unsupported visualization mode {context.vis_mode}.")
return vis_geo_infos

@staticmethod
def get_collision_geometries(link: Usd.Prim, context: UsdParserContext) -> List[Dict]:
col_geo_infos = UsdArticulationParser.create_gs_geo_infos(
context, link, UsdArticulationParser.collision_pattern, "mesh"
)
return col_geo_infos
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand why this has to be a class.

Comment on lines +205 to +210
if axis_str == "X":
return np.array([1.0, 0.0, 0.0])
elif axis_str == "Y":
return np.array([0.0, 1.0, 0.0])
elif axis_str == "Z":
return np.array([0.0, 0.0, 1.0])
Copy link
Collaborator

Choose a reason for hiding this comment

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

You should always specify dtype when allocating memory. In this case, either using gs.np_float or np.float32, np.float64 if it has to be more specific.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants