@@ -230,14 +230,17 @@ def _local_mesh_filename(self, url):
230230
231231
232232def _dae_mesh_importer (filename , precision ):
233- """This is a very simple implementation of a DAE/Collada parser."""
233+ """This is a very simple implementation of a DAE/Collada parser.
234+
235+ Collada specification: https://www.khronos.org/files/collada_spec_1_5.pdf
236+ """
234237 dae = XML .from_file (filename )
235238 meshes = []
236239 visual_scenes = dae .root .find ('library_visual_scenes' )
237240 materials = dae .root .find ('library_materials' )
238241 effects = dae .root .find ('library_effects' )
239242
240- for geometry in dae .root .findall ('./ /geometry' ):
243+ for geometry in dae .root .findall ('library_geometries /geometry' ):
241244 mesh_xml = geometry .find ('mesh' )
242245 mesh_id = geometry .attrib ['id' ]
243246 matrix_node = visual_scenes .find ('visual_scene/node/instance_geometry[@url="#{}"]/../matrix' .format (mesh_id ))
@@ -254,16 +257,28 @@ def _dae_mesh_importer(filename, precision):
254257 M = M [0 :4 ], M [4 :8 ], M [8 :12 ], M [12 :16 ]
255258 transform = Transformation .from_matrix (M )
256259
257- for triangle_set in mesh_xml .findall ('triangles' ):
258- triangle_set_data = triangle_set .find ('p' ).text .split ()
260+ # primitive elements can be any combination of:
261+ # lines, linestrips, polygons, polylist, triangles, trifans, tristrips
262+ # The current implementation only supports triangles and polylist of triangular meshes
263+ primitive_element_sets = []
264+ primitive_element_sets .extend (mesh_xml .findall ('triangles' ))
265+ primitive_element_sets .extend (mesh_xml .findall ('polylist' ))
266+
267+ if len (primitive_element_sets ) == 0 :
268+ raise Exception ('No primitive elements found (currently only triangles and polylist are supported)' )
269+
270+ for primitive_element_set in primitive_element_sets :
271+ primitive_tag = primitive_element_set .tag
272+ primitive_set_data = primitive_element_set .find ('p' ).text .split ()
259273
260274 # Try to retrieve mesh colors
261275 mesh_colors = {}
262276
263277 if materials is not None and effects is not None :
264278 try :
265279 instance_effect = None
266- material_id = triangle_set .attrib .get ('material' )
280+ material_id = primitive_element_set .attrib .get ('material' )
281+ primitive_count = int (primitive_element_set .attrib ['count' ])
267282
268283 if material_id is not None :
269284 instance_effect = materials .find ('material[@id="{}"]/instance_effect' .format (material_id ))
@@ -278,16 +293,37 @@ def _dae_mesh_importer(filename, precision):
278293 LOGGER .exception ('Exception while loading materials, all materials of mesh file %s will be ignored ' , filename )
279294
280295 # Parse vertices
281- vertices_input = triangle_set .find ('input[@semantic="VERTEX"]' )
282- vertices_link = mesh_xml .find ('vertices[@id="{}"]/input' .format (vertices_input .attrib ['source' ][1 :]))
296+ all_offsets = sorted ([int (i .attrib ['offset' ]) for i in primitive_element_set .findall ('input[@offset]' )])
297+ if not all_offsets :
298+ raise Exception ('Primitive element node does not contain offset information! Primitive tag={}' .format (primitive_tag ))
299+
300+ vertices_input = primitive_element_set .find ('input[@semantic="VERTEX"]' )
301+ vertices_id = vertices_input .attrib ['source' ][1 :]
302+ vertices_link = mesh_xml .find ('vertices[@id="{}"]/input' .format (vertices_id ))
283303 positions = mesh_xml .find ('source[@id="{}"]/float_array' .format (vertices_link .attrib ['source' ][1 :]))
284304 positions = positions .text .split ()
285305
286306 vertices = [[float (p ) for p in positions [i :i + 3 ]] for i in range (0 , len (positions ), 3 )]
287307
288308 # Parse faces
289- faces = [int (f ) for f in triangle_set_data [::2 ]] # Ignore normals (every second item is normal index)
290- faces = [faces [i :i + 3 ] for i in range (0 , len (faces ), 3 )]
309+ # Every nth element is a vertex key, we ignore the rest based on the offsets defined
310+ # Usually, every second item is the normal, but there can be other items offset in there (vertex tangents, etc)
311+ skip_step = 1 + all_offsets [- 1 ]
312+
313+ if primitive_tag == 'triangles' :
314+ vcount = [3 ] * primitive_count
315+ elif primitive_tag == 'polylist' :
316+ vcount = [int (v ) for v in primitive_element_set .find ('vcount' ).text .split ()]
317+
318+ if len (vcount ) != primitive_count :
319+ raise Exception ('Primitive count does not match vertex per face count, vertex input id={}' .format (vertices_id ))
320+
321+ fkeys = [int (f ) for f in primitive_set_data [::skip_step ]]
322+ faces = []
323+ for i in range (primitive_count ):
324+ a = i * vcount [i ]
325+ b = a + vcount [i ]
326+ faces .append (fkeys [a :b ])
291327
292328 # Rebuild vertices and faces using the same logic that other importers
293329 # use remapping everything based on a selected precision
0 commit comments