From 1f4afb1cf30fdde865230a1fe6480b331198cc3f Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Mon, 20 Jun 2022 18:16:30 +0200 Subject: [PATCH 01/16] initial support for KHR_animation_pointer --- .../Extensions/KHR_animation_pointer.map.ts | 254 +++++++++++++++ .../2.0/Extensions/KHR_animation_pointer.ts | 293 ++++++++++++++++++ .../loaders/src/glTF/2.0/Extensions/index.ts | 2 + .../dev/loaders/src/glTF/2.0/glTFLoader.ts | 2 +- .../babylon.glTF2Interface.d.ts | 10 +- 5 files changed, 558 insertions(+), 3 deletions(-) create mode 100644 packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts create mode 100644 packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts new file mode 100644 index 00000000000..5f9d8685e55 --- /dev/null +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts @@ -0,0 +1,254 @@ +import { Animation } from "core/Animations/animation"; +import type { AnimationGroup } from "core/Animations/animationGroup"; +import { Quaternion, Vector3 } from "core/Maths/math.vector"; +import { Color3 } from "core/Maths/math.color"; +import type { IGLTF, INode } from "../glTFLoaderInterfaces"; +import { Material } from "core/Materials/material"; +import type { IAnimatable } from "core/Animations/animatable.interface"; +import type { AbstractMesh } from "core/Meshes/abstractMesh"; +import { Mesh } from "core/Meshes/mesh"; + +type GetTargetFn = (source: IGLTF, index: string) => any | null; +export type GetValueFn = (target: any, source: Float32Array, offset: number, scale?: number) => any; +export type GetStrideFn = (target: any) => number; + +export interface IAnimationPointerPropertyInfos { + type: number; + name: string; + get: GetValueFn; + isValid(target: any):boolean; + buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup): void; +} + +const parseIntIndex = (str: string) => { + const targetIndex = parseInt(str); + if (isNaN(targetIndex)) { + return -1; + } + return targetIndex; +}; + +const _getNode: GetTargetFn = (gltf: IGLTF, index: string) => { + if (gltf.nodes) { + const i = parseIntIndex(index); + if (i != -1) { + return gltf.nodes[i]; + } + } + return null; +}; + +const _getMaterial: GetTargetFn = (gltf: IGLTF, index: string) => { + if (gltf.materials) { + const i = parseIntIndex(index); + if (i != -1) { + return gltf.materials[i]; + } + } + return null; +}; + +const _getVector3: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { + return scale ? Vector3.FromArray(source, offset).scaleInPlace(scale) : Vector3.FromArray(source, offset); +}; + +const _getQuaternion: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { + return scale ? Quaternion.FromArray(source, offset).scaleInPlace(scale) : Quaternion.FromArray(source, offset); +}; + +const _getColor3: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { + return scale ? Color3.FromArray(source, offset).scale(scale) : Color3.FromArray(source, offset); +}; + +const _getAlpha: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { + return scale ? source[offset + 3] * scale : source[offset + 3]; +}; + +const _getFloat: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { + return scale ? source[offset] * scale : source[offset]; +}; + +const _getWeights: GetValueFn = (target: any, source: Float32Array, offset: number, scale?: number) => { + if( target._numMorphTargets){ + const value = new Array(target._numMorphTargets!); + for (let i = 0; i < target._numMorphTargets!; i++) { + value[i] = scale ? source[offset++] * scale : source[offset++]; + } + return value; + } + return null; +}; + +abstract class AbstractAnimationPointerPropertyInfos implements IAnimationPointerPropertyInfos { + public constructor(public type: number, public name: string, public get: GetValueFn) {} + + protected _buildAnimation(animatable: IAnimatable, fps: number, keys: any[], babylonAnimationGroup: AnimationGroup) { + if (animatable) { + const animationName = `${babylonAnimationGroup!.name}_channel${babylonAnimationGroup.targetedAnimations.length}_${this.name}`; + const babylonAnimation = new Animation(animationName, this.name, fps, this.type); + babylonAnimation.setKeys(keys); + + animatable.animations = animatable.animations || Array(1); + animatable.animations.push(babylonAnimation); + babylonAnimationGroup.addTargetedAnimation(babylonAnimation, animatable); + } + } + public isValid(_target: any):boolean{ return true; } + + public abstract buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup): void; +} + +class TransformNodeAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { + public constructor(type: number, name: string, get: GetValueFn) { + super(type, name, get); + } + public isValid(target: any):boolean{ return target._babylonTransformNode !== null && target._babylonTransformNode !== undefined; } + + public buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup): void { + return this._buildAnimation(target._babylonTransformNode, fps, keys, group); + } +} + +class MaterialAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { + public constructor(type: number, name: string, get: GetValueFn, public fillMode: any = Material.TriangleFillMode) { + super(type, name, get); + } + + public isValid(target: any) : boolean { + const data = target._data; + if(data) { + const c = data[this.fillMode] ; + if(c){ + return c.babylonMaterial !== null && c.babylonMaterial !== undefined ; + } + } + return false; + } + + public buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup): void { + return this._buildAnimation(target._data[this.fillMode].babylonMaterial, fps, keys, group); + } +} + +class WeightAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { + + public constructor(type: number, name: string, get: GetValueFn) { + super(type, name, get); + } + + public buildAnimations(targetNode: any, fps: number, keys: any[], babylonAnimationGroup: AnimationGroup): void { + + if(targetNode._numMorphTargets){ + for (let targetIndex = 0; targetIndex < targetNode._numMorphTargets; targetIndex++) { + const animationName = `${babylonAnimationGroup.name}_channel${babylonAnimationGroup.targetedAnimations.length}`; + const babylonAnimation = new Animation(animationName, this.name, fps, this.type); + babylonAnimation.setKeys( + keys.map((key) => ({ + frame: key.frame, + inTangent: key.inTangent ? key.inTangent[targetIndex] : undefined, + value: key.value[targetIndex], + outTangent: key.outTangent ? key.outTangent[targetIndex] : undefined, + interpolation: key.interpolation, + })) + ); + + this._forEachPrimitive(targetNode, (babylonAbstractMesh: AbstractMesh) => { + const babylonMesh = babylonAbstractMesh as Mesh; + if (babylonMesh.morphTargetManager) { + const morphTarget = babylonMesh.morphTargetManager.getTarget(targetIndex); + const babylonAnimationClone = babylonAnimation.clone(); + morphTarget.animations.push(babylonAnimationClone); + babylonAnimationGroup.addTargetedAnimation(babylonAnimationClone, morphTarget); + } + }); + } + } + } + + private _forEachPrimitive(node: INode, callback: (babylonMesh: AbstractMesh) => void): void { + if (node._primitiveBabylonMeshes) { + for (const babylonMesh of node._primitiveBabylonMeshes) { + callback(babylonMesh); + } + } + } +} + +const CoreAnimationNodesPointerMap: any = { + hasIndex: true, + translation: { + getTarget: _getNode, + getStride: (_target: any) => { + return 3; + }, + properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_VECTOR3, "position", _getVector3)], + }, + rotation: { + getTarget: _getNode, + getStride: (_target: any) => { + return 4; + }, + properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_QUATERNION, "rotationQuaternion", _getQuaternion)], + }, + scale: { + getTarget: _getNode, + getStride: (_target: any) => { + return 3; + }, + properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_VECTOR3, "scaling", _getVector3)], + }, + weight: { + getTarget: _getNode, + getStride: (target: any) => { + return target._numMorphTargets; + }, + properties: [new WeightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "influence", _getWeights)], + }, +}; + +const CoreAnimationMaterialsPointerMap: any = { + hasIndex: true, + pbrMetallicRoughness: { + baseColorFactor: { + getTarget: _getMaterial, + getStride: (_target: any) => { + return 4; + }, + properties: [ + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "albedoColor", _getColor3), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "alpha", _getAlpha), + ], + }, + metallicFactor: { + getTarget: _getMaterial, + getStride: (_target: any) => { + return 1; + }, + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "metallic", _getFloat)], + }, + roughnessFactor: { + getTarget: _getMaterial, + getStride: (_target: any) => { + return 1; + }, + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "roughness", _getFloat)], + }, + }, + emissiveFactor: { + getTarget: _getMaterial, + getStride: (_target: any) => { + return 1; + }, + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissive", _getFloat)], + }, +}; + +const CoreAnimationCamerasPointerMap: any = { + hasIndex: true, +}; + +export const CoreAnimationPointerMap: any = { + nodes: CoreAnimationNodesPointerMap, + materials: CoreAnimationMaterialsPointerMap, + cameras: CoreAnimationCamerasPointerMap, +}; diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts new file mode 100644 index 00000000000..feca347d74b --- /dev/null +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts @@ -0,0 +1,293 @@ +import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; +import { ArrayItem, GLTFLoader } from "../glTFLoader"; +import type { Nullable } from "core/types"; +import { AnimationGroup } from "core/Animations/animationGroup"; +import { Animation } from "core/Animations/animation"; +import type { IAnimation, IAnimationChannel } from "../glTFLoaderInterfaces"; + +import { AnimationChannelTargetPath, AnimationSamplerInterpolation } from "babylonjs-gltf2interface"; +import { AnimationKeyInterpolation } from "core/Animations/animationKey"; +import { CoreAnimationPointerMap, IAnimationPointerPropertyInfos } from "./KHR_animation_pointer.map"; + +const NAME = "KHR_animation_pointer"; + +interface IAnimationChannelTarget { + stride: number; + target: any; + properties: Array; +} + +/** + * [Specification PR](https://github.com/KhronosGroup/glTF/pull/2147) + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export class KHR_animation_pointer implements IGLTFLoaderExtension { + /** + * used to gently ignore invalid pointer. If false, invalid pointer will throw exception. + */ + public static IgnoreInvalidPointer: boolean = false; + + /** + * Used internally to determine how much data to be gather from input buffer. + * @param infos the informations + * @returns + */ + static GetAnimationOutputStride(infos: Array): number { + let stride = 0; + for (const info of infos) { + switch (info.type) { + case Animation.ANIMATIONTYPE_FLOAT: { + stride++; + break; + } + case Animation.ANIMATIONTYPE_VECTOR2: + case Animation.ANIMATIONTYPE_SIZE: { + stride += 2; + break; + } + + case Animation.ANIMATIONTYPE_VECTOR3: + case Animation.ANIMATIONTYPE_COLOR3: { + stride += 3; + break; + } + + case Animation.ANIMATIONTYPE_COLOR4: + case Animation.ANIMATIONTYPE_QUATERNION: { + stride += 4; + break; + } + + case Animation.ANIMATIONTYPE_MATRIX: { + stride += 16; + break; + } + } + } + return stride; + } + + /** + * The name of this extension. + */ + public readonly name = NAME; + /** + * Defines whether this extension is enabled. + */ + public enabled: boolean; + + private _loader: GLTFLoader; + + /** + * @param loader + * @hidden + */ + constructor(loader: GLTFLoader) { + this._loader = loader; + this.enabled = this._loader.isExtensionUsed(NAME); + } + + /** @hidden */ + public dispose() { + (this._loader as any) = null; + } + + /** + * according to specification, + * It is not allowed to animate a glTFid property, as it does change the structure of the glTF in general + * It is not allowed to animate a name property in general. + * @param property + * @hidden + */ + public accept(property: string): boolean { + return property != "name"; + } + + public loadAnimationAsync?(context: string, animation: IAnimation): Nullable> { + this._loader.babylonScene._blockEntityCollection = !!this._loader._assetContainer; + const babylonAnimationGroup = new AnimationGroup(animation.name || `animation${animation.index}`, this._loader.babylonScene); + babylonAnimationGroup._parentContainer = this._loader._assetContainer; + this._loader.babylonScene._blockEntityCollection = false; + animation._babylonAnimationGroup = babylonAnimationGroup; + + const promises = new Array>(); + ArrayItem.Assign(animation.channels); + ArrayItem.Assign(animation.samplers); + + for (const channel of animation.channels) { + promises.push(this._loadAnimationChannelAsync(`${context}/channels/${channel.index}`, context, animation, channel)); + } + + return Promise.all(promises).then(() => { + babylonAnimationGroup.normalize(0); + return babylonAnimationGroup; + }); + } + + /** + * @hidden Loads a glTF animation channel. + * @param context The context when loading the asset + * @param animationContext The context of the animation when loading the asset + * @param animation The glTF animation property + * @param channel The glTF animation channel property + * @returns A void promise when the channel load is complete + */ + public _loadAnimationChannelAsync(context: string, animationContext: string, animation: IAnimation, channel: IAnimationChannel): Promise { + if (channel.target.path != AnimationChannelTargetPath.POINTER) { + throw new Error(`${context}/target/path: Invalid value (${channel.target.path})`); + } + + if (channel.target.node != undefined) { + // According to KHR_animation_pointer specification + // If this extension is used, the animation.channel.target.node must not be set. + // Because the node isn’t defined, the channel is ignored and not animated due to the specification. + return Promise.resolve(); + } + + const pointer = channel.target.extensions?.KHR_animation_pointer?.pointer; + if (!pointer) { + throw new Error(`${context}/target/extensions.KHR_animation_pointer.pointer MUST be set.`); + } + + const sampler = ArrayItem.Get(`${context}/sampler`, animation.samplers, channel.sampler); + + return this._loader._loadAnimationSamplerAsync(`${context}/samplers/${channel.sampler}`, sampler).then((data) => { + // this is where we process the pointer. + const animationTarget = this._parseAnimationPointer(`${context}/extensions/${this.name}/pointer`, pointer); + + if (animationTarget) { + // build the keys + // build the animations into the group + const babylonAnimationGroup = animation._babylonAnimationGroup; + if (babylonAnimationGroup) { + // stride is the size of each element stored into the output buffer. + // stride is the sum of property size or as per defined into the info. + const stride = animationTarget.stride ?? KHR_animation_pointer.GetAnimationOutputStride(animationTarget.properties); + const fps = this._loader.parent.targetFps; + + // we extract the corresponding values from the readed value. + // the reason for that is one GLTF value may be dispatched to several Babylon properties + // one of example is baseColorFactor which is a Color4 under GLTF and dispatched to + // - albedoColor as Color3(Color4.r,Color4.g,Color4.b) + // - alpha as Color4.a + for (const propertyInfo of animationTarget.properties) { + // Ignore animations that have no animation valid targets. + if (!propertyInfo.isValid(animationTarget.target)) { + return Promise.resolve(); + } + + // build the keys. + const keys = new Array(data.input.length); + let outputOffset = 0; + + switch (data.interpolation) { + case AnimationSamplerInterpolation.STEP: { + for (let frameIndex = 0; frameIndex < data.input.length; frameIndex++) { + keys[frameIndex] = { + frame: data.input[frameIndex] * fps, + value: propertyInfo.get(animationTarget.target, data.output, outputOffset), + interpolation: AnimationKeyInterpolation.STEP, + }; + outputOffset += stride; + } + break; + } + case AnimationSamplerInterpolation.CUBICSPLINE: { + const invfps = 1 / fps; + for (let frameIndex = 0; frameIndex < data.input.length; frameIndex++) { + const k: any = { + frame: data.input[frameIndex] * fps, + }; + + (k.inTangent = propertyInfo.get(animationTarget.target, data.output, outputOffset, invfps)), (outputOffset += stride); + (k.value = propertyInfo.get(animationTarget.target, data.output, outputOffset)), (outputOffset += stride); + (k.outTangent = propertyInfo.get(animationTarget.target, data.output, outputOffset, invfps)), (outputOffset += stride); + + keys[frameIndex] = k; + } + break; + } + case AnimationSamplerInterpolation.LINEAR: + default: { + for (let frameIndex = 0; frameIndex < data.input.length; frameIndex++) { + keys[frameIndex] = { + frame: data.input[frameIndex] * fps, + value: propertyInfo.get(animationTarget.target, data.output, outputOffset), + }; + outputOffset += stride; + } + break; + } + } + + // each properties has its own build animation process. + propertyInfo.buildAnimations(animationTarget.target, fps, keys, babylonAnimationGroup); + } + } + } + }); + } + + /** + * parsing animation pointer is the core of animation channel. + * Animation pointer is a Json pointer, which mean it locate an item into the json hierarchy. + * Consequentely the pointer has the following BNF + + * := + * := "nodes" | "materials" | "meshes" | "cameras" | "extensions" + * := | + * := | + * := "extensions" + * := | + * := "/" + * := W+ + * := D+ + * + * examples of pointer are + * - "/nodes/0/rotation" + * - "/materials/2/emissiveFactor" + * - "/materials/2/pbrMetallicRoughness/baseColorFactor" + * - "/materials/2/extensions/KHR_materials_emissive_strength/emissiveStrength" + * @param context + * @param pointer + * @return + */ + private _parseAnimationPointer(context: string, pointer: string): Nullable { + const parts = pointer.split("/"); + // we have a least 3 part + if (parts.length >= 3) { + let node = CoreAnimationPointerMap; // the map of possible path + let index: string = ""; + for (let i = 0; i != parts.length; i++) { + const part = parts[i]; + if (node.hasIndex) { + index = part; + // move to the next part + continue; + } + node = node[part]; + if (!node) { + // nothing to do so far + break; + } + if (node.getTarget) { + // this is a leaf + const t = node.getTarget(this._loader.gltf, index); + if (t != null) { + return { + target: t, + stride: node.getStride ? node.getStride(t) : KHR_animation_pointer.GetAnimationOutputStride(node.properties), + properties: node.properties, + }; + } + } + } + } + if (KHR_animation_pointer.IgnoreInvalidPointer) { + return null; + } + throw new Error(`${context} invalid pointer. ${pointer}`); + } +} + +GLTFLoader.RegisterExtension(NAME, (loader) => new KHR_animation_pointer(loader)); diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/index.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/index.ts index 52f3f11b1d7..1aa254d44b4 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/index.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/index.ts @@ -20,6 +20,8 @@ export * from "./KHR_mesh_quantization"; export * from "./KHR_texture_basisu"; export * from "./KHR_texture_transform"; export * from "./KHR_xmp_json_ld"; +export * from "./KHR_animation_pointer"; +export * from "./KHR_animation_pointer.map"; export * from "./MSFT_audio_emitter"; export * from "./MSFT_lod"; export * from "./MSFT_minecraftMesh"; diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts index e5a5460b747..59b971190f7 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts @@ -1702,7 +1702,7 @@ export class GLTFLoader implements IGLTFLoader { }); } - private _loadAnimationSamplerAsync(context: string, sampler: IAnimationSampler): Promise<_IAnimationSamplerData> { + public _loadAnimationSamplerAsync(context: string, sampler: IAnimationSampler): Promise<_IAnimationSamplerData> { if (sampler._data) { return sampler._data; } diff --git a/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts b/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts index 4e4b53c8b1e..a22f887d3c0 100644 --- a/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts +++ b/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts @@ -73,7 +73,9 @@ declare module BABYLON.GLTF2 { } /** - * The name of the node's TRS property to modify, or the weights of the Morph Targets it instantiates + * The name of the node's TRS property to modify, + * or the weights of the Morph Targets it instantiates, + * or pointer is use of KHR_animation_pointer extension */ const enum AnimationChannelTargetPath { /** @@ -91,7 +93,11 @@ declare module BABYLON.GLTF2 { /** * Weights */ - WEIGHTS = "weights", + WEIGHTS = "weights", + /** + * Pointer + */ + POINTER = "pointer", } /** From 0ef69f64054ccba1850ca03553a506dfb6496cbc Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Mon, 20 Jun 2022 21:34:00 +0200 Subject: [PATCH 02/16] turn standard animation into pointer --- .../Extensions/KHR_animation_pointer.map.ts | 4 +- .../2.0/Extensions/KHR_animation_pointer.ts | 96 +++++-- .../dev/loaders/src/glTF/2.0/glTFLoader.ts | 256 ++---------------- .../babylon.glTF2Interface.d.ts | 4 +- 4 files changed, 102 insertions(+), 258 deletions(-) diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts index 5f9d8685e55..4872ee129af 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts @@ -135,7 +135,9 @@ class WeightAnimationPointerPropertyInfos extends AbstractAnimationPointerProper public constructor(type: number, name: string, get: GetValueFn) { super(type, name, get); } - + public isValid(target: any) : boolean { + return target._numMorphTargets ; + } public buildAnimations(targetNode: any, fps: number, keys: any[], babylonAnimationGroup: AnimationGroup): void { if(targetNode._numMorphTargets){ diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts index feca347d74b..63bdc94733e 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts @@ -3,13 +3,13 @@ import { ArrayItem, GLTFLoader } from "../glTFLoader"; import type { Nullable } from "core/types"; import { AnimationGroup } from "core/Animations/animationGroup"; import { Animation } from "core/Animations/animation"; -import type { IAnimation, IAnimationChannel } from "../glTFLoaderInterfaces"; +import type { IAnimation, IAnimationChannel, _IAnimationSamplerData, IAnimationSampler} from "../glTFLoaderInterfaces"; import { AnimationChannelTargetPath, AnimationSamplerInterpolation } from "babylonjs-gltf2interface"; import { AnimationKeyInterpolation } from "core/Animations/animationKey"; import { CoreAnimationPointerMap, IAnimationPointerPropertyInfos } from "./KHR_animation_pointer.map"; -const NAME = "KHR_animation_pointer"; +const NAME = GLTFLoader._KHRAnimationPointerName; interface IAnimationChannelTarget { stride: number; @@ -28,9 +28,10 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { public static IgnoreInvalidPointer: boolean = false; /** - * Used internally to determine how much data to be gather from input buffer. + * Used internally to determine how much data to be gather from input buffer at each key frame. + * Normal usage do not call this function while a GetStrideFn is located into the map path. * @param infos the informations - * @returns + * @returns the number of item to be gather at each keyframe */ static GetAnimationOutputStride(infos: Array): number { let stride = 0; @@ -71,10 +72,6 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { * The name of this extension. */ public readonly name = NAME; - /** - * Defines whether this extension is enabled. - */ - public enabled: boolean; private _loader: GLTFLoader; @@ -84,7 +81,13 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { */ constructor(loader: GLTFLoader) { this._loader = loader; - this.enabled = this._loader.isExtensionUsed(NAME); + } + + /** + * Defines whether this extension is enabled. + */ + public get enabled(): boolean { + return this._loader.isExtensionUsed(NAME); } /** @hidden */ @@ -103,12 +106,17 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { return property != "name"; } - public loadAnimationAsync?(context: string, animation: IAnimation): Nullable> { - this._loader.babylonScene._blockEntityCollection = !!this._loader._assetContainer; - const babylonAnimationGroup = new AnimationGroup(animation.name || `animation${animation.index}`, this._loader.babylonScene); - babylonAnimationGroup._parentContainer = this._loader._assetContainer; - this._loader.babylonScene._blockEntityCollection = false; - animation._babylonAnimationGroup = babylonAnimationGroup; + public loadAnimationAsync(context: string, animation: IAnimation): Nullable> { + + // ensure an animation group is present. + if(!animation._babylonAnimationGroup){ + this._loader.babylonScene._blockEntityCollection = !!this._loader._assetContainer; + const babylonAnimationGroup = new AnimationGroup(animation.name || `animation${animation.index}`, this._loader.babylonScene); + babylonAnimationGroup._parentContainer = this._loader._assetContainer; + this._loader.babylonScene._blockEntityCollection = false; + animation._babylonAnimationGroup = babylonAnimationGroup; + } + const babylonAnimationGroup = animation._babylonAnimationGroup ; const promises = new Array>(); ArrayItem.Assign(animation.channels); @@ -151,7 +159,7 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { const sampler = ArrayItem.Get(`${context}/sampler`, animation.samplers, channel.sampler); - return this._loader._loadAnimationSamplerAsync(`${context}/samplers/${channel.sampler}`, sampler).then((data) => { + return this._loadAnimationSamplerAsync(`${context}/samplers/${channel.sampler}`, sampler).then((data) => { // this is where we process the pointer. const animationTarget = this._parseAnimationPointer(`${context}/extensions/${this.name}/pointer`, pointer); @@ -221,11 +229,46 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { } // each properties has its own build animation process. + // these logics are located into KHR_animation_pointer.map.ts propertyInfo.buildAnimations(animationTarget.target, fps, keys, babylonAnimationGroup); } } } + return Promise.resolve(); + }); + } + + private _loadAnimationSamplerAsync(context: string, sampler: IAnimationSampler): Promise<_IAnimationSamplerData> { + if (sampler._data) { + return sampler._data; + } + + const interpolation = sampler.interpolation || AnimationSamplerInterpolation.LINEAR; + switch (interpolation) { + case AnimationSamplerInterpolation.STEP: + case AnimationSamplerInterpolation.LINEAR: + case AnimationSamplerInterpolation.CUBICSPLINE: { + break; + } + default: { + throw new Error(`${context}/interpolation: Invalid value (${sampler.interpolation})`); + } + } + + const inputAccessor = ArrayItem.Get(`${context}/input`, this._loader.gltf.accessors, sampler.input); + const outputAccessor = ArrayItem.Get(`${context}/output`, this._loader.gltf.accessors, sampler.output); + sampler._data = Promise.all([ + this._loader._loadFloatAccessorAsync(`/accessors/${inputAccessor.index}`, inputAccessor), + this._loader._loadFloatAccessorAsync(`/accessors/${outputAccessor.index}`, outputAccessor), + ]).then(([inputData, outputData]) => { + return { + input: inputData, + interpolation: interpolation, + output: outputData, + }; }); + + return sampler._data; } /** @@ -253,23 +296,30 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { * @return */ private _parseAnimationPointer(context: string, pointer: string): Nullable { - const parts = pointer.split("/"); + const sep = "/"; + if (pointer.charAt(0) == sep) { + pointer = pointer.substring(1); + } + const parts = pointer.split(sep); // we have a least 3 part if (parts.length >= 3) { let node = CoreAnimationPointerMap; // the map of possible path let index: string = ""; - for (let i = 0; i != parts.length; i++) { + for (let i = 0; i < parts.length; i++) { const part = parts[i]; - if (node.hasIndex) { - index = part; - // move to the next part - continue; - } node = node[part]; + if (!node) { // nothing to do so far break; } + + if (node.hasIndex) { + index = parts[++i]; + // move to the next part + continue; + } + if (node.getTarget) { // this is a leaf const t = node.getTarget(this._loader.gltf, index); diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts index 59b971190f7..97b42814672 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts @@ -6,7 +6,6 @@ import { Tools } from "core/Misc/tools"; import { Camera } from "core/Cameras/camera"; import { FreeCamera } from "core/Cameras/freeCamera"; import { AnimationGroup } from "core/Animations/animationGroup"; -import { Animation } from "core/Animations/animation"; import { Bone } from "core/Bones/bone"; import { Skeleton } from "core/Bones/skeleton"; import { Material } from "core/Materials/material"; @@ -29,7 +28,6 @@ import { AccessorType, CameraType, AnimationChannelTargetPath, - AnimationSamplerInterpolation, AccessorComponentType, MaterialAlphaMode, TextureMinFilter, @@ -38,7 +36,6 @@ import { MeshPrimitiveMode, } from "babylonjs-gltf2interface"; import type { - _IAnimationSamplerData, IGLTF, ISampler, INode, @@ -48,8 +45,6 @@ import type { ISkin, ICamera, IAnimation, - IAnimationChannel, - IAnimationSampler, IBuffer, IBufferView, IMaterialPbrMetallicRoughness, @@ -64,9 +59,6 @@ import type { import type { IGLTFLoaderExtension } from "./glTFLoaderExtension"; import type { IGLTFLoader, IGLTFLoaderData } from "../glTFFileLoader"; import { GLTFFileLoader, GLTFLoaderState, GLTFLoaderCoordinateSystemMode, GLTFLoaderAnimationStartMode } from "../glTFFileLoader"; -import type { IAnimationKey } from "core/Animations/animationKey"; -import { AnimationKeyInterpolation } from "core/Animations/animationKey"; -import type { IAnimatable } from "core/Animations/animatable.interface"; import type { IDataBuffer } from "core/Misc/dataReader"; import { DecodeBase64UrlToBinary, IsBase64DataUrl, LoadFileError } from "core/Misc/fileTools"; import { Logger } from "core/Misc/logger"; @@ -130,6 +122,9 @@ export class ArrayItem { * The glTF 2.0 loader */ export class GLTFLoader implements IGLTFLoader { + /** @hidden */ + public static readonly _KHRAnimationPointerName = "KHR_animation_pointer"; + /** @hidden */ public readonly _completePromises = new Array>(); @@ -1501,238 +1496,35 @@ export class GLTFLoader implements IGLTFLoader { * @returns A promise that resolves with the loaded Babylon animation group when the load is complete */ public loadAnimationAsync(context: string, animation: IAnimation): Promise { - const promise = this._extensionsLoadAnimationAsync(context, animation); - if (promise) { - return promise; + // turn everything into pointer + for (const channel of animation.channels) { + if (channel.target.path == AnimationChannelTargetPath.POINTER) { + continue; + } + // decorate the channel with a KHR_animation_pointer extension. + channel.target.extensions = channel.target.extensions || {}; + channel.target.extensions.KHR_animation_pointer = { + pointer: `/nodes/${channel.target.node}/${channel.target.path}`, + }; + channel.target.path = AnimationChannelTargetPath.POINTER; + delete channel.target.node; + + // ensure to declare extension used. + this._gltf.extensionsUsed = this._gltf.extensionsUsed || []; + if (this._gltf.extensionsUsed.indexOf(GLTFLoader._KHRAnimationPointerName) === -1) { + this._gltf.extensionsUsed.push(GLTFLoader._KHRAnimationPointerName); + } } + // create the animation group to be passed to extension. this._babylonScene._blockEntityCollection = !!this._assetContainer; const babylonAnimationGroup = new AnimationGroup(animation.name || `animation${animation.index}`, this._babylonScene); babylonAnimationGroup._parentContainer = this._assetContainer; this._babylonScene._blockEntityCollection = false; animation._babylonAnimationGroup = babylonAnimationGroup; - const promises = new Array>(); - - ArrayItem.Assign(animation.channels); - ArrayItem.Assign(animation.samplers); - - for (const channel of animation.channels) { - promises.push(this._loadAnimationChannelAsync(`${context}/channels/${channel.index}`, context, animation, channel, babylonAnimationGroup)); - } - - return Promise.all(promises).then(() => { - babylonAnimationGroup.normalize(0); - return babylonAnimationGroup; - }); - } - - /** - * @hidden Loads a glTF animation channel. - * @param context The context when loading the asset - * @param animationContext The context of the animation when loading the asset - * @param animation The glTF animation property - * @param channel The glTF animation channel property - * @param babylonAnimationGroup The babylon animation group property - * @param animationTargetOverride The babylon animation channel target override property. My be null. - * @returns A void promise when the channel load is complete - */ - public _loadAnimationChannelAsync( - context: string, - animationContext: string, - animation: IAnimation, - channel: IAnimationChannel, - babylonAnimationGroup: AnimationGroup, - animationTargetOverride: Nullable = null - ): Promise { - if (channel.target.node == undefined) { - return Promise.resolve(); - } - - const targetNode = ArrayItem.Get(`${context}/target/node`, this._gltf.nodes, channel.target.node); - - // Ignore animations that have no animation targets. - if ( - (channel.target.path === AnimationChannelTargetPath.WEIGHTS && !targetNode._numMorphTargets) || - (channel.target.path !== AnimationChannelTargetPath.WEIGHTS && !targetNode._babylonTransformNode) - ) { - return Promise.resolve(); - } - - const sampler = ArrayItem.Get(`${context}/sampler`, animation.samplers, channel.sampler); - return this._loadAnimationSamplerAsync(`${animationContext}/samplers/${channel.sampler}`, sampler).then((data) => { - let targetPath: string; - let animationType: number; - switch (channel.target.path) { - case AnimationChannelTargetPath.TRANSLATION: { - targetPath = "position"; - animationType = Animation.ANIMATIONTYPE_VECTOR3; - break; - } - case AnimationChannelTargetPath.ROTATION: { - targetPath = "rotationQuaternion"; - animationType = Animation.ANIMATIONTYPE_QUATERNION; - break; - } - case AnimationChannelTargetPath.SCALE: { - targetPath = "scaling"; - animationType = Animation.ANIMATIONTYPE_VECTOR3; - break; - } - case AnimationChannelTargetPath.WEIGHTS: { - targetPath = "influence"; - animationType = Animation.ANIMATIONTYPE_FLOAT; - break; - } - default: { - throw new Error(`${context}/target/path: Invalid value (${channel.target.path})`); - } - } - - let outputBufferOffset = 0; - let getNextOutputValue: (scale: number) => Vector3 | Quaternion | Array; - switch (targetPath) { - case "position": { - getNextOutputValue = (scale) => { - const value = Vector3.FromArray(data.output, outputBufferOffset).scaleInPlace(scale); - outputBufferOffset += 3; - return value; - }; - break; - } - case "rotationQuaternion": { - getNextOutputValue = (scale) => { - const value = Quaternion.FromArray(data.output, outputBufferOffset).scaleInPlace(scale); - outputBufferOffset += 4; - return value; - }; - break; - } - case "scaling": { - getNextOutputValue = (scale) => { - const value = Vector3.FromArray(data.output, outputBufferOffset).scaleInPlace(scale); - outputBufferOffset += 3; - return value; - }; - break; - } - case "influence": { - getNextOutputValue = (scale) => { - const value = new Array(targetNode._numMorphTargets!); - for (let i = 0; i < targetNode._numMorphTargets!; i++) { - value[i] = data.output[outputBufferOffset++] * scale; - } - return value; - }; - break; - } - } - - let getNextKey: (frameIndex: number) => IAnimationKey; - switch (data.interpolation) { - case AnimationSamplerInterpolation.STEP: { - getNextKey = (frameIndex) => ({ - frame: data.input[frameIndex] * this.parent.targetFps, - value: getNextOutputValue(1), - interpolation: AnimationKeyInterpolation.STEP, - }); - break; - } - case AnimationSamplerInterpolation.LINEAR: { - getNextKey = (frameIndex) => ({ - frame: data.input[frameIndex] * this.parent.targetFps, - value: getNextOutputValue(1), - }); - break; - } - case AnimationSamplerInterpolation.CUBICSPLINE: { - const invTargetFps = 1 / this.parent.targetFps; - getNextKey = (frameIndex) => ({ - frame: data.input[frameIndex] * this.parent.targetFps, - inTangent: getNextOutputValue(invTargetFps), - value: getNextOutputValue(1), - outTangent: getNextOutputValue(invTargetFps), - }); - break; - } - } - - const keys = new Array(data.input.length); - for (let frameIndex = 0; frameIndex < data.input.length; frameIndex++) { - keys[frameIndex] = getNextKey!(frameIndex); - } - - if (targetPath === "influence") { - for (let targetIndex = 0; targetIndex < targetNode._numMorphTargets!; targetIndex++) { - const animationName = `${babylonAnimationGroup.name}_channel${babylonAnimationGroup.targetedAnimations.length}`; - const babylonAnimation = new Animation(animationName, targetPath, this.parent.targetFps, animationType); - babylonAnimation.setKeys( - keys.map((key) => ({ - frame: key.frame, - inTangent: key.inTangent ? key.inTangent[targetIndex] : undefined, - value: key.value[targetIndex], - outTangent: key.outTangent ? key.outTangent[targetIndex] : undefined, - interpolation: key.interpolation, - })) - ); - - this._forEachPrimitive(targetNode, (babylonAbstractMesh: AbstractMesh) => { - const babylonMesh = babylonAbstractMesh as Mesh; - if (babylonMesh.morphTargetManager) { - const morphTarget = babylonMesh.morphTargetManager.getTarget(targetIndex); - const babylonAnimationClone = babylonAnimation.clone(); - morphTarget.animations.push(babylonAnimationClone); - babylonAnimationGroup.addTargetedAnimation(babylonAnimationClone, morphTarget); - } - }); - } - } else { - const animationName = `${babylonAnimationGroup.name}_channel${babylonAnimationGroup.targetedAnimations.length}`; - const babylonAnimation = new Animation(animationName, targetPath, this.parent.targetFps, animationType); - babylonAnimation.setKeys(keys); - - if (animationTargetOverride != null && animationTargetOverride.animations != null) { - animationTargetOverride.animations.push(babylonAnimation); - babylonAnimationGroup.addTargetedAnimation(babylonAnimation, animationTargetOverride); - } else { - targetNode._babylonTransformNode!.animations.push(babylonAnimation); - babylonAnimationGroup.addTargetedAnimation(babylonAnimation, targetNode._babylonTransformNode!); - } - } - }); - } - - public _loadAnimationSamplerAsync(context: string, sampler: IAnimationSampler): Promise<_IAnimationSamplerData> { - if (sampler._data) { - return sampler._data; - } - - const interpolation = sampler.interpolation || AnimationSamplerInterpolation.LINEAR; - switch (interpolation) { - case AnimationSamplerInterpolation.STEP: - case AnimationSamplerInterpolation.LINEAR: - case AnimationSamplerInterpolation.CUBICSPLINE: { - break; - } - default: { - throw new Error(`${context}/interpolation: Invalid value (${sampler.interpolation})`); - } - } - - const inputAccessor = ArrayItem.Get(`${context}/input`, this._gltf.accessors, sampler.input); - const outputAccessor = ArrayItem.Get(`${context}/output`, this._gltf.accessors, sampler.output); - sampler._data = Promise.all([ - this._loadFloatAccessorAsync(`/accessors/${inputAccessor.index}`, inputAccessor), - this._loadFloatAccessorAsync(`/accessors/${outputAccessor.index}`, outputAccessor), - ]).then(([inputData, outputData]) => { - return { - input: inputData, - interpolation: interpolation, - output: outputData, - }; - }); - - return sampler._data; + const promise = this._extensionsLoadAnimationAsync(context, animation); + return promise ?? Promise.resolve(animation._babylonAnimationGroup); } /** diff --git a/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts b/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts index a22f887d3c0..9fb6eaf9da2 100644 --- a/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts +++ b/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts @@ -397,9 +397,9 @@ declare module BABYLON.GLTF2 { */ interface IAnimationChannelTarget extends IProperty { /** - * The index of the node to target + * The index of the node to target - become optional with the introduction of KHR_animation_pointer */ - node: number; + node?: number; /** * The name of the node's TRS property to modify, or the weights of the Morph Targets it instantiates */ From 28aa53e2a6ed615d21737ed694579066e6d1a052 Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Tue, 21 Jun 2022 18:30:57 +0200 Subject: [PATCH 03/16] integration of extensions to animation pointer --- .../Extensions/KHR_animation_pointer.map.ts | 281 ++++++++++++++---- .../2.0/Extensions/KHR_animation_pointer.ts | 97 +++--- .../2.0/Extensions/KHR_lights_punctual.ts | 3 +- .../dev/loaders/src/glTF/2.0/glTFLoader.ts | 3 + 4 files changed, 264 insertions(+), 120 deletions(-) diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts index 4872ee129af..26bff2268d0 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts @@ -1,14 +1,16 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import { Animation } from "core/Animations/animation"; import type { AnimationGroup } from "core/Animations/animationGroup"; -import { Quaternion, Vector3 } from "core/Maths/math.vector"; +import { Quaternion, Vector3, Matrix } from "core/Maths/math.vector"; import { Color3 } from "core/Maths/math.color"; import type { IGLTF, INode } from "../glTFLoaderInterfaces"; import { Material } from "core/Materials/material"; import type { IAnimatable } from "core/Animations/animatable.interface"; import type { AbstractMesh } from "core/Meshes/abstractMesh"; import { Mesh } from "core/Meshes/mesh"; +import type { Nullable } from "core/types"; -type GetTargetFn = (source: IGLTF, index: string) => any | null; +export type GetGltfNodeTargetFn = (source: IGLTF, indices: string) => any; export type GetValueFn = (target: any, source: Float32Array, offset: number, scale?: number) => any; export type GetStrideFn = (target: any) => number; @@ -16,11 +18,11 @@ export interface IAnimationPointerPropertyInfos { type: number; name: string; get: GetValueFn; - isValid(target: any):boolean; - buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup): void; + isValid(target: any): boolean; + buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup, animationTargetOverride: Nullable, params?: any): void; } -const parseIntIndex = (str: string) => { +const _parseIntIndex = (str: string) => { const targetIndex = parseInt(str); if (isNaN(targetIndex)) { return -1; @@ -28,9 +30,9 @@ const parseIntIndex = (str: string) => { return targetIndex; }; -const _getNode: GetTargetFn = (gltf: IGLTF, index: string) => { +const _getGltfNode: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { if (gltf.nodes) { - const i = parseIntIndex(index); + const i = _parseIntIndex(index); if (i != -1) { return gltf.nodes[i]; } @@ -38,9 +40,9 @@ const _getNode: GetTargetFn = (gltf: IGLTF, index: string) => { return null; }; -const _getMaterial: GetTargetFn = (gltf: IGLTF, index: string) => { +const _getGltfMaterial: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { if (gltf.materials) { - const i = parseIntIndex(index); + const i = _parseIntIndex(index); if (i != -1) { return gltf.materials[i]; } @@ -48,6 +50,17 @@ const _getMaterial: GetTargetFn = (gltf: IGLTF, index: string) => { return null; }; +const _getGltfExtension: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { + if (gltf.extensions && index) { + return gltf.extensions[index]; + } + return null; +}; + +const _getMatrix: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { + return scale ? Matrix.FromArray(source, offset).scale(scale) : Matrix.FromArray(source, offset); +}; + const _getVector3: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { return scale ? Vector3.FromArray(source, offset).scaleInPlace(scale) : Vector3.FromArray(source, offset); }; @@ -69,7 +82,7 @@ const _getFloat: GetValueFn = (_target: any, source: Float32Array, offset: numbe }; const _getWeights: GetValueFn = (target: any, source: Float32Array, offset: number, scale?: number) => { - if( target._numMorphTargets){ + if (target._numMorphTargets) { const value = new Array(target._numMorphTargets!); for (let i = 0; i < target._numMorphTargets!; i++) { value[i] = scale ? source[offset++] * scale : source[offset++]; @@ -82,30 +95,45 @@ const _getWeights: GetValueFn = (target: any, source: Float32Array, offset: numb abstract class AbstractAnimationPointerPropertyInfos implements IAnimationPointerPropertyInfos { public constructor(public type: number, public name: string, public get: GetValueFn) {} - protected _buildAnimation(animatable: IAnimatable, fps: number, keys: any[], babylonAnimationGroup: AnimationGroup) { - if (animatable) { + protected _buildAnimation( + animatable: Nullable, + fps: number, + keys: any[], + babylonAnimationGroup: AnimationGroup, + animationTargetOverride: Nullable = null + ) { + if (animatable || animationTargetOverride) { const animationName = `${babylonAnimationGroup!.name}_channel${babylonAnimationGroup.targetedAnimations.length}_${this.name}`; const babylonAnimation = new Animation(animationName, this.name, fps, this.type); babylonAnimation.setKeys(keys); - animatable.animations = animatable.animations || Array(1); - animatable.animations.push(babylonAnimation); - babylonAnimationGroup.addTargetedAnimation(babylonAnimation, animatable); + if (animationTargetOverride != null && animationTargetOverride.animations != null) { + animationTargetOverride.animations.push(babylonAnimation); + babylonAnimationGroup.addTargetedAnimation(babylonAnimation, animationTargetOverride); + } else if (animatable) { + animatable.animations = animatable.animations || Array(1); + animatable.animations.push(babylonAnimation); + babylonAnimationGroup.addTargetedAnimation(babylonAnimation, animatable); + } } } - public isValid(_target: any):boolean{ return true; } + public isValid(_target: any): boolean { + return true; + } - public abstract buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup): void; + public abstract buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup, animationTargetOverride: Nullable, params?: any): void; } class TransformNodeAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { public constructor(type: number, name: string, get: GetValueFn) { super(type, name, get); } - public isValid(target: any):boolean{ return target._babylonTransformNode !== null && target._babylonTransformNode !== undefined; } + public isValid(target: any): boolean { + return target._babylonTransformNode !== null && target._babylonTransformNode !== undefined; + } - public buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup): void { - return this._buildAnimation(target._babylonTransformNode, fps, keys, group); + public buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup, animationTargetOverride: Nullable = null): void { + return this._buildAnimation(target._babylonTransformNode, fps, keys, group, animationTargetOverride); } } @@ -114,33 +142,48 @@ class MaterialAnimationPointerPropertyInfos extends AbstractAnimationPointerProp super(type, name, get); } - public isValid(target: any) : boolean { + public isValid(target: any): boolean { const data = target._data; - if(data) { - const c = data[this.fillMode] ; - if(c){ - return c.babylonMaterial !== null && c.babylonMaterial !== undefined ; + if (data) { + const c = data[this.fillMode]; + if (c) { + return c.babylonMaterial !== null && c.babylonMaterial !== undefined; } } return false; } - public buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup): void { - return this._buildAnimation(target._data[this.fillMode].babylonMaterial, fps, keys, group); + public buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup, animationTargetOverride: Nullable = null): void { + return this._buildAnimation(target._data[this.fillMode].babylonMaterial, fps, keys, group, animationTargetOverride); } } -class WeightAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { +class LightAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { + public constructor(type: number, name: string, get: GetValueFn, public fillMode: any = Material.TriangleFillMode) { + super(type, name, get); + } + + public isValid(target: any): boolean { + return target && target.length != 0; + } + // note : the extensions array store directly the BabylonLight reference + public buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup, animationTargetOverride: Nullable = null, params: any): void { + const i = _parseIntIndex(params[1]); + const l = i >= 0 && i < target.lights.length ? target.lights[i] : null ; + return this._buildAnimation(l, fps, keys, group, animationTargetOverride); + } +} + +class WeightAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { public constructor(type: number, name: string, get: GetValueFn) { super(type, name, get); } - public isValid(target: any) : boolean { - return target._numMorphTargets ; + public isValid(target: any): boolean { + return target._numMorphTargets; } - public buildAnimations(targetNode: any, fps: number, keys: any[], babylonAnimationGroup: AnimationGroup): void { - - if(targetNode._numMorphTargets){ + public buildAnimations(targetNode: any, fps: number, keys: any[], babylonAnimationGroup: AnimationGroup, _animationTargetOverride: Nullable = null): void { + if (targetNode._numMorphTargets) { for (let targetIndex = 0; targetIndex < targetNode._numMorphTargets; targetIndex++) { const animationName = `${babylonAnimationGroup.name}_channel${babylonAnimationGroup.targetedAnimations.length}`; const babylonAnimation = new Animation(animationName, this.name, fps, this.type); @@ -177,30 +220,21 @@ class WeightAnimationPointerPropertyInfos extends AbstractAnimationPointerProper } const CoreAnimationNodesPointerMap: any = { + getTarget: _getGltfNode, hasIndex: true, + matrix: { + properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_MATRIX, "matrix", _getMatrix)], + }, translation: { - getTarget: _getNode, - getStride: (_target: any) => { - return 3; - }, properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_VECTOR3, "position", _getVector3)], }, rotation: { - getTarget: _getNode, - getStride: (_target: any) => { - return 4; - }, properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_QUATERNION, "rotationQuaternion", _getQuaternion)], }, scale: { - getTarget: _getNode, - getStride: (_target: any) => { - return 3; - }, properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_VECTOR3, "scaling", _getVector3)], }, - weight: { - getTarget: _getNode, + weights: { getStride: (target: any) => { return target._numMorphTargets; }, @@ -208,49 +242,166 @@ const CoreAnimationNodesPointerMap: any = { }, }; +const CoreAnimationCamerasPointerMap: any = { + hasIndex: true, +}; + const CoreAnimationMaterialsPointerMap: any = { hasIndex: true, + getTarget: _getGltfMaterial, pbrMetallicRoughness: { baseColorFactor: { - getTarget: _getMaterial, - getStride: (_target: any) => { - return 4; - }, properties: [ new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "albedoColor", _getColor3), new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "alpha", _getAlpha), ], }, metallicFactor: { - getTarget: _getMaterial, - getStride: (_target: any) => { - return 1; - }, properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "metallic", _getFloat)], }, roughnessFactor: { - getTarget: _getMaterial, - getStride: (_target: any) => { - return 1; - }, properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "roughness", _getFloat)], }, + baseColorTexture: { + extensions: { + KHR_texture_transform: { + scale: { + properties: [ + // MAY introduce set scale(Vector2) into texture. + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.uScale", _getFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.vScale", _getFloat), + ], + }, + offset: { + properties: [ + // MAY introduce set offset(Vector2) into texture. + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.uOffset", _getFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.vOffset", _getFloat), + ], + }, + }, + }, + }, }, emissiveFactor: { - getTarget: _getMaterial, - getStride: (_target: any) => { - return 1; + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "emissiveColor", _getColor3)], + }, + normalTexture: { + scale: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "bumpTexture.level", _getFloat)], + }, + }, + occlusionTexture: { + strength: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTextureStrength", _getFloat)], + }, + extensions: { + KHR_texture_transform: { + scale: { + properties: [ + // MAY introduce set scale(Vector2) into texture. + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.uScale", _getFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.vScale", _getFloat), + ], + }, + offset: { + properties: [ + // MAY introduce set offset(Vector2) into texture. + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.uOffset", _getFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.vOffset", _getFloat), + ], + }, + }, + }, }, + emissiveTexture: { + extensions: { + KHR_texture_transform: { + scale: { + properties: [ + // MAY introduce set scale(Vector2) into texture. + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.uScale", _getFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.vScale", _getFloat), + ], + }, + offset: { + properties: [ + // MAY introduce set offset(Vector2) into texture. + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.uOffset", _getFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.vOffset", _getFloat), + ], + }, + }, + }, + }, + extensions: { + KHR_materials_emissive_strength: { + emissiveStrength: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveIntensity", _getFloat)], + }, + }, + KHR_materials_transmission: { + transmissionFactor: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "subSurface.refractionIntensity", _getFloat)], + }, + }, + KHR_materials_volume: { + attenuationColor:{ + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "subSurface.tintColor", _getColor3)], + }, + attenuationDistance: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "subSurface.tintColorAtDistance", _getFloat)], + }, + thicknessFactor: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "subSurface.maximumThickness", _getFloat)], + }, + }, + KHR_materials_iridescence :{ + iridescenceFactor :{ + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "iridescence.intensity", _getFloat)], + }, + iridescenceIor :{ + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "iridescence.indexOfRefraction", _getFloat)], + }, + iridescenceThicknessMinimum :{ + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "iridescence.minimumThickness", _getFloat)], + }, + iridescenceThicknessMaximum :{ + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "iridescence.maximumThickness", _getFloat)], + }, }, - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissive", _getFloat)], }, }; -const CoreAnimationCamerasPointerMap: any = { - hasIndex: true, +const CoreAnimationExtensionsPointerMap: any = { + getTarget: _getGltfExtension, + KHR_lights_punctual: { + isIndex: true, + lights: { + hasIndex: true, // we have an array of light into the extension. + color: { + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "diffuseColor", _getColor3)], + }, + intensity: { + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "intensity", _getFloat)], + }, + range: { + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "range", _getFloat)], + }, + spot: { + innerConeAngle: { + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "innerAngle", _getFloat)], + }, + outerConeAngle: { + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "angle", _getFloat)], + }, + }, + }, + } }; export const CoreAnimationPointerMap: any = { nodes: CoreAnimationNodesPointerMap, materials: CoreAnimationMaterialsPointerMap, cameras: CoreAnimationCamerasPointerMap, + extensions: CoreAnimationExtensionsPointerMap, }; diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts index 63bdc94733e..d0134fc1b2c 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts @@ -2,19 +2,20 @@ import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; import { ArrayItem, GLTFLoader } from "../glTFLoader"; import type { Nullable } from "core/types"; import { AnimationGroup } from "core/Animations/animationGroup"; -import { Animation } from "core/Animations/animation"; +import type { IAnimatable } from "core/Animations/animatable.interface"; import type { IAnimation, IAnimationChannel, _IAnimationSamplerData, IAnimationSampler} from "../glTFLoaderInterfaces"; -import { AnimationChannelTargetPath, AnimationSamplerInterpolation } from "babylonjs-gltf2interface"; +import { AccessorType, AnimationChannelTargetPath, AnimationSamplerInterpolation } from "babylonjs-gltf2interface"; import { AnimationKeyInterpolation } from "core/Animations/animationKey"; -import { CoreAnimationPointerMap, IAnimationPointerPropertyInfos } from "./KHR_animation_pointer.map"; +import { GetGltfNodeTargetFn, CoreAnimationPointerMap, IAnimationPointerPropertyInfos } from "./KHR_animation_pointer.map"; const NAME = GLTFLoader._KHRAnimationPointerName; interface IAnimationChannelTarget { - stride: number; + stride?: number; target: any; properties: Array; + params:any; } /** @@ -25,49 +26,24 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { /** * used to gently ignore invalid pointer. If false, invalid pointer will throw exception. */ - public static IgnoreInvalidPointer: boolean = false; + public static IgnoreInvalidPointer: boolean = true; /** * Used internally to determine how much data to be gather from input buffer at each key frame. - * Normal usage do not call this function while a GetStrideFn is located into the map path. - * @param infos the informations + * @param type the accessor type * @returns the number of item to be gather at each keyframe */ - static GetAnimationOutputStride(infos: Array): number { - let stride = 0; - for (const info of infos) { - switch (info.type) { - case Animation.ANIMATIONTYPE_FLOAT: { - stride++; - break; - } - case Animation.ANIMATIONTYPE_VECTOR2: - case Animation.ANIMATIONTYPE_SIZE: { - stride += 2; - break; - } - - case Animation.ANIMATIONTYPE_VECTOR3: - case Animation.ANIMATIONTYPE_COLOR3: { - stride += 3; - break; - } - - case Animation.ANIMATIONTYPE_COLOR4: - case Animation.ANIMATIONTYPE_QUATERNION: { - stride += 4; - break; - } - - case Animation.ANIMATIONTYPE_MATRIX: { - stride += 16; - break; - } + static GetAnimationOutputStride(type:AccessorType): number { + switch (type) { + case AccessorType.SCALAR: return 1; + case AccessorType.VEC2: return 2; + case AccessorType.VEC3: return 3; + case AccessorType.VEC4: + case AccessorType.MAT2: return 4; + case AccessorType.MAT3: return 9; + case AccessorType.MAT4: return 16; } - } - return stride; } - /** * The name of this extension. */ @@ -111,10 +87,10 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { // ensure an animation group is present. if(!animation._babylonAnimationGroup){ this._loader.babylonScene._blockEntityCollection = !!this._loader._assetContainer; - const babylonAnimationGroup = new AnimationGroup(animation.name || `animation${animation.index}`, this._loader.babylonScene); - babylonAnimationGroup._parentContainer = this._loader._assetContainer; + const group = new AnimationGroup(animation.name || `animation${animation.index}`, this._loader.babylonScene); + group._parentContainer = this._loader._assetContainer; this._loader.babylonScene._blockEntityCollection = false; - animation._babylonAnimationGroup = babylonAnimationGroup; + animation._babylonAnimationGroup = group; } const babylonAnimationGroup = animation._babylonAnimationGroup ; @@ -138,9 +114,10 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { * @param animationContext The context of the animation when loading the asset * @param animation The glTF animation property * @param channel The glTF animation channel property + * @param animationTargetOverride The babylon animation channel target override property. My be null. * @returns A void promise when the channel load is complete */ - public _loadAnimationChannelAsync(context: string, animationContext: string, animation: IAnimation, channel: IAnimationChannel): Promise { + public _loadAnimationChannelAsync(context: string, animationContext: string, animation: IAnimation, channel: IAnimationChannel, animationTargetOverride: Nullable = null): Promise { if (channel.target.path != AnimationChannelTargetPath.POINTER) { throw new Error(`${context}/target/path: Invalid value (${channel.target.path})`); } @@ -168,9 +145,10 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { // build the animations into the group const babylonAnimationGroup = animation._babylonAnimationGroup; if (babylonAnimationGroup) { + + const outputAccessor = ArrayItem.Get(`${context}/output`, this._loader.gltf.accessors, sampler.output); // stride is the size of each element stored into the output buffer. - // stride is the sum of property size or as per defined into the info. - const stride = animationTarget.stride ?? KHR_animation_pointer.GetAnimationOutputStride(animationTarget.properties); + const stride = animationTarget.stride ?? KHR_animation_pointer.GetAnimationOutputStride(outputAccessor.type); const fps = this._loader.parent.targetFps; // we extract the corresponding values from the readed value. @@ -230,7 +208,7 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { // each properties has its own build animation process. // these logics are located into KHR_animation_pointer.map.ts - propertyInfo.buildAnimations(animationTarget.target, fps, keys, babylonAnimationGroup); + propertyInfo.buildAnimations(animationTarget.target, fps, keys, babylonAnimationGroup, animationTargetOverride, animationTarget.params); } } } @@ -304,7 +282,8 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { // we have a least 3 part if (parts.length >= 3) { let node = CoreAnimationPointerMap; // the map of possible path - let index: string = ""; + const indices = []; + let getTarget:Nullable = null ; for (let i = 0; i < parts.length; i++) { const part = parts[i]; node = node[part]; @@ -314,20 +293,30 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { break; } - if (node.hasIndex) { - index = parts[++i]; + if( node.getTarget){ + getTarget = node.getTarget ; + } + + if (node.hasIndex ) { + indices.push(parts[++i]); + // move to the next part + continue; + } + + if (node.isIndex ) { + indices.push(part); // move to the next part continue; } - if (node.getTarget) { - // this is a leaf - const t = node.getTarget(this._loader.gltf, index); + if (node.properties && getTarget) { + const t = getTarget(this._loader.gltf, indices[0]); if (t != null) { return { target: t, - stride: node.getStride ? node.getStride(t) : KHR_animation_pointer.GetAnimationOutputStride(node.properties), + stride: node.getStride ? node.getStride(t) : undefined, properties: node.properties, + params : indices, }; } } diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts index e49e9667876..6174e1653a6 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts @@ -30,7 +30,8 @@ export class KHR_lights implements IGLTFLoaderExtension { */ public enabled: boolean; - private _loader: GLTFLoader; + /** hidden */ + public _loader: GLTFLoader; private _lights?: IKHRLightsPunctual_Light[]; /** diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts index 97b42814672..ce6179e0675 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts @@ -122,7 +122,10 @@ export class ArrayItem { * The glTF 2.0 loader */ export class GLTFLoader implements IGLTFLoader { + /** @hidden */ + // note : KHR_animation_pointer is used to load animation in ALL case, turning everything + // into pointer. This is the reason why this value is located here. public static readonly _KHRAnimationPointerName = "KHR_animation_pointer"; /** @hidden */ From 20f2905e38aea8bf2542673ad0c5cb44d3e79161 Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Tue, 21 Jun 2022 20:01:59 +0200 Subject: [PATCH 04/16] add support for camera to animation pointer --- .../Extensions/KHR_animation_pointer.map.ts | 202 +++++++++++++----- .../2.0/Extensions/KHR_animation_pointer.ts | 62 +++--- .../dev/loaders/src/glTF/2.0/glTFLoader.ts | 1 - 3 files changed, 188 insertions(+), 77 deletions(-) diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts index 26bff2268d0..5863d7a59a4 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts @@ -50,6 +50,16 @@ const _getGltfMaterial: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { return null; }; +const _getGltfCamera: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { + if (gltf.cameras) { + const i = _parseIntIndex(index); + if (i != -1) { + return gltf.cameras[i]; + } + } + return null; +}; + const _getGltfExtension: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { if (gltf.extensions && index) { return gltf.extensions[index]; @@ -81,6 +91,10 @@ const _getFloat: GetValueFn = (_target: any, source: Float32Array, offset: numbe return scale ? source[offset] * scale : source[offset]; }; +const _getMinusFloat: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { + return -(scale ? source[offset] * scale : source[offset]); +}; + const _getWeights: GetValueFn = (target: any, source: Float32Array, offset: number, scale?: number) => { if (target._numMorphTargets) { const value = new Array(target._numMorphTargets!); @@ -125,7 +139,7 @@ abstract class AbstractAnimationPointerPropertyInfos implements IAnimationPointe } class TransformNodeAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { - public constructor(type: number, name: string, get: GetValueFn) { + public constructor(type: number, name: string, get: GetValueFn = _getVector3) { super(type, name, get); } public isValid(target: any): boolean { @@ -137,8 +151,22 @@ class TransformNodeAnimationPointerPropertyInfos extends AbstractAnimationPointe } } +class CameraAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { + public constructor(type: number, name: string, get: GetValueFn = _getFloat) { + super(type, name, get); + } + + public isValid(_target: any): boolean { + return true; + } + + public buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup, animationTargetOverride: Nullable = null): void { + return this._buildAnimation(target, fps, keys, group, animationTargetOverride); + } +} + class MaterialAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { - public constructor(type: number, name: string, get: GetValueFn, public fillMode: any = Material.TriangleFillMode) { + public constructor(type: number, name: string, get: GetValueFn = _getFloat, public fillMode: any = Material.TriangleFillMode) { super(type, name, get); } @@ -159,7 +187,7 @@ class MaterialAnimationPointerPropertyInfos extends AbstractAnimationPointerProp } class LightAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { - public constructor(type: number, name: string, get: GetValueFn, public fillMode: any = Material.TriangleFillMode) { + public constructor(type: number, name: string, get: GetValueFn = _getFloat) { super(type, name, get); } @@ -170,13 +198,13 @@ class LightAnimationPointerPropertyInfos extends AbstractAnimationPointerPropert // note : the extensions array store directly the BabylonLight reference public buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup, animationTargetOverride: Nullable = null, params: any): void { const i = _parseIntIndex(params[1]); - const l = i >= 0 && i < target.lights.length ? target.lights[i] : null ; + const l = i >= 0 && i < target.lights.length ? target.lights[i] : null; return this._buildAnimation(l, fps, keys, group, animationTargetOverride); } } class WeightAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { - public constructor(type: number, name: string, get: GetValueFn) { + public constructor(type: number, name: string, get: GetValueFn = _getWeights) { super(type, name, get); } public isValid(target: any): boolean { @@ -223,27 +251,62 @@ const CoreAnimationNodesPointerMap: any = { getTarget: _getGltfNode, hasIndex: true, matrix: { - properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_MATRIX, "matrix", _getMatrix)], + properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_MATRIX, "matrix", _getMatrix)], }, translation: { - properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_VECTOR3, "position", _getVector3)], + properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_VECTOR3, "position")], }, rotation: { properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_QUATERNION, "rotationQuaternion", _getQuaternion)], }, scale: { - properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_VECTOR3, "scaling", _getVector3)], + properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_VECTOR3, "scaling")], }, weights: { getStride: (target: any) => { return target._numMorphTargets; }, - properties: [new WeightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "influence", _getWeights)], + properties: [new WeightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "influence")], }, }; const CoreAnimationCamerasPointerMap: any = { hasIndex: true, + getTarget: _getGltfCamera, + orthographic: { + xmag: { + properties: [ + new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoLeft", _getMinusFloat), + new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoRight"), + ], + }, + ymag: { + properties: [ + new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoBottom", _getMinusFloat), + new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoTop"), + ], + }, + zfar: { + properties: [new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "maxZ")], + }, + znear: { + properties: [new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "minZ")], + }, + }, + perspective: { + aspectRatio: { + // not supported. + }, + yfov: { + properties: [new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "fov")], + }, + zfar: { + properties: [new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "maxZ")], + }, + znear: { + properties: [new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "minZ")], + }, + }, }; const CoreAnimationMaterialsPointerMap: any = { @@ -257,116 +320,155 @@ const CoreAnimationMaterialsPointerMap: any = { ], }, metallicFactor: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "metallic", _getFloat)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "metallic")], }, roughnessFactor: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "roughness", _getFloat)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "roughness")], }, baseColorTexture: { extensions: { KHR_texture_transform: { scale: { - properties: [ + properties: [ // MAY introduce set scale(Vector2) into texture. - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.uScale", _getFloat), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.vScale", _getFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.uScale"), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.vScale"), ], }, offset: { - properties: [ + properties: [ // MAY introduce set offset(Vector2) into texture. - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.uOffset", _getFloat), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.vOffset", _getFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.uOffset"), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.vOffset"), ], }, + rotation: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.wAng", _getMinusFloat)], + }, }, }, }, }, emissiveFactor: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "emissiveColor", _getColor3)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "emissiveColor", _getColor3)], }, normalTexture: { scale: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "bumpTexture.level", _getFloat)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "bumpTexture.level")], }, }, occlusionTexture: { strength: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTextureStrength", _getFloat)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTextureStrength")], }, extensions: { KHR_texture_transform: { scale: { properties: [ // MAY introduce set scale(Vector2) into texture. - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.uScale", _getFloat), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.vScale", _getFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.uScale"), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.vScale"), ], }, offset: { properties: [ // MAY introduce set offset(Vector2) into texture. - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.uOffset", _getFloat), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.vOffset", _getFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.uOffset"), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.vOffset"), ], }, + rotation: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.wAng", _getMinusFloat)], + }, }, - }, }, + }, + }, emissiveTexture: { extensions: { KHR_texture_transform: { scale: { properties: [ // MAY introduce set scale(Vector2) into texture. - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.uScale", _getFloat), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.vScale", _getFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.uScale"), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.vScale"), ], }, offset: { properties: [ // MAY introduce set offset(Vector2) into texture. - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.uOffset", _getFloat), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.vOffset", _getFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.uOffset"), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.vOffset"), ], }, + rotation: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.wAng", _getMinusFloat)], + }, }, }, }, extensions: { + KHR_materials_ior: { + ior: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "indexOfRefraction")], + }, + }, + KHR_materials_clearcoat: { + clearcoatFactor: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "clearCoat.intensity")], + }, + clearcoatRoughnessFactor: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "clearCoat.roughness")], + }, + }, + KHR_materials_sheen: { + sheenColorFactor: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "sheen.color", _getColor3)], + }, + sheenRoughnessFactor: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "sheen.roughness")], + }, + }, + KHR_materials_specular: { + specularFactor: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "metallicF0Factor")], + }, + specularColorFactor: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "metallicReflectanceColor", _getColor3)], + }, + }, KHR_materials_emissive_strength: { emissiveStrength: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveIntensity", _getFloat)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveIntensity")], }, }, KHR_materials_transmission: { transmissionFactor: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "subSurface.refractionIntensity", _getFloat)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "subSurface.refractionIntensity")], }, }, KHR_materials_volume: { - attenuationColor:{ + attenuationColor: { properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "subSurface.tintColor", _getColor3)], }, attenuationDistance: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "subSurface.tintColorAtDistance", _getFloat)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "subSurface.tintColorAtDistance")], }, thicknessFactor: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "subSurface.maximumThickness", _getFloat)], - }, + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "subSurface.maximumThickness")], + }, }, - KHR_materials_iridescence :{ - iridescenceFactor :{ - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "iridescence.intensity", _getFloat)], + KHR_materials_iridescence: { + iridescenceFactor: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "iridescence.intensity")], }, - iridescenceIor :{ - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "iridescence.indexOfRefraction", _getFloat)], + iridescenceIor: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "iridescence.indexOfRefraction")], }, - iridescenceThicknessMinimum :{ - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "iridescence.minimumThickness", _getFloat)], + iridescenceThicknessMinimum: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "iridescence.minimumThickness")], }, - iridescenceThicknessMaximum :{ - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "iridescence.maximumThickness", _getFloat)], + iridescenceThicknessMaximum: { + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "iridescence.maximumThickness")], }, }, }, @@ -379,24 +481,24 @@ const CoreAnimationExtensionsPointerMap: any = { lights: { hasIndex: true, // we have an array of light into the extension. color: { - properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "diffuseColor", _getColor3)], + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "diffuse", _getColor3)], }, intensity: { - properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "intensity", _getFloat)], + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "intensity")], }, range: { - properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "range", _getFloat)], + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "range")], }, spot: { innerConeAngle: { - properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "innerAngle", _getFloat)], + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "innerAngle")], }, outerConeAngle: { - properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "angle", _getFloat)], + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "angle")], }, }, }, - } + }, }; export const CoreAnimationPointerMap: any = { diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts index d0134fc1b2c..dea5cd3e966 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts @@ -3,7 +3,7 @@ import { ArrayItem, GLTFLoader } from "../glTFLoader"; import type { Nullable } from "core/types"; import { AnimationGroup } from "core/Animations/animationGroup"; import type { IAnimatable } from "core/Animations/animatable.interface"; -import type { IAnimation, IAnimationChannel, _IAnimationSamplerData, IAnimationSampler} from "../glTFLoaderInterfaces"; +import type { IAnimation, IAnimationChannel, _IAnimationSamplerData, IAnimationSampler } from "../glTFLoaderInterfaces"; import { AccessorType, AnimationChannelTargetPath, AnimationSamplerInterpolation } from "babylonjs-gltf2interface"; import { AnimationKeyInterpolation } from "core/Animations/animationKey"; @@ -15,7 +15,7 @@ interface IAnimationChannelTarget { stride?: number; target: any; properties: Array; - params:any; + params: any; } /** @@ -33,16 +33,22 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { * @param type the accessor type * @returns the number of item to be gather at each keyframe */ - static GetAnimationOutputStride(type:AccessorType): number { - switch (type) { - case AccessorType.SCALAR: return 1; - case AccessorType.VEC2: return 2; - case AccessorType.VEC3: return 3; - case AccessorType.VEC4: - case AccessorType.MAT2: return 4; - case AccessorType.MAT3: return 9; - case AccessorType.MAT4: return 16; - } + static _GetAnimationOutputStride(type: AccessorType): number { + switch (type) { + case AccessorType.SCALAR: + return 1; + case AccessorType.VEC2: + return 2; + case AccessorType.VEC3: + return 3; + case AccessorType.VEC4: + case AccessorType.MAT2: + return 4; + case AccessorType.MAT3: + return 9; + case AccessorType.MAT4: + return 16; + } } /** * The name of this extension. @@ -83,16 +89,15 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { } public loadAnimationAsync(context: string, animation: IAnimation): Nullable> { - // ensure an animation group is present. - if(!animation._babylonAnimationGroup){ + if (!animation._babylonAnimationGroup) { this._loader.babylonScene._blockEntityCollection = !!this._loader._assetContainer; const group = new AnimationGroup(animation.name || `animation${animation.index}`, this._loader.babylonScene); group._parentContainer = this._loader._assetContainer; this._loader.babylonScene._blockEntityCollection = false; animation._babylonAnimationGroup = group; } - const babylonAnimationGroup = animation._babylonAnimationGroup ; + const babylonAnimationGroup = animation._babylonAnimationGroup; const promises = new Array>(); ArrayItem.Assign(animation.channels); @@ -114,10 +119,16 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { * @param animationContext The context of the animation when loading the asset * @param animation The glTF animation property * @param channel The glTF animation channel property - * @param animationTargetOverride The babylon animation channel target override property. My be null. + * @param animationTargetOverride The babylon animation channel target override property. My be null. * @returns A void promise when the channel load is complete */ - public _loadAnimationChannelAsync(context: string, animationContext: string, animation: IAnimation, channel: IAnimationChannel, animationTargetOverride: Nullable = null): Promise { + public _loadAnimationChannelAsync( + context: string, + animationContext: string, + animation: IAnimation, + channel: IAnimationChannel, + animationTargetOverride: Nullable = null + ): Promise { if (channel.target.path != AnimationChannelTargetPath.POINTER) { throw new Error(`${context}/target/path: Invalid value (${channel.target.path})`); } @@ -145,10 +156,9 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { // build the animations into the group const babylonAnimationGroup = animation._babylonAnimationGroup; if (babylonAnimationGroup) { - const outputAccessor = ArrayItem.Get(`${context}/output`, this._loader.gltf.accessors, sampler.output); // stride is the size of each element stored into the output buffer. - const stride = animationTarget.stride ?? KHR_animation_pointer.GetAnimationOutputStride(outputAccessor.type); + const stride = animationTarget.stride ?? KHR_animation_pointer._GetAnimationOutputStride(outputAccessor.type); const fps = this._loader.parent.targetFps; // we extract the corresponding values from the readed value. @@ -283,7 +293,7 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { if (parts.length >= 3) { let node = CoreAnimationPointerMap; // the map of possible path const indices = []; - let getTarget:Nullable = null ; + let getTarget: Nullable = null; for (let i = 0; i < parts.length; i++) { const part = parts[i]; node = node[part]; @@ -293,17 +303,17 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { break; } - if( node.getTarget){ - getTarget = node.getTarget ; + if (node.getTarget) { + getTarget = node.getTarget; } - if (node.hasIndex ) { + if (node.hasIndex) { indices.push(parts[++i]); // move to the next part continue; } - - if (node.isIndex ) { + + if (node.isIndex) { indices.push(part); // move to the next part continue; @@ -316,7 +326,7 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { target: t, stride: node.getStride ? node.getStride(t) : undefined, properties: node.properties, - params : indices, + params: indices, }; } } diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts index ce6179e0675..ee7d22a80d1 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts @@ -122,7 +122,6 @@ export class ArrayItem { * The glTF 2.0 loader */ export class GLTFLoader implements IGLTFLoader { - /** @hidden */ // note : KHR_animation_pointer is used to load animation in ALL case, turning everything // into pointer. This is the reason why this value is located here. From e645d360265ac210c20d0fe787161cc0128e7bb3 Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Tue, 21 Jun 2022 23:30:34 +0200 Subject: [PATCH 05/16] add ref to babylon object into Gltf objects ICamera and IPonctualLight --- .../2.0/Extensions/KHR_animation_pointer.map.ts | 16 ++++++++++------ .../glTF/2.0/Extensions/KHR_lights_punctual.ts | 9 +++++---- packages/dev/loaders/src/glTF/2.0/glTFLoader.ts | 2 ++ .../loaders/src/glTF/2.0/glTFLoaderInterfaces.ts | 15 ++++++++++++++- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts index 5863d7a59a4..20560a4a8e3 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts @@ -95,6 +95,10 @@ const _getMinusFloat: GetValueFn = (_target: any, source: Float32Array, offset: return -(scale ? source[offset] * scale : source[offset]); }; +const _getFloat2: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { + return (scale ? source[offset] * scale : source[offset]) * 2; +}; + const _getWeights: GetValueFn = (target: any, source: Float32Array, offset: number, scale?: number) => { if (target._numMorphTargets) { const value = new Array(target._numMorphTargets!); @@ -156,12 +160,12 @@ class CameraAnimationPointerPropertyInfos extends AbstractAnimationPointerProper super(type, name, get); } - public isValid(_target: any): boolean { - return true; + public isValid(target: any): boolean { + return target._babylonCamera != null; } public buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup, animationTargetOverride: Nullable = null): void { - return this._buildAnimation(target, fps, keys, group, animationTargetOverride); + return this._buildAnimation(target._babylonCamera, fps, keys, group, animationTargetOverride); } } @@ -199,7 +203,7 @@ class LightAnimationPointerPropertyInfos extends AbstractAnimationPointerPropert public buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup, animationTargetOverride: Nullable = null, params: any): void { const i = _parseIntIndex(params[1]); const l = i >= 0 && i < target.lights.length ? target.lights[i] : null; - return this._buildAnimation(l, fps, keys, group, animationTargetOverride); + return this._buildAnimation(l._babylonLight, fps, keys, group, animationTargetOverride); } } @@ -491,10 +495,10 @@ const CoreAnimationExtensionsPointerMap: any = { }, spot: { innerConeAngle: { - properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "innerAngle")], + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "innerAngle", _getFloat2 )], }, outerConeAngle: { - properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "angle")], + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "angle", _getFloat2 )], }, }, }, diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts index 6174e1653a6..866b2045621 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts @@ -8,9 +8,9 @@ import { SpotLight } from "core/Lights/spotLight"; import { Light } from "core/Lights/light"; import type { TransformNode } from "core/Meshes/transformNode"; -import type { IKHRLightsPunctual_LightReference, IKHRLightsPunctual_Light, IKHRLightsPunctual } from "babylonjs-gltf2interface"; +import type { IKHRLightsPunctual_LightReference} from "babylonjs-gltf2interface"; import { KHRLightsPunctual_LightType } from "babylonjs-gltf2interface"; -import type { INode } from "../glTFLoaderInterfaces"; +import type { INode, IKHRLight } from "../glTFLoaderInterfaces"; import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; import { GLTFLoader, ArrayItem } from "../glTFLoader"; @@ -32,7 +32,7 @@ export class KHR_lights implements IGLTFLoaderExtension { /** hidden */ public _loader: GLTFLoader; - private _lights?: IKHRLightsPunctual_Light[]; + private _lights?: IKHRLight[]; /** * @param loader @@ -53,7 +53,7 @@ export class KHR_lights implements IGLTFLoaderExtension { public onLoading(): void { const extensions = this._loader.gltf.extensions; if (extensions && extensions[this.name]) { - const extension = extensions[this.name] as IKHRLightsPunctual; + const extension = extensions[this.name] as any; this._lights = extension.lights; } } @@ -105,6 +105,7 @@ export class KHR_lights implements IGLTFLoaderExtension { babylonLight.parent = babylonMesh; this._loader._babylonLights.push(babylonLight); + light._babylonLight = babylonLight; GLTFLoader.AddPointerMetadata(babylonLight, extensionContext); diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts index ee7d22a80d1..7871e87de95 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts @@ -1460,6 +1460,8 @@ export class GLTFLoader implements IGLTFLoader { GLTFLoader.AddPointerMetadata(babylonCamera, context); this._parent.onCameraLoadedObservable.notifyObservers(babylonCamera); assign(babylonCamera); + // register the camera to be used later. + camera._babylonCamera = babylonCamera; this.logClose(); diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts index 763ea2ecb07..5c094f979f2 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts @@ -5,6 +5,8 @@ import type { TransformNode } from "core/Meshes/transformNode"; import type { Buffer, VertexBuffer } from "core/Buffers/buffer"; import type { AbstractMesh } from "core/Meshes/abstractMesh"; import type { Mesh } from "core/Meshes/mesh"; +import { Camera } from "core/Cameras/camera"; +import { Light } from "core/Lights/light"; import type * as GLTF2 from "babylonjs-gltf2interface"; @@ -83,7 +85,18 @@ export interface IBufferView extends GLTF2.IBufferView, IArrayItem { /** * Loader interface with additional members. */ -export interface ICamera extends GLTF2.ICamera, IArrayItem {} +export interface IKHRLight extends GLTF2.IKHRLightsPunctual_Light { + /** @hidden */ + _babylonLight: Light; +} + +/** + * Loader interface with additional members. + */ +export interface ICamera extends GLTF2.ICamera, IArrayItem { + /** @hidden */ + _babylonCamera: Camera; +} /** * Loader interface with additional members. From f9b38d73a6e566352a945c67f11e10b60caffd4f Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Mon, 27 Jun 2022 16:08:15 +0200 Subject: [PATCH 06/16] add some base for animation pointer export --- .../Extensions/KHR_animation_pointer.map.ts | 31 +++++++------------ .../serializers/src/glTF/2.0/glTFAnimation.ts | 2 ++ 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts index 20560a4a8e3..7d6f20e8fac 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts @@ -3,10 +3,9 @@ import { Animation } from "core/Animations/animation"; import type { AnimationGroup } from "core/Animations/animationGroup"; import { Quaternion, Vector3, Matrix } from "core/Maths/math.vector"; import { Color3 } from "core/Maths/math.color"; -import type { IGLTF, INode } from "../glTFLoaderInterfaces"; +import type { IGLTF } from "../glTFLoaderInterfaces"; import { Material } from "core/Materials/material"; import type { IAnimatable } from "core/Animations/animatable.interface"; -import type { AbstractMesh } from "core/Meshes/abstractMesh"; import { Mesh } from "core/Meshes/mesh"; import type { Nullable } from "core/types"; @@ -161,7 +160,7 @@ class CameraAnimationPointerPropertyInfos extends AbstractAnimationPointerProper } public isValid(target: any): boolean { - return target._babylonCamera != null; + return target._babylonCamera != null && target._babylonCamera !== undefined; } public buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup, animationTargetOverride: Nullable = null): void { @@ -229,23 +228,17 @@ class WeightAnimationPointerPropertyInfos extends AbstractAnimationPointerProper })) ); - this._forEachPrimitive(targetNode, (babylonAbstractMesh: AbstractMesh) => { - const babylonMesh = babylonAbstractMesh as Mesh; - if (babylonMesh.morphTargetManager) { - const morphTarget = babylonMesh.morphTargetManager.getTarget(targetIndex); - const babylonAnimationClone = babylonAnimation.clone(); - morphTarget.animations.push(babylonAnimationClone); - babylonAnimationGroup.addTargetedAnimation(babylonAnimationClone, morphTarget); + if (targetNode._primitiveBabylonMeshes) { + for (const m of targetNode._primitiveBabylonMeshes) { + const babylonMesh = m as Mesh; + if (babylonMesh.morphTargetManager) { + const morphTarget = babylonMesh.morphTargetManager.getTarget(targetIndex); + const babylonAnimationClone = babylonAnimation.clone(); + morphTarget.animations.push(babylonAnimationClone); + babylonAnimationGroup.addTargetedAnimation(babylonAnimationClone, morphTarget); + } } - }); - } - } - } - - private _forEachPrimitive(node: INode, callback: (babylonMesh: AbstractMesh) => void): void { - if (node._primitiveBabylonMeshes) { - for (const babylonMesh of node._primitiveBabylonMeshes) { - callback(babylonMesh); + } } } } diff --git a/packages/dev/serializers/src/glTF/2.0/glTFAnimation.ts b/packages/dev/serializers/src/glTF/2.0/glTFAnimation.ts index 0cdcc60e1a5..f50d59d8fd8 100644 --- a/packages/dev/serializers/src/glTF/2.0/glTFAnimation.ts +++ b/packages/dev/serializers/src/glTF/2.0/glTFAnimation.ts @@ -463,6 +463,8 @@ export class _GLTFAnimation { } } } + } else { + // this is the place for the KHR_animation_pointer. } } morphAnimationMeshes.forEach((mesh) => { From cd1c4f764fd926f7de9076a0cb90f6b65ea65861 Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Tue, 19 Jul 2022 22:28:41 +0200 Subject: [PATCH 07/16] Update KHR_animation_pointer.map.ts --- .../Extensions/KHR_animation_pointer.map.ts | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts index 7d6f20e8fac..0e66363ee3e 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts @@ -94,7 +94,11 @@ const _getMinusFloat: GetValueFn = (_target: any, source: Float32Array, offset: return -(scale ? source[offset] * scale : source[offset]); }; -const _getFloat2: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { +const _getNextFloat: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { + return (scale ? source[offset + 1] * scale : source[offset + 1]); +}; + +const _getFloatBy2: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { return (scale ? source[offset] * scale : source[offset]) * 2; }; @@ -274,13 +278,13 @@ const CoreAnimationCamerasPointerMap: any = { xmag: { properties: [ new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoLeft", _getMinusFloat), - new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoRight"), + new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoRight", _getNextFloat), ], }, ymag: { properties: [ new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoBottom", _getMinusFloat), - new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoTop"), + new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoTop", _getNextFloat), ], }, zfar: { @@ -329,14 +333,14 @@ const CoreAnimationMaterialsPointerMap: any = { properties: [ // MAY introduce set scale(Vector2) into texture. new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.uScale"), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.vScale"), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.vScale", _getNextFloat), ], }, offset: { properties: [ // MAY introduce set offset(Vector2) into texture. new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.uOffset"), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.vOffset"), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.vOffset", _getNextFloat), ], }, rotation: { @@ -364,14 +368,14 @@ const CoreAnimationMaterialsPointerMap: any = { properties: [ // MAY introduce set scale(Vector2) into texture. new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.uScale"), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.vScale"), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.vScale", _getNextFloat), ], }, offset: { properties: [ // MAY introduce set offset(Vector2) into texture. new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.uOffset"), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.vOffset"), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.vOffset", _getNextFloat), ], }, rotation: { @@ -387,14 +391,14 @@ const CoreAnimationMaterialsPointerMap: any = { properties: [ // MAY introduce set scale(Vector2) into texture. new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.uScale"), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.vScale"), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.vScale", _getNextFloat), ], }, offset: { properties: [ // MAY introduce set offset(Vector2) into texture. new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.uOffset"), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.vOffset"), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.vOffset", _getNextFloat), ], }, rotation: { @@ -488,10 +492,10 @@ const CoreAnimationExtensionsPointerMap: any = { }, spot: { innerConeAngle: { - properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "innerAngle", _getFloat2 )], + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "innerAngle", _getFloatBy2 )], }, outerConeAngle: { - properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "angle", _getFloat2 )], + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "angle", _getFloatBy2 )], }, }, }, From 01a6df9aaa2543a7801ed31540e5077ed58a8d70 Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Tue, 19 Jul 2022 22:39:09 +0200 Subject: [PATCH 08/16] formating --- .../src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts index 0e66363ee3e..bd3e1da4a93 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts @@ -95,7 +95,7 @@ const _getMinusFloat: GetValueFn = (_target: any, source: Float32Array, offset: }; const _getNextFloat: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { - return (scale ? source[offset + 1] * scale : source[offset + 1]); + return scale ? source[offset + 1] * scale : source[offset + 1]; }; const _getFloatBy2: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { @@ -492,10 +492,10 @@ const CoreAnimationExtensionsPointerMap: any = { }, spot: { innerConeAngle: { - properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "innerAngle", _getFloatBy2 )], + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "innerAngle", _getFloatBy2)], }, outerConeAngle: { - properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "angle", _getFloatBy2 )], + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "angle", _getFloatBy2)], }, }, }, From d7501661f618abfc11a11f4be4a343c708bdb518 Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Tue, 19 Jul 2022 23:10:35 +0200 Subject: [PATCH 09/16] formatting --- .../src/glTF/2.0/Extensions/KHR_lights_punctual.ts | 2 +- packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts index 866b2045621..fdad2fc4b8c 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts @@ -8,7 +8,7 @@ import { SpotLight } from "core/Lights/spotLight"; import { Light } from "core/Lights/light"; import type { TransformNode } from "core/Meshes/transformNode"; -import type { IKHRLightsPunctual_LightReference} from "babylonjs-gltf2interface"; +import type { IKHRLightsPunctual_LightReference } from "babylonjs-gltf2interface"; import { KHRLightsPunctual_LightType } from "babylonjs-gltf2interface"; import type { INode, IKHRLight } from "../glTFLoaderInterfaces"; import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts index 5c094f979f2..055e2ee53b8 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts @@ -86,16 +86,16 @@ export interface IBufferView extends GLTF2.IBufferView, IArrayItem { * Loader interface with additional members. */ export interface IKHRLight extends GLTF2.IKHRLightsPunctual_Light { - /** @hidden */ - _babylonLight: Light; + /** @hidden */ + _babylonLight: Light; } /** * Loader interface with additional members. */ export interface ICamera extends GLTF2.ICamera, IArrayItem { - /** @hidden */ - _babylonCamera: Camera; + /** @hidden */ + _babylonCamera: Camera; } /** From c08cd0d273f954449bd9dd72d18af15d22da0ea6 Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Tue, 19 Jul 2022 23:34:56 +0200 Subject: [PATCH 10/16] linting --- .../src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts | 2 +- .../loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts | 3 ++- packages/public/glTF2Interface/babylon.glTF2Interface.d.ts | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts index bd3e1da4a93..47cff46cc87 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts @@ -6,7 +6,7 @@ import { Color3 } from "core/Maths/math.color"; import type { IGLTF } from "../glTFLoaderInterfaces"; import { Material } from "core/Materials/material"; import type { IAnimatable } from "core/Animations/animatable.interface"; -import { Mesh } from "core/Meshes/mesh"; +import type { Mesh } from "core/Meshes/mesh"; import type { Nullable } from "core/types"; export type GetGltfNodeTargetFn = (source: IGLTF, indices: string) => any; diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts index dea5cd3e966..556dcb5c0e1 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts @@ -7,7 +7,8 @@ import type { IAnimation, IAnimationChannel, _IAnimationSamplerData, IAnimationS import { AccessorType, AnimationChannelTargetPath, AnimationSamplerInterpolation } from "babylonjs-gltf2interface"; import { AnimationKeyInterpolation } from "core/Animations/animationKey"; -import { GetGltfNodeTargetFn, CoreAnimationPointerMap, IAnimationPointerPropertyInfos } from "./KHR_animation_pointer.map"; +import { CoreAnimationPointerMap } from "./KHR_animation_pointer.map"; +import type { GetGltfNodeTargetFn, IAnimationPointerPropertyInfos } from "./KHR_animation_pointer.map"; const NAME = GLTFLoader._KHRAnimationPointerName; diff --git a/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts b/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts index 9fb6eaf9da2..ead9c4e5be3 100644 --- a/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts +++ b/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts @@ -73,7 +73,7 @@ declare module BABYLON.GLTF2 { } /** - * The name of the node's TRS property to modify, + * The name of the node's TRS property to modify, * or the weights of the Morph Targets it instantiates, * or pointer is use of KHR_animation_pointer extension */ @@ -93,7 +93,7 @@ declare module BABYLON.GLTF2 { /** * Weights */ - WEIGHTS = "weights", + WEIGHTS = "weights", /** * Pointer */ From cd86a5c23077b3553543cd2fda0f55dd5505ab9d Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Tue, 19 Jul 2022 23:51:17 +0200 Subject: [PATCH 11/16] linting --- packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts index 055e2ee53b8..ad02a95fb1a 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts @@ -5,8 +5,8 @@ import type { TransformNode } from "core/Meshes/transformNode"; import type { Buffer, VertexBuffer } from "core/Buffers/buffer"; import type { AbstractMesh } from "core/Meshes/abstractMesh"; import type { Mesh } from "core/Meshes/mesh"; -import { Camera } from "core/Cameras/camera"; -import { Light } from "core/Lights/light"; +import type { Camera } from "core/Cameras/camera"; +import type { Light } from "core/Lights/light"; import type * as GLTF2 from "babylonjs-gltf2interface"; From 8fa6c617edbdb4dbe04bf3355480483e702a47e5 Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Thu, 21 Jul 2022 12:07:55 +0200 Subject: [PATCH 12/16] minor adjustment --- .../Extensions/KHR_animation_pointer.map.ts | 107 +++++++++--------- .../2.0/Extensions/KHR_animation_pointer.ts | 31 +---- .../2.0/Extensions/KHR_lights_punctual.ts | 2 +- .../loaders/src/glTF/2.0/Extensions/index.ts | 1 - .../dev/loaders/src/glTF/2.0/glTFUtilities.ts | 30 +++++ .../babylon.glTF2Interface.d.ts | 2 +- 6 files changed, 90 insertions(+), 83 deletions(-) create mode 100644 packages/dev/loaders/src/glTF/2.0/glTFUtilities.ts diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts index 47cff46cc87..d2c288225e0 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.map.ts @@ -10,8 +10,7 @@ import type { Mesh } from "core/Meshes/mesh"; import type { Nullable } from "core/types"; export type GetGltfNodeTargetFn = (source: IGLTF, indices: string) => any; -export type GetValueFn = (target: any, source: Float32Array, offset: number, scale?: number) => any; -export type GetStrideFn = (target: any) => number; +type GetValueFn = (target: any, source: Float32Array, offset: number, scale?: number) => any; export interface IAnimationPointerPropertyInfos { type: number; @@ -21,7 +20,7 @@ export interface IAnimationPointerPropertyInfos { buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup, animationTargetOverride: Nullable, params?: any): void; } -const _parseIntIndex = (str: string) => { +const parseIntIndex = (str: string) => { const targetIndex = parseInt(str); if (isNaN(targetIndex)) { return -1; @@ -29,9 +28,9 @@ const _parseIntIndex = (str: string) => { return targetIndex; }; -const _getGltfNode: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { +const getGltfNode: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { if (gltf.nodes) { - const i = _parseIntIndex(index); + const i = parseIntIndex(index); if (i != -1) { return gltf.nodes[i]; } @@ -39,9 +38,9 @@ const _getGltfNode: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { return null; }; -const _getGltfMaterial: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { +const getGltfMaterial: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { if (gltf.materials) { - const i = _parseIntIndex(index); + const i = parseIntIndex(index); if (i != -1) { return gltf.materials[i]; } @@ -49,9 +48,9 @@ const _getGltfMaterial: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { return null; }; -const _getGltfCamera: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { +const getGltfCamera: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { if (gltf.cameras) { - const i = _parseIntIndex(index); + const i = parseIntIndex(index); if (i != -1) { return gltf.cameras[i]; } @@ -59,50 +58,50 @@ const _getGltfCamera: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { return null; }; -const _getGltfExtension: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { +const getGltfExtension: GetGltfNodeTargetFn = (gltf: IGLTF, index: string) => { if (gltf.extensions && index) { return gltf.extensions[index]; } return null; }; -const _getMatrix: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { +const getMatrix: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { return scale ? Matrix.FromArray(source, offset).scale(scale) : Matrix.FromArray(source, offset); }; -const _getVector3: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { +const getVector3: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { return scale ? Vector3.FromArray(source, offset).scaleInPlace(scale) : Vector3.FromArray(source, offset); }; -const _getQuaternion: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { +const getQuaternion: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { return scale ? Quaternion.FromArray(source, offset).scaleInPlace(scale) : Quaternion.FromArray(source, offset); }; -const _getColor3: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { +const getColor3: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { return scale ? Color3.FromArray(source, offset).scale(scale) : Color3.FromArray(source, offset); }; -const _getAlpha: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { +const getAlpha: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { return scale ? source[offset + 3] * scale : source[offset + 3]; }; -const _getFloat: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { +const getFloat: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { return scale ? source[offset] * scale : source[offset]; }; -const _getMinusFloat: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { +const getMinusFloat: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { return -(scale ? source[offset] * scale : source[offset]); }; -const _getNextFloat: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { +const getNextFloat: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { return scale ? source[offset + 1] * scale : source[offset + 1]; }; -const _getFloatBy2: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { +const getFloatBy2: GetValueFn = (_target: any, source: Float32Array, offset: number, scale?: number) => { return (scale ? source[offset] * scale : source[offset]) * 2; }; -const _getWeights: GetValueFn = (target: any, source: Float32Array, offset: number, scale?: number) => { +const getWeights: GetValueFn = (target: any, source: Float32Array, offset: number, scale?: number) => { if (target._numMorphTargets) { const value = new Array(target._numMorphTargets!); for (let i = 0; i < target._numMorphTargets!; i++) { @@ -146,7 +145,7 @@ abstract class AbstractAnimationPointerPropertyInfos implements IAnimationPointe } class TransformNodeAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { - public constructor(type: number, name: string, get: GetValueFn = _getVector3) { + public constructor(type: number, name: string, get: GetValueFn = getVector3) { super(type, name, get); } public isValid(target: any): boolean { @@ -159,7 +158,7 @@ class TransformNodeAnimationPointerPropertyInfos extends AbstractAnimationPointe } class CameraAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { - public constructor(type: number, name: string, get: GetValueFn = _getFloat) { + public constructor(type: number, name: string, get: GetValueFn = getFloat) { super(type, name, get); } @@ -173,7 +172,7 @@ class CameraAnimationPointerPropertyInfos extends AbstractAnimationPointerProper } class MaterialAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { - public constructor(type: number, name: string, get: GetValueFn = _getFloat, public fillMode: any = Material.TriangleFillMode) { + public constructor(type: number, name: string, get: GetValueFn = getFloat, public fillMode: any = Material.TriangleFillMode) { super(type, name, get); } @@ -194,7 +193,7 @@ class MaterialAnimationPointerPropertyInfos extends AbstractAnimationPointerProp } class LightAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { - public constructor(type: number, name: string, get: GetValueFn = _getFloat) { + public constructor(type: number, name: string, get: GetValueFn = getFloat) { super(type, name, get); } @@ -204,14 +203,14 @@ class LightAnimationPointerPropertyInfos extends AbstractAnimationPointerPropert // note : the extensions array store directly the BabylonLight reference public buildAnimations(target: any, fps: number, keys: any[], group: AnimationGroup, animationTargetOverride: Nullable = null, params: any): void { - const i = _parseIntIndex(params[1]); + const i = parseIntIndex(params[1]); const l = i >= 0 && i < target.lights.length ? target.lights[i] : null; return this._buildAnimation(l._babylonLight, fps, keys, group, animationTargetOverride); } } class WeightAnimationPointerPropertyInfos extends AbstractAnimationPointerPropertyInfos { - public constructor(type: number, name: string, get: GetValueFn = _getWeights) { + public constructor(type: number, name: string, get: GetValueFn = getWeights) { super(type, name, get); } public isValid(target: any): boolean { @@ -249,16 +248,16 @@ class WeightAnimationPointerPropertyInfos extends AbstractAnimationPointerProper } const CoreAnimationNodesPointerMap: any = { - getTarget: _getGltfNode, + getTarget: getGltfNode, hasIndex: true, matrix: { - properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_MATRIX, "matrix", _getMatrix)], + properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_MATRIX, "matrix", getMatrix)], }, translation: { properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_VECTOR3, "position")], }, rotation: { - properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_QUATERNION, "rotationQuaternion", _getQuaternion)], + properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_QUATERNION, "rotationQuaternion", getQuaternion)], }, scale: { properties: [new TransformNodeAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_VECTOR3, "scaling")], @@ -273,18 +272,18 @@ const CoreAnimationNodesPointerMap: any = { const CoreAnimationCamerasPointerMap: any = { hasIndex: true, - getTarget: _getGltfCamera, + getTarget: getGltfCamera, orthographic: { xmag: { properties: [ - new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoLeft", _getMinusFloat), - new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoRight", _getNextFloat), + new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoLeft", getMinusFloat), + new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoRight", getNextFloat), ], }, ymag: { properties: [ - new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoBottom", _getMinusFloat), - new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoTop", _getNextFloat), + new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoBottom", getMinusFloat), + new CameraAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "orthoTop", getNextFloat), ], }, zfar: { @@ -312,12 +311,12 @@ const CoreAnimationCamerasPointerMap: any = { const CoreAnimationMaterialsPointerMap: any = { hasIndex: true, - getTarget: _getGltfMaterial, + getTarget: getGltfMaterial, pbrMetallicRoughness: { baseColorFactor: { properties: [ - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "albedoColor", _getColor3), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "alpha", _getAlpha), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "albedoColor", getColor3), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "alpha", getAlpha), ], }, metallicFactor: { @@ -333,25 +332,25 @@ const CoreAnimationMaterialsPointerMap: any = { properties: [ // MAY introduce set scale(Vector2) into texture. new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.uScale"), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.vScale", _getNextFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.vScale", getNextFloat), ], }, offset: { properties: [ // MAY introduce set offset(Vector2) into texture. new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.uOffset"), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.vOffset", _getNextFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.vOffset", getNextFloat), ], }, rotation: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.wAng", _getMinusFloat)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "albedoTexture.wAng", getMinusFloat)], }, }, }, }, }, emissiveFactor: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "emissiveColor", _getColor3)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "emissiveColor", getColor3)], }, normalTexture: { scale: { @@ -368,18 +367,18 @@ const CoreAnimationMaterialsPointerMap: any = { properties: [ // MAY introduce set scale(Vector2) into texture. new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.uScale"), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.vScale", _getNextFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.vScale", getNextFloat), ], }, offset: { properties: [ // MAY introduce set offset(Vector2) into texture. new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.uOffset"), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.vOffset", _getNextFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.vOffset", getNextFloat), ], }, rotation: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.wAng", _getMinusFloat)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "ambientTexture.wAng", getMinusFloat)], }, }, }, @@ -391,18 +390,18 @@ const CoreAnimationMaterialsPointerMap: any = { properties: [ // MAY introduce set scale(Vector2) into texture. new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.uScale"), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.vScale", _getNextFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.vScale", getNextFloat), ], }, offset: { properties: [ // MAY introduce set offset(Vector2) into texture. new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.uOffset"), - new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.vOffset", _getNextFloat), + new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.vOffset", getNextFloat), ], }, rotation: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.wAng", _getMinusFloat)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "emissiveTexture.wAng", getMinusFloat)], }, }, }, @@ -423,7 +422,7 @@ const CoreAnimationMaterialsPointerMap: any = { }, KHR_materials_sheen: { sheenColorFactor: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "sheen.color", _getColor3)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "sheen.color", getColor3)], }, sheenRoughnessFactor: { properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "sheen.roughness")], @@ -434,7 +433,7 @@ const CoreAnimationMaterialsPointerMap: any = { properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "metallicF0Factor")], }, specularColorFactor: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "metallicReflectanceColor", _getColor3)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "metallicReflectanceColor", getColor3)], }, }, KHR_materials_emissive_strength: { @@ -449,7 +448,7 @@ const CoreAnimationMaterialsPointerMap: any = { }, KHR_materials_volume: { attenuationColor: { - properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "subSurface.tintColor", _getColor3)], + properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "subSurface.tintColor", getColor3)], }, attenuationDistance: { properties: [new MaterialAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "subSurface.tintColorAtDistance")], @@ -476,13 +475,13 @@ const CoreAnimationMaterialsPointerMap: any = { }; const CoreAnimationExtensionsPointerMap: any = { - getTarget: _getGltfExtension, + getTarget: getGltfExtension, KHR_lights_punctual: { isIndex: true, lights: { hasIndex: true, // we have an array of light into the extension. color: { - properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "diffuse", _getColor3)], + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_COLOR3, "diffuse", getColor3)], }, intensity: { properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "intensity")], @@ -492,10 +491,10 @@ const CoreAnimationExtensionsPointerMap: any = { }, spot: { innerConeAngle: { - properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "innerAngle", _getFloatBy2)], + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "innerAngle", getFloatBy2)], }, outerConeAngle: { - properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "angle", _getFloatBy2)], + properties: [new LightAnimationPointerPropertyInfos(Animation.ANIMATIONTYPE_FLOAT, "angle", getFloatBy2)], }, }, }, diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts index 556dcb5c0e1..6ce062034ea 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts @@ -5,10 +5,11 @@ import { AnimationGroup } from "core/Animations/animationGroup"; import type { IAnimatable } from "core/Animations/animatable.interface"; import type { IAnimation, IAnimationChannel, _IAnimationSamplerData, IAnimationSampler } from "../glTFLoaderInterfaces"; -import { AccessorType, AnimationChannelTargetPath, AnimationSamplerInterpolation } from "babylonjs-gltf2interface"; +import { AnimationChannelTargetPath, AnimationSamplerInterpolation } from "babylonjs-gltf2interface"; import { AnimationKeyInterpolation } from "core/Animations/animationKey"; import { CoreAnimationPointerMap } from "./KHR_animation_pointer.map"; import type { GetGltfNodeTargetFn, IAnimationPointerPropertyInfos } from "./KHR_animation_pointer.map"; +import { _GLTFUtilities } from "../glTFUtilities"; const NAME = GLTFLoader._KHRAnimationPointerName; @@ -29,28 +30,6 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { */ public static IgnoreInvalidPointer: boolean = true; - /** - * Used internally to determine how much data to be gather from input buffer at each key frame. - * @param type the accessor type - * @returns the number of item to be gather at each keyframe - */ - static _GetAnimationOutputStride(type: AccessorType): number { - switch (type) { - case AccessorType.SCALAR: - return 1; - case AccessorType.VEC2: - return 2; - case AccessorType.VEC3: - return 3; - case AccessorType.VEC4: - case AccessorType.MAT2: - return 4; - case AccessorType.MAT3: - return 9; - case AccessorType.MAT4: - return 16; - } - } /** * The name of this extension. */ @@ -143,7 +122,7 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { const pointer = channel.target.extensions?.KHR_animation_pointer?.pointer; if (!pointer) { - throw new Error(`${context}/target/extensions.KHR_animation_pointer.pointer MUST be set.`); + throw new Error(`${context}/target/extensions/${this.name}: Pointer is missing`); } const sampler = ArrayItem.Get(`${context}/sampler`, animation.samplers, channel.sampler); @@ -159,10 +138,10 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { if (babylonAnimationGroup) { const outputAccessor = ArrayItem.Get(`${context}/output`, this._loader.gltf.accessors, sampler.output); // stride is the size of each element stored into the output buffer. - const stride = animationTarget.stride ?? KHR_animation_pointer._GetAnimationOutputStride(outputAccessor.type); + const stride = animationTarget.stride ?? _GLTFUtilities._GetDataAccessorElementCount(outputAccessor.type); const fps = this._loader.parent.targetFps; - // we extract the corresponding values from the readed value. + // we extract the corresponding values from the read value. // the reason for that is one GLTF value may be dispatched to several Babylon properties // one of example is baseColorFactor which is a Color4 under GLTF and dispatched to // - albedoColor as Color3(Color4.r,Color4.g,Color4.b) diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts index fdad2fc4b8c..60d84dee56e 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts @@ -31,7 +31,7 @@ export class KHR_lights implements IGLTFLoaderExtension { public enabled: boolean; /** hidden */ - public _loader: GLTFLoader; + private _loader: GLTFLoader; private _lights?: IKHRLight[]; /** diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/index.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/index.ts index 1aa254d44b4..0f0cab8eeae 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/index.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/index.ts @@ -21,7 +21,6 @@ export * from "./KHR_texture_basisu"; export * from "./KHR_texture_transform"; export * from "./KHR_xmp_json_ld"; export * from "./KHR_animation_pointer"; -export * from "./KHR_animation_pointer.map"; export * from "./MSFT_audio_emitter"; export * from "./MSFT_lod"; export * from "./MSFT_minecraftMesh"; diff --git a/packages/dev/loaders/src/glTF/2.0/glTFUtilities.ts b/packages/dev/loaders/src/glTF/2.0/glTFUtilities.ts new file mode 100644 index 00000000000..8b3c4703c5b --- /dev/null +++ b/packages/dev/loaders/src/glTF/2.0/glTFUtilities.ts @@ -0,0 +1,30 @@ +import { AccessorType } from "babylonjs-gltf2interface"; + +/** + * @hidden + */ +export class _GLTFUtilities { + + /** + * Used internally to determine how much data to be gather from input buffer at each key frame. + * @param accessorType the accessor type + * @returns the number of item to be gather at each keyframe + */ + public static _GetDataAccessorElementCount(accessorType: AccessorType) { + switch (accessorType) { + case AccessorType.SCALAR: + return 1; + case AccessorType.VEC2: + return 2; + case AccessorType.VEC3: + return 3; + case AccessorType.VEC4: + case AccessorType.MAT2: + return 4; + case AccessorType.MAT3: + return 9; + case AccessorType.MAT4: + return 16; + } + } +} diff --git a/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts b/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts index ead9c4e5be3..d00a1305a79 100644 --- a/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts +++ b/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts @@ -397,7 +397,7 @@ declare module BABYLON.GLTF2 { */ interface IAnimationChannelTarget extends IProperty { /** - * The index of the node to target - become optional with the introduction of KHR_animation_pointer + * The index of the node to target */ node?: number; /** From b01f911de9a51bb526fad5bf78076bd7c66c242f Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Thu, 21 Jul 2022 12:15:32 +0200 Subject: [PATCH 13/16] change option from static to per instance --- .../loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts index 6ce062034ea..b2eb8d8bad0 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts @@ -28,7 +28,7 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { /** * used to gently ignore invalid pointer. If false, invalid pointer will throw exception. */ - public static IgnoreInvalidPointer: boolean = true; + public ignoreInvalidPointer: boolean = true; /** * The name of this extension. @@ -312,7 +312,7 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { } } } - if (KHR_animation_pointer.IgnoreInvalidPointer) { + if (this.ignoreInvalidPointer) { return null; } throw new Error(`${context} invalid pointer. ${pointer}`); From 1c4f995b2ea45054e648059ced4d9b23272ba396 Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Thu, 21 Jul 2022 12:37:05 +0200 Subject: [PATCH 14/16] formatting --- .../loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts | 2 +- packages/dev/loaders/src/glTF/2.0/glTFUtilities.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts index b2eb8d8bad0..daf3ac65818 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts @@ -116,7 +116,7 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { if (channel.target.node != undefined) { // According to KHR_animation_pointer specification // If this extension is used, the animation.channel.target.node must not be set. - // Because the node isn’t defined, the channel is ignored and not animated due to the specification. + // Because the node is defined, the channel is ignored and not animated due to the specification. return Promise.resolve(); } diff --git a/packages/dev/loaders/src/glTF/2.0/glTFUtilities.ts b/packages/dev/loaders/src/glTF/2.0/glTFUtilities.ts index 8b3c4703c5b..82ad7e5ee5f 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFUtilities.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFUtilities.ts @@ -4,7 +4,6 @@ import { AccessorType } from "babylonjs-gltf2interface"; * @hidden */ export class _GLTFUtilities { - /** * Used internally to determine how much data to be gather from input buffer at each key frame. * @param accessorType the accessor type From af95695d735c89071cc35a4652a75257f9603327 Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Thu, 21 Jul 2022 15:00:28 +0200 Subject: [PATCH 15/16] avoid modifying GLTF model --- .../src/glTF/2.0/Extensions/KHR_animation_pointer.ts | 11 +++++++++-- packages/dev/loaders/src/glTF/2.0/glTFLoader.ts | 3 +-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts index daf3ac65818..22be58ff9cd 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts @@ -30,6 +30,11 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { */ public ignoreInvalidPointer: boolean = true; + /** + * used to define the extension as additional one created during the loading process. + */ + public lazzy: boolean = false; + /** * The name of this extension. */ @@ -109,11 +114,13 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { channel: IAnimationChannel, animationTargetOverride: Nullable = null ): Promise { - if (channel.target.path != AnimationChannelTargetPath.POINTER) { + const lazzy = channel.target.extensions?.KHR_animation_pointer?.lazzy; + + if (!lazzy && channel.target.path != AnimationChannelTargetPath.POINTER) { throw new Error(`${context}/target/path: Invalid value (${channel.target.path})`); } - if (channel.target.node != undefined) { + if (!lazzy && channel.target.node != undefined) { // According to KHR_animation_pointer specification // If this extension is used, the animation.channel.target.node must not be set. // Because the node is defined, the channel is ignored and not animated due to the specification. diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts index 7871e87de95..d600283a936 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts @@ -1509,9 +1509,8 @@ export class GLTFLoader implements IGLTFLoader { channel.target.extensions = channel.target.extensions || {}; channel.target.extensions.KHR_animation_pointer = { pointer: `/nodes/${channel.target.node}/${channel.target.path}`, + lazzy: true, }; - channel.target.path = AnimationChannelTargetPath.POINTER; - delete channel.target.node; // ensure to declare extension used. this._gltf.extensionsUsed = this._gltf.extensionsUsed || []; From 35b67840c65ca1f96e2aeeee162b127fd714878d Mon Sep 17 00:00:00 2001 From: Guillaume Pelletier Date: Thu, 21 Jul 2022 22:38:14 +0200 Subject: [PATCH 16/16] minors adjustements --- .../2.0/Extensions/KHR_animation_pointer.ts | 149 +++++++++--------- .../dev/loaders/src/glTF/2.0/glTFLoader.ts | 4 +- .../dev/loaders/src/glTF/2.0/glTFUtilities.ts | 41 +++-- 3 files changed, 92 insertions(+), 102 deletions(-) diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts index 22be58ff9cd..1830d02c892 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.ts @@ -9,7 +9,7 @@ import { AnimationChannelTargetPath, AnimationSamplerInterpolation } from "babyl import { AnimationKeyInterpolation } from "core/Animations/animationKey"; import { CoreAnimationPointerMap } from "./KHR_animation_pointer.map"; import type { GetGltfNodeTargetFn, IAnimationPointerPropertyInfos } from "./KHR_animation_pointer.map"; -import { _GLTFUtilities } from "../glTFUtilities"; +import { getDataAccessorElementCount } from "../glTFUtilities"; const NAME = GLTFLoader._KHRAnimationPointerName; @@ -30,11 +30,6 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { */ public ignoreInvalidPointer: boolean = true; - /** - * used to define the extension as additional one created during the loading process. - */ - public lazzy: boolean = false; - /** * The name of this extension. */ @@ -114,13 +109,11 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { channel: IAnimationChannel, animationTargetOverride: Nullable = null ): Promise { - const lazzy = channel.target.extensions?.KHR_animation_pointer?.lazzy; - - if (!lazzy && channel.target.path != AnimationChannelTargetPath.POINTER) { + if (channel.target.path != AnimationChannelTargetPath.POINTER) { throw new Error(`${context}/target/path: Invalid value (${channel.target.path})`); } - if (!lazzy && channel.target.node != undefined) { + if (channel.target.node != undefined) { // According to KHR_animation_pointer specification // If this extension is used, the animation.channel.target.node must not be set. // Because the node is defined, the channel is ignored and not animated due to the specification. @@ -138,78 +131,80 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { // this is where we process the pointer. const animationTarget = this._parseAnimationPointer(`${context}/extensions/${this.name}/pointer`, pointer); - if (animationTarget) { - // build the keys - // build the animations into the group - const babylonAnimationGroup = animation._babylonAnimationGroup; - if (babylonAnimationGroup) { - const outputAccessor = ArrayItem.Get(`${context}/output`, this._loader.gltf.accessors, sampler.output); - // stride is the size of each element stored into the output buffer. - const stride = animationTarget.stride ?? _GLTFUtilities._GetDataAccessorElementCount(outputAccessor.type); - const fps = this._loader.parent.targetFps; - - // we extract the corresponding values from the read value. - // the reason for that is one GLTF value may be dispatched to several Babylon properties - // one of example is baseColorFactor which is a Color4 under GLTF and dispatched to - // - albedoColor as Color3(Color4.r,Color4.g,Color4.b) - // - alpha as Color4.a - for (const propertyInfo of animationTarget.properties) { - // Ignore animations that have no animation valid targets. - if (!propertyInfo.isValid(animationTarget.target)) { - return Promise.resolve(); - } + if (!animationTarget) { + return; + } + // build the keys + // build the animations into the group + const babylonAnimationGroup = animation._babylonAnimationGroup; + if (!babylonAnimationGroup) { + return; + } - // build the keys. - const keys = new Array(data.input.length); - let outputOffset = 0; - - switch (data.interpolation) { - case AnimationSamplerInterpolation.STEP: { - for (let frameIndex = 0; frameIndex < data.input.length; frameIndex++) { - keys[frameIndex] = { - frame: data.input[frameIndex] * fps, - value: propertyInfo.get(animationTarget.target, data.output, outputOffset), - interpolation: AnimationKeyInterpolation.STEP, - }; - outputOffset += stride; - } - break; - } - case AnimationSamplerInterpolation.CUBICSPLINE: { - const invfps = 1 / fps; - for (let frameIndex = 0; frameIndex < data.input.length; frameIndex++) { - const k: any = { - frame: data.input[frameIndex] * fps, - }; - - (k.inTangent = propertyInfo.get(animationTarget.target, data.output, outputOffset, invfps)), (outputOffset += stride); - (k.value = propertyInfo.get(animationTarget.target, data.output, outputOffset)), (outputOffset += stride); - (k.outTangent = propertyInfo.get(animationTarget.target, data.output, outputOffset, invfps)), (outputOffset += stride); - - keys[frameIndex] = k; - } - break; - } - case AnimationSamplerInterpolation.LINEAR: - default: { - for (let frameIndex = 0; frameIndex < data.input.length; frameIndex++) { - keys[frameIndex] = { - frame: data.input[frameIndex] * fps, - value: propertyInfo.get(animationTarget.target, data.output, outputOffset), - }; - outputOffset += stride; - } - break; - } - } + const outputAccessor = ArrayItem.Get(`${context}/output`, this._loader.gltf.accessors, sampler.output); + // stride is the size of each element stored into the output buffer. + const stride = animationTarget.stride ?? getDataAccessorElementCount(outputAccessor.type); + const fps = this._loader.parent.targetFps; + + // we extract the corresponding values from the read value. + // the reason for that is one GLTF value may be dispatched to several Babylon properties + // one of example is baseColorFactor which is a Color4 under GLTF and dispatched to + // - albedoColor as Color3(Color4.r,Color4.g,Color4.b) + // - alpha as Color4.a + for (const propertyInfo of animationTarget.properties) { + // Ignore animations that have no animation valid targets. + if (!propertyInfo.isValid(animationTarget.target)) { + return; + } - // each properties has its own build animation process. - // these logics are located into KHR_animation_pointer.map.ts - propertyInfo.buildAnimations(animationTarget.target, fps, keys, babylonAnimationGroup, animationTargetOverride, animationTarget.params); + // build the keys. + const keys = new Array(data.input.length); + let outputOffset = 0; + + switch (data.interpolation) { + case AnimationSamplerInterpolation.STEP: { + for (let frameIndex = 0; frameIndex < data.input.length; frameIndex++) { + keys[frameIndex] = { + frame: data.input[frameIndex] * fps, + value: propertyInfo.get(animationTarget.target, data.output, outputOffset), + interpolation: AnimationKeyInterpolation.STEP, + }; + outputOffset += stride; + } + break; + } + case AnimationSamplerInterpolation.CUBICSPLINE: { + const invfps = 1 / fps; + for (let frameIndex = 0; frameIndex < data.input.length; frameIndex++) { + const k: any = { + frame: data.input[frameIndex] * fps, + }; + + (k.inTangent = propertyInfo.get(animationTarget.target, data.output, outputOffset, invfps)), (outputOffset += stride); + (k.value = propertyInfo.get(animationTarget.target, data.output, outputOffset)), (outputOffset += stride); + (k.outTangent = propertyInfo.get(animationTarget.target, data.output, outputOffset, invfps)), (outputOffset += stride); + + keys[frameIndex] = k; + } + break; + } + case AnimationSamplerInterpolation.LINEAR: + default: { + for (let frameIndex = 0; frameIndex < data.input.length; frameIndex++) { + keys[frameIndex] = { + frame: data.input[frameIndex] * fps, + value: propertyInfo.get(animationTarget.target, data.output, outputOffset), + }; + outputOffset += stride; + } + break; } } + + // each properties has its own build animation process. + // these logics are located into KHR_animation_pointer.map.ts + propertyInfo.buildAnimations(animationTarget.target, fps, keys, babylonAnimationGroup, animationTargetOverride, animationTarget.params); } - return Promise.resolve(); }); } diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts index d600283a936..65743a2b5fd 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts @@ -1509,9 +1509,9 @@ export class GLTFLoader implements IGLTFLoader { channel.target.extensions = channel.target.extensions || {}; channel.target.extensions.KHR_animation_pointer = { pointer: `/nodes/${channel.target.node}/${channel.target.path}`, - lazzy: true, }; - + channel.target.path = AnimationChannelTargetPath.POINTER; + delete channel.target.node; // ensure to declare extension used. this._gltf.extensionsUsed = this._gltf.extensionsUsed || []; if (this._gltf.extensionsUsed.indexOf(GLTFLoader._KHRAnimationPointerName) === -1) { diff --git a/packages/dev/loaders/src/glTF/2.0/glTFUtilities.ts b/packages/dev/loaders/src/glTF/2.0/glTFUtilities.ts index 82ad7e5ee5f..385b51be067 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFUtilities.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFUtilities.ts @@ -1,29 +1,24 @@ import { AccessorType } from "babylonjs-gltf2interface"; /** - * @hidden + * Used internally to determine how much data to be gather from input buffer at each key frame. + * @param accessorType the accessor type + * @returns the number of item to be gather at each keyframe */ -export class _GLTFUtilities { - /** - * Used internally to determine how much data to be gather from input buffer at each key frame. - * @param accessorType the accessor type - * @returns the number of item to be gather at each keyframe - */ - public static _GetDataAccessorElementCount(accessorType: AccessorType) { - switch (accessorType) { - case AccessorType.SCALAR: - return 1; - case AccessorType.VEC2: - return 2; - case AccessorType.VEC3: - return 3; - case AccessorType.VEC4: - case AccessorType.MAT2: - return 4; - case AccessorType.MAT3: - return 9; - case AccessorType.MAT4: - return 16; - } +export function getDataAccessorElementCount(accessorType: AccessorType) { + switch (accessorType) { + case AccessorType.SCALAR: + return 1; + case AccessorType.VEC2: + return 2; + case AccessorType.VEC3: + return 3; + case AccessorType.VEC4: + case AccessorType.MAT2: + return 4; + case AccessorType.MAT3: + return 9; + case AccessorType.MAT4: + return 16; } }