PageRenderTime 83ms CodeModel.GetById 8ms app.highlight 66ms RepoModel.GetById 1ms app.codeStats 2ms

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