babylon.actionManager.js 23 KB

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