[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