Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/engine-orbit-fly-camera.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@jolly-pixel/engine": minor
---

Add `OrbitFlyCamera`, a WASD + mouse-look camera with an optional `"lock"` (fixed-pivot orbit) or `"elastic"` (trailing pivot) focus mode, consolidated from voxel-map and voxel-model's near-identical copies.
36 changes: 18 additions & 18 deletions packages/editors/voxel-map/src/app/EditorScene.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Import Third-party Dependencies
import {
Systems
Systems,
OrbitFlyCamera
} from "@jolly-pixel/engine";
import {
VoxelRenderer
Expand All @@ -21,7 +22,6 @@ import * as THREE from "three";

// Import Internal Dependencies
import {
FreeFlyCamera,
GridRenderer,
viewFocusPoint,
ViewFocus
Expand Down Expand Up @@ -72,20 +72,20 @@ export class EditorScene extends Systems.Scene {
#blockSelections: BlockSelectionPresence | undefined;
#layerSelections: LayerSelectionPresence | undefined;
#peerFrustums: PeerFrustums | undefined;
#freeFlyCamera: FreeFlyCamera | undefined;
#orbitFlyCamera: OrbitFlyCamera | undefined;
#viewFocus: ViewFocus;
#handles = Promise.withResolvers<EditorSceneHandles>();
#subscriptions: Array<() => void> = [];

#orbiting = false;

#onExitOrbitFocusKey = (): void => {
this.#freeFlyCamera?.exitOrbitFocus();
this.#orbitFlyCamera?.exitOrbitFocus();
this.#announceCameraMode();
};

#announceCameraMode(): void {
const orbiting = this.#freeFlyCamera?.isOrbiting ?? false;
const orbiting = this.#orbitFlyCamera?.isOrbiting ?? false;
if (orbiting === this.#orbiting) {
return;
}
Expand Down Expand Up @@ -147,16 +147,16 @@ export class EditorScene extends Systems.Scene {
installTransparency(world.renderer)
);

const freeFlyCamera = world
const orbitFlyCamera = world
.createActor("camera")
.addComponentAndGet(FreeFlyCamera, {
.addComponentAndGet(OrbitFlyCamera, {
position: { x: 8, y: 12, z: 32 },
focusMode: "lock"
});
this.#freeFlyCamera = freeFlyCamera;
this.#orbitFlyCamera = orbitFlyCamera;
this.#subscriptions.push(
this.editorState.selection.watch("gizmoDraggingChange", (dragging) => {
freeFlyCamera.enabled = !dragging;
orbitFlyCamera.enabled = !dragging;
})
);

Expand All @@ -182,7 +182,7 @@ export class EditorScene extends Systems.Scene {
this.engine = engine;

this.#viewFocus.provider = () => viewFocusPoint(
freeFlyCamera.camera,
orbitFlyCamera.camera,
engine.root
);

Expand Down Expand Up @@ -265,13 +265,13 @@ export class EditorScene extends Systems.Scene {
const brush = world.createActor("brush")
.addComponentAndGet(LocalBrush, {
engine,
camera: freeFlyCamera.camera,
camera: orbitFlyCamera.camera,
brush: this.editorState.brush,
selection: this.editorState.selection,
color: this.#identity?.color
});
brush.onFocusRequest = (point) => {
freeFlyCamera.enterOrbitFocus(point);
orbitFlyCamera.enterOrbitFocus(point);
this.#announceCameraMode();
};

Expand All @@ -297,22 +297,22 @@ export class EditorScene extends Systems.Scene {
this.#peerFrustums = world.createActor("peer-frustums")
.addComponentAndGet(PeerFrustums, {
room: this.#voxelRoom,
camera: freeFlyCamera.camera
camera: orbitFlyCamera.camera
});
}

world.createActor("gizmo")
.addComponent(VoxelLayerGizmo, {
world: voxelWorld,
camera: freeFlyCamera.camera,
camera: orbitFlyCamera.camera,
selection: this.editorState.selection,
worldStore: this.editorState.world
});

world.createActor("object-layer-renderer")
.addComponent(ObjectLayerRenderer, {
world: voxelWorld,
camera: freeFlyCamera.camera,
camera: orbitFlyCamera.camera,
selection: this.editorState.selection,
worldStore: this.editorState.world
});
Expand All @@ -331,11 +331,11 @@ export class EditorScene extends Systems.Scene {
clientId: string
): boolean {
const pose = this.#peerFrustums?.poseOf(clientId);
if (pose === undefined || this.#freeFlyCamera === undefined) {
if (pose === undefined || this.#orbitFlyCamera === undefined) {
return false;
}

this.#freeFlyCamera.teleport(pose);
this.#orbitFlyCamera.teleport(pose);

return true;
}
Expand Down Expand Up @@ -377,7 +377,7 @@ export class EditorScene extends Systems.Scene {
this.#layerSelections?.dispose();
this.#layerSelections = undefined;
this.#peerFrustums = undefined;
this.#freeFlyCamera = undefined;
this.#orbitFlyCamera = undefined;
}

#registerDefaultBlocks(): void {
Expand Down
121 changes: 0 additions & 121 deletions packages/editors/voxel-map/src/scene/camera/ElasticFocus.ts

This file was deleted.

29 changes: 0 additions & 29 deletions packages/editors/voxel-map/src/scene/camera/pivotMarker.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/editors/voxel-map/src/scene/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export { FreeFlyCamera } from "./camera/FreeFlyCamera.ts";
export { GridRenderer } from "./GridRenderer.ts";
export {
castViewRay,
Expand Down
12 changes: 9 additions & 3 deletions packages/editors/voxel-model/src/app/ModelEditorScene.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// Import Third-party Dependencies
import * as THREE from "three";
import { Systems } from "@jolly-pixel/engine";
import { Systems, OrbitFlyCamera } from "@jolly-pixel/engine";
import type * as network from "@jolly-pixel/network";

// Import Internal Dependencies
import { FreeFlyCamera } from "../scene/camera/FreeFlyCamera.ts";
import { ModelSceneComponent } from "./ModelSceneComponent.ts";
import type { EditorIdentity } from "../collaboration/identity.ts";
import type {
Expand Down Expand Up @@ -45,9 +44,16 @@ export class ModelEditorScene extends Systems.Scene {

const camera = this.world
.createActor("camera")
.addComponentAndGet(FreeFlyCamera, {
.addComponentAndGet(OrbitFlyCamera, {
focusMode: "elastic",
position: { x: 0, y: 1, z: 5 },
pivotPosition: { x: 0, y: 2, z: 0 },
pitch: -0.3,
moveSpeed: 6,
minMoveSpeed: 1,
maxMoveSpeed: 60,
scrollSpeed: 1,
maxPivotDistance: 20,
initialTrailDistance: 12
});

Expand Down
11 changes: 7 additions & 4 deletions packages/editors/voxel-model/src/app/ModelSceneComponent.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
// Import Third-party Dependencies
import * as THREE from "three";
import { ActorComponent, type Actor } from "@jolly-pixel/engine";
import {
ActorComponent,
type Actor,
type OrbitFlyCamera
} from "@jolly-pixel/engine";
import type * as network from "@jolly-pixel/network";
import type { PixelArtCanvas } from "@jolly-pixel/pixel-draw.renderer";

// Import Internal Dependencies
import ModelManager from "../features/groups/ModelManager.ts";
import type GroupManager from "../features/groups/GroupManager.ts";
import type { ModelHookEvent } from "../features/groups/hooks.ts";
import type { FreeFlyCamera } from "../scene/camera/FreeFlyCamera.ts";
import GizmoManager, { type GizmoConfig } from "../features/transform/GizmoManager.ts";
import { GroupSelectionPresence } from "../collaboration/GroupSelectionPresence.ts";
import { GroupTransformGhostSync } from "../collaboration/GroupTransformGhostSync.ts";
Expand All @@ -24,7 +27,7 @@ import type {
export type { GizmoMode, GizmoTarget, GizmoSpace, GizmoConfig } from "../features/transform/GizmoManager.ts";

export interface ModelSceneComponentOptions {
camera: FreeFlyCamera;
camera: OrbitFlyCamera;
room?: network.Room<ModelNetworkCommand, ModelServerMessage>;
/** Absent offline, no collaboration is wired up. */
identity?: EditorIdentity;
Expand All @@ -36,7 +39,7 @@ export interface ModelSceneComponentOptions {
* `ModelManager`/`GroupManager` themselves are untouched by this move.
*/
export class ModelSceneComponent extends ActorComponent {
#camera: FreeFlyCamera;
#camera: OrbitFlyCamera;
#cameraRaycaster = new THREE.Raycaster();
#gizmo!: GizmoManager;
#modelManager!: ModelManager;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Import Third-party Dependencies
import * as THREE from "three";
import { TransformControls } from "three/examples/jsm/controls/TransformControls.js";
import type { OrbitFlyCamera } from "@jolly-pixel/engine";

// Import Internal Dependencies
import type GroupManager from "../groups/GroupManager.ts";
import type { GroupTransformSnapshot } from "../groups/hooks.ts";
import { snapshotTransform } from "../groups/transformCodec.ts";
import type { FreeFlyCamera } from "../../scene/camera/FreeFlyCamera.ts";

export type GizmoMode = "translate" | "rotate" | "scale";
export type GizmoTarget = "group" | "pivot" | "mesh";
Expand All @@ -19,7 +19,7 @@ export interface GizmoConfig {
}

export interface GizmoManagerOptions {
camera: FreeFlyCamera;
camera: OrbitFlyCamera;
canvas: HTMLCanvasElement;
getSelectedGroup(): GroupManager | null;
commitTransform(uuid: string): void;
Expand All @@ -31,7 +31,7 @@ export interface GizmoManagerOptions {
}

export default class GizmoManager {
#camera: FreeFlyCamera;
#camera: OrbitFlyCamera;
#getSelectedGroup: () => GroupManager | null;
#commitTransform: (uuid: string) => void;
#onDragProgress: ((uuid: string, transform: GroupTransformSnapshot) => void) | undefined;
Expand Down
Loading
Loading