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); }
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); }
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>(); Result[i].transform.localRotation = Result[i].transform.localRotation * Quaternion.Euler(0, 180, 0); 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; }