rdeioris / glTFRuntime

Unreal Engine Plugin for loading glTF files at runtime
MIT License
346 stars 119 forks source link

SkeletalMesh is detected as StaticMesh #68

Closed improbable-andreaskrugersen closed 1 year ago

improbable-andreaskrugersen commented 1 year ago

Hi,

when loading https://ht290.s3.amazonaws.com/kitten_walk.glb, the contained mesh is interpreted as a static mesh, although the GLB file contains animations. If I load this in other clients, e.g. with the three.js GLTFLoader or in the Windows 3D Viewer, the mesh is correctly animated though.

If I understand correctly, that decision is made based on whether the mesh node contains a skin value. Looking at the JSON data, it seems that the kitten_GEO node does indeed not have this value. And a quick look at the three.js GLTFLoader code suggests that it's also using that check to determine whether it's a skinned mesh. But both those loaders seem to interpret it correctly after all while glTFRuntime doesn't.

rdeioris commented 1 year ago

Hi, this is not related to glTFRuntime, but to how Unreal works:

In glTFRuntime the first problem has been solved by adding tick-based node animations, in-fact if you open the outliner details you will see that the asset exposes a node based curve. Unfortunately the curve modifies the morph targets, and they do not exist in a static mesh (and so we fall into the second problem).

The second problem can be addressed by forcing the generation of a dumb skeleton (just the root) by enabling the StaticMeshAsSkeletalMesh flag:

image

The cool side-effect is that now the cat walking animation become a true unreal skeletal-animation (that obviously just modifies morph targets)

improbable-andreaskrugersen commented 1 year ago

Thanks for responding.

Unfortunately, we can't use this flag for various reasons, the most important being that our users can import arbitrary, user-generated glb files and if I understand correctly, setting this flag would turn all static meshes into skeletal meshes, which is not what we want.

We'd need a way to determine programmatically whether this case was encountered or not. How can I check that? I should mention that we're not using AglTFRuntimeAssetActor or AglTFRuntimeAssetActorAsync directly but we've extracted the logic of the latter into our own actor, so we're flexible in adding additional logic here.

rdeioris commented 1 year ago

@improbable-andreaskrugersen probably (for your case) the best approach is to set statics as skeletals if morph targets are present:

{
    "primitives": [
        {
            "attributes": {
                "NORMAL": 23,
                "POSITION": 22,
                "TANGENT": 24,
                "TEXCOORD_0": 25
            },
            "indices": 21,
            "material": 3,
            "targets": [
                {
                    "NORMAL": 33,
                    "POSITION": 32,
                    "TANGENT": 34
                },
                {
                    "NORMAL": 43,
                    "POSITION": 42,
                    "TANGENT": 44
                }
            ]
        }
    ],
    "weights": [0, 0.5]
}

"targets" is a property of the primitive object, so you can check for the size of the array len(meshes[mesh_id][primitives][any_id][targets]) > 0.

You can use the low level JSON api of Unreal (given that you are working in cpp) or you can rely on the gltfruntime simplified api described here: https://github.com/rdeioris/glTFRuntime-docs/blob/master/VRM.md#extensionsvrmmeta

alexellismpg commented 1 year ago

The second problem can be addressed by forcing the generation of a dumb skeleton (just the root) by enabling the StaticMeshAsSkeletalMesh flag:

image

@rdeioris Hi, whereabouts is this StaticMeshAsSkeletalMesh flag? I'm struggling to find it

rdeioris commented 12 months ago

@alexellismpg hi, it is in the options you get when you use the "spawn actor from class" node and select the gltfruntime asset actor