123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607 |
- /**
- * @author arodic / https://github.com/arodic
- */
- THREE.TransformControls = function ( camera, domElement ) {
- // TODO: Make non-uniform scale and rotate play nice in hierarchies
- // TODO: prevent infinite horizon translate
- THREE.Object3D.call( this );
- domElement = ( domElement !== undefined ) ? domElement : document;
- this.visible = false;
- var _gizmo = new THREE.TransformControlsGizmo();
- this.add( _gizmo );
- var _plane = new THREE.TransformControlsPlane();
- this.add( _plane );
- var scope = this;
- defineProperty( "camera", camera );
- defineProperty( "object", undefined );
- defineProperty( "axis", null );
- defineProperty( "mode", "translate" );
- defineProperty( "translationSnap", null );
- defineProperty( "rotationSnap", null );
- defineProperty( "space", "world" );
- defineProperty( "size", 1 );
- scope.dragging = false;
- var changeEvent = { type: "change" };
- var mouseDownEvent = { type: "mouseDown" };
- var mouseUpEvent = { type: "mouseUp", mode: scope.mode };
- var objectChangeEvent = { type: "objectChange" };
- var ray = new THREE.Raycaster();
- var pointerVector = new THREE.Vector2();
- var point = new THREE.Vector3();
- var offset = new THREE.Vector3();
- var rotation = new THREE.Vector3();
- var offsetRotation = new THREE.Vector3();
- var lookAtMatrix = new THREE.Matrix4();
- var _eye = new THREE.Vector3();
- var _tempMatrix = new THREE.Matrix4();
- var _tempVector = new THREE.Vector3();
- var _tempQuaternion = new THREE.Quaternion();
- var _unitX = new THREE.Vector3( 1, 0, 0 );
- var _unitY = new THREE.Vector3( 0, 1, 0 );
- var _unitZ = new THREE.Vector3( 0, 0, 1 );
- var _identityEuler = new THREE.Euler();
- // var oldPosition = new THREE.Vector3();
- // var oldScale = new THREE.Vector3();
- // var oldRotationMatrix = new THREE.Matrix4();
- var _worldRotationStart = new THREE.Quaternion();
- var _worldPositionStart = new THREE.Vector3();
- var _worldPointStart = new THREE.Vector3();
- var _localPointStart = new THREE.Vector3();
- var _localScale = new THREE.Vector3();
- var _localPoint = new THREE.Vector3();
- var _worldPoint = new THREE.Vector3();
- var _worldShift = new THREE.Vector3();
- var _localShift = new THREE.Vector3();
- var _worldCross = new THREE.Vector3();
- var _localCross = new THREE.Vector3();
- var _worldQuaternion = new THREE.Quaternion();
- var _localQuaternion = new THREE.Quaternion();
- var _worldPosition = new THREE.Vector3();
- var _worldRotation = new THREE.Euler();
- var alignVector = new THREE.Vector3();
- var _positionStart = new THREE.Vector3();
- var _quaternionStart = new THREE.Quaternion();
- var _scaleStart = new THREE.Vector3();
- domElement.addEventListener( "mousedown", onPointerDown, false );
- domElement.addEventListener( "touchstart", onPointerDown, false );
- domElement.addEventListener( "mousemove", onPointerHover, false );
- domElement.addEventListener( "touchmove", onPointerHover, false );
- domElement.addEventListener( "mousemove", onPointerMove, false );
- domElement.addEventListener( "touchmove", onPointerMove, false );
- domElement.addEventListener( "mouseup", onPointerUp, false );
- domElement.addEventListener( "mouseleave", onPointerUp, false );
- domElement.addEventListener( "mouseout", onPointerUp, false );
- domElement.addEventListener( "touchend", onPointerUp, false );
- domElement.addEventListener( "touchcancel", onPointerUp, false );
- domElement.addEventListener( "touchleave", onPointerUp, false );
- domElement.addEventListener( "contextmenu", onContext, false );
- this.dispose = function () {
- domElement.removeEventListener( "mousedown", onPointerDown );
- domElement.removeEventListener( "touchstart", onPointerDown );
- domElement.removeEventListener( "mousemove", onPointerHover );
- domElement.removeEventListener( "touchmove", onPointerHover );
- domElement.removeEventListener( "mousemove", onPointerMove );
- domElement.removeEventListener( "touchmove", onPointerMove );
- domElement.removeEventListener( "mouseup", onPointerUp );
- domElement.removeEventListener( "mouseleave", onPointerUp );
- domElement.removeEventListener( "mouseout", onPointerUp );
- domElement.removeEventListener( "touchend", onPointerUp );
- domElement.removeEventListener( "touchcancel", onPointerUp );
- domElement.removeEventListener( "touchleave", onPointerUp );
- domElement.removeEventListener( "contextmenu", onContext );
- };
- this.attach = function ( object ) {
- this.object = object;
- this.visible = true;
- };
- this.detach = function () {
- this.object = undefined;
- this.visible = false;
- this.axis = null;
- };
- function defineProperty( propName, defaultValue ) {
- var propValue = defaultValue;
- Object.defineProperty( scope, propName, {
- get: function() {
- return propValue !== undefined ? propValue : defaultValue;
- },
- set: function( value ) {
- if ( propValue !== value ) {
- propValue = value;
- scope.dispatchEvent( changeEvent );
- }
- }
- });
- scope[ propName ] = defaultValue;
- }
- this.updateMatrixWorld = function () {
- if ( this.object === undefined ) return;
- if ( this.mode === 'scale') this.space = 'local';
- // camera.updateMatrixWorld();
- // this.object.updateMatrixWorld();
- _worldPosition.setFromMatrixPosition( this.object.matrixWorld );
- _worldRotation.setFromRotationMatrix( _tempMatrix.extractRotation( this.object.matrixWorld ) );
- this.position.copy( _worldPosition );
- if ( camera instanceof THREE.PerspectiveCamera ) {
- _eye.setFromMatrixPosition( camera.matrixWorld ).sub( _worldPosition ).normalize();
- } else if ( camera instanceof THREE.OrthographicCamera ) {
- _eye.setFromMatrixPosition( camera.matrixWorld ).normalize();
- }
- // TODO
- this._worldPosition = _worldPosition;
- this._worldRotation = _worldRotation;
- this._eye = _eye;
- var scale = _worldPosition.distanceTo( _tempVector.setFromMatrixPosition( camera.matrixWorld ) ) / 6 * this.size;
- this.scale.set( scale, scale, scale );
- THREE.Object3D.prototype.updateMatrixWorld.call( this );
- };
- function onContext( event ) {
- event.preventDefault();
- }
- function onPointerHover( event ) {
- if ( scope.object === undefined || scope.dragging === true || ( event.button !== undefined && event.button !== 0 ) ) return;
- var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
- var intersect = intersectObjects( pointer, _gizmo.picker[ scope.mode ].children );
- if ( intersect ) {
- scope.axis = intersect.object.name;
- event.preventDefault();
- } else {
- scope.axis = null;
- }
- }
- function onPointerDown( event ) {
- if ( scope.object === undefined || scope.dragging === true || ( event.button !== undefined && event.button !== 0 ) ) return;
- var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
- if ( pointer.button === 0 || pointer.button === undefined ) {
- var intersect = intersectObjects( pointer, _gizmo.picker[ scope.mode ].children );
- if ( intersect ) {
- event.preventDefault();
- event.stopPropagation();
- scope.axis = intersect.object.name;
- _plane.update( scope.space === "local" ? _worldRotation : _identityEuler, _eye );
- var planeIntersect = intersectObjects( pointer, [ _plane ] );
- if ( planeIntersect ) {
- _positionStart.copy( scope.object.position );
- _quaternionStart.copy( scope.object.quaternion );
- _scaleStart.copy( scope.object.scale );
- _worldRotationStart.setFromRotationMatrix( scope.object.matrixWorld );
- _worldPositionStart.setFromMatrixPosition( scope.object.matrixWorld );
- _worldPointStart.copy( planeIntersect.point ).sub( _worldPositionStart );
- _localPointStart.copy( _worldPointStart ).applyQuaternion( _worldRotationStart.clone().inverse() );
- }
- } else {
- scope.axis = null;
- }
- }
- scope.dragging = true;
- }
- function onPointerMove( event ) {
- var axis = scope.axis;
- var mode = scope.mode;
- var object = scope.object;
- var space = scope.space;
- if ( object === undefined || axis === null || scope.dragging === false || ( event.button !== undefined && event.button !== 0 ) ) return;
- var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
- var planeIntersect = intersectObjects( pointer, [ _plane ] );
- if ( planeIntersect === false ) return;
- event.preventDefault();
- event.stopPropagation();
- _worldPoint.copy( planeIntersect.point ).sub( _worldPositionStart );
- _worldShift.subVectors( _worldPoint, _worldPointStart );
- _localPoint.copy( _worldPoint );
- _localPoint.applyQuaternion( _worldRotationStart.clone().inverse() );
- _localShift.subVectors( _localPoint, _localPointStart );
- _worldCross.copy( _worldPoint ).cross( _worldPointStart );
- _localCross.copy( _localPoint ).cross( _localPointStart );
- if ( mode === 'translate' ) {
- if ( axis.search( 'X' ) === -1 ) {
- _worldShift.x = 0;
- _localShift.x = 0;
- }
- if ( axis.search( 'Y' ) === -1 ) {
- _worldShift.y = 0;
- _localShift.y = 0;
- }
- if ( axis.search( 'Z' ) === -1 ) {
- _worldShift.z = 0;
- _localShift.z = 0;
- }
- // Apply translate
- if ( space === 'local' ) {
- object.position.copy(_localShift).applyQuaternion( _quaternionStart );
- } else {
- object.position.copy( _worldShift );
- }
- object.position.add( _positionStart );
- if ( scope.translationSnap ) {
- if ( space === 'local' ) {
- object.position.applyQuaternion(_tempQuaternion.copy( _quaternionStart ).inverse() );
- if ( axis.search( 'X' ) !== -1 ) {
- object.position.x = Math.round( object.position.x / scope.translationSnap ) * scope.translationSnap;
- }
- if ( axis.search( 'Y' ) !== -1 ) {
- object.position.y = Math.round( object.position.y / scope.translationSnap ) * scope.translationSnap;
- }
- if ( axis.search( 'Z' ) !== -1 ) {
- object.position.z = Math.round( object.position.z / scope.translationSnap ) * scope.translationSnap;
- }
- object.position.applyQuaternion( _quaternionStart );
- }
- if ( space === 'world' ) {
- if ( object.parent ) {
- object.position.add( _tempVector.setFromMatrixPosition( object.parent.matrixWorld ) );
- }
- if ( axis.search( 'X' ) !== -1 ) {
- object.position.x = Math.round( object.position.x / scope.translationSnap ) * scope.translationSnap;
- }
- if ( axis.search( 'Y' ) !== -1 ) {
- object.position.y = Math.round( object.position.y / scope.translationSnap ) * scope.translationSnap;
- }
- if ( axis.search( 'Z' ) !== -1 ) {
- object.position.z = Math.round( object.position.z / scope.translationSnap ) * scope.translationSnap;
- }
- if ( object.parent ) {
- object.position.sub( _tempVector.setFromMatrixPosition( object.parent.matrixWorld ) );
- }
- }
- }
- } else if ( mode === 'scale' ) {
- if ( axis === 'XYZ' ) {
- _localScale.set( _worldShift.y / 50, _worldShift.y / 50, _worldShift.y / 50 ).addScalar( 1 );
- } else {
- _localScale.set(
- axis.search( 'X' ) !== -1 ? _localShift.x / 100 : 0,
- axis.search( 'Y' ) !== -1 ? _localShift.y / 100 : 0,
- axis.search( 'Z' ) !== -1 ? _localShift.z / 100 : 0
- ).addScalar( 1 );
- }
- // Apply scale
- object.scale.copy( _scaleStart ).multiply( _localScale );
- } else if ( mode === 'rotate' ) {
- if ( axis === 'E' ) {
- _localCross.applyQuaternion( _worldRotationStart ).normalize();
- direction = _localCross.dot( _eye ) < 0 ? 1 : -1;
- _worldQuaternion.setFromAxisAngle( _eye, _localPoint.angleTo( _localPointStart ) * direction );
- } else if ( axis === 'XYZE' ) {
- // TODO: not working
- // _tempVector.copy( _worldShift ).cross( _eye ).normalize();
- // _worldQuaternion.setFromAxisAngle( _tempVector, -2 * offset.length() );
- } else {
- var rotation = scope.space === "local" ? _worldRotation : _identityEuler;
- // TODO: make rotation speed relative to pointer movement in view space.
- var LINEAR_ROTATION_SPEED = 0.005;
- if ( axis === 'X' ) {
- alignVector.set( 1, 0, 0 ).applyEuler( rotation );
- if ( Math.abs( alignVector.dot( _eye ) ) > 0.3 ) {
- _localQuaternion.setFromAxisAngle( _unitX, _localPoint.angleTo( _localPointStart ) * ( _localCross.x > 0 ? -1 : 1 ) );
- _worldQuaternion.setFromAxisAngle( _unitX, _worldPoint.angleTo( _worldPointStart ) * ( _worldCross.x > 0 ? -1 : 1 ) );
- } else {
- _localQuaternion.setFromAxisAngle( _unitX, _worldPoint.sub( _worldPointStart ).dot( _unitX.clone().applyQuaternion( _worldRotationStart ).cross( _eye ) ) * LINEAR_ROTATION_SPEED );
- _worldQuaternion.setFromAxisAngle( _unitX, _worldPoint.sub( _worldPointStart ).dot( _unitX.clone().cross( _eye ) ) * LINEAR_ROTATION_SPEED );
- }
- } else if ( axis === 'Y' ) {
- alignVector.set( 0, 1, 0 ).applyEuler( rotation );
- if ( Math.abs( alignVector.dot( _eye ) ) > 0.3 ) {
- _localQuaternion.setFromAxisAngle( _unitY, _localPoint.angleTo( _localPointStart ) * ( _localCross.y > 0 ? -1 : 1 ) );
- _worldQuaternion.setFromAxisAngle( _unitY, _worldPoint.angleTo( _worldPointStart ) * ( _worldCross.y > 0 ? -1 : 1 ) );
- } else {
- _localQuaternion.setFromAxisAngle( _unitY, _worldPoint.sub( _worldPointStart ).dot( _unitY.clone().applyEuler( rotation ).cross( _eye ) ) * LINEAR_ROTATION_SPEED );
- _worldQuaternion.setFromAxisAngle( _unitY, _worldPoint.sub( _worldPointStart ).dot( _unitY.clone().cross( _eye ) ) * LINEAR_ROTATION_SPEED );
- }
- } else if ( axis === 'Z' ) {
- alignVector.set( 0, 0, 1 ).applyEuler( rotation );
- if ( Math.abs( alignVector.dot( _eye ) ) > 0.3 ) {
- _localQuaternion.setFromAxisAngle( _unitZ, _localPoint.angleTo( _localPointStart ) * ( _localCross.z > 0 ? -1 : 1 ) );
- _worldQuaternion.setFromAxisAngle( _unitZ, _worldPoint.angleTo( _worldPointStart ) * ( _worldCross.z > 0 ? -1 : 1 ) );
- } else {
- _localQuaternion.setFromAxisAngle( _unitZ, _worldPoint.sub( _worldPointStart ).dot( _unitZ.clone().applyEuler( rotation ).cross( _eye ) ) * LINEAR_ROTATION_SPEED );
- _worldQuaternion.setFromAxisAngle( _unitZ, _worldPoint.sub( _worldPointStart ).dot( _unitZ.clone().cross( _eye ) ) * LINEAR_ROTATION_SPEED );
- }
- }
- }
- // Apply rotate
- if ( axis === 'E' || axis === 'XYZE' ) {
- space = 'world';
- }
- if ( space === 'local' ) {
- object.quaternion.copy( _quaternionStart );
- object.quaternion.multiply( _localQuaternion );
- } else {
- object.quaternion.copy( _worldQuaternion );
- object.quaternion.multiply( _quaternionStart );
- }
- if ( scope.snapAngle) {
- // TODO: implement rotation snap
- }
- }
- scope.dispatchEvent( changeEvent );
- scope.dispatchEvent( objectChangeEvent );
- }
- function onPointerUp( event ) {
- event.preventDefault(); // Prevent MouseEvent on mobile
- if ( event.button !== undefined && event.button !== 0 ) return;
- if ( scope.dragging && ( scope.axis !== null ) ) {
- mouseUpEvent.mode = scope.mode;
- scope.dispatchEvent( mouseUpEvent );
- }
- scope.dragging = false;
- if ( 'TouchEvent' in window && event instanceof TouchEvent ) {
- // Force "rollover"
- scope.axis = null;
- } else {
- onPointerHover( event );
- }
- }
- function intersectObjects( pointer, objects ) {
- var rect = domElement.getBoundingClientRect();
- var x = ( pointer.clientX - rect.left ) / rect.width;
- var y = ( pointer.clientY - rect.top ) / rect.height;
- pointerVector.set( ( x * 2 ) - 1, - ( y * 2 ) + 1 );
- ray.setFromCamera( pointerVector, camera );
- var intersections = ray.intersectObjects( objects, true );
- return intersections[ 0 ] ? intersections[ 0 ] : false;
- }
- // TODO: depricate
- this.getMode = function () {
- return scope.mode;
- console.warn( 'THREE.TransformControls: getMode function has been depricated.' );
- };
- this.setMode = function ( mode ) {
- scope.mode = mode;
- console.warn( 'THREE.TransformControls: setMode function has been depricated.' );
- };
- this.setTranslationSnap = function ( translationSnap ) {
- scope.translationSnap = translationSnap;
- console.warn( 'THREE.TransformControls: setTranslationSnap function has been depricated.' );
- };
- this.setRotationSnap = function ( rotationSnap ) {
- scope.rotationSnap = rotationSnap;
- console.warn( 'THREE.TransformControls: setRotationSnap function has been depricated.' );
- };
- this.setSize = function ( size ) {
- scope.size = size;
- console.warn( 'THREE.TransformControls: setSize function has been depricated.' );
- };
- this.setSpace = function ( space ) {
- scope.space = space;
- console.warn( 'THREE.TransformControls: setSpace function has been depricated.' );
- };
- this.update = function () {
- console.warn( 'THREE.TransformControls: update function has been depricated.' );
- }
- };
- THREE.TransformControls.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), {
- constructor: THREE.TransformControls,
- isTransformControls: true
- } );
|