ManipulatorInteractionHelper.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. var Vector4 = BABYLON.Vector2;
  2. var Sandbox;
  3. (function (Sandbox) {
  4. var Vector2 = BABYLON.Vector2;
  5. var Vector3 = BABYLON.Vector3;
  6. var Matrix = BABYLON.Matrix;
  7. var Plane = BABYLON.Plane;
  8. var AbstractMesh = BABYLON.AbstractMesh;
  9. var Quaternion = BABYLON.Quaternion;
  10. var PointerEventTypes = BABYLON.PointerEventTypes;
  11. /**
  12. * This class is used to manipulated a single node.
  13. * Right now only node of type AbstractMesh is support.
  14. * In the future, manipulation on multiple selection could be possible.
  15. *
  16. * A manipulation start when left clicking and moving the mouse. It can be cancelled if the right mouse button is clicked before releasing the left one (this feature is only possible if noPreventContextMenu is false).
  17. * Per default translation is peformed when manipulating the arrow (axis or cone) or the plane anchor. If you press the shift key it will switch to rotation manipulation. The Shift key can be toggle while manipulating, the current manipulation is accept and a new one starts.
  18. *
  19. * You can set the rotation/translationStep (in radian) to enable snapping.
  20. *
  21. * The current implementation of this class creates a radix with all the features selected.
  22. */
  23. var ManipulatorInteractionHelper = (function () {
  24. function ManipulatorInteractionHelper(scene) {
  25. var _this = this;
  26. this.noPreventContextMenu = false;
  27. this._flags = 0;
  28. this._rotationFactor = 1;
  29. this._scene = scene;
  30. this._radix = new Sandbox.Radix(scene);
  31. this._shiftKeyState = false;
  32. this._scene.onBeforeRenderObservable.add(function (e, s) { return _this.onBeforeRender(e, s); });
  33. this._scene.onPointerObservable.add(function (e, s) { return _this.onPointer(e, s); }, -1, true);
  34. window.oncontextmenu = function (ev) {
  35. if (!_this.noPreventContextMenu) {
  36. ev.preventDefault();
  37. }
  38. };
  39. }
  40. /**
  41. * Attach a node to manipulate. Right now, only manipulation on a single node is supported, but this api will allow manipulation on a multiple selection in the future.
  42. * @param node
  43. */
  44. ManipulatorInteractionHelper.prototype.attachManipulatedNode = function (node) {
  45. this._manipulatedNode = node;
  46. this._radix.show();
  47. };
  48. /**
  49. * Detach the node to manipulate. Right now, only manipulation on a single node is supported, but this api will allow manipulation on a multiple selection in the future.
  50. */
  51. ManipulatorInteractionHelper.prototype.detachManipulatedNode = function (node) {
  52. this._manipulatedNode = null;
  53. this._radix.hide();
  54. };
  55. ManipulatorInteractionHelper.prototype.onBeforeRender = function (scene, state) {
  56. this.renderManipulator();
  57. };
  58. ManipulatorInteractionHelper.prototype.onPointer = function (e, state) {
  59. if (!this._manipulatedNode) {
  60. return;
  61. }
  62. var rayPos = this.getRayPosition(e.event);
  63. var shiftKeyState = e.event.shiftKey;
  64. // Detect Modifier Key changes for shift while manipulating: commit and start a new manipulation
  65. if (this.hasManFlags(1 /* DragMode */) && shiftKeyState !== this._shiftKeyState) {
  66. this.beginDrag(rayPos, e.event);
  67. }
  68. // Mouse move
  69. if (e.type === PointerEventTypes.POINTERMOVE) {
  70. // Right button release while left is down => cancel the manipulation. only processed when the context menu is not showed during manipulation
  71. if (!this.noPreventContextMenu && e.event.button === 2 && e.event.buttons === 1) {
  72. this.setManipulatedNodeWorldMatrix(this._firstTransform);
  73. this.setManFlags(8 /* Exiting */);
  74. }
  75. else if (this.hasManFlags(1 /* DragMode */) && !this.hasManFlags(8 /* Exiting */)) {
  76. state.skipNextObservers = true;
  77. if (shiftKeyState || this.hasManipulatedMode(1792 /* Rotations */)) {
  78. this.doRot(rayPos);
  79. }
  80. else {
  81. this.doPos(rayPos);
  82. }
  83. }
  84. else {
  85. this._radix.highlighted = this._radix.intersect(rayPos);
  86. }
  87. }
  88. else if (e.type === PointerEventTypes.POINTERDOWN && e.event.button === 0) {
  89. this._manipulatedMode = this._radix.intersect(rayPos);
  90. if (this._manipulatedMode !== 0 /* None */) {
  91. state.skipNextObservers = true;
  92. this.beginDrag(rayPos, e.event);
  93. if (this.hasManipulatedMode(1792 /* Rotations */)) {
  94. this.doRot(rayPos);
  95. }
  96. else {
  97. this.doPos(rayPos);
  98. }
  99. }
  100. }
  101. else if (e.type === PointerEventTypes.POINTERUP) {
  102. if (this.hasManFlags(1 /* DragMode */)) {
  103. state.skipNextObservers = true;
  104. }
  105. this._radix.highlighted = this._radix.intersect(rayPos);
  106. // Left up: end manipulation
  107. if (e.event.button === 0) {
  108. this.endDragMode();
  109. }
  110. }
  111. };
  112. ManipulatorInteractionHelper.prototype.beginDrag = function (rayPos, event) {
  113. this._firstMousePos = rayPos;
  114. this._prevMousePos = this._firstMousePos.clone();
  115. this._shiftKeyState = event.shiftKey;
  116. var mtx = this.getManipulatedNodeWorldMatrix();
  117. this._pos = mtx.getTranslation();
  118. this._right = mtx.getRow(0).toVector3();
  119. this._up = mtx.getRow(1).toVector3();
  120. this._view = mtx.getRow(2).toVector3();
  121. this._oldPos = this._pos.clone();
  122. this._firstTransform = mtx.clone();
  123. this._flags |= 2 /* FirstHit */ | 1 /* DragMode */;
  124. };
  125. ManipulatorInteractionHelper.prototype.endDragMode = function () {
  126. this.clearManFlags(1 /* DragMode */ | 8 /* Exiting */);
  127. };
  128. ManipulatorInteractionHelper.prototype.doRot = function (rayPos) {
  129. if (this.hasManFlags(2 /* FirstHit */)) {
  130. this.clearManFlags(2 /* FirstHit */);
  131. return;
  132. }
  133. var dx = rayPos.x - this._prevMousePos.x;
  134. var dy = rayPos.y - this._prevMousePos.y;
  135. var cr = this._scene.getEngine().getRenderingCanvasClientRect();
  136. var ax = (dx / cr.width) * Math.PI * 2 * this._rotationFactor;
  137. var ay = (dy / cr.height) * Math.PI * 2 * this._rotationFactor;
  138. if (this.rotationStep) {
  139. var rem = ax % this.rotationStep;
  140. ax -= rem;
  141. rem = ay % this.rotationStep;
  142. ay -= rem;
  143. }
  144. var mtx = Matrix.Identity();
  145. if (this.hasManipulatedMode(1 /* ArrowX */ | 256 /* RotationX */)) {
  146. mtx = Matrix.RotationX(ay);
  147. }
  148. else if (this.hasManipulatedMode(2 /* ArrowY */ | 512 /* RotationY */)) {
  149. mtx = Matrix.RotationY(ay);
  150. }
  151. else if (this.hasManipulatedMode(4 /* ArrowZ */ | 1024 /* RotationZ */)) {
  152. mtx = Matrix.RotationZ(ay);
  153. }
  154. else {
  155. if (this.hasManipulatedMode(/*RadixFeatures.CenterSquare |*/ 16 /* PlaneSelectionXY */ | 32 /* PlaneSelectionXZ */)) {
  156. mtx = mtx.multiply(Matrix.RotationX(ay));
  157. }
  158. if (this.hasManipulatedMode(16 /* PlaneSelectionXY */ | 64 /* PlaneSelectionYZ */)) {
  159. mtx = mtx.multiply(Matrix.RotationY(ax));
  160. }
  161. if (this.hasManipulatedMode(32 /* PlaneSelectionXZ */)) {
  162. mtx = mtx.multiply(Matrix.RotationZ(ay));
  163. }
  164. if (this.hasManipulatedMode(/*RadixFeatures.CenterSquare |*/ 32 /* PlaneSelectionXZ */)) {
  165. mtx = mtx.multiply(Matrix.RotationZ(ax));
  166. }
  167. }
  168. var tmtx = mtx.multiply(this._firstTransform);
  169. this.setManipulatedNodeWorldMatrix(tmtx);
  170. };
  171. ManipulatorInteractionHelper.prototype.doPos = function (rayPos) {
  172. var v = Vector3.Zero();
  173. var ray = this._scene.createPickingRay(rayPos.x, rayPos.y, Matrix.Identity(), this._scene.activeCamera);
  174. if (this.hasManipulatedMode(16 /* PlaneSelectionXY */ | 32 /* PlaneSelectionXZ */ | 64 /* PlaneSelectionYZ */)) {
  175. var pl0;
  176. var hit;
  177. if (this.hasManipulatedMode(16 /* PlaneSelectionXY */)) {
  178. pl0 = Plane.FromPoints(this._pos, this._pos.add(this._right), this._pos.add(this._up));
  179. }
  180. else if (this.hasManipulatedMode(32 /* PlaneSelectionXZ */)) {
  181. pl0 = Plane.FromPoints(this._pos, this._pos.add(this._right), this._pos.add(this._view));
  182. }
  183. else if (this.hasManipulatedMode(64 /* PlaneSelectionYZ */)) {
  184. pl0 = Plane.FromPoints(this._pos, this._pos.add(this._up), this._pos.add(this._view));
  185. }
  186. else {
  187. }
  188. var clip = 0.06;
  189. //Check if the plane is too parallel to the ray
  190. if (Math.abs(Vector3.Dot(pl0.normal, ray.direction)) < clip) {
  191. return;
  192. }
  193. //Make the intersection
  194. var distance = ray.intersectsPlane(pl0);
  195. hit = ManipulatorInteractionHelper.ComputeRayHit(ray, distance);
  196. //Check if it's the first call
  197. if (this.hasManFlags(2 /* FirstHit */)) {
  198. this._flags &= ~2 /* FirstHit */;
  199. this._prevHit = hit;
  200. return;
  201. }
  202. //Compute the vector
  203. v = hit.subtract(this._prevHit);
  204. }
  205. else if ((this._manipulatedMode & (1 /* ArrowX */ | 2 /* ArrowY */ | 4 /* ArrowZ */)) !== 0) {
  206. var pl0, pl1;
  207. var hit;
  208. var s;
  209. if (this.hasManFlags(2 /* FirstHit */)) {
  210. var res = this.setupIntersectionPlanes(this._manipulatedMode);
  211. pl0 = res.p0;
  212. pl1 = res.p1;
  213. if (Math.abs(Vector3.Dot(pl0.normal, ray.direction)) > Math.abs(Vector3.Dot(pl1.normal, ray.direction))) {
  214. var distance = ray.intersectsPlane(pl0);
  215. hit = ManipulatorInteractionHelper.ComputeRayHit(ray, distance);
  216. var number = ~4 /* Plane2 */;
  217. this._flags &= number;
  218. }
  219. else {
  220. var distance = ray.intersectsPlane(pl1);
  221. hit = ManipulatorInteractionHelper.ComputeRayHit(ray, distance);
  222. this._flags |= 4 /* Plane2 */;
  223. }
  224. this._flags &= ~2 /* FirstHit */;
  225. this._prevHit = hit;
  226. return;
  227. }
  228. else {
  229. var axis;
  230. var res = this.setupIntersectionPlane(this._manipulatedMode, this.hasManFlags(4 /* Plane2 */));
  231. pl0 = res.plane;
  232. axis = res.axis;
  233. var distance = ray.intersectsPlane(pl0);
  234. hit = ManipulatorInteractionHelper.ComputeRayHit(ray, distance);
  235. v = hit.subtract(this._prevHit);
  236. s = Vector3.Dot(axis, v);
  237. v = axis.multiplyByFloats(s, s, s);
  238. }
  239. }
  240. if (this.translationStep) {
  241. v.x -= v.x % this.translationStep;
  242. v.y -= v.y % this.translationStep;
  243. v.z -= v.z % this.translationStep;
  244. }
  245. var mtx = this._firstTransform.clone();
  246. mtx.setTranslation(mtx.getTranslation().add(v));
  247. this._pos = mtx.getTranslation();
  248. this.setManipulatedNodeWorldMatrix(mtx);
  249. };
  250. ManipulatorInteractionHelper.prototype.hasManipulatedMode = function (value) {
  251. return (this._manipulatedMode & value) !== 0;
  252. };
  253. ManipulatorInteractionHelper.prototype.hasManFlags = function (value) {
  254. return (this._flags & value) !== 0;
  255. };
  256. ManipulatorInteractionHelper.prototype.clearManFlags = function (values) {
  257. this._flags &= ~values;
  258. return this._flags;
  259. };
  260. ManipulatorInteractionHelper.prototype.setManFlags = function (values) {
  261. this._flags |= values;
  262. return this._flags;
  263. };
  264. ManipulatorInteractionHelper.ComputeRayHit = function (ray, distance) {
  265. return ray.origin.add(ray.direction.multiplyByFloats(distance, distance, distance));
  266. };
  267. ManipulatorInteractionHelper.prototype.setManipulatedNodeWorldMatrix = function (mtx) {
  268. if (!this._manipulatedNode) {
  269. return null;
  270. }
  271. if (this._manipulatedNode instanceof AbstractMesh) {
  272. var mesh = this._manipulatedNode;
  273. if (mesh.parent) {
  274. mtx = mtx.multiply(mesh.parent.getWorldMatrix().clone().invert());
  275. }
  276. var pos = Vector3.Zero();
  277. var scale = Vector3.Zero();
  278. var rot = new Quaternion();
  279. mtx.decompose(scale, rot, pos);
  280. mesh.position = pos;
  281. mesh.rotationQuaternion = rot;
  282. mesh.scaling = scale;
  283. }
  284. };
  285. ManipulatorInteractionHelper.prototype.getManipulatedNodeWorldMatrix = function () {
  286. if (!this._manipulatedNode) {
  287. return null;
  288. }
  289. if (this._manipulatedNode instanceof AbstractMesh) {
  290. return this._manipulatedNode.getWorldMatrix();
  291. }
  292. };
  293. ManipulatorInteractionHelper.prototype.setupIntersectionPlane = function (mode, plane2) {
  294. var res = this.setupIntersectionPlanes(mode);
  295. var pl = plane2 ? res.p1 : res.p0;
  296. var axis;
  297. switch (mode) {
  298. case 1 /* ArrowX */:
  299. axis = this._right;
  300. break;
  301. case 2 /* ArrowY */:
  302. axis = this._up;
  303. break;
  304. case 4 /* ArrowZ */:
  305. axis = this._view;
  306. break;
  307. default:
  308. axis = Vector3.Zero();
  309. break;
  310. }
  311. return { plane: pl, axis: axis };
  312. };
  313. ManipulatorInteractionHelper.prototype.setupIntersectionPlanes = function (mode) {
  314. var p0, p1;
  315. switch (mode) {
  316. case 1 /* ArrowX */:
  317. p0 = Plane.FromPoints(this._pos, this._pos.add(this._view), this._pos.add(this._right));
  318. p1 = Plane.FromPoints(this._pos, this._pos.add(this._right), this._pos.add(this._up));
  319. break;
  320. case 2 /* ArrowY */:
  321. p0 = Plane.FromPoints(this._pos, this._pos.add(this._up), this._pos.add(this._right));
  322. p1 = Plane.FromPoints(this._pos, this._pos.add(this._up), this._pos.add(this._view));
  323. break;
  324. case 4 /* ArrowZ */:
  325. p0 = Plane.FromPoints(this._pos, this._pos.add(this._view), this._pos.add(this._right));
  326. p1 = Plane.FromPoints(this._pos, this._pos.add(this._view), this._pos.add(this._up));
  327. break;
  328. }
  329. return { p0: p0, p1: p1 };
  330. };
  331. ManipulatorInteractionHelper.prototype.getRayPosition = function (event) {
  332. var canvasRect = this._scene.getEngine().getRenderingCanvasClientRect();
  333. var x = event.clientX - canvasRect.left;
  334. var y = event.clientY - canvasRect.top;
  335. return new Vector2(x, y);
  336. };
  337. ManipulatorInteractionHelper.prototype.renderManipulator = function () {
  338. if (!this._manipulatedNode) {
  339. return;
  340. }
  341. if (this._manipulatedNode instanceof AbstractMesh) {
  342. var mesh = this._manipulatedNode;
  343. var worldMtx = mesh.getWorldMatrix();
  344. var l = Vector3.Distance(this._scene.activeCamera.position, worldMtx.getTranslation());
  345. var vpWidth = this._scene.getEngine().getRenderWidth();
  346. var s = this.fromScreenToWorld(vpWidth / 100, l) * 20;
  347. var scale = Vector3.Zero();
  348. var position = Vector3.Zero();
  349. var rotation = Quaternion.Identity();
  350. var res = Matrix.Scaling(s, s, s).multiply(worldMtx);
  351. res.decompose(scale, rotation, position);
  352. this._radix.setWorld(position, rotation, scale);
  353. }
  354. };
  355. ManipulatorInteractionHelper.prototype.fromScreenToWorld = function (l, z) {
  356. var camera = this._scene.activeCamera;
  357. var r0 = this._scene.createPickingRay(0, 0, Matrix.Identity(), camera, true);
  358. var r1 = this._scene.createPickingRay(l, 0, Matrix.Identity(), camera, true);
  359. var p0 = ManipulatorInteractionHelper.evalPosition(r0, z);
  360. var p1 = ManipulatorInteractionHelper.evalPosition(r1, z);
  361. return p1.x - p0.x;
  362. };
  363. ManipulatorInteractionHelper.evalPosition = function (ray, u) {
  364. return ray.origin.add(ray.direction.multiplyByFloats(u, u, u));
  365. };
  366. return ManipulatorInteractionHelper;
  367. }());
  368. Sandbox.ManipulatorInteractionHelper = ManipulatorInteractionHelper;
  369. })(Sandbox || (Sandbox = {}));
  370. //# sourceMappingURL=ManipulatorInteractionHelper.js.map