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 QuatCorrection quatCorr = new QuatCorrection(); // restart correction for each curve 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; if (exportParameters.kuesaContQuats) { quatCorr.Quat = rotationQuatBabylon; rotationQuatBabylon = quatCorr.Quat; } 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 void ExportNodeAnimation(GLTFAnimation gltfAnimation, int startFrame, int endFrame, GLTF gltf, BabylonNode babylonNode, GLTFNode gltfNode, BabylonScene babylonScene) { var channelList = gltfAnimation.ChannelList; var samplerList = gltfAnimation.SamplerList; bool exportNonAnimated = Loader.Core.RootNode.GetBoolProperty("babylonjs_animgroup_exportnonanimated"); // Combine babylon animations from .babylon file and cached ones var babylonAnimations = new List <BabylonAnimation>(); if (babylonNode.animations != null) { babylonAnimations.AddRange(babylonNode.animations); } if (babylonNode.extraAnimations != null) { babylonAnimations.AddRange(babylonNode.extraAnimations); } // Filter animations to only keep TRS ones babylonAnimations = babylonAnimations.FindAll(babylonAnimation => _getTargetPath(babylonAnimation.property) != null); // Optimize animations to only keep ones animated between start and end frames var optimizeAnimations = !Loader.Core.RootNode.GetBoolProperty("babylonjs_donotoptimizeanimations"); // reverse negation for clarity if (optimizeAnimations) { List <BabylonAnimation> babylonAnimationsOptimized = new List <BabylonAnimation>(); foreach (BabylonAnimation babylonAnimation in babylonAnimations) { // 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, true); if (IsAnimationKeysRelevant(keysInRangeFull)) { // Override animation keys babylonAnimation.keys = keysInRangeFull.ToArray(); babylonAnimationsOptimized.Add(babylonAnimation); } } // From now, use optimized animations instead babylonAnimations = babylonAnimationsOptimized; } if (babylonAnimations.Count > 0 || exportNonAnimated) { if (babylonAnimations.Count > 0) { RaiseMessage("GLTFExporter.Animation | Export animations of node named: " + babylonNode.name, 2); } else if (exportNonAnimated) { RaiseMessage("GLTFExporter.Animation | Export dummy animation for node named: " + babylonNode.name, 2); // Export a dummy animation babylonAnimations.Add(GetDummyAnimation(gltfNode, startFrame, endFrame)); } foreach (BabylonAnimation babylonAnimation in babylonAnimations) { // Target var gltfTarget = new GLTFChannelTarget { node = gltfNode.index }; gltfTarget.path = _getTargetPath(babylonAnimation.property); // --- Input --- var accessorInput = _createAndPopulateInput(gltf, babylonAnimation, startFrame, endFrame); if (accessorInput == null) { continue; } // --- Output --- GLTFAccessor accessorOutput = _createAccessorOfPath(gltfTarget.path, gltf); // Populate accessor int numKeys = 0; QuatCorrection quatCorr = new QuatCorrection(); // restart correction for each curve foreach (var babylonAnimationKey in babylonAnimation.keys) { if (babylonAnimationKey.frame < startFrame) { continue; } if (babylonAnimationKey.frame > endFrame) { continue; } numKeys++; // copy data before changing it in case animation groups overlap float[] outputValues = new float[babylonAnimationKey.values.Length]; babylonAnimationKey.values.CopyTo(outputValues, 0); // Switch coordinate system at object level if (babylonAnimation.property == "position") { outputValues[2] *= -1; } else if (babylonAnimation.property == "rotationQuaternion") { quatCorr.Quat = new BabylonQuaternion(outputValues[0], outputValues[1], outputValues[2], outputValues[3]); if (exportParameters.kuesaContQuats) { quatCorr.Quat = new BabylonQuaternion(outputValues[0], outputValues[1], outputValues[2], outputValues[3]); outputValues[0] = quatCorr.Quat.X; outputValues[1] = quatCorr.Quat.Y; outputValues[2] = quatCorr.Quat.Z; outputValues[3] = quatCorr.Quat.W; } outputValues[0] *= -1; outputValues[1] *= -1; } // Store values as bytes foreach (var outputValue in outputValues) { accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue)); } } ; accessorOutput.count = numKeys; // bail out if no keyframes to export (?) // todo [KeyInterpolation]: bail out only when there are no keyframes at all (?) and otherwise add the appropriate (interpolated) keyframes if (numKeys == 0) { continue; } // Animation sampler var gltfAnimationSampler = new GLTFAnimationSampler { input = accessorInput.index, output = accessorOutput.index }; gltfAnimationSampler.index = samplerList.Count; samplerList.Add(gltfAnimationSampler); // Channel var gltfChannel = new GLTFChannel { sampler = gltfAnimationSampler.index, target = gltfTarget }; channelList.Add(gltfChannel); } } if (babylonNode.GetType() == typeof(BabylonMesh)) { var babylonMesh = babylonNode as BabylonMesh; // Morph targets var babylonMorphTargetManager = GetBabylonMorphTargetManager(babylonScene, babylonMesh); if (babylonMorphTargetManager != null) { ExportMorphTargetWeightAnimation(babylonMorphTargetManager, gltf, gltfNode, channelList, samplerList, startFrame, endFrame); } } }