Compare commits

...
Author SHA1 Message Date
Tanish Manku d60680161e world/local transform space toggle for 3D editor gizmo
Defaults translate and rotate to world space. X key and toolbar pill
toggle between world and local. Scale mode is locked to local space.
2026-03-25 09:02:47 -07:00
4 changed files with 50 additions and 3 deletions
@@ -42,6 +42,7 @@ import {
} from "~/signals";
import { outlinerState, updateObjectPanel } from "../signals";
import { transformSpace } from "../signals/selectedMode";
import { IGenerationOptions } from "../models/generationOptions";
import { toEngineActions } from "../Queue/toEngineActions";
import { SceneGenereationMetaData } from "../models/sceneGenerationMetadata";
@@ -592,8 +593,7 @@ class Editor {
this.cameraViewControls.enabled = true;
this.control = new TransformControls(this.camera, this.renderer.domElement);
this.control.space = "local"; // Local transformation mode
// .space = 'world'; // Global mode
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);
@@ -1538,9 +1538,18 @@ class Editor {
return;
}
this.control.mode = type;
this.control.space = type === "scale" ? "local" : transformSpace.value;
this.transform_interaction = true;
}
toggleTransformSpace() {
if (this.control == undefined || this.control.mode === "scale") {
return;
}
transformSpace.value = transformSpace.value === "world" ? "local" : "world";
this.control.space = transformSpace.value;
}
async stopPlaybackAndUploadVideo() {
await this.videoGeneration.stopPlaybackAndUploadVideo();
}
@@ -20,6 +20,7 @@ import {
selectedMode,
poseMode,
showPoseControls,
transformSpace,
} from "../signals/selectedMode";
import { Euler } from "three";
@@ -341,16 +342,25 @@ export class MouseControls {
} else if (event.key === "t") {
// transform
this.control?.setMode("translate");
if (this.control) this.control.space = transformSpace.value;
selectedMode.value = "move";
return;
} else if (event.key === "x") {
// toggle world/local space (blocked in scale mode)
if (this.control?.mode === "scale") return;
transformSpace.value = transformSpace.value === "world" ? "local" : "world";
if (this.control) this.control.space = transformSpace.value;
return;
} else if (event.key === "r" && !event.ctrlKey) {
// rotate
this.control?.setMode("rotate");
if (this.control) this.control.space = transformSpace.value;
selectedMode.value = "rotate";
return;
} else if (event.key === "g") {
// scale
this.control?.setMode("scale");
if (this.control) this.control.space = transformSpace.value;
selectedMode.value = "scale";
return;
} else if (event.key === "k") {
@@ -22,7 +22,7 @@ import { useTabStore } from "~/pages/Stores/TabState";
// eslint-disable-next-line import/no-unresolved
// import { AssetType } from "~/enums";
import { AssetModal } from "../AssetMenu/AssetModal";
import { selectedMode } from "../../signals/selectedMode";
import { selectedMode, transformSpace } from "../../signals/selectedMode";
import { useSignals } from "@preact/signals-react/runtime";
import { outlinerState } from "../../signals/outliner/outliner";
// eslint-disable-next-line import/no-unresolved
@@ -320,6 +320,33 @@ export const Controls3D = () => {
onOptionChange={handleModeChange}
selectedOption={selectedMode.value}
/>
{selectedMode.value === "scale" ? (
<Tooltip
content="Scale is always in local space"
position="bottom"
delay={300}
>
<button
disabled
className="h-9 rounded-[10px] px-2.5 text-[10px] font-semibold font-mono bg-white/15 uppercase tracking-wide opacity-40 cursor-not-allowed"
>
Local
</button>
</Tooltip>
) : (
<Tooltip
content={`Transform space: ${transformSpace.value} (X to toggle)`}
position="bottom"
delay={300}
>
<button
className="h-9 rounded-[10px] px-2.5 text-[10px] font-semibold font-mono bg-white/15 hover:bg-white/25 transition-colors uppercase tracking-wide"
onClick={() => editorEngine?.toggleTransformSpace()}
>
{transformSpace.value === "world" ? "World" : "Local"}
</button>
</Tooltip>
)}
</div>
</div>
</div>
@@ -3,3 +3,4 @@ import { signal } from "@preact/signals-react";
export const selectedMode = signal("move");
export const showPoseControls = signal(false);
export const poseMode = signal("select");
export const transformSpace = signal<"world" | "local">("world");