/src/away3d/tools/utils/Drag3D.as

http://github.com/away3d/away3d-core-fp11 · ActionScript · 479 lines · 330 code · 78 blank · 71 comment · 57 complexity · 290aae8e86c23b58a3654f22ef8b09ce MD5 · raw file

  1. package away3d.tools.utils
  2. {
  3. import away3d.cameras.lenses.PerspectiveLens;
  4. import away3d.containers.ObjectContainer3D;
  5. import away3d.containers.View3D;
  6. import away3d.core.math.Matrix3DUtils;
  7. import away3d.entities.Mesh;
  8. import away3d.materials.ColorMaterial;
  9. import away3d.primitives.PlaneGeometry;
  10. import flash.geom.Vector3D;
  11. /**
  12. * Class Drag3D allows free dragging of an ObjectContainer3D onto a given plane.
  13. *
  14. * locks on world planes
  15. * locks on ObjectContainer3D planes
  16. * locks on ObjectContainer3D rotations planes
  17. */
  18. public class Drag3D
  19. {
  20. public static const PLANE_XZ:String = "xz";
  21. public static const PLANE_XY:String = "xy";
  22. public static const PLANE_ZY:String = "zy";
  23. private const EPS:Number = 0.000001;
  24. private var _view:View3D;
  25. private var _debug:Boolean;
  26. private var _object3d:ObjectContainer3D;
  27. private var _planeXZ:Mesh;
  28. private var _planeXY:Mesh;
  29. private var _planeZY:Mesh;
  30. private var _red:ColorMaterial;
  31. private var _green:ColorMaterial;
  32. private var _blue:ColorMaterial;
  33. private var _planesContainer:ObjectContainer3D;
  34. private var _np:Vector3D = new Vector3D(0.0, 0.0, 0.0);
  35. private var _intersect:Vector3D = new Vector3D(0.0, 0.0, 0.0);
  36. private var _rotations:Vector3D;
  37. private var _baserotations:Vector3D;
  38. private var _offsetCenter:Vector3D = new Vector3D(0.0, 0.0, 0.0);
  39. private var _bSetOffset:Boolean;
  40. private var _a:Number = 0;
  41. private var _b:Number = 0;
  42. private var _c:Number = 0;
  43. private var _d:Number = 1;
  44. private var _planeid:String;
  45. private var _useRotations:Boolean;
  46. // TODO: not used
  47. // private var _inverse:Matrix3D = new Matrix3D();
  48. /**
  49. * Class Drag3D allows to drag 3d objects with the mouse.<code>Drag3D</code>
  50. * @param view View3D. The view3d where the object to drag is or will be addChilded.
  51. * @param object3d [optional] ObjectContainer3D. The object3D to drag.
  52. * @param plane [optional] String. The plane to drag on.
  53. */
  54. public function Drag3D(view:View3D, object3d:ObjectContainer3D = null, plane:String = PLANE_XZ)
  55. {
  56. _view = view;
  57. _object3d = object3d;
  58. _planeid = plane;
  59. updateNormalPlanes();
  60. init();
  61. }
  62. public function get object3d():ObjectContainer3D
  63. {
  64. return _object3d;
  65. }
  66. /**
  67. * Defines if the target object3d plane will be aligned to object rotations or not
  68. *
  69. * @param b Boolean. Defines if the target object3d planes will be aligned to object rotations or not. Default is false.
  70. */
  71. public function set useRotations(b:Boolean):void
  72. {
  73. _useRotations = b;
  74. if (!b && _rotations)
  75. _baserotations = null;
  76. _rotations = null;
  77. if (_planesContainer)
  78. updateDebug();
  79. }
  80. public function get useRotations():Boolean
  81. {
  82. return _useRotations;
  83. }
  84. /**
  85. * Defines an offset for the drag from center mesh to mouse position.
  86. * object3d must have been set previously for this setter. if not an error is triggered
  87. * Since the offset is set from center to mouse projection, its usually a good practice to set it during firt mouse down
  88. * prior to drag.
  89. */
  90. public function set offsetCenter(b:Boolean):void
  91. {
  92. if (b && !_object3d)
  93. throw new Error("offsetCenter requires that an object3d as been assigned to the Drag3D class first!");
  94. if (b) {
  95. _offsetCenter.x = _object3d.scenePosition.x;
  96. _offsetCenter.y = _object3d.scenePosition.y;
  97. _offsetCenter.z = _object3d.scenePosition.z;
  98. if (_offsetCenter.x == 0 && _offsetCenter.y == 0 && _offsetCenter.z == 0) {
  99. _offsetCenter.x = EPS;
  100. _offsetCenter.y = EPS;
  101. _offsetCenter.z = EPS;
  102. }
  103. } else {
  104. _offsetCenter.x = EPS;
  105. _offsetCenter.y = EPS;
  106. _offsetCenter.z = EPS;
  107. }
  108. _bSetOffset = b;
  109. }
  110. public function get offsetCenter():Boolean
  111. {
  112. return _bSetOffset;
  113. }
  114. /**
  115. * getIntersect method returns the 3d point in space (Vector3D) where mouse hits the given plane.
  116. *@return Vector3D the difference mouse mouse hit to object center
  117. */
  118. public function get offsetMouseCenter():Vector3D
  119. {
  120. return _offsetCenter;
  121. }
  122. /**
  123. * Displays the planes for debug/visual aid purposes
  124. *
  125. * @param b Boolean. Display the planes of the dragged object3d. Default is false;
  126. */
  127. public function set debug(b:Boolean):void
  128. {
  129. _debug = b;
  130. if (_debug && _planesContainer == null) {
  131. var size:Number = 1000;
  132. _red = new ColorMaterial(0xFF0000);
  133. _red.bothSides = true;
  134. _green = new ColorMaterial(0x00FF00);
  135. _green.bothSides = true;
  136. _blue = new ColorMaterial(0x0000FF);
  137. _blue.bothSides = true;
  138. _red.alpha = _green.alpha = _blue.alpha = .5;
  139. _planeXZ = new Mesh(new PlaneGeometry(size, size, 2, 2, true), _red);
  140. _planeXY = new Mesh(new PlaneGeometry(size, size, 2, 2, false), _green);
  141. _planeZY = new Mesh(new PlaneGeometry(size, size, 2, 2, false), _blue);
  142. _planeZY.rotationY = 90;
  143. _planesContainer = new ObjectContainer3D();
  144. _planesContainer.addChild(_planeXZ);
  145. _planesContainer.addChild(_planeXY);
  146. _planesContainer.addChild(_planeZY);
  147. _view.scene.addChild(_planesContainer);
  148. toggleDebug();
  149. updateDebug();
  150. } else {
  151. if (_planesContainer) {
  152. _planesContainer.removeChild(_planeXZ);
  153. _planesContainer.removeChild(_planeXY);
  154. _planesContainer.removeChild(_planeZY);
  155. _view.scene.removeChild(_planesContainer);
  156. _planesContainer = null;
  157. _planeXZ = _planeXY = _planeZY = null;
  158. _red = _green = _blue = null;
  159. }
  160. }
  161. }
  162. public function get debug():Boolean
  163. {
  164. return _debug;
  165. }
  166. /**
  167. * Changes the plane the object will be considered on.
  168. * If class debug is set to true. It display the selected plane for debug/visual aid purposes with a brighter color.
  169. * @param planeid String. Plane to drag the object3d on.
  170. * Possible strings are Drag3D.PLANE_XZ ("xz"), Drag3D.PLANE_XY ("xy") or Drag3D.PLANE_ZY ("zy"). Default is Drag3D.PLANE_XZ;
  171. */
  172. public function set plane(planeid:String):void
  173. {
  174. _planeid = planeid.toLowerCase();
  175. if (_planeid != PLANE_XZ && _planeid != PLANE_XY && _planeid != PLANE_ZY)
  176. throw new Error("Unvalid plane description, use: Drag3D.PLANE_XZ, Drag3D.PLANE_XY, or Drag3D.PLANE_ZY");
  177. planeObject3d = null;
  178. updateNormalPlanes();
  179. toggleDebug();
  180. }
  181. /**
  182. * Returns the Vector3D where mouse to scene ray hits the plane set for the class.
  183. *
  184. * @return Vector3D The intersection Vector3D
  185. * If both x and y params are NaN, the class will return the hit from mouse coordinates
  186. * @param x [optional] Number. x coordinate.
  187. * @param y [optional] Number. y coordinate.
  188. */
  189. public function getIntersect(x:Number = NaN, y:Number = NaN):Vector3D
  190. {
  191. intersect(x, y);
  192. return _intersect;
  193. }
  194. /**
  195. * if an ObjectContainer3D is set this handler will calculate the mouse intersection on given plane and will update position
  196. * and rotations of the ObjectContainer3D set accordingly
  197. */
  198. public function updateDrag():void
  199. {
  200. var localIntersect:Vector3D;
  201. if (_object3d == null)
  202. throw new Error("Drag3D error: no ObjectContainer3D or world planes specified");
  203. if (_debug)
  204. updateDebug();
  205. intersect();
  206. localIntersect = Matrix3DUtils.transformVector(_object3d.parent.inverseSceneTransform,_intersect);
  207. if (_offsetCenter == null) {
  208. _object3d.x = localIntersect.x;
  209. _object3d.y = localIntersect.y;
  210. _object3d.z = localIntersect.z;
  211. } else {
  212. _object3d.x = localIntersect.x + _offsetCenter.x;
  213. _object3d.y = localIntersect.y + _offsetCenter.y;
  214. _object3d.z = localIntersect.z + _offsetCenter.z;
  215. }
  216. }
  217. /**
  218. * Sets the target ObjectContainer3D to the class. The ObjectContainer3D that will be dragged
  219. *
  220. * @param object3d ObjectContainer3D. The ObjectContainer3D that will be dragged. Default is null. When null planes will be considered at 0,0,0 world
  221. */
  222. public function set object3d(object3d:ObjectContainer3D):void
  223. {
  224. _object3d = object3d;
  225. if (_debug)
  226. updateDebug();
  227. }
  228. /**
  229. * Defines planes as the position of a given ObjectContainer3D
  230. *
  231. * @param object3d ObjectContainer3D. The object3d that will be used to define the planes
  232. */
  233. public function set planeObject3d(object3d:ObjectContainer3D):void
  234. {
  235. updateNormalPlanes(object3d);
  236. if (_debug)
  237. updateDebug();
  238. }
  239. /**
  240. * Defines planes position by a postion Vector3D
  241. *
  242. * @param pos Vector3D. The Vector3D that will be used to define the planes position
  243. */
  244. public function set planePosition(pos:Vector3D):void
  245. {
  246. switch (_planeid) {
  247. //XZ
  248. case PLANE_XZ:
  249. _np.x = 0;
  250. _np.y = 1;
  251. _np.z = 0;
  252. break;
  253. //XY
  254. case PLANE_XY:
  255. _np.x = 0;
  256. _np.y = 0;
  257. _np.z = 1;
  258. break;
  259. //ZY
  260. case PLANE_ZY:
  261. _np.x = 1;
  262. _np.y = 0;
  263. _np.z = 0;
  264. break;
  265. }
  266. _a = -pos.x;
  267. _b = -pos.y;
  268. _c = -pos.z;
  269. _d = -(_a*_np.x + _b*_np.y + _c*_np.z);
  270. if (_debug)
  271. updateDebug();
  272. }
  273. private function init():void
  274. {
  275. if (!_view.camera.lens is PerspectiveLens)
  276. _view.camera.lens = new PerspectiveLens();
  277. }
  278. private function updateDebug():void
  279. {
  280. if (_a == 0 && _b == 0 && _c == 0) {
  281. _planesContainer.x = _planesContainer.y = _planesContainer.z = 0;
  282. _planesContainer.rotationX = _planesContainer.rotationY = _planesContainer.rotationZ = 0;
  283. } else {
  284. _planesContainer.x = -_a;
  285. _planesContainer.y = -_b;
  286. _planesContainer.z = -_c;
  287. if (_useRotations && _rotations) {
  288. _planesContainer.rotationX = _rotations.x;
  289. _planesContainer.rotationY = _rotations.y;
  290. _planesContainer.rotationZ = _rotations.z;
  291. }
  292. }
  293. }
  294. private function toggleDebug():void
  295. {
  296. if (_planeXZ) {
  297. var lowA:Number = .05;
  298. var highA:Number = .4;
  299. switch (_planeid) {
  300. case "zx":
  301. _planeid = PLANE_XZ;
  302. case PLANE_XZ:
  303. _red.alpha = highA;
  304. _green.alpha = _blue.alpha = lowA;
  305. break;
  306. case "yx":
  307. _planeid = PLANE_XY;
  308. case PLANE_XY:
  309. _red.alpha = _blue.alpha = lowA;
  310. _green.alpha = highA;
  311. break;
  312. case "yz":
  313. _planeid = PLANE_ZY;
  314. case PLANE_ZY:
  315. _red.alpha = _green.alpha = lowA;
  316. _blue.alpha = highA;
  317. break;
  318. default:
  319. throw new Error("Unvalid plane description, use: Drag3D.PLANE_XZ, Drag3D.PLANE_XY, or Drag3D.PLANE_ZY");
  320. }
  321. }
  322. }
  323. private function intersect(x:Number = NaN, y:Number = NaN):void
  324. {
  325. var pMouse:Vector3D = (isNaN(x) && isNaN(y))? _view.unproject(_view.mouseX, _view.mouseY, 1, Matrix3DUtils.CALCULATION_VECTOR3D) : _view.unproject(x, y, 1, Matrix3DUtils.CALCULATION_VECTOR3D);
  326. var cam:Vector3D = _view.camera.position;
  327. var d0:Number = _np.x*cam.x + _np.y*cam.y + _np.z*cam.z - _d;
  328. var d1:Number = _np.x*pMouse.x + _np.y*pMouse.y + _np.z*pMouse.z - _d;
  329. var m:Number = d1/( d1 - d0 );
  330. _intersect.x = pMouse.x + ( cam.x - pMouse.x )*m;
  331. _intersect.y = pMouse.y + ( cam.y - pMouse.y )*m;
  332. _intersect.z = pMouse.z + ( cam.z - pMouse.z )*m;
  333. if (_bSetOffset) {
  334. _bSetOffset = false;
  335. _offsetCenter.x = _offsetCenter.x - _intersect.x;
  336. _offsetCenter.y = _offsetCenter.y - _intersect.y;
  337. _offsetCenter.z = _offsetCenter.z - _intersect.z;
  338. }
  339. }
  340. private function updateNormalPlanes(obj:ObjectContainer3D = null):void
  341. {
  342. var world:Boolean = (obj == null)? true : false;
  343. if (_useRotations && !world) {
  344. switch (_planeid) {
  345. case PLANE_XZ:
  346. _np.x = obj.transform.rawData[4];
  347. _np.y = obj.transform.rawData[5];
  348. _np.z = obj.transform.rawData[6];
  349. break;
  350. case PLANE_XY:
  351. _np.x = obj.transform.rawData[8];
  352. _np.y = obj.transform.rawData[9];
  353. _np.z = obj.transform.rawData[10];
  354. break;
  355. case PLANE_ZY:
  356. _np.x = obj.transform.rawData[0];
  357. _np.y = obj.transform.rawData[1];
  358. _np.z = obj.transform.rawData[2];
  359. break;
  360. }
  361. if (!_rotations) {
  362. _rotations = new Vector3D();
  363. _baserotations = new Vector3D();
  364. }
  365. _rotations.x = obj.rotationX;
  366. _rotations.y = obj.rotationY;
  367. _rotations.z = obj.rotationZ;
  368. _baserotations.x = obj.rotationX;
  369. _baserotations.y = obj.rotationY;
  370. _baserotations.z = obj.rotationZ;
  371. _np.normalize();
  372. } else {
  373. if (_rotations && _baserotations) {
  374. _baserotations.x = _baserotations.y = _baserotations.z = 0;
  375. _rotations.x = _rotations.y = _rotations.z = 0;
  376. }
  377. switch (_planeid) {
  378. case PLANE_XZ:
  379. _np.x = 0;
  380. _np.y = 1;
  381. _np.z = 0;
  382. break;
  383. case PLANE_XY:
  384. _np.x = 0;
  385. _np.y = 0;
  386. _np.z = 1;
  387. break;
  388. case PLANE_ZY:
  389. _np.x = 1;
  390. _np.y = 0;
  391. _np.z = 0;
  392. break;
  393. }
  394. }
  395. _a = (world)? 0 : -obj.scenePosition.x;
  396. _b = (world)? 0 : -obj.scenePosition.y;
  397. _c = (world)? 0 : -obj.scenePosition.z;
  398. _d = -(_a*_np.x + _b*_np.y + _c*_np.z);
  399. }
  400. }
  401. }