diff --git a/src/extras/gizmo/scale-gizmo.js b/src/extras/gizmo/scale-gizmo.js index c38eafc794c..63478337df5 100644 --- a/src/extras/gizmo/scale-gizmo.js +++ b/src/extras/gizmo/scale-gizmo.js @@ -20,6 +20,7 @@ const tmpQ1 = new Quat(); // constants const GLANCE_EPSILON = 0.98; +const CAMERA_EPSILON = 0.999; /** * Scaling gizmo. @@ -95,6 +96,14 @@ class ScaleGizmo extends TransformGizmo { */ _nodeScales = new Map(); + /** + * Internal forward vector of the camera in the direction of the gizmo. + * + * @type {Vec3} + * @private + */ + _forward = new Vec3(); + /** * Internal state if transform should use uniform scaling. * @@ -113,7 +122,7 @@ class ScaleGizmo extends TransformGizmo { * * @type {boolean} */ - flipPlanes = true; + flipShapes = true; /** * The lower bound for scaling. @@ -154,7 +163,7 @@ class ScaleGizmo extends TransformGizmo { }); this._app.on('prerender', () => { - this._planesLookAtCamera(); + this._shapesLookAtCamera(); }); } @@ -371,22 +380,46 @@ class ScaleGizmo extends TransformGizmo { /** * @private */ - _planesLookAtCamera() { - tmpV1.cross(this._camera.entity.forward, this.root.right); + _shapesLookAtCamera() { + tmpV1.copy(this.root.getPosition()).sub(this._camera.entity.getPosition()).normalize(); + if (tmpV1.dot(this._forward) > CAMERA_EPSILON) { + return; + } + this._forward.copy(tmpV1); + + let dot = this._forward.dot(this.root.right); + this._shapes.x.entity.enabled = Math.abs(dot) < GLANCE_EPSILON; + if (this.flipShapes) { + this._shapes.x.flipped = dot > 0; + } + + dot = this._forward.dot(this.root.up); + this._shapes.y.entity.enabled = Math.abs(dot) < GLANCE_EPSILON; + if (this.flipShapes) { + this._shapes.y.flipped = dot > 0; + } + + dot = this._forward.dot(this.root.forward); + this._shapes.z.entity.enabled = Math.abs(dot) < GLANCE_EPSILON; + if (this.flipShapes) { + this._shapes.z.flipped = dot < 0; + } + + tmpV1.cross(this._forward, this.root.right); this._shapes.yz.entity.enabled = tmpV1.length() < GLANCE_EPSILON; - if (this.flipPlanes) { + if (this.flipShapes) { this._shapes.yz.flipped = tmpV2.set(0, +(tmpV1.dot(this.root.forward) > 0), +(tmpV1.dot(this.root.up) > 0)); } - tmpV1.cross(this._camera.entity.forward, this.root.forward); + tmpV1.cross(this._forward, this.root.forward); this._shapes.xy.entity.enabled = tmpV1.length() < GLANCE_EPSILON; - if (this.flipPlanes) { + if (this.flipShapes) { this._shapes.xy.flipped = tmpV2.set(+(tmpV1.dot(this.root.up) > 0), +(tmpV1.dot(this.root.right) < 0), 0); } - tmpV1.cross(this._camera.entity.forward, this.root.up); + tmpV1.cross(this._forward, this.root.up); this._shapes.xz.entity.enabled = tmpV1.length() < GLANCE_EPSILON; - if (this.flipPlanes) { + if (this.flipShapes) { this._shapes.xz.flipped = tmpV2.set(+(tmpV1.dot(this.root.forward) < 0), 0, +(tmpV1.dot(this.root.right) < 0)); } } diff --git a/src/extras/gizmo/shape/arrow-shape.js b/src/extras/gizmo/shape/arrow-shape.js index 78064e102cd..c7dca58777d 100644 --- a/src/extras/gizmo/shape/arrow-shape.js +++ b/src/extras/gizmo/shape/arrow-shape.js @@ -27,6 +27,8 @@ class ArrowShape extends Shape { _line; + _flipped = false; + constructor(device, options = {}) { super(device, options); @@ -95,6 +97,25 @@ class ArrowShape extends Shape { return this._tolerance; } + set flipped(value) { + if (this._flipped === value) { + return; + } + this._flipped = value; + if (this._rotation.equals(Vec3.ZERO)) { + tmpV1.set(0, 0, this._flipped ? 180 : 0); + } else { + tmpV1.copy(this._rotation).mulScalar(this._flipped ? -1 : 1); + } + + this._line.enabled = !this._flipped; + this.entity.setLocalEulerAngles(tmpV1); + } + + get flipped() { + return this._flipped; + } + _createArrow() { this._createRoot('arrow'); diff --git a/src/extras/gizmo/shape/boxline-shape.js b/src/extras/gizmo/shape/boxline-shape.js index 14ef087f71d..871c953c2ec 100644 --- a/src/extras/gizmo/shape/boxline-shape.js +++ b/src/extras/gizmo/shape/boxline-shape.js @@ -25,6 +25,8 @@ class BoxLineShape extends Shape { _line; + _flipped = false; + constructor(device, options = {}) { super(device, options); @@ -84,6 +86,25 @@ class BoxLineShape extends Shape { return this._tolerance; } + set flipped(value) { + if (this._flipped === value) { + return; + } + this._flipped = value; + if (this._rotation.equals(Vec3.ZERO)) { + tmpV1.set(0, 0, this._flipped ? 180 : 0); + } else { + tmpV1.copy(this._rotation).mulScalar(this._flipped ? -1 : 1); + } + + this._line.enabled = !this._flipped; + this.entity.setLocalEulerAngles(tmpV1); + } + + get flipped() { + return this._flipped; + } + _createBoxLine() { this._createRoot('boxLine'); diff --git a/src/extras/gizmo/translate-gizmo.js b/src/extras/gizmo/translate-gizmo.js index b7909789273..e11d84c1038 100644 --- a/src/extras/gizmo/translate-gizmo.js +++ b/src/extras/gizmo/translate-gizmo.js @@ -26,6 +26,7 @@ const tmpQ1 = new Quat(); // constants const GLANCE_EPSILON = 0.98; +const CAMERA_EPSILON = 0.999; /** * Translation gizmo. @@ -107,6 +108,14 @@ class TranslateGizmo extends TransformGizmo { */ _nodePositions = new Map(); + /** + * Internal forward vector of the camera in the direction of the gizmo. + * + * @type {Vec3} + * @private + */ + _forward = new Vec3(); + /** * @override */ @@ -117,7 +126,7 @@ class TranslateGizmo extends TransformGizmo { * * @type {boolean} */ - flipPlanes = true; + flipShapes = true; /** * Creates a new TranslateGizmo object. @@ -151,7 +160,7 @@ class TranslateGizmo extends TransformGizmo { }); this._app.on('prerender', () => { - this._planesLookAtCamera(); + this._shapesLookAtCamera(); }); } @@ -360,22 +369,46 @@ class TranslateGizmo extends TransformGizmo { /** * @private */ - _planesLookAtCamera() { - tmpV1.cross(this._camera.entity.forward, this.root.right); + _shapesLookAtCamera() { + tmpV1.copy(this.root.getPosition()).sub(this._camera.entity.getPosition()).normalize(); + if (tmpV1.dot(this._forward) > CAMERA_EPSILON) { + return; + } + this._forward.copy(tmpV1); + + let dot = this._forward.dot(this.root.right); + this._shapes.x.entity.enabled = Math.abs(dot) < GLANCE_EPSILON; + if (this.flipShapes) { + this._shapes.x.flipped = dot > 0; + } + + dot = this._forward.dot(this.root.up); + this._shapes.y.entity.enabled = Math.abs(dot) < GLANCE_EPSILON; + if (this.flipShapes) { + this._shapes.y.flipped = dot > 0; + } + + dot = this._forward.dot(this.root.forward); + this._shapes.z.entity.enabled = Math.abs(dot) < GLANCE_EPSILON; + if (this.flipShapes) { + this._shapes.z.flipped = dot < 0; + } + + tmpV1.cross(this._forward, this.root.right); this._shapes.yz.entity.enabled = tmpV1.length() < GLANCE_EPSILON; - if (this.flipPlanes) { + if (this.flipShapes) { this._shapes.yz.flipped = tmpV2.set(0, +(tmpV1.dot(this.root.forward) > 0), +(tmpV1.dot(this.root.up) > 0)); } - tmpV1.cross(this._camera.entity.forward, this.root.forward); + tmpV1.cross(this._forward, this.root.forward); this._shapes.xy.entity.enabled = tmpV1.length() < GLANCE_EPSILON; - if (this.flipPlanes) { + if (this.flipShapes) { this._shapes.xy.flipped = tmpV2.set(+(tmpV1.dot(this.root.up) > 0), +(tmpV1.dot(this.root.right) < 0), 0); } - tmpV1.cross(this._camera.entity.forward, this.root.up); + tmpV1.cross(this._forward, this.root.up); this._shapes.xz.entity.enabled = tmpV1.length() < GLANCE_EPSILON; - if (this.flipPlanes) { + if (this.flipShapes) { this._shapes.xz.flipped = tmpV2.set(+(tmpV1.dot(this.root.forward) < 0), 0, +(tmpV1.dot(this.root.right) < 0)); } }