33import base64
44import warnings
55import 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
88import numpy as np
99
1010import meshcat
1111from meshcat .geometry import (
12- ReferenceSceneElement , Geometry , TriangularMeshGeometry , pack_numpy_array )
12+ ReferenceSceneElement , Geometry , TriangularMeshGeometry )
1313
1414import hppfcl
1515import 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-
9285class 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
0 commit comments