UNPKG

@babylonjs/loaders

Version:

For usage documentation please visit https://doc.babylonjs.com/features/featuresDeepDive/importers/loadingFileTypes/.

151 lines 6.61 kB
import { FlowGraphCoordinator } from "@babylonjs/core/FlowGraph/flowGraphCoordinator.js"; import { ParseFlowGraphAsync } from "@babylonjs/core/FlowGraph/flowGraphParser.js"; import { registerGLTFExtension, unregisterGLTFExtension } from "../glTFLoaderExtensionRegistry.js"; import { AddObjectAccessorToKey, GetPathToObjectConverter } from "./objectModelMapping.js"; import { InteractivityGraphToFlowGraphParser } from "./KHR_interactivity/interactivityGraphParser.js"; import { addToBlockFactory } from "@babylonjs/core/FlowGraph/Blocks/flowGraphBlockFactory.js"; import { Quaternion, Vector3 } from "@babylonjs/core/Maths/math.vector.js"; const NAME = "KHR_interactivity"; /** * Loader extension for KHR_interactivity */ export class KHR_interactivity { /** * @internal * @param _loader */ constructor(_loader) { this._loader = _loader; /** * The name of this extension. */ this.name = NAME; 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); } } dispose() { this._loader = null; delete this._pathConverter; } // eslint-disable-next-line no-restricted-syntax, @typescript-eslint/no-misused-promises async onReady() { if (!this._loader.babylonScene || !this._pathConverter) { return; } const scene = this._loader.babylonScene; const interactivityDefinition = this._loader.gltf.extensions?.KHR_interactivity; if (!interactivityDefinition) { // This can technically throw, but it's not a critical error return; } 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 }))); coordinator.start(); } } /** * @internal * populates the object model with the interactivity extension */ export function _AddInteractivityObjectModel(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) => { return animation._babylonAnimationGroup?.isPlaying ?? false; }, type: "boolean", getTarget: (animation) => { return animation._babylonAnimationGroup; }, }); AddObjectAccessorToKey("/animations/{}/extensions/KHR_interactivity/minTime", { get: (animation) => { return (animation._babylonAnimationGroup?.from ?? 0) / 60; // fixed factor for duration-to-frames conversion }, type: "number", getTarget: (animation) => { return animation._babylonAnimationGroup; }, }); AddObjectAccessorToKey("/animations/{}/extensions/KHR_interactivity/maxTime", { get: (animation) => { return (animation._babylonAnimationGroup?.to ?? 0) / 60; // fixed factor for duration-to-frames conversion }, type: "number", getTarget: (animation) => { return animation._babylonAnimationGroup; }, }); // playhead AddObjectAccessorToKey("/animations/{}/extensions/KHR_interactivity/playhead", { get: (animation) => { return (animation._babylonAnimationGroup?.getCurrentFrame() ?? 0) / 60; // fixed factor for duration-to-frames conversion }, type: "number", getTarget: (animation) => { 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) => { return (animation._babylonAnimationGroup?.getCurrentFrame() ?? 0) / 60; // fixed factor for duration-to-frames conversion }, type: "number", getTarget: (animation) => { return animation._babylonAnimationGroup; }, }); } // Register flow graph blocks. Do it here so they are available when the extension is enabled. addToBlockFactory(NAME, "FlowGraphGLTFDataProvider", async () => { return (await import("./KHR_interactivity/flowGraphGLTFDataProvider.js")).FlowGraphGLTFDataProvider; }); unregisterGLTFExtension(NAME); registerGLTFExtension(NAME, true, (loader) => new KHR_interactivity(loader)); //# sourceMappingURL=KHR_interactivity.js.map