Sitelet https://github.com/pycollada/pycollada/commit/15e2431be4ad5ee6a327f6e91849c3e546d53683
Skip to content

Commit 15e2431

Browse files
authored
More speed-ups (~8%) and clean-ups for DAE loading. (#160)
1 parent 321e9b6 commit 15e2431

13 files changed

Lines changed: 218 additions & 192 deletions

‎collada/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ def _loadScenes(self):
471471

472472
def _loadDefaultScene(self):
473473
"""Loads the default scene from <scene> tag in the root node."""
474-
node = self.xmlnode.find('%s/%s' % (self.tag('scene'), self.tag('instance_visual_scene')))
474+
node = self.xmlnode.find(f"{self.tag('scene')}/{self.tag('instance_visual_scene')}")
475475
try:
476476
if node is not None:
477477
sceneid = node.get('url')

‎collada/animation.py‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,9 @@ def load(collada, localscope, node):
3535
name = node.get('name') or ''
3636

3737
sourcebyid = localscope
38-
sources = []
3938
sourcenodes = node.findall(collada.tag('source'))
4039
for sourcenode in sourcenodes:
4140
ch = source.Source.load(collada, {}, sourcenode)
42-
sources.append(ch)
4341
sourcebyid[ch.id] = ch
4442

4543
child_nodes = node.findall(collada.tag('animation'))

‎collada/asset.py‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,7 @@ def save(self):
207207
@staticmethod
208208
def load(collada, localscope, node):
209209
contributornodes = node.findall(collada.tag('contributor'))
210-
contributors = []
211-
for contributornode in contributornodes:
212-
contributors.append(Contributor.load(collada, localscope, contributornode))
210+
contributors = [Contributor.load(collada, localscope, cn) for cn in contributornodes]
213211

214212
created = node.find(collada.tag('created'))
215213
if created is not None:

‎collada/camera.py‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class Camera(DaeObject):
2424

2525
@staticmethod
2626
def load(collada, localscope, node):
27-
tecnode = node.find('%s/%s' % (collada.tag('optics'), collada.tag('technique_common')))
27+
tecnode = node.find(f"{collada.tag('optics')}/{collada.tag('technique_common')}")
2828
if tecnode is None or len(tecnode) == 0:
2929
raise DaeIncompleteError('Missing common technique in camera')
3030
camnode = tecnode[0]
@@ -134,8 +134,7 @@ def save(self):
134134

135135
@staticmethod
136136
def load(collada, localscope, node):
137-
persnode = node.find('%s/%s/%s' % (collada.tag('optics'), collada.tag('technique_common'),
138-
collada.tag('perspective')))
137+
persnode = node.find(f"{collada.tag('optics')}/{collada.tag('technique_common')}/{collada.tag('perspective')}")
139138

140139
if persnode is None:
141140
raise DaeIncompleteError('Missing perspective for camera definition')
@@ -283,10 +282,7 @@ def save(self):
283282

284283
@staticmethod
285284
def load(collada, localscope, node):
286-
orthonode = node.find('%s/%s/%s' % (
287-
collada.tag('optics'),
288-
collada.tag('technique_common'),
289-
collada.tag('orthographic')))
285+
orthonode = node.find(f"{collada.tag('optics')}/{collada.tag('technique_common')}/{collada.tag('orthographic')}")
290286

291287
if orthonode is None:
292288
raise DaeIncompleteError('Missing orthographic for camera definition')

‎collada/controller.py‎

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,9 @@ def load(collada, localscope, node):
3939
raise DaeUnsupportedError('Unknown controller node')
4040

4141
sourcebyid = {}
42-
sources = []
43-
sourcenodes = node.findall('%s/%s' % (controller.tag, collada.tag('source')))
42+
sourcenodes = node.findall(f"{controller.tag}/{collada.tag('source')}")
4443
for sourcenode in sourcenodes:
4544
ch = source.Source.load(collada, {}, sourcenode)
46-
sources.append(ch)
4745
sourcebyid[ch.id] = ch
4846

4947
if controller.tag == collada.tag('skin'):
@@ -130,9 +128,7 @@ def __init__(self, sourcebyid, bind_shape_matrix, joint_source, joint_matrix_sou
130128
joint_matrices.shape = (-1, 4, 4)
131129
if len(joint_names) != len(joint_matrices):
132130
raise DaeMalformedError("Skin joint and matrix inputs must be same length")
133-
self.joint_matrices = {}
134-
for n, m in zip(joint_names, joint_matrices):
135-
self.joint_matrices[n] = m
131+
self.joint_matrices = dict(zip(joint_names, joint_matrices))
136132

137133
if not (weight_source in sourcebyid and weight_joint_source in sourcebyid):
138134
raise DaeBrokenRefError("Weights input in joints not found")
@@ -200,7 +196,7 @@ def load(collada, localscope, skinnode, controllernode):
200196
raise DaeMalformedError('Corrupted bind shape matrix in skin')
201197
bind_shape_mat = numpy.array(values, dtype=numpy.float32)
202198

203-
inputnodes = skinnode.findall('%s/%s' % (collada.tag('joints'), collada.tag('input')))
199+
inputnodes = skinnode.findall(f"{collada.tag('joints')}/{collada.tag('input')}")
204200
if inputnodes is None or len(inputnodes) < 2:
205201
raise DaeIncompleteError("Not enough inputs in skin joints")
206202

@@ -358,7 +354,7 @@ def load(collada, localscope, morphnode, controllernode):
358354
if not (method == 'NORMALIZED' or method == 'RELATIVE'):
359355
raise DaeMalformedError("Morph method must be either NORMALIZED or RELATIVE. Found '%s'" % method)
360356

361-
inputnodes = morphnode.findall('%s/%s' % (collada.tag('targets'), collada.tag('input')))
357+
inputnodes = morphnode.findall(f"{collada.tag('targets')}/{collada.tag('input')}")
362358
if inputnodes is None or len(inputnodes) < 2:
363359
raise DaeIncompleteError("Not enough inputs in a morph")
364360

‎collada/geometry.py‎

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,6 @@ def createPolygons(self, indices, inputlist, materialid=None):
170170
def load(collada, localscope, node):
171171
id = node.get("id") or ""
172172
name = node.get("name") or ""
173-
174173
tag_mesh = collada.tag('mesh')
175174
tag_source = collada.tag('source')
176175
tag_vertices = collada.tag('vertices')
@@ -187,55 +186,70 @@ def load(collada, localscope, node):
187186
meshnode = node.find(tag_mesh)
188187
if meshnode is None:
189188
raise DaeUnsupportedError('Unknown geometry node')
189+
190+
# Single pass over meshnode: collect sources, vertices, and primitive nodes
190191
sourcebyid = {}
191-
sources = []
192-
sourcenodes = node.findall('%s/%s' % (tag_mesh, tag_source))
193-
for sourcenode in sourcenodes:
194-
ch = source.Source.load(collada, {}, sourcenode)
195-
sources.append(ch)
196-
sourcebyid[ch.id] = ch
197-
198-
verticesnode = meshnode.find(tag_vertices)
192+
verticesnode = None
193+
primitive_nodes = []
194+
tri_tags = {tag_triangles, tag_tristrips, tag_trifans}
195+
196+
for subnode in meshnode:
197+
tag = subnode.tag
198+
if tag == tag_source:
199+
ch = source.Source.load(collada, {}, subnode)
200+
sourcebyid[ch.id] = ch
201+
elif tag == tag_vertices:
202+
verticesnode = subnode
203+
elif tag == tag_polylist:
204+
primitive_nodes.append(('polylist', subnode))
205+
elif tag in tri_tags:
206+
primitive_nodes.append(('triangles', subnode))
207+
elif tag == tag_lines:
208+
primitive_nodes.append(('lines', subnode))
209+
elif tag == tag_polygons:
210+
primitive_nodes.append(('polygons', subnode))
211+
elif tag != tag_extra:
212+
raise DaeUnsupportedError('Unknown geometry tag %s' % tag)
213+
214+
# Process vertices node (needs sourcebyid populated)
199215
if verticesnode is not None:
200216
inputnodes = {}
201-
for inputnode in verticesnode.findall(tag_input):
217+
for inputnode in verticesnode.iterfind(tag_input):
202218
semantic = inputnode.get('semantic')
203219
inputsource = inputnode.get('source')
204220
if not semantic or not inputsource or not inputsource.startswith('#'):
205221
raise DaeIncompleteError('Bad input definition inside vertices')
206222
inputnodes[semantic] = sourcebyid.get(inputsource[1:])
207-
if (not verticesnode.get('id') or len(inputnodes) == 0 or
208-
'POSITION' not in inputnodes):
223+
vertices_id = verticesnode.get('id')
224+
if not vertices_id or len(inputnodes) == 0 or 'POSITION' not in inputnodes:
209225
raise DaeIncompleteError('Bad vertices definition in mesh')
210-
sourcebyid[verticesnode.get('id')] = inputnodes
211-
verticesnode.get('id')
226+
sourcebyid[vertices_id] = inputnodes
212227

213-
double_sided_node = node.find('.//%s//%s' % (tag_extra, tag_double_sided))
228+
# Check for double_sided in extra nodes (on parent node, not meshnode)
214229
double_sided = False
215-
if double_sided_node is not None and double_sided_node.text is not None:
216-
try:
217-
val = int(double_sided_node.text)
218-
if val == 1:
219-
double_sided = True
220-
except ValueError:
221-
pass
222-
230+
for extra in node.iterfind(tag_extra):
231+
double_sided_node = extra.find(f".//{tag_double_sided}")
232+
if double_sided_node is not None and double_sided_node.text is not None:
233+
try:
234+
if int(double_sided_node.text) == 1:
235+
double_sided = True
236+
break
237+
except ValueError:
238+
pass
239+
240+
# Load primitives (needs complete sourcebyid with vertices)
223241
_primitives = []
224-
tri_tags = (tag_triangles, tag_tristrips, tag_trifans)
225-
226-
for subnode in meshnode:
227-
if subnode.tag == tag_polylist:
228-
_primitives.append(polylist.Polylist.load(collada, sourcebyid, subnode))
229-
elif subnode.tag in tri_tags:
230-
_primitives.append(triangleset.TriangleSet.load(collada, sourcebyid, subnode))
231-
elif subnode.tag == tag_lines:
232-
_primitives.append(lineset.LineSet.load(collada, sourcebyid, subnode))
233-
elif subnode.tag == tag_polygons:
234-
_primitives.append(polygons.Polygons.load(collada, sourcebyid, subnode))
235-
elif subnode.tag != tag_source and subnode.tag != tag_vertices and subnode.tag != tag_extra:
236-
raise DaeUnsupportedError('Unknown geometry tag %s' % subnode.tag)
237-
geom = Geometry(collada, id, name, sourcebyid, _primitives, xmlnode=node, double_sided=double_sided)
238-
return geom
242+
for ptype, pnode in primitive_nodes:
243+
if ptype == 'polylist':
244+
_primitives.append(polylist.Polylist.load(collada, sourcebyid, pnode))
245+
elif ptype == 'triangles':
246+
_primitives.append(triangleset.TriangleSet.load(collada, sourcebyid, pnode))
247+
elif ptype == 'lines':
248+
_primitives.append(lineset.LineSet.load(collada, sourcebyid, pnode))
249+
else: # polygons
250+
_primitives.append(polygons.Polygons.load(collada, sourcebyid, pnode))
251+
252+
return Geometry(collada, id, name, sourcebyid, _primitives, xmlnode=node, double_sided=double_sided)
239253

240254
def save(self):
241255
"""Saves the geometry back to :attr:`xmlnode`"""

‎collada/light.py‎

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -81,18 +81,16 @@ def save(self):
8181
"""Saves the light's properties back to :attr:`xmlnode`"""
8282
self.xmlnode.set('id', self.id)
8383
self.xmlnode.set('name', self.id)
84-
colornode = self.xmlnode.find('%s/%s/%s' % (tag('technique_common'),
85-
tag('directional'), tag('color')))
84+
colornode = self.xmlnode.find(f"{tag('technique_common')}/{tag('directional')}/{tag('color')}")
8685
colornode.text = ' '.join(map(str, self.color))
8786

8887
@staticmethod
8988
def load(collada, localscope, node):
90-
colornode = node.find('%s/%s/%s' % (collada.tag('technique_common'), collada.tag('directional'),
91-
collada.tag('color')))
89+
colornode = node.find(f"{collada.tag('technique_common')}/{collada.tag('directional')}/{collada.tag('color')}")
9290
if colornode is None:
9391
raise DaeIncompleteError('Missing color for directional light')
9492
try:
95-
color = tuple([float(v) for v in colornode.text.split()])
93+
color = tuple(float(v) for v in colornode.text.split())
9694
except ValueError:
9795
raise DaeMalformedError('Corrupted color values in light definition')
9896
return DirectionalLight(node.get('id'), color, xmlnode=node)
@@ -152,18 +150,16 @@ def save(self):
152150
"""Saves the light's properties back to :attr:`xmlnode`"""
153151
self.xmlnode.set('id', self.id)
154152
self.xmlnode.set('name', self.id)
155-
colornode = self.xmlnode.find('%s/%s/%s' % (tag('technique_common'),
156-
tag('ambient'), tag('color')))
153+
colornode = self.xmlnode.find(f"{tag('technique_common')}/{tag('ambient')}/{tag('color')}")
157154
colornode.text = ' '.join(map(str, self.color))
158155

159156
@staticmethod
160157
def load(collada, localscope, node):
161-
colornode = node.find('%s/%s/%s' % (collada.tag('technique_common'),
162-
collada.tag('ambient'), collada.tag('color')))
158+
colornode = node.find(f"{collada.tag('technique_common')}/{collada.tag('ambient')}/{collada.tag('color')}")
163159
if colornode is None:
164160
raise DaeIncompleteError('Missing color for ambient light')
165161
try:
166-
color = tuple([float(v) for v in colornode.text.split()])
162+
color = tuple(float(v) for v in colornode.text.split())
167163
except ValueError:
168164
raise DaeMalformedError('Corrupted color values in light definition')
169165
return AmbientLight(node.get('id'), color, xmlnode=node)
@@ -252,7 +248,7 @@ def save(self):
252248
"""Saves the light's properties back to :attr:`xmlnode`"""
253249
self.xmlnode.set('id', self.id)
254250
self.xmlnode.set('name', self.id)
255-
pnode = self.xmlnode.find('%s/%s' % (tag('technique_common'), tag('point')))
251+
pnode = self.xmlnode.find(f"{tag('technique_common')}/{tag('point')}")
256252
colornode = pnode.find(tag('color'))
257253
colornode.text = ' '.join(map(str, self.color))
258254
_correctValInNode(pnode, 'constant_attenuation', self.constant_att)
@@ -262,12 +258,12 @@ def save(self):
262258

263259
@staticmethod
264260
def load(collada, localscope, node):
265-
pnode = node.find('%s/%s' % (collada.tag('technique_common'), collada.tag('point')))
261+
pnode = node.find(f"{collada.tag('technique_common')}/{collada.tag('point')}")
266262
colornode = pnode.find(collada.tag('color'))
267263
if colornode is None:
268264
raise DaeIncompleteError('Missing color for point light')
269265
try:
270-
color = tuple([float(v) for v in colornode.text.split()])
266+
color = tuple(float(v) for v in colornode.text.split())
271267
except ValueError:
272268
raise DaeMalformedError('Corrupted color values in light definition')
273269
constant_att = linear_att = quad_att = zfar = None
@@ -376,7 +372,7 @@ def save(self):
376372
"""Saves the light's properties back to :attr:`xmlnode`"""
377373
self.xmlnode.set('id', self.id)
378374
self.xmlnode.set('name', self.id)
379-
pnode = self.xmlnode.find('%s/%s' % (tag('technique_common'), tag('spot')))
375+
pnode = self.xmlnode.find(f"{tag('technique_common')}/{tag('spot')}")
380376
colornode = pnode.find(tag('color'))
381377
colornode.text = ' '.join(map(str, self.color))
382378
_correctValInNode(pnode, 'constant_attenuation', self.constant_att)
@@ -387,12 +383,12 @@ def save(self):
387383

388384
@staticmethod
389385
def load(collada, localscope, node):
390-
pnode = node.find('%s/%s' % (collada.tag('technique_common'), collada.tag('spot')))
386+
pnode = node.find(f"{collada.tag('technique_common')}/{collada.tag('spot')}")
391387
colornode = pnode.find(collada.tag('color'))
392388
if colornode is None:
393389
raise DaeIncompleteError('Missing color for spot light')
394390
try:
395-
color = tuple([float(v) for v in colornode.text.split()])
391+
color = tuple(float(v) for v in colornode.text.split())
396392
except ValueError:
397393
raise DaeMalformedError('Corrupted color values in spot light definition')
398394
constant_att = linear_att = quad_att = falloff_ang = falloff_exp = None

‎collada/material.py‎

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -582,15 +582,14 @@ def getEffectParameters(collada, parentnode, localscope, params):
582582
params.append(param)
583583
localscope[param.id] = param
584584
else:
585-
floatnode = paramnode.find(collada.tag('float'))
586-
if floatnode is None:
587-
floatnode = paramnode.find(collada.tag('float2'))
588-
if floatnode is None:
589-
floatnode = paramnode.find(collada.tag('float3'))
590-
if floatnode is None:
591-
floatnode = paramnode.find(collada.tag('float4'))
585+
# Try float variants in order
586+
floatnode = None
587+
for float_type in ('float', 'float2', 'float3', 'float4'):
588+
floatnode = paramnode.find(collada.tag(float_type))
589+
if floatnode is not None:
590+
break
592591
paramid = paramnode.get('sid')
593-
if floatnode is not None and paramid is not None and len(paramid) > 0 and floatnode.text is not None:
592+
if floatnode is not None and paramid and len(paramid) > 0 and floatnode.text is not None:
594593
localscope[paramid] = [float(v) for v in floatnode.text.split()]
595594

596595
@staticmethod
@@ -661,13 +660,13 @@ def load(collada, localscope, node):
661660
props['opaque_mode'] = OPAQUE_MODE.RGB_ZERO
662661
props['xmlnode'] = node
663662

664-
bumpnode = node.find('.//%s//%s' % (collada.tag('extra'), collada.tag('texture')))
663+
bumpnode = node.find(f".//{collada.tag('extra')}//{collada.tag('texture')}")
665664
if bumpnode is not None:
666665
bumpmap = Map.load(collada, localscope, bumpnode)
667666
else:
668667
bumpmap = None
669668

670-
double_sided_node = node.find('.//%s//%s' % (collada.tag('extra'), collada.tag('double_sided')))
669+
double_sided_node = node.find(f".//{collada.tag('extra')}//{collada.tag('double_sided')}")
671670
double_sided = False
672671
if double_sided_node is not None and double_sided_node.text is not None:
673672
try:
@@ -687,7 +686,7 @@ def _loadShadingParam(collada, localscope, node):
687686
vnode = children[0]
688687
if vnode.tag == collada.tag('color'):
689688
try:
690-
value = tuple([float(v) for v in vnode.text.split()])
689+
value = tuple(float(v) for v in vnode.text.split())
691690
except ValueError:
692691
raise DaeMalformedError('Corrupted color definition in effect `{}`'.format(id))
693692
except IndexError:
@@ -780,7 +779,7 @@ def getPropNode(prop, value):
780779
if value is not None:
781780
shadnode.append(getPropNode(prop, value))
782781

783-
double_sided_node = profilenode.find('.//%s//%s' % (tag('extra'), tag('double_sided')))
782+
double_sided_node = profilenode.find(f".//{tag('extra')}//{tag('double_sided')}")
784783
if double_sided_node is None or double_sided_node.text is None:
785784
extranode = profilenode.find(tag('extra'))
786785
if extranode is None:

‎collada/polygons.py‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,8 @@ def __init__(self, sources, material, polygons, xmlnode=None):
3838
creating a geometry instance.
3939
"""
4040

41-
max_offset = max([max([input[0] for input in input_type_array])
42-
for input_type_array in sources.values()
43-
if len(input_type_array) > 0])
41+
# find max offset - flatten and find max in one pass
42+
max_offset = max(inp[0] for arr in sources.values() for inp in arr)
4443

4544
vcounts = numpy.zeros(len(polygons), dtype=numpy.int32)
4645
for i, poly in enumerate(polygons):

‎collada/polylist.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def __init__(self, sources, material, index, vcounts, xmlnode=None):
131131
if 'VERTEX' not in sources:
132132
raise DaeIncompleteError('Polylist requires vertex input')
133133

134-
# find max offset
134+
# find max offset - flatten and find max in one pass
135135
max_offset = max([max([input[0] for input in input_type_array])
136136
for input_type_array in sources.values() if len(input_type_array) > 0])
137137

0 commit comments

Comments
 (0)