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 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; }
public ImportResult Import(GLTFAccessor.ImportResult[] accessors, GLTFNode.ImportResult[] nodes, ImportSettings importSettings) { ImportResult result = new ImportResult(); result.clip = new AnimationClip(); result.clip.name = name; result.clip.frameRate = importSettings.frameRate; if (importSettings.useLegacyClips) { result.clip.legacy = true; } for (int i = 0; i < channels.Length; i++) { Channel channel = channels[i]; if (samplers.Length <= channel.sampler) { Debug.LogWarning("Animation channel points to sampler at index " + channel.sampler + " which doesn't exist. Skipping animation clip."); continue; } Sampler sampler = samplers[channel.sampler]; ImportSettings.InterpolationMode GetInterpolationMode(string samplerInterpolationMode) { if (importSettings.interpolationMode == ImportSettings.InterpolationMode.ImportFromFile) { if (samplerInterpolationMode == "STEP") { return(ImportSettings.InterpolationMode.Step); } else if (samplerInterpolationMode == "LINEAR") { return(ImportSettings.InterpolationMode.Linear); } else if (samplerInterpolationMode == "CUBICSPLINE") { return(ImportSettings.InterpolationMode.CubicSpline); } else { Debug.LogWarning($"Unsupported interpolation mode: {samplerInterpolationMode}. Defaulting to STEP."); return(ImportSettings.InterpolationMode.Step); } } else { return(importSettings.interpolationMode); } } var interpolationMode = GetInterpolationMode(sampler.interpolation); 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; } } 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(keyframeInput[k], pos[k].x, interpolationMode)); posY.AddKey(CreateKeyframe(keyframeInput[k], pos[k].y, interpolationMode)); posZ.AddKey(CreateKeyframe(keyframeInput[k], -pos[k].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(); bool willProcessSteppedKeyframes = interpolationMode == ImportSettings.InterpolationMode.Step && Application.isEditor && !Application.isPlaying; // @HACK: Creating stepped tangent keyframes is only supported in-editor -- not at runtime (Unity API restriction) #if UNITY_EDITOR // 🤢🤮 if (willProcessSteppedKeyframes) { AnimationCurve rotX = new AnimationCurve(); AnimationCurve rotY = new AnimationCurve(); AnimationCurve rotZ = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { Vector3 eulerRotation = new Quaternion(rot[k].x, rot[k].y, -rot[k].z, -rot[k].w).eulerAngles; rotX.AddKey(CreateKeyframe(keyframeInput[k], eulerRotation.x, interpolationMode)); rotY.AddKey(CreateKeyframe(keyframeInput[k], eulerRotation.y, interpolationMode)); rotZ.AddKey(CreateKeyframe(keyframeInput[k], eulerRotation.z, interpolationMode)); } EditorCurveBinding GetEditorBinding(string property) { return(EditorCurveBinding.DiscreteCurve(relativePath, typeof(Transform), property)); } // Null out any other euler rotation curves on this clip, just to be safe. // https://forum.unity.com/threads/new-animationclip-property-names.367288/#post-2384172 AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAngles.x"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAngles.y"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAngles.z"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("m_LocalEulerAngles.x"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("m_LocalEulerAngles.y"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("m_LocalEulerAngles.z"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAnglesBaked.x"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAnglesBaked.y"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAnglesBaked.z"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("m_LocalEulerAnglesBaked.x"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("m_LocalEulerAnglesBaked.y"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("m_LocalEulerAnglesBaked.z"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAnglesRaw.x"), rotX); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAnglesRaw.y"), rotY); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAnglesRaw.z"), rotZ); } #endif if (!willProcessSteppedKeyframes) { AnimationCurve rotX = new AnimationCurve(); AnimationCurve rotY = new AnimationCurve(); AnimationCurve rotZ = new AnimationCurve(); AnimationCurve rotW = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { rotX.AddKey(CreateKeyframe(keyframeInput[k], rot[k].x, interpolationMode)); rotY.AddKey(CreateKeyframe(keyframeInput[k], rot[k].y, interpolationMode)); rotZ.AddKey(CreateKeyframe(keyframeInput[k], -rot[k].z, interpolationMode)); rotW.AddKey(CreateKeyframe(keyframeInput[k], -rot[k].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(keyframeInput[k], scale[k].x, interpolationMode)); scaleY.AddKey(CreateKeyframe(keyframeInput[k], scale[k].y, interpolationMode)); scaleZ.AddKey(CreateKeyframe(keyframeInput[k], scale[k].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[] 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; float weightValue = weights[weightIndex]; bool addKey = true; if (importSettings.compressBlendShapeKeyFrames) { if (k == 0 || !Mathf.Approximately(weightValue, previouslyKeyedValues[j])) { previouslyKeyedValues[j] = weightValue; addKey = true; } else { addKey = false; } } if (addKey) { blendShapeCurves[j].AddKey(CreateKeyframe(keyframeInput[k], weightValue, 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 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.legacy = importSettings.useLegacyClips; 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.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": Debug.LogWarning("GLTFUtility: Morph weights in animation is not supported"); break; } } return(result); }
public ImportResult Import(GLTFAccessor.ImportResult[] accessors, GLTFNode.ImportResult[] nodes, ImportSettings importSettings) { ImportResult result = new ImportResult(); result.clip = new AnimationClip(); result.clip.name = name; if (importSettings.useLegacyClips) { result.clip.legacy = true; } for (int i = 0; i < channels.Length; i++) { Channel channel = channels[i]; if (samplers.Length <= channel.sampler) { Debug.LogWarning("Animation channel points to sampler at index " + channel.sampler + " which doesn't exist. Skipping animation clip."); continue; } Sampler sampler = samplers[channel.sampler]; 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; } } 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(keyframeInput[k], pos[k].x); posY.AddKey(keyframeInput[k], pos[k].y); posZ.AddKey(keyframeInput[k], -pos[k].z); } 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++) { rotX.AddKey(keyframeInput[k], rot[k].x); rotY.AddKey(keyframeInput[k], rot[k].y); rotZ.AddKey(keyframeInput[k], -rot[k].z); rotW.AddKey(keyframeInput[k], -rot[k].w); } 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(keyframeInput[k], scale[k].x); scaleY.AddKey(keyframeInput[k], scale[k].y); scaleZ.AddKey(keyframeInput[k], scale[k].z); } 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": Debug.LogWarning("morph weights in animation is not supported"); break; } } return(result); }
public static GLTFNode.ImportResult[] Import(this List <GLTFNode> nodes, GLTFMesh.ImportResult[] meshes, GLTFSkin.ImportResult[] skins) { GLTFNode.ImportResult[] results = new GLTFNode.ImportResult[nodes.Count]; // Initialize transforms for (int i = 0; i < results.Length; i++) { results[i] = new GLTFNode.ImportResult(); results[i].transform = new GameObject().transform; results[i].transform.name = nodes[i].name; } // Set up hierarchy for (int i = 0; i < results.Length; i++) { if (nodes[i].children != null) { int[] children = nodes[i].children; results[i].children = children; for (int k = 0; k < children.Length; k++) { int childIndex = children[k]; results[childIndex].parent = i; results[childIndex].transform.parent = results[i].transform; } } } // Apply TRS for (int i = 0; i < results.Length; i++) { nodes[i].ApplyTRS(results[i].transform); } // Setup components for (int i = 0; i < results.Length; i++) { if (nodes[i].mesh.HasValue) { GLTFMesh.ImportResult meshResult = meshes[nodes[i].mesh.Value]; Mesh mesh = meshResult.mesh; Renderer renderer; if (nodes[i].skin.HasValue) { GLTFSkin.ImportResult skin = skins[nodes[i].skin.Value]; renderer = skin.SetupSkinnedRenderer(results[i].transform.gameObject, mesh, results); } else { MeshRenderer mr = results[i].transform.gameObject.AddComponent <MeshRenderer>(); MeshFilter mf = results[i].transform.gameObject.AddComponent <MeshFilter>(); renderer = mr; mf.sharedMesh = mesh; } //Materials renderer.materials = meshResult.materials; if (string.IsNullOrEmpty(results[i].transform.name)) { results[i].transform.name = "node" + i; } } else { if (string.IsNullOrEmpty(results[i].transform.name)) { results[i].transform.name = "node" + i; } } } return(results); }
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); }
protected override void OnMainThreadFinalize() { if (nodes == null) { return; } 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++) { 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 { 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; } } } }
protected override void OnMainThreadFinalize() { if (nodes == null) { return; } 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 { 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; } } } }