123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- import { Vector3, Vector4 } from "../Maths/math.vector.js";
- import { Color3, Color4 } from "../Maths/math.color.js";
- import { Condition, ValueCondition } from "./condition.js";
- import { Action } from "./action.js";
- import { DoNothingAction } from "./directActions.js";
- import { EngineStore } from "../Engines/engineStore.js";
- import { Logger } from "../Misc/logger.js";
- import { DeepCopier } from "../Misc/deepCopier.js";
- import { GetClass } from "../Misc/typeStore.js";
- import { AbstractActionManager } from "./abstractActionManager.js";
- /**
- * Action Manager manages all events to be triggered on a given mesh or the global scene.
- * A single scene can have many Action Managers to handle predefined actions on specific meshes.
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions
- */
- export class ActionManager extends AbstractActionManager {
- /**
- * Creates a new action manager
- * @param scene defines the hosting scene
- */
- constructor(scene) {
- super();
- scene = scene || EngineStore.LastCreatedScene;
- if (!scene) {
- return;
- }
- this._scene = scene;
- scene.actionManagers.push(this);
- }
- // Methods
- /**
- * Releases all associated resources
- */
- dispose() {
- const sceneIndex = this._scene.actionManagers.indexOf(this);
- for (let i = 0; i < this.actions.length; i++) {
- const action = this.actions[i];
- ActionManager.Triggers[action.trigger]--;
- if (ActionManager.Triggers[action.trigger] === 0) {
- delete ActionManager.Triggers[action.trigger];
- }
- }
- this.actions.length = 0;
- if (sceneIndex > -1) {
- this._scene.actionManagers.splice(sceneIndex, 1);
- }
- const ownerMeshes = this._scene.meshes.filter((m) => m.actionManager === this);
- for (const ownerMesh of ownerMeshes) {
- ownerMesh.actionManager = null;
- }
- }
- /**
- * Gets hosting scene
- * @returns the hosting scene
- */
- getScene() {
- return this._scene;
- }
- /**
- * Does this action manager handles actions of any of the given triggers
- * @param triggers defines the triggers to be tested
- * @returns a boolean indicating whether one (or more) of the triggers is handled
- */
- hasSpecificTriggers(triggers) {
- for (let index = 0; index < this.actions.length; index++) {
- const action = this.actions[index];
- if (triggers.indexOf(action.trigger) > -1) {
- return true;
- }
- }
- return false;
- }
- /**
- * Does this action manager handles actions of any of the given triggers. This function takes two arguments for
- * speed.
- * @param triggerA defines the trigger to be tested
- * @param triggerB defines the trigger to be tested
- * @returns a boolean indicating whether one (or more) of the triggers is handled
- */
- hasSpecificTriggers2(triggerA, triggerB) {
- for (let index = 0; index < this.actions.length; index++) {
- const action = this.actions[index];
- if (triggerA == action.trigger || triggerB == action.trigger) {
- return true;
- }
- }
- return false;
- }
- /**
- * Does this action manager handles actions of a given trigger
- * @param trigger defines the trigger to be tested
- * @param parameterPredicate defines an optional predicate to filter triggers by parameter
- * @returns whether the trigger is handled
- */
- hasSpecificTrigger(trigger, parameterPredicate) {
- for (let index = 0; index < this.actions.length; index++) {
- const action = this.actions[index];
- if (action.trigger === trigger) {
- if (parameterPredicate) {
- if (parameterPredicate(action.getTriggerParameter())) {
- return true;
- }
- }
- else {
- return true;
- }
- }
- }
- return false;
- }
- /**
- * Does this action manager has pointer triggers
- */
- get hasPointerTriggers() {
- for (let index = 0; index < this.actions.length; index++) {
- const action = this.actions[index];
- if (action.trigger >= ActionManager.OnPickTrigger && action.trigger <= ActionManager.OnPointerOutTrigger) {
- return true;
- }
- }
- return false;
- }
- /**
- * Does this action manager has pick triggers
- */
- get hasPickTriggers() {
- for (let index = 0; index < this.actions.length; index++) {
- const action = this.actions[index];
- if (action.trigger >= ActionManager.OnPickTrigger && action.trigger <= ActionManager.OnPickUpTrigger) {
- return true;
- }
- }
- return false;
- }
- /**
- * Registers an action to this action manager
- * @param action defines the action to be registered
- * @returns the action amended (prepared) after registration
- */
- registerAction(action) {
- if (action.trigger === ActionManager.OnEveryFrameTrigger) {
- if (this.getScene().actionManager !== this) {
- Logger.Warn("OnEveryFrameTrigger can only be used with scene.actionManager");
- return null;
- }
- }
- this.actions.push(action);
- this.getScene()._registeredActions++;
- if (ActionManager.Triggers[action.trigger]) {
- ActionManager.Triggers[action.trigger]++;
- }
- else {
- ActionManager.Triggers[action.trigger] = 1;
- }
- action._actionManager = this;
- action._prepare();
- return action;
- }
- /**
- * Unregisters an action to this action manager
- * @param action defines the action to be unregistered
- * @returns a boolean indicating whether the action has been unregistered
- */
- unregisterAction(action) {
- const index = this.actions.indexOf(action);
- if (index !== -1) {
- this.actions.splice(index, 1);
- ActionManager.Triggers[action.trigger] -= 1;
- if (ActionManager.Triggers[action.trigger] === 0) {
- delete ActionManager.Triggers[action.trigger];
- }
- action._actionManager = null;
- this.getScene()._registeredActions--;
- return true;
- }
- return false;
- }
- /**
- * Process a specific trigger
- * @param trigger defines the trigger to process
- * @param evt defines the event details to be processed
- */
- processTrigger(trigger, evt) {
- for (let index = 0; index < this.actions.length; index++) {
- const action = this.actions[index];
- if (action.trigger === trigger) {
- if (evt) {
- if (trigger === ActionManager.OnKeyUpTrigger || trigger === ActionManager.OnKeyDownTrigger) {
- const parameter = action.getTriggerParameter();
- if (typeof parameter === "function") {
- if (!parameter(evt)) {
- continue;
- }
- }
- else if (parameter && parameter !== evt.sourceEvent.keyCode) {
- if (!parameter.toLowerCase) {
- continue;
- }
- const lowerCase = parameter.toLowerCase();
- if (lowerCase !== evt.sourceEvent.key) {
- const unicode = evt.sourceEvent.charCode ? evt.sourceEvent.charCode : evt.sourceEvent.keyCode;
- const actualkey = String.fromCharCode(unicode).toLowerCase();
- if (actualkey !== lowerCase) {
- continue;
- }
- }
- }
- }
- }
- action._executeCurrent(evt);
- }
- }
- }
- /**
- * @internal
- */
- _getEffectiveTarget(target, propertyPath) {
- const properties = propertyPath.split(".");
- for (let index = 0; index < properties.length - 1; index++) {
- target = target[properties[index]];
- }
- return target;
- }
- /**
- * @internal
- */
- _getProperty(propertyPath) {
- const properties = propertyPath.split(".");
- return properties[properties.length - 1];
- }
- /**
- * Serialize this manager to a JSON object
- * @param name defines the property name to store this manager
- * @returns a JSON representation of this manager
- */
- serialize(name) {
- const root = {
- children: new Array(),
- name: name,
- type: 3,
- properties: new Array(), // Empty for root but required
- };
- for (let i = 0; i < this.actions.length; i++) {
- const triggerObject = {
- type: 0,
- children: new Array(),
- name: ActionManager.GetTriggerName(this.actions[i].trigger),
- properties: new Array(),
- };
- const triggerOptions = this.actions[i].triggerOptions;
- if (triggerOptions && typeof triggerOptions !== "number") {
- if (triggerOptions.parameter instanceof Node) {
- triggerObject.properties.push(Action._GetTargetProperty(triggerOptions.parameter));
- }
- else if (typeof triggerOptions.parameter === "object") {
- const parameter = {};
- DeepCopier.DeepCopy(triggerOptions.parameter, parameter, ["mesh"]);
- if (triggerOptions.parameter && triggerOptions.parameter.mesh) {
- parameter._meshId = triggerOptions.parameter.mesh.id;
- }
- triggerObject.properties.push({ name: "parameter", targetType: null, value: parameter });
- }
- else {
- triggerObject.properties.push({ name: "parameter", targetType: null, value: triggerOptions.parameter });
- }
- }
- // Serialize child action, recursively
- this.actions[i].serialize(triggerObject);
- // Add serialized trigger
- root.children.push(triggerObject);
- }
- return root;
- }
- /**
- * Creates a new ActionManager from a JSON data
- * @param parsedActions defines the JSON data to read from
- * @param object defines the hosting mesh
- * @param scene defines the hosting scene
- */
- static Parse(parsedActions, object, scene) {
- const actionManager = new ActionManager(scene);
- if (object === null) {
- scene.actionManager = actionManager;
- }
- else {
- object.actionManager = actionManager;
- }
- // instantiate a new object
- const instantiate = (name, params) => {
- const internalClassType = GetClass("BABYLON." + name);
- return internalClassType && new internalClassType(...params);
- };
- const parseParameter = (name, value, target, propertyPath) => {
- if (propertyPath === null) {
- // String, boolean or float
- const floatValue = parseFloat(value);
- if (value === "true" || value === "false") {
- return value === "true";
- }
- else {
- return isNaN(floatValue) ? value : floatValue;
- }
- }
- const effectiveTarget = propertyPath.split(".");
- const values = value.split(",");
- // Get effective Target
- for (let i = 0; i < effectiveTarget.length; i++) {
- target = target[effectiveTarget[i]];
- }
- // Return appropriate value with its type
- if (typeof target === "boolean") {
- return values[0] === "true";
- }
- if (typeof target === "string") {
- return values[0];
- }
- // Parameters with multiple values such as Vector3 etc.
- const split = [];
- for (let i = 0; i < values.length; i++) {
- split.push(parseFloat(values[i]));
- }
- if (target instanceof Vector3) {
- return Vector3.FromArray(split);
- }
- if (target instanceof Vector4) {
- return Vector4.FromArray(split);
- }
- if (target instanceof Color3) {
- return Color3.FromArray(split);
- }
- if (target instanceof Color4) {
- return Color4.FromArray(split);
- }
- return parseFloat(values[0]);
- };
- // traverse graph per trigger
- const traverse = (parsedAction, trigger, condition, action, combineArray = null) => {
- if (parsedAction.detached) {
- return;
- }
- const parameters = [];
- let target = null;
- let propertyPath = null;
- const combine = parsedAction.combine && parsedAction.combine.length > 0;
- // Parameters
- if (parsedAction.type === 2) {
- parameters.push(actionManager);
- }
- else {
- parameters.push(trigger);
- }
- if (combine) {
- const actions = [];
- for (let j = 0; j < parsedAction.combine.length; j++) {
- traverse(parsedAction.combine[j], ActionManager.NothingTrigger, condition, action, actions);
- }
- parameters.push(actions);
- }
- else {
- for (let i = 0; i < parsedAction.properties.length; i++) {
- let value = parsedAction.properties[i].value;
- const name = parsedAction.properties[i].name;
- const targetType = parsedAction.properties[i].targetType;
- if (name === "target") {
- if (targetType === "SceneProperties") {
- value = target = scene;
- }
- else if (targetType === "MaterialProperties") {
- value = target = scene.getMaterialByName(value);
- }
- else {
- value = target = scene.getNodeByName(value);
- }
- }
- else if (name === "parent") {
- value = scene.getNodeByName(value);
- }
- else if (name === "sound") {
- // Can not externalize to component, so only checks for the presence off the API.
- if (scene.getSoundByName) {
- value = scene.getSoundByName(value);
- }
- }
- else if (name !== "propertyPath") {
- if (parsedAction.type === 2 && name === "operator") {
- value = ValueCondition[value];
- }
- else {
- value = parseParameter(name, value, target, name === "value" ? propertyPath : null);
- }
- }
- else {
- propertyPath = value;
- }
- parameters.push(value);
- }
- }
- if (combineArray === null) {
- parameters.push(condition);
- }
- else {
- parameters.push(null);
- }
- // If interpolate value action
- if (parsedAction.name === "InterpolateValueAction") {
- const param = parameters[parameters.length - 2];
- parameters[parameters.length - 1] = param;
- parameters[parameters.length - 2] = condition;
- }
- // Action or condition(s) and not CombineAction
- let newAction = instantiate(parsedAction.name, parameters);
- if (newAction instanceof Condition && condition !== null) {
- const nothing = new DoNothingAction(trigger, condition);
- if (action) {
- action.then(nothing);
- }
- else {
- actionManager.registerAction(nothing);
- }
- action = nothing;
- }
- if (combineArray === null) {
- if (newAction instanceof Condition) {
- condition = newAction;
- newAction = action;
- }
- else {
- condition = null;
- if (action) {
- action.then(newAction);
- }
- else {
- actionManager.registerAction(newAction);
- }
- }
- }
- else {
- combineArray.push(newAction);
- }
- for (let i = 0; i < parsedAction.children.length; i++) {
- traverse(parsedAction.children[i], trigger, condition, newAction, null);
- }
- };
- // triggers
- for (let i = 0; i < parsedActions.children.length; i++) {
- let triggerParams;
- const trigger = parsedActions.children[i];
- if (trigger.properties.length > 0) {
- const param = trigger.properties[0].value;
- const value = trigger.properties[0].targetType === null ? param : scene.getMeshByName(param);
- if (value._meshId) {
- value.mesh = scene.getMeshById(value._meshId);
- }
- triggerParams = { trigger: ActionManager[trigger.name], parameter: value };
- }
- else {
- triggerParams = ActionManager[trigger.name];
- }
- for (let j = 0; j < trigger.children.length; j++) {
- if (!trigger.detached) {
- traverse(trigger.children[j], triggerParams, null, null);
- }
- }
- }
- }
- /**
- * Get a trigger name by index
- * @param trigger defines the trigger index
- * @returns a trigger name
- */
- static GetTriggerName(trigger) {
- switch (trigger) {
- case 0:
- return "NothingTrigger";
- case 1:
- return "OnPickTrigger";
- case 2:
- return "OnLeftPickTrigger";
- case 3:
- return "OnRightPickTrigger";
- case 4:
- return "OnCenterPickTrigger";
- case 5:
- return "OnPickDownTrigger";
- case 6:
- return "OnDoublePickTrigger"; // start;
- case 7:
- return "OnPickUpTrigger";
- case 8:
- return "OnLongPressTrigger";
- case 9:
- return "OnPointerOverTrigger";
- case 10:
- return "OnPointerOutTrigger";
- case 11:
- return "OnEveryFrameTrigger";
- case 12:
- return "OnIntersectionEnterTrigger";
- case 13:
- return "OnIntersectionExitTrigger";
- case 14:
- return "OnKeyDownTrigger";
- case 15:
- return "OnKeyUpTrigger";
- case 16:
- return "OnPickOutTrigger";
- default:
- return "";
- }
- }
- }
- /**
- * Nothing
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.NothingTrigger = 0;
- /**
- * On pick
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnPickTrigger = 1;
- /**
- * On left pick
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnLeftPickTrigger = 2;
- /**
- * On right pick
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnRightPickTrigger = 3;
- /**
- * On center pick
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnCenterPickTrigger = 4;
- /**
- * On pick down
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnPickDownTrigger = 5;
- /**
- * On double pick
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnDoublePickTrigger = 6;
- /**
- * On pick up
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnPickUpTrigger = 7;
- /**
- * On pick out.
- * This trigger will only be raised if you also declared a OnPickDown
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnPickOutTrigger = 16;
- /**
- * On long press
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnLongPressTrigger = 8;
- /**
- * On pointer over
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnPointerOverTrigger = 9;
- /**
- * On pointer out
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnPointerOutTrigger = 10;
- /**
- * On every frame
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnEveryFrameTrigger = 11;
- /**
- * On intersection enter
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnIntersectionEnterTrigger = 12;
- /**
- * On intersection exit
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnIntersectionExitTrigger = 13;
- /**
- * On key down
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnKeyDownTrigger = 14;
- /**
- * On key up
- * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers
- */
- ActionManager.OnKeyUpTrigger = 15;
- //# sourceMappingURL=actionManager.js.map
|