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.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 = true; // TODO - Retreive from Maya 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); bool keysInRangeAreRelevant = IsAnimationKeysRelevant(keysInRangeFull, babylonAnimation.property); // if we are baking the animation frames, then do a less efficient check against all frames in the scene for this animation channel if the first check fails. if (!keysInRangeAreRelevant && _bakeAnimationFrames) { List <BabylonAnimationKey> optimizedKeysFull = new List <BabylonAnimationKey>(babylonAnimation.keysFull); OptimizeAnimations(optimizedKeysFull, true); keysInRangeAreRelevant = IsAnimationKeysRelevant(optimizedKeysFull, babylonAnimation.property); } // If we have any significant animation keys in this channel // (or if we are baking the animation track and have any relevant (non-identity) animation keys in the whole track) // then add the optimized keys in range to the animation track if (keysInRangeAreRelevant) { // 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; 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 IList <BabylonAnimationGroup> ExportAnimationGroups(BabylonScene babylonScene) { IList <BabylonAnimationGroup> animationGroups = new List <BabylonAnimationGroup>(); // Retrieve and parse animation group data AnimationGroupList animationList = InitAnimationGroups(); bool exportNonAnimated = Loader.GetBoolProperty("babylonjs_animgroup_exportnonanimated"); foreach (AnimationGroup animGroup in animationList) { RaiseMessage("Exporter.animationGroups | " + animGroup.Name, 1); BabylonAnimationGroup animationGroup = new BabylonAnimationGroup { name = animGroup.Name, from = animGroup.FrameStart, to = animGroup.FrameEnd, targetedAnimations = new List <BabylonTargetedAnimation>() }; // add animations of each nodes in the animGroup List <BabylonNode> nodes = new List <BabylonNode>(); nodes.AddRange(babylonScene.MeshesList); nodes.AddRange(babylonScene.CamerasList); nodes.AddRange(babylonScene.LightsList); foreach (BabylonMorphTargetManager morphTargetManager in babylonScene.MorphTargetManagersList) { var morphTargets = morphTargetManager.targets; foreach (BabylonMorphTarget morphTarget in morphTargets) { var animations = GetSubAnimations(morphTarget, animationGroup.from, animationGroup.to); foreach (BabylonAnimation animation in animations) { BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation { animation = animation, targetId = morphTarget.id }; animationGroup.targetedAnimations.Add(targetedAnimation); } } } foreach (BabylonNode node in nodes) { if (node.animations != null && node.animations.Length != 0) { IList <BabylonAnimation> animations = GetSubAnimations(node, animationGroup.from, animationGroup.to); foreach (BabylonAnimation animation in animations) { BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation { animation = animation, targetId = node.id }; animationGroup.targetedAnimations.Add(targetedAnimation); } } else if (exportNonAnimated) { BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation { animation = CreatePositionAnimation(animationGroup.from, animationGroup.to, node.position), targetId = node.id }; animationGroup.targetedAnimations.Add(targetedAnimation); } } foreach (BabylonSkeleton skel in babylonScene.SkeletonsList) { foreach (BabylonBone bone in skel.bones) { if (bone.animation != null) { IList <BabylonAnimation> animations = GetSubAnimations(bone, animationGroup.from, animationGroup.to); foreach (BabylonAnimation animation in animations) { BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation { animation = animation, targetId = bone.id }; animationGroup.targetedAnimations.Add(targetedAnimation); } } else if (exportNonAnimated) { BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation { animation = CreateMatrixAnimation(animationGroup.from, animationGroup.to, bone.matrix), targetId = bone.id }; animationGroup.targetedAnimations.Add(targetedAnimation); } } } if (animationGroup.targetedAnimations.Count > 0) { animationGroups.Add(animationGroup); } } return(animationGroups); }