mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-07 10:31:46 +00:00
PageScene: extract GizmoController from editor.ts
Third step of the editor.ts decomposition. Pulls TransformControls
ownership and the gizmo lifecycle out of Editor into a focused module.
GizmoController owns:
control (TransformControls).
Methods take their dependencies as method arguments — no `editor`
reference, no `import type Editor`, no circular import:
configure(camera, domElement, scene, callbacks)
changeMode(type, space)
setSpace(space)
canToggleSpace()
attach(object), detach()
addToScene(scene), removeFromScene(scene)
setVisible(v), isVisible()
Editor exposes the gizmo as `editor.gizmo` and constructs it empty in
the constructor; configure() is called from initialize() once camera
and renderer exist. The two existing public methods stay on Editor as
facades, so the external callers (actions/setTransformMode.ts,
comps/Controls3D.tsx, engine/keymap.ts) keep working unchanged:
Editor.change_mode reads the store's transformSpace and forwards.
Editor.toggleTransformSpace toggles the store and pushes into gizmo.
External readers updated:
helper.ts (SceneUtils.lockUnlockObject + removeTransformControls)
routes through editor.gizmo.attach/detach/addToScene/removeFromScene
instead of poking editor.control directly.
Editor.snapShotOfCurrentFrame uses editor.gizmo.setVisible /
isVisible instead of touching control.visible.
Editor passes editor.gizmo.control to the MouseControls constructor
where it previously passed editor.control.
Dead code removed:
- transform_interaction on Editor: passed by value to MouseControls
(which has its own internal copy that overrides) and the `= true`
write in change_mode mutated a field nothing read. Pure dead code.
- The "if (this.control == undefined) return;" guard immediately
after `new TransformControls(...)` — that branch was unreachable.
- Unused TransformControls import in editor.ts.
Net: editor.ts down to 1196 LOC. Typecheck error count drops by 3.
This commit is contained in:
1 parent
4c521ab55c
commit
abf2e1d6c2
3 files changed
+129
-54
No files matched your search
@@ -5,7 +5,6 @@ import {
|
||||
lookAtFromCamera,
|
||||
type FreeCamControlState,
|
||||
} from "./cameraMath";
|
||||
import { TransformControls } from "./TransformControls.js";
|
||||
import Scene from "./scene.js";
|
||||
import { APIManager } from "./api_manager.js";
|
||||
import { PointerLockControls } from "three/addons/controls/PointerLockControls.js";
|
||||
@@ -27,6 +26,7 @@ import { MediaUploadApi } from "~/Classes/ApiManager";
|
||||
import { SceneManager } from "./scene_manager_api";
|
||||
import { ViewportController } from "./editor/ViewportController";
|
||||
import { PostProcessingPipeline } from "./editor/PostProcessingPipeline";
|
||||
import { GizmoController } from "./editor/GizmoController";
|
||||
|
||||
import Stats from "three/examples/jsm/libs/stats.module.js";
|
||||
import { SparkRenderer } from "@sparkjsdev/spark";
|
||||
@@ -63,13 +63,11 @@ class Editor {
|
||||
|
||||
last_cam_pos: THREE.Vector3;
|
||||
last_cam_rot: THREE.Euler;
|
||||
control: TransformControls | undefined;
|
||||
raycaster: THREE.Raycaster | undefined;
|
||||
mouse: THREE.Vector2 | undefined;
|
||||
selected: THREE.Object3D | undefined;
|
||||
last_selected: THREE.Object3D | undefined;
|
||||
last_selected_sum: number | undefined;
|
||||
transform_interaction = false;
|
||||
rendering: boolean;
|
||||
api_manager: APIManager;
|
||||
freeCamState: FreeCamControlState | null = null;
|
||||
@@ -111,6 +109,8 @@ class Editor {
|
||||
viewport: ViewportController;
|
||||
// Owns the EffectComposer chains and post-process passes.
|
||||
postProcessing: PostProcessingPipeline;
|
||||
// Owns the TransformControls gizmo.
|
||||
gizmo: GizmoController;
|
||||
|
||||
selectedCanvas: boolean;
|
||||
startRenderHeight: number;
|
||||
@@ -188,6 +188,7 @@ class Editor {
|
||||
// PostProcessingPipeline must exist before Scene because Scene's
|
||||
// load paths invoke updateSurfaceIdAttributeToMesh as a callback.
|
||||
this.postProcessing = new PostProcessingPipeline();
|
||||
this.gizmo = new GizmoController();
|
||||
|
||||
this.activeScene = new Scene(
|
||||
"" + this.version,
|
||||
@@ -432,33 +433,26 @@ class Editor {
|
||||
// FreeCam math + listeners now live in hooks/useFreeCam.ts; the
|
||||
// editor reads `freeCamState` (set by that hook) on every render.
|
||||
|
||||
this.control = new TransformControls(this.camera, this.renderer.domElement);
|
||||
this.control.space = "world"; // Default to world space for translate mode
|
||||
this.control.setScaleSnap(0.01);
|
||||
this.control.setTranslationSnap(0.01);
|
||||
this.control.setRotationSnap(0.01);
|
||||
//console.log("Control Sensitivity:", this.control.sensitivity);
|
||||
this.gizmo.configure(
|
||||
this.camera,
|
||||
this.renderer.domElement,
|
||||
this.activeScene.scene,
|
||||
{
|
||||
onChange: () => this.renderScene(),
|
||||
onDraggingChanged: (dragging) => {
|
||||
this.updateSelectedUI();
|
||||
this.camera_last_pos.copy(new THREE.Vector3(-99999, -99999, -99999));
|
||||
this.focused = !dragging;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// Base control and debug stuff remove debug in prod.
|
||||
if (this.control == undefined) {
|
||||
return;
|
||||
}
|
||||
this.control.addEventListener("change", this.renderScene.bind(this));
|
||||
this.control.addEventListener("dragging-changed", (event: any) => {
|
||||
//TODO: any should be the following
|
||||
this.updateSelectedUI();
|
||||
this.camera_last_pos.copy(new THREE.Vector3(-99999, -99999, -99999));
|
||||
this.focused = !event.value;
|
||||
// this.update_properties()
|
||||
});
|
||||
this.control.setSize(0.5); // Good default value for visuals.
|
||||
this.raycaster = new THREE.Raycaster();
|
||||
// Configure raycaster to check both layers
|
||||
this.raycaster.layers.set(0); // Enable default layer
|
||||
this.raycaster.layers.enable(1); // Also check objects on the custom layer
|
||||
|
||||
this.mouse = new THREE.Vector2();
|
||||
this.activeScene.scene.add(this.control);
|
||||
// Resets canvas size.
|
||||
this.viewport.onWindowResize();
|
||||
|
||||
@@ -485,12 +479,12 @@ class Editor {
|
||||
this.mouse,
|
||||
this.mouse,
|
||||
this.raycaster,
|
||||
this.control,
|
||||
this.gizmo.control,
|
||||
this.postProcessing.outlinePass,
|
||||
this.activeScene.scene,
|
||||
this.publishSelect.bind(this),
|
||||
this.updateSelectedUI.bind(this),
|
||||
this.transform_interaction,
|
||||
false,
|
||||
this.last_selected,
|
||||
this.getAssetType.bind(this),
|
||||
this.setSelected.bind(this),
|
||||
@@ -587,10 +581,8 @@ class Editor {
|
||||
store.setGridVisible(false);
|
||||
|
||||
// Store and hide transform controls
|
||||
const wasControlVisible = this.control?.visible ?? false;
|
||||
if (this.control) {
|
||||
this.control.visible = false;
|
||||
}
|
||||
const wasControlVisible = this.gizmo.isVisible();
|
||||
this.gizmo.setVisible(false);
|
||||
|
||||
// Store and disable outline pass
|
||||
const outlinePass = this.postProcessing.outlinePass;
|
||||
@@ -696,9 +688,7 @@ class Editor {
|
||||
usePageSceneStore.getState().setGridVisible(wasGridVisible);
|
||||
|
||||
// Restore transform controls visibility
|
||||
if (this.control) {
|
||||
this.control.visible = wasControlVisible;
|
||||
}
|
||||
this.gizmo.setVisible(wasControlVisible);
|
||||
|
||||
// Restore outline pass
|
||||
if (outlinePass) {
|
||||
@@ -1051,26 +1041,22 @@ class Editor {
|
||||
console.log("3D Editor Engine unmounted");
|
||||
}
|
||||
|
||||
// Facade for actions/setTransformMode + keymap dispatch. Reads the
|
||||
// store's current transform space and forwards to the gizmo.
|
||||
change_mode(type: "translate" | "rotate" | "scale") {
|
||||
if (this.control == undefined) {
|
||||
return;
|
||||
}
|
||||
this.control.mode = type;
|
||||
this.control.space =
|
||||
type === "scale"
|
||||
? "local"
|
||||
: usePageSceneStore.getState().transformSpace;
|
||||
this.transform_interaction = true;
|
||||
const space =
|
||||
type === "scale" ? "local" : usePageSceneStore.getState().transformSpace;
|
||||
this.gizmo.changeMode(type, space);
|
||||
}
|
||||
|
||||
// Facade for Controls3D button + keymap. Toggles the store's
|
||||
// transform space and pushes the new value into the gizmo.
|
||||
toggleTransformSpace() {
|
||||
if (this.control == undefined || this.control.mode === "scale") {
|
||||
return;
|
||||
}
|
||||
if (!this.gizmo.canToggleSpace()) return;
|
||||
const store = usePageSceneStore.getState();
|
||||
const next = store.transformSpace === "world" ? "local" : "world";
|
||||
store.setTransformSpace(next);
|
||||
this.control.space = next;
|
||||
this.gizmo.setSpace(next);
|
||||
}
|
||||
|
||||
togglePlayback() {
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// Owns the TransformControls gizmo used for translating/rotating/scaling
|
||||
// the selected object. Construction is two-phase to match the existing
|
||||
// editor lifecycle: the controller is created up front (held as a field
|
||||
// on Editor), then `configure()` wires it to the camera/renderer once
|
||||
// initialize() has produced them.
|
||||
//
|
||||
// No `editor` reference, no `import type Editor`. Store access stays on
|
||||
// Editor's facades (change_mode, toggleTransformSpace); this module only
|
||||
// translates them into TransformControls calls.
|
||||
|
||||
import type * as THREE from "three";
|
||||
import { TransformControls } from "../TransformControls.js";
|
||||
|
||||
export type TransformMode = "translate" | "rotate" | "scale";
|
||||
export type TransformSpace = "world" | "local";
|
||||
|
||||
export type GizmoCallbacks = {
|
||||
// Fires on every internal TransformControls "change" event — Editor
|
||||
// uses this to re-render the scene during a drag.
|
||||
onChange: () => void;
|
||||
// Fires when a drag starts or ends. `dragging` is true on drag start.
|
||||
onDraggingChanged: (dragging: boolean) => void;
|
||||
};
|
||||
|
||||
export class GizmoController {
|
||||
control: TransformControls | undefined;
|
||||
|
||||
configure(
|
||||
camera: THREE.PerspectiveCamera | null,
|
||||
domElement: HTMLElement,
|
||||
scene: THREE.Scene,
|
||||
callbacks: GizmoCallbacks,
|
||||
) {
|
||||
if (camera == undefined) return;
|
||||
this.control = new TransformControls(camera, domElement);
|
||||
this.control.space = "world";
|
||||
this.control.setScaleSnap(0.01);
|
||||
this.control.setTranslationSnap(0.01);
|
||||
this.control.setRotationSnap(0.01);
|
||||
this.control.setSize(0.5);
|
||||
this.control.addEventListener("change", callbacks.onChange);
|
||||
this.control.addEventListener("dragging-changed", (event: any) => {
|
||||
callbacks.onDraggingChanged(event.value);
|
||||
});
|
||||
scene.add(this.control);
|
||||
}
|
||||
|
||||
changeMode(type: TransformMode, space: TransformSpace) {
|
||||
if (this.control == undefined) return;
|
||||
this.control.mode = type;
|
||||
this.control.space = space;
|
||||
}
|
||||
|
||||
setSpace(space: TransformSpace) {
|
||||
if (this.control == undefined) return;
|
||||
this.control.space = space;
|
||||
}
|
||||
|
||||
// True when the gizmo exists and is in a mode where toggling between
|
||||
// world/local space is meaningful (scale is always local).
|
||||
canToggleSpace(): boolean {
|
||||
return this.control != undefined && this.control.mode !== "scale";
|
||||
}
|
||||
|
||||
attach(object: THREE.Object3D) {
|
||||
this.control?.attach(object);
|
||||
}
|
||||
|
||||
detach() {
|
||||
this.control?.detach();
|
||||
}
|
||||
|
||||
// Add/remove the gizmo from the scene. Used by SceneUtils when
|
||||
// locking/unlocking objects: the gizmo is yanked out of the scene on
|
||||
// lock and re-added on unlock.
|
||||
addToScene(scene: THREE.Scene) {
|
||||
if (this.control) scene.add(this.control);
|
||||
}
|
||||
|
||||
removeFromScene(scene: THREE.Scene) {
|
||||
if (this.control) scene.remove(this.control);
|
||||
}
|
||||
|
||||
setVisible(visible: boolean) {
|
||||
if (this.control) this.control.visible = visible;
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
return this.control?.visible ?? false;
|
||||
}
|
||||
}
|
||||
@@ -64,12 +64,10 @@ export class SceneUtils {
|
||||
|
||||
if (object.userData["locked"]) {
|
||||
this.removeTransformControls(false);
|
||||
} else if (this.editor.control) {
|
||||
this.scene.scene.add(this.editor.control);
|
||||
if (this.editor.sceneManager?.selected_objects)
|
||||
this.editor.control.attach(
|
||||
this.editor.sceneManager?.selected_objects[0],
|
||||
);
|
||||
} else {
|
||||
this.editor.gizmo.addToScene(this.scene.scene);
|
||||
const selected = this.editor.sceneManager?.selected_objects?.[0];
|
||||
if (selected) this.editor.gizmo.attach(selected);
|
||||
}
|
||||
|
||||
return object.userData["locked"];
|
||||
@@ -80,7 +78,7 @@ export class SceneUtils {
|
||||
|
||||
// Removes transform controls and publishes selected.
|
||||
removeTransformControls(remove_outline: boolean = true) {
|
||||
if (this.editor.control == undefined) {
|
||||
if (this.editor.gizmo.control == undefined) {
|
||||
return;
|
||||
}
|
||||
const outlinePass = this.editor.postProcessing.outlinePass;
|
||||
@@ -92,8 +90,8 @@ export class SceneUtils {
|
||||
outlinePass.selectedObjects = [];
|
||||
this.editor.publishSelect();
|
||||
}
|
||||
this.editor.control.detach();
|
||||
this.editor.activeScene.scene.remove(this.editor.control);
|
||||
this.editor.gizmo.detach();
|
||||
this.editor.gizmo.removeFromScene(this.editor.activeScene.scene);
|
||||
if (remove_outline) outlinePass.selectedObjects = [];
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user