Skip to content

Commit 28ef278

Browse files
committed
GLTF: Support loading multi-primitive meshes
1 parent 3010aab commit 28ef278

File tree

1 file changed

+60
-47
lines changed

1 file changed

+60
-47
lines changed

demosys/scene/loaders/gltf.py

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ def load(self, scene, file=None):
107107
self.load_images()
108108
self.load_samplers()
109109
self.load_textures()
110-
self.load_meshes()
111110
self.load_materials()
111+
self.load_meshes()
112112
self.load_nodes()
113113

114114
self.scene.calc_scene_bbox()
@@ -170,10 +170,12 @@ def load_textures(self):
170170
self.textures.append(mt)
171171

172172
def load_meshes(self):
173-
for mesh in self.meta.meshes:
174-
m = mesh.load()
175-
self.meshes.append(m)
176-
self.scene.meshes.append(m)
173+
for meta_mesh in self.meta.meshes:
174+
# Returns a list of meshes
175+
meshes = meta_mesh.load(self.materials)
176+
self.meshes.append(meshes)
177+
for m in meshes:
178+
self.scene.meshes.append(m)
177179

178180
def load_materials(self):
179181
# Create material objects
@@ -187,11 +189,6 @@ def load_materials(self):
187189
self.materials.append(m)
188190
self.scene.materials.append(m)
189191

190-
# Map to meshes
191-
for i, mesh in enumerate(self.meta.meshes):
192-
if mesh.primitives[0].material is not None:
193-
self.meshes[i].material = self.materials[mesh.primitives[0].material]
194-
195192
def load_nodes(self):
196193
# Start with root nodes in the scene
197194
for node_id in self.meta.scenes[0].nodes:
@@ -207,7 +204,14 @@ def load_node(self, meta, parent=None):
207204
node.matrix = Matrix44(value=meta.matrix)
208205

209206
if meta.mesh is not None:
210-
node.mesh = self.meshes[meta.mesh]
207+
# Since we split up meshes with multiple primitives, this can be a list
208+
# If only one mesh we set it on the node as normal
209+
if len(self.meshes[meta.mesh]) == 1:
210+
node.mesh = self.meshes[meta.mesh][0]
211+
# If multiple meshes we add them as new child node
212+
elif len(self.meshes[meta.mesh]) > 1:
213+
for mesh in self.meshes[meta.mesh]:
214+
node.add_child(Node(mesh=mesh))
211215

212216
if meta.camera is not None:
213217
# FIXME: Use a proper camera class
@@ -345,9 +349,7 @@ def __init__(self, data):
345349
self.name = data.get('name')
346350
self.primitives = [Primitives(p) for p in data.get('primitives')]
347351

348-
def load(self):
349-
self.prepare_attrib_mapping()
350-
352+
def load(self, materials):
351353
name_map = {
352354
'POSITION': 'in_position',
353355
'NORMAL': 'in_normal',
@@ -358,48 +360,58 @@ def load(self):
358360
'COLOR_0': 'in_color0',
359361
}
360362

361-
vbos = self.prepare_attrib_mapping()
362-
vao = VAO(self.name, mode=self.primitives[0].mode or GL.GL_TRIANGLES)
363+
meshes = []
363364

364-
# Index buffer
365-
component_type, index_vbo = self.load_indices()
366-
if index_vbo:
367-
vao.set_element_buffer(component_type.value, index_vbo)
365+
# Read all primitives as separate meshes for now
366+
# According to the spec they can have different materials and vertex format
367+
for primitive in self.primitives:
368368

369-
attributes = {}
369+
vao = VAO(self.name, mode=primitive.mode or GL.GL_TRIANGLES)
370370

371-
for vbo_info in vbos:
372-
vbo = vbo_info.create()
373-
vao.add_array_buffer(vbo_info.component_type.value, vbo)
371+
# Index buffer
372+
component_type, index_vbo = self.load_indices(primitive)
373+
if index_vbo:
374+
vao.set_element_buffer(component_type.value, index_vbo)
374375

375-
for attr in vbo_info.attributes:
376-
vao.map_buffer(vbo, name_map[attr[0]], attr[1])
377-
attributes[attr[0]] = {
378-
'name': name_map[attr[0]],
379-
'components': attr[1],
380-
'type': vbo_info.component_type.value,
381-
}
376+
attributes = {}
382377

383-
vao.build()
378+
vbos = self.prepare_attrib_mapping(primitive)
384379

385-
bbox_min, bbox_max = self.get_bbox()
386-
return Mesh(
387-
self.name, vao=vao, attributes=attributes,
388-
bbox_min=bbox_min, bbox_max=bbox_max,
389-
)
380+
for vbo_info in vbos:
381+
vbo = vbo_info.create()
382+
vao.add_array_buffer(vbo_info.component_type.value, vbo)
383+
384+
for attr in vbo_info.attributes:
385+
vao.map_buffer(vbo, name_map[attr[0]], attr[1])
386+
attributes[attr[0]] = {
387+
'name': name_map[attr[0]],
388+
'components': attr[1],
389+
'type': vbo_info.component_type.value,
390+
}
391+
392+
vao.build()
393+
394+
bbox_min, bbox_max = self.get_bbox(primitive)
395+
meshes.append(Mesh(
396+
self.name, vao=vao, attributes=attributes,
397+
material=materials[primitive.material],
398+
bbox_min=bbox_min, bbox_max=bbox_max,
399+
))
400+
401+
return meshes
390402

391-
def load_indices(self):
392-
"""Loads the index buffer / polygon list"""
393-
if getattr(self.primitives[0], "indices") is None:
403+
def load_indices(self, primitive):
404+
"""Loads the index buffer / polygon list for a primitive"""
405+
if getattr(primitive, "indices") is None:
394406
return None, None
395407

396-
_, component_type, vbo = self.primitives[0].indices.read(target=GL.GL_ELEMENT_ARRAY_BUFFER)
408+
_, component_type, vbo = primitive.indices.read(target=GL.GL_ELEMENT_ARRAY_BUFFER)
397409
return component_type, vbo
398410

399-
def prepare_attrib_mapping(self):
411+
def prepare_attrib_mapping(self, primitive):
412+
"""Pre-parse buffer mappings for each VBO to detect interleaved data for a primitive"""
400413
buffer_info = []
401-
"""Pre-parse buffer mappings for each VBO to detect interleaved data"""
402-
for name, accessor in self.primitives[0].attributes.items():
414+
for name, accessor in primitive.attributes.items():
403415
info = VBOInfo(*accessor.info(target=GL.GL_ARRAY_BUFFER))
404416
info.attributes.append((name, info.components))
405417

@@ -412,9 +424,9 @@ def prepare_attrib_mapping(self):
412424

413425
return buffer_info
414426

415-
def get_bbox(self):
427+
def get_bbox(self, primitive):
416428
"""Get the bounding box for the mesh"""
417-
accessor = self.primitives[0].attributes.get('POSITION')
429+
accessor = primitive.attributes.get('POSITION')
418430
return accessor.min, accessor.max
419431

420432

@@ -610,7 +622,8 @@ def is_resource_node(self):
610622
class GLTFMaterial:
611623
def __init__(self, data):
612624
self.name = data.get('name')
613-
self.doubleSided = data.get('doubleSided') or False
625+
# Defaults to true if not defined
626+
self.doubleSided = data.get('doubleSided') or True
614627

615628
pbr = data['pbrMetallicRoughness']
616629
self.baseColorFactor = pbr.get('baseColorFactor')

0 commit comments

Comments
 (0)