babylon.actionManager.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. module BABYLON {
  2. /**
  3. * ActionEvent is the event beint sent when an action is triggered.
  4. */
  5. export class ActionEvent {
  6. /**
  7. * @constructor
  8. * @param source The mesh or sprite that triggered the action.
  9. * @param pointerX The X mouse cursor position at the time of the event
  10. * @param pointerY The Y mouse cursor position at the time of the event
  11. * @param meshUnderPointer The mesh that is currently pointed at (can be null)
  12. * @param sourceEvent the original (browser) event that triggered the ActionEvent
  13. */
  14. constructor(public source: any, public pointerX: number, public pointerY: number, public meshUnderPointer: AbstractMesh, public sourceEvent?: any, public additionalData?: any) {
  15. }
  16. /**
  17. * Helper function to auto-create an ActionEvent from a source mesh.
  18. * @param source The source mesh that triggered the event
  19. * @param evt {Event} The original (browser) event
  20. */
  21. public static CreateNew(source: AbstractMesh, evt?: Event, additionalData?: any): ActionEvent {
  22. var scene = source.getScene();
  23. return new ActionEvent(source, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt, additionalData);
  24. }
  25. /**
  26. * Helper function to auto-create an ActionEvent from a source mesh.
  27. * @param source The source sprite that triggered the event
  28. * @param scene Scene associated with the sprite
  29. * @param evt {Event} The original (browser) event
  30. */
  31. public static CreateNewFromSprite(source: Sprite, scene: Scene, evt?: Event, additionalData?: any): ActionEvent {
  32. return new ActionEvent(source, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt, additionalData);
  33. }
  34. /**
  35. * Helper function to auto-create an ActionEvent from a scene. If triggered by a mesh use ActionEvent.CreateNew
  36. * @param scene the scene where the event occurred
  37. * @param evt {Event} The original (browser) event
  38. */
  39. public static CreateNewFromScene(scene: Scene, evt: Event): ActionEvent {
  40. return new ActionEvent(null, scene.pointerX, scene.pointerY, scene.meshUnderPointer, evt);
  41. }
  42. }
  43. /**
  44. * Action Manager manages all events to be triggered on a given mesh or the global scene.
  45. * A single scene can have many Action Managers to handle predefined actions on specific meshes.
  46. */
  47. export class ActionManager {
  48. // Statics
  49. private static _NothingTrigger = 0;
  50. private static _OnPickTrigger = 1;
  51. private static _OnLeftPickTrigger = 2;
  52. private static _OnRightPickTrigger = 3;
  53. private static _OnCenterPickTrigger = 4;
  54. private static _OnPointerOverTrigger = 5;
  55. private static _OnPointerOutTrigger = 6;
  56. private static _OnEveryFrameTrigger = 7;
  57. private static _OnIntersectionEnterTrigger = 8;
  58. private static _OnIntersectionExitTrigger = 9;
  59. private static _OnKeyDownTrigger = 10;
  60. private static _OnKeyUpTrigger = 11;
  61. private static _OnPickUpTrigger = 12;
  62. public static get NothingTrigger(): number {
  63. return ActionManager._NothingTrigger;
  64. }
  65. public static get OnPickTrigger(): number {
  66. return ActionManager._OnPickTrigger;
  67. }
  68. public static get OnLeftPickTrigger(): number {
  69. return ActionManager._OnLeftPickTrigger;
  70. }
  71. public static get OnRightPickTrigger(): number {
  72. return ActionManager._OnRightPickTrigger;
  73. }
  74. public static get OnCenterPickTrigger(): number {
  75. return ActionManager._OnCenterPickTrigger;
  76. }
  77. public static get OnPointerOverTrigger(): number {
  78. return ActionManager._OnPointerOverTrigger;
  79. }
  80. public static get OnPointerOutTrigger(): number {
  81. return ActionManager._OnPointerOutTrigger;
  82. }
  83. public static get OnEveryFrameTrigger(): number {
  84. return ActionManager._OnEveryFrameTrigger;
  85. }
  86. public static get OnIntersectionEnterTrigger(): number {
  87. return ActionManager._OnIntersectionEnterTrigger;
  88. }
  89. public static get OnIntersectionExitTrigger(): number {
  90. return ActionManager._OnIntersectionExitTrigger;
  91. }
  92. public static get OnKeyDownTrigger(): number {
  93. return ActionManager._OnKeyDownTrigger;
  94. }
  95. public static get OnKeyUpTrigger(): number {
  96. return ActionManager._OnKeyUpTrigger;
  97. }
  98. public static get OnPickUpTrigger(): number {
  99. return ActionManager._OnPickUpTrigger;
  100. }
  101. // Members
  102. public actions = new Array<Action>();
  103. private _scene: Scene;
  104. constructor(scene: Scene) {
  105. this._scene = scene;
  106. scene._actionManagers.push(this);
  107. }
  108. // Methods
  109. public dispose(): void {
  110. var index = this._scene._actionManagers.indexOf(this);
  111. if (index > -1) {
  112. this._scene._actionManagers.splice(index, 1);
  113. }
  114. }
  115. public getScene(): Scene {
  116. return this._scene;
  117. }
  118. /**
  119. * Does this action manager handles actions of any of the given triggers
  120. * @param {number[]} triggers - the triggers to be tested
  121. * @return {boolean} whether one (or more) of the triggers is handeled
  122. */
  123. public hasSpecificTriggers(triggers: number[]): boolean {
  124. for (var index = 0; index < this.actions.length; index++) {
  125. var action = this.actions[index];
  126. if (triggers.indexOf(action.trigger) > -1) {
  127. return true;
  128. }
  129. }
  130. return false;
  131. }
  132. /**
  133. * Does this action manager handles actions of a given trigger
  134. * @param {number} trigger - the trigger to be tested
  135. * @return {boolean} whether the trigger is handeled
  136. */
  137. public hasSpecificTrigger(trigger: number): boolean {
  138. for (var index = 0; index < this.actions.length; index++) {
  139. var action = this.actions[index];
  140. if (action.trigger === trigger) {
  141. return true;
  142. }
  143. }
  144. return false;
  145. }
  146. /**
  147. * Does this action manager has pointer triggers
  148. * @return {boolean} whether or not it has pointer triggers
  149. */
  150. public get hasPointerTriggers(): boolean {
  151. for (var index = 0; index < this.actions.length; index++) {
  152. var action = this.actions[index];
  153. if (action.trigger >= ActionManager._OnPickTrigger && action.trigger <= ActionManager._OnPointerOutTrigger) {
  154. return true;
  155. }
  156. if (action.trigger === ActionManager._OnPickUpTrigger) {
  157. return true;
  158. }
  159. }
  160. return false;
  161. }
  162. /**
  163. * Does this action manager has pick triggers
  164. * @return {boolean} whether or not it has pick triggers
  165. */
  166. public get hasPickTriggers(): boolean {
  167. for (var index = 0; index < this.actions.length; index++) {
  168. var action = this.actions[index];
  169. if (action.trigger >= ActionManager._OnPickTrigger && action.trigger <= ActionManager._OnCenterPickTrigger) {
  170. return true;
  171. }
  172. if (action.trigger === ActionManager._OnPickUpTrigger) {
  173. return true;
  174. }
  175. }
  176. return false;
  177. }
  178. /**
  179. * Registers an action to this action manager
  180. * @param {BABYLON.Action} action - the action to be registered
  181. * @return {BABYLON.Action} the action amended (prepared) after registration
  182. */
  183. public registerAction(action: Action): Action {
  184. if (action.trigger === ActionManager.OnEveryFrameTrigger) {
  185. if (this.getScene().actionManager !== this) {
  186. Tools.Warn("OnEveryFrameTrigger can only be used with scene.actionManager");
  187. return null;
  188. }
  189. }
  190. this.actions.push(action);
  191. action._actionManager = this;
  192. action._prepare();
  193. return action;
  194. }
  195. /**
  196. * Process a specific trigger
  197. * @param {number} trigger - the trigger to process
  198. * @param evt {BABYLON.ActionEvent} the event details to be processed
  199. */
  200. public processTrigger(trigger: number, evt: ActionEvent): void {
  201. for (var index = 0; index < this.actions.length; index++) {
  202. var action = this.actions[index];
  203. if (action.trigger === trigger) {
  204. if (trigger === ActionManager.OnKeyUpTrigger
  205. || trigger === ActionManager.OnKeyDownTrigger) {
  206. var parameter = action.getTriggerParameter();
  207. if (parameter) {
  208. var unicode = evt.sourceEvent.charCode ? evt.sourceEvent.charCode : evt.sourceEvent.keyCode;
  209. var actualkey = String.fromCharCode(unicode).toLowerCase();
  210. if (actualkey !== parameter.toLowerCase()) {
  211. continue;
  212. }
  213. }
  214. }
  215. action._executeCurrent(evt);
  216. }
  217. }
  218. }
  219. public _getEffectiveTarget(target: any, propertyPath: string): any {
  220. var properties = propertyPath.split(".");
  221. for (var index = 0; index < properties.length - 1; index++) {
  222. target = target[properties[index]];
  223. }
  224. return target;
  225. }
  226. public _getProperty(propertyPath: string): string {
  227. var properties = propertyPath.split(".");
  228. return properties[properties.length - 1];
  229. }
  230. public static ParseActions(parsedActions: any, object: AbstractMesh, scene: Scene) {
  231. var actionManager = new BABYLON.ActionManager(scene);
  232. if (object === null)
  233. scene.actionManager = actionManager;
  234. else
  235. object.actionManager = actionManager;
  236. // instanciate a new object
  237. var instanciate = (name: any, params: Array<any>): any => {
  238. var newInstance: Object = Object.create(BABYLON[name].prototype);
  239. newInstance.constructor.apply(newInstance, params);
  240. return newInstance;
  241. };
  242. var parseParameter = (name: string, value: string, target: any, propertyPath: string): any => {
  243. if (propertyPath === null) {
  244. // String, boolean or float
  245. var floatValue = parseFloat(value);
  246. if (value === "true" || value === "false")
  247. return value === "true";
  248. else
  249. return isNaN(floatValue) ? value : floatValue;
  250. }
  251. var effectiveTarget = propertyPath.split(".");
  252. var values = value.split(",");
  253. // Get effective Target
  254. for (var i = 0; i < effectiveTarget.length; i++) {
  255. target = target[effectiveTarget[i]];
  256. }
  257. // Return appropriate value with its type
  258. if (typeof (target) === "boolean")
  259. return values[0] === "true";
  260. if (typeof (target) === "string")
  261. return values[0];
  262. // Parameters with multiple values such as Vector3 etc.
  263. var split = new Array<number>();
  264. for (var i = 0; i < values.length; i++)
  265. split.push(parseFloat(values[i]));
  266. if (target instanceof Vector3)
  267. return Vector3.FromArray(split);
  268. if (target instanceof Vector4)
  269. return Vector4.FromArray(split);
  270. if (target instanceof Color3)
  271. return Color3.FromArray(split);
  272. if (target instanceof Color4)
  273. return Color4.FromArray(split);
  274. return parseFloat(values[0]);
  275. };
  276. // traverse graph per trigger
  277. var traverse = (parsedAction: any, trigger: any, condition: Condition, action: Action, combineArray: Array<Action> = null) => {
  278. if (parsedAction.detached)
  279. return;
  280. var parameters = new Array<any>();
  281. var target: any = null;
  282. var propertyPath: string = null;
  283. var combine = parsedAction.combine && parsedAction.combine.length > 0;
  284. // Parameters
  285. if (parsedAction.type === 2)
  286. parameters.push(actionManager);
  287. else
  288. parameters.push(trigger);
  289. if (combine) {
  290. var actions = new Array<Action>();
  291. for (var j = 0; j < parsedAction.combine.length; j++) {
  292. traverse(parsedAction.combine[j], ActionManager.NothingTrigger, condition, action, actions);
  293. }
  294. parameters.push(actions);
  295. }
  296. else {
  297. for (var i = 0; i < parsedAction.properties.length; i++) {
  298. var value = parsedAction.properties[i].value;
  299. var name = parsedAction.properties[i].name;
  300. var targetType = parsedAction.properties[i].targetType;
  301. if (name === "target")
  302. if (targetType !== null && targetType === "SceneProperties")
  303. value = target = scene;
  304. else
  305. value = target = scene.getNodeByName(value);
  306. else if (name === "parent")
  307. value = scene.getNodeByName(value);
  308. else if (name === "sound")
  309. value = scene.getSoundByName(value);
  310. else if (name !== "propertyPath") {
  311. if (parsedAction.type === 2 && name === "operator")
  312. value = ValueCondition[value];
  313. else
  314. value = parseParameter(name, value, target, name === "value" ? propertyPath : null);
  315. } else {
  316. propertyPath = value;
  317. }
  318. parameters.push(value);
  319. }
  320. }
  321. if (combineArray === null) {
  322. parameters.push(condition);
  323. }
  324. else {
  325. parameters.push(null);
  326. }
  327. // If interpolate value action
  328. if (parsedAction.name === "InterpolateValueAction") {
  329. var param = parameters[parameters.length - 2];
  330. parameters[parameters.length - 1] = param;
  331. parameters[parameters.length - 2] = condition;
  332. }
  333. // Action or condition(s) and not CombineAction
  334. var newAction = instanciate(parsedAction.name, parameters);
  335. if (newAction instanceof Condition && condition !== null) {
  336. var nothing = new DoNothingAction(trigger, condition);
  337. if (action)
  338. action.then(nothing);
  339. else
  340. actionManager.registerAction(nothing);
  341. action = nothing;
  342. }
  343. if (combineArray === null) {
  344. if (newAction instanceof Condition) {
  345. condition = newAction;
  346. newAction = action;
  347. } else {
  348. condition = null;
  349. if (action)
  350. action.then(newAction);
  351. else
  352. actionManager.registerAction(newAction);
  353. }
  354. }
  355. else {
  356. combineArray.push(newAction);
  357. }
  358. for (var i = 0; i < parsedAction.children.length; i++)
  359. traverse(parsedAction.children[i], trigger, condition, newAction, null);
  360. };
  361. // triggers
  362. for (var i = 0; i < parsedActions.children.length; i++) {
  363. var triggerParams: any;
  364. var trigger = parsedActions.children[i];
  365. if (trigger.properties.length > 0) {
  366. var param = trigger.properties[0].value;
  367. var value = trigger.properties[0].targetType === null ? param : scene.getMeshByName(param);
  368. triggerParams = { trigger: BABYLON.ActionManager[trigger.name], parameter: value };
  369. }
  370. else
  371. triggerParams = BABYLON.ActionManager[trigger.name];
  372. for (var j = 0; j < trigger.children.length; j++) {
  373. if (!trigger.detached)
  374. traverse(trigger.children[j], triggerParams, null, null);
  375. }
  376. }
  377. }
  378. }
  379. }