diff --git a/packages/dev/core/src/FlowGraph/Blocks/Data/Transformers/flowGraphJsonPointerParserBlock.ts b/packages/dev/core/src/FlowGraph/Blocks/Data/Transformers/flowGraphJsonPointerParserBlock.ts index 68de8555ec8..7315b66b95f 100644 --- a/packages/dev/core/src/FlowGraph/Blocks/Data/Transformers/flowGraphJsonPointerParserBlock.ts +++ b/packages/dev/core/src/FlowGraph/Blocks/Data/Transformers/flowGraphJsonPointerParserBlock.ts @@ -85,10 +85,10 @@ export class FlowGraphJsonPointerParserBlock

Animation[] { - const accessorContainer = this.templateComponent.getAccessor(this.config.pathConverter, context); + const accessor = this.templateComponent.getAccessor(this.config.pathConverter, context); return (keys: any[], fps: number, animationType: number, easingFunction?: EasingFunction) => { const animations: Animation[] = []; // make sure keys are of the right type (in case of float3 color/vector) - const type = accessorContainer.info.type; + const type = accessor.info.type; if (type.startsWith("Color")) { keys = keys.map((key) => { return { @@ -137,8 +137,8 @@ export class FlowGraphJsonPointerParserBlock

{ - const name = accessorContainer.info.getPropertyName?.[index](accessorContainer.object) || "Animation-interpolation-" + index; + accessor.info.interpolation?.forEach((info, index) => { + const name = accessor.info.getPropertyName?.[index](accessor.object) || "Animation-interpolation-" + index; // generate the keys based on interpolation info let newKeys: any[] = keys; if (animationType !== info.type) { @@ -150,7 +150,7 @@ export class FlowGraphJsonPointerParserBlock

{ /** * The original object. */ object: O; + /** + * The index of the array in question when applicable. + */ + index?: number; /** * Information about the object. */ diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/EXT_lights_image_based.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/EXT_lights_image_based.ts index 8ce46393e5f..fa18bed7683 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/EXT_lights_image_based.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/EXT_lights_image_based.ts @@ -73,9 +73,7 @@ export class EXT_lights_image_based implements IGLTFLoaderExtension { } } - /** - * @internal - */ + /** @internal */ // eslint-disable-next-line no-restricted-syntax public loadSceneAsync(context: string, scene: IScene): Nullable> { return GLTFLoader.LoadExtensionAsync(context, scene, this.name, async (extensionContext, extension) => { diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.data.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.data.ts index 085cff13007..be2d7e0c4fe 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.data.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_animation_pointer.data.ts @@ -5,7 +5,7 @@ import type { ICamera, IKHRLightsPunctual_Light, IMaterial } from "../glTFLoader import type { IAnimatable } from "core/Animations/animatable.interface"; import { AnimationPropertyInfo } from "../glTFLoaderAnimation"; import { Color3 } from "core/Maths/math.color"; -import { SetInterpolationForKey } from "./objectModelMapping"; +import { AddInterpolation } from "./objectModelMapping"; function getColor3(_target: any, source: Float32Array, offset: number, scale: number): Color3 { return Color3.FromArray(source, offset).scale(scale); @@ -73,256 +73,237 @@ class LightAnimationPropertyInfo extends AnimationPropertyInfo { } } -SetInterpolationForKey("/cameras/{}/orthographic/xmag", [ +AddInterpolation("/cameras/{}/orthographic/xmag", [ new CameraAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "orthoLeft", getMinusFloat, () => 1), new CameraAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "orthoRight", getNextFloat, () => 1), ]); -SetInterpolationForKey("/cameras/{}/orthographic/ymag", [ +AddInterpolation("/cameras/{}/orthographic/ymag", [ new CameraAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "orthoBottom", getMinusFloat, () => 1), new CameraAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "orthoTop", getNextFloat, () => 1), ]); -SetInterpolationForKey("/cameras/{}/orthographic/zfar", [new CameraAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "maxZ", getFloat, () => 1)]); -SetInterpolationForKey("/cameras/{}/orthographic/znear", [new CameraAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "minZ", getFloat, () => 1)]); +AddInterpolation("/cameras/{}/orthographic/zfar", [new CameraAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "maxZ", getFloat, () => 1)]); +AddInterpolation("/cameras/{}/orthographic/znear", [new CameraAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "minZ", getFloat, () => 1)]); -SetInterpolationForKey("/cameras/{}/perspective/yfov", [new CameraAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "fov", getFloat, () => 1)]); -SetInterpolationForKey("/cameras/{}/perspective/zfar", [new CameraAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "maxZ", getFloat, () => 1)]); -SetInterpolationForKey("/cameras/{}/perspective/znear", [new CameraAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "minZ", getFloat, () => 1)]); +AddInterpolation("/cameras/{}/perspective/yfov", [new CameraAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "fov", getFloat, () => 1)]); +AddInterpolation("/cameras/{}/perspective/zfar", [new CameraAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "maxZ", getFloat, () => 1)]); +AddInterpolation("/cameras/{}/perspective/znear", [new CameraAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "minZ", getFloat, () => 1)]); // add interpolation to the materials mapping -SetInterpolationForKey("/materials/{}/pbrMetallicRoughness/baseColorFactor", [ +AddInterpolation("/materials/{}/pbrMetallicRoughness/baseColorFactor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_COLOR3, "albedoColor", getColor3, () => 4), new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "alpha", getAlpha, () => 4), ]); -SetInterpolationForKey("/materials/{}/pbrMetallicRoughness/metallicFactor", [new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "metallic", getFloat, () => 1)]); -SetInterpolationForKey("/materials/{}/pbrMetallicRoughness/metallicFactor", [new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "roughness", getFloat, () => 1)]); +AddInterpolation("/materials/{}/pbrMetallicRoughness/metallicFactor", [new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "metallic", getFloat, () => 1)]); +AddInterpolation("/materials/{}/pbrMetallicRoughness/roughnessFactor", [new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "roughness", getFloat, () => 1)]); const baseColorTextureInterpolation = getTextureTransformTree("albedoTexture"); -SetInterpolationForKey("/materials/{}/pbrMetallicRoughness/baseColorTexture/extensions/KHR_texture_transform/scale", baseColorTextureInterpolation.scale); -SetInterpolationForKey("/materials/{}/pbrMetallicRoughness/baseColorTexture/extensions/KHR_texture_transform/offset", baseColorTextureInterpolation.offset); -SetInterpolationForKey("/materials/{}/pbrMetallicRoughness/baseColorTexture/extensions/KHR_texture_transform/rotation", baseColorTextureInterpolation.rotation); +AddInterpolation("/materials/{}/pbrMetallicRoughness/baseColorTexture/extensions/KHR_texture_transform/scale", baseColorTextureInterpolation.scale); +AddInterpolation("/materials/{}/pbrMetallicRoughness/baseColorTexture/extensions/KHR_texture_transform/offset", baseColorTextureInterpolation.offset); +AddInterpolation("/materials/{}/pbrMetallicRoughness/baseColorTexture/extensions/KHR_texture_transform/rotation", baseColorTextureInterpolation.rotation); const metallicRoughnessTextureInterpolation = getTextureTransformTree("metallicTexture"); -SetInterpolationForKey("//materials/{}/pbrMetallicRoughness/metallicRoughnessTexture/scale", metallicRoughnessTextureInterpolation.scale); -SetInterpolationForKey("//materials/{}/pbrMetallicRoughness/metallicRoughnessTexture/offset", metallicRoughnessTextureInterpolation.offset); -SetInterpolationForKey("//materials/{}/pbrMetallicRoughness/metallicRoughnessTexture/rotation", metallicRoughnessTextureInterpolation.rotation); +AddInterpolation("/materials/{}/pbrMetallicRoughness/metallicRoughnessTexture/extensions/KHR_texture_transform/scale", metallicRoughnessTextureInterpolation.scale); +AddInterpolation("/materials/{}/pbrMetallicRoughness/metallicRoughnessTexture/extensions/KHR_texture_transform/offset", metallicRoughnessTextureInterpolation.offset); +AddInterpolation("/materials/{}/pbrMetallicRoughness/metallicRoughnessTexture/extensions/KHR_texture_transform/rotation", metallicRoughnessTextureInterpolation.rotation); -SetInterpolationForKey("/materials/{}/emissiveFactor", [new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_COLOR3, "emissiveColor", getColor3, () => 3)]); +AddInterpolation("/materials/{}/emissiveFactor", [new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_COLOR3, "emissiveColor", getColor3, () => 3)]); const normalTextureInterpolation = getTextureTransformTree("bumpTexture"); -SetInterpolationForKey("/materials/{}/normalTexture/scale", [new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "bumpTexture.level", getFloat, () => 1)]); +AddInterpolation("/materials/{}/normalTexture/scale", [new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "bumpTexture.level", getFloat, () => 1)]); -SetInterpolationForKey("/materials/{}/normalTexture/extensions/KHR_texture_transform/scale", normalTextureInterpolation.scale); -SetInterpolationForKey("/materials/{}/normalTexture/extensions/KHR_texture_transform/offset", normalTextureInterpolation.offset); -SetInterpolationForKey("/materials/{}/normalTexture/extensions/KHR_texture_transform/rotation", normalTextureInterpolation.rotation); +AddInterpolation("/materials/{}/normalTexture/extensions/KHR_texture_transform/scale", normalTextureInterpolation.scale); +AddInterpolation("/materials/{}/normalTexture/extensions/KHR_texture_transform/offset", normalTextureInterpolation.offset); +AddInterpolation("/materials/{}/normalTexture/extensions/KHR_texture_transform/rotation", normalTextureInterpolation.rotation); -SetInterpolationForKey("/materials/{}/occlusionTexture/strength", [new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "ambientTextureStrength", getFloat, () => 1)]); +AddInterpolation("/materials/{}/occlusionTexture/strength", [new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "ambientTextureStrength", getFloat, () => 1)]); const occlusionTextureInterpolation = getTextureTransformTree("ambientTexture"); -SetInterpolationForKey("/materials/{}/occlusionTexture/extensions/KHR_texture_transform/scale", occlusionTextureInterpolation.scale); -SetInterpolationForKey("/materials/{}/occlusionTexture/extensions/KHR_texture_transform/offset", occlusionTextureInterpolation.offset); -SetInterpolationForKey("/materials/{}/occlusionTexture/extensions/KHR_texture_transform/rotation", occlusionTextureInterpolation.rotation); +AddInterpolation("/materials/{}/occlusionTexture/extensions/KHR_texture_transform/scale", occlusionTextureInterpolation.scale); +AddInterpolation("/materials/{}/occlusionTexture/extensions/KHR_texture_transform/offset", occlusionTextureInterpolation.offset); +AddInterpolation("/materials/{}/occlusionTexture/extensions/KHR_texture_transform/rotation", occlusionTextureInterpolation.rotation); const emissiveTextureInterpolation = getTextureTransformTree("emissiveTexture"); -SetInterpolationForKey("/materials/{}/emissiveTexture/extensions/KHR_texture_transform/scale", emissiveTextureInterpolation.scale); -SetInterpolationForKey("/materials/{}/emissiveTexture/extensions/KHR_texture_transform/offset", emissiveTextureInterpolation.offset); -SetInterpolationForKey("/materials/{}/emissiveTexture/extensions/KHR_texture_transform/rotation", emissiveTextureInterpolation.rotation); +AddInterpolation("/materials/{}/emissiveTexture/extensions/KHR_texture_transform/scale", emissiveTextureInterpolation.scale); +AddInterpolation("/materials/{}/emissiveTexture/extensions/KHR_texture_transform/offset", emissiveTextureInterpolation.offset); +AddInterpolation("/materials/{}/emissiveTexture/extensions/KHR_texture_transform/rotation", emissiveTextureInterpolation.rotation); // materials extensions -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_anisotropy/anisotropyStrength", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_anisotropy/anisotropyStrength", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "anisotropy.intensity", getFloat, () => 1), ]); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_anisotropy/anisotropyRotation", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_anisotropy/anisotropyRotation", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "anisotropy.angle", getFloat, () => 1), ]); const anisotropyTextureInterpolation = getTextureTransformTree("anisotropy.texture"); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_anisotropy/anisotropyTexture/extensions/KHR_texture_transform/scale", anisotropyTextureInterpolation.scale); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_anisotropy/anisotropyTexture/extensions/KHR_texture_transform/offset", anisotropyTextureInterpolation.offset); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_anisotropy/anisotropyTexture/extensions/KHR_texture_transform/rotation", anisotropyTextureInterpolation.rotation); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatFactor", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_anisotropy/anisotropyTexture/extensions/KHR_texture_transform/scale", anisotropyTextureInterpolation.scale); +AddInterpolation("/materials/{}/extensions/KHR_materials_anisotropy/anisotropyTexture/extensions/KHR_texture_transform/offset", anisotropyTextureInterpolation.offset); +AddInterpolation("/materials/{}/extensions/KHR_materials_anisotropy/anisotropyTexture/extensions/KHR_texture_transform/rotation", anisotropyTextureInterpolation.rotation); +AddInterpolation("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatFactor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "clearCoat.intensity", getFloat, () => 1), ]); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatRoughnessFactor", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatRoughnessFactor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "clearCoat.roughness", getFloat, () => 1), ]); const clearcoatTextureInterpolation = getTextureTransformTree("clearCoat.texture"); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatTexture/extensions/KHR_texture_transform/scale", clearcoatTextureInterpolation.scale); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatTexture/extensions/KHR_texture_transform/offset", clearcoatTextureInterpolation.offset); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatTexture/extensions/KHR_texture_transform/rotation", clearcoatTextureInterpolation.rotation); +AddInterpolation("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatTexture/extensions/KHR_texture_transform/scale", clearcoatTextureInterpolation.scale); +AddInterpolation("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatTexture/extensions/KHR_texture_transform/offset", clearcoatTextureInterpolation.offset); +AddInterpolation("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatTexture/extensions/KHR_texture_transform/rotation", clearcoatTextureInterpolation.rotation); const clearcoatNormalTextureInterpolation = getTextureTransformTree("clearCoat.bumpTexture"); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatNormalTexture/scale", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatNormalTexture/scale", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "clearCoat.bumpTexture.level", getFloat, () => 1), ]); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatNormalTexture/extensions/KHR_texture_transform/scale", clearcoatNormalTextureInterpolation.scale); -SetInterpolationForKey( - "/materials/{}/extensions/KHR_materials_clearcoat/clearcoatNormalTexture/extensions/KHR_texture_transform/offset", - clearcoatNormalTextureInterpolation.offset -); -SetInterpolationForKey( - "/materials/{}/extensions/KHR_materials_clearcoat/clearcoatNormalTexture/extensions/KHR_texture_transform/rotation", - clearcoatNormalTextureInterpolation.rotation -); +AddInterpolation("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatNormalTexture/extensions/KHR_texture_transform/scale", clearcoatNormalTextureInterpolation.scale); +AddInterpolation("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatNormalTexture/extensions/KHR_texture_transform/offset", clearcoatNormalTextureInterpolation.offset); +AddInterpolation("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatNormalTexture/extensions/KHR_texture_transform/rotation", clearcoatNormalTextureInterpolation.rotation); const clearcoatRoughnessTextureInterpolation = getTextureTransformTree("clearCoat.textureRoughness"); -SetInterpolationForKey( - "/materials/{}/extensions/KHR_materials_clearcoat/clearcoatRoughnessTexture/extensions/KHR_texture_transform/scale", - clearcoatRoughnessTextureInterpolation.scale -); -SetInterpolationForKey( +AddInterpolation("/materials/{}/extensions/KHR_materials_clearcoat/clearcoatRoughnessTexture/extensions/KHR_texture_transform/scale", clearcoatRoughnessTextureInterpolation.scale); +AddInterpolation( "/materials/{}/extensions/KHR_materials_clearcoat/clearcoatRoughnessTexture/extensions/KHR_texture_transform/offset", clearcoatRoughnessTextureInterpolation.offset ); -SetInterpolationForKey( +AddInterpolation( "/materials/{}/extensions/KHR_materials_clearcoat/clearcoatRoughnessTexture/extensions/KHR_texture_transform/rotation", clearcoatRoughnessTextureInterpolation.rotation ); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_dispersion/dispersionFactor", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_dispersion/dispersionFactor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "subSurface.dispersion", getFloat, () => 1), ]); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_emissive_strength/emissiveStrength", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_emissive_strength/emissiveStrength", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "emissiveIntensity", getFloat, () => 1), ]); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_ior/ior", [ - new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "indexOfRefraction", getFloat, () => 1), -]); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_iridescence/iridescenceFactor", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_ior/ior", [new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "indexOfRefraction", getFloat, () => 1)]); +AddInterpolation("/materials/{}/extensions/KHR_materials_iridescence/iridescenceFactor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "iridescence.intensity", getFloat, () => 1), ]); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_iridescence/iridescenceIor", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_iridescence/iridescenceIor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "iridescence.indexOfRefraction", getFloat, () => 1), ]); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_iridescence/iridescenceThicknessMinimum", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_iridescence/iridescenceThicknessMinimum", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "iridescence.minimumThickness", getFloat, () => 1), ]); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_iridescence/iridescenceThicknessMaximum", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_iridescence/iridescenceThicknessMaximum", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "iridescence.maximumThickness", getFloat, () => 1), ]); const iridescenceTextureInterpolation = getTextureTransformTree("iridescence.texture"); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_iridescence/iridescenceTexture/extensions/KHR_texture_transform/scale", iridescenceTextureInterpolation.scale); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_iridescence/iridescenceTexture/extensions/KHR_texture_transform/offset", iridescenceTextureInterpolation.offset); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_iridescence/iridescenceTexture/extensions/KHR_texture_transform/rotation", iridescenceTextureInterpolation.rotation); +AddInterpolation("/materials/{}/extensions/KHR_materials_iridescence/iridescenceTexture/extensions/KHR_texture_transform/scale", iridescenceTextureInterpolation.scale); +AddInterpolation("/materials/{}/extensions/KHR_materials_iridescence/iridescenceTexture/extensions/KHR_texture_transform/offset", iridescenceTextureInterpolation.offset); +AddInterpolation("/materials/{}/extensions/KHR_materials_iridescence/iridescenceTexture/extensions/KHR_texture_transform/rotation", iridescenceTextureInterpolation.rotation); const iridescenceThicknessTextureInterpolation = getTextureTransformTree("iridescence.thicknessTexture"); -SetInterpolationForKey( +AddInterpolation( "/materials/{}/extensions/KHR_materials_iridescence/iridescenceThicknessTexture/extensions/KHR_texture_transform/scale", iridescenceThicknessTextureInterpolation.scale ); -SetInterpolationForKey( +AddInterpolation( "/materials/{}/extensions/KHR_materials_iridescence/iridescenceThicknessTexture/extensions/KHR_texture_transform/offset", iridescenceThicknessTextureInterpolation.offset ); -SetInterpolationForKey( +AddInterpolation( "/materials/{}/extensions/KHR_materials_iridescence/iridescenceThicknessTexture/extensions/KHR_texture_transform/rotation", iridescenceThicknessTextureInterpolation.rotation ); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_sheen/sheenColorFactor", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_sheen/sheenColorFactor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_COLOR3, "sheen.color", getColor3, () => 3), ]); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_sheen/sheenRoughnessFactor", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_sheen/sheenRoughnessFactor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "sheen.roughness", getFloat, () => 1), ]); const sheenTextureInterpolation = getTextureTransformTree("sheen.texture"); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_sheen/sheenColorTexture/extensions/KHR_texture_transform/scale", sheenTextureInterpolation.scale); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_sheen/sheenColorTexture/extensions/KHR_texture_transform/offset", sheenTextureInterpolation.offset); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_sheen/sheenColorTexture/extensions/KHR_texture_transform/rotation", sheenTextureInterpolation.rotation); +AddInterpolation("/materials/{}/extensions/KHR_materials_sheen/sheenColorTexture/extensions/KHR_texture_transform/scale", sheenTextureInterpolation.scale); +AddInterpolation("/materials/{}/extensions/KHR_materials_sheen/sheenColorTexture/extensions/KHR_texture_transform/offset", sheenTextureInterpolation.offset); +AddInterpolation("/materials/{}/extensions/KHR_materials_sheen/sheenColorTexture/extensions/KHR_texture_transform/rotation", sheenTextureInterpolation.rotation); const sheenRoughnessTextureInterpolation = getTextureTransformTree("sheen.textureRoughness"); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_sheen/sheenRoughnessTexture/extensions/KHR_texture_transform/scale", sheenRoughnessTextureInterpolation.scale); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_sheen/sheenRoughnessTexture/extensions/KHR_texture_transform/offset", sheenRoughnessTextureInterpolation.offset); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_sheen/sheenRoughnessTexture/extensions/KHR_texture_transform/rotation", sheenRoughnessTextureInterpolation.rotation); +AddInterpolation("/materials/{}/extensions/KHR_materials_sheen/sheenRoughnessTexture/extensions/KHR_texture_transform/scale", sheenRoughnessTextureInterpolation.scale); +AddInterpolation("/materials/{}/extensions/KHR_materials_sheen/sheenRoughnessTexture/extensions/KHR_texture_transform/offset", sheenRoughnessTextureInterpolation.offset); +AddInterpolation("/materials/{}/extensions/KHR_materials_sheen/sheenRoughnessTexture/extensions/KHR_texture_transform/rotation", sheenRoughnessTextureInterpolation.rotation); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_specular/specularFactor", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_specular/specularFactor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "metallicF0Factor", getFloat, () => 1), ]); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_specular/specularColorFactor", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_specular/specularColorFactor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_COLOR3, "metallicReflectanceColor", getColor3, () => 3), ]); const specularTextureInterpolation = getTextureTransformTree("metallicReflectanceTexture"); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_specular/specularTexture/extensions/KHR_texture_transform/scale", specularTextureInterpolation.scale); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_specular/specularTexture/extensions/KHR_texture_transform/offset", specularTextureInterpolation.offset); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_specular/specularTexture/extensions/KHR_texture_transform/rotation", specularTextureInterpolation.rotation); +AddInterpolation("/materials/{}/extensions/KHR_materials_specular/specularTexture/extensions/KHR_texture_transform/scale", specularTextureInterpolation.scale); +AddInterpolation("/materials/{}/extensions/KHR_materials_specular/specularTexture/extensions/KHR_texture_transform/offset", specularTextureInterpolation.offset); +AddInterpolation("/materials/{}/extensions/KHR_materials_specular/specularTexture/extensions/KHR_texture_transform/rotation", specularTextureInterpolation.rotation); const specularColorTextureInterpolation = getTextureTransformTree("reflectanceTexture"); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_specular/specularColorTexture/extensions/KHR_texture_transform/scale", specularColorTextureInterpolation.scale); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_specular/specularColorTexture/extensions/KHR_texture_transform/offset", specularColorTextureInterpolation.offset); -SetInterpolationForKey( - "/materials/{}/extensions/KHR_materials_specular/specularColorTexture/extensions/KHR_texture_transform/rotation", - specularColorTextureInterpolation.rotation -); +AddInterpolation("/materials/{}/extensions/KHR_materials_specular/specularColorTexture/extensions/KHR_texture_transform/scale", specularColorTextureInterpolation.scale); +AddInterpolation("/materials/{}/extensions/KHR_materials_specular/specularColorTexture/extensions/KHR_texture_transform/offset", specularColorTextureInterpolation.offset); +AddInterpolation("/materials/{}/extensions/KHR_materials_specular/specularColorTexture/extensions/KHR_texture_transform/rotation", specularColorTextureInterpolation.rotation); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_transmission/transmissionFactor", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_transmission/transmissionFactor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "subSurface.refractionIntensity", getFloat, () => 1), ]); const transmissionTextureInterpolation = getTextureTransformTree("subSurface.refractionIntensityTexture"); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_transmission/transmissionTexture/extensions/KHR_texture_transform/scale", transmissionTextureInterpolation.scale); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_transmission/transmissionTexture/extensions/KHR_texture_transform/offset", transmissionTextureInterpolation.offset); -SetInterpolationForKey( - "/materials/{}/extensions/KHR_materials_transmission/transmissionTexture/extensions/KHR_texture_transform/rotation", - transmissionTextureInterpolation.rotation -); +AddInterpolation("/materials/{}/extensions/KHR_materials_transmission/transmissionTexture/extensions/KHR_texture_transform/scale", transmissionTextureInterpolation.scale); +AddInterpolation("/materials/{}/extensions/KHR_materials_transmission/transmissionTexture/extensions/KHR_texture_transform/offset", transmissionTextureInterpolation.offset); +AddInterpolation("/materials/{}/extensions/KHR_materials_transmission/transmissionTexture/extensions/KHR_texture_transform/rotation", transmissionTextureInterpolation.rotation); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_volume/attenuationColor", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_volume/attenuationColor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_COLOR3, "subSurface.tintColor", getColor3, () => 3), ]); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_volume/attenuationDistance", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_volume/attenuationDistance", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "subSurface.tintColorAtDistance", getFloat, () => 1), ]); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_volume/thicknessFactor", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_volume/thicknessFactor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "subSurface.maximumThickness", getFloat, () => 1), ]); const thicknessTextureInterpolation = getTextureTransformTree("subSurface.thicknessTexture"); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_volume/thicknessTexture/extensions/KHR_texture_transform/scale", thicknessTextureInterpolation.scale); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_volume/thicknessTexture/extensions/KHR_texture_transform/offset", thicknessTextureInterpolation.offset); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_volume/thicknessTexture/extensions/KHR_texture_transform/rotation", thicknessTextureInterpolation.rotation); +AddInterpolation("/materials/{}/extensions/KHR_materials_volume/thicknessTexture/extensions/KHR_texture_transform/scale", thicknessTextureInterpolation.scale); +AddInterpolation("/materials/{}/extensions/KHR_materials_volume/thicknessTexture/extensions/KHR_texture_transform/offset", thicknessTextureInterpolation.offset); +AddInterpolation("/materials/{}/extensions/KHR_materials_volume/thicknessTexture/extensions/KHR_texture_transform/rotation", thicknessTextureInterpolation.rotation); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionFactor", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionFactor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "subSurface.translucencyIntensity", getFloat, () => 1), ]); const diffuseTransmissionTextureInterpolation = getTextureTransformTree("subSurface.translucencyIntensityTexture"); -SetInterpolationForKey( - "materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionTexture/extensions/KHR_texture_transform/scale", +AddInterpolation( + "/materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionTexture/extensions/KHR_texture_transform/scale", diffuseTransmissionTextureInterpolation.scale ); -SetInterpolationForKey( - "materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionTexture/extensions/KHR_texture_transform/offset", +AddInterpolation( + "/materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionTexture/extensions/KHR_texture_transform/offset", diffuseTransmissionTextureInterpolation.offset ); -SetInterpolationForKey( - "materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionTexture/extensions/KHR_texture_transform/rotation", +AddInterpolation( + "/materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionTexture/extensions/KHR_texture_transform/rotation", diffuseTransmissionTextureInterpolation.rotation ); -SetInterpolationForKey("/materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionColorFactor", [ +AddInterpolation("/materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionColorFactor", [ new MaterialAnimationPropertyInfo(Animation.ANIMATIONTYPE_COLOR3, "subSurface.translucencyColor", getColor3, () => 3), ]); const diffuseTransmissionColorTextureInterpolation = getTextureTransformTree("subSurface.translucencyColorTexture"); -SetInterpolationForKey( - "materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionColorTexture/extensions/KHR_texture_transform/scale", +AddInterpolation( + "/materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionColorTexture/extensions/KHR_texture_transform/scale", diffuseTransmissionColorTextureInterpolation.scale ); -SetInterpolationForKey( - "materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionColorTexture/extensions/KHR_texture_transform/offset", +AddInterpolation( + "/materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionColorTexture/extensions/KHR_texture_transform/offset", diffuseTransmissionColorTextureInterpolation.offset ); -SetInterpolationForKey( - "materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionColorTexture/extensions/KHR_texture_transform/rotation", +AddInterpolation( + "/materials/{}/extensions/KHR_materials_diffuse_transmission/diffuseTransmissionColorTexture/extensions/KHR_texture_transform/rotation", diffuseTransmissionColorTextureInterpolation.rotation ); -SetInterpolationForKey("/extensions/KHR_lights_punctual/lights/{}/color", [new LightAnimationPropertyInfo(Animation.ANIMATIONTYPE_COLOR3, "diffuse", getColor3, () => 3)]); -SetInterpolationForKey("/extensions/KHR_lights_punctual/lights/{}/intensity", [new LightAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "intensity", getFloat, () => 1)]); -SetInterpolationForKey("/extensions/KHR_lights_punctual/lights/{}/range", [new LightAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "range", getFloat, () => 1)]); -SetInterpolationForKey("/extensions/KHR_lights_punctual/lights/{}/spot/innerConeAngle", [ +AddInterpolation("/extensions/KHR_lights_punctual/lights/{}/color", [new LightAnimationPropertyInfo(Animation.ANIMATIONTYPE_COLOR3, "diffuse", getColor3, () => 3)]); +AddInterpolation("/extensions/KHR_lights_punctual/lights/{}/intensity", [new LightAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "intensity", getFloat, () => 1)]); +AddInterpolation("/extensions/KHR_lights_punctual/lights/{}/range", [new LightAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "range", getFloat, () => 1)]); +AddInterpolation("/extensions/KHR_lights_punctual/lights/{}/spot/innerConeAngle", [ new LightAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "innerAngle", getFloatBy2, () => 1), ]); -SetInterpolationForKey("/extensions/KHR_lights_punctual/lights/{}/spot/outerConeAngle", [ - new LightAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "angle", getFloatBy2, () => 1), -]); +AddInterpolation("/extensions/KHR_lights_punctual/lights/{}/spot/outerConeAngle", [new LightAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "angle", getFloatBy2, () => 1)]); -SetInterpolationForKey("/nodes/{}/extensions/EXT_lights_ies/color", [new LightAnimationPropertyInfo(Animation.ANIMATIONTYPE_COLOR3, "diffuse", getColor3, () => 3)]); -SetInterpolationForKey("/nodes/{}/extensions/EXT_lights_ies/multiplier", [new LightAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "intensity", getFloat, () => 1)]); +AddInterpolation("/nodes/{}/extensions/EXT_lights_ies/color", [new LightAnimationPropertyInfo(Animation.ANIMATIONTYPE_COLOR3, "diffuse", getColor3, () => 3)]); +AddInterpolation("/nodes/{}/extensions/EXT_lights_ies/multiplier", [new LightAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "intensity", getFloat, () => 1)]); 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 730df0b8374..37f0399290d 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 @@ -97,26 +97,29 @@ export class KHR_animation_pointer implements IGLTFLoaderExtension { throw new Error(`${extensionContext}: Pointer is missing`); } + let obj: { object: any; info: any }; try { - const obj = this._pathToObjectConverter.convert(pointer); - if (!obj.info.interpolation) { - throw new Error(`${extensionContext}/pointer: Interpolation is missing`); - } - return this._loader._loadAnimationChannelFromTargetInfoAsync( - context, - animationContext, - animation, - channel, - { - object: obj.object, - info: obj.info.interpolation, - }, - onLoad - ); + obj = this._pathToObjectConverter.convert(pointer); } catch (e) { Logger.Warn(`${extensionContext}/pointer: Invalid pointer (${pointer}) skipped`); return null; } + + if (!obj.info.interpolation) { + throw new Error(`${extensionContext}/pointer: Interpolation is missing`); + } + + return this._loader._loadAnimationChannelFromTargetInfoAsync( + context, + animationContext, + animation, + channel, + { + object: obj.object, + info: obj.info.interpolation, + }, + onLoad + ); } } diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_interactivity.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_interactivity.ts index 308095d9ae6..1b7a2ffadc5 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_interactivity.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_interactivity.ts @@ -1,17 +1,16 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import type { IKHRInteractivity } from "babylonjs-gltf2interface"; import type { GLTFLoader } from "../glTFLoader"; import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; import { FlowGraphCoordinator } from "core/FlowGraph/flowGraphCoordinator"; import { ParseFlowGraphAsync } from "core/FlowGraph/flowGraphParser"; import { registerGLTFExtension, unregisterGLTFExtension } from "../glTFLoaderExtensionRegistry"; import type { GLTFPathToObjectConverter } from "./gltfPathToObjectConverter"; -import { AddObjectAccessorToKey, GetPathToObjectConverter } from "./objectModelMapping"; +import { AddObjectAccessor, SetTargetObject, GetPathToObjectConverter } from "./objectModelMapping"; import { InteractivityGraphToFlowGraphParser } from "./KHR_interactivity/interactivityGraphParser"; import { addToBlockFactory } from "core/FlowGraph/Blocks/flowGraphBlockFactory"; import { Quaternion, Vector3 } from "core/Maths/math.vector"; -import type { Scene } from "core/scene"; -import type { IAnimation } from "../glTFLoaderInterfaces"; +import type { IAnimation, IScene, IKHRInteractivity } from "../glTFLoaderInterfaces"; +import type { Nullable } from "core/types"; const NAME = "KHR_interactivity"; @@ -39,144 +38,165 @@ export class KHR_interactivity implements IGLTFLoaderExtension { */ public enabled: boolean; - private _pathConverter?: GLTFPathToObjectConverter; + private readonly _pathConverter: GLTFPathToObjectConverter; + + private _loader?: GLTFLoader; + private _coordinator?: FlowGraphCoordinator; /** * @internal * @param _loader */ - constructor(private _loader: GLTFLoader) { + constructor(_loader: GLTFLoader) { + this._loader = _loader; this.enabled = this._loader.isExtensionUsed(NAME); this._pathConverter = GetPathToObjectConverter(this._loader.gltf); // avoid starting animations automatically. _loader._skipStartAnimationStep = true; - // Update object model with new pointers - - const scene = _loader.babylonScene; - if (scene) { - _AddInteractivityObjectModel(scene); + // Give the interactivity object a reference to the scene. + const interactivity = this._loader.gltf.extensions?.KHR_interactivity as IKHRInteractivity; + if (interactivity) { + interactivity._babylonScene = _loader.babylonScene; } } public dispose() { - (this._loader as any) = null; - delete this._pathConverter; + delete this._loader; + delete this._coordinator; } - // eslint-disable-next-line no-restricted-syntax, @typescript-eslint/no-misused-promises - public async onReady(): Promise { - if (!this._loader.babylonScene || !this._pathConverter) { - return; + /** @internal */ + public onReady(): void { + if (this._coordinator) { + this._coordinator.start(); } - const scene = this._loader.babylonScene; - const interactivityDefinition = this._loader.gltf.extensions?.KHR_interactivity as IKHRInteractivity; - if (!interactivityDefinition) { - // This can technically throw, but it's not a critical error - return; + } + + /** @internal */ + // eslint-disable-next-line no-restricted-syntax + public loadSceneAsync(context: string, scene: IScene): Nullable> { + if (!this._loader) { + return null; } - const coordinator = new FlowGraphCoordinator({ scene }); - coordinator.dispatchEventsSynchronously = false; // glTF interactivity dispatches events asynchronously - const graphs = interactivityDefinition.graphs.map((graph) => { - const parser = new InteractivityGraphToFlowGraphParser(graph, this._loader.gltf, this._loader.parent.targetFps); - return parser.serializeToFlowGraph(); - }); - // parse each graph async - await Promise.all(graphs.map(async (graph) => await ParseFlowGraphAsync(graph, { coordinator, pathConverter: this._pathConverter }))); + return this._loader.loadSceneAsync(context, scene).then(() => { + if (!this._loader) { + return; + } + + const scene = this._loader.babylonScene; + const gltf = this._loader.gltf; + const targetFps = this._loader.parent.targetFps; + const interactivityDefinition = gltf.extensions?.KHR_interactivity as IKHRInteractivity; + if (!interactivityDefinition) { + // This can technically throw, but it's not a critical error + return; + } - coordinator.start(); + const coordinator = new FlowGraphCoordinator({ scene }); + coordinator.dispatchEventsSynchronously = false; // glTF interactivity dispatches events asynchronously + + const graphs = interactivityDefinition.graphs.map((graph) => { + const parser = new InteractivityGraphToFlowGraphParser(graph, gltf, targetFps); + return parser.serializeToFlowGraph(); + }); + + return Promise.all(graphs.map((graph) => ParseFlowGraphAsync(graph, { coordinator, pathConverter: this._pathConverter }))).then(() => { + this._coordinator = coordinator; + }); + }); } } -/** - * @internal - * populates the object model with the interactivity extension - */ -export function _AddInteractivityObjectModel(scene: Scene) { - // Note - all of those are read-only, as per the specs! - - // active camera rotation - AddObjectAccessorToKey("/extensions/KHR_interactivity/?/activeCamera/rotation", { - get: () => { - if (!scene.activeCamera) { - return new Quaternion(NaN, NaN, NaN, NaN); - } - const quat = Quaternion.FromRotationMatrix(scene.activeCamera.getWorldMatrix()).normalize(); - if (!scene.useRightHandedSystem) { - quat.w *= -1; // glTF uses right-handed system, while babylon uses left-handed - quat.x *= -1; // glTF uses right-handed system, while babylon uses left-handed - } - return quat; - }, - type: "Quaternion", - getTarget: () => scene.activeCamera, - }); - // activeCamera position - AddObjectAccessorToKey("/extensions/KHR_interactivity/?/activeCamera/position", { - get: () => { - if (!scene.activeCamera) { - return new Vector3(NaN, NaN, NaN); - } - const pos = scene.activeCamera.getWorldMatrix().getTranslation(); // not global position - if (!scene.useRightHandedSystem) { - pos.x *= -1; // glTF uses right-handed system, while babylon uses left-handed - } - return pos; - }, - type: "Vector3", - getTarget: () => scene.activeCamera, - }); - - // /animations/{} pointers: - AddObjectAccessorToKey("/animations/{}/extensions/KHR_interactivity/isPlaying", { - get: (animation: IAnimation) => { - return animation._babylonAnimationGroup?.isPlaying ?? false; - }, - type: "boolean", - getTarget: (animation: IAnimation) => { - return animation._babylonAnimationGroup; - }, - }); - AddObjectAccessorToKey("/animations/{}/extensions/KHR_interactivity/minTime", { - get: (animation: IAnimation) => { - return (animation._babylonAnimationGroup?.from ?? 0) / 60; // fixed factor for duration-to-frames conversion - }, - type: "number", - getTarget: (animation: IAnimation) => { - return animation._babylonAnimationGroup; - }, - }); - AddObjectAccessorToKey("/animations/{}/extensions/KHR_interactivity/maxTime", { - get: (animation: IAnimation) => { - return (animation._babylonAnimationGroup?.to ?? 0) / 60; // fixed factor for duration-to-frames conversion - }, - type: "number", - getTarget: (animation: IAnimation) => { - return animation._babylonAnimationGroup; - }, - }); - // playhead - AddObjectAccessorToKey("/animations/{}/extensions/KHR_interactivity/playhead", { - get: (animation: IAnimation) => { - return (animation._babylonAnimationGroup?.getCurrentFrame() ?? 0) / 60; // fixed factor for duration-to-frames conversion - }, - type: "number", - getTarget: (animation: IAnimation) => { - return animation._babylonAnimationGroup; - }, - }); - //virtualPlayhead - TODO, do we support this property in our animations? getCurrentFrame is the only method we have for this. - AddObjectAccessorToKey("/animations/{}/extensions/KHR_interactivity/virtualPlayhead", { - get: (animation: IAnimation) => { - return (animation._babylonAnimationGroup?.getCurrentFrame() ?? 0) / 60; // fixed factor for duration-to-frames conversion - }, - type: "number", - getTarget: (animation: IAnimation) => { - return animation._babylonAnimationGroup; - }, - }); -} +// Add object accessors to object model. +// Note - all of those are read-only, as per the specs! + +// Add a target object for the extension for access to scene properties +SetTargetObject("/extensions/KHR_interactivity"); + +// active camera rotation +AddObjectAccessor("/extensions/KHR_interactivity/activeCamera/rotation", { + get: (interactivity: IKHRInteractivity) => { + const scene = interactivity._babylonScene!; + if (!scene.activeCamera) { + return new Quaternion(NaN, NaN, NaN, NaN); + } + const quat = Quaternion.FromRotationMatrix(scene.activeCamera.getWorldMatrix()).normalize(); + if (!scene.useRightHandedSystem) { + quat.w *= -1; // glTF uses right-handed system, while babylon uses left-handed + quat.x *= -1; // glTF uses right-handed system, while babylon uses left-handed + } + return quat; + }, + type: "Quaternion", + getTarget: (interactivity: IKHRInteractivity) => interactivity._babylonScene!.activeCamera, +}); +// activeCamera position +AddObjectAccessor("/extensions/KHR_interactivity/activeCamera/position", { + get: (interactivity: IKHRInteractivity) => { + const scene = interactivity._babylonScene!; + if (!scene.activeCamera) { + return new Vector3(NaN, NaN, NaN); + } + const pos = scene.activeCamera.getWorldMatrix().getTranslation(); // not global position + if (!scene.useRightHandedSystem) { + pos.x *= -1; // glTF uses right-handed system, while babylon uses left-handed + } + return pos; + }, + type: "Vector3", + getTarget: (interactivity: IKHRInteractivity) => interactivity._babylonScene!.activeCamera, +}); + +// /animations/{} pointers: +AddObjectAccessor("/animations/{}/extensions/KHR_interactivity/isPlaying", { + get: (animation: IAnimation) => { + return animation._babylonAnimationGroup?.isPlaying ?? false; + }, + type: "boolean", + getTarget: (animation: IAnimation) => { + return animation._babylonAnimationGroup; + }, +}); +AddObjectAccessor("/animations/{}/extensions/KHR_interactivity/minTime", { + get: (animation: IAnimation) => { + return (animation._babylonAnimationGroup?.from ?? 0) / 60; // fixed factor for duration-to-frames conversion + }, + type: "number", + getTarget: (animation: IAnimation) => { + return animation._babylonAnimationGroup; + }, +}); +AddObjectAccessor("/animations/{}/extensions/KHR_interactivity/maxTime", { + get: (animation: IAnimation) => { + return (animation._babylonAnimationGroup?.to ?? 0) / 60; // fixed factor for duration-to-frames conversion + }, + type: "number", + getTarget: (animation: IAnimation) => { + return animation._babylonAnimationGroup; + }, +}); +// playhead +AddObjectAccessor("/animations/{}/extensions/KHR_interactivity/playhead", { + get: (animation: IAnimation) => { + return (animation._babylonAnimationGroup?.getCurrentFrame() ?? 0) / 60; // fixed factor for duration-to-frames conversion + }, + type: "number", + getTarget: (animation: IAnimation) => { + return animation._babylonAnimationGroup; + }, +}); +// virtualPlayhead - TODO, do we support this property in our animations? getCurrentFrame is the only method we have for this. +AddObjectAccessor("/animations/{}/extensions/KHR_interactivity/virtualPlayhead", { + get: (animation: IAnimation) => { + return (animation._babylonAnimationGroup?.getCurrentFrame() ?? 0) / 60; // fixed factor for duration-to-frames conversion + }, + type: "number", + getTarget: (animation: IAnimation) => { + return animation._babylonAnimationGroup; + }, +}); // Register flow graph blocks. Do it here so they are available when the extension is enabled. addToBlockFactory(NAME, "FlowGraphGLTFDataProvider", async () => { diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_variants.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_variants.ts index a18f3d9594b..d0a90256bb8 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_variants.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_variants.ts @@ -249,9 +249,7 @@ export class KHR_materials_variants implements IGLTFLoaderExtension { } } - /** - * @internal - */ + /** @internal */ // eslint-disable-next-line no-restricted-syntax public _loadMeshPrimitiveAsync( context: string, diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_node_hoverability.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_node_hoverability.ts index 0e5ef9c3af4..f1fc8ee168d 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_node_hoverability.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_node_hoverability.ts @@ -4,7 +4,7 @@ import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; import { registerGLTFExtension, unregisterGLTFExtension } from "../glTFLoaderExtensionRegistry"; import { addNewInteractivityFlowGraphMapping } from "./KHR_interactivity/declarationMapper"; import type { INode } from "../glTFLoaderInterfaces"; -import { AddObjectAccessorToKey } from "./objectModelMapping"; +import { AddObjectAccessor } from "./objectModelMapping"; const NAME = "KHR_node_hoverability"; @@ -156,7 +156,7 @@ addNewInteractivityFlowGraphMapping("event/onHoverOut", NAME, { }, }); -AddObjectAccessorToKey("/nodes/{}/extensions/KHR_node_hoverability/hoverable", { +AddObjectAccessor("/nodes/{}/extensions/KHR_node_hoverability/hoverable", { get: (node: INode) => { const tn = node._babylonTransformNode as any; if (tn && tn.pointerOverDisableMeshTesting !== undefined) { @@ -191,16 +191,14 @@ export class KHR_node_hoverability implements IGLTFLoaderExtension { private _loader: GLTFLoader; - /** - * @internal - */ + /** @internal */ constructor(loader: GLTFLoader) { this._loader = loader; this.enabled = loader.isExtensionUsed(NAME); } - // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-misused-promises - public async onReady(): Promise { + /** @internal */ + public onReady(): void { this._loader.gltf.nodes?.forEach((node) => { // default is true, so only apply if false if (node.extensions?.KHR_node_hoverability && node.extensions?.KHR_node_hoverability.hoverable === false) { diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_node_selectability.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_node_selectability.ts index 80caea0758b..8de6906f4a9 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_node_selectability.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_node_selectability.ts @@ -4,7 +4,7 @@ import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; import { registerGLTFExtension, unregisterGLTFExtension } from "../glTFLoaderExtensionRegistry"; import { addNewInteractivityFlowGraphMapping } from "./KHR_interactivity/declarationMapper"; import type { INode } from "../glTFLoaderInterfaces"; -import { AddObjectAccessorToKey } from "./objectModelMapping"; +import { AddObjectAccessor } from "./objectModelMapping"; const NAME = "KHR_node_selectability"; @@ -90,7 +90,7 @@ addNewInteractivityFlowGraphMapping("event/onSelect", NAME, { }); // object model extension for selectable -AddObjectAccessorToKey("/nodes/{}/extensions/KHR_node_selectability/selectable", { +AddObjectAccessor("/nodes/{}/extensions/KHR_node_selectability/selectable", { get: (node: INode) => { const tn = node._babylonTransformNode as any; if (tn && tn.isPickable !== undefined) { @@ -124,16 +124,14 @@ export class KHR_node_selectability implements IGLTFLoaderExtension { private _loader: GLTFLoader; - /** - * @internal - */ + /** @internal */ constructor(loader: GLTFLoader) { this._loader = loader; this.enabled = loader.isExtensionUsed(NAME); } - // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-misused-promises - public async onReady(): Promise { + /** @internal */ + public onReady(): void { this._loader.gltf.nodes?.forEach((node) => { if (node.extensions?.KHR_node_selectability && node.extensions?.KHR_node_selectability.selectable === false) { node._babylonTransformNode?.getChildMeshes().forEach((mesh) => { diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_node_visibility.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_node_visibility.ts index fc57756d63d..e4823df069d 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_node_visibility.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_node_visibility.ts @@ -3,7 +3,7 @@ import type { GLTFLoader } from "../glTFLoader"; import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; import { registerGLTFExtension, unregisterGLTFExtension } from "../glTFLoaderExtensionRegistry"; import type { INode } from "../glTFLoaderInterfaces"; -import { AddObjectAccessorToKey } from "./objectModelMapping"; +import { AddObjectAccessor } from "./objectModelMapping"; const NAME = "KHR_node_visibility"; @@ -19,7 +19,7 @@ declare module "../../glTFFileLoader" { } // object model extension for visibility -AddObjectAccessorToKey("/nodes/{}/extensions/KHR_node_visibility/visible", { +AddObjectAccessor("/nodes/{}/extensions/KHR_node_visibility/visible", { get: (node: INode) => { const tn = node._babylonTransformNode as any; if (tn && tn.isVisible !== undefined) { @@ -59,16 +59,14 @@ export class KHR_node_visibility implements IGLTFLoaderExtension { private _loader: GLTFLoader; - /** - * @internal - */ + /** @internal */ constructor(loader: GLTFLoader) { this._loader = loader; this.enabled = loader.isExtensionUsed(NAME); } - // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-misused-promises - public async onReady(): Promise { + /** @internal */ + public onReady(): void { this._loader.gltf.nodes?.forEach((node) => { node._primitiveBabylonMeshes?.forEach((mesh) => { mesh.inheritVisibility = true; diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/gltfPathToObjectConverter.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/gltfPathToObjectConverter.ts index 7766a216fdb..915be0e2475 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/gltfPathToObjectConverter.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/gltfPathToObjectConverter.ts @@ -2,20 +2,6 @@ import type { IObjectInfo, IPathToObjectConverter } from "core/ObjectModel/objec import type { IGLTF } from "../glTFLoaderInterfaces"; import type { IObjectAccessor } from "core/FlowGraph/typeDefinitions"; -/** - * Adding an exception here will break traversing through the glTF object tree. - * This is used for properties that might not be in the glTF object model, but are optional and have a default value. - * For example, the path /nodes/\{\}/extensions/KHR_node_visibility/visible is optional - the object can be deferred without the object fully existing. - */ -export const OptionalPathExceptionsList: { - regex: RegExp; -}[] = [ - { - // get the node as object when reading an extension - regex: new RegExp(`^/nodes/\\d+/extensions/`), - }, -]; - /** * A converter that takes a glTF Object Model JSON Pointer * and transforms it into an ObjectAccessorContainer, allowing @@ -42,7 +28,7 @@ export class GLTFPathToObjectConverter implements * * Examples: * - "/nodes/0/rotation" - * - "/nodes.length" + * - "/nodes.length" * - "/materials/2/emissiveFactor" * - "/materials/2/pbrMetallicRoughness/baseColorFactor" * - "/materials/2/extensions/KHR_materials_emissive_strength/emissiveStrength" @@ -61,51 +47,40 @@ export class GLTFPathToObjectConverter implements const parts = path.split("/"); parts.shift(); - //if the last part has ".length" in it, separate that as an extra part - if (parts[parts.length - 1].includes(".length")) { + // If the last part ends with `.length`, separate that as an extra part + if (parts[parts.length - 1].endsWith(".length")) { const lastPart = parts[parts.length - 1]; const split = lastPart.split("."); parts.pop(); parts.push(...split); } - let ignoreObjectTree = false; + let index: number | undefined = undefined; for (const part of parts) { - const isLength = part === "length"; - if (isLength && !infoTree.__array__) { - throw new Error(`Path ${path} is invalid`); - } - if (infoTree.__ignoreObjectTree__) { - ignoreObjectTree = true; - } - if (infoTree.__array__ && !isLength) { - infoTree = infoTree.__array__; - } else { + if (infoTree[part]) { infoTree = infoTree[part]; - if (!infoTree) { - throw new Error(`Path ${path} is invalid`); + } else if (infoTree["{}"]) { + infoTree = infoTree["{}"]; + if (target) { + index = parseInt(part, 10); } + } else { + throw new Error(`Path ${path} is invalid`); } - if (!ignoreObjectTree) { - if (objectTree === undefined) { - // check if the path is in the exception list. If it is, break and return the last object that was found - const exception = OptionalPathExceptionsList.find((e) => e.regex.test(path)); - if (!exception) { - throw new Error(`Path ${path} is invalid`); - } - } else if (!isLength) { - objectTree = objectTree?.[part]; - } + + if (!target) { + objectTree = objectTree[part]; } - if (infoTree.__target__ || isLength) { + if (infoTree.__target__) { target = objectTree; } } return { object: target, + index: index, info: infoTree, }; } diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/objectModelMapping.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/objectModelMapping.ts index e47b3443384..0b96fa5083a 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/objectModelMapping.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/objectModelMapping.ts @@ -25,17 +25,17 @@ export interface IGLTFObjectModelTree { extensions: IGLTFObjectModelTreeExtensionsObject; animations: { length: IObjectAccessor; - __array__: {}; + "{}": {}; }; meshes: { length: IObjectAccessor; - __array__: {}; + "{}": {}; }; } export interface IGLTFObjectModelTreeNodesObject { length: IObjectAccessor; - __array__: { + "{}": { __target__: boolean; translation: IObjectAccessor; rotation: IObjectAccessor; @@ -44,7 +44,7 @@ export interface IGLTFObjectModelTreeNodesObject; weights: { length: IObjectAccessor; - __array__: { __target__: boolean } & IObjectAccessor; + "{}": IObjectAccessor; } & IObjectAccessor; extensions: { EXT_lights_ies?: { @@ -56,7 +56,7 @@ export interface IGLTFObjectModelTreeNodesObject; @@ -74,7 +74,7 @@ export interface IGLTFObjectModelTreeCamerasObject { } export interface IGLTFObjectModelTreeMaterialsObject { - __array__: { + "{}": { __target__: boolean; pbrMetallicRoughness: { baseColorFactor: IObjectAccessor; @@ -240,7 +240,7 @@ export interface IGLTFObjectModelTreeExtensionsObject { KHR_lights_punctual: { lights: { length: IObjectAccessor; - __array__: { + "{}": { __target__: boolean; color: IObjectAccessor; intensity: IObjectAccessor; @@ -259,7 +259,7 @@ export interface IGLTFObjectModelTreeExtensionsObject { }; EXT_lights_image_based: { lights: { - __array__: { + "{}": { __target__: boolean; intensity: IObjectAccessor; rotation: IObjectAccessor; @@ -276,7 +276,7 @@ const nodesTree: IGLTFObjectModelTreeNodesObject = { getTarget: (nodes: INode[]) => nodes.map((node) => node._babylonTransformNode!), getPropertyName: [() => "length"], }, - __array__: { + "{}": { __target__: true, translation: { type: "Vector3", @@ -304,21 +304,33 @@ const nodesTree: IGLTFObjectModelTreeNodesObject = { type: "number", get: (node: INode) => node._numMorphTargets, getTarget: (node: INode) => node._babylonTransformNode, - getPropertyName: [() => "influence"], }, - __array__: { - __target__: true, + "{}": { type: "number", - get: (node: INode, index?: number) => (index !== undefined ? node._primitiveBabylonMeshes?.[0].morphTargetManager?.getTarget(index).influence : undefined), - // set: (value: number, node: INode, index?: number) => node._babylonTransformNode?.getMorphTargetManager()?.getTarget(index)?.setInfluence(value), + get: (node: INode, arrayIndex?: number) => node._primitiveBabylonMeshes![0].morphTargetManager!.getTarget(arrayIndex!).influence, + set: (value: number, node: INode, arrayIndex?: number) => { + for (const primitive of node._primitiveBabylonMeshes!) { + const target = primitive.morphTargetManager!.getTarget(arrayIndex!); + if (target) { + target.influence = value; + } + } + }, getTarget: (node: INode) => node._babylonTransformNode, - getPropertyName: [() => "influence"], }, type: "number[]", - get: (node: INode, index?: number) => [0], // TODO: get the weights correctly - // set: (value: number, node: INode, index?: number) => node._babylonTransformNode?.getMorphTargetManager()?.getTarget(index)?.setInfluence(value), + get: (node: INode) => Array.from({ length: node._numMorphTargets! }, (_, index) => node._primitiveBabylonMeshes![0].morphTargetManager!.getTarget(index).influence!), + set: (value: number[], node: INode) => { + for (const primitive of node._primitiveBabylonMeshes!) { + for (let i = 0; i < value.length; i++) { + const target = primitive.morphTargetManager!.getTarget(i); + if (target) { + target.influence = value[i]; + } + } + } + }, getTarget: (node: INode) => node._babylonTransformNode, - getPropertyName: [() => "influence"], }, // readonly! matrix: { @@ -397,7 +409,7 @@ const animationsTree = { getTarget: (animations: IAnimation[]) => animations.map((animation) => animation._babylonAnimationGroup!), getPropertyName: [() => "length"], }, - __array__: {}, + "{}": {}, }; const meshesTree = { @@ -407,11 +419,11 @@ const meshesTree = { getTarget: (meshes: IMesh[]) => meshes.map((mesh) => mesh.primitives[0]._instanceData?.babylonSourceMesh), getPropertyName: [() => "length"], }, - __array__: {}, + "{}": {}, }; const camerasTree: IGLTFObjectModelTreeCamerasObject = { - __array__: { + "{}": { __target__: true, orthographic: { xmag: { @@ -509,7 +521,7 @@ const camerasTree: IGLTFObjectModelTreeCamerasObject = { }; const materialsTree: IGLTFObjectModelTreeMaterialsObject = { - __array__: { + "{}": { __target__: true, emissiveFactor: { type: "Color3", @@ -861,7 +873,7 @@ const extensionsTree: IGLTFObjectModelTreeExtensionsObject = { getTarget: (lights: IKHRLightsPunctual_Light[]) => lights.map((light) => light._babylonLight!), getPropertyName: [(_lights: IKHRLightsPunctual_Light[]) => "length"], }, - __array__: { + "{}": { __target__: true, color: { type: "Color3", @@ -921,7 +933,7 @@ const extensionsTree: IGLTFObjectModelTreeExtensionsObject = { getTarget: (lights) => lights.map((light) => light._babylonTexture!), getPropertyName: [(_lights) => "length"], }, - __array__: { + "{}": { __target__: true, intensity: { type: "number", @@ -1018,6 +1030,40 @@ const objectModelMapping: IGLTFObjectModelTree = { meshes: meshesTree, }; +function GetMapping(path: string): any { + if (!path.startsWith("/")) { + throw new Error(`Path "${path}" must start with a slash (/)`); + } + + const parts = path.split("/"); + let current = objectModelMapping as any; + for (let index = 1; index < parts.length; index++) { + current = current[parts[index]]; + } + + return current; +} + +function EnsureMapping(path: string): any { + if (!path.startsWith("/")) { + throw new Error(`Path "${path}" must start with a slash (/)`); + } + + const keyParts = path.split("/"); + let current = objectModelMapping as any; + for (let index = 1; index < keyParts.length; index++) { + const key = keyParts[index]; + current[key] ||= {}; + current = current[key]; + } + + return current; +} + +function IsObjectAccessor(current: any): current is IObjectAccessor { + return current && current.type && current.get; +} + /** * get a path-to-object converter for the given glTF tree * @param gltf the glTF tree to use @@ -1028,81 +1074,53 @@ export function GetPathToObjectConverter(gltf: IGLTF) { } /** - * This function will return the object accessor for the given key in the object model - * If the key is not found, it will return undefined - * @param key the key to get the mapping for, for example /materials/\{\}/emissiveFactor + * This function will return the object accessor for the given path in the object model + * @param path the path for the object accessor * @returns an object accessor for the given key, or undefined if the key is not found */ -export function GetMappingForKey(key: string): IObjectAccessor | undefined { - // replace every `{}` in key with __array__ to match the object model - const keyParts = key.split("/").map((part) => part.replace(/{}/g, "__array__")); - let current = objectModelMapping as any; - for (const part of keyParts) { - // make sure part is not empty - if (!part) { - continue; - } - current = current[part]; - } - // validate that current is an object accessor - if (current && current.type && current.get) { - return current; - } - return undefined; +export function GetObjectAccessor(path: string): IObjectAccessor | undefined { + const current = GetMapping(path); + return IsObjectAccessor(current) ? current : undefined; } /** - * Set interpolation for a specific key in the object model - * @param key the key to set, for example /materials/\{\}/emissiveFactor - * @param interpolation the interpolation elements array + * This will set a new target object in the object model at the given path. + * @param path the path to set as the target object. */ -export function SetInterpolationForKey(key: string, interpolation?: IInterpolationPropertyInfo[]): void { - // replace every `{}` in key with __array__ to match the object model - const keyParts = key.split("/").map((part) => part.replace(/{}/g, "__array__")); - let current = objectModelMapping as any; - for (const part of keyParts) { - // make sure part is not empty - if (!part) { - continue; - } - current = current[part]; - } - // validate that the current object is an object accessor - if (current && current.type && current.get) { - (current as IObjectAccessor).interpolation = interpolation; - } +export function SetTargetObject(path: string): void { + const current = EnsureMapping(path); + current.__target__ = true; } /** - * This will ad a new object accessor in the object model at the given key. - * Note that this will NOT change the typescript types. To do that you will need to change the interface itself (extending it in the module that uses it) - * @param key the key to add the object accessor at. For example /cameras/\{\}/perspective/aspectRatio + * This will set a new object accessor in the object model at the given path. + * @param path the path for the object accessor * @param accessor the object accessor to add */ -export function AddObjectAccessorToKey( - key: string, - accessor: IObjectAccessor -): void { - // replace every `{}` in key with __array__ to match the object model - const keyParts = key.split("/").map((part) => part.replace(/{}/g, "__array__")); - let current = objectModelMapping as any; - for (const part of keyParts) { - // make sure part is not empty - if (!part) { - continue; - } - if (!current[part]) { - if (part === "?") { - current.__ignoreObjectTree__ = true; - continue; - } - current[part] = {}; - // if the part is __array__ then add the __target__ property - if (part === "__array__") { - current[part].__target__ = true; - } - } - current = current[part]; +export function AddObjectAccessor(path: string, accessor: IObjectAccessor): void { + const current = EnsureMapping(path); + if (IsObjectAccessor(current)) { + throw new Error(`Path "${path}" already points to a valid object accessor.`); } Object.assign(current, accessor); } + +/** + * Set the interpolation for a object accessor in the object model + * @param path the object accessor path for the interpolation + * @param interpolation the interpolation elements array + */ +export function AddInterpolation(path: string, interpolation: IInterpolationPropertyInfo[]): void { + const current = EnsureMapping(path); + + // TODO: don't throw for now because we don't have object accessors for all the extensions yet + // if (!IsObjectAccessor(current)) { + // throw new Error(`Path "${path}" does not point to a valid object accessor.`); + // } + + if (current.interpolation) { + throw new Error(`Path "${path}" already has an interpolation defined.`); + } + + current.interpolation = interpolation; +} diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts index b199e919678..593656d08e7 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts @@ -80,7 +80,7 @@ import type { IObjectInfo } from "core/ObjectModel/objectModelInterfaces"; import { registeredGLTFExtensions, registerGLTFExtension, unregisterGLTFExtension } from "./glTFLoaderExtensionRegistry"; import type { GLTFExtensionFactory } from "./glTFLoaderExtensionRegistry"; import type { IInterpolationPropertyInfo } from "core/FlowGraph/typeDefinitions"; -import { GetMappingForKey } from "./Extensions/objectModelMapping"; +import { GetObjectAccessor } from "./Extensions/objectModelMapping"; import { deepMerge } from "core/Misc/deepMerger"; import { GetTypedArrayConstructor } from "core/Buffers/bufferUtils"; @@ -1724,19 +1724,19 @@ export class GLTFLoader implements IGLTFLoader { let properties: IInterpolationPropertyInfo[]; switch (channelTargetPath) { case AnimationChannelTargetPath.TRANSLATION: { - properties = GetMappingForKey("/nodes/{}/translation")?.interpolation!; + properties = GetObjectAccessor("/nodes/{}/translation")?.interpolation!; break; } case AnimationChannelTargetPath.ROTATION: { - properties = GetMappingForKey("/nodes/{}/rotation")?.interpolation!; + properties = GetObjectAccessor("/nodes/{}/rotation")?.interpolation!; break; } case AnimationChannelTargetPath.SCALE: { - properties = GetMappingForKey("/nodes/{}/scale")?.interpolation!; + properties = GetObjectAccessor("/nodes/{}/scale")?.interpolation!; break; } case AnimationChannelTargetPath.WEIGHTS: { - properties = GetMappingForKey("/nodes/{}/weights")?.interpolation!; + properties = GetObjectAccessor("/nodes/{}/weights")?.interpolation!; break; } default: { diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoaderAnimation.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoaderAnimation.ts index a9bc6e02e5c..a32d5fc556a 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoaderAnimation.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoaderAnimation.ts @@ -2,7 +2,7 @@ import { Animation } from "core/Animations/animation"; import { Quaternion, Vector3 } from "core/Maths/math.vector"; import type { INode } from "./glTFLoaderInterfaces"; import type { IAnimatable } from "core/Animations/animatable.interface"; -import { SetInterpolationForKey } from "./Extensions/objectModelMapping"; +import { AddInterpolation } from "./Extensions/objectModelMapping"; /** @internal */ export type GetValueFn = (target: any, source: Float32Array, offset: number, scale: number) => any; @@ -93,7 +93,7 @@ export class WeightAnimationPropertyInfo extends AnimationPropertyInfo { } } -SetInterpolationForKey("/nodes/{}/translation", [new TransformNodeAnimationPropertyInfo(Animation.ANIMATIONTYPE_VECTOR3, "position", getVector3, () => 3)]); -SetInterpolationForKey("/nodes/{}/rotation", [new TransformNodeAnimationPropertyInfo(Animation.ANIMATIONTYPE_QUATERNION, "rotationQuaternion", getQuaternion, () => 4)]); -SetInterpolationForKey("/nodes/{}/scale", [new TransformNodeAnimationPropertyInfo(Animation.ANIMATIONTYPE_VECTOR3, "scaling", getVector3, () => 3)]); -SetInterpolationForKey("/nodes/{}/weights", [new WeightAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "influence", getWeights, (target) => target._numMorphTargets!)]); +AddInterpolation("/nodes/{}/translation", [new TransformNodeAnimationPropertyInfo(Animation.ANIMATIONTYPE_VECTOR3, "position", getVector3, () => 3)]); +AddInterpolation("/nodes/{}/rotation", [new TransformNodeAnimationPropertyInfo(Animation.ANIMATIONTYPE_QUATERNION, "rotationQuaternion", getQuaternion, () => 4)]); +AddInterpolation("/nodes/{}/scale", [new TransformNodeAnimationPropertyInfo(Animation.ANIMATIONTYPE_VECTOR3, "scaling", getVector3, () => 3)]); +AddInterpolation("/nodes/{}/weights", [new WeightAnimationPropertyInfo(Animation.ANIMATIONTYPE_FLOAT, "influence", getWeights, (target) => target._numMorphTargets!)]); diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts index d120662d5dd..7e85d864145 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoaderInterfaces.ts @@ -7,6 +7,7 @@ import type { AbstractMesh } from "core/Meshes/abstractMesh"; import type { Mesh } from "core/Meshes/mesh"; import type { Camera } from "core/Cameras/camera"; import type { Light } from "core/Lights/light"; +import type { Scene } from "core/scene"; import type * as GLTF2 from "babylonjs-gltf2interface"; @@ -312,3 +313,9 @@ export interface IEXTLightsIES_Light extends GLTF2.IEXTLightsIES_Light, IArrayIt /** @hidden */ _babylonLight?: Light; } + +/** @internal */ +export interface IKHRInteractivity extends GLTF2.IKHRInteractivity { + /** @hidden */ + _babylonScene?: Scene; +} diff --git a/packages/dev/loaders/test/unit/Interactivity/objectModel.test.ts b/packages/dev/loaders/test/unit/Interactivity/objectModel.test.ts index aa591176698..75339380c82 100644 --- a/packages/dev/loaders/test/unit/Interactivity/objectModel.test.ts +++ b/packages/dev/loaders/test/unit/Interactivity/objectModel.test.ts @@ -8,7 +8,7 @@ import { ParseFlowGraphAsync } from "core/FlowGraph"; import { InteractivityGraphToFlowGraphParser } from "loaders/glTF/2.0/Extensions/KHR_interactivity/interactivityGraphParser"; import "loaders/glTF/2.0/glTFLoaderAnimation"; import "loaders/glTF/2.0/Extensions/KHR_animation_pointer.data"; -import { _AddInteractivityObjectModel } from "loaders/glTF/2.0/Extensions/KHR_interactivity"; +import "loaders/glTF/2.0/Extensions/KHR_interactivity"; import { GetPathToObjectConverter } from "loaders/glTF/2.0/Extensions/objectModelMapping"; import { IKHRInteractivity_Declaration, IKHRInteractivity_Graph, IKHRInteractivity_Node, IKHRInteractivity_Type, IKHRInteractivity_Variable } from "babylonjs-gltf2interface"; import { Mesh } from "core/Meshes/mesh"; @@ -72,7 +72,6 @@ describe("glTF interactivity Object Model", () => { new ArcRotateCamera("", Math.PI / 2, Math.PI / 2, 4, new Vector3(0, 0, 0)); log.mockClear(); errorLog.mockClear(); - _AddInteractivityObjectModel(scene); renderInterval = setInterval(() => scene?.render(), 16); }); @@ -169,7 +168,9 @@ describe("glTF interactivity Object Model", () => { await generateSimpleNodeGraph( { extensions: { - KHR_interactivity: {}, + KHR_interactivity: { + _babylonScene: scene, + }, }, }, [{ op: "pointer/get" }, { op: "flow/log", extension: "BABYLON" }], @@ -206,7 +207,9 @@ describe("glTF interactivity Object Model", () => { await generateSimpleNodeGraph( { extensions: { - KHR_interactivity: {}, + KHR_interactivity: { + _babylonScene: scene, + }, }, }, [{ op: "pointer/get" }, { op: "flow/log", extension: "BABYLON" }], diff --git a/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts b/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts index 6578b6e423b..11b00761207 100644 --- a/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts +++ b/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts @@ -691,7 +691,7 @@ declare module BABYLON.GLTF2 { */ mode?: MeshPrimitiveMode; /** - * An array of Morph Targets, each Morph Target is a dictionary mapping attributes (only POSITION, NORMAL, and TANGENT supported) to their deviations in the Morph Target + * An array of morph targets, each morph target is a dictionary mapping attributes (only POSITION, NORMAL, and TANGENT supported) to their deviations in the morph target */ targets?: { [name: string]: number; diff --git a/packages/tools/sandbox/src/sandbox.tsx b/packages/tools/sandbox/src/sandbox.tsx index d39f4591015..7fc149353dc 100644 --- a/packages/tools/sandbox/src/sandbox.tsx +++ b/packages/tools/sandbox/src/sandbox.tsx @@ -93,7 +93,7 @@ export class Sandbox extends React.Component< this._globalState.showDebugLayer(); } - this.setState({ errorMessage: error.message ? `${error.message} Check the developer console.` : "Unable to load scene. Check the developer console." }); + this.setState({ errorMessage: error.message ? error.message : "Unable to load scene. Check the developer console." }); this._engine && this._engine.hideLoadingUI();