Skip to content

Commit 05ea76d

Browse files
Merge pull request #1155 from StanfordVL/og-dev-improved
Improved version of import_custom_object.py and additional function for generate_urdf_for_mesh()
2 parents f629f9c + ce668d0 commit 05ea76d

File tree

3 files changed

+637
-233
lines changed

3 files changed

+637
-233
lines changed

omnigibson/examples/objects/import_custom_object.py

Lines changed: 71 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
"""
22
Helper script to download OmniGibson dataset and assets.
3+
Improved version that can import obj file and articulated file (glb, gltf).
34
"""
45

5-
import math
66
from typing import Literal
7-
87
import click
9-
import trimesh
10-
8+
import sys
9+
import shutil
10+
import select
11+
import tempfile
1112
import omnigibson as og
13+
1214
from omnigibson.utils.asset_conversion_utils import (
13-
generate_collision_meshes,
14-
generate_urdf_for_obj,
1515
import_og_asset_from_urdf,
16+
generate_urdf_for_mesh,
1617
)
17-
from omnigibson.utils.python_utils import assert_valid_key
1818

1919

2020
@click.command()
@@ -43,108 +43,106 @@
4343
default=32,
4444
help="Maximum number of convex hulls to decompose individual visual meshes into. Only relevant if --collision-method=coacd",
4545
)
46-
@click.option("--scale", type=float, default=1.0, help="Scale factor to apply to the mesh.")
4746
@click.option("--up-axis", type=click.Choice(["z", "y"]), default="z", help="Up axis for the mesh.")
4847
@click.option("--headless", is_flag=True, help="Run the script in headless mode.")
49-
@click.option("--confirm-bbox", default=True, help="Whether to confirm the scale factor.")
48+
@click.option("--scale", type=int, default=1, help="User choice scale, will be overwritten if check_scale and rescale")
49+
@click.option("--check_scale", is_flag=True, help="Check meshes scale based on heuristic")
50+
@click.option("--rescale", is_flag=True, help="Rescale meshes based on heuristic if check_scale ")
5051
@click.option("--overwrite", is_flag=True, help="Overwrite any pre-existing files")
52+
@click.option("--n_submesh", type=int, help="Maximum of submesh numnber")
5153
def import_custom_object(
5254
asset_path: str,
5355
category: str,
5456
model: str,
5557
collision_method: Literal["coacd", "convex", "none"],
5658
hull_count: int,
57-
scale: float,
5859
up_axis: Literal["z", "y"],
5960
headless: bool,
60-
confirm_bbox: bool,
61+
scale: int,
62+
check_scale: bool,
63+
rescale: bool,
6164
overwrite: bool,
65+
n_submesh: int,
6266
):
6367
"""
6468
Imports a custom-defined object asset into an OmniGibson-compatible USD format and saves the imported asset
6569
files to the custom dataset directory (gm.CUSTOM_DATASET_PATH)
6670
"""
71+
6772
assert len(model) == 6 and model.isalpha(), "Model name must be 6 characters long and contain only letters."
6873
collision_method = None if collision_method == "none" else collision_method
6974

7075
# Sanity check mesh type
71-
valid_formats = trimesh.available_formats()
7276
mesh_format = asset_path.split(".")[-1]
7377

7478
# If we're not a URDF, import the mesh directly first
79+
urdf_dep_paths = None
80+
temp_dirs = []
7581
if mesh_format != "urdf":
76-
assert_valid_key(key=mesh_format, valid_keys=valid_formats, name="mesh format")
77-
78-
# Load the mesh
79-
visual_mesh: trimesh.Trimesh = trimesh.load(asset_path, force="mesh", process=False)
80-
81-
# Generate collision meshes if requested
82-
collision_meshes = (
83-
generate_collision_meshes(visual_mesh, method=collision_method, hull_count=hull_count)
84-
if collision_method is not None
85-
else []
82+
temp_urdf_dir = tempfile.mkdtemp()
83+
temp_dirs.append(temp_urdf_dir)
84+
85+
# Try to generate URDF, may raise ValueError if too many submeshes
86+
urdf_path = generate_urdf_for_mesh(
87+
asset_path,
88+
temp_urdf_dir,
89+
category,
90+
model,
91+
collision_method,
92+
hull_count,
93+
up_axis,
94+
scale=scale,
95+
check_scale=check_scale,
96+
rescale=rescale,
97+
overwrite=overwrite,
98+
n_submesh=n_submesh,
8699
)
87-
88-
# If the up axis is y, we need to rotate the meshes
89-
if up_axis == "y":
90-
rotation_matrix = trimesh.transformations.rotation_matrix(math.pi / 2, [1, 0, 0])
91-
visual_mesh.apply_transform(rotation_matrix)
92-
for collision_mesh in collision_meshes:
93-
collision_mesh.apply_transform(rotation_matrix)
94-
95-
# If the scale is nonzero, we apply it to the meshes
96-
if scale != 1.0:
97-
scale_transform = trimesh.transformations.scale_matrix(scale)
98-
visual_mesh.apply_transform(scale_transform)
99-
for collision_mesh in collision_meshes:
100-
collision_mesh.apply_transform(scale_transform)
101-
102-
# Check the bounding box size and complain if it's larger than 3 meters
103-
bbox_size = visual_mesh.bounding_box.extents
104-
click.echo(f"Visual mesh bounding box size: {bbox_size}")
105-
106-
if confirm_bbox:
107-
if any(size > 3.0 for size in bbox_size):
108-
click.echo(
109-
"Warning: The bounding box sounds a bit large. Are you sure you don't need to scale? "
110-
"We just wanted to confirm this is intentional. You can skip this check by passing --no-confirm-bbox."
111-
)
112-
click.confirm("Do you want to continue?", abort=True)
113-
114-
elif any(size < 0.01 for size in bbox_size):
115-
click.echo(
116-
"Warning: The bounding box sounds a bit small. Are you sure you don't need to scale? "
117-
"We just wanted to confirm this is intentional. You can skip this check by passing --no-confirm-bbox."
118-
)
119-
click.confirm("Do you want to continue?", abort=True)
120-
121-
# Generate the URDF
122-
click.echo(f"Generating URDF for {category}/{model}...")
123-
generate_urdf_for_obj(visual_mesh, collision_meshes, category, model, overwrite=overwrite)
124-
click.echo("URDF generation complete!")
125-
126-
urdf_path = None
127-
collision_method = None
100+
if urdf_path is not None:
101+
click.echo("URDF generation complete!")
102+
urdf_dep_paths = ["material"]
103+
collision_method = None
104+
else:
105+
# Clean up temp directories before exiting
106+
for tmp_dir in temp_dirs:
107+
shutil.rmtree(tmp_dir)
108+
click.echo("Error during URDF generation")
109+
raise click.Abort()
128110
else:
129111
urdf_path = asset_path
130112
collision_method = collision_method
131113

132-
# Convert to USD
133-
import_og_asset_from_urdf(
134-
category=category,
135-
model=model,
136-
urdf_path=urdf_path,
137-
collision_method=collision_method,
138-
hull_count=hull_count,
139-
overwrite=overwrite,
140-
use_usda=False,
141-
)
114+
try:
115+
# Convert to USD
116+
import_og_asset_from_urdf(
117+
category=category,
118+
model=model,
119+
urdf_path=urdf_path,
120+
urdf_dep_paths=urdf_dep_paths,
121+
collision_method=collision_method,
122+
hull_count=hull_count,
123+
overwrite=overwrite,
124+
use_usda=False,
125+
)
126+
127+
except Exception as e:
128+
click.echo(f"Error during USD conversion: {str(e)}")
129+
# Clean up temp directories before exiting
130+
for tmp_dir in temp_dirs:
131+
shutil.rmtree(tmp_dir)
132+
raise click.Abort()
133+
134+
# Clean up temp directories
135+
for tmp_dir in temp_dirs:
136+
shutil.rmtree(tmp_dir)
142137

143138
# Visualize if not headless
144139
if not headless:
145140
click.echo("The asset has been successfully imported. You can view it and make changes and save if you'd like.")
146141
while True:
147142
og.sim.render()
143+
if select.select([sys.stdin], [], [], 0)[0]:
144+
sys.stdin.readline() # Clear the input buffer
145+
break
148146

149147

150148
if __name__ == "__main__":

0 commit comments

Comments
 (0)