From e318e7f545fe9ddfbdbffba41bf2f66aa9af8cc6 Mon Sep 17 00:00:00 2001 From: Vincent Fretin Date: Thu, 11 Jun 2026 11:07:45 +0200 Subject: [PATCH] Update to latest TransformControls --- package-lock.json | 8 +- package.json | 2 +- src/lib/TransformControls.js | 1365 ---------------------------------- src/lib/viewport.js | 49 +- 4 files changed, 38 insertions(+), 1386 deletions(-) delete mode 100644 src/lib/TransformControls.js diff --git a/package-lock.json b/package-lock.json index c83384c75..3bb8ec489 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-select": "^5.8.0", - "three": "0.168.0" + "three": "0.182.0" }, "devDependencies": { "@babel/core": "^7.24.0", @@ -14633,9 +14633,9 @@ } }, "node_modules/three": { - "version": "0.168.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.168.0.tgz", - "integrity": "sha512-6m6jXtDwMJEK/GGMbAOTSAmxNdzKvvBzgd7q8bE/7Tr6m7PaBh5kKLrN7faWtlglXbzj7sVba48Idwx+NRsZXw==", + "version": "0.182.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.182.0.tgz", + "integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==", "license": "MIT" }, "node_modules/thunky": { diff --git a/package.json b/package.json index 7e07e60c5..9c80f767b 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-select": "^5.8.0", - "three": "0.168.0" + "three": "0.182.0" }, "devDependencies": { "@babel/core": "^7.24.0", diff --git a/src/lib/TransformControls.js b/src/lib/TransformControls.js deleted file mode 100644 index bda0aadcd..000000000 --- a/src/lib/TransformControls.js +++ /dev/null @@ -1,1365 +0,0 @@ -/* eslint-disable curly */ -/** - * @author arodic / https://github.com/arodic - */ - -(function() { - 'use strict'; - - class GizmoMaterial extends THREE.MeshBasicMaterial { - constructor(parameters) { - super(); - - this.depthTest = false; - this.depthWrite = false; - this.fog = false; - this.side = THREE.FrontSide; - this.transparent = true; - - this.setValues(parameters); - - this.oldColor = this.color.clone(); - this.oldOpacity = this.opacity; - - this.highlight = function(highlighted) { - if (highlighted) { - this.color.setRGB(1, 1, 0); - this.opacity = 1; - } else { - this.color.copy(this.oldColor); - this.opacity = this.oldOpacity; - } - }; - } - } - - class GizmoLineMaterial extends THREE.LineBasicMaterial { - constructor(parameters) { - super(); - - this.depthTest = false; - this.depthWrite = false; - this.fog = false; - this.transparent = true; - this.linewidth = 1; - - this.setValues(parameters); - - this.oldColor = this.color.clone(); - this.oldOpacity = this.opacity; - - this.highlight = function(highlighted) { - if (highlighted) { - this.color.setRGB(1, 1, 0); - this.opacity = 1; - } else { - this.color.copy(this.oldColor); - this.opacity = this.oldOpacity; - } - }; - } - } - - var pickerMaterial = new GizmoMaterial({ - visible: false, - transparent: false - }); - - THREE.TransformGizmo = class TransformGizmo extends THREE.Object3D { - init() { - this.handles = new THREE.Object3D(); - this.pickers = new THREE.Object3D(); - this.planes = new THREE.Object3D(); - - this.add(this.handles); - this.add(this.pickers); - this.add(this.planes); - - // PLANES - - var planeGeometry = new THREE.PlaneGeometry(50, 50, 2, 2); - var planeMaterial = new THREE.MeshBasicMaterial({ - visible: false, - side: THREE.DoubleSide - }); - - var planes = { - XY: new THREE.Mesh(planeGeometry, planeMaterial), - YZ: new THREE.Mesh(planeGeometry, planeMaterial), - XZ: new THREE.Mesh(planeGeometry, planeMaterial), - XYZE: new THREE.Mesh(planeGeometry, planeMaterial) - }; - - this.activePlane = planes['XYZE']; - - planes['YZ'].rotation.set(0, Math.PI / 2, 0); - planes['XZ'].rotation.set(-Math.PI / 2, 0, 0); - - for (var i in planes) { - planes[i].name = i; - this.planes.add(planes[i]); - this.planes[i] = planes[i]; - } - - // HANDLES AND PICKERS - - var setupGizmos = function(gizmoMap, parent) { - for (var name in gizmoMap) { - for (i = gizmoMap[name].length; i--;) { - var object = gizmoMap[name][i][0]; - var position = gizmoMap[name][i][1]; - var rotation = gizmoMap[name][i][2]; - - object.name = name; - - object.renderOrder = Infinity; // avoid being hidden by other transparent objects - - if (position) - object.position.set(position[0], position[1], position[2]); - if (rotation) - object.rotation.set(rotation[0], rotation[1], rotation[2]); - - parent.add(object); - } - } - }; - - setupGizmos(this.handleGizmos, this.handles); - setupGizmos(this.pickerGizmos, this.pickers); - - // reset Transformations - - this.traverse(function(child) { - if (child instanceof THREE.Mesh) { - child.updateMatrix(); - - var tempGeometry = child.geometry.clone(); - tempGeometry.applyMatrix4(child.matrix); - child.geometry = tempGeometry; - - child.position.set(0, 0, 0); - child.rotation.set(0, 0, 0); - child.scale.set(1, 1, 1); - } - }); - } - - highlight(axis) { - this.traverse(function(child) { - if (child.material && child.material.highlight) { - if (child.name === axis) { - child.material.highlight(true); - } else { - child.material.highlight(false); - } - } - }); - } - - update(rotation, eye) { - var vec1 = new THREE.Vector3(0, 0, 0); - var vec2 = new THREE.Vector3(0, 1, 0); - var lookAtMatrix = new THREE.Matrix4(); - - this.traverse(function(child) { - if (child.name.search('E') !== -1) { - child.quaternion.setFromRotationMatrix( - lookAtMatrix.lookAt(eye, vec1, vec2) - ); - } else if ( - child.name.search('X') !== -1 || - child.name.search('Y') !== -1 || - child.name.search('Z') !== -1 - ) { - child.quaternion.setFromEuler(rotation); - } - }); - } - }; - - THREE.TransformGizmoTranslate = class TransformGizmoTranslate extends THREE.TransformGizmo { - constructor() { - super(); - - var arrowGeometry = new THREE.ConeGeometry(0.05, 0.2, 12, 1, false); - arrowGeometry.translate(0, 0.5, 0); - - var lineXGeometry = new THREE.BufferGeometry(); - lineXGeometry.setAttribute( - 'position', - new THREE.Float32BufferAttribute([0, 0, 0, 1, 0, 0], 3) - ); - - var lineYGeometry = new THREE.BufferGeometry(); - lineYGeometry.setAttribute( - 'position', - new THREE.Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3) - ); - - var lineZGeometry = new THREE.BufferGeometry(); - lineZGeometry.setAttribute( - 'position', - new THREE.Float32BufferAttribute([0, 0, 0, 0, 0, 1], 3) - ); - - this.handleGizmos = { - X: [ - [ - new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0xff0000 })), - [0.5, 0, 0], - [0, 0, -Math.PI / 2] - ], - [ - new THREE.Line( - lineXGeometry, - new GizmoLineMaterial({ color: 0xff0000 }) - ) - ] - ], - - Y: [ - [ - new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0x00ff00 })), - [0, 0.5, 0] - ], - [ - new THREE.Line( - lineYGeometry, - new GizmoLineMaterial({ color: 0x00ff00 }) - ) - ] - ], - - Z: [ - [ - new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0x0000ff })), - [0, 0, 0.5], - [Math.PI / 2, 0, 0] - ], - [ - new THREE.Line( - lineZGeometry, - new GizmoLineMaterial({ color: 0x0000ff }) - ) - ] - ], - - XYZ: [ - [ - new THREE.Mesh( - new THREE.OctahedronGeometry(0.1, 0), - new GizmoMaterial({ color: 0xffffff, opacity: 0.25 }) - ), - [0, 0, 0], - [0, 0, 0] - ] - ], - - XY: [ - [ - new THREE.Mesh( - new THREE.PlaneGeometry(0.29, 0.29), - new GizmoMaterial({ color: 0xffff00, opacity: 0.25 }) - ), - [0.15, 0.15, 0] - ] - ], - - YZ: [ - [ - new THREE.Mesh( - new THREE.PlaneGeometry(0.29, 0.29), - new GizmoMaterial({ color: 0x00ffff, opacity: 0.25 }) - ), - [0, 0.15, 0.15], - [0, Math.PI / 2, 0] - ] - ], - - XZ: [ - [ - new THREE.Mesh( - new THREE.PlaneGeometry(0.29, 0.29), - new GizmoMaterial({ color: 0xff00ff, opacity: 0.25 }) - ), - [0.15, 0, 0.15], - [-Math.PI / 2, 0, 0] - ] - ] - }; - - this.pickerGizmos = { - X: [ - [ - new THREE.Mesh( - new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, false), - pickerMaterial - ), - [0.6, 0, 0], - [0, 0, -Math.PI / 2] - ] - ], - - Y: [ - [ - new THREE.Mesh( - new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, false), - pickerMaterial - ), - [0, 0.6, 0] - ] - ], - - Z: [ - [ - new THREE.Mesh( - new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, false), - pickerMaterial - ), - [0, 0, 0.6], - [Math.PI / 2, 0, 0] - ] - ], - - XYZ: [ - [new THREE.Mesh(new THREE.OctahedronGeometry(0.2, 0), pickerMaterial)] - ], - - XY: [ - [ - new THREE.Mesh( - new THREE.PlaneGeometry(0.4, 0.4), - pickerMaterial - ), - [0.2, 0.2, 0] - ] - ], - - YZ: [ - [ - new THREE.Mesh( - new THREE.PlaneGeometry(0.4, 0.4), - pickerMaterial - ), - [0, 0.2, 0.2], - [0, Math.PI / 2, 0] - ] - ], - - XZ: [ - [ - new THREE.Mesh( - new THREE.PlaneGeometry(0.4, 0.4), - pickerMaterial - ), - [0.2, 0, 0.2], - [-Math.PI / 2, 0, 0] - ] - ] - }; - - this.init(); - } - - setActivePlane(axis, eye) { - var tempMatrix = new THREE.Matrix4(); - eye.applyMatrix4( - tempMatrix - .copy(tempMatrix.extractRotation(this.planes['XY'].matrixWorld)) - .invert() - ); - - if (axis === 'X') { - this.activePlane = this.planes['XY']; - - if (Math.abs(eye.y) > Math.abs(eye.z)) - this.activePlane = this.planes['XZ']; - } - - if (axis === 'Y') { - this.activePlane = this.planes['XY']; - - if (Math.abs(eye.x) > Math.abs(eye.z)) - this.activePlane = this.planes['YZ']; - } - - if (axis === 'Z') { - this.activePlane = this.planes['XZ']; - - if (Math.abs(eye.x) > Math.abs(eye.y)) - this.activePlane = this.planes['YZ']; - } - - if (axis === 'XYZ') this.activePlane = this.planes['XYZE']; - - if (axis === 'XY') this.activePlane = this.planes['XY']; - - if (axis === 'YZ') this.activePlane = this.planes['YZ']; - - if (axis === 'XZ') this.activePlane = this.planes['XZ']; - } - }; - - THREE.TransformGizmoRotate = class TransformGizmoRotate extends THREE.TransformGizmo { - constructor() { - super(); - - var CircleGeometry = function(radius, facing, arc) { - var geometry = new THREE.BufferGeometry(); - var vertices = []; - arc = arc || 1; - - for (var i = 0; i <= 64 * arc; ++i) { - if (facing === 'x') - vertices.push( - 0, - Math.cos((i / 32) * Math.PI) * radius, - Math.sin((i / 32) * Math.PI) * radius - ); - if (facing === 'y') - vertices.push( - Math.cos((i / 32) * Math.PI) * radius, - 0, - Math.sin((i / 32) * Math.PI) * radius - ); - if (facing === 'z') - vertices.push( - Math.sin((i / 32) * Math.PI) * radius, - Math.cos((i / 32) * Math.PI) * radius, - 0 - ); - } - - geometry.setAttribute( - 'position', - new THREE.Float32BufferAttribute(vertices, 3) - ); - return geometry; - }; - - this.handleGizmos = { - X: [ - [ - new THREE.Line( - new CircleGeometry(1, 'x', 0.5), - new GizmoLineMaterial({ color: 0xff0000 }) - ) - ] - ], - - Y: [ - [ - new THREE.Line( - new CircleGeometry(1, 'y', 0.5), - new GizmoLineMaterial({ color: 0x00ff00 }) - ) - ] - ], - - Z: [ - [ - new THREE.Line( - new CircleGeometry(1, 'z', 0.5), - new GizmoLineMaterial({ color: 0x0000ff }) - ) - ] - ], - - E: [ - [ - new THREE.Line( - new CircleGeometry(1.25, 'z', 1), - new GizmoLineMaterial({ color: 0xcccc00 }) - ) - ] - ], - - XYZE: [ - [ - new THREE.Line( - new CircleGeometry(1, 'z', 1), - new GizmoLineMaterial({ color: 0x787878 }) - ) - ] - ] - }; - - this.pickerGizmos = { - X: [ - [ - new THREE.Mesh( - new THREE.TorusGeometry(1, 0.12, 4, 12, Math.PI), - pickerMaterial - ), - [0, 0, 0], - [0, -Math.PI / 2, -Math.PI / 2] - ] - ], - - Y: [ - [ - new THREE.Mesh( - new THREE.TorusGeometry(1, 0.12, 4, 12, Math.PI), - pickerMaterial - ), - [0, 0, 0], - [Math.PI / 2, 0, 0] - ] - ], - - Z: [ - [ - new THREE.Mesh( - new THREE.TorusGeometry(1, 0.12, 4, 12, Math.PI), - pickerMaterial - ), - [0, 0, 0], - [0, 0, -Math.PI / 2] - ] - ], - - E: [ - [ - new THREE.Mesh( - new THREE.TorusGeometry(1.25, 0.12, 2, 24), - pickerMaterial - ) - ] - ], - - XYZE: [ - [ - new THREE.Mesh( - new THREE.TorusGeometry(1, 0.12, 2, 24), - pickerMaterial - ) - ] - ] - }; - - this.pickerGizmos.XYZE[0][0].visible = false; // disable XYZE picker gizmo - - this.init(); - } - - setActivePlane(axis) { - if (axis === 'E') this.activePlane = this.planes['XYZE']; - - if (axis === 'X') this.activePlane = this.planes['YZ']; - - if (axis === 'Y') this.activePlane = this.planes['XZ']; - - if (axis === 'Z') this.activePlane = this.planes['XY']; - } - - update(rotation, eye2) { - super.update(rotation, eye2); - - var tempMatrix = new THREE.Matrix4(); - var worldRotation = new THREE.Euler(0, 0, 1); - 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 quaternionX = new THREE.Quaternion(); - var quaternionY = new THREE.Quaternion(); - var quaternionZ = new THREE.Quaternion(); - var eye = eye2.clone(); - - worldRotation.copy(this.planes['XY'].rotation); - tempQuaternion.setFromEuler(worldRotation); - - tempMatrix - .makeRotationFromQuaternion(tempQuaternion) - .copy(tempMatrix) - .invert(); - eye.applyMatrix4(tempMatrix); - - this.traverse(function(child) { - tempQuaternion.setFromEuler(worldRotation); - - if (child.name === 'X') { - quaternionX.setFromAxisAngle(unitX, Math.atan2(-eye.y, eye.z)); - tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX); - child.quaternion.copy(tempQuaternion); - } - - if (child.name === 'Y') { - quaternionY.setFromAxisAngle(unitY, Math.atan2(eye.x, eye.z)); - tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionY); - child.quaternion.copy(tempQuaternion); - } - - if (child.name === 'Z') { - quaternionZ.setFromAxisAngle(unitZ, Math.atan2(eye.y, eye.x)); - tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionZ); - child.quaternion.copy(tempQuaternion); - } - }); - } - }; - - THREE.TransformGizmoScale = class TransformGizmoScale extends THREE.TransformGizmo { - constructor() { - super(); - - var arrowGeometry = new THREE.BoxGeometry(0.125, 0.125, 0.125); - arrowGeometry.translate(0, 0.5, 0); - - var lineXGeometry = new THREE.BufferGeometry(); - lineXGeometry.setAttribute( - 'position', - new THREE.Float32BufferAttribute([0, 0, 0, 1, 0, 0], 3) - ); - - var lineYGeometry = new THREE.BufferGeometry(); - lineYGeometry.setAttribute( - 'position', - new THREE.Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3) - ); - - var lineZGeometry = new THREE.BufferGeometry(); - lineZGeometry.setAttribute( - 'position', - new THREE.Float32BufferAttribute([0, 0, 0, 0, 0, 1], 3) - ); - - this.handleGizmos = { - X: [ - [ - new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0xff0000 })), - [0.5, 0, 0], - [0, 0, -Math.PI / 2] - ], - [ - new THREE.Line( - lineXGeometry, - new GizmoLineMaterial({ color: 0xff0000 }) - ) - ] - ], - - Y: [ - [ - new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0x00ff00 })), - [0, 0.5, 0] - ], - [ - new THREE.Line( - lineYGeometry, - new GizmoLineMaterial({ color: 0x00ff00 }) - ) - ] - ], - - Z: [ - [ - new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0x0000ff })), - [0, 0, 0.5], - [Math.PI / 2, 0, 0] - ], - [ - new THREE.Line( - lineZGeometry, - new GizmoLineMaterial({ color: 0x0000ff }) - ) - ] - ], - - XYZ: [ - [ - new THREE.Mesh( - new THREE.BoxGeometry(0.125, 0.125, 0.125), - new GizmoMaterial({ color: 0xffffff, opacity: 0.25 }) - ) - ] - ] - }; - - this.pickerGizmos = { - X: [ - [ - new THREE.Mesh( - new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, false), - pickerMaterial - ), - [0.6, 0, 0], - [0, 0, -Math.PI / 2] - ] - ], - - Y: [ - [ - new THREE.Mesh( - new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, false), - pickerMaterial - ), - [0, 0.6, 0] - ] - ], - - Z: [ - [ - new THREE.Mesh( - new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, false), - pickerMaterial - ), - [0, 0, 0.6], - [Math.PI / 2, 0, 0] - ] - ], - - XYZ: [ - [ - new THREE.Mesh( - new THREE.BoxGeometry(0.4, 0.4, 0.4), - pickerMaterial - ) - ] - ] - }; - - this.init(); - } - - setActivePlane (axis, eye) { - var tempMatrix = new THREE.Matrix4(); - eye.applyMatrix4( - tempMatrix - .copy(tempMatrix.extractRotation(this.planes['XY'].matrixWorld)) - .invert() - ); - - if (axis === 'X') { - this.activePlane = this.planes['XY']; - if (Math.abs(eye.y) > Math.abs(eye.z)) - this.activePlane = this.planes['XZ']; - } - - if (axis === 'Y') { - this.activePlane = this.planes['XY']; - if (Math.abs(eye.x) > Math.abs(eye.z)) - this.activePlane = this.planes['YZ']; - } - - if (axis === 'Z') { - this.activePlane = this.planes['XZ']; - if (Math.abs(eye.x) > Math.abs(eye.y)) - this.activePlane = this.planes['YZ']; - } - - if (axis === 'XYZ') this.activePlane = this.planes['XYZE']; - } - }; - - THREE.TransformControls = class TransformControls extends THREE.Object3D { - constructor(_camera, domElement) { - // TODO: Make non-uniform scale and rotate play nice in hierarchies - // TODO: ADD RXYZ contol - - super(); - - domElement = domElement !== undefined ? domElement : document; - - this.object = undefined; - this.visible = false; - this.translationSnap = null; - this.rotationSnap = null; - this.space = 'world'; - this.size = 1; - this.axis = null; - - var camera = _camera; - var scope = this; - - var _mode = 'translate'; - var _dragging = false; - var _gizmo = { - translate: new THREE.TransformGizmoTranslate(), - rotate: new THREE.TransformGizmoRotate(), - scale: new THREE.TransformGizmoScale() - }; - - for (var type in _gizmo) { - var gizmoObj = _gizmo[type]; - - gizmoObj.visible = type === _mode; - this.add(gizmoObj); - } - - var changeEvent = { type: 'change' }; - var mouseDownEvent = { type: 'mouseDown' }; - var mouseUpEvent = { type: 'mouseUp', mode: _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 scale = 1; - - 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 tempEuler = new THREE.Euler(); - 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 quaternionXYZ = new THREE.Quaternion(); - var quaternionX = new THREE.Quaternion(); - var quaternionY = new THREE.Quaternion(); - var quaternionZ = new THREE.Quaternion(); - var quaternionE = new THREE.Quaternion(); - - var oldPosition = new THREE.Vector3(); - var oldScale = new THREE.Vector3(); - var oldRotationMatrix = new THREE.Matrix4(); - - var parentRotationMatrix = new THREE.Matrix4(); - var parentScale = new THREE.Vector3(); - - var worldPosition = new THREE.Vector3(); - var worldRotation = new THREE.Euler(); - var worldRotationMatrix = new THREE.Matrix4(); - var camPosition = new THREE.Vector3(); - var camRotation = new THREE.Euler(); - - this.setCamera = function (_camera) { - camera = _camera; - }; - - this.activate = function() { - 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('mouseout', onPointerUp, false); - domElement.addEventListener('touchend', onPointerUp, false); - domElement.addEventListener('touchcancel', onPointerUp, false); - domElement.addEventListener('touchleave', onPointerUp, false); - }; - - this.activate(); - - 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('mouseout', onPointerUp); - domElement.removeEventListener('touchend', onPointerUp); - domElement.removeEventListener('touchcancel', onPointerUp); - domElement.removeEventListener('touchleave', onPointerUp); - }; - - this.attach = function(object) { - this.object = object; - this.visible = true; - this.update(true); - }; - - this.detach = function() { - this.object = undefined; - this.visible = false; - this.axis = null; - }; - - this.getMode = function() { - return _mode; - }; - - this.setMode = function(mode) { - _mode = mode || _mode; - - if (_mode === 'scale') scope.space = 'local'; - - for (var type in _gizmo) _gizmo[type].visible = type === _mode; - - this.update(); - scope.dispatchEvent(changeEvent); - }; - - this.setTranslationSnap = function(translationSnap) { - scope.translationSnap = translationSnap; - }; - - this.setRotationSnap = function(rotationSnap) { - scope.rotationSnap = rotationSnap; - }; - - this.setSize = function(size) { - scope.size = size; - this.update(true); - scope.dispatchEvent(changeEvent); - }; - - this.setSpace = function(space) { - scope.space = space; - this.update(); - scope.dispatchEvent(changeEvent); - }; - - this.update = function(updateScale) { - if (scope.object === undefined) return; - - scope.object.updateMatrixWorld(); - worldPosition.setFromMatrixPosition(scope.object.matrixWorld); - worldRotation.setFromRotationMatrix( - tempMatrix.extractRotation(scope.object.matrixWorld) - ); - - camera.updateMatrixWorld(); - camPosition.setFromMatrixPosition(camera.matrixWorld); - camRotation.setFromRotationMatrix( - tempMatrix.extractRotation(camera.matrixWorld) - ); - - scale = (worldPosition.distanceTo(camPosition) / 6) * scope.size; - this.position.copy(worldPosition); - if (updateScale) { - this.scale.set(scale, scale, scale); - } - - if (camera instanceof THREE.PerspectiveCamera) { - eye - .copy(camPosition) - .sub(worldPosition) - .normalize(); - } else if (camera instanceof THREE.OrthographicCamera) { - eye.copy(camPosition).normalize(); - } - - if (scope.space === 'local') { - _gizmo[_mode].update(worldRotation, eye); - } else if (scope.space === 'world') { - _gizmo[_mode].update(new THREE.Euler(), eye); - } - - _gizmo[_mode].highlight(scope.axis); - }; - - function onPointerHover(event) { - if ( - scope.object === undefined || - _dragging === true || - (event.button !== undefined && event.button !== 0) - ) - return; - - var pointer = event.changedTouches ? event.changedTouches[0] : event; - - var intersect = intersectObjects(pointer, _gizmo[_mode].pickers.children); - - var axis = null; - - if (intersect) { - axis = intersect.object.name; - - event.preventDefault(); - } - - if (scope.axis !== axis) { - scope.axis = axis; - scope.update(); - scope.dispatchEvent(changeEvent); - } - } - - function onPointerDown(event) { - if ( - scope.object === undefined || - _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[_mode].pickers.children - ); - - if (intersect) { - event.preventDefault(); - event.stopPropagation(); - - scope.axis = intersect.object.name; - - scope.dispatchEvent(mouseDownEvent); - - scope.update(); - - eye - .copy(camPosition) - .sub(worldPosition) - .normalize(); - - _gizmo[_mode].setActivePlane(scope.axis, eye); - - var planeIntersect = intersectObjects(pointer, [ - _gizmo[_mode].activePlane - ]); - - if (planeIntersect) { - oldPosition.copy(scope.object.position); - oldScale.copy(scope.object.scale); - - oldRotationMatrix.extractRotation(scope.object.matrix); - worldRotationMatrix.extractRotation(scope.object.matrixWorld); - - parentRotationMatrix.extractRotation( - scope.object.parent.matrixWorld - ); - parentScale.setFromMatrixScale( - tempMatrix.copy(scope.object.parent.matrixWorld).invert() - ); - - offset.copy(planeIntersect.point); - } - } - } - - _dragging = true; - } - - function onPointerMove(event) { - if ( - scope.object === undefined || - scope.axis === null || - _dragging === false || - (event.button !== undefined && event.button !== 0) - ) - return; - - var pointer = event.changedTouches ? event.changedTouches[0] : event; - - var planeIntersect = intersectObjects(pointer, [ - _gizmo[_mode].activePlane - ]); - - if (planeIntersect === false) return; - - event.preventDefault(); - event.stopPropagation(); - - point.copy(planeIntersect.point); - - if (_mode === 'translate') { - point.sub(offset); - point.multiply(parentScale); - - if (scope.space === 'local') { - point.applyMatrix4(tempMatrix.copy(worldRotationMatrix).invert()); - - if (scope.axis.search('X') === -1) point.x = 0; - if (scope.axis.search('Y') === -1) point.y = 0; - if (scope.axis.search('Z') === -1) point.z = 0; - - point.applyMatrix4(oldRotationMatrix); - - scope.object.position.copy(oldPosition); - scope.object.position.add(point); - } - - if (scope.space === 'world' || scope.axis.search('XYZ') !== -1) { - if (scope.axis.search('X') === -1) point.x = 0; - if (scope.axis.search('Y') === -1) point.y = 0; - if (scope.axis.search('Z') === -1) point.z = 0; - - point.applyMatrix4(tempMatrix.copy(parentRotationMatrix).invert()); - - scope.object.position.copy(oldPosition); - scope.object.position.add(point); - } - - if (scope.translationSnap !== null) { - if (scope.space === 'local') { - scope.object.position.applyMatrix4( - tempMatrix.copy(worldRotationMatrix).invert() - ); - } - - if (scope.axis.search('X') !== -1) - scope.object.position.x = - Math.round(scope.object.position.x / scope.translationSnap) * - scope.translationSnap; - if (scope.axis.search('Y') !== -1) - scope.object.position.y = - Math.round(scope.object.position.y / scope.translationSnap) * - scope.translationSnap; - if (scope.axis.search('Z') !== -1) - scope.object.position.z = - Math.round(scope.object.position.z / scope.translationSnap) * - scope.translationSnap; - - if (scope.space === 'local') { - scope.object.position.applyMatrix4(worldRotationMatrix); - } - } - } else if (_mode === 'scale') { - point.sub(offset); - point.multiply(parentScale); - - if (scope.space === 'local') { - if (scope.axis === 'XYZ') { - scale = 1 + point.y / Math.max(oldScale.x, oldScale.y, oldScale.z); - - scope.object.scale.x = oldScale.x * scale; - scope.object.scale.y = oldScale.y * scale; - scope.object.scale.z = oldScale.z * scale; - } else { - point.applyMatrix4(tempMatrix.copy(worldRotationMatrix).invert()); - - if (scope.axis === 'X') - scope.object.scale.x = oldScale.x * (1 + point.x / oldScale.x); - if (scope.axis === 'Y') - scope.object.scale.y = oldScale.y * (1 + point.y / oldScale.y); - if (scope.axis === 'Z') - scope.object.scale.z = oldScale.z * (1 + point.z / oldScale.z); - } - } - } else if (_mode === 'rotate') { - point.sub(worldPosition); - point.multiply(parentScale); - tempVector.copy(offset).sub(worldPosition); - tempVector.multiply(parentScale); - - if (scope.axis === 'E') { - point.applyMatrix4(tempMatrix.copy(lookAtMatrix).invert()); - tempVector.applyMatrix4(tempMatrix.copy(lookAtMatrix).invert()); - - rotation.set( - Math.atan2(point.z, point.y), - Math.atan2(point.x, point.z), - Math.atan2(point.y, point.x) - ); - offsetRotation.set( - Math.atan2(tempVector.z, tempVector.y), - Math.atan2(tempVector.x, tempVector.z), - Math.atan2(tempVector.y, tempVector.x) - ); - - tempQuaternion.setFromRotationMatrix( - tempMatrix.copy(parentRotationMatrix).invert() - ); - - quaternionE.setFromAxisAngle(eye, rotation.z - offsetRotation.z); - quaternionXYZ.setFromRotationMatrix(worldRotationMatrix); - - tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionE); - tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ); - - scope.object.quaternion.copy(tempQuaternion); - } else if (scope.axis === 'XYZE') { - var p = point - .clone() - .cross(tempVector) - .normalize(); - quaternionE.setFromEuler(tempEuler.set(p.x, p.y, p.z)); // rotation axis - - tempQuaternion.setFromRotationMatrix( - tempMatrix.copy(parentRotationMatrix).invert() - ); - quaternionX.setFromAxisAngle( - quaternionE, - -point.clone().angleTo(tempVector) - ); - quaternionXYZ.setFromRotationMatrix(worldRotationMatrix); - - tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX); - tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ); - - scope.object.quaternion.copy(tempQuaternion); - } else if (scope.space === 'local') { - point.applyMatrix4(tempMatrix.copy(worldRotationMatrix).invert()); - - tempVector.applyMatrix4(tempMatrix.copy(worldRotationMatrix).invert()); - - rotation.set( - Math.atan2(point.z, point.y), - Math.atan2(point.x, point.z), - Math.atan2(point.y, point.x) - ); - offsetRotation.set( - Math.atan2(tempVector.z, tempVector.y), - Math.atan2(tempVector.x, tempVector.z), - Math.atan2(tempVector.y, tempVector.x) - ); - - quaternionXYZ.setFromRotationMatrix(oldRotationMatrix); - - if (scope.rotationSnap !== null) { - quaternionX.setFromAxisAngle( - unitX, - Math.round((rotation.x - offsetRotation.x) / scope.rotationSnap) * - scope.rotationSnap - ); - quaternionY.setFromAxisAngle( - unitY, - Math.round((rotation.y - offsetRotation.y) / scope.rotationSnap) * - scope.rotationSnap - ); - quaternionZ.setFromAxisAngle( - unitZ, - Math.round((rotation.z - offsetRotation.z) / scope.rotationSnap) * - scope.rotationSnap - ); - } else { - quaternionX.setFromAxisAngle(unitX, rotation.x - offsetRotation.x); - quaternionY.setFromAxisAngle(unitY, rotation.y - offsetRotation.y); - quaternionZ.setFromAxisAngle(unitZ, rotation.z - offsetRotation.z); - } - - if (scope.axis === 'X') - quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionX); - if (scope.axis === 'Y') - quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionY); - if (scope.axis === 'Z') - quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionZ); - - scope.object.quaternion.copy(quaternionXYZ); - } else if (scope.space === 'world') { - rotation.set( - Math.atan2(point.z, point.y), - Math.atan2(point.x, point.z), - Math.atan2(point.y, point.x) - ); - offsetRotation.set( - Math.atan2(tempVector.z, tempVector.y), - Math.atan2(tempVector.x, tempVector.z), - Math.atan2(tempVector.y, tempVector.x) - ); - - tempQuaternion.setFromRotationMatrix( - tempMatrix.copy(parentRotationMatrix).invert() - ); - - if (scope.rotationSnap !== null) { - quaternionX.setFromAxisAngle( - unitX, - Math.round((rotation.x - offsetRotation.x) / scope.rotationSnap) * - scope.rotationSnap - ); - quaternionY.setFromAxisAngle( - unitY, - Math.round((rotation.y - offsetRotation.y) / scope.rotationSnap) * - scope.rotationSnap - ); - quaternionZ.setFromAxisAngle( - unitZ, - Math.round((rotation.z - offsetRotation.z) / scope.rotationSnap) * - scope.rotationSnap - ); - } else { - quaternionX.setFromAxisAngle(unitX, rotation.x - offsetRotation.x); - quaternionY.setFromAxisAngle(unitY, rotation.y - offsetRotation.y); - quaternionZ.setFromAxisAngle(unitZ, rotation.z - offsetRotation.z); - } - - quaternionXYZ.setFromRotationMatrix(worldRotationMatrix); - - if (scope.axis === 'X') - tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX); - if (scope.axis === 'Y') - tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionY); - if (scope.axis === 'Z') - tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionZ); - - tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ); - - scope.object.quaternion.copy(tempQuaternion); - } - } - - // Trim decimals. - if (_mode === 'translate') { - scope.object.position.x = parseFloat( - scope.object.position.x.toFixed(5) - ); - scope.object.position.y = parseFloat( - scope.object.position.y.toFixed(5) - ); - scope.object.position.z = parseFloat( - scope.object.position.z.toFixed(5) - ); - } else if (_mode === 'rotate') { - scope.object.rotation.x = parseFloat( - scope.object.rotation.x.toFixed(5) - ); - scope.object.rotation.y = parseFloat( - scope.object.rotation.y.toFixed(5) - ); - scope.object.rotation.z = parseFloat( - scope.object.rotation.z.toFixed(5) - ); - } else { - scope.object.scale.x = parseFloat(scope.object.scale.x.toFixed(5)); - scope.object.scale.y = parseFloat(scope.object.scale.y.toFixed(5)); - scope.object.scale.z = parseFloat(scope.object.scale.z.toFixed(5)); - } - - scope.update(); - scope.dispatchEvent(changeEvent); - objectChangeEvent.mode = _mode; - scope.dispatchEvent(objectChangeEvent); - } - - function onPointerUp(event) { - event.preventDefault(); // Prevent MouseEvent on mobile - - if (event.button !== undefined && event.button !== 0) return; - - if (_dragging && scope.axis !== null) { - mouseUpEvent.mode = _mode; - scope.dispatchEvent(mouseUpEvent); - } - - _dragging = false; - - if ('TouchEvent' in window && event instanceof TouchEvent) { - // Force "rollover" - - scope.axis = null; - scope.update(); - scope.dispatchEvent(changeEvent); - } 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; - } - } - }; -})(); diff --git a/src/lib/viewport.js b/src/lib/viewport.js index c8d5a3457..c040267d8 100644 --- a/src/lib/viewport.js +++ b/src/lib/viewport.js @@ -1,5 +1,4 @@ -// eslint-disable-next-line no-unused-vars -import TransformControls from './TransformControls.js'; +import { TransformControls } from 'three/addons/controls/TransformControls.js'; // eslint-disable-next-line no-unused-vars import EditorControls from './EditorControls.js'; @@ -43,10 +42,7 @@ export function Viewport(inspector) { } const camera = inspector.camera; - const transformControls = new THREE.TransformControls( - camera, - inspector.container - ); + const transformControls = new TransformControls(camera, inspector.container); transformControls.size = 0.75; // Pose snapshot taken on the gizmo's mouseDown, BEFORE TransformControls @@ -55,12 +51,35 @@ export function Viewport(inspector) { // already post-mutation by the time objectChange fires. let transformPreDragValues = null; - transformControls.addEventListener('objectChange', (evt) => { + transformControls.addEventListener('objectChange', () => { const object = transformControls.object; if (object === undefined) { return; } + const mode = transformControls.mode; + + // Trim to 3 decimals. + if (mode === 'translate') { + object.position.set( + parseFloat(object.position.x.toFixed(3)), + parseFloat(object.position.y.toFixed(3)), + parseFloat(object.position.z.toFixed(3)) + ); + } else if (mode === 'rotate') { + object.rotation.set( + parseFloat(object.rotation.x.toFixed(3)), + parseFloat(object.rotation.y.toFixed(3)), + parseFloat(object.rotation.z.toFixed(3)) + ); + } else if (mode === 'scale') { + object.scale.set( + parseFloat(object.scale.x.toFixed(3)), + parseFloat(object.scale.y.toFixed(3)), + parseFloat(object.scale.z.toFixed(3)) + ); + } + selectionBox.setFromObject(object); updateHelpers(object); @@ -68,16 +87,16 @@ export function Viewport(inspector) { // Emit update event for watcher. let component; let value; - if (evt.mode === 'translate') { + if (mode === 'translate') { component = 'position'; value = `${object.position.x} ${object.position.y} ${object.position.z}`; - } else if (evt.mode === 'rotate') { + } else if (mode === 'rotate') { component = 'rotation'; const d = THREE.MathUtils.radToDeg; value = `${d(object.rotation.x)} ${d(object.rotation.y)} ${d( object.rotation.z )}`; - } else if (evt.mode === 'scale') { + } else if (mode === 'scale') { component = 'scale'; value = `${object.scale.x} ${object.scale.y} ${object.scale.z}`; } @@ -109,7 +128,7 @@ export function Viewport(inspector) { controls.enabled = true; }); - sceneHelpers.add(transformControls); + sceneHelpers.add(transformControls.getHelper()); Events.on('entityupdate', (detail) => { const object = detail.entity.object3D; @@ -128,25 +147,24 @@ export function Viewport(inspector) { controls.zoomSpeed = 0.05; controls.setAspectRatio(sceneEl.canvas.width / sceneEl.canvas.height); controls.addEventListener('change', () => { - transformControls.update(true); // true is updateScale Events.emit('camerachanged'); }); Events.on('cameratoggle', (data) => { controls.setCamera(data.camera); - transformControls.setCamera(data.camera); + transformControls.camera = data.camera; updateAspectRatio(); }); function disableControls() { mouseCursor.disable(); - transformControls.dispose(); + transformControls.enabled = false; controls.enabled = false; } function enableControls() { mouseCursor.enable(); - transformControls.activate(); + transformControls.enabled = true; controls.enabled = true; } enableControls(); @@ -218,7 +236,6 @@ export function Viewport(inspector) { } } - transformControls.update(); if (object instanceof THREE.PerspectiveCamera) { object.updateProjectionMatrix(); }