Skip to content

Commit ef8c968

Browse files
committed
Migrate to native Python API for 'splashsurf'.
1 parent 3538246 commit ef8c968

File tree

6 files changed

+46
-61
lines changed

6 files changed

+46
-61
lines changed

genesis/engine/entities/particle_entity.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ def sample(self):
247247
self._vfaces = np.array([], dtype=gs.np_float)
248248
elif isinstance(self._morph, gs.options.morphs.MeshSet):
249249
for i in range(len(self._morph.files)):
250-
pos_i, euler_i = map(np.asarray, (self._morph.poss[i], self._morph.eulers[i]))
250+
pos_i = np.asarray(self._morph.poss[i], dtype=gs.np_float)
251+
euler_i = np.asarray(self._morph.eulers[i], dtype=gs.np_float)
251252
quat_i = gs.utils.geom.xyz_to_quat(euler_i, rpy=True, degrees=True)
252253
self._vmesh[i].apply_transform(gu.trans_quat_to_T(pos_i, quat_i))
253254

@@ -284,7 +285,8 @@ def sample(self):
284285
origin = np.mean(self._morph.poss, dtype=gs.np_float)
285286
else:
286287
# transform vmesh
287-
pos, quat = map(np.asarray, (self._morph.pos, self._morph.quat))
288+
pos = np.asarray(self._morph.pos, dtype=gs.np_float)
289+
quat = np.asarray(self._morph.quat, dtype=gs.np_float)
288290
self._vmesh.apply_transform(gu.trans_quat_to_T(pos, quat))
289291
# transform particles
290292
particles = gu.transform_by_trans_quat(

genesis/engine/materials/MPM/elasto_plastic.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ class ElastoPlastic(Base):
1212
1313
Note
1414
----
15-
Default yield ratio comes from the SNOW material in taichi's MPM implementation: https://github.com/taichi-dev/taichi_elements/blob/d19678869a28b09a32ef415b162e35dc929b792d/engine/mpm_solver.py#L434
15+
Default yield ratio comes from the SNOW material in taichi's MPM implementation:
16+
https://github.com/taichi-dev/taichi_elements/blob/d19678869a28b09a32ef415b162e35dc929b792d/engine/mpm_solver.py#L434
1617
1718
Parameters
1819
----------

genesis/options/surfaces.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,16 @@
1313
class Surface(Options):
1414
"""
1515
Base class for all surfaces types in Genesis.
16-
A ``Surface`` object encapsulates all visual information used for rendering an entity or its sub-components (links, geoms, etc.)
17-
The surface contains different types textures: diffuse_texture, specular_texture, roughness_texture, metallic_texture, transmission_texture, normal_texture, and emissive_texture. Each one of them is a `gs.textures.Texture` object.
16+
17+
A ``Surface`` object encapsulates all visual information used for rendering an entity or its sub-components (links,
18+
geoms, ...). The surface contains different types textures: diffuse_texture, specular_texture, roughness_texture,
19+
metallic_texture, transmission_texture, normal_texture, and emissive_texture. Each one of them is a
20+
`gs.textures.Texture` object.
1821
1922
Tip
2023
---
21-
If any of the textures only has single value (instead of a map), you can use the shortcut attribute (e.g., `color`, `roughness`, `metallic`, `emissive`) instead of creating a texture object.
24+
If any of the textures only has single value (instead of a map), you can use the shortcut attribute (e.g., `color`,
25+
`roughness`, `metallic`, `emissive`) instead of creating a texture object.
2226
2327
Note
2428
----
@@ -49,26 +53,26 @@ class Surface(Options):
4953
emissive_texture : gs.textures.Texture | None, optional
5054
Emissive texture of the surface.
5155
default_roughness : float, optional
52-
Default roughness value when `roughness` is not set and the asset does not have a roughness texture. Defaults to 1.0.
56+
Default roughness value when `roughness` is not set and the asset does not have a roughness texture. Defaults
57+
to 1.0.
5358
vis_mode : str | None, optional
54-
How the entity should be visualized. Possible values are ['visual', 'particle', 'collision', 'sdf', 'recon'].
55-
59+
How the entity should be visualized, e.g.
5660
- 'visual': Render the entity's visual geometry.
5761
- 'collision': Render the entity's collision geometry.
5862
- 'particle': Render the entity's particle representation (if applicable).
5963
- 'sdf': Render the reconstructed surface mesh of the entity's sdf.
6064
- 'recon': Render the reconstructed surface mesh of the entity's particle representation.
61-
6265
smooth : bool, optional
6366
Whether to smooth face normals by interpolating vertex normals.
6467
double_sided : bool | None, optional
65-
Whether to render both sides of the surface. Useful for non-watertight 2D objects. Defaults to True for Cloth material and False for others.
68+
Whether to render both sides of the surface. Useful for non-watertight 2D objects. Defaults to True for Cloth
69+
material and False for others.
6670
beam_angle : float
6771
The beam angle of emission. Defaults to 180.0.
6872
normal_diff_clamp : float, optional
6973
Controls the threshold for computing surface normals by interpolating vertex normals.
7074
recon_backend : str, optional
71-
Backend for surface reconstruction. Possible values are ['splashsurf', 'openvdb'].
75+
Backend for surface reconstruction. Possible values are ['splashsurf', 'openvdb']. Defaults to 'splashsurf'.
7276
generate_foam : bool, optional
7377
Whether to generate foam particles for visual effects for particle-based entities.
7478
foam_options : gs.options.FoamOptions, optional
@@ -92,14 +96,12 @@ class Surface(Options):
9296

9397
default_roughness: float = 1.0
9498

95-
vis_mode: Optional[str] = None # ['visual', 'particle', 'collision', 'sdf', 'recon']
96-
smooth: bool = True # whether to smooth face normals by interpolating vertex normals
97-
double_sided: Optional[bool] = (
98-
None # whether to render both sides of the surface. Defaults to True for Cloth material and False for others.
99-
)
99+
vis_mode: Optional[str] = None
100+
smooth: bool = True
101+
double_sided: Optional[bool] = None
100102
beam_angle: float = 180
101103
normal_diff_clamp: float = 180
102-
recon_backend: str = "splashsurf" # backend for surface recon
104+
recon_backend: str = "splashsurf"
103105
generate_foam: bool = False
104106
foam_options: Optional[FoamOptions] = None
105107

genesis/utils/particle.py

Lines changed: 20 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import tempfile
77

88
import igl
9+
import pysplashsurf
910
import numpy as np
1011
import trimesh
1112

@@ -347,48 +348,26 @@ def parse_args(backend):
347348
return trimesh.Trimesh(vertices, faces, process=False)
348349

349350
elif "splashsurf" in backend:
350-
if gs.platform != "Linux":
351-
gs.raise_exception("Backend 'splashsurf' is only supported on Linux.")
352-
353-
fd, xyz_path = tempfile.mkstemp(suffix=".xyz")
354-
os.close(fd)
355-
fd, obj_path = tempfile.mkstemp(suffix=".obj")
356-
os.close(fd)
357-
positions.astype(np.float32, copy=False).tofile(xyz_path)
358-
359351
# Suggested value is 1.4-1.6, but 1.0 seems more detailed
360-
radius_scale = args_dict.get("rscale", 1.0)
361-
smooth_iter = args_dict.get("smooth")
362-
r = radius * radius_scale
363-
364-
try:
365-
command = ["splashsurf", "reconstruct", xyz_path, f"-r={r}", "-c=0.8", "-l=2.0", "-t=0.6", "-o", obj_path]
366-
if smooth_iter is not None:
367-
command += [
368-
"--mesh-cleanup=on",
369-
"--mesh-smoothing-weights=on",
370-
f"--mesh-smoothing-iters={int(smooth_iter)}",
371-
"--normals=on",
372-
"--normals-smoothing-iters=10",
373-
]
374-
375-
result = subprocess.run(map(str, command), capture_output=True, text=True)
376-
if result.stdout:
377-
gs.logger.debug(result.stdout)
378-
if result.stderr:
379-
gs.logger.warning(result.stderr)
380-
if os.path.getsize(obj_path) == 0:
381-
raise OSError("Output OBJ file is empty.")
382-
383-
# Read the generated OBJ file
384-
mesh = trimesh.load_mesh(obj_path)
385-
gs.logger.debug(f"[splashsurf]: reconstruct vertices: {mesh.vertices.shape}, {mesh.faces.shape}")
386-
except OSError as e:
387-
gs.raise_exception_from("Surface reconstruction failed.", e)
388-
finally:
389-
os.remove(xyz_path)
390-
os.remove(obj_path)
391-
352+
mesh_with_data, _ = pysplashsurf.reconstruction_pipeline(
353+
positions,
354+
particle_radius=radius * args_dict.get("rscale", 1.0),
355+
smoothing_length=2.0,
356+
cube_size=0.8,
357+
iso_surface_threshold=0.6,
358+
mesh_smoothing_weights=True,
359+
mesh_smoothing_iters=int(args_dict.get("smooth", 25)),
360+
normals_smoothing_iters=10,
361+
mesh_cleanup=True,
362+
compute_normals=True,
363+
enable_multi_threading=True,
364+
)
365+
mesh = trimesh.Trimesh(
366+
vertices=mesh_with_data.mesh.vertices,
367+
faces=mesh_with_data.mesh.triangles,
368+
face_normals=mesh_with_data.get_point_attribute("normals"),
369+
)
370+
gs.logger.debug(f"[splashsurf]: reconstruct vertices: {mesh.vertices.shape}, {mesh.faces.shape}")
392371
return mesh
393372

394373
else:

genesis/utils/path_planing.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -617,12 +617,11 @@ def plan(
617617

618618
@ti.data_oriented
619619
class RRTConnect(PathPlanner):
620-
def _init_rrt_connect_fields(self, goal_bias=0.1, max_nodes=4000, pos_tol=5e-3, max_step_size=0.05):
620+
def _init_rrt_connect_fields(self, goal_bias=0.1, max_nodes=4000, max_step_size=0.05):
621621
self._is_rrt_connect_init = getattr(self, "_is_rrt_connect_init", False)
622622
if not self._is_rrt_connect_init:
623623
self._rrt_goal_bias = goal_bias
624624
self._rrt_max_nodes = max_nodes
625-
self._rrt_pos_tol = pos_tol
626625
self._rrt_max_step_size = max_step_size
627626
self._rrt_start_configuration = ti.field(
628627
dtype=gs.ti_float, shape=self._solver._batch_shape(self._entity.n_qs)

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ dependencies = [
5959
"gs-madrona>=0.0.2; platform_system == 'Linux' and (platform_machine == 'x86_64' or platform_machine == 'AMD64')",
6060
# Used for mesh simplification
6161
"fast_simplification>=0.1.12",
62+
# Surface reconstruction library for particle data from SPH simulations
63+
"pysplashsurf",
6264
]
6365

6466
[project.optional-dependencies]

0 commit comments

Comments
 (0)