Skip to content

Instantly share code, notes, and snippets.

@JohnBrookes
Created April 8, 2014 10:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JohnBrookes/4898c006c23a6aa71d0b to your computer and use it in GitHub Desktop.
Save JohnBrookes/4898c006c23a6aa71d0b to your computer and use it in GitHub Desktop.
Drag3D for both ortho and perspective Lens.
package away3d.tools.utils
{
import away3d.cameras.lenses.PerspectiveLens;
import away3d.containers.ObjectContainer3D;
import away3d.containers.View3D;
import away3d.core.math.Matrix3DUtils;
import away3d.entities.Mesh;
import away3d.materials.ColorMaterial;
import away3d.primitives.PlaneGeometry;
import flash.geom.Vector3D;
/**
* Class Drag3D allows free dragging of an ObjectContainer3D onto a given plane.
*
* locks on world planes
* locks on ObjectContainer3D planes
* locks on ObjectContainer3D rotations planes
*/
public class Drag3D
{
public static const PLANE_XZ:String = "xz";
public static const PLANE_XY:String = "xy";
public static const PLANE_ZY:String = "zy";
private const EPS:Number = 0.000001;
private var _view:View3D;
private var _debug:Boolean;
private var _object3d:ObjectContainer3D;
private var _planeXZ:Mesh;
private var _planeXY:Mesh;
private var _planeZY:Mesh;
private var _red:ColorMaterial;
private var _green:ColorMaterial;
private var _blue:ColorMaterial;
private var _planesContainer:ObjectContainer3D;
private var _np:Vector3D = new Vector3D(0.0, 0.0, 0.0);
private var _intersect:Vector3D = new Vector3D(0.0, 0.0, 0.0);
private var _rotations:Vector3D;
private var _baserotations:Vector3D;
private var _offsetCenter:Vector3D = new Vector3D(0.0, 0.0, 0.0);
private var _bSetOffset:Boolean;
private var _a:Number = 0;
private var _b:Number = 0;
private var _c:Number = 0;
private var _d:Number = 1;
private var _planeid:String;
private var _useRotations:Boolean;
// TODO: not used
// private var _inverse:Matrix3D = new Matrix3D();
/**
* Class Drag3D allows to drag 3d objects with the mouse.<code>Drag3D</code>
* @param view View3D. The view3d where the object to drag is or will be addChilded.
* @param object3d [optional] ObjectContainer3D. The object3D to drag.
* @param plane [optional] String. The plane to drag on.
*/
public function Drag3D(view:View3D, object3d:ObjectContainer3D = null, plane:String = PLANE_XZ)
{
_view = view;
_object3d = object3d;
_planeid = plane;
updateNormalPlanes();
init();
}
public function get object3d():ObjectContainer3D
{
return _object3d;
}
/**
* Defines if the target object3d plane will be aligned to object rotations or not
*
* @param b Boolean. Defines if the target object3d planes will be aligned to object rotations or not. Default is false.
*/
public function set useRotations(b:Boolean):void
{
_useRotations = b;
if (!b && _rotations)
_baserotations = null;
_rotations = null;
if (_planesContainer)
updateDebug();
}
public function get useRotations():Boolean
{
return _useRotations;
}
/**
* Defines an offset for the drag from center mesh to mouse position.
* object3d must have been set previously for this setter. if not an error is triggered
* Since the offset is set from center to mouse projection, its usually a good practice to set it during firt mouse down
* prior to drag.
*/
public function set offsetCenter(b:Boolean):void
{
if (b && !_object3d)
throw new Error("offsetCenter requires that an object3d as been assigned to the Drag3D class first!");
if (b) {
_offsetCenter.x = _object3d.scenePosition.x;
_offsetCenter.y = _object3d.scenePosition.y;
_offsetCenter.z = _object3d.scenePosition.z;
if (_offsetCenter.x == 0 && _offsetCenter.y == 0 && _offsetCenter.z == 0) {
_offsetCenter.x = EPS;
_offsetCenter.y = EPS;
_offsetCenter.z = EPS;
}
} else {
_offsetCenter.x = EPS;
_offsetCenter.y = EPS;
_offsetCenter.z = EPS;
}
_bSetOffset = b;
}
public function get offsetCenter():Boolean
{
return _bSetOffset;
}
/**
* getIntersect method returns the 3d point in space (Vector3D) where mouse hits the given plane.
*@return Vector3D the difference mouse mouse hit to object center
*/
public function get offsetMouseCenter():Vector3D
{
return _offsetCenter;
}
/**
* Displays the planes for debug/visual aid purposes
*
* @param b Boolean. Display the planes of the dragged object3d. Default is false;
*/
public function set debug(b:Boolean):void
{
_debug = b;
if (_debug && _planesContainer == null) {
var size:Number = 1000;
_red = new ColorMaterial(0xFF0000);
_red.bothSides = true;
_green = new ColorMaterial(0x00FF00);
_green.bothSides = true;
_blue = new ColorMaterial(0x0000FF);
_blue.bothSides = true;
_red.alpha = _green.alpha = _blue.alpha = .5;
_planeXZ = new Mesh(new PlaneGeometry(size, size, 2, 2, true), _red);
_planeXY = new Mesh(new PlaneGeometry(size, size, 2, 2, false), _green);
_planeZY = new Mesh(new PlaneGeometry(size, size, 2, 2, false), _blue);
_planeZY.rotationY = 90;
_planesContainer = new ObjectContainer3D();
_planesContainer.addChild(_planeXZ);
_planesContainer.addChild(_planeXY);
_planesContainer.addChild(_planeZY);
_view.scene.addChild(_planesContainer);
toggleDebug();
updateDebug();
} else {
if (_planesContainer) {
_planesContainer.removeChild(_planeXZ);
_planesContainer.removeChild(_planeXY);
_planesContainer.removeChild(_planeZY);
_view.scene.removeChild(_planesContainer);
_planesContainer = null;
_planeXZ = _planeXY = _planeZY = null;
_red = _green = _blue = null;
}
}
}
public function get debug():Boolean
{
return _debug;
}
/**
* Changes the plane the object will be considered on.
* If class debug is set to true. It display the selected plane for debug/visual aid purposes with a brighter color.
* @param planeid String. Plane to drag the object3d on.
* Possible strings are Drag3D.PLANE_XZ ("xz"), Drag3D.PLANE_XY ("xy") or Drag3D.PLANE_ZY ("zy"). Default is Drag3D.PLANE_XZ;
*/
public function set plane(planeid:String):void
{
_planeid = planeid.toLowerCase();
if (_planeid != PLANE_XZ && _planeid != PLANE_XY && _planeid != PLANE_ZY)
throw new Error("Unvalid plane description, use: Drag3D.PLANE_XZ, Drag3D.PLANE_XY, or Drag3D.PLANE_ZY");
planeObject3d = null;
updateNormalPlanes();
toggleDebug();
}
/**
* Returns the Vector3D where mouse to scene ray hits the plane set for the class.
*
* @return Vector3D The intersection Vector3D
* If both x and y params are NaN, the class will return the hit from mouse coordinates
* @param x [optional] Number. x coordinate.
* @param y [optional] Number. y coordinate.
*/
public function getIntersect(x:Number = NaN, y:Number = NaN):Vector3D
{
intersect(x, y);
return _intersect;
}
/**
* if an ObjectContainer3D is set this handler will calculate the mouse intersection on given plane and will update position
* and rotations of the ObjectContainer3D set accordingly
*/
public function updateDrag():void
{
var localIntersect:Vector3D;
if (_object3d == null)
throw new Error("Drag3D error: no ObjectContainer3D or world planes specified");
if (_debug)
updateDebug();
intersect();
localIntersect = Matrix3DUtils.transformVector(_object3d.parent.inverseSceneTransform,_intersect);
if (_offsetCenter == null) {
_object3d.x = localIntersect.x;
_object3d.y = localIntersect.y;
_object3d.z = localIntersect.z;
} else {
_object3d.x = localIntersect.x + _offsetCenter.x;
_object3d.y = localIntersect.y + _offsetCenter.y;
_object3d.z = localIntersect.z + _offsetCenter.z;
}
}
/**
* Sets the target ObjectContainer3D to the class. The ObjectContainer3D that will be dragged
*
* @param object3d ObjectContainer3D. The ObjectContainer3D that will be dragged. Default is null. When null planes will be considered at 0,0,0 world
*/
public function set object3d(object3d:ObjectContainer3D):void
{
_object3d = object3d;
if (_debug)
updateDebug();
}
/**
* Defines planes as the position of a given ObjectContainer3D
*
* @param object3d ObjectContainer3D. The object3d that will be used to define the planes
*/
public function set planeObject3d(object3d:ObjectContainer3D):void
{
updateNormalPlanes(object3d);
if (_debug)
updateDebug();
}
/**
* Defines planes position by a postion Vector3D
*
* @param pos Vector3D. The Vector3D that will be used to define the planes position
*/
public function set planePosition(pos:Vector3D):void
{
switch (_planeid) {
//XZ
case PLANE_XZ:
_np.x = 0;
_np.y = 1;
_np.z = 0;
break;
//XY
case PLANE_XY:
_np.x = 0;
_np.y = 0;
_np.z = 1;
break;
//ZY
case PLANE_ZY:
_np.x = 1;
_np.y = 0;
_np.z = 0;
break;
}
_a = -pos.x;
_b = -pos.y;
_c = -pos.z;
_d = -(_a*_np.x + _b*_np.y + _c*_np.z);
if (_debug)
updateDebug();
}
private function init():void
{
if (!_view.camera.lens is PerspectiveLens)
_view.camera.lens = new PerspectiveLens();
}
private function updateDebug():void
{
if (_a == 0 && _b == 0 && _c == 0) {
_planesContainer.x = _planesContainer.y = _planesContainer.z = 0;
_planesContainer.rotationX = _planesContainer.rotationY = _planesContainer.rotationZ = 0;
} else {
_planesContainer.x = -_a;
_planesContainer.y = -_b;
_planesContainer.z = -_c;
if (_useRotations && _rotations) {
_planesContainer.rotationX = _rotations.x;
_planesContainer.rotationY = _rotations.y;
_planesContainer.rotationZ = _rotations.z;
}
}
}
private function toggleDebug():void
{
if (_planeXZ) {
var lowA:Number = .05;
var highA:Number = .4;
switch (_planeid) {
case "zx":
_planeid = PLANE_XZ;
case PLANE_XZ:
_red.alpha = highA;
_green.alpha = _blue.alpha = lowA;
break;
case "yx":
_planeid = PLANE_XY;
case PLANE_XY:
_red.alpha = _blue.alpha = lowA;
_green.alpha = highA;
break;
case "yz":
_planeid = PLANE_ZY;
case PLANE_ZY:
_red.alpha = _green.alpha = lowA;
_blue.alpha = highA;
break;
default:
throw new Error("Unvalid plane description, use: Drag3D.PLANE_XZ, Drag3D.PLANE_XY, or Drag3D.PLANE_ZY");
}
}
}
private function intersect(x:Number = NaN, y:Number = NaN):void
{
var rayPosition:Vector3D = (isNaN(x) && isNaN(y))? _view.unproject(_view.mouseX, _view.mouseY, 0) : _view.unproject(x, y, 0);
var rayDirection:Vector3D = (isNaN(x) && isNaN(y))? _view.unproject(_view.mouseX, _view.mouseY, 1) : _view.unproject(x, y, 1);
rayDirection.x = rayDirection.x - rayPosition.x;
rayDirection.y = rayDirection.y - rayPosition.y;
rayDirection.z = rayDirection.z - rayPosition.z;
var nDotV:Number = _np.x * rayDirection.x + _np.y * +rayDirection.y + _np.z * rayDirection.z;
var disToPlane:Number = -( _np.x * rayPosition.x + _np.y * rayPosition.y + _np.z * rayPosition.z + _d );
var t:Number = disToPlane / nDotV;
_intersect.x = rayPosition.x + t * rayDirection.x;
_intersect.y = rayPosition.y + t * rayDirection.y;
_intersect.z = rayPosition.z + t * rayDirection.z;
if (_bSetOffset) {
_bSetOffset = false;
_offsetCenter.x = _offsetCenter.x - _intersect.x;
_offsetCenter.y = _offsetCenter.y - _intersect.y;
_offsetCenter.z = _offsetCenter.z - _intersect.z;
}
}
private function updateNormalPlanes(obj:ObjectContainer3D = null):void
{
var world:Boolean = (obj == null)? true : false;
if (_useRotations && !world) {
switch (_planeid) {
case PLANE_XZ:
_np.x = obj.transform.rawData[4];
_np.y = obj.transform.rawData[5];
_np.z = obj.transform.rawData[6];
break;
case PLANE_XY:
_np.x = obj.transform.rawData[8];
_np.y = obj.transform.rawData[9];
_np.z = obj.transform.rawData[10];
break;
case PLANE_ZY:
_np.x = obj.transform.rawData[0];
_np.y = obj.transform.rawData[1];
_np.z = obj.transform.rawData[2];
break;
}
if (!_rotations) {
_rotations = new Vector3D();
_baserotations = new Vector3D();
}
_rotations.x = obj.rotationX;
_rotations.y = obj.rotationY;
_rotations.z = obj.rotationZ;
_baserotations.x = obj.rotationX;
_baserotations.y = obj.rotationY;
_baserotations.z = obj.rotationZ;
_np.normalize();
} else {
if (_rotations && _baserotations) {
_baserotations.x = _baserotations.y = _baserotations.z = 0;
_rotations.x = _rotations.y = _rotations.z = 0;
}
switch (_planeid) {
case PLANE_XZ:
_np.x = 0;
_np.y = 1;
_np.z = 0;
break;
case PLANE_XY:
_np.x = 0;
_np.y = 0;
_np.z = 1;
break;
case PLANE_ZY:
_np.x = 1;
_np.y = 0;
_np.z = 0;
break;
}
}
_a = (world)? 0 : -obj.scenePosition.x;
_b = (world)? 0 : -obj.scenePosition.y;
_c = (world)? 0 : -obj.scenePosition.z;
_d = -(_a*_np.x + _b*_np.y + _c*_np.z);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment