private void ExportBoneAnimation(GLTFAnimation gltfAnimation, int startFrame, int endFrame, GLTF gltf, BabylonNode babylonNode, GLTFNode gltfNode, BabylonAnimationGroup animationGroup = null) { var channelList = gltfAnimation.ChannelList; var samplerList = gltfAnimation.SamplerList; if (babylonNode.animations != null && babylonNode.animations[0].property == "_matrix") { logger.RaiseMessage("GLTFExporter.Animation | Export animation of bone named: " + babylonNode.name, 2); BabylonAnimation babylonAnimation = null; if (animationGroup != null) { var targetedAnimation = animationGroup.targetedAnimations.FirstOrDefault(animation => animation.targetId == babylonNode.id); if (targetedAnimation != null) { babylonAnimation = targetedAnimation.animation; } } // otherwise fall back to the full animation track on the node. if (babylonAnimation == null) { babylonAnimation = babylonNode.animations[0]; } var babylonAnimationKeysInRange = babylonAnimation.keys.Where(key => key.frame >= startFrame && key.frame <= endFrame); if (babylonAnimationKeysInRange.Count() <= 0) { return; } // --- Input --- var accessorInput = _createAndPopulateInput(gltf, babylonAnimation, startFrame, endFrame); if (accessorInput == null) { return; } // --- Output --- var paths = new string[] { "translation", "rotation", "scale" }; var accessorOutputByPath = new Dictionary <string, GLTFAccessor>(); foreach (string path in paths) { GLTFAccessor accessorOutput = _createAccessorOfPath(path, gltf); accessorOutputByPath.Add(path, accessorOutput); } // Populate accessors foreach (var babylonAnimationKey in babylonAnimationKeysInRange) { var matrix = new BabylonMatrix(); matrix.m = babylonAnimationKey.values; var translationBabylon = new BabylonVector3(); var rotationQuatBabylon = new BabylonQuaternion(); var scaleBabylon = new BabylonVector3(); matrix.decompose(scaleBabylon, rotationQuatBabylon, translationBabylon); // Switch coordinate system at object level translationBabylon.Z *= -1; rotationQuatBabylon.X *= -1; rotationQuatBabylon.Y *= -1; var outputValuesByPath = new Dictionary <string, float[]>(); outputValuesByPath.Add("translation", translationBabylon.ToArray()); outputValuesByPath.Add("rotation", rotationQuatBabylon.ToArray()); outputValuesByPath.Add("scale", scaleBabylon.ToArray()); // Store values as bytes foreach (string path in paths) { var accessorOutput = accessorOutputByPath[path]; var outputValues = outputValuesByPath[path]; foreach (var outputValue in outputValues) { accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue)); } accessorOutput.count++; } } ; foreach (string path in paths) { var accessorOutput = accessorOutputByPath[path]; // Animation sampler var gltfAnimationSampler = new GLTFAnimationSampler { input = accessorInput.index, output = accessorOutput.index }; gltfAnimationSampler.index = samplerList.Count; samplerList.Add(gltfAnimationSampler); // Target var gltfTarget = new GLTFChannelTarget { node = gltfNode.index }; gltfTarget.path = path; // Channel var gltfChannel = new GLTFChannel { sampler = gltfAnimationSampler.index, target = gltfTarget }; channelList.Add(gltfChannel); } } ExportGLTFExtension(babylonNode, ref gltfAnimation, gltf); }
/// <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; } // Optimization OptimizeAnimations(babylonAnimationKeys, true); // 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(); } } // Ensure animation has at least 2 frames if (babylonAnimationKeys.Count > 1) { var animationPresent = true; // Ensure animation has at least 2 non equal frames if (babylonAnimationKeys.Count == 2) { if (babylonAnimationKeys[0].values.IsEqualTo(babylonAnimationKeys[1].values)) { animationPresent = false; } } if (animationPresent) { // 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() }); } } } return(animationsObject); }
private void ExportBoneAnimation(GLTFAnimation gltfAnimation, int startFrame, int endFrame, GLTF gltf, BabylonBone babylonBone, GLTFNode gltfNode) { var channelList = gltfAnimation.ChannelList; var samplerList = gltfAnimation.SamplerList; if (babylonBone.animation != null && babylonBone.animation.property == "_matrix") { logger.RaiseMessage("GLTFExporter.Animation | Export animation of bone named: " + babylonBone.name, 2); var babylonAnimation = babylonBone.animation; // --- Input --- var accessorInput = _createAndPopulateInput(gltf, babylonAnimation, startFrame, endFrame); if (accessorInput == null) { return; } // --- Output --- var paths = new string[] { "translation", "rotation", "scale" }; var accessorOutputByPath = new Dictionary <string, GLTFAccessor>(); foreach (string path in paths) { GLTFAccessor accessorOutput = _createAccessorOfPath(path, gltf); accessorOutputByPath.Add(path, accessorOutput); } // Populate accessors foreach (var babylonAnimationKey in babylonAnimation.keys) { if (babylonAnimationKey.frame < startFrame) { continue; } if (babylonAnimationKey.frame > endFrame) { continue; } var matrix = new BabylonMatrix(); matrix.m = babylonAnimationKey.values; var translationBabylon = new BabylonVector3(); var rotationQuatBabylon = new BabylonQuaternion(); var scaleBabylon = new BabylonVector3(); matrix.decompose(scaleBabylon, rotationQuatBabylon, translationBabylon); // Switch coordinate system at object level translationBabylon.Z *= -1; translationBabylon *= exportParameters.scaleFactor; rotationQuatBabylon.X *= -1; rotationQuatBabylon.Y *= -1; var outputValuesByPath = new Dictionary <string, float[]>(); outputValuesByPath.Add("translation", translationBabylon.ToArray()); outputValuesByPath.Add("rotation", rotationQuatBabylon.ToArray()); outputValuesByPath.Add("scale", scaleBabylon.ToArray()); // Store values as bytes foreach (string path in paths) { var accessorOutput = accessorOutputByPath[path]; var outputValues = outputValuesByPath[path]; foreach (var outputValue in outputValues) { accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue)); } accessorOutput.count++; } } ; foreach (string path in paths) { var accessorOutput = accessorOutputByPath[path]; // Animation sampler var gltfAnimationSampler = new GLTFAnimationSampler { input = accessorInput.index, output = accessorOutput.index }; gltfAnimationSampler.index = samplerList.Count; samplerList.Add(gltfAnimationSampler); // Target var gltfTarget = new GLTFChannelTarget { node = gltfNode.index }; gltfTarget.path = path; // Channel var gltfChannel = new GLTFChannel { sampler = gltfAnimationSampler.index, target = gltfTarget }; channelList.Add(gltfChannel); } } }
private GLTFNode _exportBone(BabylonBone babylonBone, GLTF gltf, BabylonSkeleton babylonSkeleton, List <BabylonBone> bones) { var nodeNodePair = nodeToGltfNodeMap.FirstOrDefault(pair => pair.Key.id.Equals(babylonBone.id)); if (nodeNodePair.Key != null) { return(nodeNodePair.Value); } var boneNodePair = boneToGltfNodeMap.FirstOrDefault(pair => pair.Key.id.Equals(babylonBone.id)); if (boneNodePair.Key != null) { return(boneNodePair.Value); } // Node var gltfNode = new GLTFNode { name = babylonBone.name, index = gltf.NodesList.Count }; gltf.NodesList.Add(gltfNode); boneToGltfNodeMap.Add(babylonBone, gltfNode); // Hierarchy if (babylonBone.parentBoneIndex >= 0) { var babylonParentBone = bones.Find(_babylonBone => _babylonBone.index == babylonBone.parentBoneIndex); var gltfParentNode = _exportBone(babylonParentBone, gltf, babylonSkeleton, bones); RaiseMessage("GLTFExporter.Skin | Add " + babylonBone.name + " as child to " + gltfParentNode.name, 3); gltfParentNode.ChildrenList.Add(gltfNode.index); gltfNode.parent = gltfParentNode; } else { // It's a root node // Only root nodes are listed in a gltf scene RaiseMessage("GLTFExporter.Skin | Add " + babylonBone.name + " as root node to scene", 3); gltf.scenes[0].NodesList.Add(gltfNode.index); } // Transform // Bones transform are exported through translation/rotation/scale (TRS) rather than matrix // Because gltf node animation can only target TRS properties, not the matrix one // Create matrix from array var babylonMatrix = new BabylonMatrix(); babylonMatrix.m = babylonBone.matrix; // Decompose matrix into TRS var translationBabylon = new BabylonVector3(); var rotationQuatBabylon = new BabylonQuaternion(); var scaleBabylon = new BabylonVector3(); babylonMatrix.decompose(scaleBabylon, rotationQuatBabylon, translationBabylon); // Store TRS values gltfNode.translation = translationBabylon.ToArray(); gltfNode.rotation = rotationQuatBabylon.ToArray(); gltfNode.scale = scaleBabylon.ToArray(); // Animations //ExportBoneAnimation(babylonBone, gltf, gltfNode); return(gltfNode); }
private BabylonCamera ExportCamera(IIGameScene scene, IIGameNode cameraNode, BabylonScene babylonScene) { if (IsCameraExportable(cameraNode) == false) { return(null); } var gameCamera = cameraNode.IGameObject.AsGameCamera(); var maxCamera = gameCamera.MaxObject as ICameraObject; var initialized = gameCamera.InitializeData; var babylonCamera = new BabylonCamera(); RaiseMessage(cameraNode.Name, 1); babylonCamera.name = cameraNode.Name; babylonCamera.id = cameraNode.MaxNode.GetGuid().ToString(); if (cameraNode.NodeParent != null) { babylonCamera.parentId = cameraNode.NodeParent.MaxNode.GetGuid().ToString(); } babylonCamera.fov = Tools.ConvertFov(maxCamera.GetFOV(0, Tools.Forever)); if (maxCamera.ManualClip == 1) { babylonCamera.minZ = maxCamera.GetClipDist(0, 1, Tools.Forever); babylonCamera.maxZ = maxCamera.GetClipDist(0, 2, Tools.Forever); } else { babylonCamera.minZ = 0.1f; babylonCamera.maxZ = 10000.0f; } if (babylonCamera.minZ == 0.0f) { babylonCamera.minZ = 0.1f; } // Type babylonCamera.type = cameraNode.MaxNode.GetStringProperty("babylonjs_type", "FreeCamera"); // Control babylonCamera.speed = cameraNode.MaxNode.GetFloatProperty("babylonjs_speed", 1.0f); babylonCamera.inertia = cameraNode.MaxNode.GetFloatProperty("babylonjs_inertia", 0.9f); // Collisions babylonCamera.checkCollisions = cameraNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions"); babylonCamera.applyGravity = cameraNode.MaxNode.GetBoolProperty("babylonjs_applygravity"); babylonCamera.ellipsoid = cameraNode.MaxNode.GetVector3Property("babylonjs_ellipsoid"); // Position / rotation var localTM = cameraNode.GetObjectTM(0); if (cameraNode.NodeParent != null) { var parentWorld = cameraNode.NodeParent.GetObjectTM(0); localTM.MultiplyBy(parentWorld.Inverse); } var position = localTM.Translation; var rotation = localTM.Rotation; babylonCamera.position = new[] { position.X, position.Y, position.Z }; var rotationQuaternion = new BabylonQuaternion { X = rotation.X, Y = rotation.Y, Z = rotation.Z, W = -rotation.W }; if (ExportQuaternionsInsteadOfEulers) { babylonCamera.rotationQuaternion = rotationQuaternion.ToArray(); } else { babylonCamera.rotation = rotationQuaternion.toEulerAngles().ToArray(); } // Target var target = gameCamera.CameraTarget; if (target != null) { babylonCamera.lockedTargetId = target.MaxNode.GetGuid().ToString(); } else { var dir = localTM.GetRow(3); babylonCamera.target = new [] { position.X - dir.X, position.Y - dir.Y, position.Z - dir.Z }; } // Animations var animations = new List <BabylonAnimation>(); ExportVector3Animation("position", animations, key => { var tm = cameraNode.GetLocalTM(key); if (cameraNode.NodeParent != null) { var parentWorld = cameraNode.NodeParent.GetObjectTM(key); tm.MultiplyBy(parentWorld.Inverse); } var translation = tm.Translation; return(new [] { translation.X, translation.Y, translation.Z }); }); if (gameCamera.CameraTarget == null) { ExportVector3Animation("target", animations, key => { var tm = cameraNode.GetLocalTM(key); if (cameraNode.NodeParent != null) { var parentWorld = cameraNode.NodeParent.GetObjectTM(key); tm.MultiplyBy(parentWorld.Inverse); } var translation = tm.Translation; var dir = tm.GetRow(3); return(new float[] { translation.X - dir.X, translation.Y - dir.Y, translation.Z - dir.Z }); }); } ExportFloatAnimation("fov", animations, key => new[] { Tools.ConvertFov((gameCamera.MaxObject as ICameraObject).GetFOV(key, Tools.Forever)) }); babylonCamera.animations = animations.ToArray(); if (cameraNode.MaxNode.GetBoolProperty("babylonjs_autoanimate")) { babylonCamera.autoAnimate = true; babylonCamera.autoAnimateFrom = (int)cameraNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_from"); babylonCamera.autoAnimateTo = (int)cameraNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_to"); babylonCamera.autoAnimateLoop = cameraNode.MaxNode.GetBoolProperty("babylonjs_autoanimateloop"); } babylonScene.CamerasList.Add(babylonCamera); return(babylonCamera); }
/// <summary> /// In 3DS Max default element can look in different direction than the same default element in Babylon or in glTF. /// This function correct the node rotation. /// </summary> /// <param name="node"></param> /// <param name="babylonScene"></param> /// <param name="angle"></param> private void FixNodeRotation(ref BabylonNode node, ref BabylonScene babylonScene, double angle) { string id = node.id; IList <BabylonMesh> meshes = babylonScene.MeshesList.FindAll(mesh => mesh.parentId == null ? false : mesh.parentId.Equals(id)); RaiseMessage($"{node.name}", 2); // fix the vue // Rotation around the axis X of PI / 2 in the indirect direction for camera // double angle = Math.PI / 2; // for camera // double angle = -Math.PI / 2; // for light if (node.rotation != null) { node.rotation[0] += (float)angle; } if (node.rotationQuaternion != null) { BabylonQuaternion rotationQuaternion = FixCameraQuaternion(node, angle); node.rotationQuaternion = rotationQuaternion.ToArray(); node.rotation = rotationQuaternion.toEulerAngles().ToArray(); } // animation List <BabylonAnimation> animations = new List <BabylonAnimation>(node.animations); BabylonAnimation animationRotationQuaternion = animations.Find(animation => animation.property.Equals("rotationQuaternion")); if (animationRotationQuaternion != null) { foreach (BabylonAnimationKey key in animationRotationQuaternion.keys) { key.values = FixCameraQuaternion(key.values, angle); } } else // if the camera has a lockedTargetId, it is the extraAnimations that stores the rotation animation { if (node.extraAnimations != null) { List <BabylonAnimation> extraAnimations = new List <BabylonAnimation>(node.extraAnimations); animationRotationQuaternion = extraAnimations.Find(animation => animation.property.Equals("rotationQuaternion")); if (animationRotationQuaternion != null) { foreach (BabylonAnimationKey key in animationRotationQuaternion.keys) { key.values = FixCameraQuaternion(key.values, angle); } } } } // fix direct children // Rotation around the axis X of -PI / 2 in the direct direction for camera children angle = -angle; foreach (var mesh in meshes) { RaiseVerbose($"{mesh.name}", 3); mesh.position = new float[] { mesh.position[0], mesh.position[2], -mesh.position[1] }; // Add a rotation of PI/2 axis X in direct direction if (mesh.rotationQuaternion != null) { // Rotation around the axis X of -PI / 2 in the direct direction BabylonQuaternion quaternion = FixChildQuaternion(mesh, angle); mesh.rotationQuaternion = quaternion.ToArray(); } if (mesh.rotation != null) { mesh.rotation[0] += (float)angle; } // Animations animations = new List <BabylonAnimation>(mesh.animations); // Position BabylonAnimation animationPosition = animations.Find(animation => animation.property.Equals("position")); if (animationPosition != null) { foreach (BabylonAnimationKey key in animationPosition.keys) { key.values = new float[] { key.values[0], key.values[2], -key.values[1] }; } } // Rotation animationRotationQuaternion = animations.Find(animation => animation.property.Equals("rotationQuaternion")); if (animationRotationQuaternion != null) { foreach (BabylonAnimationKey key in animationRotationQuaternion.keys) { key.values = FixChildQuaternion(key.values, angle); } } } }
private void ExportBoneAnimation(GLTFAnimation gltfAnimation, int startFrame, int endFrame, GLTF gltf, BabylonBone babylonBone, GLTFNode gltfNode) { var channelList = gltfAnimation.ChannelList; var samplerList = gltfAnimation.SamplerList; if (babylonBone.animation != null && babylonBone.animation.property == "_matrix") { RaiseMessage("GLTFExporter.Animation | Export animation of bone named: " + babylonBone.name, 2); var babylonAnimation = babylonBone.animation; // Optimize animation var optimizeAnimations = !Loader.Core.RootNode.GetBoolProperty("babylonjs_donotoptimizeanimations"); // reverse negation for clarity if (optimizeAnimations) { // Filter animation keys to only keep frames between start and end List <BabylonAnimationKey> keysInRangeFull = babylonAnimation.keysFull.FindAll(babylonAnimationKey => babylonAnimationKey.frame >= startFrame && babylonAnimationKey.frame <= endFrame); // Optimization process always keeps first and last frames OptimizeAnimations(keysInRangeFull, false); if (IsAnimationKeysRelevant(keysInRangeFull)) { // From now, use optimized animation instead // Override animation keys babylonAnimation.keys = keysInRangeFull.ToArray(); } } // --- Input --- var accessorInput = _createAndPopulateInput(gltf, babylonAnimation, startFrame, endFrame); if (accessorInput == null) { return; } // --- Output --- var paths = new string[] { "translation", "rotation", "scale" }; var accessorOutputByPath = new Dictionary <string, GLTFAccessor>(); foreach (string path in paths) { GLTFAccessor accessorOutput = _createAccessorOfPath(path, gltf); accessorOutputByPath.Add(path, accessorOutput); } // Populate accessors foreach (var babylonAnimationKey in babylonAnimation.keys) { if (babylonAnimationKey.frame < startFrame) { continue; } if (babylonAnimationKey.frame > endFrame) { continue; } var matrix = new BabylonMatrix(); matrix.m = babylonAnimationKey.values; var translationBabylon = new BabylonVector3(); var rotationQuatBabylon = new BabylonQuaternion(); var scaleBabylon = new BabylonVector3(); matrix.decompose(scaleBabylon, rotationQuatBabylon, translationBabylon); translationBabylon.Z *= -1; rotationQuatBabylon.X *= -1; rotationQuatBabylon.Y *= -1; var outputValuesByPath = new Dictionary <string, float[]>(); outputValuesByPath.Add("translation", translationBabylon.ToArray()); outputValuesByPath.Add("rotation", rotationQuatBabylon.ToArray()); outputValuesByPath.Add("scale", scaleBabylon.ToArray()); // Store values as bytes foreach (string path in paths) { var accessorOutput = accessorOutputByPath[path]; var outputValues = outputValuesByPath[path]; foreach (var outputValue in outputValues) { accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue)); } accessorOutput.count++; } } ; foreach (string path in paths) { var accessorOutput = accessorOutputByPath[path]; // Animation sampler var gltfAnimationSampler = new GLTFAnimationSampler { input = accessorInput.index, output = accessorOutput.index }; gltfAnimationSampler.index = samplerList.Count; samplerList.Add(gltfAnimationSampler); // Target var gltfTarget = new GLTFChannelTarget { node = gltfNode.index }; gltfTarget.path = path; // Channel var gltfChannel = new GLTFChannel { sampler = gltfAnimationSampler.index, target = gltfTarget }; channelList.Add(gltfChannel); } } }