123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782 |
- /// <reference path="../../../../dist/preview release/glTF2Interface/babylon.glTF2Interface.d.ts"/>
- module BABYLON.GLTF2 {
- /**
- * @hidden
- * Interface to store animation data.
- */
- export interface _IAnimationData {
- /**
- * Keyframe data.
- */
- inputs: number[],
- /**
- * Value data.
- */
- outputs: number[][],
- /**
- * Animation interpolation data.
- */
- samplerInterpolation: AnimationSamplerInterpolation,
- /**
- * Minimum keyframe value.
- */
- inputsMin: number,
- /**
- * Maximum keyframe value.
- */
- inputsMax: number,
- }
- /**
- * @hidden
- */
- export interface _IAnimationInfo {
- /**
- * The target channel for the animation
- */
- animationChannelTargetPath: AnimationChannelTargetPath,
- /**
- * The glTF accessor type for the data.
- */
- dataAccessorType: AccessorType.VEC3 | AccessorType.VEC4,
- /**
- * Specifies if quaternions should be used.
- */
- useQuaternion: boolean
- }
- /**
- * @hidden
- * Enum for handling in tangent and out tangent.
- */
- enum _TangentType {
- /**
- * Specifies that input tangents are used.
- */
- INTANGENT,
- /**
- * Specifies that output tangents are used.
- */
- OUTTANGENT
- }
- /**
- * @hidden
- * Utility class for generating glTF animation data from BabylonJS.
- */
- export class _GLTFAnimation {
- /**
- * @ignore
- *
- * Creates glTF channel animation from BabylonJS animation.
- * @param babylonTransformNode - BabylonJS mesh.
- * @param animation - animation.
- * @param animationChannelTargetPath - The target animation channel.
- * @param convertToRightHandedSystem - Specifies if the values should be converted to right-handed.
- * @param useQuaternion - Specifies if quaternions are used.
- * @returns nullable IAnimationData
- */
- public static _CreateNodeAnimation(babylonTransformNode: TransformNode, animation: Animation, animationChannelTargetPath: AnimationChannelTargetPath, convertToRightHandedSystem: boolean, useQuaternion: boolean, animationSampleRate: number): Nullable<_IAnimationData> {
- const inputs: number[] = [];
- const outputs: number[][] = [];
- const keyFrames = animation.getKeys();
- const minMaxKeyFrames = _GLTFAnimation.calculateMinMaxKeyFrames(keyFrames);
- const interpolationOrBake = _GLTFAnimation._DeduceInterpolation(keyFrames, animationChannelTargetPath, useQuaternion);
- const frameDelta = minMaxKeyFrames.max - minMaxKeyFrames.min;
- const interpolation = interpolationOrBake.interpolationType;
- const shouldBakeAnimation = interpolationOrBake.shouldBakeAnimation;
- if (shouldBakeAnimation) {
- _GLTFAnimation._CreateBakedAnimation(babylonTransformNode, animation, animationChannelTargetPath, minMaxKeyFrames.min, minMaxKeyFrames.max, animation.framePerSecond, animationSampleRate, inputs, outputs, minMaxKeyFrames, convertToRightHandedSystem, useQuaternion);
- }
- else {
- if (interpolation === AnimationSamplerInterpolation.LINEAR || interpolation === AnimationSamplerInterpolation.STEP) {
- _GLTFAnimation._CreateLinearOrStepAnimation(babylonTransformNode, animation, animationChannelTargetPath, frameDelta, inputs, outputs, convertToRightHandedSystem, useQuaternion);
- }
- else if (interpolation === AnimationSamplerInterpolation.CUBICSPLINE) {
- _GLTFAnimation._CreateCubicSplineAnimation(babylonTransformNode, animation, animationChannelTargetPath, frameDelta, inputs, outputs, convertToRightHandedSystem, useQuaternion);
- }
- else {
- _GLTFAnimation._CreateBakedAnimation(babylonTransformNode, animation, animationChannelTargetPath, minMaxKeyFrames.min, minMaxKeyFrames.max, animation.framePerSecond, animationSampleRate, inputs, outputs, minMaxKeyFrames, convertToRightHandedSystem, useQuaternion);
- }
- }
- if (inputs.length && outputs.length) {
- const result: _IAnimationData = {
- inputs: inputs,
- outputs: outputs,
- samplerInterpolation: interpolation,
- inputsMin: shouldBakeAnimation ? minMaxKeyFrames.min : Tools.FloatRound(minMaxKeyFrames.min / animation.framePerSecond),
- inputsMax: shouldBakeAnimation ? minMaxKeyFrames.max : Tools.FloatRound(minMaxKeyFrames.max / animation.framePerSecond)
- }
- return result;
- }
- return null;
- }
- private static _DeduceAnimationInfo(animation: Animation): Nullable<_IAnimationInfo> {
- let animationChannelTargetPath: Nullable<AnimationChannelTargetPath> = null;
- let dataAccessorType = AccessorType.VEC3;
- let useQuaternion: boolean = false;
- let property = animation.targetProperty.split('.');
- switch (property[0]) {
- case 'scaling': {
- animationChannelTargetPath = AnimationChannelTargetPath.SCALE;
- break;
- }
- case 'position': {
- animationChannelTargetPath = AnimationChannelTargetPath.TRANSLATION;
- break;
- }
- case 'rotation': {
- dataAccessorType = AccessorType.VEC4;
- animationChannelTargetPath = AnimationChannelTargetPath.ROTATION;
- break;
- }
- case 'rotationQuaternion': {
- dataAccessorType = AccessorType.VEC4;
- useQuaternion = true;
- animationChannelTargetPath = AnimationChannelTargetPath.ROTATION;
- break;
- }
- default: {
- Tools.Error(`Unsupported animatable property ${property[0]}`);
- }
- }
- if (animationChannelTargetPath) {
- return { animationChannelTargetPath: animationChannelTargetPath, dataAccessorType: dataAccessorType, useQuaternion: useQuaternion };
- }
- else {
- Tools.Error('animation channel target path and data accessor type could be deduced');
- }
- return null;
- }
- /**
- * @ignore
- * Create node animations from the transform node animations
- * @param babylonTransformNode
- * @param runtimeGLTFAnimation
- * @param idleGLTFAnimations
- * @param nodeMap
- * @param nodes
- * @param binaryWriter
- * @param bufferViews
- * @param accessors
- * @param convertToRightHandedSystem
- */
- public static _CreateNodeAnimationFromTransformNodeAnimations(babylonTransformNode: TransformNode, runtimeGLTFAnimation: IAnimation, idleGLTFAnimations: IAnimation[], nodeMap: { [key: number]: number }, nodes: INode[], binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystem: boolean, animationSampleRate: number) {
- let glTFAnimation: IAnimation;
- if (babylonTransformNode.animations) {
- for (let animation of babylonTransformNode.animations) {
- let animationInfo = _GLTFAnimation._DeduceAnimationInfo(animation);
- if (animationInfo) {
- glTFAnimation = {
- name: animation.name,
- samplers: [],
- channels: []
- }
- _GLTFAnimation.AddAnimation(`${animation.name}`,
- animation.hasRunningRuntimeAnimations ? runtimeGLTFAnimation : glTFAnimation,
- babylonTransformNode,
- animation,
- animationInfo.dataAccessorType,
- animationInfo.animationChannelTargetPath,
- nodeMap,
- binaryWriter,
- bufferViews,
- accessors,
- convertToRightHandedSystem,
- animationInfo.useQuaternion,
- animationSampleRate
- );
- if (glTFAnimation.samplers.length && glTFAnimation.channels.length) {
- idleGLTFAnimations.push(glTFAnimation);
- }
- }
- };
- }
- }
- /**
- * @ignore
- * Create node animations from the animation groups
- * @param babylonScene
- * @param glTFAnimations
- * @param nodeMap
- * @param nodes
- * @param binaryWriter
- * @param bufferViews
- * @param accessors
- * @param convertToRightHandedSystem
- */
- public static _CreateNodeAnimationFromAnimationGroups(babylonScene: Scene, glTFAnimations: IAnimation[], nodeMap: { [key: number]: number }, nodes: INode[], binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystem: boolean, animationSampleRate: number) {
- let glTFAnimation: IAnimation;
- if (babylonScene.animationGroups) {
- let animationGroups = babylonScene.animationGroups;
- for (let animationGroup of animationGroups) {
- glTFAnimation = {
- name: animationGroup.name,
- channels: [],
- samplers: []
- }
- for (let targetAnimation of animationGroup.targetedAnimations) {
- let target = targetAnimation.target;
- let animation = targetAnimation.animation;
- if (target instanceof Mesh || target.length === 1 && target[0] instanceof Mesh) { // TODO: Update to support bones
- let animationInfo = _GLTFAnimation._DeduceAnimationInfo(targetAnimation.animation);
- if (animationInfo) {
- let babylonMesh = target instanceof Mesh ? target : target[0] as Mesh;
- _GLTFAnimation.AddAnimation(`${animation.name}`,
- glTFAnimation,
- babylonMesh,
- animation,
- animationInfo.dataAccessorType,
- animationInfo.animationChannelTargetPath,
- nodeMap,
- binaryWriter,
- bufferViews,
- accessors,
- convertToRightHandedSystem,
- animationInfo.useQuaternion,
- animationSampleRate
- );
- }
- }
- };
- if (glTFAnimation.channels.length && glTFAnimation.samplers.length) {
- glTFAnimations.push(glTFAnimation);
- }
- };
- }
- }
- private static AddAnimation(name: string, glTFAnimation: IAnimation, babylonTransformNode: TransformNode, animation: Animation, dataAccessorType: AccessorType, animationChannelTargetPath: AnimationChannelTargetPath, nodeMap: { [key: number]: number }, binaryWriter: _BinaryWriter, bufferViews: IBufferView[], accessors: IAccessor[], convertToRightHandedSystem: boolean, useQuaternion: boolean, animationSampleRate: number) {
- let animationData = _GLTFAnimation._CreateNodeAnimation(babylonTransformNode, animation, animationChannelTargetPath, convertToRightHandedSystem, useQuaternion, animationSampleRate);
- let bufferView: IBufferView;
- let accessor: IAccessor;
- let keyframeAccessorIndex: number;
- let dataAccessorIndex: number;
- let outputLength: number;
- let animationSampler: IAnimationSampler;
- let animationChannel: IAnimationChannel;
- if (animationData) {
- let nodeIndex = nodeMap[babylonTransformNode.uniqueId];
- // Creates buffer view and accessor for key frames.
- let byteLength = animationData.inputs.length * 4;
- bufferView = _GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, `${name} keyframe data view`);
- bufferViews.push(bufferView);
- animationData.inputs.forEach(function (input) {
- binaryWriter.setFloat32(input);
- });
- accessor = _GLTFUtilities.CreateAccessor(bufferViews.length - 1, `${name} keyframes`, AccessorType.SCALAR, AccessorComponentType.FLOAT, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
- accessors.push(accessor);
- keyframeAccessorIndex = accessors.length - 1;
- // create bufferview and accessor for keyed values.
- outputLength = animationData.outputs.length;
- byteLength = dataAccessorType === AccessorType.VEC3 ? animationData.outputs.length * 12 : animationData.outputs.length * 16;
- // check for in and out tangents
- bufferView = _GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, `${name} data view`);
- bufferViews.push(bufferView);
- animationData.outputs.forEach(function (output) {
- output.forEach(function (entry) {
- binaryWriter.setFloat32(entry);
- });
- });
- accessor = _GLTFUtilities.CreateAccessor(bufferViews.length - 1, `${name} data`, dataAccessorType, AccessorComponentType.FLOAT, outputLength, null, null, null);
- accessors.push(accessor);
- dataAccessorIndex = accessors.length - 1;
- // create sampler
- animationSampler = {
- interpolation: animationData.samplerInterpolation,
- input: keyframeAccessorIndex,
- output: dataAccessorIndex
- }
- glTFAnimation.samplers.push(animationSampler);
- // create channel
- animationChannel = {
- sampler: glTFAnimation.samplers.length - 1,
- target: {
- node: nodeIndex,
- path: animationChannelTargetPath
- }
- }
- glTFAnimation.channels.push(animationChannel);
- }
- }
- /**
- * Create a baked animation
- * @param babylonTransformNode BabylonJS mesh
- * @param animation BabylonJS animation corresponding to the BabylonJS mesh
- * @param animationChannelTargetPath animation target channel
- * @param minFrame minimum animation frame
- * @param maxFrame maximum animation frame
- * @param fps frames per second of the animation
- * @param inputs input key frames of the animation
- * @param outputs output key frame data of the animation
- * @param convertToRightHandedSystem converts the values to right-handed
- * @param useQuaternion specifies if quaternions should be used
- */
- private static _CreateBakedAnimation(babylonTransformNode: TransformNode, animation: Animation, animationChannelTargetPath: AnimationChannelTargetPath, minFrame: number, maxFrame: number, fps: number, sampleRate: number, inputs: number[], outputs: number[][], minMaxFrames: { min: number, max: number }, convertToRightHandedSystem: boolean, useQuaternion: boolean) {
- let value: number | Vector3 | Quaternion;
- let quaternionCache: Quaternion = Quaternion.Identity();
- let previousTime: Nullable<number> = null;
- let time: number;
- let maxUsedFrame: Nullable<number> = null;
- let currKeyFrame: Nullable<IAnimationKey> = null;
- let nextKeyFrame: Nullable<IAnimationKey> = null;
- let prevKeyFrame: Nullable<IAnimationKey> = null;
- let endFrame: Nullable<number> = null;
- minMaxFrames.min = Tools.FloatRound(minFrame / fps);
- let keyFrames = animation.getKeys();
- for (let i = 0, length = keyFrames.length; i < length; ++i) {
- endFrame = null;
- currKeyFrame = keyFrames[i];
- if (i + 1 < length) {
- nextKeyFrame = keyFrames[i + 1];
- if (currKeyFrame.value.equals(nextKeyFrame.value)) {
- if (i === 0) { // set the first frame to itself
- endFrame = currKeyFrame.frame;
- }
- else {
- continue;
- }
- }
- else {
- endFrame = nextKeyFrame.frame;
- }
- }
- else {
- // at the last key frame
- prevKeyFrame = keyFrames[i - 1];
- if (currKeyFrame.value.equals(prevKeyFrame.value)) {
- continue;
- }
- else {
- endFrame = maxFrame;
- }
- }
- if (endFrame) {
- for (let f = currKeyFrame.frame; f <= endFrame; f += sampleRate) {
- time = Tools.FloatRound(f / fps);
- if (time === previousTime) {
- continue;
- }
- previousTime = time;
- maxUsedFrame = time;
- value = animation._interpolate(f, 0, undefined, animation.loopMode);
- _GLTFAnimation._SetInterpolatedValue(babylonTransformNode, value, time, animation, animationChannelTargetPath, quaternionCache, inputs, outputs, convertToRightHandedSystem, useQuaternion);
- }
- }
- }
- if (maxUsedFrame) {
- minMaxFrames.max = maxUsedFrame;
- }
- }
- private static _ConvertFactorToVector3OrQuaternion(factor: number, babylonTransformNode: TransformNode, animation: Animation, animationType: number, animationChannelTargetPath: AnimationChannelTargetPath, convertToRightHandedSystem: boolean, useQuaternion: boolean): Nullable<Vector3 | Quaternion> {
- let property: string[];
- let componentName: string;
- let value: Nullable<Quaternion | Vector3> = null;
- const basePositionRotationOrScale = _GLTFAnimation._GetBasePositionRotationOrScale(babylonTransformNode, animationChannelTargetPath, convertToRightHandedSystem, useQuaternion);
- if (animationType === Animation.ANIMATIONTYPE_FLOAT) { // handles single component x, y, z or w component animation by using a base property and animating over a component.
- property = animation.targetProperty.split('.');
- componentName = property ? property[1] : ''; // x, y, or z component
- value = useQuaternion ? BABYLON.Quaternion.FromArray(basePositionRotationOrScale).normalize() : BABYLON.Vector3.FromArray(basePositionRotationOrScale);
- switch (componentName) {
- case 'x': {
- value[componentName] = (convertToRightHandedSystem && useQuaternion && (animationChannelTargetPath !== AnimationChannelTargetPath.SCALE)) ? -factor : factor;
- break;
- }
- case 'y': {
- value[componentName] = (convertToRightHandedSystem && useQuaternion && (animationChannelTargetPath !== AnimationChannelTargetPath.SCALE)) ? -factor : factor;
- break;
- }
- case 'z': {
- value[componentName] = (convertToRightHandedSystem && !useQuaternion && (animationChannelTargetPath !== AnimationChannelTargetPath.SCALE)) ? -factor : factor;
- break;
- }
- case 'w': {
- (value as Quaternion).w = factor;
- break;
- }
- default: {
- Tools.Error(`glTFAnimation: Unsupported component type "${componentName}" for scale animation!`);
- }
- }
- }
- return value;
- }
- private static _SetInterpolatedValue(babylonTransformNode: TransformNode, value: Nullable<number | Vector3 | Quaternion>, time: number, animation: Animation, animationChannelTargetPath: AnimationChannelTargetPath, quaternionCache: Quaternion, inputs: number[], outputs: number[][], convertToRightHandedSystem: boolean, useQuaternion: boolean) {
- const animationType = animation.dataType;
- let cacheValue: Vector3 | Quaternion;
- inputs.push(time);
- if (typeof value === "number") {
- value = this._ConvertFactorToVector3OrQuaternion(value as number, babylonTransformNode, animation, animationType, animationChannelTargetPath, convertToRightHandedSystem, useQuaternion);
- }
- if (value) {
- if (animationChannelTargetPath === AnimationChannelTargetPath.ROTATION) {
- if (useQuaternion) {
- quaternionCache = value as Quaternion;
- }
- else {
- cacheValue = value as Vector3;
- Quaternion.RotationYawPitchRollToRef(cacheValue.y, cacheValue.x, cacheValue.z, quaternionCache);
- }
- if (convertToRightHandedSystem) {
- _GLTFUtilities.GetRightHandedQuaternionFromRef(quaternionCache);
- if (!babylonTransformNode.parent) {
- quaternionCache = Quaternion.FromArray([0, 1, 0, 0]).multiply(quaternionCache);
- }
- }
- outputs.push(quaternionCache.asArray());
- }
- else {
- cacheValue = value as Vector3;
- if (convertToRightHandedSystem && (animationChannelTargetPath !== AnimationChannelTargetPath.SCALE)) {
- _GLTFUtilities.GetRightHandedPositionVector3FromRef(cacheValue);
- if (!babylonTransformNode.parent) {
- cacheValue.x *= -1;
- cacheValue.z *= -1;
- }
- }
- outputs.push(cacheValue.asArray());
- }
- }
- }
- /**
- * Creates linear animation from the animation key frames
- * @param babylonTransformNode BabylonJS mesh
- * @param animation BabylonJS animation
- * @param animationChannelTargetPath The target animation channel
- * @param frameDelta The difference between the last and first frame of the animation
- * @param inputs Array to store the key frame times
- * @param outputs Array to store the key frame data
- * @param convertToRightHandedSystem Specifies if the position data should be converted to right handed
- * @param useQuaternion Specifies if quaternions are used in the animation
- */
- private static _CreateLinearOrStepAnimation(babylonTransformNode: TransformNode, animation: Animation, animationChannelTargetPath: AnimationChannelTargetPath, frameDelta: number, inputs: number[], outputs: number[][], convertToRightHandedSystem: boolean, useQuaternion: boolean) {
- for (let keyFrame of animation.getKeys()) {
- inputs.push(keyFrame.frame / animation.framePerSecond); // keyframes in seconds.
- _GLTFAnimation._AddKeyframeValue(keyFrame, animation, outputs, animationChannelTargetPath, babylonTransformNode, convertToRightHandedSystem, useQuaternion);
- };
- }
- /**
- * Creates cubic spline animation from the animation key frames
- * @param babylonTransformNode BabylonJS mesh
- * @param animation BabylonJS animation
- * @param animationChannelTargetPath The target animation channel
- * @param frameDelta The difference between the last and first frame of the animation
- * @param inputs Array to store the key frame times
- * @param outputs Array to store the key frame data
- * @param convertToRightHandedSystem Specifies if the position data should be converted to right handed
- * @param useQuaternion Specifies if quaternions are used in the animation
- */
- private static _CreateCubicSplineAnimation(babylonTransformNode: TransformNode, animation: Animation, animationChannelTargetPath: AnimationChannelTargetPath, frameDelta: number, inputs: number[], outputs: number[][], convertToRightHandedSystem: boolean, useQuaternion: boolean) {
- animation.getKeys().forEach(function (keyFrame) {
- inputs.push(keyFrame.frame / animation.framePerSecond); // keyframes in seconds.
- _GLTFAnimation.AddSplineTangent(
- babylonTransformNode,
- _TangentType.INTANGENT,
- outputs,
- animationChannelTargetPath,
- AnimationSamplerInterpolation.CUBICSPLINE,
- keyFrame,
- frameDelta,
- useQuaternion,
- convertToRightHandedSystem
- );
- _GLTFAnimation._AddKeyframeValue(keyFrame, animation, outputs, animationChannelTargetPath, babylonTransformNode, convertToRightHandedSystem, useQuaternion);
- _GLTFAnimation.AddSplineTangent(
- babylonTransformNode,
- _TangentType.OUTTANGENT,
- outputs,
- animationChannelTargetPath,
- AnimationSamplerInterpolation.CUBICSPLINE,
- keyFrame,
- frameDelta,
- useQuaternion,
- convertToRightHandedSystem
- );
- });
- }
- private static _GetBasePositionRotationOrScale(babylonTransformNode: TransformNode, animationChannelTargetPath: AnimationChannelTargetPath, convertToRightHandedSystem: boolean, useQuaternion: boolean) {
- let basePositionRotationOrScale: number[];
- if (animationChannelTargetPath === AnimationChannelTargetPath.ROTATION) {
- if (useQuaternion) {
- if (babylonTransformNode.rotationQuaternion) {
- basePositionRotationOrScale = babylonTransformNode.rotationQuaternion.asArray();
- if (convertToRightHandedSystem) {
- _GLTFUtilities.GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
- if (!babylonTransformNode.parent) {
- basePositionRotationOrScale = Quaternion.FromArray([0, 1, 0, 0]).multiply(Quaternion.FromArray(basePositionRotationOrScale)).asArray();
- }
- }
- }
- else {
- basePositionRotationOrScale = BABYLON.Quaternion.Identity().asArray();
- }
- }
- else {
- basePositionRotationOrScale = babylonTransformNode.rotation.asArray();
- _GLTFUtilities.GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
- }
- }
- else if (animationChannelTargetPath === AnimationChannelTargetPath.TRANSLATION) {
- basePositionRotationOrScale = babylonTransformNode.position.asArray();
- if (convertToRightHandedSystem) {
- _GLTFUtilities.GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
- }
- }
- else { // scale
- basePositionRotationOrScale = babylonTransformNode.scaling.asArray();
- }
- return basePositionRotationOrScale;
- }
- /**
- * Adds a key frame value
- * @param keyFrame
- * @param animation
- * @param outputs
- * @param animationChannelTargetPath
- * @param basePositionRotationOrScale
- * @param convertToRightHandedSystem
- * @param useQuaternion
- */
- private static _AddKeyframeValue(keyFrame: IAnimationKey, animation: Animation, outputs: number[][], animationChannelTargetPath: AnimationChannelTargetPath, babylonTransformNode: TransformNode, convertToRightHandedSystem: boolean, useQuaternion: boolean) {
- let value: number[];
- let newPositionRotationOrScale: Nullable<Vector3 | Quaternion>;
- const animationType = animation.dataType;
- if (animationType === Animation.ANIMATIONTYPE_VECTOR3) {
- value = keyFrame.value.asArray();
- if (animationChannelTargetPath === AnimationChannelTargetPath.ROTATION) {
- const array = Vector3.FromArray(value);
- let rotationQuaternion = Quaternion.RotationYawPitchRoll(array.y, array.x, array.z);
- if (convertToRightHandedSystem) {
- _GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
- if (!babylonTransformNode.parent) {
- rotationQuaternion = Quaternion.FromArray([0, 1, 0, 0]).multiply(rotationQuaternion);
- }
- }
- value = rotationQuaternion.asArray();
- }
- else if (animationChannelTargetPath === AnimationChannelTargetPath.TRANSLATION) {
- if (convertToRightHandedSystem) {
- _GLTFUtilities.GetRightHandedNormalArray3FromRef(value);
- if (!babylonTransformNode.parent) {
- value[0] *= -1;
- value[2] *= -1;
- }
- }
- }
- outputs.push(value); // scale vector.
- }
- else if (animationType === Animation.ANIMATIONTYPE_FLOAT) { // handles single component x, y, z or w component animation by using a base property and animating over a component.
- newPositionRotationOrScale = this._ConvertFactorToVector3OrQuaternion(keyFrame.value as number, babylonTransformNode, animation, animationType, animationChannelTargetPath, convertToRightHandedSystem, useQuaternion);
- if (newPositionRotationOrScale) {
- if (animationChannelTargetPath === AnimationChannelTargetPath.ROTATION) {
- let posRotScale = useQuaternion ? newPositionRotationOrScale as Quaternion : Quaternion.RotationYawPitchRoll(newPositionRotationOrScale.y, newPositionRotationOrScale.x, newPositionRotationOrScale.z).normalize();
- if (convertToRightHandedSystem) {
- _GLTFUtilities.GetRightHandedQuaternionFromRef(posRotScale);
- if (!babylonTransformNode.parent) {
- posRotScale = Quaternion.FromArray([0, 1, 0, 0]).multiply(posRotScale);
- }
- }
- outputs.push(posRotScale.asArray());
- }
- else if (animationChannelTargetPath === AnimationChannelTargetPath.TRANSLATION) {
- if (convertToRightHandedSystem) {
- _GLTFUtilities.GetRightHandedNormalVector3FromRef(newPositionRotationOrScale as Vector3);
- if (!babylonTransformNode.parent) {
- newPositionRotationOrScale.x *= -1;
- newPositionRotationOrScale.z *= -1;
- }
- }
- }
- outputs.push(newPositionRotationOrScale.asArray());
- }
- }
- else if (animationType === Animation.ANIMATIONTYPE_QUATERNION) {
- value = (keyFrame.value as Quaternion).normalize().asArray();
- if (convertToRightHandedSystem) {
- _GLTFUtilities.GetRightHandedQuaternionArrayFromRef(value);
- if (!babylonTransformNode.parent) {
- value = Quaternion.FromArray([0, 1, 0, 0]).multiply(Quaternion.FromArray(value)).asArray();
- }
- }
- outputs.push(value);
- }
- else {
- Tools.Error('glTFAnimation: Unsupported key frame values for animation!');
- }
- }
- /**
- * Determine the interpolation based on the key frames
- * @param keyFrames
- * @param animationChannelTargetPath
- * @param useQuaternion
- */
- private static _DeduceInterpolation(keyFrames: IAnimationKey[], animationChannelTargetPath: AnimationChannelTargetPath, useQuaternion: boolean): { interpolationType: AnimationSamplerInterpolation, shouldBakeAnimation: boolean } {
- let interpolationType: AnimationSamplerInterpolation | undefined;
- let shouldBakeAnimation = false;
- let key: IAnimationKey;
- if (animationChannelTargetPath === AnimationChannelTargetPath.ROTATION && !useQuaternion) {
- return { interpolationType: AnimationSamplerInterpolation.LINEAR, shouldBakeAnimation: true };
- }
- for (let i = 0, length = keyFrames.length; i < length; ++i) {
- key = keyFrames[i];
- if (key.inTangent || key.outTangent) {
- if (interpolationType) {
- if (interpolationType !== AnimationSamplerInterpolation.CUBICSPLINE) {
- interpolationType = AnimationSamplerInterpolation.LINEAR;
- shouldBakeAnimation = true;
- break;
- }
- }
- else {
- interpolationType = AnimationSamplerInterpolation.CUBICSPLINE;
- }
- }
- else {
- if (interpolationType) {
- if (interpolationType === AnimationSamplerInterpolation.CUBICSPLINE ||
- (key.interpolation && (key.interpolation === AnimationKeyInterpolation.STEP) && interpolationType !== AnimationSamplerInterpolation.STEP)) {
- interpolationType = AnimationSamplerInterpolation.LINEAR;
- shouldBakeAnimation = true;
- break;
- }
- }
- else {
- if (key.interpolation && (key.interpolation === AnimationKeyInterpolation.STEP)) {
- interpolationType = AnimationSamplerInterpolation.STEP;
- }
- else {
- interpolationType = AnimationSamplerInterpolation.LINEAR;
- }
- }
- }
- }
- if (!interpolationType) {
- interpolationType = AnimationSamplerInterpolation.LINEAR;
- }
- return { interpolationType: interpolationType, shouldBakeAnimation: shouldBakeAnimation };
- }
- /**
- * Adds an input tangent or output tangent to the output data
- * If an input tangent or output tangent is missing, it uses the zero vector or zero quaternion
- * @param tangentType Specifies which type of tangent to handle (inTangent or outTangent)
- * @param outputs The animation data by keyframe
- * @param animationChannelTargetPath The target animation channel
- * @param interpolation The interpolation type
- * @param keyFrame The key frame with the animation data
- * @param frameDelta Time difference between two frames used to scale the tangent by the frame delta
- * @param useQuaternion Specifies if quaternions are used
- * @param convertToRightHandedSystem Specifies if the values should be converted to right-handed
- */
- private static AddSplineTangent(babylonTransformNode: TransformNode, tangentType: _TangentType, outputs: number[][], animationChannelTargetPath: AnimationChannelTargetPath, interpolation: AnimationSamplerInterpolation, keyFrame: IAnimationKey, frameDelta: number, useQuaternion: boolean, convertToRightHandedSystem: boolean) {
- let tangent: number[];
- let tangentValue: Vector3 | Quaternion = tangentType === _TangentType.INTANGENT ? keyFrame.inTangent : keyFrame.outTangent;
- if (interpolation === AnimationSamplerInterpolation.CUBICSPLINE) {
- if (animationChannelTargetPath === AnimationChannelTargetPath.ROTATION) {
- if (tangentValue) {
- if (useQuaternion) {
- tangent = (tangentValue as Quaternion).scale(frameDelta).asArray();
- }
- else {
- const array = (tangentValue as Vector3).scale(frameDelta);
- tangent = Quaternion.RotationYawPitchRoll(array.y, array.x, array.z).asArray();
- }
- if (convertToRightHandedSystem) {
- _GLTFUtilities.GetRightHandedQuaternionArrayFromRef(tangent);
- if (!babylonTransformNode.parent) {
- tangent = Quaternion.FromArray([0, 1, 0, 0]).multiply(Quaternion.FromArray(tangent)).asArray();
- }
- }
- }
- else {
- tangent = [0, 0, 0, 0];
- }
- }
- else {
- if (tangentValue) {
- tangent = (tangentValue as Vector3).scale(frameDelta).asArray();
- if (convertToRightHandedSystem) {
- if (animationChannelTargetPath === AnimationChannelTargetPath.TRANSLATION) {
- _GLTFUtilities.GetRightHandedPositionArray3FromRef(tangent);
- if (!babylonTransformNode.parent) {
- tangent[0] *= -1; // x
- tangent[2] *= -1; // z
- }
- }
- }
- }
- else {
- tangent = [0, 0, 0];
- }
- }
- outputs.push(tangent);
- }
- }
- /**
- * Get the minimum and maximum key frames' frame values
- * @param keyFrames animation key frames
- * @returns the minimum and maximum key frame value
- */
- private static calculateMinMaxKeyFrames(keyFrames: IAnimationKey[]): { min: number, max: number } {
- let min: number = Infinity;
- let max: number = -Infinity;
- keyFrames.forEach(function (keyFrame) {
- min = Math.min(min, keyFrame.frame);
- max = Math.max(max, keyFrame.frame);
- });
- return { min: min, max: max };
- }
- }
- }
|