Skip to content

Commit e8c1459

Browse files
author
Alexis Duburcq
committed
[python/viewer] Fix capsule shading in Meshcat.
1 parent 2c74584 commit e8c1459

File tree

3 files changed

+39
-34
lines changed

3 files changed

+39
-34
lines changed

python/jiminy_py/src/jiminy_py/viewer/meshcat/meshcat_visualizer.py

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
import base64
44
import warnings
55
import xml.etree.ElementTree as Et
6-
from typing import Optional, Any, Dict, Union, Type
6+
from typing import Optional, Any, Dict, Union, Type, Set
77

88
import numpy as np
99

1010
import meshcat
1111
from meshcat.geometry import (
12-
ReferenceSceneElement, Geometry, TriangularMeshGeometry, pack_numpy_array)
12+
ReferenceSceneElement, Geometry, TriangularMeshGeometry)
1313

1414
import hppfcl
1515
import pinocchio as pin
@@ -56,7 +56,7 @@ def __init__(self, height: float, radius: float, num_segments: int = 32):
5656
for side, rng in enumerate([
5757
range(int(N // 4) + 1), range(int(N // 4), int(N // 2) + 1)]):
5858
for i in rng:
59-
for j in range(N + 1):
59+
for j in range(N):
6060
theta = j * 2 * math.pi / N
6161
phi = math.pi * (i / (N // 2) - 1 / 2)
6262
vertex = np.empty(3)
@@ -71,26 +71,21 @@ def __init__(self, height: float, radius: float, num_segments: int = 32):
7171
faces = []
7272
for i in range(int(N//2) + 1):
7373
for j in range(N):
74-
vec = np.array([i * (N + 1) + j,
75-
i * (N + 1) + (j + 1),
76-
(i + 1) * (N + 1) + (j + 1),
77-
(i + 1) * (N + 1) + j])
78-
if i == N // 4:
79-
faces.append(vec[[0, 2, 3]])
80-
faces.append(vec[[0, 1, 2]])
81-
else:
82-
faces.append(vec[[0, 1, 2]])
83-
faces.append(vec[[0, 2, 3]])
74+
vec = np.array([i * N + j,
75+
i * N + (j + 1) % N,
76+
(i + 1) * N + (j + 1) % N,
77+
(i + 1) * N + j])
78+
faces.append(vec[[0, 1, 2]])
79+
faces.append(vec[[0, 2, 3]])
8480
faces = np.vstack(faces)
8581

8682
# Init base class
8783
super().__init__(vertices, faces)
8884

89-
# TODO: Make the cache local to the meshcat server
90-
all_textures = set()
91-
9285
class DaeMeshGeometryWithTexture(ReferenceSceneElement):
93-
def __init__(self, dae_path: str) -> None:
86+
def __init__(self,
87+
dae_path: str,
88+
cache: Optional[Set[str]] = None) -> None:
9489
"""Load Collada files with texture images.
9590
9691
Inspired from
@@ -117,13 +112,17 @@ def __init__(self, dae_path: str) -> None:
117112
e.text for e in img_lib_element.iter()
118113
if e.tag.count('init_from')]
119114

120-
# Encode each texture in base64 for Three.js ColladaLoader to load it
115+
# Convert textures to data URL for Three.js ColladaLoader to load them
121116
self.img_resources = {}
122117
for img_path in img_resource_paths:
123-
if img_path in all_textures:
124-
self.img_resources[img_path] = ""
125-
continue
126-
all_textures.add(img_path)
118+
# Return empty string if already in cache
119+
if cache is not None:
120+
if img_path in cache:
121+
self.img_resources[img_path] = ""
122+
continue
123+
cache.add(img_path)
124+
125+
# Encode texture in base64
127126
img_path_abs = img_path
128127
if not os.path.isabs(img_path):
129128
img_path_abs = os.path.normpath(
@@ -151,7 +150,7 @@ def lower(self) -> Dict[str, Any]:
151150
'type':'_meshfile_object',
152151
'format': 'dae',
153152
'data': self.dae_raw,
154-
'resources': self.img_resources # Very costly
153+
'resources': self.img_resources
155154
}
156155
}
157156
}
@@ -169,13 +168,16 @@ class MeshcatVisualizer(BaseVisualizer):
169168
""" # noqa: E501
170169
def initViewer(self,
171170
viewer: meshcat.Visualizer = None,
171+
cache: Optional[Set[str]] = None,
172172
loadModel: bool = False,
173-
mustOpen: bool = False):
173+
mustOpen: bool = False,
174+
**kwargs: Any) -> None:
174175
"""Start a new MeshCat server and client.
175176
Note: the server can also be started separately using the
176177
"meshcat-server" command in a terminal: this enables the server to
177178
remain active after the current script ends.
178179
"""
180+
self.cache = cache
179181
self.root_name = None
180182
self.visual_group = None
181183
self.collision_group = None
@@ -255,24 +257,23 @@ def loadPrimitive(self, geometry_object: hppfcl.CollisionGeometry):
255257

256258
def loadMesh(self, geometry_object: hppfcl.CollisionGeometry):
257259
# Mesh path is empty if Pinocchio is built without HPP-FCL bindings
258-
if geometry_object.meshPath == "":
260+
mesh_path = geometry_object.meshPath
261+
if mesh_path == "":
259262
msg = ("Display of geometric primitives is supported only if "
260263
"pinocchio is build with HPP-FCL bindings.")
261264
warnings.warn(msg, category=UserWarning, stacklevel=2)
262265
return None
263266

264267
# Get file type from filename extension
265-
_, file_extension = os.path.splitext(geometry_object.meshPath)
268+
_, file_extension = os.path.splitext(mesh_path)
266269
if file_extension.lower() == ".dae":
267-
obj = DaeMeshGeometryWithTexture(geometry_object.meshPath)
270+
obj = DaeMeshGeometryWithTexture(mesh_path, self.cache)
268271
elif file_extension.lower() == ".obj":
269-
obj = meshcat.geometry.ObjMeshGeometry.from_file(
270-
geometry_object.meshPath)
272+
obj = meshcat.geometry.ObjMeshGeometry.from_file(mesh_path)
271273
elif file_extension.lower() == ".stl":
272-
obj = meshcat.geometry.StlMeshGeometry.from_file(
273-
geometry_object.meshPath)
274+
obj = meshcat.geometry.StlMeshGeometry.from_file(mesh_path)
274275
else:
275-
msg = f"Unknown mesh file format: {geometry_object.meshPath}."
276+
msg = f"Unknown mesh file format: {mesh_path}."
276277
warnings.warn(msg, category=UserWarning, stacklevel=2)
277278
obj = None
278279

python/jiminy_py/src/jiminy_py/viewer/panda3d/panda3d_visualizer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,8 @@ class Panda3dVisualizer(BaseVisualizer):
15371537
""" # noqa: E501
15381538
def initViewer(self,
15391539
viewer: Optional[Union[Panda3dViewer, Panda3dApp]] = None,
1540-
loadModel: bool = False) -> None:
1540+
loadModel: bool = False,
1541+
**kwargs: Any) -> None:
15411542
"""Init the viewer by attaching to / creating a GUI viewer.
15421543
"""
15431544
self.visual_group = None

python/jiminy_py/src/jiminy_py/viewer/viewer.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ class Viewer:
298298
"""
299299
backend = None
300300
window_name = 'jiminy'
301+
_cache = set()
301302
_has_gui = False
302303
_backend_obj = None
303304
_backend_proc = None
@@ -597,7 +598,8 @@ def _setup(self,
597598
pin.Force.Zero() for _ in range(njoints - 1)])
598599

599600
# Initialize the viewer
600-
self._client.initViewer(viewer=self._gui, loadModel=False)
601+
self._client.initViewer(
602+
viewer=self._gui, cache=Viewer._cache, loadModel=False)
601603

602604
# Create the scene and load robot
603605
self._client.loadViewerModel(
@@ -930,6 +932,7 @@ def close(self=None) -> None:
930932
Viewer._backend_obj = None
931933
Viewer._backend_proc = None
932934
Viewer._has_gui = False
935+
Viewer._cache.clear()
933936
else:
934937
# Disable travelling if associated with this viewer instance
935938
if (Viewer._camera_travelling is not None and

0 commit comments

Comments
 (0)