private List <AnimationClip> ImportAnimationClips(glTF gltf, Axises invertAxis) { var animationClips = new List <AnimationClip>(); for (var i = 0; i < gltf.animations.Count; ++i) { var clip = new AnimationClip(); clip.ClearCurves(); clip.legacy = true; clip.name = gltf.animations[i].name; if (string.IsNullOrEmpty(clip.name)) { clip.name = $"legacy_{i}"; } clip.wrapMode = WrapMode.Loop; var animation = gltf.animations[i]; if (string.IsNullOrEmpty(animation.name)) { animation.name = $"animation:{i}"; } AxisInverter inverter = default; switch (invertAxis) { case Axises.X: inverter = AxisInverter.ReverseX; break; case Axises.Z: inverter = AxisInverter.ReverseZ; break; default: throw new System.Exception(); } animationClips.Add(AnimationImporterUtil.ConvertAnimationClip(gltf, animation, inverter)); } return(animationClips); }
public static void SetupSkinning(ImporterContext context, List <TransformWithSkin> nodes, int i, AxisInverter inverter) { var x = nodes[i]; var skinnedMeshRenderer = x.Transform.GetComponent <SkinnedMeshRenderer>(); if (skinnedMeshRenderer != null) { var mesh = skinnedMeshRenderer.sharedMesh; if (x.SkinIndex.HasValue) { if (mesh == null) { throw new Exception(); } if (skinnedMeshRenderer == null) { throw new Exception(); } if (x.SkinIndex.Value < context.GLTF.skins.Count) { // calculate internal values(boundingBox etc...) when sharedMesh assigned ? skinnedMeshRenderer.sharedMesh = null; var skin = context.GLTF.skins[x.SkinIndex.Value]; var joints = skin.joints.Select(y => nodes[y].Transform).ToArray(); if (joints.Any()) { // have bones skinnedMeshRenderer.bones = joints; if (skin.inverseBindMatrices != -1) { var bindPoses = context.GLTF.GetArrayFromAccessor <Matrix4x4>(skin.inverseBindMatrices) .Select(inverter.InvertMat4) .ToArray() ; mesh.bindposes = bindPoses; } else { // // calc default matrices // https://docs.unity3d.com/ScriptReference/Mesh-bindposes.html // var meshCoords = skinnedMeshRenderer.transform; // ? var calculatedBindPoses = joints.Select(y => y.worldToLocalMatrix * meshCoords.localToWorldMatrix).ToArray(); mesh.bindposes = calculatedBindPoses; } } else { // BlendShape only ? } skinnedMeshRenderer.sharedMesh = mesh; if (skin.skeleton >= 0 && skin.skeleton < nodes.Count) { skinnedMeshRenderer.rootBone = nodes[skin.skeleton].Transform; } } } } }
public static AnimationClip ConvertAnimationClip(glTF gltf, glTFAnimation animation, AxisInverter inverter, glTFNode root = null) { var clip = new AnimationClip(); clip.ClearCurves(); clip.legacy = true; clip.name = animation.name; clip.wrapMode = WrapMode.Loop; foreach (var channel in animation.channels) { var relativePath = RelativePathFrom(gltf.nodes, root, gltf.nodes[channel.target.node]); switch (channel.target.path) { case glTFAnimationTarget.PATH_TRANSLATION: { var sampler = animation.samplers[channel.sampler]; var input = gltf.GetArrayFromAccessor <float>(sampler.input); var output = gltf.FlatternFloatArrayFromAccessor(sampler.output); AnimationImporterUtil.SetAnimationCurve( clip, relativePath, new string[] { "localPosition.x", "localPosition.y", "localPosition.z" }, input, output, sampler.interpolation, typeof(Transform), (values, last) => { Vector3 temp = new Vector3(values[0], values[1], values[2]); return(inverter.InvertVector3(temp).ToArray()); } ); } break; case glTFAnimationTarget.PATH_ROTATION: { var sampler = animation.samplers[channel.sampler]; var input = gltf.GetArrayFromAccessor <float>(sampler.input); var output = gltf.FlatternFloatArrayFromAccessor(sampler.output); AnimationImporterUtil.SetAnimationCurve( clip, relativePath, new string[] { "localRotation.x", "localRotation.y", "localRotation.z", "localRotation.w" }, input, output, sampler.interpolation, typeof(Transform), (values, last) => { Quaternion currentQuaternion = new Quaternion(values[0], values[1], values[2], values[3]); Quaternion lastQuaternion = new Quaternion(last[0], last[1], last[2], last[3]); return(AnimationImporterUtil.GetShortest(lastQuaternion, inverter.InvertQuaternion(currentQuaternion)).ToArray()); } ); clip.EnsureQuaternionContinuity(); } break; case glTFAnimationTarget.PATH_SCALE: { var sampler = animation.samplers[channel.sampler]; var input = gltf.GetArrayFromAccessor <float>(sampler.input); var output = gltf.FlatternFloatArrayFromAccessor(sampler.output); AnimationImporterUtil.SetAnimationCurve( clip, relativePath, new string[] { "localScale.x", "localScale.y", "localScale.z" }, input, output, sampler.interpolation, typeof(Transform), (values, last) => values); } break; case glTFAnimationTarget.PATH_WEIGHT: { var node = gltf.nodes[channel.target.node]; var mesh = gltf.meshes[node.mesh]; var primitive = mesh.primitives.FirstOrDefault(); var targets = primitive.targets; if (!gltf_mesh_extras_targetNames.TryGet(mesh, out List <string> targetNames)) { throw new Exception("glTF BlendShape Animation. targetNames invalid."); } var keyNames = targetNames .Where(x => !string.IsNullOrEmpty(x)) .Select(x => "blendShape." + x) .ToArray(); var sampler = animation.samplers[channel.sampler]; var input = gltf.GetArrayFromAccessor <float>(sampler.input); var output = gltf.GetArrayFromAccessor <float>(sampler.output); AnimationImporterUtil.SetAnimationCurve( clip, relativePath, keyNames, input, output, sampler.interpolation, typeof(SkinnedMeshRenderer), (values, last) => { for (int j = 0; j < values.Length; j++) { values[j] *= 100.0f; } return(values); }); } break; default: Debug.LogWarningFormat("unknown path: {0}", channel.target.path); break; } } return(clip); }
// // fix node's coordinate. z-back to z-forward // public static void FixCoordinate(ImporterContext context, List <TransformWithSkin> nodes, AxisInverter inverter) { var globalTransformMap = nodes.ToDictionary(x => x.Transform, x => new PosRot { Position = x.Transform.position, Rotation = x.Transform.rotation, }); foreach (var x in context.GLTF.rootnodes) { // fix nodes coordinate // reverse Z in global var t = nodes[x].Transform; //t.SetParent(root.transform, false); foreach (var transform in t.Traverse()) { var g = globalTransformMap[transform]; transform.position = inverter.InvertVector3(g.Position); transform.rotation = inverter.InvertQuaternion(g.Rotation); } } }