private BabylonAnimation GetAnimationsFrameByFrameMatrix(MFnTransform mFnTransform) { int start = GetMinTime()[0]; int end = GetMaxTime()[0]; BabylonAnimation animation = null; // get keys List <BabylonAnimationKey> keys = new List <BabylonAnimationKey>(); for (int currentFrame = start; currentFrame <= end; currentFrame++) { // Set the animation key BabylonAnimationKey key = new BabylonAnimationKey() { frame = currentFrame, values = GetBabylonMatrix(mFnTransform, currentFrame).m.ToArray() }; keys.Add(key); } // Optimization OptimizeAnimations(keys, false); // Do not remove linear animation keys for bones // Ensure animation has at least 2 frames if (keys.Count > 1) { var animationPresent = true; // Ensure animation has at least 2 non equal frames if (keys.Count == 2) { if (keys[0].values.IsEqualTo(keys[1].values)) { animationPresent = false; } } if (animationPresent) { // Create BabylonAnimation // Animations animation = new BabylonAnimation() { name = mFnTransform.name + "Animation", // override default animation name dataType = (int)BabylonAnimation.DataType.Matrix, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, framePerSecond = GetFPS(), keys = keys.ToArray(), property = "_matrix" }; } } return(animation); }
private static void ExportSkeletonAnimationClipData(Animator animator, bool autoPlay, BabylonSkeleton skeleton, Transform[] bones, BabylonMesh babylonMesh, AnimationClip clip) { var frameTime = 1.0f / clip.frameRate; int animationFrameCount = (int)(clip.length * clip.frameRate); if (autoPlay) { babylonMesh.autoAnimate = true; babylonMesh.autoAnimateFrom = 0; babylonMesh.autoAnimateTo = animationFrameCount; babylonMesh.autoAnimateLoop = true; } foreach (var bone in skeleton.bones) { var keys = new List <BabylonAnimationKey>(); var transform = bones.Single(b => b.name == bone.name); AnimationMode.BeginSampling(); for (var i = 0; i < animationFrameCount; i++) { clip.SampleAnimation(animator.gameObject, i * frameTime); var local = (transform.parent.localToWorldMatrix.inverse * transform.localToWorldMatrix); float[] matrix = new[] { local[0, 0], local[1, 0], local[2, 0], local[3, 0], local[0, 1], local[1, 1], local[2, 1], local[3, 1], local[0, 2], local[1, 2], local[2, 2], local[3, 2], local[0, 3], local[1, 3], local[2, 3], local[3, 3] }; var key = new BabylonAnimationKey { frame = i, values = matrix, }; keys.Add(key); } AnimationMode.EndSampling(); var babylonAnimation = new BabylonAnimation { name = bone.name + "Animation", property = "_matrix", dataType = (int)BabylonAnimation.DataType.Matrix, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, framePerSecond = (int)clip.frameRate, keys = keys.ToArray() }; bone.animation = babylonAnimation; } }
/// <summary> /// Export the morph target influence animation. /// </summary> /// <param name="blendShapeDeformerName"></param> /// <param name="weightIndex"></param> /// <returns>A list containing all animations</returns> private IList <BabylonAnimation> GetAnimationsInfluence(string blendShapeDeformerName, int weightIndex) { IList <BabylonAnimation> animations = new List <BabylonAnimation>(); BabylonAnimation animation = null; IDictionary <double, IList <double> > morphWeights = GetMorphWeightsByFrame(blendShapeDeformerName); // get keys List <BabylonAnimationKey> keys = new List <BabylonAnimationKey>(); for (int index = 0; index < morphWeights.Count; index++) { KeyValuePair <double, IList <double> > keyValue = morphWeights.ElementAt(index); // Set the animation key BabylonAnimationKey key = new BabylonAnimationKey() { frame = (int)keyValue.Key, values = new float[] { (float)keyValue.Value[weightIndex] } }; keys.Add(key); } List <BabylonAnimationKey> keysFull = new List <BabylonAnimationKey>(keys); // Optimization OptimizeAnimations(keys, false); // Ensure animation has at least 2 frames if (IsAnimationKeysRelevant(keys)) { // Animations animation = new BabylonAnimation() { name = "influence animation", // override default animation name dataType = (int)BabylonAnimation.DataType.Float, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, framePerSecond = Loader.GetFPS(), keys = keys.ToArray(), keysFull = keysFull, property = "influence" }; animations.Add(animation); } return(animations); }
/// <summary> /// Using MEL commands, it return the babylon animation /// </summary> /// <param name="objectName">The name of the Maya object</param> /// <param name="mayaProperty">The attribut in Maya</param> /// <param name="babylonProperty">The attribut in Babylon</param> /// <returns>A Babylon animation that represents the Maya animation</returns> public BabylonAnimation GetAnimationFloat(string objectName, string mayaProperty, string babylonProperty) { // Get keyframes IList <double> keyframes = GetKeyframes(objectName); BabylonAnimation animation = null; // set the key for each keyframe List <BabylonAnimationKey> keys = new List <BabylonAnimationKey>(); for (int index = 0; index < keyframes.Count; index++) { double keyframe = keyframes[index]; MGlobal.executeCommand($"getAttr -t {keyframe} {objectName}.{mayaProperty}", out double value); // Set the animation key BabylonAnimationKey key = new BabylonAnimationKey() { frame = (int)keyframe, values = new float[] { (float)value } }; keys.Add(key); } List <BabylonAnimationKey> keysFull = new List <BabylonAnimationKey>(keys); // Optimization OptimizeAnimations(keys, false); // Ensure animation has at least 2 frames if (IsAnimationKeysRelevant(keys)) { // Animations animation = new BabylonAnimation() { name = $"{babylonProperty} animation", // override default animation name dataType = (int)BabylonAnimation.DataType.Float, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, framePerSecond = Loader.GetFPS(), keys = keys.ToArray(), keysFull = keysFull, property = babylonProperty }; } return(animation); }
private BabylonAnimation GetAnimationsFrameByFrameMatrix(MFnTransform mFnTransform) { int start = Loader.GetMinTime(); int end = Loader.GetMaxTime(); BabylonAnimation animation = null; // get keys List <BabylonAnimationKey> keys = new List <BabylonAnimationKey>(); for (int currentFrame = start; currentFrame <= end; currentFrame++) { // Set the animation key BabylonAnimationKey key = new BabylonAnimationKey() { frame = currentFrame, values = GetBabylonMatrix(mFnTransform, currentFrame).m.ToArray() }; keys.Add(key); } var keysFull = new List <BabylonAnimationKey>(keys); // Optimization OptimizeAnimations(keys, false); // Do not remove linear animation keys for bones // Ensure animation has at least 2 frames if (IsAnimationKeysRelevant(keys)) { // Animations animation = new BabylonAnimation() { name = mFnTransform.name + "Animation", // override default animation name dataType = (int)BabylonAnimation.DataType.Matrix, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, framePerSecond = Loader.GetFPS(), keys = keys.ToArray(), keysFull = keysFull, property = "_matrix" }; } return(animation); }
private List <BabylonAnimationKey> ExportBabylonKeysFromGameController(IIGameControl control, IGameControlType type, Func <IIGameKey, float[]> extractValueFunc) { if (control == null) { return(null); } ITab <IIGameKey> gameKeyTab = GlobalInterface.Instance.Tab.Create <IIGameKey>(); control.GetQuickSampledKeys(gameKeyTab, type); if (gameKeyTab == null) { return(null); } var keys = new List <BabylonAnimationKey>(); for (int indexKey = 0; indexKey < gameKeyTab.Count; indexKey++) { #if MAX2017 || MAX2018 var gameKey = gameKeyTab[indexKey]; #else var indexer = Marshal.AllocHGlobal(sizeof(int)); Marshal.WriteInt32(indexer, indexKey); var gameKey = gameKeyTab[indexer]; Marshal.FreeHGlobal(indexer); #endif var key = new BabylonAnimationKey() { frame = gameKey.T / Ticks, values = extractValueFunc(gameKey) }; keys.Add(key); } return(keys); }
private BabylonAnimation GetDummyAnimation(GLTFNode gltfNode, int startFrame, int endFrame) { BabylonAnimation dummyAnimation = new BabylonAnimation(); dummyAnimation.name = "Dummy"; dummyAnimation.property = "scaling"; dummyAnimation.framePerSecond = Loader.Global.FrameRate; dummyAnimation.dataType = (int)BabylonAnimation.DataType.Vector3; BabylonAnimationKey startKey = new BabylonAnimationKey(); startKey.frame = startFrame; startKey.values = gltfNode.scale; BabylonAnimationKey endKey = new BabylonAnimationKey(); endKey.frame = endFrame; endKey.values = gltfNode.scale; dummyAnimation.keys = new BabylonAnimationKey[] { startKey, endKey }; return(dummyAnimation); }
public BabylonAnimation GetDummyAnimation(GLTFNode gltfNode, int startFrame, int endFrame, BabylonScene babylonScene) { BabylonAnimation dummyAnimation = new BabylonAnimation(); dummyAnimation.name = "Dummy"; dummyAnimation.property = "scaling"; dummyAnimation.framePerSecond = babylonScene.TimelineFramesPerSecond; dummyAnimation.dataType = (int)BabylonAnimation.DataType.Vector3; BabylonAnimationKey startKey = new BabylonAnimationKey(); startKey.frame = startFrame; startKey.values = gltfNode.scale; BabylonAnimationKey endKey = new BabylonAnimationKey(); endKey.frame = endFrame; endKey.values = gltfNode.scale; dummyAnimation.keys = new BabylonAnimationKey[] { startKey, endKey }; return(dummyAnimation); }
private List <BabylonAnimationKey> ExportBabylonKeysFromGameController(IIGameControl control, IGameControlType type, Func <IIGameKey, float[]> extractValueFunc) { if (control == null) { return(null); } ITab <IIGameKey> gameKeyTab = GlobalInterface.Instance.Tab.Create <IIGameKey>(); control.GetQuickSampledKeys(gameKeyTab, type); if (gameKeyTab == null) { return(null); } var keys = new List <BabylonAnimationKey>(); for (int indexKey = 0; indexKey < gameKeyTab.Count; indexKey++) { #if MAX2017 || MAX2018 || MAX2019 var gameKey = gameKeyTab[indexKey]; #else var gameKey = gameKeyTab[new IntPtr(indexKey)]; #endif var key = new BabylonAnimationKey() { frame = gameKey.T / Loader.Global.TicksPerFrame, values = extractValueFunc(gameKey) }; keys.Add(key); } return(keys); }
/// <summary> /// The keys of each BabylonMorphTarget animation ARE NOT assumed to be identical. /// This function merges together all keys and binds to each an influence value for all targets. /// A target influence value is automatically computed when necessary. /// Computation rules are: /// - linear interpolation between target key range /// - constant value outside target key range /// </summary> /// <example> /// When: /// animation1.keys = {0, 25, 50, 100} /// animation2.keys = {50, 75, 100} /// /// Gives: /// mergedKeys = {0, 25, 50, 100, 75} /// range1=[0, 100] /// range2=[50, 100] /// for animation1, the value associated to key=75 is the interpolation of its values between 50 and 100 /// for animation2, the value associated to key=0 is equal to the one at key=50 since 0 is out of range [50, 100] (same for key=25)</example> /// <param name="babylonMorphTargetManager"></param> /// <returns>A map which for each frame, gives the influence value of all targets</returns> private Dictionary <int, List <float> > _getTargetManagerAnimationsData(BabylonMorphTargetManager babylonMorphTargetManager) { // Merge all keys into a single set (no duplicated frame) var mergedFrames = new HashSet <int>(); foreach (var babylonMorphTarget in babylonMorphTargetManager.targets) { if (babylonMorphTarget.animations != null) { var animation = babylonMorphTarget.animations[0]; foreach (BabylonAnimationKey animationKey in animation.keys) { mergedFrames.Add(animationKey.frame); } } } // For each frame, gives the influence value of all targets (gltf structure) var influencesPerFrame = new Dictionary <int, List <float> >(); foreach (var frame in mergedFrames) { influencesPerFrame.Add(frame, new List <float>()); } foreach (var babylonMorphTarget in babylonMorphTargetManager.targets) { // For a given target, for each frame, gives the influence value of the target (babylon structure) var influencePerFrameForTarget = new Dictionary <int, float>(); if (babylonMorphTarget.animations != null && babylonMorphTarget.animations.Length > 0) { var animation = babylonMorphTarget.animations[0]; if (animation.keys.Length == 1) { // Same influence for all frames var influence = animation.keys[0].values[0]; foreach (var frame in mergedFrames) { influencePerFrameForTarget.Add(frame, influence); } } else { // Retreive target animation key range [min, max] var babylonAnimationKeys = new List <BabylonAnimationKey>(animation.keys); babylonAnimationKeys.Sort(); var minAnimationKey = babylonAnimationKeys[0]; var maxAnimationKey = babylonAnimationKeys[babylonAnimationKeys.Count - 1]; foreach (var frame in mergedFrames) { // Surround the current frame with closest keys available for the target BabylonAnimationKey lowerAnimationKey = minAnimationKey; BabylonAnimationKey upperAnimationKey = maxAnimationKey; foreach (BabylonAnimationKey animationKey in animation.keys) { if (lowerAnimationKey.frame < animationKey.frame && animationKey.frame <= frame) { lowerAnimationKey = animationKey; } if (frame <= animationKey.frame && animationKey.frame < upperAnimationKey.frame) { upperAnimationKey = animationKey; } } // In case the target has a key for this frame // or the current frame is out of target animation key range if (lowerAnimationKey.frame == upperAnimationKey.frame) { influencePerFrameForTarget.Add(frame, lowerAnimationKey.values[0]); } else { // Interpolate influence values var t = 1.0f * (frame - lowerAnimationKey.frame) / (upperAnimationKey.frame - lowerAnimationKey.frame); var influence = Tools.Lerp(lowerAnimationKey.values[0], upperAnimationKey.values[0], t); influencePerFrameForTarget.Add(frame, influence); } } } } else { // Target is not animated // Fill all frames with 0 foreach (var frame in mergedFrames) { influencePerFrameForTarget.Add(frame, 0); } } // Switch from babylon to gltf storage representation foreach (var frame in mergedFrames) { List <float> influences = influencesPerFrame[frame]; influences.Add(influencePerFrameForTarget[frame]); } } return(influencesPerFrame); }
private static void ExportSkeletonAnimationClipData(GameObject source, BabylonSkeleton skeleton, SkinnedMeshRenderer skinnedMesh, BabylonMesh babylonMesh, UnityEditor.AnimationState animationState, ref List <AnimationClip> states, ref UnityMetaData metaData, Animator animator) { ExporterWindow.ReportProgress(1, "Exporting skeleton clips: " + skinnedMesh.name); //string sourceId = GetID(source); int frameRate = 0; int firstClipEnd = 0; int totalFrameCount = 0; Transform[] bones = skinnedMesh.bones; List <string> stateNameCache = new List <string>(); if (!AnimationMode.InAnimationMode()) { AnimationMode.StartAnimationMode(); } //var anims = new List<BabylonAnimation>(); //var pxkeys = new List<BabylonAnimationKey>(); float playbackSpeed = (animationState != null) ? animationState.playbackSpeed : 1.0f; float clampFeetPositions = (animationState != null) ? animationState.clampFeetPositions : 0.0f; BabylonAnimationBaking bakeRootTransforms = (animationState != null) ? animationState.bakeRootTransforms : BabylonAnimationBaking.GameBlend; foreach (var bone in skeleton.bones) { int frameOffest = 0; var keys = new List <BabylonAnimationKey>(); Transform transform = bones.Single(b => b.name == bone.name); foreach (var state in states) { if (state == null) { continue; } AnimationClip clip = state as AnimationClip; if (frameRate <= 0) { frameRate = (int)clip.frameRate; } var frameTime = 1.0f / frameRate; int clipFrameCount = (int)(clip.length * frameRate); if (firstClipEnd <= 0) { firstClipEnd = (clipFrameCount - 1); } var settings = AnimationUtility.GetAnimationClipSettings(clip); BabylonLoopBehavior behavior = (settings.loopTime) ? BabylonLoopBehavior.Cycle : BabylonLoopBehavior.Constant; if (settings.loopTime && settings.loopBlend) { behavior = BabylonLoopBehavior.Relative; } ExporterWindow.ReportProgress(1, "Sampling: " + babylonMesh.name + " - " + bone.name + " - " + clip.name); // Set Animation State Meta Data if (!stateNameCache.Contains(clip.name)) { stateNameCache.Add(clip.name); // Animation Clip Information Dictionary <string, object> animStateInfo = new Dictionary <string, object>(); animStateInfo.Add("type", "skeleton"); animStateInfo.Add("name", clip.name); animStateInfo.Add("start", frameOffest); animStateInfo.Add("stop", (frameOffest + clipFrameCount - 1)); animStateInfo.Add("rate", frameRate); animStateInfo.Add("behavior", (int)behavior); animStateInfo.Add("playback", playbackSpeed); metaData.animationClips.Add(animStateInfo); } AnimationMode.BeginSampling(); for (var i = 0; i < clipFrameCount; i++) { Matrix4x4 local; int frameIndex = (i + frameOffest); float originalPX = transform.localPosition.x; float originalPY = transform.localPosition.y; float originalPZ = transform.localPosition.z; float originalRY = transform.localRotation.eulerAngles.y; clip.SampleAnimation(source, i * frameTime); if (transform == skinnedMesh.rootBone) { float positionX = transform.localPosition.x; float positionY = transform.localPosition.y; float positionZ = transform.localPosition.z; Quaternion rotationQT = transform.localRotation; if (settings.loopBlendOrientation || settings.keepOriginalOrientation) { if (settings.keepOriginalOrientation) { // Original Rotation - ??? rotationQT = Quaternion.Euler(rotationQT.eulerAngles.x, originalRY, rotationQT.eulerAngles.z); } else { // Body Orientation - ??? rotationQT = Quaternion.Euler(rotationQT.eulerAngles.x, settings.orientationOffsetY, rotationQT.eulerAngles.z); } } if (settings.loopBlendPositionY || settings.keepOriginalPositionY) { if (settings.keepOriginalPositionY) { // Original Position Y positionY = originalPY; } else if (settings.heightFromFeet) { // Feet Position Y positionY = (settings.level + clampFeetPositions); } else { // Center Of Mass positionY = 0.0f; } } if (settings.loopBlendPositionXZ || settings.keepOriginalPositionXZ) { if (settings.keepOriginalPositionXZ) { // Original Position XZ positionX = originalPX; positionZ = originalPZ; } else { // Center Of Mass positionX = 0.0f; positionZ = 0.0f; } } if (bakeRootTransforms == BabylonAnimationBaking.GameBlend) { positionX = 0.0f; positionZ = 0.0f; } local = Matrix4x4.TRS(new Vector3(positionX, positionY, positionZ), rotationQT, transform.localScale); } else { // DEPRECIATED: local = (transform.parent.localToWorldMatrix.inverse * transform.localToWorldMatrix); local = Matrix4x4.TRS(transform.localPosition, transform.localRotation, transform.localScale); } float[] matrix = new[] { local[0, 0], local[1, 0], local[2, 0], local[3, 0], local[0, 1], local[1, 1], local[2, 1], local[3, 1], local[0, 2], local[1, 2], local[2, 2], local[3, 2], local[0, 3], local[1, 3], local[2, 3], local[3, 3] }; var key = new BabylonAnimationKey { frame = frameIndex, values = matrix }; keys.Add(key); } AnimationMode.EndSampling(); frameOffest += clipFrameCount; totalFrameCount += clipFrameCount; } var babylonAnimation = new BabylonAnimation { name = bone.name + "Animation", property = "_matrix", dataType = (int)BabylonAnimation.DataType.Matrix, loopBehavior = (int)BabylonAnimation.LoopBehavior.Relative, enableBlending = false, blendingSpeed = 0.0f, framePerSecond = frameRate, keys = keys.ToArray() }; bone.animation = babylonAnimation; } if (AnimationMode.InAnimationMode()) { AnimationMode.StopAnimationMode(); } /* * // * // TODO: Format Custom Curve Keys * // * string property = "none"; * if (pxkeys.Count > 0) * { * property = "metadata.state.animPosition.x"; * anims.Add(new BabylonAnimation * { * dataType = (int)BabylonAnimation.DataType.Float, * name = property + " animation", * keys = pxkeys.ToArray(), * framePerSecond = frameRate, * enableBlending = false, * blendingSpeed = 0.0f, * loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, * property = property * }); * } * // * // Cache Babylon Animation Keys * // * if (anims.Count > 0) * { * List<BabylonAnimation> sourceAnimiamtions = null; * if (SceneBuilder.AnimationCurveKeys.ContainsKey(sourceId)) { * sourceAnimiamtions = SceneBuilder.AnimationCurveKeys[sourceId]; * } else { * sourceAnimiamtions = new List<BabylonAnimation>(); * SceneBuilder.AnimationCurveKeys.Add(sourceId, sourceAnimiamtions); * } * foreach (var anim in anims) { * sourceAnimiamtions.Add(anim); * } * } */ }
private List <BabylonAnimation> GetAnimationsFrameByFrame(MFnTransform mFnTransform) { int start = Loader.GetMinTime(); int end = Loader.GetMaxTime(); // Animations List <BabylonAnimation> animations = new List <BabylonAnimation>(); string[] babylonAnimationProperties = new string[] { "scaling", "rotationQuaternion", "position", "visibility" }; Dictionary <string, List <BabylonAnimationKey> > keysPerProperty = new Dictionary <string, List <BabylonAnimationKey> >(); keysPerProperty.Add("scaling", new List <BabylonAnimationKey>()); keysPerProperty.Add("rotationQuaternion", new List <BabylonAnimationKey>()); keysPerProperty.Add("position", new List <BabylonAnimationKey>()); keysPerProperty.Add("visibility", new List <BabylonAnimationKey>()); // get keys for (int currentFrame = start; currentFrame <= end; currentFrame++) { // get transformation matrix at this frame MDoubleArray mDoubleMatrix = new MDoubleArray(); MGlobal.executeCommand($"getAttr -t {currentFrame} {mFnTransform.fullPathName}.matrix", mDoubleMatrix); mDoubleMatrix.get(out float[] localMatrix); MMatrix matrix = new MMatrix(localMatrix); var transformationMatrix = new MTransformationMatrix(matrix); // Retreive TRS vectors from matrix var position = transformationMatrix.getTranslation(); var rotationQuaternion = transformationMatrix.getRotationQuaternion(); var scaling = transformationMatrix.getScale(); // Switch coordinate system at object level position[2] *= -1; rotationQuaternion[0] *= -1; rotationQuaternion[1] *= -1; // create animation key for each property for (int indexAnimation = 0; indexAnimation < babylonAnimationProperties.Length; indexAnimation++) { string babylonAnimationProperty = babylonAnimationProperties[indexAnimation]; BabylonAnimationKey key = new BabylonAnimationKey(); key.frame = currentFrame; switch (indexAnimation) { case 0: // scaling key.values = scaling.ToArray(); break; case 1: // rotationQuaternion key.values = rotationQuaternion.ToArray(); break; case 2: // position key.values = position.ToArray(); break; case 3: // visibility key.values = new float[] { Loader.GetVisibility(mFnTransform.fullPathName, currentFrame) }; break; } keysPerProperty[babylonAnimationProperty].Add(key); } } // create animation for each property for (int indexAnimation = 0; indexAnimation < babylonAnimationProperties.Length; indexAnimation++) { string babylonAnimationProperty = babylonAnimationProperties[indexAnimation]; List <BabylonAnimationKey> keys = keysPerProperty[babylonAnimationProperty]; var keysFull = new List <BabylonAnimationKey>(keys); // Optimization OptimizeAnimations(keys, true); // Ensure animation has at least 2 frames if (IsAnimationKeysRelevant(keys)) { int dataType = 0; // "scaling", "rotationQuaternion", "position", "visibility" if (indexAnimation == 0 || indexAnimation == 2) // scaling and position { dataType = (int)BabylonAnimation.DataType.Vector3; } else if (indexAnimation == 1) // rotationQuaternion { dataType = (int)BabylonAnimation.DataType.Quaternion; } else // visibility { dataType = (int)BabylonAnimation.DataType.Float; } // Create BabylonAnimation animations.Add(new BabylonAnimation() { dataType = dataType, name = babylonAnimationProperty + " animation", framePerSecond = Loader.GetFPS(), loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, property = babylonAnimationProperty, keys = keys.ToArray(), keysFull = keysFull }); } } return(animations); }
/// <summary> /// Get TRS and visiblity animations of the transform /// </summary> /// <param name="transform">Transform above mesh/camera/light</param> /// <returns></returns> private List <BabylonAnimation> GetAnimation(MFnTransform transform) { // Animations MPlugArray connections = new MPlugArray(); MStringArray animCurvList = new MStringArray(); MIntArray keysTime = new MIntArray(); MDoubleArray keysValue = new MDoubleArray(); MFloatArray translateValues = new MFloatArray(); MFloatArray rotateValues = new MFloatArray(); MFloatArray scaleValues = new MFloatArray(); MFloatArray visibilityValues = new MFloatArray(); MFloatArray keyTimes = new MFloatArray(); List <BabylonAnimationKey> keys = new List <BabylonAnimationKey>(); List <BabylonAnimation> animationsObject = new List <BabylonAnimation>(); //Get the animCurve MGlobal.executeCommand("listConnections -type \"animCurve\" " + transform.fullPathName + ";", animCurvList); List <AnimCurvData> animCurvesData = new List <AnimCurvData>(); foreach (String animCurv in animCurvList) { AnimCurvData animCurvData = new AnimCurvData(); animCurvesData.Add(animCurvData); animCurvData.animCurv = animCurv; //Get the key time for each curves MGlobal.executeCommand("keyframe -q " + animCurv + ";", keysTime); //Get the value for each curves MGlobal.executeCommand("keyframe - q -vc -absolute " + animCurv + ";", keysValue); if (animCurv.EndsWith("translateZ") || animCurv.EndsWith("rotateX") || animCurv.EndsWith("rotateY")) { for (int index = 0; index < keysTime.Count; index++) { // Switch coordinate system at object level animCurvData.valuePerFrame.Add(keysTime[index], (float)keysValue[index] * -1.0f); } } else { for (int index = 0; index < keysTime.Count; index++) { animCurvData.valuePerFrame.Add(keysTime[index], (float)keysValue[index]); } } } string[] mayaAnimationProperties = new string[] { "translate", "rotate", "scale" }; string[] babylonAnimationProperties = new string[] { "position", "rotationQuaternion", "scaling" }; string[] axis = new string[] { "X", "Y", "Z" }; // Init TRS default values Dictionary <string, float> defaultValues = new Dictionary <string, float>(); float[] position = null; float[] rotationQuaternion = null; float[] rotation = null; float[] scaling = null; GetTransform(transform, ref position, ref rotationQuaternion, ref rotation, ref scaling); // coordinate system already switched defaultValues.Add("translateX", position[0]); defaultValues.Add("translateY", position[1]); defaultValues.Add("translateZ", position[2]); defaultValues.Add("rotateX", rotation[0]); defaultValues.Add("rotateY", rotation[1]); defaultValues.Add("rotateZ", rotation[2]); defaultValues.Add("scaleX", scaling[0]); defaultValues.Add("scaleY", scaling[1]); defaultValues.Add("scaleZ", scaling[2]); for (int indexAnimationProperty = 0; indexAnimationProperty < mayaAnimationProperties.Length; indexAnimationProperty++) { string mayaAnimationProperty = mayaAnimationProperties[indexAnimationProperty]; // Retreive animation curves data for current animation property // Ex: all "translate" data are "translateX", "translateY", "translateZ" List <AnimCurvData> animDataProperty = animCurvesData.Where(data => data.animCurv.Contains(mayaAnimationProperty)).ToList(); if (animDataProperty.Count == 0) { // Property is not animated continue; } // Get all frames for this property List <int> framesProperty = new List <int>(); foreach (var animData in animDataProperty) { framesProperty.AddRange(animData.valuePerFrame.Keys); } framesProperty = framesProperty.Distinct().ToList(); framesProperty.Sort(); // Get default values for this property BabylonAnimationKey lastBabylonAnimationKey = new BabylonAnimationKey(); lastBabylonAnimationKey.frame = 0; lastBabylonAnimationKey.values = new float[] { defaultValues[mayaAnimationProperty + "X"], defaultValues[mayaAnimationProperty + "Y"], defaultValues[mayaAnimationProperty + "Z"] }; // Compute all values for this property List <BabylonAnimationKey> babylonAnimationKeys = new List <BabylonAnimationKey>(); foreach (var frameProperty in framesProperty) { BabylonAnimationKey babylonAnimationKey = new BabylonAnimationKey(); babylonAnimationKeys.Add(babylonAnimationKey); // Frame babylonAnimationKey.frame = frameProperty; // Values float[] valuesProperty = new float[3]; for (int indexAxis = 0; indexAxis < axis.Length; indexAxis++) { AnimCurvData animCurvDataAxis = animDataProperty.Find(data => data.animCurv.EndsWith(axis[indexAxis])); float value; if (animCurvDataAxis != null && animCurvDataAxis.valuePerFrame.ContainsKey(frameProperty)) { value = animCurvDataAxis.valuePerFrame[frameProperty]; } else { value = lastBabylonAnimationKey.values[indexAxis]; } valuesProperty[indexAxis] = value; } babylonAnimationKey.values = valuesProperty.ToArray(); // Update last known values lastBabylonAnimationKey = babylonAnimationKey; } // Convert euler to quaternion angles if (indexAnimationProperty == 1) // Rotation { foreach (var babylonAnimationKey in babylonAnimationKeys) { BabylonVector3 eulerAngles = BabylonVector3.FromArray(babylonAnimationKey.values); BabylonQuaternion quaternionAngles = eulerAngles.toQuaternion(); babylonAnimationKey.values = quaternionAngles.ToArray(); } } var keysFull = new List <BabylonAnimationKey>(babylonAnimationKeys); // Optimization OptimizeAnimations(babylonAnimationKeys, true); // Ensure animation has at least 2 frames if (IsAnimationKeysRelevant(keys)) { // Create BabylonAnimation string babylonAnimationProperty = babylonAnimationProperties[indexAnimationProperty]; animationsObject.Add(new BabylonAnimation() { dataType = indexAnimationProperty == 1 ? (int)BabylonAnimation.DataType.Quaternion : (int)BabylonAnimation.DataType.Vector3, name = babylonAnimationProperty + " animation", framePerSecond = 30, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, property = babylonAnimationProperty, keys = babylonAnimationKeys.ToArray(), keysFull = keysFull }); } } return(animationsObject); }
private List <BabylonAnimation> GetAnimationsFrameByFrame(MFnTransform mFnTransform) { int start = GetMinTime()[0]; int end = GetMaxTime()[0]; // Animations List <BabylonAnimation> animations = new List <BabylonAnimation>(); string[] babylonAnimationProperties = new string[] { "scaling", "rotationQuaternion", "position" }; Dictionary <string, List <BabylonAnimationKey> > keysPerProperty = new Dictionary <string, List <BabylonAnimationKey> >(); keysPerProperty.Add("scaling", new List <BabylonAnimationKey>()); keysPerProperty.Add("rotationQuaternion", new List <BabylonAnimationKey>()); keysPerProperty.Add("position", new List <BabylonAnimationKey>()); // get keys for (int currentFrame = start; currentFrame <= end; currentFrame++) { // get transformation matrix at this frame MDoubleArray mDoubleMatrix = new MDoubleArray(); MGlobal.executeCommand($"getAttr -t {currentFrame} {mFnTransform.fullPathName}.matrix", mDoubleMatrix); mDoubleMatrix.get(out float[] localMatrix); MMatrix matrix = new MMatrix(localMatrix); var transformationMatrix = new MTransformationMatrix(matrix); // Retreive TRS vectors from matrix var position = transformationMatrix.getTranslation(); var rotationQuaternion = transformationMatrix.getRotationQuaternion(); var scaling = transformationMatrix.getScale(); // Switch coordinate system at object level position[2] *= -1; rotationQuaternion[0] *= -1; rotationQuaternion[1] *= -1; // create animation key for each property for (int indexAnimation = 0; indexAnimation < babylonAnimationProperties.Length; indexAnimation++) { string babylonAnimationProperty = babylonAnimationProperties[indexAnimation]; BabylonAnimationKey key = new BabylonAnimationKey(); key.frame = currentFrame; switch (indexAnimation) { case 0: // scaling key.values = scaling.ToArray(); break; case 1: // rotationQuaternion key.values = rotationQuaternion.ToArray(); break; case 2: // position key.values = position.ToArray(); break; } keysPerProperty[babylonAnimationProperty].Add(key); } } // create animation for each property for (int indexAnimation = 0; indexAnimation < babylonAnimationProperties.Length; indexAnimation++) { string babylonAnimationProperty = babylonAnimationProperties[indexAnimation]; List <BabylonAnimationKey> keys = keysPerProperty[babylonAnimationProperty]; // Optimization OptimizeAnimations(keys, true); // Ensure animation has at least 2 frames if (keys.Count > 1) { var animationPresent = true; // Ensure animation has at least 2 non equal frames if (keys.Count == 2) { if (keys[0].values.IsEqualTo(keys[1].values)) { animationPresent = false; } } if (animationPresent) { // Create BabylonAnimation animations.Add(new BabylonAnimation() { dataType = indexAnimation == 1 ? (int)BabylonAnimation.DataType.Quaternion : (int)BabylonAnimation.DataType.Vector3, name = babylonAnimationProperty + " animation", framePerSecond = GetFPS(), loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, property = babylonAnimationProperty, keys = keys.ToArray() }); } } } return(animations); }
private static void ExportSkeletonAnimationClipData(GameObject source, BabylonSkeleton skeleton, SkinnedMeshRenderer skinnedMesh, BabylonMesh babylonMesh, ref List <AnimationClip> clips, ref UnityMetaData metaData, List <UnityEditor.AnimationParameters> aparams) { ExporterWindow.ReportProgress(1, "Baking " + skinnedMesh.name.ToLower() + " skeleton... This may take a while."); string sourceId = GetID(source); int frameRate = 0; Transform[] bones = skinnedMesh.bones; var anims = new List <BabylonAnimation>(); List <BabylonRange> ranges = new List <BabylonRange>(); List <string> stateNameCache = new List <string>(); if (!AnimationMode.InAnimationMode()) { AnimationMode.StartAnimationMode(); } foreach (var bone in skeleton.bones) { int frameOffest = 0; var keys = new List <BabylonAnimationKey>(); Transform transform = bones.Single(b => b.name == bone.name); foreach (var clip in clips) { if (clip == null) { continue; } string clipName = FormatSafeClipName(clip.name); var settings = AnimationUtility.GetAnimationClipSettings(clip); BabylonLoopBehavior behavior = (settings.loopTime) ? BabylonLoopBehavior.Relative : BabylonLoopBehavior.Constant; if (settings.loopTime && settings.loopBlend) { behavior = BabylonLoopBehavior.Cycle; } // .. int framePadding = 1; float deltaTime = 1.0f / clip.frameRate; if (frameRate <= 0) { frameRate = (int)clip.frameRate; } float clipFrameTotal = clip.length * clip.frameRate; int clipFrameCount = (int)clipFrameTotal + framePadding; int lastFrameCount = clipFrameCount - 1; // .. AnimationMode.BeginSampling(); for (int i = 0; i < clipFrameCount; i++) { Matrix4x4 local; int frameIndex = (int)(i + frameOffest); float sampleTime = (i < lastFrameCount) ? (i * deltaTime) : clip.length; clip.SampleAnimation(source, sampleTime); if (transform == skinnedMesh.rootBone) { float positionX = transform.localPosition.x; float positionY = transform.localPosition.y; float positionZ = transform.localPosition.z; Quaternion rotationQT = transform.localRotation; if (settings.loopBlendOrientation) { if (settings.keepOriginalOrientation) { rotationQT = Quaternion.Euler(rotationQT.eulerAngles.x, (rotationQT.eulerAngles.y + settings.orientationOffsetY), rotationQT.eulerAngles.z); } else { rotationQT = Quaternion.Euler(rotationQT.eulerAngles.x, settings.orientationOffsetY, rotationQT.eulerAngles.z); } } if (settings.loopBlendPositionY) { if (settings.keepOriginalPositionY || settings.heightFromFeet) { positionY += settings.level; } else { positionY = settings.level; } } if (settings.loopBlendPositionXZ) { positionX = 0.0f; positionZ = 0.0f; } local = Matrix4x4.TRS(new Vector3(positionX, positionY, positionZ), rotationQT, transform.localScale); } else { local = Matrix4x4.TRS(transform.localPosition, transform.localRotation, transform.localScale); } float[] matrix = new[] { local[0, 0], local[1, 0], local[2, 0], local[3, 0], local[0, 1], local[1, 1], local[2, 1], local[3, 1], local[0, 2], local[1, 2], local[2, 2], local[3, 2], local[0, 3], local[1, 3], local[2, 3], local[3, 3] }; var key = new BabylonAnimationKey { frame = frameIndex, values = matrix }; keys.Add(key); } AnimationMode.EndSampling(); // .. // Set Animation State Meta Data // .. if (!stateNameCache.Contains(clipName)) { stateNameCache.Add(clipName); // Animation Clip Information int fromFrame = frameOffest, toFrame = frameOffest + lastFrameCount; Dictionary <string, object> animStateInfo = new Dictionary <string, object>(); animStateInfo.Add("type", "skeleton"); animStateInfo.Add("wrap", clip.wrapMode); animStateInfo.Add("name", clipName); animStateInfo.Add("start", fromFrame); animStateInfo.Add("stop", toFrame); animStateInfo.Add("rate", clip.frameRate); animStateInfo.Add("frames", clipFrameCount); animStateInfo.Add("weight", 1.0f); animStateInfo.Add("behavior", (int)behavior); animStateInfo.Add("apparentSpeed", clip.apparentSpeed); animStateInfo.Add("averageSpeed", clip.averageSpeed.ToFloat()); animStateInfo.Add("averageDuration", clip.averageDuration); animStateInfo.Add("averageAngularSpeed", clip.averageAngularSpeed); List <string> customCurveKeyNames = new List <string>(); // .. // UnityEditor.AnimationParameters; // .. if (aparams != null && aparams.Count > 0) { foreach (var aparam in aparams) { if (aparam.curve == true) { var curve = Tools.GetAnimationCurve(clip, aparam.name); if (curve != null) { IEnumerable <BabylonAnimationKey> cx_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey { frame = (int)(keyFrame.time * frameRate), values = new[] { keyFrame.value } }); BabylonAnimationKey[] xkeys = (cx_keys != null && cx_keys.Count() > 0) ? cx_keys.ToArray() : null; if (xkeys != null && xkeys.Length > 0) { string xkey = aparam.name; string xprop = "metadata.state.floats." + xkey; string xname = "curve:" + clipName.Replace(" ", "") + ":" + System.Guid.NewGuid().ToString(); customCurveKeyNames.Add(xname); anims.Add(new BabylonAnimation { dataType = (int)BabylonAnimation.DataType.Float, name = xname, keys = xkeys, framePerSecond = frameRate, enableBlending = false, blendingSpeed = 0.0f, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, property = xprop }); } } } } } animStateInfo.Add("customCurveKeyNames", (customCurveKeyNames.Count > 0) ? customCurveKeyNames.ToArray() : null); metaData.animationClips.Add(animStateInfo); ranges.Add(new BabylonRange { name = clipName, from = fromFrame, to = toFrame }); } // .. frameOffest += clipFrameCount; } var babylonAnimation = new BabylonAnimation { name = "skeleton:" + bone.name.ToLower() + ":animation", property = "_matrix", dataType = (int)BabylonAnimation.DataType.Matrix, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, enableBlending = false, blendingSpeed = 0.0f, framePerSecond = frameRate, keys = keys.ToArray() }; bone.animation = babylonAnimation; } if (AnimationMode.InAnimationMode()) { AnimationMode.StopAnimationMode(); } // // Serialize Skeleton Clip Ranges // skeleton.ranges = (ranges.Count > 0) ? ranges.ToArray() : null; // // Cache Babylon Animation Keys // if (anims.Count > 0) { List <BabylonAnimation> sourceAnimiamtions = null; if (SceneBuilder.AnimationCurveKeys.ContainsKey(sourceId)) { sourceAnimiamtions = SceneBuilder.AnimationCurveKeys[sourceId]; } else { sourceAnimiamtions = new List <BabylonAnimation>(); SceneBuilder.AnimationCurveKeys.Add(sourceId, sourceAnimiamtions); } foreach (var anim in anims) { sourceAnimiamtions.Add(anim); } } }