Example #1
0
            public SkinnedMeshRenderer SetupSkinnedRenderer(GameObject go, Mesh mesh, GLTFNode.ImportResult[] nodes)
            {
                SkinnedMeshRenderer smr = go.AddComponent <SkinnedMeshRenderer>();

                Transform[] bones = new Transform[joints.Length];
                for (int i = 0; i < bones.Length; i++)
                {
                    int jointNodeIndex = joints[i];
                    GLTFNode.ImportResult jointNode = nodes[jointNodeIndex];
                    bones[i] = jointNode.transform;
                    if (string.IsNullOrEmpty(jointNode.transform.name))
                    {
                        jointNode.transform.name = "joint" + i;
                    }
                }
                smr.bones    = bones;
                smr.rootBone = bones[0];

                // Bindposes
                if (inverseBindMatrices != null)
                {
                    if (inverseBindMatrices.Length != joints.Length)
                    {
                        Debug.LogWarning("InverseBindMatrices count and joints count not the same");
                    }
                    Matrix4x4   m         = nodes[0].transform.localToWorldMatrix;
                    Matrix4x4[] bindPoses = new Matrix4x4[joints.Length];
                    for (int i = 0; i < joints.Length; i++)
                    {
                        bindPoses[i] = inverseBindMatrices[i];
                    }
                    mesh.bindposes = bindPoses;
                }
                else
                {
                    Matrix4x4   m         = nodes[0].transform.localToWorldMatrix;
                    Matrix4x4[] bindPoses = new Matrix4x4[joints.Length];
                    for (int i = 0; i < joints.Length; i++)
                    {
                        bindPoses[i] = nodes[joints[i]].transform.worldToLocalMatrix * m;
                    }
                    mesh.bindposes = bindPoses;
                }
                smr.sharedMesh = mesh;
                return(smr);
            }
Example #2
0
        public ImportResult Import(GLTFAccessor.ImportResult[] accessors, GLTFNode.ImportResult[] nodes, ImportSettings importSettings)
        {
            bool multiRoots = nodes.Where(x => x.IsRoot).Count() > 1;

            ImportResult result = new ImportResult();

            result.clip           = new AnimationClip();
            result.clip.name      = name;
            result.clip.frameRate = importSettings.animationSettings.frameRate;

            result.clip.legacy = importSettings.animationSettings.useLegacyClips;

            if (result.clip.legacy && importSettings.animationSettings.looping)
            {
                result.clip.wrapMode = WrapMode.Loop;
            }

            for (int i = 0; i < channels.Length; i++)
            {
                Channel channel = channels[i];
                if (samplers.Length <= channel.sampler)
                {
                    Debug.LogWarning($"GLTFUtility: Animation channel points to sampler at index {channel.sampler} which doesn't exist. Skipping animation clip.");
                    continue;
                }
                Sampler sampler = samplers[channel.sampler];

                // Get interpolation mode
                InterpolationMode interpolationMode = importSettings.animationSettings.interpolationMode;
                if (interpolationMode == InterpolationMode.ImportFromFile)
                {
                    interpolationMode = sampler.interpolation;
                }
                if (interpolationMode == InterpolationMode.CUBICSPLINE)
                {
                    Debug.LogWarning("Animation interpolation mode CUBICSPLINE not fully supported, result might look different.");
                }

                string relativePath = "";

                GLTFNode.ImportResult node = nodes[channel.target.node.Value];
                while (node != null && !node.IsRoot)
                {
                    if (string.IsNullOrEmpty(relativePath))
                    {
                        relativePath = node.transform.name;
                    }
                    else
                    {
                        relativePath = node.transform.name + "/" + relativePath;
                    }

                    if (node.parent.HasValue)
                    {
                        node = nodes[node.parent.Value];
                    }
                    else
                    {
                        node = null;
                    }
                }

                // If file has multiple root nodes, a new parent will be created for them as a final step of the import process. This parent f***s up the curve relative paths.
                // Add node.transform.name to path if there are multiple roots. This is not the most elegant fix but it works.
                // See GLTFNodeExtensions.GetRoot
                if (multiRoots)
                {
                    relativePath = node.transform.name + "/" + relativePath;
                }

                System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;
                float[] keyframeInput = accessors[sampler.input].ReadFloat().ToArray();
                switch (channel.target.path)
                {
                case "translation":
                    Vector3[]      pos  = accessors[sampler.output].ReadVec3().ToArray();
                    AnimationCurve posX = new AnimationCurve();
                    AnimationCurve posY = new AnimationCurve();
                    AnimationCurve posZ = new AnimationCurve();
                    for (int k = 0; k < keyframeInput.Length; k++)
                    {
                        posX.AddKey(CreateKeyframe(k, keyframeInput, pos, x => - x.x, interpolationMode));
                        posY.AddKey(CreateKeyframe(k, keyframeInput, pos, x => x.y, interpolationMode));
                        posZ.AddKey(CreateKeyframe(k, keyframeInput, pos, x => x.z, interpolationMode));
                    }
                    result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.x", posX);
                    result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.y", posY);
                    result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.z", posZ);
                    break;

                case "rotation":
                    Vector4[]      rot  = accessors[sampler.output].ReadVec4().ToArray();
                    AnimationCurve rotX = new AnimationCurve();
                    AnimationCurve rotY = new AnimationCurve();
                    AnimationCurve rotZ = new AnimationCurve();
                    AnimationCurve rotW = new AnimationCurve();
                    for (int k = 0; k < keyframeInput.Length; k++)
                    {
                        // The Animation window in Unity shows keyframes incorrectly converted to euler. This is only to deceive you. The quaternions underneath work correctly
                        rotX.AddKey(CreateKeyframe(k, keyframeInput, rot, x => x.x, interpolationMode));
                        rotY.AddKey(CreateKeyframe(k, keyframeInput, rot, x => - x.y, interpolationMode));
                        rotZ.AddKey(CreateKeyframe(k, keyframeInput, rot, x => - x.z, interpolationMode));
                        rotW.AddKey(CreateKeyframe(k, keyframeInput, rot, x => x.w, interpolationMode));
                    }
                    result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.x", rotX);
                    result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.y", rotY);
                    result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.z", rotZ);
                    result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.w", rotW);
                    break;

                case "scale":
                    Vector3[]      scale  = accessors[sampler.output].ReadVec3().ToArray();
                    AnimationCurve scaleX = new AnimationCurve();
                    AnimationCurve scaleY = new AnimationCurve();
                    AnimationCurve scaleZ = new AnimationCurve();
                    for (int k = 0; k < keyframeInput.Length; k++)
                    {
                        scaleX.AddKey(CreateKeyframe(k, keyframeInput, scale, x => x.x, interpolationMode));
                        scaleY.AddKey(CreateKeyframe(k, keyframeInput, scale, x => x.y, interpolationMode));
                        scaleZ.AddKey(CreateKeyframe(k, keyframeInput, scale, x => x.z, interpolationMode));
                    }
                    result.clip.SetCurve(relativePath, typeof(Transform), "localScale.x", scaleX);
                    result.clip.SetCurve(relativePath, typeof(Transform), "localScale.y", scaleY);
                    result.clip.SetCurve(relativePath, typeof(Transform), "localScale.z", scaleZ);
                    break;

                case "weights":
                    GLTFNode.ImportResult skinnedMeshNode     = nodes[channel.target.node.Value];
                    SkinnedMeshRenderer   skinnedMeshRenderer = skinnedMeshNode.transform.GetComponent <SkinnedMeshRenderer>();

                    int numberOfBlendShapes           = skinnedMeshRenderer.sharedMesh.blendShapeCount;
                    AnimationCurve[] blendShapeCurves = new AnimationCurve[numberOfBlendShapes];
                    for (int j = 0; j < numberOfBlendShapes; ++j)
                    {
                        blendShapeCurves[j] = new AnimationCurve();
                    }

                    float[] weights      = accessors[sampler.output].ReadFloat().ToArray();
                    float[] weightValues = new float[keyframeInput.Length];

                    float[] previouslyKeyedValues = new float[numberOfBlendShapes];

                    // Reference for my future self:
                    // keyframeInput.Length = number of keyframes
                    // keyframeInput[ k ] = timestamp of keyframe
                    // weights.Length = number of keyframes * number of blendshapes
                    // weights[ j ] = actual animated weight of a specific blend shape
                    // (index into weights[] array accounts for keyframe index and blend shape index)

                    for (int k = 0; k < keyframeInput.Length; ++k)
                    {
                        for (int j = 0; j < numberOfBlendShapes; ++j)
                        {
                            int weightIndex = (k * numberOfBlendShapes) + j;
                            weightValues[k] = weights[weightIndex];

                            bool addKey = true;
                            if (importSettings.animationSettings.compressBlendShapeKeyFrames)
                            {
                                if (k == 0 || !Mathf.Approximately(weightValues[k], previouslyKeyedValues[j]))
                                {
                                    if (k > 0)
                                    {
                                        weightValues[k - 1] = previouslyKeyedValues[j];
                                        blendShapeCurves[j].AddKey(CreateKeyframe(k - 1, keyframeInput, weightValues, x => x, interpolationMode));
                                    }
                                    addKey = true;
                                    previouslyKeyedValues[j] = weightValues[k];
                                }
                                else
                                {
                                    addKey = false;
                                }
                            }

                            if (addKey)
                            {
                                blendShapeCurves[j].AddKey(CreateKeyframe(k, keyframeInput, weightValues, x => x, interpolationMode));
                            }
                        }
                    }

                    for (int j = 0; j < numberOfBlendShapes; ++j)
                    {
                        string propertyName = "blendShape." + skinnedMeshRenderer.sharedMesh.GetBlendShapeName(j);
                        result.clip.SetCurve(relativePath, typeof(SkinnedMeshRenderer), propertyName, blendShapeCurves[j]);
                    }
                    break;
                }
            }
            return(result);
        }
Example #3
0
            public override IEnumerator OnCoroutine(Action <float> onProgress = null)
            {
                // No nodes
                if (nodes == null)
                {
                    if (onProgress != null)
                    {
                        onProgress.Invoke(1f);
                    }
                    IsCompleted = true;
                    yield break;
                }

                Result = new ImportResult[nodes.Count];


                // Initialize transforms
                for (int i = 0; i < Result.Length; i++)
                {
                    Result[i]                = new GLTFNode.ImportResult();
                    Result[i].transform      = new GameObject().transform;
                    Result[i].transform.name = nodes[i].name;
                }
                // Set up hierarchy
                for (int i = 0; i < Result.Length; i++)
                {
                    if (nodes[i].children != null)
                    {
                        int[] children = nodes[i].children;
                        Result[i].children = children;
                        for (int k = 0; k < children.Length; k++)
                        {
                            int childIndex = children[k];
                            Result[childIndex].parent           = i;
                            Result[childIndex].transform.parent = Result[i].transform;
                        }
                    }
                }
                // Apply TRS
                for (int i = 0; i < Result.Length; i++)
                {
                    nodes[i].ApplyTRS(Result[i].transform);
                }
                // Setup components
                for (int i = 0; i < Result.Length; i++)
                {
                    // Setup mesh
                    if (nodes[i].mesh.HasValue)
                    {
                        GLTFMesh.ImportResult meshResult = meshTask.Result[nodes[i].mesh.Value];
                        if (meshResult == null)
                        {
                            continue;
                        }

                        Mesh     mesh = meshResult.mesh;
                        Renderer renderer;
                        if (nodes[i].skin.HasValue)
                        {
                            GLTFSkin.ImportResult skin = skinTask.Result[nodes[i].skin.Value];
                            renderer = skin.SetupSkinnedRenderer(Result[i].transform.gameObject, mesh, Result);
                        }
                        else if (mesh.blendShapeCount > 0)
                        {
                            // Blend shapes require skinned mesh renderer
                            SkinnedMeshRenderer mr = Result[i].transform.gameObject.AddComponent <SkinnedMeshRenderer>();
                            mr.sharedMesh = mesh;
                            renderer      = mr;
                        }
                        else
                        {
                            MeshRenderer mr = Result[i].transform.gameObject.AddComponent <MeshRenderer>();
                            MeshFilter   mf = Result[i].transform.gameObject.AddComponent <MeshFilter>();
                            renderer      = mr;
                            mf.sharedMesh = mesh;
                        }
                        //Materials
                        renderer.materials = meshResult.materials;
                        if (string.IsNullOrEmpty(Result[i].transform.name))
                        {
                            Result[i].transform.name = "node" + i;
                        }
                    }
                    else
                    {
                        if (string.IsNullOrEmpty(Result[i].transform.name))
                        {
                            Result[i].transform.name = "node" + i;
                        }
                    }

                    // Setup camera
                    if (nodes[i].camera.HasValue)
                    {
                        GLTFCamera cameraData = cameras[nodes[i].camera.Value];
                        Camera     camera     = Result[i].transform.gameObject.AddComponent <Camera>();
                        if (cameraData.type == CameraType.orthographic)
                        {
                            camera.orthographic     = true;
                            camera.nearClipPlane    = cameraData.orthographic.znear;
                            camera.farClipPlane     = cameraData.orthographic.zfar;
                            camera.orthographicSize = cameraData.orthographic.ymag;
                        }
                        else
                        {
                            camera.orthographic  = false;
                            camera.nearClipPlane = cameraData.perspective.znear;
                            if (cameraData.perspective.zfar.HasValue)
                            {
                                camera.farClipPlane = cameraData.perspective.zfar.Value;
                            }
                            if (cameraData.perspective.aspectRatio.HasValue)
                            {
                                camera.aspect = cameraData.perspective.aspectRatio.Value;
                            }
                            camera.fieldOfView = Mathf.Rad2Deg * cameraData.perspective.yfov;
                        }
                    }
                }
                IsCompleted = true;
            }