[Bf-extensions-cvs] [bb4dc6f1] master: glTF importer: performance: rewrite importer using numpy
Julien Duroure
noreply at git.blender.org
Tue Jul 21 20:33:17 CEST 2020
Commit: bb4dc6f1daab2cc19c79e981222387069fb97ec4
Author: Julien Duroure
Date: Tue Jul 21 20:21:30 2020 +0200
Branches: master
https://developer.blender.org/rBAbb4dc6f1daab2cc19c79e981222387069fb97ec4
glTF importer: performance: rewrite importer using numpy
Thanks scurest!
===================================================================
M io_scene_gltf2/__init__.py
M io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
M io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
D io_scene_gltf2/blender/imp/gltf2_blender_primitive.py
M io_scene_gltf2/io/imp/gltf2_io_binary.py
M io_scene_gltf2/io/imp/gltf2_io_gltf.py
===================================================================
diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py
index 0beb10a1..6404fc20 100755
--- a/io_scene_gltf2/__init__.py
+++ b/io_scene_gltf2/__init__.py
@@ -15,7 +15,7 @@
bl_info = {
'name': 'glTF 2.0 format',
'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
- "version": (1, 3, 34),
+ "version": (1, 3, 35),
'blender': (2, 90, 0),
'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0',
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py b/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
index efa7f003..226720a3 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
@@ -50,9 +50,9 @@ class BlenderGlTF():
@staticmethod
def set_convert_functions(gltf):
- yup2zup = bpy.app.debug_value != 100
+ gltf.yup2zup = bpy.app.debug_value != 100
- if yup2zup:
+ if gltf.yup2zup:
# glTF Y-Up space --> Blender Z-up space
# X,Y,Z --> X,-Z,Y
def convert_loc(x): return Vector([x[0], -x[2], x[1]])
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py b/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
index 7914a41b..33578de8 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
@@ -13,11 +13,12 @@
# limitations under the License.
import bpy
-import bmesh
+from mathutils import Vector, Matrix
+import numpy as np
+from ...io.imp.gltf2_io_binary import BinaryData
from ..com.gltf2_blender_extras import set_extras
from .gltf2_blender_material import BlenderMaterial
-from .gltf2_blender_primitive import BlenderPrimitive
class BlenderMesh():
@@ -28,118 +29,511 @@ class BlenderMesh():
@staticmethod
def create(gltf, mesh_idx, skin_idx):
"""Mesh creation."""
- pymesh = gltf.data.meshes[mesh_idx]
+ return create_mesh(gltf, mesh_idx, skin_idx)
- # Create one bmesh, add all primitives to it, and then convert it to a
- # mesh.
- bme = bmesh.new()
- # List of all the materials this mesh will use. The material each
- # primitive uses is set by giving an index into this list.
- materials = []
+# Maximum number of TEXCOORD_n/COLOR_n sets to import
+UV_MAX = 8
+COLOR_MAX = 8
- # Process all primitives
- for prim in pymesh.primitives:
- if prim.material is None:
- material_idx = None
- else:
- pymaterial = gltf.data.materials[prim.material]
-
- vertex_color = None
- if 'COLOR_0' in prim.attributes:
- vertex_color = 'COLOR_0'
- # Create Blender material if needed
- if vertex_color not in pymaterial.blender_material:
- BlenderMaterial.create(gltf, prim.material, vertex_color)
- material_name = pymaterial.blender_material[vertex_color]
- material = bpy.data.materials[material_name]
+def create_mesh(gltf, mesh_idx, skin_idx):
+ pymesh = gltf.data.meshes[mesh_idx]
+ name = pymesh.name or 'Mesh_%d' % mesh_idx
+ mesh = bpy.data.meshes.new(name)
- try:
- material_idx = materials.index(material.name)
- except ValueError:
- materials.append(material.name)
- material_idx = len(materials) - 1
+ # Temporarily parent the mesh to an object.
+ # This is used to set skin weights and shapekeys.
+ tmp_ob = None
+ try:
+ tmp_ob = bpy.data.objects.new('##gltf-import:tmp-object##', mesh)
+ do_primitives(gltf, mesh_idx, skin_idx, mesh, tmp_ob)
- BlenderPrimitive.add_primitive_to_bmesh(gltf, bme, pymesh, prim, skin_idx, material_idx)
+ finally:
+ if tmp_ob:
+ bpy.data.objects.remove(tmp_ob)
- name = pymesh.name or 'Mesh_' + str(mesh_idx)
- mesh = bpy.data.meshes.new(name)
- BlenderMesh.bmesh_to_mesh(gltf, pymesh, bme, mesh)
- bme.free()
- for name_material in materials:
- mesh.materials.append(bpy.data.materials[name_material])
- mesh.update()
+ return mesh
- set_extras(mesh, pymesh.extras, exclude=['targetNames'])
- # Clear accessor cache after all primitives are done
- gltf.accessor_cache = {}
+def do_primitives(gltf, mesh_idx, skin_idx, mesh, ob):
+ """Put all primitive data into the mesh."""
+ pymesh = gltf.data.meshes[mesh_idx]
- return mesh
+ # Scan the primitives to find out what we need to create
- @staticmethod
- def bmesh_to_mesh(gltf, pymesh, bme, mesh):
- bme.to_mesh(mesh)
-
- # Unfortunately need to do shapekeys/normals/smoothing ourselves.
-
- # Shapekeys
- if len(bme.verts.layers.shape) != 0:
- # The only way I could find to create a shape key was to temporarily
- # parent mesh to an object and use obj.shape_key_add.
- tmp_ob = None
- try:
- tmp_ob = bpy.data.objects.new('##gltf-import:tmp-object##', mesh)
- tmp_ob.shape_key_add(name='Basis')
- mesh.shape_keys.name = mesh.name
- for layer_name in bme.verts.layers.shape.keys():
- tmp_ob.shape_key_add(name=layer_name)
- key_block = mesh.shape_keys.key_blocks[layer_name]
- layer = bme.verts.layers.shape[layer_name]
-
- for i, v in enumerate(bme.verts):
- key_block.data[i].co = v[layer]
- finally:
- if tmp_ob:
- bpy.data.objects.remove(tmp_ob)
-
- # Normals
- mesh.update()
+ has_normals = False
+ num_uvs = 0
+ num_cols = 0
+ num_joint_sets = 0
+ for prim in pymesh.primitives:
+ if 'POSITION' not in prim.attributes:
+ continue
if gltf.import_settings['import_shading'] == "NORMALS":
- mesh.create_normals_split()
-
- use_smooths = [] # whether to smooth for each poly
- face_idx = 0
- for prim in pymesh.primitives:
- if gltf.import_settings['import_shading'] == "FLAT" or \
- 'NORMAL' not in prim.attributes:
- use_smooths += [False] * prim.num_faces
- elif gltf.import_settings['import_shading'] == "SMOOTH":
- use_smooths += [True] * prim.num_faces
- elif gltf.import_settings['import_shading'] == "NORMALS":
- mesh_loops = mesh.loops
- for fi in range(face_idx, face_idx + prim.num_faces):
- poly = mesh.polygons[fi]
- # "Flat normals" are when all the vertices in poly have the
- # poly's normal. Otherwise, smooth the poly.
- for loop_idx in range(poly.loop_start, poly.loop_start + poly.loop_total):
- vi = mesh_loops[loop_idx].vertex_index
- if poly.normal.dot(bme.verts[vi].normal) <= 0.9999999:
- use_smooths.append(True)
- break
- else:
- use_smooths.append(False)
+ if 'NORMAL' in prim.attributes:
+ has_normals = True
+
+ if skin_idx is not None:
+ i = 0
+ while ('JOINTS_%d' % i) in prim.attributes and \
+ ('WEIGHTS_%d' % i) in prim.attributes:
+ i += 1
+ num_joint_sets = max(i, num_joint_sets)
+
+ i = 0
+ while i < UV_MAX and ('TEXCOORD_%d' % i) in prim.attributes: i += 1
+ num_uvs = max(i, num_uvs)
+
+ i = 0
+ while i < COLOR_MAX and ('COLOR_%d' % i) in prim.attributes: i += 1
+ num_cols = max(i, num_cols)
+
+ num_shapekeys = 0
+ for morph_i, _ in enumerate(pymesh.primitives[0].targets or []):
+ if pymesh.shapekey_names[morph_i] is not None:
+ num_shapekeys += 1
+
+ # -------------
+ # We'll process all the primitives gathering arrays to feed into the
+ # various foreach_set function that create the mesh data.
+
+ num_faces = 0 # total number of faces
+ vert_locs = np.empty(dtype=np.float32, shape=(0,3)) # coordinate for each vert
+ vert_normals = np.empty(dtype=np.float32, shape=(0,3)) # normal for each vert
+ edge_vidxs = np.array([], dtype=np.uint32) # vertex_index for each loose edge
+ loop_vidxs = np.array([], dtype=np.uint32) # vertex_index for each loop
+ loop_uvs = [
+ np.empty(dtype=np.float32, shape=(0,2)) # UV for each loop for each layer
+ for _ in range(num_uvs)
+ ]
+ loop_cols = [
+ np.empty(dtype=np.float32, shape=(0,4)) # color for each loop for each layer
+ for _ in range(num_cols)
+ ]
+ vert_joints = [
+ np.empty(dtype=np.uint32, shape=(0,4)) # 4 joints for each vert for each set
+ for _ in range(num_joint_sets)
+ ]
+ vert_weights = [
+ np.empty(dtype=np.float32, shape=(0,4)) # 4 weights for each vert for each set
+ for _ in range(num_joint_sets)
+ ]
+ sk_vert_locs = [
+ np.empty(dtype=np.float32, shape=(0,3)) # coordinate for each vert for each shapekey
+ for _ in range(num_shapekeys)
+ ]
+
+ for prim in pymesh.primitives:
+ prim.num_faces = 0
+
+ if 'POSITION' not in prim.attributes:
+ continue
+
+ vert_index_base = len(vert_locs)
+
+ if prim.indices is not None:
+ indices = BinaryData.decode_accessor(gltf, prim.indices)
+ indices = indices.reshape(len(indices))
+ else:
+ num_verts = gltf.data.accessors[prim.attributes['POSITION']].count
+ indices = np.arange(0, num_verts, dtype=np.uint32)
+
+ mode = 4 if prim.mode is None else prim.mode
+ points, edges, tris = points_edges_tris(mode, indices)
+ if points is not None:
+ indices = points
+ elif edges is not None:
+ indices = edges
+
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-extensions-cvs
mailing list