question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Can't import mesh containing morph targets

See original GitHub issue

Describe the bug If a mesh contain the morph target which is on any primitive other than 1st, then it failed to import it.

To Reproduce Steps to reproduce the behavior:

  1. Import the attached .glb file.

Expected behavior not crash and import the file with all the morph targets.

File F_MED_BLK_Red_Head_01_LOD0.glb

Version

  • OS: Windows
  • Blender Version 3.0.0 and 3.1.2

Additional context

glTF file was written using SharpGLTF. Traceback:

Python: Traceback (most recent call last):
  File "C:\Program Files\Blender Foundation\Blender 3.1\3.1\scripts\addons\io_scene_gltf2\__init__.py", line 1109, in execute
    return self.import_gltf2(context)
  File "C:\Program Files\Blender Foundation\Blender 3.1\3.1\scripts\addons\io_scene_gltf2\__init__.py", line 1137, in import_gltf2
    if self.unit_import(path, import_settings) == {'FINISHED'}:
  File "C:\Program Files\Blender Foundation\Blender 3.1\3.1\scripts\addons\io_scene_gltf2\__init__.py", line 1157, in unit_import
    BlenderGlTF.create(gltf_importer)
  File "C:\Program Files\Blender Foundation\Blender 3.1\3.1\scripts\addons\io_scene_gltf2\blender\imp\gltf2_blender_gltf.py", line 42, in create
    BlenderGlTF._create(gltf)
  File "C:\Program Files\Blender Foundation\Blender 3.1\3.1\scripts\addons\io_scene_gltf2\blender\imp\gltf2_blender_gltf.py", line 49, in _create
    BlenderScene.create(gltf)
  File "C:\Program Files\Blender Foundation\Blender 3.1\3.1\scripts\addons\io_scene_gltf2\blender\imp\gltf2_blender_scene.py", line 47, in create
    BlenderNode.create_vnode(gltf, 'root')
  File "C:\Program Files\Blender Foundation\Blender 3.1\3.1\scripts\addons\io_scene_gltf2\blender\imp\gltf2_blender_node.py", line 55, in create_vnode
    BlenderNode.create_vnode(gltf, child)
  File "C:\Program Files\Blender Foundation\Blender 3.1\3.1\scripts\addons\io_scene_gltf2\blender\imp\gltf2_blender_node.py", line 55, in create_vnode
    BlenderNode.create_vnode(gltf, child)
  File "C:\Program Files\Blender Foundation\Blender 3.1\3.1\scripts\addons\io_scene_gltf2\blender\imp\gltf2_blender_node.py", line 41, in create_vnode
    obj = BlenderNode.create_object(gltf, vnode_id)
  File "C:\Program Files\Blender Foundation\Blender 3.1\3.1\scripts\addons\io_scene_gltf2\blender\imp\gltf2_blender_node.py", line 62, in create_object
    obj = BlenderNode.create_mesh_object(gltf, vnode)
  File "C:\Program Files\Blender Foundation\Blender 3.1\3.1\scripts\addons\io_scene_gltf2\blender\imp\gltf2_blender_node.py", line 234, in create_mesh_object
    mesh = BlenderMesh.create(gltf, pynode.mesh, pynode.skin)
  File "C:\Program Files\Blender Foundation\Blender 3.1\3.1\scripts\addons\io_scene_gltf2\blender\imp\gltf2_blender_mesh.py", line 35, in create
    return create_mesh(gltf, mesh_idx, skin_idx)
  File "C:\Program Files\Blender Foundation\Blender 3.1\3.1\scripts\addons\io_scene_gltf2\blender\imp\gltf2_blender_mesh.py", line 56, in create_mesh
    do_primitives(gltf, mesh_idx, skin_idx, mesh, tmp_ob)
  File "C:\Program Files\Blender Foundation\Blender 3.1\3.1\scripts\addons\io_scene_gltf2\blender\imp\gltf2_blender_mesh.py", line 192, in do_primitives
    if pymesh.shapekey_names[morph_i] is None:
IndexError: list index out of range
https://github.khronos.org/glTF-Validator/ Result
{
    "uri": "F_MED_BLK_Red_Head_01_LOD0.glb",
    "mimeType": "model/gltf-binary",
    "validatorVersion": "2.0.0-dev.3.7",
    "validatedAt": "2022-04-30T09:46:57.050Z",
    "issues": {
        "numErrors": 0,
        "numWarnings": 0,
        "numInfos": 18,
        "numHints": 0,
        "messages": [
            {
                "code": "UNUSED_MESH_TANGENT",
                "message": "Tangents are not used because the material has no normal texture.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/0/attributes/TANGENT"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/0/attributes/TEXCOORD_0"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/0/attributes/TEXCOORD_1"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/0/attributes/TEXCOORD_2"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/0/attributes/TEXCOORD_3"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/0/attributes/TEXCOORD_4"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/0/attributes/TEXCOORD_5"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/0/attributes/TEXCOORD_6"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/0/attributes/TEXCOORD_7"
            },
            {
                "code": "UNUSED_MESH_TANGENT",
                "message": "Tangents are not used because the material has no normal texture.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/1/attributes/TANGENT"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/1/attributes/TEXCOORD_0"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/1/attributes/TEXCOORD_1"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/1/attributes/TEXCOORD_2"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/1/attributes/TEXCOORD_3"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/1/attributes/TEXCOORD_4"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/1/attributes/TEXCOORD_5"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/1/attributes/TEXCOORD_6"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/meshes/0/primitives/1/attributes/TEXCOORD_7"
            }
        ],
        "truncated": false
    },
    "info": {
        "version": "2.0",
        "generator": "SharpGLTF 1.0.0-alpha0023",
        "resources": [
            {
                "pointer": "/buffers/0",
                "mimeType": "application/gltf-buffer",
                "storage": "glb",
                "byteLength": 848792
            }
        ],
        "animationCount": 0,
        "materialCount": 2,
        "hasMorphTargets": true,
        "hasSkins": true,
        "hasTextures": false,
        "hasDefaultScene": true,
        "drawCallCount": 2,
        "totalVertexCount": 5420,
        "totalTriangleCount": 7880,
        "maxUVs": 8,
        "maxInfluences": 4,
        "maxAttributes": 14
    }
}

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:5 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
scurestcommented, Apr 30, 2022

Here’s the patch I wrote. It allows mixing in prims without targets, and also mixing prims that do and do not morph POSITION in the same target.

Patch
diff --git a/addons/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py b/addons/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
index 92f8f469..1ea84321 100755
--- a/addons/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
+++ b/addons/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
@@ -167,22 +167,36 @@ class BlenderGlTF():
             mesh.shapekey_names = []
             used_names = set(['Basis']) #Be sure to not use 'Basis' name at import, this is a reserved name
 
-            # Some invalid glTF files has empty primitive tab
-            if len(mesh.primitives) > 0:
-                for sk, target in enumerate(mesh.primitives[0].targets or []):
-                    if 'POSITION' not in target:
+            # Look for primitive with morph targets
+            for prim in (mesh.primitives or []):
+                if not prim.targets:
+                    continue
+
+                for sk, _ in enumerate(prim.targets):
+                    # Skip shape key for target that doesn't morph POSITION
+                    morphs_position = any(
+                        (prim.targets and 'POSITION' in prim.targets[sk])
+                        for prim in mesh.primitives
+                    )
+                    if not morphs_position:
                         mesh.shapekey_names.append(None)
                         continue
 
-                    # Check if glTF file has some extras with targetNames. Otherwise
-                    # use the name of the POSITION accessor on the first primitive.
                     shapekey_name = None
-                    if mesh.extras is not None:
-                        if 'targetNames' in mesh.extras and sk < len(mesh.extras['targetNames']):
-                            shapekey_name = mesh.extras['targetNames'][sk]
+
+                    # Try to use name from extras.targetNames
+                    try:
+                        shapekey_name = str(mesh.extras['targetNames'][sk])
+                    except Exception:
+                        pass
+
+                    # Try to get name from first primitive's POSITION accessor
                     if shapekey_name is None:
-                        if gltf.data.accessors[target['POSITION']].name is not None:
-                            shapekey_name = gltf.data.accessors[target['POSITION']].name
+                        try:
+                            shapekey_name = gltf.data.accessors[mesh.primitives[0].targets[sk]['POSITION']].name
+                        except Exception:
+                            pass
+
                     if shapekey_name is None:
                         shapekey_name = "target_" + str(sk)
 
@@ -191,6 +205,8 @@ class BlenderGlTF():
 
                     mesh.shapekey_names.append(shapekey_name)
 
+                break
+
     @staticmethod
     def find_unused_name(haystack, desired_name):
         """Finds a name not in haystack and <= 63 UTF-8 bytes.
diff --git a/addons/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py b/addons/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
index 41dd4d03..e94df1c3 100644
--- a/addons/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
+++ b/addons/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py
@@ -98,11 +98,7 @@ def do_primitives(gltf, mesh_idx, skin_idx, mesh, ob):
         while i < COLOR_MAX and ('COLOR_%d' % i) in prim.attributes: i += 1
         num_cols = max(i, num_cols)
 
-    num_shapekeys = 0
-    if len(pymesh.primitives) > 0: # Empty primitive tab is not allowed, but some invalid files...
-        for morph_i, _ in enumerate(pymesh.primitives[0].targets or []):
-            if pymesh.shapekey_names[morph_i] is not None:
-                num_shapekeys += 1
+    num_shapekeys = sum(sk_name is not None for sk_name in pymesh.shapekey_names)
 
     # -------------
     # We'll process all the primitives gathering arrays to feed into the
@@ -188,12 +184,17 @@ def do_primitives(gltf, mesh_idx, skin_idx, mesh, ob):
             vert_joints[i] = np.concatenate((vert_joints[i], js))
             vert_weights[i] = np.concatenate((vert_weights[i], ws))
 
-        for morph_i, target in enumerate(prim.targets or []):
-            if pymesh.shapekey_names[morph_i] is None:
+        sk_i = 0
+        for sk, sk_name in enumerate(pymesh.shapekey_names):
+            if sk_name is None:
                 continue
-            morph_vs = BinaryData.decode_accessor(gltf, target['POSITION'], cache=True)
-            morph_vs = morph_vs[unique_indices]
-            sk_vert_locs[morph_i] = np.concatenate((sk_vert_locs[morph_i], morph_vs))
+            if prim.targets and 'POSITION' in prim.targets[sk]:
+                morph_vs = BinaryData.decode_accessor(gltf, prim.targets[sk]['POSITION'], cache=True)
+                morph_vs = morph_vs[unique_indices]
+            else:
+                morph_vs = np.zeros((len(unique_indices), 3), dtype=np.float32)
+            sk_vert_locs[sk_i] = np.concatenate((sk_vert_locs[sk_i], morph_vs))
+            sk_i += 1
 
         # inv_indices are the indices into the verts just for this prim;
         # calculate indices into the overall verts array
1reaction
scurestcommented, Apr 30, 2022

Okay, so since there are multiple ways to read the spec, this should probably be fixed anyway. There’s some other issues with morph targets now that I look. I can probably put up a patch later today.

edit: Changed my mind again. Based on the fact the validator flags this if the primitives are in the other order, it seems the fact this isn’t flagged invalid is just a minor bug in the validator.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Cannot import morph targets with skeletal mesh
Hello, When I try to import morph targets using the latest version of the Editor. They don't import, even though I checked to...
Read more >
Importing Morph Targets Always Fails - Epic Games Forums
I made a test cube mesh with different morph targets just to experiment with the process, but everytime I try to import it,...
Read more >
UE4 Importing Morph Targets - YouTube
How to import and control morph targets. ... playback doesn't begin shortly, try restarting your device. Your browser can't play this video.
Read more >
Maya 2019 fbx import with morphs problem - Autodesk Forums
when I import a fbx file with morphs in it (daz genesis 3 fbx with blendshapes), the morph targets don't show in the...
Read more >
Morph targets not showing from blender : r/unrealengine
Created a simple mesh with 5 shape keys in blender. Exported with animations and imported as a skeletal mesh into unreal with animation....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found