diff --git a/docs/examples/en/controls/DragControls.html b/docs/examples/en/controls/DragControls.html
index 8671d98c049004..cb6ddd78119173 100644
--- a/docs/examples/en/controls/DragControls.html
+++ b/docs/examples/en/controls/DragControls.html
@@ -127,21 +127,6 @@
Methods
See the base [page:Controls] class for common methods.
- [method:undefined connect] ()
-
- Adds the event listeners of the controls.
-
-
- [method:undefined disconnect] ()
-
- Removes the event listeners of the controls.
-
-
- [method:undefined dispose] ()
-
- Should be called if the controls is no longer required.
-
-
Source
diff --git a/docs/examples/en/controls/TransformControls.html b/docs/examples/en/controls/TransformControls.html
index 334ca36614f04c..8e91adadda2cc7 100644
--- a/docs/examples/en/controls/TransformControls.html
+++ b/docs/examples/en/controls/TransformControls.html
@@ -7,7 +7,7 @@
- [page:Object3D] →
+ [page:Controls] →
[name]
@@ -41,7 +41,7 @@ [name]( [param:Camera camera], [param:HTMLDOMElement domElement] )
[page:Camera camera]: The camera of the rendered scene.
- [page:HTMLDOMElement domElement]: The HTML element used for event listeners.
+ [page:HTMLDOMElement domElement]: The HTML element used for event listeners. (optional)
Creates a new instance of [name].
@@ -73,7 +73,7 @@
objectChange
Properties
- See the base [page:Object3D] class for common properties.
+ See the base [page:Controls] class for common properties.
[property:String axis]
@@ -85,32 +85,16 @@
[property:Camera camera]
The camera of the rendered scene.
- [property:HTMLDOMElement domElement]
-
- The HTMLDOMElement used to listen for mouse / touch events. This must be passed in the constructor; changing it here will
- not set up new event listeners.
-
-
[property:Boolean dragging]
Whether or not dragging is currently performed. Read-only property.
- [property:Boolean enabled]
-
- Whether or not the controls are enabled.
-
-
[property:String mode]
The current transformation mode. Possible values are "translate", "rotate" and "scale". Default is `translate`.
- [property:Object3D object]
-
- The 3D object being controlled.
-
-
[property:Number rotationSnap]
By default, 3D objects are continuously rotated. If you set this property to a numeric value (radians), you can define in which
@@ -150,7 +134,7 @@
[property:Number translationSnap]
Methods
- See the base [page:Object3D] class for common methods.
+ See the base [page:Controls] class for common methods.
[method:TransformControls attach] ( [param:Object3D object] )
@@ -167,9 +151,10 @@
[method:TransformControls detach] ()
Removes the current 3D object from the controls and makes the helper UI invisible.
- [method:undefined dispose] ()
+ [method:Object3D getGizmo] ()
- Should be called if the controls is no longer required.
+ Returns the visual representation of the controls. Add the gizmo to your scene to visually transform the attached
+ 3D object.
[method:Raycaster getRaycaster] ()
diff --git a/editor/js/Viewport.js b/editor/js/Viewport.js
index 3b04fc8b4f91c1..cbe2e3a42812f9 100644
--- a/editor/js/Viewport.js
+++ b/editor/js/Viewport.js
@@ -142,7 +142,7 @@ function Viewport( editor ) {
} );
- sceneHelpers.add( transformControls );
+ sceneHelpers.add( transformControls.getGizmo() );
//
diff --git a/examples/jsm/controls/TransformControls.js b/examples/jsm/controls/TransformControls.js
index 38ee44ae6f322a..fe24c66c1f36e3 100644
--- a/examples/jsm/controls/TransformControls.js
+++ b/examples/jsm/controls/TransformControls.js
@@ -1,6 +1,7 @@
import {
BoxGeometry,
BufferGeometry,
+ Controls,
CylinderGeometry,
DoubleSide,
Euler,
@@ -36,32 +37,22 @@ const _mouseDownEvent = { type: 'mouseDown', mode: null };
const _mouseUpEvent = { type: 'mouseUp', mode: null };
const _objectChangeEvent = { type: 'objectChange' };
-class TransformControls extends Object3D {
+class TransformControls extends Controls {
- constructor( camera, domElement ) {
+ constructor( camera, domElement = null ) {
- super();
-
- if ( domElement === undefined ) {
-
- console.warn( 'THREE.TransformControls: The second parameter "domElement" is now mandatory.' );
- domElement = document;
+ super( undefined, domElement );
- }
+ const root = new TransformControlsRoot( this );
+ this._root = root;
- this.isTransformControls = true;
+ const gizmo = new TransformControlsGizmo();
+ this._gizmo = gizmo;
+ root.add( gizmo );
- this.visible = false;
- this.domElement = domElement;
- this.domElement.style.touchAction = 'none'; // disable touch scroll
-
- const _gizmo = new TransformControlsGizmo();
- this._gizmo = _gizmo;
- this.add( _gizmo );
-
- const _plane = new TransformControlsPlane();
- this._plane = _plane;
- this.add( _plane );
+ const plane = new TransformControlsPlane();
+ this._plane = plane;
+ root.add( plane );
const scope = this;
@@ -83,8 +74,8 @@ class TransformControls extends Object3D {
if ( propValue !== value ) {
propValue = value;
- _plane[ propName ] = value;
- _gizmo[ propName ] = value;
+ plane[ propName ] = value;
+ gizmo[ propName ] = value;
scope.dispatchEvent( { type: propName + '-changed', value: value } );
scope.dispatchEvent( _changeEvent );
@@ -96,8 +87,8 @@ class TransformControls extends Object3D {
} );
scope[ propName ] = defaultValue;
- _plane[ propName ] = defaultValue;
- _gizmo[ propName ] = defaultValue;
+ plane[ propName ] = defaultValue;
+ gizmo[ propName ] = defaultValue;
}
@@ -172,50 +163,38 @@ class TransformControls extends Object3D {
this._onPointerMove = onPointerMove.bind( this );
this._onPointerUp = onPointerUp.bind( this );
- this.domElement.addEventListener( 'pointerdown', this._onPointerDown );
- this.domElement.addEventListener( 'pointermove', this._onPointerHover );
- this.domElement.addEventListener( 'pointerup', this._onPointerUp );
-
- }
-
- // updateMatrixWorld updates key transformation variables
- updateMatrixWorld( force ) {
-
- if ( this.object !== undefined ) {
+ if ( domElement !== null ) {
- this.object.updateMatrixWorld();
-
- if ( this.object.parent === null ) {
-
- console.error( 'TransformControls: The attached 3D object must be a part of the scene graph.' );
-
- } else {
+ this.connect();
- this.object.parent.matrixWorld.decompose( this._parentPosition, this._parentQuaternion, this._parentScale );
+ }
- }
+ }
- this.object.matrixWorld.decompose( this.worldPosition, this.worldQuaternion, this._worldScale );
+ connect() {
- this._parentQuaternionInv.copy( this._parentQuaternion ).invert();
- this._worldQuaternionInv.copy( this.worldQuaternion ).invert();
+ this.domElement.addEventListener( 'pointerdown', this._onPointerDown );
+ this.domElement.addEventListener( 'pointermove', this._onPointerHover );
+ this.domElement.addEventListener( 'pointerup', this._onPointerUp );
- }
+ this.domElement.style.touchAction = 'none'; // disable touch scroll
- this.camera.updateMatrixWorld();
- this.camera.matrixWorld.decompose( this.cameraPosition, this.cameraQuaternion, this._cameraScale );
+ }
- if ( this.camera.isOrthographicCamera ) {
+ disconnect() {
- this.camera.getWorldDirection( this.eye ).negate();
+ this.domElement.removeEventListener( 'pointerdown', this._onPointerDown );
+ this.domElement.removeEventListener( 'pointermove', this._onPointerHover );
+ this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
+ this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
- } else {
+ this.domElement.style.touchAction = 'auto';
- this.eye.copy( this.cameraPosition ).sub( this.worldPosition ).normalize();
+ }
- }
+ getGizmo() {
- super.updateMatrixWorld( force );
+ return this._root;
}
@@ -555,10 +534,7 @@ class TransformControls extends Object3D {
dispose() {
- this.domElement.removeEventListener( 'pointerdown', this._onPointerDown );
- this.domElement.removeEventListener( 'pointermove', this._onPointerHover );
- this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
- this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
+ this.disconnect();
this.traverse( function ( child ) {
@@ -573,7 +549,7 @@ class TransformControls extends Object3D {
attach( object ) {
this.object = object;
- this.visible = true;
+ this._root.visible = true;
return this;
@@ -583,9 +559,10 @@ class TransformControls extends Object3D {
detach() {
this.object = undefined;
- this.visible = false;
this.axis = null;
+ this._root.visible = false;
+
return this;
}
@@ -778,6 +755,64 @@ const _v1 = new Vector3();
const _v2 = new Vector3();
const _v3 = new Vector3();
+class TransformControlsRoot extends Object3D {
+
+ constructor( controls ) {
+
+ super();
+
+ this.isTransformControlsRoot = true;
+
+ this.controls = controls;
+ this.visible = false;
+
+ }
+
+ // updateMatrixWorld updates key transformation variables
+ updateMatrixWorld( force ) {
+
+ const controls = this.controls;
+
+ if ( controls.object !== undefined ) {
+
+ controls.object.updateMatrixWorld();
+
+ if ( controls.object.parent === null ) {
+
+ console.error( 'TransformControls: The attached 3D object must be a part of the scene graph.' );
+
+ } else {
+
+ controls.object.parent.matrixWorld.decompose( controls._parentPosition, controls._parentQuaternion, controls._parentScale );
+
+ }
+
+ controls.object.matrixWorld.decompose( controls.worldPosition, controls.worldQuaternion, controls._worldScale );
+
+ controls._parentQuaternionInv.copy( controls._parentQuaternion ).invert();
+ controls._worldQuaternionInv.copy( controls.worldQuaternion ).invert();
+
+ }
+
+ controls.camera.updateMatrixWorld();
+ controls.camera.matrixWorld.decompose( controls.cameraPosition, controls.cameraQuaternion, controls._cameraScale );
+
+ if ( controls.camera.isOrthographicCamera ) {
+
+ controls.camera.getWorldDirection( controls.eye ).negate();
+
+ } else {
+
+ controls.eye.copy( controls.cameraPosition ).sub( controls.worldPosition ).normalize();
+
+ }
+
+ super.updateMatrixWorld( force );
+
+ }
+
+}
+
class TransformControlsGizmo extends Object3D {
constructor() {
diff --git a/examples/misc_controls_transform.html b/examples/misc_controls_transform.html
index b19805c8ff63ec..12cf80d4c2b472 100644
--- a/examples/misc_controls_transform.html
+++ b/examples/misc_controls_transform.html
@@ -78,7 +78,6 @@
control = new TransformControls( currentCamera, renderer.domElement );
control.addEventListener( 'change', render );
-
control.addEventListener( 'dragging-changed', function ( event ) {
orbit.enabled = ! event.value;
@@ -89,7 +88,9 @@
scene.add( mesh );
control.attach( mesh );
- scene.add( control );
+
+ const gizmo = control.getGizmo();
+ scene.add( gizmo );
window.addEventListener( 'resize', onWindowResize );
diff --git a/examples/webgl_animation_skinning_ik.html b/examples/webgl_animation_skinning_ik.html
index 90475be5bc4359..5537f3310df2cd 100644
--- a/examples/webgl_animation_skinning_ik.html
+++ b/examples/webgl_animation_skinning_ik.html
@@ -151,7 +151,7 @@
transformControls.showX = false;
transformControls.space = 'world';
transformControls.attach( OOI.target_hand_l );
- scene.add( transformControls );
+ scene.add( transformControls.getGizmo() );
// disable orbitControls while using transformControls
transformControls.addEventListener( 'mouseDown', () => orbitControls.enabled = false );
diff --git a/examples/webgl_geometry_spline_editor.html b/examples/webgl_geometry_spline_editor.html
index 9ec6fecd5064b0..c43358a5969b94 100644
--- a/examples/webgl_geometry_spline_editor.html
+++ b/examples/webgl_geometry_spline_editor.html
@@ -145,7 +145,7 @@
controls.enabled = ! event.value;
} );
- scene.add( transformControl );
+ scene.add( transformControl.getGizmo() );
transformControl.addEventListener( 'objectChange', function () {
diff --git a/examples/webgl_modifier_curve.html b/examples/webgl_modifier_curve.html
index 8e4dc45f880b63..22d3fe81654d1c 100644
--- a/examples/webgl_modifier_curve.html
+++ b/examples/webgl_modifier_curve.html
@@ -191,7 +191,7 @@
const target = intersects[ 0 ].object;
control.attach( target );
- scene.add( control );
+ scene.add( control.getGizmo() );
}
diff --git a/examples/webgl_modifier_curve_instanced.html b/examples/webgl_modifier_curve_instanced.html
index ac8e7fc035d7ef..5c65f7121d8153 100644
--- a/examples/webgl_modifier_curve_instanced.html
+++ b/examples/webgl_modifier_curve_instanced.html
@@ -221,7 +221,7 @@
const target = intersects[ 0 ].object;
control.attach( target );
- scene.add( control );
+ scene.add( control.getGizmo() );
}
diff --git a/examples/webgl_shadowmap_progressive.html b/examples/webgl_shadowmap_progressive.html
index 795046abbfe8b9..eb10c97247e324 100644
--- a/examples/webgl_shadowmap_progressive.html
+++ b/examples/webgl_shadowmap_progressive.html
@@ -76,7 +76,7 @@
} );
control.attach( lightOrigin );
- scene.add( control );
+ scene.add( control.getGizmo() );
// create 8 directional lights to speed up the convergence
for ( let l = 0; l < lightCount; l ++ ) {
@@ -142,7 +142,7 @@
} );
control2.attach( object );
- scene.add( control2 );
+ scene.add( control2.getGizmo() );
const lightTarget = new THREE.Group();
lightTarget.position.set( 0, 20, 0 );
for ( let l = 0; l < dirLights.length; l ++ ) {
diff --git a/examples/webgpu_tsl_compute_attractors_particles.html b/examples/webgpu_tsl_compute_attractors_particles.html
index b1e0128a06f769..86c78957904f65 100644
--- a/examples/webgpu_tsl_compute_attractors_particles.html
+++ b/examples/webgpu_tsl_compute_attractors_particles.html
@@ -118,7 +118,7 @@
attractor.controls.attach( attractor.reference );
attractor.controls.visible = true;
attractor.controls.enabled = attractor.controls.visible;
- scene.add( attractor.controls );
+ scene.add( attractor.controls.getGizmo() );
attractor.controls.addEventListener( 'dragging-changed', ( event ) => {
diff --git a/examples/webxr_xr_controls_transform.html b/examples/webxr_xr_controls_transform.html
index 649a19835454d6..fe0d7572281fd9 100644
--- a/examples/webxr_xr_controls_transform.html
+++ b/examples/webxr_xr_controls_transform.html
@@ -162,7 +162,7 @@
controls = new TransformControls( camera, renderer.domElement );
controls.attach( group.children[ 0 ] );
- scene.add( controls );
+ scene.add( controls.getGizmo() );
//