private void LoadAnimation(GLTF.Schema.Animation gltfAnimation, int index, AnimationClip clip) { clip.name = gltfAnimation.Name != null && gltfAnimation.Name.Length > 0 ? gltfAnimation.Name : "GLTFAnimation_" + index; for (int i = 0; i < gltfAnimation.Channels.Count; ++i) { AnimationChannel channel = gltfAnimation.Channels[i]; addGLTFChannelDataToClip(gltfAnimation.Channels[i], clip); } clip.EnsureQuaternionContinuity(); }
private GLTF.Schema.Animation SaveAnimationClip(Transform tr, AnimationClip clip, string prefix) { if (_animClip2anim.ContainsKey(clip)) { return _animClip2anim[clip]; } var anim = new GLTF.Schema.Animation { Name = prefix + "@" + clip.name }; var targets = BakeAnimationClip(anim, tr, clip); var accessors = _animClip2Accessors[clip]; var timeAccessorId = _animClip2TimeAccessor[clip]; int targetId = 0; int accessorId = 0; foreach (var target in targets) { var targetTr = tr.Find(targets[targetId]); var targetNode = tr2node[targetTr]; foreach (var accessor in accessors[targetId]) { var path = accessor.Key; anim.Channels.Add( new AnimationChannel { Sampler = new AnimationSamplerId { Id = accessorId }, Target = new AnimationChannelTarget { Path = path, Node = new NodeId { Id = root.Nodes.IndexOf(targetNode) } } } ); anim.Samplers.Add( new AnimationSampler { Input = timeAccessorId, Output = accessor.Value, Interpolation = InterpolationType.LINEAR } ); accessorId += 1; } targetId += 1; } root.Animations.Add(anim); _animClip2anim.Add(clip, anim); return anim; }
private List<string> BakeAnimationClip(GLTF.Schema.Animation anim, Transform tr, AnimationClip clip) { var needGenerate = !_animClip2Accessors.ContainsKey(clip); Dictionary<string, Dictionary<GLTFAnimationChannelPath, AnimationCurve[]>> curves = null; Dictionary<string, bool> rotationIsEuler = null; if (needGenerate) { curves = new Dictionary<string, Dictionary<GLTFAnimationChannelPath, AnimationCurve[]>>(); rotationIsEuler = new Dictionary<string, bool>(); } List<string> targets = new List<string>(); foreach (var binding in AnimationUtility.GetCurveBindings(clip)) { var path = binding.path; var curve = AnimationUtility.GetEditorCurve(clip, binding); if (!curves.ContainsKey(path)) { targets.Add(path); if (needGenerate) { curves.Add(path, new Dictionary<GLTFAnimationChannelPath, AnimationCurve[]>()); rotationIsEuler.Add(path, false); } } if (!needGenerate) { continue; } var current = curves[path]; if (binding.propertyName.Contains("m_LocalPosition")) { if (!current.ContainsKey(GLTFAnimationChannelPath.translation)) { current.Add(GLTFAnimationChannelPath.translation, new AnimationCurve[3]); } if (binding.propertyName.Contains(".x")) current[GLTFAnimationChannelPath.translation][0] = curve; else if (binding.propertyName.Contains(".y")) current[GLTFAnimationChannelPath.translation][1] = curve; else if (binding.propertyName.Contains(".z")) current[GLTFAnimationChannelPath.translation][2] = curve; } else if (binding.propertyName.Contains("m_LocalScale")) { if (!current.ContainsKey(GLTFAnimationChannelPath.scale)) { current.Add(GLTFAnimationChannelPath.scale, new AnimationCurve[3]); } if (binding.propertyName.Contains(".x")) current[GLTFAnimationChannelPath.scale][0] = curve; else if (binding.propertyName.Contains(".y")) current[GLTFAnimationChannelPath.scale][1] = curve; else if (binding.propertyName.Contains(".z")) current[GLTFAnimationChannelPath.scale][2] = curve; } else if (binding.propertyName.ToLower().Contains("localrotation")) { if (!current.ContainsKey(GLTFAnimationChannelPath.rotation)) { current.Add(GLTFAnimationChannelPath.rotation, new AnimationCurve[4]); } if (binding.propertyName.Contains(".x")) current[GLTFAnimationChannelPath.rotation][0] = curve; else if (binding.propertyName.Contains(".y")) current[GLTFAnimationChannelPath.rotation][1] = curve; else if (binding.propertyName.Contains(".z")) current[GLTFAnimationChannelPath.rotation][2] = curve; else if (binding.propertyName.Contains(".w")) current[GLTFAnimationChannelPath.rotation][3] = curve; } // Takes into account 'localEuler', 'localEulerAnglesBaked' and 'localEulerAnglesRaw' else if (binding.propertyName.ToLower().Contains("localeuler")) { if (!current.ContainsKey(GLTFAnimationChannelPath.rotation)) { current.Add(GLTFAnimationChannelPath.rotation, new AnimationCurve[3]); rotationIsEuler[path] = true; } if (binding.propertyName.Contains(".x")) current[GLTFAnimationChannelPath.rotation][0] = curve; else if (binding.propertyName.Contains(".y")) current[GLTFAnimationChannelPath.rotation][1] = curve; else if (binding.propertyName.Contains(".z")) current[GLTFAnimationChannelPath.rotation][2] = curve; } //todo: weights } if (!needGenerate) { return targets; } var bufferView = CreateStreamBufferView("animation-" + anim.Name); int nbSamples = (int)(clip.length * 30); float deltaTime = clip.length / nbSamples; var accessors = new List<Dictionary<GLTFAnimationChannelPath, AccessorId>>(); _animClip2Accessors.Add(clip, accessors); foreach (var path in curves.Keys) { var accessor = new Dictionary<GLTFAnimationChannelPath, AccessorId>(); accessors.Add(accessor); float[] times = new float[nbSamples]; Vector3[] translations = null; Vector3[] scales = null; Vector4[] rotations = null; foreach (var curve in curves[path]) { if (curve.Key == GLTFAnimationChannelPath.translation) { translations = new Vector3[nbSamples]; } else if (curve.Key == GLTFAnimationChannelPath.scale) { scales = new Vector3[nbSamples]; } else if (curve.Key == GLTFAnimationChannelPath.rotation) { rotations = new Vector4[nbSamples]; } } for (int i = 0; i < nbSamples; i += 1) { var currentTime = i * deltaTime; times[i] = currentTime; if (translations != null) { var curve = curves[path][GLTFAnimationChannelPath.translation]; translations[i] = new Vector3(curve[0].Evaluate(currentTime), curve[1].Evaluate(currentTime), curve[2].Evaluate(currentTime)); } if (scales != null) { var curve = curves[path][GLTFAnimationChannelPath.scale]; scales[i] = new Vector3(curve[0].Evaluate(currentTime), curve[1].Evaluate(currentTime), curve[2].Evaluate(currentTime)); } if (rotations != null) { var curve = curves[path][GLTFAnimationChannelPath.rotation]; if (rotationIsEuler[path]) { var q = Quaternion.Euler(curve[0].Evaluate(currentTime), curve[1].Evaluate(currentTime), curve[2].Evaluate(currentTime)); rotations[i] = new Vector4(q.x, q.y, q.z, q.w); } else { rotations[i] = new Vector4(curve[0].Evaluate(currentTime), curve[1].Evaluate(currentTime), curve[2].Evaluate(currentTime), curve[3].Evaluate(currentTime)); } } } if (!_animClip2TimeAccessor.ContainsKey(clip)) { var timeView = ExporterUtils.PackToBuffer(bufferView.streamBuffer, times, GLTFComponentType.Float); _animClip2TimeAccessor.Add(clip, AccessorToId(timeView, bufferView)); timeView.Name += "-times"; } AccessorId id = null; if (translations != null) { id = AccessorToId(ExporterUtils.PackToBuffer(bufferView.streamBuffer, translations, GLTFComponentType.Float, (Vector3[] data, int i) => { return Utils.ConvertVector3LeftToRightHandedness(ref data[i]); }), bufferView); accessor.Add(GLTFAnimationChannelPath.translation, id); id.Value.Name += "-" + path + "-" + "translation"; } if (scales != null) { id = AccessorToId(ExporterUtils.PackToBuffer(bufferView.streamBuffer, scales, GLTFComponentType.Float), bufferView); accessor.Add(GLTFAnimationChannelPath.scale, id); id.Value.Name += "-" + path + "-" + "scales"; } if (rotations != null) { id = AccessorToId(ExporterUtils.PackToBuffer(bufferView.streamBuffer, rotations, GLTFComponentType.Float, (Vector4[] data, int i) => { return Utils.ConvertVector4LeftToRightHandedness(ref data[i]); }), bufferView); accessor.Add(GLTFAnimationChannelPath.rotation, id); id.Value.Name += "-" + path + "-" + "rotations"; } } return targets; }
private void _exportAnimation(UnityEngine.AnimationClip animationClip) { // var frameCount = (int)Math.Floor(animationClip.length * animationClip.frameRate) + 1; var curveBinds = UnityEditor.AnimationUtility.GetCurveBindings(animationClip); var ignoreCurves = new List <UnityEditor.EditorCurveBinding>(); var glTFAnimation = new GLTF.Schema.Animation() { Name = animationClip.name, Channels = new List <AnimationChannel>(), Samplers = new List <AnimationSampler>(), Extensions = new Dictionary <string, IExtension>() { { AnimationExtensionFactory.EXTENSION_NAME, new AnimationExtension() { frameRate = animationClip.frameRate, clips = new List <AnimationClip>() { new AnimationClip() { name = animationClip.name, playTimes = _getPlayTimes(animationClip), position = 0.0f, duration = (float)Math.Round(animationClip.length, 6), } } } }, }, }; var ext = glTFAnimation.Extensions[AnimationExtensionFactory.EXTENSION_NAME] as AnimationExtension; this._root.Animations.Add(glTFAnimation); // Input. var inputAccessor = new Accessor(); inputAccessor.Count = frameCount; inputAccessor.Type = GLTFAccessorAttributeType.SCALAR; inputAccessor.ComponentType = GLTFComponentType.Float; inputAccessor.BufferView = new BufferViewId { Id = 0, Root = _root }; this._root.Accessors.Add(inputAccessor); // Write input. for (var i = 0; i < frameCount; ++i) { //_bufferWriter.Write(Math.Round(Math.Min(animationClip.length * i / (frameCount - 1), animationClip.length), 6)); // TODO _bufferWriter.Write(i / animationClip.frameRate); } var MainTex_STy = new List <float>(); foreach (var curveBind in curveBinds) { // Curve has been parsed. if (ignoreCurves.Contains(curveBind)) { continue; } // No target. var animationTarget = _target.Find(curveBind.path); if (animationTarget == null) { continue; } // Create node. var nodeIndex = _animationTargets.IndexOf(animationTarget); if (nodeIndex < 0) { _animationTargets.Add(animationTarget); nodeIndex = _root.Nodes.Count; _root.Nodes.Add(new Node() { Name = _target == animationTarget ? "__root__" : animationTarget.name, }); if (animationTarget.transform.parent == _target) { _root.Scenes[0].Nodes.Add( new NodeId() { Id = nodeIndex, Root = _root, } ); } } // Output. var outputAccessor = new Accessor(); outputAccessor.Count = frameCount; outputAccessor.ComponentType = GLTFComponentType.Float; outputAccessor.BufferView = inputAccessor.BufferView; outputAccessor.ByteOffset = (int)_bufferWriter.BaseStream.Position; this._root.Accessors.Add(outputAccessor); // var animationSampler = new AnimationSampler() { Input = new AccessorId() { Id = this._root.Accessors.IndexOf(inputAccessor), Root = _root, }, Interpolation = InterpolationType.LINEAR, Output = new AccessorId() { Id = this._root.Accessors.IndexOf(outputAccessor), Root = _root, }, }; glTFAnimation.Samplers.Add(animationSampler); // var animationChannel = new AnimationChannel() { Sampler = new SamplerId() { Id = glTFAnimation.Samplers.IndexOf(animationSampler), Root = _root, }, Target = new AnimationChannelTarget() { Node = new NodeId() { Id = nodeIndex, Root = _root, } } }; glTFAnimation.Channels.Add(animationChannel); if (curveBind.type == typeof(Transform)) { var curveGroup = _getCurveGroup(curveBinds, curveBind); ignoreCurves.AddRange(curveGroup); switch (curveBind.propertyName) { case "m_LocalPosition.x": case "m_LocalPosition.y": case "m_LocalPosition.z": animationChannel.Target.Path = GLTFAnimationChannelPath.translation; outputAccessor.Type = GLTFAccessorAttributeType.VEC3; for (var i = 0; i < frameCount; ++i) { var time = i / animationClip.frameRate; var curveX = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[0]); var curveY = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[1]); var curveZ = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[2]); var value = curveX != null?curveX.Evaluate(time) : animationTarget.transform.localPosition.x; _bufferWriter.Write(value); value = curveY != null?curveY.Evaluate(time) : animationTarget.transform.localPosition.y; _bufferWriter.Write(value); value = curveZ != null?curveZ.Evaluate(time) : animationTarget.transform.localPosition.z; _bufferWriter.Write(value); } break; case "m_LocalRotation.x": case "m_LocalRotation.y": case "m_LocalRotation.z": case "m_LocalRotation.w": animationChannel.Target.Path = GLTFAnimationChannelPath.rotation; outputAccessor.Type = GLTFAccessorAttributeType.VEC4; for (var i = 0; i < frameCount; ++i) { var time = i / animationClip.frameRate; var curveX = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[0]); var curveY = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[1]); var curveZ = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[2]); var curveW = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[3]); var valueX = curveX != null?curveX.Evaluate(time) : animationTarget.transform.localRotation.x; var valueY = curveY != null?curveY.Evaluate(time) : animationTarget.transform.localRotation.y; var valueZ = curveZ != null?curveZ.Evaluate(time) : animationTarget.transform.localRotation.z; var valueW = curveW != null?curveW.Evaluate(time) : animationTarget.transform.localRotation.w; _bufferWriter.Write(valueX); _bufferWriter.Write(valueY); _bufferWriter.Write(valueZ); _bufferWriter.Write(valueW); } break; case "localEulerAnglesRaw.x": case "localEulerAnglesRaw.y": case "localEulerAnglesRaw.z": animationChannel.Target.Path = GLTFAnimationChannelPath.rotation; outputAccessor.Type = GLTFAccessorAttributeType.VEC4; for (var i = 0; i < frameCount; ++i) { var time = i / animationClip.frameRate; var curveX = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[0]); var curveY = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[1]); var curveZ = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[2]); var valueX = curveX != null?curveX.Evaluate(time) : animationTarget.transform.localEulerAngles.x; var valueY = curveY != null?curveY.Evaluate(time) : animationTarget.transform.localEulerAngles.y; var valueZ = curveZ != null?curveZ.Evaluate(time) : animationTarget.transform.localEulerAngles.z; var quaternion = Quaternion.Euler(valueX, valueY, valueZ); _bufferWriter.Write(quaternion.x); _bufferWriter.Write(quaternion.y); _bufferWriter.Write(quaternion.z); _bufferWriter.Write(quaternion.w); } break; case "m_LocalScale.x": case "m_LocalScale.y": case "m_LocalScale.z": animationChannel.Target.Path = GLTFAnimationChannelPath.scale; outputAccessor.Type = GLTFAccessorAttributeType.VEC3; for (var i = 0; i < frameCount; ++i) { var time = i / animationClip.frameRate; var curveX = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[0]); var curveY = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[1]); var curveZ = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveGroup[2]); var value = curveX != null?curveX.Evaluate(time) : animationTarget.transform.localScale.x; _bufferWriter.Write(value); value = curveY != null?curveY.Evaluate(time) : animationTarget.transform.localScale.y; _bufferWriter.Write(value); value = curveZ != null?curveZ.Evaluate(time) : animationTarget.transform.localScale.z; _bufferWriter.Write(value); } break; } } else { animationChannel.Target.Path = GLTFAnimationChannelPath.custom; outputAccessor.Type = GLTFAccessorAttributeType.SCALAR; var type = ""; var property = ""; var uri = ""; var needUpdate = -1; if (curveBind.type == typeof(GameObject)) { type = "paper.GameObject"; switch (curveBind.propertyName) { case "m_IsActive": property = "activeSelf"; animationSampler.Interpolation = InterpolationType.STEP; break; } // for (var i = 0; i < frameCount; ++i) // TODO // { // var time = animationClip.length * i / frameCountSO; // TODO // var curve = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveBind); // var value = curve.Evaluate(time); // _bufferWriter.Write(value); // } } else if (curveBind.type == typeof(UnityEngine.MeshRenderer)) { type = "egret3d.MeshRenderer"; uri = "materials/0/$/_uvTransform"; needUpdate = 1; // animationSampler.Interpolation = InterpolationType.STEP; switch (curveBind.propertyName) { case "material._MainTex_ST.z": property = "0"; break; case "material._MainTex_ST.w": property = "1"; break; case "material._MainTex_ST.x": property = "2"; break; case "material._MainTex_ST.y": property = "3"; break; } } else { Debug.Log("Unknown type and property." + curveBind.type.ToString() + curveBind.propertyName); } // Extensions. animationChannel.Extensions = new Dictionary <string, IExtension>() { { AnimationExtensionFactory.EXTENSION_NAME, new AnimationChannelExtension() { type = type, property = property, uri = uri, needUpdate = needUpdate, } }, }; for (var i = 0; i < frameCount; ++i) { var curve = UnityEditor.AnimationUtility.GetEditorCurve(animationClip, curveBind); if (curve != null) { var value = curve.Evaluate(i / animationClip.frameRate); if (curveBind.propertyName == "material._MainTex_ST.w") { if (i < MainTex_STy.Count) { _bufferWriter.Write(1.0f - value - MainTex_STy[i]); } else { _bufferWriter.Write(value); } } else { _bufferWriter.Write(value); if (curveBind.propertyName == "material._MainTex_ST.y") { MainTex_STy.Add(value); } } } } } } foreach (var evt in animationClip.events) { var glTFFrameEvent = new AnimationFrameEvent(); glTFFrameEvent.name = evt.functionName; glTFFrameEvent.position = evt.time; glTFFrameEvent.intVariable = evt.intParameter; glTFFrameEvent.floatVariable = evt.floatParameter; glTFFrameEvent.stringVariable = evt.stringParameter; ext.events.Add(glTFFrameEvent); } ext.events.Sort(); }
private void ExportAnimation() { var player = this._target.GetComponent <FB.PosePlus.AniPlayer>(); var totalBones = player.bones.Count; var totalFrame = player.totalFrame(); var bufSize = UnityEngine.Quaternion.identity.GetBytes().Length + UnityEngine.Vector3.zero.GetBytes().Length; var binarySingleFrameSize = totalBones * bufSize; var animation = new GLTF.Schema.Animation() { Name = player.name, Extensions = new Dictionary <string, IExtension>() }; var ext = new AnimationExtension(); ext.frameRate = player.clips[0].fps; ext.clips = new List <AnimationClip>(); float position = 0; for (var aniIndex = 0; aniIndex < player.clips.Count; aniIndex++) { var rawClip = player.clips[aniIndex]; var ani = new AnimationClip(); ani.name = rawClip.clipName; ani.position = position; ani.duration = (rawClip.frames.Count - 1) / ext.frameRate; position += rawClip.frames.Count / ext.frameRate; /*for (var evtIndex = 0; evtIndex < rawClip.events.Count; evtIndex++) * { * var rawEvent = rawClip.events[evtIndex]; * var evt = new Schema.FrameEvent(); * evt.name = rawEvent.name; * evt.position = rawEvent.position; * evt.intVariable = rawEvent.intVariable; * evt.floatVariable = rawEvent.floatVariable; * evt.stringVariable = rawEvent.stringVariable; * ani.events.Add(evt); * }*/ ext.clips.Add(ani); } var bufferOffset = this._bufferWriter.BaseStream.Position; WriteBinary(player, totalFrame, binarySingleFrameSize); var bufferLength = this._bufferWriter.BaseStream.Position; var accessor = new Accessor(); accessor.Count = (int)(bufferLength / 4); accessor.Type = GLTFAccessorAttributeType.SCALAR; accessor.ComponentType = GLTFComponentType.Float; accessor.BufferView = ExportBufferView((int)0, (int)bufferLength); this._root.Accessors.Add(accessor); animation.Extensions.Add(AnimationExtensionFactory.EXTENSION_NAME, ext); this._root.Animations.Add(animation); }