Skip to content

Commit e727f83

Browse files
LeonLiu4YilingQiao
authored andcommitted
[FEATURE] Enabling rendering plane geometry one-sided. (Genesis-Embodied-AI#1396)
1 parent 522a85e commit e727f83

File tree

3 files changed

+54
-14
lines changed

3 files changed

+54
-14
lines changed

genesis/engine/entities/rigid_entity/rigid_entity.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,24 +127,27 @@ def _load_primitive(self, morph, surface):
127127
if isinstance(morph, gs.options.morphs.Box):
128128
extents = np.array(morph.size)
129129
tmesh = mu.create_box(extents=extents)
130+
cmesh = tmesh
130131
geom_data = extents
131132
geom_type = gs.GEOM_TYPE.BOX
132133
link_name_prefix = "box"
133134

134135
elif isinstance(morph, gs.options.morphs.Sphere):
135136
tmesh = mu.create_sphere(radius=morph.radius)
137+
cmesh = tmesh
136138
geom_data = np.array([morph.radius])
137139
geom_type = gs.GEOM_TYPE.SPHERE
138140
link_name_prefix = "sphere"
139141

140142
elif isinstance(morph, gs.options.morphs.Cylinder):
141143
tmesh = mu.create_cylinder(radius=morph.radius, height=morph.height)
144+
cmesh = tmesh
142145
geom_data = None
143146
geom_type = gs.GEOM_TYPE.MESH
144147
link_name_prefix = "cylinder"
145148

146149
elif isinstance(morph, gs.options.morphs.Plane):
147-
tmesh = mu.create_plane(normal=morph.normal)
150+
tmesh, cmesh = mu.create_plane(normal=morph.normal)
148151
geom_data = np.array(morph.normal)
149152
geom_type = gs.GEOM_TYPE.PLANE
150153
link_name_prefix = "plane"
@@ -167,7 +170,7 @@ def _load_primitive(self, morph, surface):
167170
dict(
168171
contype=1,
169172
conaffinity=1,
170-
mesh=gs.Mesh.from_trimesh(tmesh, surface=gs.surfaces.Collision()),
173+
mesh=gs.Mesh.from_trimesh(cmesh, surface=gs.surfaces.Collision()),
171174
type=geom_type,
172175
data=geom_data,
173176
sol_params=gu.default_solver_params(),

genesis/utils/mesh.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -863,18 +863,33 @@ def create_plane(size=1e3, color=None, normal=(0.0, 0.0, 1.0)):
863863
mesh = trimesh.creation.box(extents=[size, size, thickness])
864864
mesh.vertices[:, 2] -= thickness / 2
865865
mesh.vertices = gu.transform_by_R(mesh.vertices, gu.z_up_to_R(np.asarray(normal, dtype=np.float32)))
866+
867+
half = size * 0.5
868+
verts = np.array(
869+
[
870+
[-half, -half, 0.0],
871+
[half, -half, 0.0],
872+
[half, half, 0.0],
873+
[-half, -half, 0.0],
874+
[half, half, 0.0],
875+
[-half, half, 0.0],
876+
],
877+
dtype=np.float32,
878+
)
879+
faces = np.arange(6, dtype=np.int32).reshape(-1, 3)
880+
vmesh = trimesh.Trimesh(verts, faces, process=False)
881+
vmesh.vertices[:, 2] -= thickness / 2
882+
vmesh.vertices = gu.transform_by_R(vmesh.vertices, gu.z_up_to_R(np.asarray(normal, dtype=np.float32)))
866883
if color is None: # use checkerboard texture
867-
mesh.visual = trimesh.visual.TextureVisuals(
884+
vmesh.visual = trimesh.visual.TextureVisuals(
868885
uv=np.array(
869886
[
870887
[0, 0],
871-
[0, 0],
872-
[0, size],
873-
[0, size],
874-
[size, 0],
875888
[size, 0],
876889
[size, size],
890+
[0, 0],
877891
[size, size],
892+
[0, size],
878893
],
879894
dtype=np.float32,
880895
),
@@ -883,10 +898,11 @@ def create_plane(size=1e3, color=None, normal=(0.0, 0.0, 1.0)):
883898
),
884899
)
885900
else:
886-
mesh.visual = trimesh.visual.ColorVisuals(
887-
vertex_colors=np.tile(np.asarray(color, dtype=np.float32), (len(mesh.vertices), 1))
901+
vmesh.visual = trimesh.visual.ColorVisuals(
902+
vertex_colors=np.tile(np.asarray(color, dtype=np.float32), (len(vmesh.vertices), 1))
888903
)
889-
return mesh
904+
905+
return vmesh, mesh
890906

891907

892908
def generate_tetgen_config_from_morph(morph):

genesis/utils/terrain.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ def parse_terrain(morph: Terrain, surface):
155155
heightfield,
156156
horizontal_scale=morph.horizontal_scale,
157157
vertical_scale=morph.vertical_scale,
158+
surface=surface,
158159
uv_scale=morph.uv_scale if need_uvs else None,
159160
)
160161

@@ -214,7 +215,12 @@ def fractal_terrain(terrain, levels=8, scale=1.0):
214215

215216

216217
def convert_heightfield_to_watertight_trimesh(
217-
height_field_raw, horizontal_scale, vertical_scale, slope_threshold=None, uv_scale=None
218+
height_field_raw,
219+
horizontal_scale,
220+
vertical_scale,
221+
surface,
222+
slope_threshold=None,
223+
uv_scale=None,
218224
):
219225
"""
220226
Adapted from Issac Gym's `convert_heightfield_to_trimesh` function.
@@ -285,6 +291,20 @@ def convert_heightfield_to_watertight_trimesh(
285291
triangles_top[start + 1 : stop : 2, 1] = ind2
286292
triangles_top[start + 1 : stop : 2, 2] = ind3
287293

294+
if not surface.double_sided:
295+
if uv_scale is not None:
296+
uv_top = np.column_stack(
297+
(
298+
(xx.flat - xx.min()) / (xx.max() - xx.min()) * uv_scale,
299+
(yy.flat - yy.min()) / (yy.max() - yy.min()) * uv_scale,
300+
)
301+
)
302+
visual = trimesh.visual.TextureVisuals(uv=uv_top)
303+
else:
304+
visual = None
305+
306+
vmesh_single = trimesh.Trimesh(vertices_top, triangles_top, visual=visual, process=False)
307+
288308
# bottom plane
289309
z_min = np.min(vertices_top[:, 2]) - 1.0
290310

@@ -382,15 +402,16 @@ def convert_heightfield_to_watertight_trimesh(
382402

383403
uv_simp = uvs[idx_map]
384404

385-
mesh = trimesh.Trimesh(
405+
vmesh_full = trimesh.Trimesh(
386406
v_simp,
387407
f_simp,
388408
visual=trimesh.visual.TextureVisuals(uv=uv_simp),
389409
)
390410
else:
391-
mesh = trimesh.Trimesh(v_simp, f_simp)
411+
vmesh_full = trimesh.Trimesh(v_simp, f_simp)
392412

393-
return mesh, sdf_mesh
413+
vmesh_out = vmesh_single if not surface.double_sided else vmesh_full
414+
return vmesh_out, sdf_mesh
394415

395416

396417
def mesh_to_heightfield(

0 commit comments

Comments
 (0)