babylon.actionManager.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. var BABYLON;
  2. (function (BABYLON) {
  3. /**
  4. * ActionEvent is the event beint sent when an action is triggered.
  5. */
  6. var ActionEvent = (function () {
  7. /**
  8. * @constructor
  9. * @param source The mesh or sprite that triggered the action.
  10. * @param pointerX The X mouse cursor position at the time of the event
  11. * @param pointerY The Y mouse cursor position at the time of the event
  12. * @param meshUnderPointer The mesh that is currently pointed at (can be null)
  13. * @param sourceEvent the original (browser) event that triggered the ActionEvent
  14. */
  15. function ActionEvent(source, pointerX, pointerY, meshUnderPointer, sourceEvent, additionalData) {
  16. this.source = source;
  17. this.pointerX = pointerX;
  18. this.pointerY = pointerY;
  19. this.meshUnderPointer = meshUnderPointer;
  20. this.sourceEvent = sourceEvent;
  21. this.additionalData = additionalData;
  22. }
  23. /**
  24. * Helper function to auto-create an ActionEvent from a source mesh.
  25. * @param source The source mesh that triggered the event
  26. * @param evt {Event} The original (browser) event
  27. */
  28. ActionEvent.CreateNew = function (source, evt, additionalData) {
  29. var scene = source.getScene();
  30. return new ActionEvent(source, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt, additionalData);
  31. };
  32. /**
  33. * Helper function to auto-create an ActionEvent from a source mesh.
  34. * @param source The source sprite that triggered the event
  35. * @param scene Scene associated with the sprite
  36. * @param evt {Event} The original (browser) event
  37. */
  38. ActionEvent.CreateNewFromSprite = function (source, scene, evt, additionalData) {
  39. return new ActionEvent(source, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt, additionalData);
  40. };
  41. /**
  42. * Helper function to auto-create an ActionEvent from a scene. If triggered by a mesh use ActionEvent.CreateNew
  43. * @param scene the scene where the event occurred
  44. * @param evt {Event} The original (browser) event
  45. */
  46. ActionEvent.CreateNewFromScene = function (scene, evt) {
  47. return new ActionEvent(null, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt);
  48. };
  49. ActionEvent.CreateNewFromPrimitive = function (prim, pointerPos, evt, additionalData) {
  50. return new ActionEvent(prim, pointerPos.x, pointerPos.y, null, evt, additionalData);
  51. };
  52. return ActionEvent;
  53. })();
  54. BABYLON.ActionEvent = ActionEvent;
  55. /**
  56. * Action Manager manages all events to be triggered on a given mesh or the global scene.
  57. * A single scene can have many Action Managers to handle predefined actions on specific meshes.
  58. */
  59. var ActionManager = (function () {
  60. function ActionManager(scene) {
  61. // Members
  62. this.actions = new Array();
  63. this._scene = scene;
  64. scene._actionManagers.push(this);
  65. }
  66. Object.defineProperty(ActionManager, "NothingTrigger", {
  67. get: function () {
  68. return ActionManager._NothingTrigger;
  69. },
  70. enumerable: true,
  71. configurable: true
  72. });
  73. Object.defineProperty(ActionManager, "OnPickTrigger", {
  74. get: function () {
  75. return ActionManager._OnPickTrigger;
  76. },
  77. enumerable: true,
  78. configurable: true
  79. });
  80. Object.defineProperty(ActionManager, "OnLeftPickTrigger", {
  81. get: function () {
  82. return ActionManager._OnLeftPickTrigger;
  83. },
  84. enumerable: true,
  85. configurable: true
  86. });
  87. Object.defineProperty(ActionManager, "OnRightPickTrigger", {
  88. get: function () {
  89. return ActionManager._OnRightPickTrigger;
  90. },
  91. enumerable: true,
  92. configurable: true
  93. });
  94. Object.defineProperty(ActionManager, "OnCenterPickTrigger", {
  95. get: function () {
  96. return ActionManager._OnCenterPickTrigger;
  97. },
  98. enumerable: true,
  99. configurable: true
  100. });
  101. Object.defineProperty(ActionManager, "OnPickDownTrigger", {
  102. get: function () {
  103. return ActionManager._OnPickDownTrigger;
  104. },
  105. enumerable: true,
  106. configurable: true
  107. });
  108. Object.defineProperty(ActionManager, "OnPickUpTrigger", {
  109. get: function () {
  110. return ActionManager._OnPickUpTrigger;
  111. },
  112. enumerable: true,
  113. configurable: true
  114. });
  115. Object.defineProperty(ActionManager, "OnPickOutTrigger", {
  116. /// This trigger will only be raised if you also declared a OnPickDown
  117. get: function () {
  118. return ActionManager._OnPickOutTrigger;
  119. },
  120. enumerable: true,
  121. configurable: true
  122. });
  123. Object.defineProperty(ActionManager, "OnLongPressTrigger", {
  124. get: function () {
  125. return ActionManager._OnLongPressTrigger;
  126. },
  127. enumerable: true,
  128. configurable: true
  129. });
  130. Object.defineProperty(ActionManager, "OnPointerOverTrigger", {
  131. get: function () {
  132. return ActionManager._OnPointerOverTrigger;
  133. },
  134. enumerable: true,
  135. configurable: true
  136. });
  137. Object.defineProperty(ActionManager, "OnPointerOutTrigger", {
  138. get: function () {
  139. return ActionManager._OnPointerOutTrigger;
  140. },
  141. enumerable: true,
  142. configurable: true
  143. });
  144. Object.defineProperty(ActionManager, "OnEveryFrameTrigger", {
  145. get: function () {
  146. return ActionManager._OnEveryFrameTrigger;
  147. },
  148. enumerable: true,
  149. configurable: true
  150. });
  151. Object.defineProperty(ActionManager, "OnIntersectionEnterTrigger", {
  152. get: function () {
  153. return ActionManager._OnIntersectionEnterTrigger;
  154. },
  155. enumerable: true,
  156. configurable: true
  157. });
  158. Object.defineProperty(ActionManager, "OnIntersectionExitTrigger", {
  159. get: function () {
  160. return ActionManager._OnIntersectionExitTrigger;
  161. },
  162. enumerable: true,
  163. configurable: true
  164. });
  165. Object.defineProperty(ActionManager, "OnKeyDownTrigger", {
  166. get: function () {
  167. return ActionManager._OnKeyDownTrigger;
  168. },
  169. enumerable: true,
  170. configurable: true
  171. });
  172. Object.defineProperty(ActionManager, "OnKeyUpTrigger", {
  173. get: function () {
  174. return ActionManager._OnKeyUpTrigger;
  175. },
  176. enumerable: true,
  177. configurable: true
  178. });
  179. // Methods
  180. ActionManager.prototype.dispose = function () {
  181. var index = this._scene._actionManagers.indexOf(this);
  182. if (index > -1) {
  183. this._scene._actionManagers.splice(index, 1);
  184. }
  185. };
  186. ActionManager.prototype.getScene = function () {
  187. return this._scene;
  188. };
  189. /**
  190. * Does this action manager handles actions of any of the given triggers
  191. * @param {number[]} triggers - the triggers to be tested
  192. * @return {boolean} whether one (or more) of the triggers is handeled
  193. */
  194. ActionManager.prototype.hasSpecificTriggers = function (triggers) {
  195. for (var index = 0; index < this.actions.length; index++) {
  196. var action = this.actions[index];
  197. if (triggers.indexOf(action.trigger) > -1) {
  198. return true;
  199. }
  200. }
  201. return false;
  202. };
  203. /**
  204. * Does this action manager handles actions of a given trigger
  205. * @param {number} trigger - the trigger to be tested
  206. * @return {boolean} whether the trigger is handeled
  207. */
  208. ActionManager.prototype.hasSpecificTrigger = function (trigger) {
  209. for (var index = 0; index < this.actions.length; index++) {
  210. var action = this.actions[index];
  211. if (action.trigger === trigger) {
  212. return true;
  213. }
  214. }
  215. return false;
  216. };
  217. Object.defineProperty(ActionManager.prototype, "hasPointerTriggers", {
  218. /**
  219. * Does this action manager has pointer triggers
  220. * @return {boolean} whether or not it has pointer triggers
  221. */
  222. get: function () {
  223. for (var index = 0; index < this.actions.length; index++) {
  224. var action = this.actions[index];
  225. if (action.trigger >= ActionManager._OnPickTrigger && action.trigger <= ActionManager._OnPointerOutTrigger) {
  226. return true;
  227. }
  228. }
  229. return false;
  230. },
  231. enumerable: true,
  232. configurable: true
  233. });
  234. Object.defineProperty(ActionManager.prototype, "hasPickTriggers", {
  235. /**
  236. * Does this action manager has pick triggers
  237. * @return {boolean} whether or not it has pick triggers
  238. */
  239. get: function () {
  240. for (var index = 0; index < this.actions.length; index++) {
  241. var action = this.actions[index];
  242. if (action.trigger >= ActionManager._OnPickTrigger && action.trigger <= ActionManager._OnPickUpTrigger) {
  243. return true;
  244. }
  245. }
  246. return false;
  247. },
  248. enumerable: true,
  249. configurable: true
  250. });
  251. /**
  252. * Registers an action to this action manager
  253. * @param {BABYLON.Action} action - the action to be registered
  254. * @return {BABYLON.Action} the action amended (prepared) after registration
  255. */
  256. ActionManager.prototype.registerAction = function (action) {
  257. if (action.trigger === ActionManager.OnEveryFrameTrigger) {
  258. if (this.getScene().actionManager !== this) {
  259. BABYLON.Tools.Warn("OnEveryFrameTrigger can only be used with scene.actionManager");
  260. return null;
  261. }
  262. }
  263. this.actions.push(action);
  264. action._actionManager = this;
  265. action._prepare();
  266. return action;
  267. };
  268. /**
  269. * Process a specific trigger
  270. * @param {number} trigger - the trigger to process
  271. * @param evt {BABYLON.ActionEvent} the event details to be processed
  272. */
  273. ActionManager.prototype.processTrigger = function (trigger, evt) {
  274. for (var index = 0; index < this.actions.length; index++) {
  275. var action = this.actions[index];
  276. if (action.trigger === trigger) {
  277. if (trigger === ActionManager.OnKeyUpTrigger
  278. || trigger === ActionManager.OnKeyDownTrigger) {
  279. var parameter = action.getTriggerParameter();
  280. if (parameter) {
  281. var unicode = evt.sourceEvent.charCode ? evt.sourceEvent.charCode : evt.sourceEvent.keyCode;
  282. var actualkey = String.fromCharCode(unicode).toLowerCase();
  283. if (actualkey !== parameter.toLowerCase()) {
  284. continue;
  285. }
  286. }
  287. }
  288. action._executeCurrent(evt);
  289. }
  290. }
  291. };
  292. ActionManager.prototype._getEffectiveTarget = function (target, propertyPath) {
  293. var properties = propertyPath.split(".");
  294. for (var index = 0; index < properties.length - 1; index++) {
  295. target = target[properties[index]];
  296. }
  297. return target;
  298. };
  299. ActionManager.prototype._getProperty = function (propertyPath) {
  300. var properties = propertyPath.split(".");
  301. return properties[properties.length - 1];
  302. };
  303. ActionManager.prototype.serialize = function (name) {
  304. var root = {
  305. children: [],
  306. name: name,
  307. type: 3,
  308. properties: [] // Empty for root but required
  309. };
  310. for (var i = 0; i < this.actions.length; i++) {
  311. var triggerObject = {
  312. type: 0,
  313. children: [],
  314. name: ActionManager.GetTriggerName(this.actions[i].trigger),
  315. properties: []
  316. };
  317. var triggerOptions = this.actions[i].triggerOptions;
  318. if (triggerOptions && typeof triggerOptions !== "number") {
  319. if (triggerOptions.parameter instanceof BABYLON.Node) {
  320. triggerObject.properties.push(BABYLON.Action._GetTargetProperty(triggerOptions.parameter));
  321. }
  322. else {
  323. triggerObject.properties.push({ name: "parameter", targetType: null, value: triggerOptions.parameter });
  324. }
  325. }
  326. // Serialize child action, recursively
  327. this.actions[i].serialize(triggerObject);
  328. // Add serialized trigger
  329. root.children.push(triggerObject);
  330. }
  331. return root;
  332. };
  333. ActionManager.Parse = function (parsedActions, object, scene) {
  334. var actionManager = new BABYLON.ActionManager(scene);
  335. if (object === null)
  336. scene.actionManager = actionManager;
  337. else
  338. object.actionManager = actionManager;
  339. // instanciate a new object
  340. var instanciate = function (name, params) {
  341. var newInstance = Object.create(BABYLON[name].prototype);
  342. newInstance.constructor.apply(newInstance, params);
  343. return newInstance;
  344. };
  345. var parseParameter = function (name, value, target, propertyPath) {
  346. if (propertyPath === null) {
  347. // String, boolean or float
  348. var floatValue = parseFloat(value);
  349. if (value === "true" || value === "false")
  350. return value === "true";
  351. else
  352. return isNaN(floatValue) ? value : floatValue;
  353. }
  354. var effectiveTarget = propertyPath.split(".");
  355. var values = value.split(",");
  356. // Get effective Target
  357. for (var i = 0; i < effectiveTarget.length; i++) {
  358. target = target[effectiveTarget[i]];
  359. }
  360. // Return appropriate value with its type
  361. if (typeof (target) === "boolean")
  362. return values[0] === "true";
  363. if (typeof (target) === "string")
  364. return values[0];
  365. // Parameters with multiple values such as Vector3 etc.
  366. var split = new Array();
  367. for (var i = 0; i < values.length; i++)
  368. split.push(parseFloat(values[i]));
  369. if (target instanceof BABYLON.Vector3)
  370. return BABYLON.Vector3.FromArray(split);
  371. if (target instanceof BABYLON.Vector4)
  372. return BABYLON.Vector4.FromArray(split);
  373. if (target instanceof BABYLON.Color3)
  374. return BABYLON.Color3.FromArray(split);
  375. if (target instanceof BABYLON.Color4)
  376. return BABYLON.Color4.FromArray(split);
  377. return parseFloat(values[0]);
  378. };
  379. // traverse graph per trigger
  380. var traverse = function (parsedAction, trigger, condition, action, combineArray) {
  381. if (combineArray === void 0) { combineArray = null; }
  382. if (parsedAction.detached)
  383. return;
  384. var parameters = new Array();
  385. var target = null;
  386. var propertyPath = null;
  387. var combine = parsedAction.combine && parsedAction.combine.length > 0;
  388. // Parameters
  389. if (parsedAction.type === 2)
  390. parameters.push(actionManager);
  391. else
  392. parameters.push(trigger);
  393. if (combine) {
  394. var actions = new Array();
  395. for (var j = 0; j < parsedAction.combine.length; j++) {
  396. traverse(parsedAction.combine[j], ActionManager.NothingTrigger, condition, action, actions);
  397. }
  398. parameters.push(actions);
  399. }
  400. else {
  401. for (var i = 0; i < parsedAction.properties.length; i++) {
  402. var value = parsedAction.properties[i].value;
  403. var name = parsedAction.properties[i].name;
  404. var targetType = parsedAction.properties[i].targetType;
  405. if (name === "target")
  406. if (targetType !== null && targetType === "SceneProperties")
  407. value = target = scene;
  408. else
  409. value = target = scene.getNodeByName(value);
  410. else if (name === "parent")
  411. value = scene.getNodeByName(value);
  412. else if (name === "sound")
  413. value = scene.getSoundByName(value);
  414. else if (name !== "propertyPath") {
  415. if (parsedAction.type === 2 && name === "operator")
  416. value = BABYLON.ValueCondition[value];
  417. else
  418. value = parseParameter(name, value, target, name === "value" ? propertyPath : null);
  419. }
  420. else {
  421. propertyPath = value;
  422. }
  423. parameters.push(value);
  424. }
  425. }
  426. if (combineArray === null) {
  427. parameters.push(condition);
  428. }
  429. else {
  430. parameters.push(null);
  431. }
  432. // If interpolate value action
  433. if (parsedAction.name === "InterpolateValueAction") {
  434. var param = parameters[parameters.length - 2];
  435. parameters[parameters.length - 1] = param;
  436. parameters[parameters.length - 2] = condition;
  437. }
  438. // Action or condition(s) and not CombineAction
  439. var newAction = instanciate(parsedAction.name, parameters);
  440. if (newAction instanceof BABYLON.Condition && condition !== null) {
  441. var nothing = new BABYLON.DoNothingAction(trigger, condition);
  442. if (action)
  443. action.then(nothing);
  444. else
  445. actionManager.registerAction(nothing);
  446. action = nothing;
  447. }
  448. if (combineArray === null) {
  449. if (newAction instanceof BABYLON.Condition) {
  450. condition = newAction;
  451. newAction = action;
  452. }
  453. else {
  454. condition = null;
  455. if (action)
  456. action.then(newAction);
  457. else
  458. actionManager.registerAction(newAction);
  459. }
  460. }
  461. else {
  462. combineArray.push(newAction);
  463. }
  464. for (var i = 0; i < parsedAction.children.length; i++)
  465. traverse(parsedAction.children[i], trigger, condition, newAction, null);
  466. };
  467. // triggers
  468. for (var i = 0; i < parsedActions.children.length; i++) {
  469. var triggerParams;
  470. var trigger = parsedActions.children[i];
  471. if (trigger.properties.length > 0) {
  472. var param = trigger.properties[0].value;
  473. var value = trigger.properties[0].targetType === null ? param : scene.getMeshByName(param);
  474. triggerParams = { trigger: BABYLON.ActionManager[trigger.name], parameter: value };
  475. }
  476. else
  477. triggerParams = BABYLON.ActionManager[trigger.name];
  478. for (var j = 0; j < trigger.children.length; j++) {
  479. if (!trigger.detached)
  480. traverse(trigger.children[j], triggerParams, null, null);
  481. }
  482. }
  483. };
  484. ActionManager.GetTriggerName = function (trigger) {
  485. switch (trigger) {
  486. case 0: return "NothingTrigger";
  487. case 1: return "OnPickTrigger";
  488. case 2: return "OnLeftPickTrigger";
  489. case 3: return "OnRightPickTrigger";
  490. case 4: return "OnCenterPickTrigger";
  491. case 5: return "OnPickDownTrigger";
  492. case 6: return "OnPickUpTrigger";
  493. case 7: return "OnLongPressTrigger";
  494. case 8: return "OnPointerOverTrigger";
  495. case 9: return "OnPointerOutTrigger";
  496. case 10: return "OnEveryFrameTrigger";
  497. case 11: return "OnIntersectionEnterTrigger";
  498. case 12: return "OnIntersectionExitTrigger";
  499. case 13: return "OnKeyDownTrigger";
  500. case 14: return "OnKeyUpTrigger";
  501. case 15: return "OnPickOutTrigger";
  502. default: return "";
  503. }
  504. };
  505. // Statics
  506. ActionManager._NothingTrigger = 0;
  507. ActionManager._OnPickTrigger = 1;
  508. ActionManager._OnLeftPickTrigger = 2;
  509. ActionManager._OnRightPickTrigger = 3;
  510. ActionManager._OnCenterPickTrigger = 4;
  511. ActionManager._OnPickDownTrigger = 5;
  512. ActionManager._OnPickUpTrigger = 6;
  513. ActionManager._OnLongPressTrigger = 7;
  514. ActionManager._OnPointerOverTrigger = 8;
  515. ActionManager._OnPointerOutTrigger = 9;
  516. ActionManager._OnEveryFrameTrigger = 10;
  517. ActionManager._OnIntersectionEnterTrigger = 11;
  518. ActionManager._OnIntersectionExitTrigger = 12;
  519. ActionManager._OnKeyDownTrigger = 13;
  520. ActionManager._OnKeyUpTrigger = 14;
  521. ActionManager._OnPickOutTrigger = 15;
  522. ActionManager.DragMovementThreshold = 10; // in pixels
  523. ActionManager.LongPressDelay = 500; // in milliseconds
  524. return ActionManager;
  525. })();
  526. BABYLON.ActionManager = ActionManager;
  527. })(BABYLON || (BABYLON = {}));