@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
210 lines • 8.26 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { MathUtils } from "three";
import { TransformControls } from "three/examples/jsm/controls/TransformControls.js";
import * as params from "../engine/engine_default_parameters.js";
import { serializable } from "../engine/engine_serialization_decorator.js";
import { Behaviour, GameObject } from "./Component.js";
import { OrbitControls } from "./OrbitControls.js";
import { SyncedTransform } from "./SyncedTransform.js";
/**
* TransformGizmo displays manipulation controls for translating, rotating, and scaling objects in the scene.
* It wraps three.js {@link TransformControls} and provides keyboard shortcuts for changing modes and settings.
* @category Helpers
* @group Components
*/
export class TransformGizmo extends Behaviour {
/**
* When true, this is considered a helper gizmo and will only be shown if showGizmos is enabled in engine parameters.
*/
isGizmo = false;
/**
* Specifies the translation grid snap value in world units.
* Applied when holding Shift while translating an object.
*/
translationSnap = 1;
/**
* Specifies the rotation snap angle in degrees.
* Applied when holding Shift while rotating an object.
*/
rotationSnapAngle = 15;
/**
* Specifies the scale snapping value.
* Applied when holding Shift while scaling an object.
*/
scaleSnap = .25;
/**
* Gets the underlying three.js {@link TransformControls} instance.
* @returns The TransformControls instance or undefined if not initialized.
*/
get control() {
return this._control;
}
_control;
orbit;
/** @internal */
onEnable() {
if (this.isGizmo && !params.showGizmos)
return;
if (!this.context.mainCamera)
return;
if (!this._control) {
this._control = new TransformControls(this.context.mainCamera, this.context.renderer.domElement);
this._control.enabled = true;
this._control.getRaycaster().layers.set(2);
this._control.size = 1;
const obj = ("_root" in this._control ? this._control._root : this._control);
obj.traverse(x => {
const mesh = x;
mesh.layers.set(2);
if (mesh) {
const gizmoMat = mesh.material;
if (gizmoMat) {
gizmoMat.opacity = 0.3;
}
}
});
this.orbit = GameObject.getComponentInParent(this.context.mainCamera, OrbitControls) ?? undefined;
}
if (this._control) {
const obj = this._control.getHelper();
this.context.scene.add(obj);
this._control.attach(this.gameObject);
this._control?.addEventListener('dragging-changed', this.onControlChangedEvent);
window.addEventListener('keydown', this.windowKeyDownListener);
window.addEventListener('keyup', this.windowKeyUpListener);
}
}
/** @internal */
onDisable() {
this._control?.getHelper()?.removeFromParent();
this._control?.removeEventListener('dragging-changed', this.onControlChangedEvent);
window.removeEventListener('keydown', this.windowKeyDownListener);
window.removeEventListener('keyup', this.windowKeyUpListener);
}
/**
* Enables grid snapping for transform operations according to set snap values.
* This applies the translationSnap, rotationSnapAngle, and scaleSnap properties to the controls.
*/
enableSnapping() {
if (this._control) {
this._control.setTranslationSnap(this.translationSnap);
this._control.setRotationSnap(MathUtils.degToRad(this.rotationSnapAngle));
this._control.setScaleSnap(this.scaleSnap);
}
}
/**
* Disables grid snapping for transform operations.
* Removes all snapping constraints from the transform controls.
*/
disableSnapping() {
if (this._control) {
this._control.setTranslationSnap(null);
this._control.setRotationSnap(null);
this._control.setScaleSnap(null);
}
}
/**
* Event handler for when dragging state changes.
* Disables orbit controls during dragging and requests ownership of the transform if it's synchronized.
* @param event The drag change event
*/
onControlChangedEvent = (event) => {
const orbit = this.orbit;
if (orbit)
orbit.enabled = !event.value;
if (event.value) {
// request ownership on drag start
const sync = GameObject.getComponentInParent(this.gameObject, SyncedTransform);
if (sync) {
sync.requestOwnership();
}
}
};
/**
* Handles keyboard shortcuts for transform operations:
* - Q: Toggle local/world space
* - W: Translation mode
* - E: Rotation mode
* - R: Scale mode
* - Shift: Enable snapping (while held)
* - +/-: Adjust gizmo size
* - X/Y/Z: Toggle visibility of respective axis
* - Spacebar: Toggle controls enabled state
* @param event The keyboard event
*/
windowKeyDownListener = (event) => {
if (!this.enabled)
return;
if (!this._control)
return;
switch (event.keyCode) {
case 81: // Q
this._control.setSpace(this._control.space === 'local' ? 'world' : 'local');
break;
case 16: // Shift
this.enableSnapping();
break;
case 87: // W
this._control.setMode('translate');
break;
case 69: // E
this._control.setMode('rotate');
break;
case 82: // R
this._control.setMode('scale');
break;
case 187:
case 107: // +, =, num+
this._control.setSize(this._control.size + 0.1);
break;
case 189:
case 109: // -, _, num-
this._control.setSize(Math.max(this._control.size - 0.1, 0.1));
break;
case 88: // X
this._control.showX = !this._control.showX;
break;
case 89: // Y
this._control.showY = !this._control.showY;
break;
case 90: // Z
this._control.showZ = !this._control.showZ;
break;
case 32: // Spacebar
this._control.enabled = !this._control.enabled;
break;
}
};
/**
* Handles keyboard key release events.
* Currently only handles releasing Shift key to disable snapping.
* @param event The keyboard event
*/
windowKeyUpListener = (event) => {
if (!this.enabled)
return;
switch (event.keyCode) {
case 16: // Shift
this.disableSnapping();
break;
}
};
}
__decorate([
serializable()
], TransformGizmo.prototype, "isGizmo", void 0);
__decorate([
serializable()
], TransformGizmo.prototype, "translationSnap", void 0);
__decorate([
serializable()
], TransformGizmo.prototype, "rotationSnapAngle", void 0);
__decorate([
serializable()
], TransformGizmo.prototype, "scaleSnap", void 0);
//# sourceMappingURL=TransformGizmo.js.map