private void ExportAnimationGroups(GLTF gltf, BabylonScene babylonScene) { AnimationGroupList animationList = new AnimationGroupList(); animationList.LoadFromData(); gltf.AnimationsList.Clear(); gltf.AnimationsList.Capacity = Math.Max(gltf.AnimationsList.Capacity, animationList.Count); foreach (AnimationGroup animGroup in animationList) { GLTFAnimation gltfAnimation = new GLTFAnimation(); gltfAnimation.name = animGroup.Name; foreach (uint nodeHandle in animGroup.NodeHandles) { // todo: make something a little more efficient.. IINode maxNode = Loader.Core.RootNode.FindChildNode(nodeHandle); string id = maxNode.GetGuid().ToString(); BabylonNode babylonNode = babylonNodes.Find(node => node.id.Equals(id)); if (babylonNode != null && nodeToGltfNodeMap.TryGetValue(babylonNode, out GLTFNode gltfNode)) { ExportNodeAnimation(gltfAnimation, animGroup.FrameStart, animGroup.FrameEnd, gltf, babylonNode, gltfNode, babylonScene); } // export all bones that match this id foreach (KeyValuePair <BabylonBone, GLTFNode> pair in boneToGltfNodeMap) { if (pair.Key.id.Equals(id)) { ExportBoneAnimation(gltfAnimation, animGroup.FrameStart, animGroup.FrameEnd, gltf, pair.Key, pair.Value); } } } gltf.AnimationsList.Add(gltfAnimation); } }
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); }
private void ExportNodeAnimation(GLTFAnimation gltfAnimation, int startFrame, int endFrame, GLTF gltf, BabylonNode babylonNode, GLTFNode gltfNode, BabylonScene babylonScene, BabylonAnimationGroup animationGroup = null) { var channelList = gltfAnimation.ChannelList; var samplerList = gltfAnimation.SamplerList; bool exportNonAnimated = exportParameters.animgroupExportNonAnimated; // Combine babylon animations from .babylon file and cached ones var babylonAnimations = new List <BabylonAnimation>(); if (animationGroup != null) { var targetedAnimations = animationGroup.targetedAnimations.Where(animation => animation.targetId == babylonNode.id); foreach (var targetedAnimation in targetedAnimations) { babylonAnimations.Add(targetedAnimation.animation); } } // Do not include the node animations if a provided animation group already includes them. if (babylonAnimations.Count <= 0) { 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); if (babylonAnimations.Count > 0 || exportNonAnimated) { if (babylonAnimations.Count > 0) { logger.RaiseMessage("GLTFExporter.Animation | Export animations of node named: " + babylonNode.name, 2); } else if (exportNonAnimated) { logger.RaiseMessage("GLTFExporter.Animation | Export dummy animation for node named: " + babylonNode.name, 2); // Export a dummy animation babylonAnimations.Add(GetDummyAnimation(gltfNode, startFrame, endFrame, babylonScene)); } foreach (BabylonAnimation babylonAnimation in babylonAnimations) { var babylonAnimationKeysInRange = babylonAnimation.keys.Where(key => key.frame >= startFrame && key.frame <= endFrame); if (babylonAnimationKeysInRange.Count() <= 0) { continue; } // 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; foreach (var babylonAnimationKey in babylonAnimationKeysInRange) { 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") { outputValues[0] *= -1; outputValues[1] *= -1; } // Store values as bytes foreach (var outputValue in outputValues) { accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue)); } } ; accessorOutput.count = numKeys; if (accessorOutput.count == 0) { logger.RaiseWarning(String.Format("GLTFExporter.Animation | No frames to export in node animation \"{1}\" of node named \"{0}\". This will cause an error in the output gltf.", babylonNode.name, babylonAnimation.name)); } // 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); } } ExportGLTFExtension(babylonNode, ref gltfAnimation, gltf); }
private void ExportAnimationGroups(GLTF gltf, BabylonScene babylonScene) { // Retreive and parse animation group data var animationGroupList = babylonScene.animationGroups; var animationGroupCount = animationGroupList == null ? 0 : animationGroupList.Count; gltf.AnimationsList.Clear(); gltf.AnimationsList.Capacity = Math.Max(gltf.AnimationsList.Capacity, animationGroupCount); if (animationGroupCount <= 0) { logger.RaiseMessage("GLTFExporter.Animation | No AnimationGroups: exporting all animations together.", 1); GLTFAnimation gltfAnimation = new GLTFAnimation(); gltfAnimation.name = "All Animations"; int startFrame = babylonScene.TimelineStartFrame; int endFrame = babylonScene.TimelineEndFrame; foreach (var pair in nodeToGltfNodeMap) { BabylonNode node = pair.Key; GLTFNode gltfNode = pair.Value; bool nodeHasAnimations = node.animations != null && node.animations.Length > 0 && node.animations[0] != null; bool nodeHasExtraAnimations = node.extraAnimations != null && node.extraAnimations.Count > 0 && node.extraAnimations[0] != null; BabylonMesh meshNode = node as BabylonMesh; BabylonMorphTargetManager morphTargetManager = null; bool nodeHasAnimatedMorphTargets = false; if (meshNode != null && meshNode.morphTargetManagerId != null) { morphTargetManager = GetBabylonMorphTargetManager(babylonScene, meshNode); if (morphTargetManager != null) { nodeHasAnimatedMorphTargets = morphTargetManager.targets.Any(target => target.animations != null && target.animations.Length > 0 && target.animations[0] != null); } } if (!nodeHasAnimations && !nodeHasExtraAnimations && !nodeHasAnimatedMorphTargets) { continue; } if (nodeHasAnimations && node.animations[0].property == "_matrix") { ExportBoneAnimation(gltfAnimation, startFrame, endFrame, gltf, node, pair.Value); } else { ExportNodeAnimation(gltfAnimation, startFrame, endFrame, gltf, node, gltfNode, babylonScene); } if (nodeHasAnimatedMorphTargets) { ExportMorphTargetWeightAnimation(morphTargetManager, gltf, gltfNode, gltfAnimation.ChannelList, gltfAnimation.SamplerList, startFrame, endFrame, babylonScene); } } if (gltfAnimation.ChannelList.Count > 0) { gltf.AnimationsList.Add(gltfAnimation); } else { logger.RaiseMessage("GLTFExporter.Animation | No animation data for this animation, it is ignored.", 2); } } else { foreach (BabylonAnimationGroup animGroup in animationGroupList) { logger.RaiseMessage("GLTFExporter.Animation | " + animGroup.name, 1); GLTFAnimation gltfAnimation = new GLTFAnimation(); gltfAnimation.name = animGroup.name; int startFrame = MathUtilities.RoundToInt(animGroup.from); int endFrame = MathUtilities.RoundToInt(animGroup.to); var uniqueNodeIds = animGroup.targetedAnimations.Select(targetAnim => targetAnim.targetId).Distinct(); foreach (var id in uniqueNodeIds) { BabylonNode babylonNode = babylonNodes.Find(node => node.id.Equals(id)); GLTFNode gltfNode = null; // search the babylon scene id map for the babylon node that matches this id if (babylonNode != null) { BabylonMorphTargetManager morphTargetManager = null; // search our babylon->gltf node mapping to see if this node is included in the exported gltf scene if (!nodeToGltfNodeMap.TryGetValue(babylonNode, out gltfNode)) { continue; } bool nodeHasAnimations = babylonNode.animations != null && babylonNode.animations.Length > 0 && babylonNode.animations[0] != null; bool nodeHasExtraAnimations = babylonNode.extraAnimations != null && babylonNode.extraAnimations.Count > 0 && babylonNode.extraAnimations[0] != null; if (!nodeHasAnimations && !nodeHasExtraAnimations) { continue; } if (nodeHasAnimations && babylonNode.animations[0].property == "_matrix") //TODO: Is this check accurate for deciphering between bones and nodes? { ExportBoneAnimation(gltfAnimation, startFrame, endFrame, gltf, babylonNode, gltfNode, animGroup); } else { ExportNodeAnimation(gltfAnimation, startFrame, endFrame, gltf, babylonNode, gltfNode, babylonScene, animGroup); } } else { // if the node isn't found in the scene id map, check if it is the id for a morph target BabylonMorphTargetManager morphTargetManager = babylonScene.morphTargetManagers.FirstOrDefault(mtm => mtm.targets.Any(target => target.animations != null && target.animations.Length > 0 && target.animations[0] != null)); if (morphTargetManager != null) { BabylonMesh mesh = morphTargetManager.sourceMesh; if (mesh != null && nodeToGltfNodeMap.TryGetValue(mesh, out gltfNode)) { ExportMorphTargetWeightAnimation(morphTargetManager, gltf, gltfNode, gltfAnimation.ChannelList, gltfAnimation.SamplerList, startFrame, endFrame, babylonScene); } } } } if (gltfAnimation.ChannelList.Count > 0) { gltf.AnimationsList.Add(gltfAnimation); } else { logger.RaiseMessage("No data exported for this animation, it is ignored.", 2); } // clear the exported morph target cache, since we are exporting a new animation group. //TODO: we should probably do this more elegantly. exportedMorphTargets.Clear(); } } }
private void ExportAnimationGroups(GLTF gltf, BabylonScene babylonScene) { // Retreive and parse animation group data AnimationGroupList animationList = InitAnimationGroups(); gltf.AnimationsList.Clear(); gltf.AnimationsList.Capacity = Math.Max(gltf.AnimationsList.Capacity, animationList.Count); if (animationList.Count <= 0) { RaiseMessage("GLTFExporter.Animation | No AnimationGroups: exporting all animations together.", 1); GLTFAnimation gltfAnimation = new GLTFAnimation(); gltfAnimation.name = "All Animations"; int minFrame = Loader.GetMinTime(); int maxFrame = Loader.GetMaxTime(); foreach (var pair in nodeToGltfNodeMap) { ExportNodeAnimation(gltfAnimation, minFrame, maxFrame, gltf, pair.Key, pair.Value, babylonScene); } foreach (var pair in boneToGltfNodeMap) { ExportBoneAnimation(gltfAnimation, minFrame, maxFrame, gltf, pair.Key, pair.Value); } if (gltfAnimation.ChannelList.Count > 0) { gltf.AnimationsList.Add(gltfAnimation); } else { RaiseMessage("GLTFExporter.Animation | No animation data for this animation, it is ignored.", 2); } } else { foreach (AnimationGroup animGroup in animationList) { RaiseMessage("GLTFExporter.Animation | " + animGroup.Name, 1); GLTFAnimation gltfAnimation = new GLTFAnimation(); gltfAnimation.name = animGroup.Name; int startFrame = animGroup.FrameStart; int endFrame = animGroup.FrameEnd; foreach (var pair in nodeToGltfNodeMap) { ExportNodeAnimation(gltfAnimation, startFrame, endFrame, gltf, pair.Key, pair.Value, babylonScene); } foreach (var pair in boneToGltfNodeMap) { ExportBoneAnimation(gltfAnimation, startFrame, endFrame, gltf, pair.Key, pair.Value); } if (gltfAnimation.ChannelList.Count > 0) { gltf.AnimationsList.Add(gltfAnimation); } else { RaiseMessage("No data exported for this animation, it is ignored.", 2); } } } }
private GLTFAnimation ExportBoneAnimation(BabylonBone babylonBone, GLTF gltf, GLTFNode gltfNode) { GLTFAnimation gltfAnimation = null; if (gltf.AnimationsList.Count > 0) { gltfAnimation = gltf.AnimationsList[0]; } else { gltfAnimation = new GLTFAnimation(); gltf.AnimationsList.Add(gltfAnimation); } 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; // --- Input --- var accessorInput = _createAndPopulateInput(gltf, babylonAnimation); // --- 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) { 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; BabylonVector3 rotationVector3 = rotationQuatBabylon.toEulerAngles(); rotationVector3.X *= -1; rotationVector3.Y *= -1; rotationQuatBabylon = rotationVector3.toQuaternion(); 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); } } return(gltfAnimation); }
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 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; if ((babylonNode.animations != null && babylonNode.animations.Length > 0) || (babylonNode.extraAnimations != null && babylonNode.extraAnimations.Count > 0)) { RaiseMessage("GLTFExporter.Animation | Export animation of node named: " + babylonNode.name, 2); // 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); } foreach (BabylonAnimation babylonAnimation in babylonAnimations) { // Target var gltfTarget = new GLTFChannelTarget { node = gltfNode.index }; gltfTarget.path = _getTargetPath(babylonAnimation.property); if (gltfTarget.path == null) { // Unkown babylon animation property //RaiseWarning("GLTFExporter.Animation | Unkown animation property '" + babylonAnimation.property + "'", 3); // Ignore this babylon animation continue; } // --- Input --- var accessorInput = _createAndPopulateInput(gltf, babylonAnimation, startFrame, endFrame); if (accessorInput == null) { continue; } // --- Output --- GLTFAccessor accessorOutput = _createAccessorOfPath(gltfTarget.path, gltf); // Populate accessor int numKeys = 0; foreach (var babylonAnimationKey in babylonAnimation.keys) { if (babylonAnimationKey.frame < startFrame) { continue; } if (babylonAnimationKey.frame > endFrame) { continue; } numKeys++; var outputValues = babylonAnimationKey.values; // 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); } } }
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); 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; 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") { 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); } } }
private static float FPS_FACTOR = 30.0f; // TODO - Which FPS factor ? private GLTFAnimation ExportNodeAnimation(BabylonNode babylonNode, GLTF gltf, GLTFNode gltfNode, BabylonScene babylonScene = null) { var channelList = new List <GLTFChannel>(); var samplerList = new List <GLTFAnimationSampler>(); if (babylonNode.animations != null && babylonNode.animations.Length > 0) { RaiseMessage("GLTFExporter.Animation | Export animation of node named: " + babylonNode.name, 2); foreach (BabylonAnimation babylonAnimation in babylonNode.animations) { // Target var gltfTarget = new GLTFChannelTarget { node = gltfNode.index }; gltfTarget.path = _getTargetPath(babylonAnimation.property); if (gltfTarget.path == null) { // Unkown babylon animation property RaiseWarning("GLTFExporter.Animation | Unkown animation property '" + babylonAnimation.property + "'", 3); // Ignore this babylon animation continue; } // --- Input --- var accessorInput = _createAndPopulateInput(gltf, babylonAnimation); // --- Output --- GLTFAccessor accessorOutput = _createAccessorOfPath(gltfTarget.path, gltf); // Populate accessor foreach (var babylonAnimationKey in babylonAnimation.keys) { var outputValues = babylonAnimationKey.values; // Store values as bytes foreach (var outputValue in outputValues) { accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue)); } } ; accessorOutput.count = babylonAnimation.keys.Length; // 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); } } // Do not export empty arrays if (channelList.Count > 0) { // Animation var gltfAnimation = new GLTFAnimation { channels = channelList.ToArray(), samplers = samplerList.ToArray() }; gltf.AnimationsList.Add(gltfAnimation); return(gltfAnimation); } else { return(null); } }
private void ExportAnimatedNode(GLTF gltf, BabylonNode node, GLTFNode gltfNode, GLTFAnimation gltfAnimation, int startFrame, int endFrame, BabylonAnimationGroup animGroup = null) { bool nodeHasAnimations = node.animations != null && node.animations.Length > 0 && node.animations[0] != null; bool nodeHasExtraAnimations = node.extraAnimations != null && node.extraAnimations.Count > 0 && node.extraAnimations[0] != null; BabylonMesh meshNode = node as BabylonMesh; BabylonMorphTargetManager morphTargetManager = null; bool nodeHasAnimatedMorphTargets = false; if (meshNode != null && meshNode.morphTargetManagerId != null) { morphTargetManager = GetBabylonMorphTargetManager(babylonScene, meshNode); if (morphTargetManager != null) { nodeHasAnimatedMorphTargets = morphTargetManager.targets.Any(target => target.animations != null && target.animations.Length > 0 && target.animations[0] != null); } } if (!nodeHasAnimations && !nodeHasExtraAnimations && !nodeHasAnimatedMorphTargets) { return; } if (nodeHasAnimations && node.animations[0].property == "_matrix") { ExportBoneAnimation(gltfAnimation, startFrame, endFrame, gltf, node, gltfNode, animGroup); } else { ExportNodeAnimation(gltfAnimation, startFrame, endFrame, gltf, node, gltfNode, babylonScene, animGroup); } if (nodeHasAnimatedMorphTargets) { ExportMorphTargetWeightAnimation(morphTargetManager, gltf, gltfNode, gltfAnimation.ChannelList, gltfAnimation.SamplerList, startFrame, endFrame, babylonScene); } }
private void ExportGenericPropertyAnimation <T1, T2>(GLTFAnimation gltfAnimation, int startFrame, int endFrame, GLTF gltf, T1 babylonObject, T2 gltfObject, BabylonScene babylonScene) { AnimationExtensionInfo info = new AnimationExtensionInfo(startFrame, endFrame); ExportGLTFExtension(babylonObject, ref gltfAnimation, gltf, info); }
private void ExportMaterialAnimation(GLTFAnimation gltfAnimation, int startFrame, int endFrame, GLTF gltf, BabylonMaterial babylonMaterial, GLTFMaterial gltfMaterial, BabylonScene babylonScene) { AnimationExtensionInfo info = new AnimationExtensionInfo(startFrame, endFrame); ExportGLTFExtension(babylonMaterial, ref gltfAnimation, gltf, info); }
private void ExportAnimationGroups(GLTF gltf, BabylonScene babylonScene) { // Retreive and parse animation group data AnimationGroupList animationList = InitAnimationGroups(); gltf.AnimationsList.Clear(); gltf.AnimationsList.Capacity = Math.Max(gltf.AnimationsList.Capacity, animationList.Count); if (animationList.Count <= 0) { RaiseMessage("GLTFExporter.Animation | No AnimationGroups: exporting all animations together.", 1); GLTFAnimation gltfAnimation = new GLTFAnimation(); gltfAnimation.name = "All Animations"; int minFrame = Loader.Core.AnimRange.Start / Loader.Global.TicksPerFrame; int maxFrame = Loader.Core.AnimRange.End / Loader.Global.TicksPerFrame; foreach (var pair in nodeToGltfNodeMap) { ExportNodeAnimation(gltfAnimation, minFrame, maxFrame, gltf, pair.Key, pair.Value, babylonScene); } foreach (var pair in boneToGltfNodeMap) { ExportBoneAnimation(gltfAnimation, minFrame, maxFrame, gltf, pair.Key, pair.Value); } if (gltfAnimation.ChannelList.Count > 0) { gltf.AnimationsList.Add(gltfAnimation); } else { RaiseMessage("GLTFExporter.Animation | No animation data for this animation, it is ignored.", 2); } } else { foreach (AnimationGroup animGroup in animationList) { RaiseMessage("GLTFExporter.Animation | " + animGroup.Name, 1); GLTFAnimation gltfAnimation = new GLTFAnimation(); gltfAnimation.name = animGroup.Name; int startFrame = animGroup.FrameStart; int endFrame = animGroup.FrameEnd; foreach (uint nodeHandle in animGroup.NodeHandles) { // todo: make something a little more efficient.. IINode maxNode = Loader.Core.RootNode.FindChildNode(nodeHandle); // node could have been deleted, silently ignore it if (maxNode == null) { continue; } string id = maxNode.GetGuid().ToString(); BabylonNode babylonNode = babylonNodes.Find(node => node.id.Equals(id)); if (babylonNode != null && nodeToGltfNodeMap.TryGetValue(babylonNode, out GLTFNode gltfNode)) { ExportNodeAnimation(gltfAnimation, startFrame, endFrame, gltf, babylonNode, gltfNode, babylonScene); } // export all bones that match this id foreach (KeyValuePair <BabylonBone, GLTFNode> pair in boneToGltfNodeMap) { if (pair.Key.id.Equals(id)) { ExportBoneAnimation(gltfAnimation, startFrame, endFrame, gltf, pair.Key, pair.Value); } } } if (gltfAnimation.ChannelList.Count > 0) { gltf.AnimationsList.Add(gltfAnimation); } else { RaiseMessage("No data exported for this animation, it is ignored.", 2); } } } }
private int AddParameterSamplerAnimation(BabylonCamera babylonCamera, GLTFAnimation gltfAnimation, GLTFExporter exporter, GLTF gltf, int startFrame, int endFrame) { var samplerList = gltfAnimation.SamplerList; // Combine babylon animations from .babylon file and cached ones var babylonAnimations = new List <BabylonAnimation>(); if (babylonCamera.animations != null) { IEnumerable <BabylonAnimation> extendedAnimations = babylonCamera.animations.Where(anim => anim.name == "fov animation"); babylonAnimations.AddRange(extendedAnimations); } if (babylonAnimations.Count > 0) { if (babylonAnimations.Count > 0) { exporter.logger.RaiseMessage("GLTFExporter.Animation | Export animations of node named: " + babylonCamera.name, 2); } foreach (BabylonAnimation babylonAnimation in babylonAnimations) { var babylonAnimationKeysInRange = babylonAnimation.keys.Where(key => key.frame >= startFrame && key.frame <= endFrame); if (babylonAnimationKeysInRange.Count() <= 0) { continue; } string target_path = babylonAnimation.property; // --- Input --- var accessorInput = exporter._createAndPopulateInput(gltf, babylonAnimation, startFrame, endFrame); if (accessorInput == null) { continue; } // --- Output --- GLTFAccessor accessorOutput = FlightSimAsoboPropertyAnimationExtension._createAccessorOfProperty(target_path, gltf); if (accessorOutput == null) { continue; } // Populate accessor int numKeys = 0; foreach (var babylonAnimationKey in babylonAnimationKeysInRange) { numKeys++; // copy data before changing it in case animation groups overlap float[] outputValues = new float[babylonAnimationKey.values.Length]; babylonAnimationKey.values.CopyTo(outputValues, 0); // Store values as bytes foreach (var outputValue in outputValues) { accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue)); } } ; accessorOutput.count = numKeys; if (accessorOutput.count == 0) { exporter.logger.RaiseWarning(String.Format("GLTFExporter.Animation | No frames to export in material animation \"{1}\" of node named \"{0}\". This will cause an error in the output gltf.", babylonCamera.name, babylonAnimation.name)); } // Animation sampler var gltfAnimationSampler = new GLTFAnimationSampler { input = accessorInput.index, output = accessorOutput.index }; gltfAnimationSampler.index = samplerList.Count; samplerList.Add(gltfAnimationSampler); } } else { return(-1); } return(samplerList.Count - 1); }
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); } } }
private static float FPS_FACTOR = 30.0f; // TODO - Which FPS factor ? private GLTFAnimation ExportNodeAnimation(BabylonNode babylonNode, GLTF gltf, GLTFNode gltfNode, BabylonScene babylonScene = null) { GLTFAnimation gltfAnimation = null; if (gltf.AnimationsList.Count > 0) { gltfAnimation = gltf.AnimationsList[0]; } else { gltfAnimation = new GLTFAnimation(); gltf.AnimationsList.Add(gltfAnimation); } var channelList = gltfAnimation.ChannelList; var samplerList = gltfAnimation.SamplerList; if ((babylonNode.animations != null && babylonNode.animations.Length > 0) || (babylonNode.extraAnimations != null && babylonNode.extraAnimations.Count > 0)) { RaiseMessage("GLTFExporter.Animation | Export animation of node named: " + babylonNode.name, 2); // 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); } foreach (BabylonAnimation babylonAnimation in babylonAnimations) { // Target var gltfTarget = new GLTFChannelTarget { node = gltfNode.index }; gltfTarget.path = _getTargetPath(babylonAnimation.property); if (gltfTarget.path == null) { // Unkown babylon animation property //RaiseWarning("GLTFExporter.Animation | Unkown animation property '" + babylonAnimation.property + "'", 3); // Ignore this babylon animation continue; } // --- Input --- var accessorInput = _createAndPopulateInput(gltf, babylonAnimation); // --- Output --- GLTFAccessor accessorOutput = _createAccessorOfPath(gltfTarget.path, gltf); // Populate accessor foreach (var babylonAnimationKey in babylonAnimation.keys) { var outputValues = babylonAnimationKey.values; // Switch coordinate system at object level if (babylonAnimation.property == "position") { outputValues[2] *= -1; } else if (babylonAnimation.property == "rotationQuaternion") { outputValues[0] *= -1; outputValues[1] *= -1; } // Store values as bytes foreach (var outputValue in outputValues) { accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue)); } } ; accessorOutput.count = babylonAnimation.keys.Length; // 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); } } return(gltfAnimation); }
private static float FPS_FACTOR = 60.0f; // TODO - Which FPS factor ? private GLTFAnimation ExportNodeAnimation(BabylonNode babylonNode, GLTF gltf, GLTFNode gltfNode, BabylonScene babylonScene = null) { var channelList = new List <GLTFChannel>(); var samplerList = new List <GLTFAnimationSampler>(); if (babylonNode.animations != null && babylonNode.animations.Length > 0) { RaiseMessage("GLTFExporter.Animation | Export animation of node named: " + babylonNode.name, 2); foreach (BabylonAnimation babylonAnimation in babylonNode.animations) { // Target var gltfTarget = new GLTFChannelTarget { node = gltfNode.index }; gltfTarget.path = _getTargetPath(babylonAnimation.property); if (gltfTarget.path == null) { // Unkown babylon animation property RaiseWarning("GLTFExporter.Animation | Unkown animation property '" + babylonAnimation.property + "'", 3); // Ignore this babylon animation continue; } // Buffer var buffer = GLTFBufferService.Instance.GetBuffer(gltf); // --- Input --- var accessorInput = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewAnimationFloatScalar(gltf, buffer), "accessorAnimationInput", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.SCALAR ); // Populate accessor accessorInput.min = new float[] { float.MaxValue }; accessorInput.max = new float[] { float.MinValue }; foreach (var babylonAnimationKey in babylonAnimation.keys) { var inputValue = babylonAnimationKey.frame / FPS_FACTOR; // Store values as bytes accessorInput.bytesList.AddRange(BitConverter.GetBytes(inputValue)); // Update min and max values GLTFBufferService.UpdateMinMaxAccessor(accessorInput, inputValue); } ; accessorInput.count = babylonAnimation.keys.Length; // --- Output --- GLTFAccessor accessorOutput = null; switch (gltfTarget.path) { case "translation": accessorOutput = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewAnimationFloatVec3(gltf, buffer), "accessorAnimationPositions", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.VEC3 ); break; case "rotation": accessorOutput = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewAnimationFloatVec4(gltf, buffer), "accessorAnimationRotations", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.VEC4 ); break; case "scale": accessorOutput = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewAnimationFloatVec3(gltf, buffer), "accessorAnimationScales", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.VEC3 ); break; } // Populate accessor foreach (var babylonAnimationKey in babylonAnimation.keys) { var outputValues = babylonAnimationKey.values; // Store values as bytes foreach (var outputValue in outputValues) { accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue)); } } ; accessorOutput.count = babylonAnimation.keys.Length; // 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); } } // Do not export empty arrays if (channelList.Count > 0) { // Animation var gltfAnimation = new GLTFAnimation { channels = channelList.ToArray(), samplers = samplerList.ToArray() }; gltf.AnimationsList.Add(gltfAnimation); return(gltfAnimation); } else { return(null); } }
private void ExportAnimationGroups(GLTF gltf, BabylonScene babylonScene) { // Retreive and parse animation group data var animationGroupList = babylonScene.animationGroups; var animationGroupCount = animationGroupList == null ? 0 : animationGroupList.Count; gltf.AnimationsList.Clear(); gltf.AnimationsList.Capacity = Math.Max(gltf.AnimationsList.Capacity, animationGroupCount); if (animationGroupCount <= 0) { logger.RaiseMessage("GLTFExporter.Animation | No AnimationGroups: exporting all animations together.", 1); GLTFAnimation gltfAnimation = new GLTFAnimation(); gltfAnimation.name = "All Animations"; int minFrame = babylonScene.TimelineStartFrame; int maxFrame = babylonScene.TimelineEndFrame; foreach (var pair in nodeToGltfNodeMap) { ExportNodeAnimation(gltfAnimation, minFrame, maxFrame, gltf, pair.Key, pair.Value, babylonScene); } foreach (var pair in boneToGltfNodeMap) { ExportBoneAnimation(gltfAnimation, minFrame, maxFrame, gltf, pair.Key, pair.Value); } if (gltfAnimation.ChannelList.Count > 0) { gltf.AnimationsList.Add(gltfAnimation); } else { logger.RaiseMessage("GLTFExporter.Animation | No animation data for this animation, it is ignored.", 2); } } else { foreach (BabylonAnimationGroup animGroup in animationGroupList) { logger.RaiseMessage("GLTFExporter.Animation | " + animGroup.name, 1); GLTFAnimation gltfAnimation = new GLTFAnimation(); gltfAnimation.name = animGroup.name; int startFrame = MathUtilities.RoundToInt(animGroup.from); int endFrame = MathUtilities.RoundToInt(animGroup.to); var uniqueNodeIds = animGroup.targetedAnimations.Select(targetAnim => targetAnim.targetId).Distinct(); foreach (var id in uniqueNodeIds) { BabylonNode babylonNode = babylonNodes.Find(node => node.id.Equals(id)); GLTFNode gltfNode; if (babylonNode != null && nodeToGltfNodeMap.TryGetValue(babylonNode, out gltfNode)) { ExportNodeAnimation(gltfAnimation, startFrame, endFrame, gltf, babylonNode, gltfNode, babylonScene); } // export all bones that match this id foreach (KeyValuePair <BabylonBone, GLTFNode> pair in boneToGltfNodeMap) { if (pair.Key.id.Equals(id)) { ExportBoneAnimation(gltfAnimation, startFrame, endFrame, gltf, pair.Key, pair.Value); } } } if (gltfAnimation.ChannelList.Count > 0) { gltf.AnimationsList.Add(gltfAnimation); } else { logger.RaiseMessage("No data exported for this animation, it is ignored.", 2); } } } }