
Thatopen Impl Navigation
- 5 installs
- 17 repo stars
- Updated July 8, 2026
- openaec-foundation/thatopen-claude-skill-package
Helps with ai & agent building tasks.
About
thatopen-impl-navigation is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- thatopen-impl-navigation
- AI & Agent Building
- AI-coding skill
Thatopen Impl Navigation by the numbers
- 5 all-time installs (skills.sh)
- Ranked #13,065 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 21, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/thatopen-claude-skill-package --skill thatopen-impl-navigationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 5 |
|---|---|
| repo stars | ★ 17 |
| Last updated | July 8, 2026 |
| Repository | openaec-foundation/thatopen-claude-skill-package ↗ |
What it does
Helps with ai & agent building tasks.
Files
ThatOpen Navigation: OrthoPerspectiveCamera
Overview
OrthoPerspectiveCamera extends SimpleCamera and provides three built-in navigation modes (Orbit, FirstPerson, Plan) with switchable perspective and orthographic projections. It wraps the camera-controls library and manages dual Three.js cameras (PerspectiveCamera + OrthographicCamera).
Package: @thatopen/components (core — works in browser and Node.js) Dependency: camera-controls >= 3.1.2
Prerequisites
- World must be created with
OrthoPerspectiveCameraas the camera type.
See thatopen-impl-viewer for full viewer setup.
components.init()MUST be called after camera assignment for the render
loop to start.
import * as OBC from "@thatopen/components";
const components = new OBC.Components();
const worlds = components.get(OBC.Worlds);
const world = worlds.create<
OBC.SimpleScene,
OBC.OrthoPerspectiveCamera,
OBC.SimpleRenderer
>();
world.scene = new OBC.SimpleScene(components);
world.scene.setup();
world.renderer = new OBC.SimpleRenderer(components, container);
world.camera = new OBC.OrthoPerspectiveCamera(components);
components.init();Navigation Modes
Three built-in modes control how the user interacts with the 3D viewport. Switch modes with camera.set(mode).
Mode Comparison Table
| Property | Orbit | FirstPerson | Plan |
|---|---|---|---|
| ID | "Orbit" | "FirstPerson" | "Plan" |
| Default | Yes (active at start) | No | No |
| Rotation | Full orbit around target | Look around from position | Disabled (locked) |
| Pan | Truck (right-drag) | Truck (high speed) | Truck (left-drag) |
| Zoom | Dolly (scroll) | Dolly (scroll) | Zoom (scroll) |
| Projection | Any | Perspective ONLY | Any (ortho preferred) |
| Use case | 3D model inspection | Walkthroughs | Floor plan views |
Orbit Mode (Default)
Standard 3D orbit navigation. Rotates around a target point.
Camera-controls settings applied:
minDistance: 1maxDistance: 300truckSpeed: 2- Orbit target calculated from camera direction and distance
world.camera.set("Orbit");FirstPerson Mode
FPS-style navigation. Camera stays at a fixed position; user looks around and moves through the scene.
Camera-controls settings applied:
minDistance: 1,maxDistance: 1,distance: 1truckSpeed: 50- Mouse wheel mapped to dolly action
- Two-finger touch mapped to zoom-truck
CRITICAL: FirstPerson mode ALWAYS requires perspective projection. It CANNOT run with orthographic projection. ALWAYS validate before switching:
const { current } = world.camera.projection;
if (current === "Orthographic") {
// Switch to perspective first, or skip FirstPerson
await world.camera.projection.set("Perspective");
}
world.camera.set("FirstPerson");If you call set("FirstPerson") while in orthographic mode, the camera falls back to Orbit mode instead.
Plan Mode
2D floor plan navigation. Rotation is disabled; only pan and zoom work. Ideal for architectural floor plans combined with clipping planes.
Camera-controls settings applied:
- Azimuth rotation speed: 0 (disabled)
- Polar rotation speed: 0 (disabled)
- Left mouse mapped to TRUCK (pan)
- Single-touch mapped to TOUCH_TRUCK
- Dual-touch mapped to TOUCH_ZOOM
When Plan mode is deactivated, original rotation speeds and mouse/touch mappings are restored.
// Switch to Plan mode for floor plan viewing
world.camera.set("Plan");
// Combine with orthographic projection for true floor plans
await world.camera.projection.set("Orthographic");Camera Controls Interaction Table
| Input | Orbit | FirstPerson | Plan |
|---|---|---|---|
| Left mouse drag | Rotate | Look around | Pan |
| Right mouse drag | Pan (truck) | Pan (truck) | Pan (truck) |
| Scroll wheel | Zoom (dolly) | Move (dolly) | Zoom |
| Single-finger touch | Rotate | Look around | Pan |
| Two-finger touch | Zoom + Pan | Zoom + Truck | Zoom |
Projection System
ProjectionManager handles switching between perspective and orthographic cameras. Access it via camera.projection.
Switching Projection
// Set to orthographic
await world.camera.projection.set("Orthographic");
// Set to perspective
await world.camera.projection.set("Perspective");
// Toggle between the two
await world.camera.projection.toggle();
// Read current projection
const current: OBC.CameraProjection = world.camera.projection.current;
// Returns "Perspective" or "Orthographic"Projection Changed Event
Listen for projection changes to update UI or other components:
world.camera.projection.onChanged.add(() => {
const projection = world.camera.projection.current;
console.log("Projection changed to:", projection);
// Common pattern: toggle grid fade based on projection
grid.fade = projection === "Perspective";
});onChanged fires after the projection switch completes. The event emits the new active THREE.Camera instance.
Projection Compatibility Matrix
| Projection | Orbit | FirstPerson | Plan |
|---|---|---|---|
| Perspective | Yes | Yes | Yes |
| Orthographic | Yes | NO | Yes |
NEVER combine FirstPerson mode with Orthographic projection. ALWAYS validate before allowing user selection of mode/projection combinations.
Dual Camera Access
OrthoPerspectiveCamera maintains two Three.js camera instances:
// Perspective camera (THREE.PerspectiveCamera)
const perspCam = world.camera.threePersp;
// Orthographic camera (THREE.OrthographicCamera)
const orthoCam = world.camera.threeOrtho;
// Active camera (whichever is current based on projection)
const activeCam = world.camera.three;threePerspandthreeOrthoALWAYS exist, regardless of current mode.camera.threereturns whichever camera is currently active.- The frustum size for orthographic projection is fixed at 50.
- Aspect ratio is automatically updated when the renderer resizes.
Fit / Frame Objects
fit() positions the camera to frame a set of meshes in the viewport.
// Fit specific meshes with default offset (1.5)
await world.camera.fit(meshes);
// Fit with custom offset (larger = more padding)
await world.camera.fit(meshes, 2.0);Parameters:
meshes:Iterable<THREE.Mesh>— the meshes to frameoffset:number(default1.5) — padding multiplier around the
bounding box. Values > 1 add space around the objects.
There is also a convenience method fitToItems() that fits to all loaded fragment models:
// Frame all loaded models
world.camera.fitToItems();User Input Control
Temporarily disable or enable all camera controls:
// Disable all user camera input
world.camera.setUserInput(false);
// Re-enable user camera input
world.camera.setUserInput(true);When disabled, setUserInput(false) stores the current mouse button mappings and then nullifies them. When re-enabled, mappings are restored.
Use this to lock the camera during animations, automated camera movements, or guided tours.
Custom Navigation Modes
Register a custom navigation mode that follows the NavigationMode interface:
interface NavigationMode {
id: string; // Unique mode identifier
enabled: boolean; // Whether this mode is currently active
set(active: boolean): void; // Called when mode is activated/deactivated
}Register and use:
const flyMode: OBC.NavigationMode = {
id: "Fly",
enabled: false,
set(active: boolean) {
this.enabled = active;
if (active) {
// Configure camera-controls for fly behavior
const controls = world.camera.controls;
controls.minDistance = 0;
controls.maxDistance = Infinity;
controls.truckSpeed = 10;
}
},
};
world.camera.addCustomNavigationMode(flyMode);
world.camera.set("Fly");Custom modes are stored in the internal _navigationModes map alongside the built-in Orbit, FirstPerson, and Plan modes.
Camera Controls (Low-Level)
Direct access to the underlying camera-controls instance:
const controls = world.camera.controls;
// Set camera position and look-at target
await controls.setLookAt(x, y, z, targetX, targetY, targetZ);
// Smooth transition
await controls.setLookAt(x, y, z, tx, ty, tz, true); // animated
// Listen for camera updates (e.g., to update fragment LOD)
controls.addEventListener("update", () => {
fragments.core.update();
});ALWAYS use controls.setLookAt() for programmatic camera positioning. NEVER set camera.three.position directly — camera-controls will overwrite it on the next frame.
Floor Plan Setup Pattern
Complete pattern for floor plan viewing with Plan mode:
// 1. Switch to Plan mode (disables rotation)
world.camera.set("Plan");
// 2. Switch to orthographic projection
await world.camera.projection.set("Orthographic");
// 3. Position camera looking straight down
await world.camera.controls.setLookAt(
centerX, height, centerZ, // camera position (above the floor)
centerX, 0, centerZ, // look-at target (floor level)
);
// 4. Disable grid fade for orthographic
grid.fade = false;
// 5. Add clipping plane at floor level (see thatopen-impl-clipping-plans)
// const clipper = components.get(OBC.Clipper);
// clipper.createFromNormalAndCoplanarPoint(world, normal, point);Critical Rules
1. NEVER combine FirstPerson mode with Orthographic projection. The camera silently falls back to Orbit mode, causing confusing behavior. 2. ALWAYS validate mode/projection compatibility before switching. 3. NEVER set camera.three.position directly. ALWAYS use camera.controls.setLookAt() or camera.controls.moveTo(). 4. ALWAYS call projection.set() with await — it returns a Promise. 5. NEVER forget to listen to projection.onChanged when your UI or components depend on the current projection type. 6. ALWAYS use setUserInput(false) during programmatic camera animations to prevent user interference. 7. ALWAYS call camera.dispose() on cleanup (handled automatically when the world is disposed via worlds.delete(world)). 8. NEVER instantiate OrthoPerspectiveCamera with new and forget to assign it to world.camera. The camera needs a world context.
Reference Files
- references/methods.md — Full API signatures for
OrthoPerspectiveCamera, ProjectionManager, NavigationMode interface
- references/examples.md — Mode switching,
fit patterns, custom mode, plan view setup
- references/anti-patterns.md — Common
mistakes with navigation and projection
Source Verification
All API signatures verified against:
- GitHub:
ThatOpen/engine_componentsmain branch
(packages/core/src/core/OrthoPerspectiveCamera/)
- Example:
OrthoPerspectiveCamera/example.ts - Research:
docs/research/vooronderzoek-thatopen.mdSection 2.5
Navigation Anti-Patterns
AP-1: FirstPerson with Orthographic Projection
WRONG — FirstPerson mode silently falls back to Orbit when orthographic is active:
// BAD: No validation before switching
await world.camera.projection.set("Orthographic");
world.camera.set("FirstPerson"); // Silently reverts to Orbit!CORRECT — ALWAYS validate projection compatibility:
// GOOD: Validate before switching
if (world.camera.projection.current !== "Orthographic") {
world.camera.set("FirstPerson");
} else {
console.warn("Switch to Perspective first.");
}---
AP-2: Setting Camera Position Directly on Three.js Object
WRONG — camera-controls overwrites direct position changes on the next frame:
// BAD: Position is overwritten by camera-controls
world.camera.three.position.set(10, 20, 30);
world.camera.three.lookAt(0, 0, 0);CORRECT — ALWAYS use camera-controls methods:
// GOOD: camera-controls manages position internally
await world.camera.controls.setLookAt(10, 20, 30, 0, 0, 0);---
AP-3: Forgetting to Await Projection Changes
WRONG — projection switching returns a Promise; not awaiting it causes race conditions:
// BAD: Projection may not have finished switching
world.camera.projection.set("Orthographic");
world.camera.set("Plan"); // May run before projection is readyCORRECT — ALWAYS await projection changes:
// GOOD: Wait for projection to complete
await world.camera.projection.set("Orthographic");
world.camera.set("Plan");---
AP-4: Not Disabling User Input During Animations
WRONG — User can interfere with programmatic camera movement:
// BAD: User input can conflict with animation
await world.camera.controls.setLookAt(x, y, z, tx, ty, tz, true);CORRECT — Lock controls during animation:
// GOOD: Prevent user interference
world.camera.setUserInput(false);
await world.camera.controls.setLookAt(x, y, z, tx, ty, tz, true);
world.camera.setUserInput(true);---
AP-5: Not Listening to Projection Changes
WRONG — Components that depend on projection type become out of sync:
// BAD: Grid fade never updates when projection changes
grid.fade = true; // Only set once at initCORRECT — ALWAYS listen for projection changes:
// GOOD: React to projection changes
world.camera.projection.onChanged.add(() => {
grid.fade = world.camera.projection.current === "Perspective";
});---
AP-6: Using Wrong Mode for Floor Plans
WRONG — Using Orbit mode for floor plans allows unwanted rotation:
// BAD: User can rotate out of the top-down view
world.camera.set("Orbit");
await world.camera.projection.set("Orthographic");
await world.camera.controls.setLookAt(0, 100, 0, 0, 0, 0);
// User rotates and loses the floor plan viewCORRECT — ALWAYS use Plan mode for floor plans:
// GOOD: Plan mode locks rotation
world.camera.set("Plan");
await world.camera.projection.set("Orthographic");
await world.camera.controls.setLookAt(0, 100, 0, 0, 0, 0);
// Rotation is disabled — view stays top-down---
AP-7: Not Updating Fragments on Camera Change
WRONG — Fragment LOD and culling become stale:
// BAD: No camera update listener
// Fragments render with wrong LOD as camera movesCORRECT — ALWAYS connect camera updates to fragment system:
// GOOD: Update fragments when camera moves
world.camera.controls.addEventListener("update", () => {
fragments.core.update();
});
world.onCameraChanged.add((camera) => {
for (const [, model] of fragments.list) {
model.useCamera(camera.three);
}
fragments.core.update(true);
});---
AP-8: Modifying camera-controls Settings Without Understanding Mode Resets
WRONG — Custom camera-controls settings are overwritten when switching modes:
// BAD: Custom settings lost on mode switch
world.camera.controls.truckSpeed = 100;
world.camera.set("Orbit"); // Resets truckSpeed to 2CORRECT — Use a custom navigation mode for persistent settings:
// GOOD: Custom mode preserves settings across switches
const customMode: OBC.NavigationMode = {
id: "FastOrbit",
enabled: false,
set(active: boolean) {
this.enabled = active;
if (active) {
const controls = world.camera.controls;
controls.minDistance = 1;
controls.maxDistance = 300;
controls.truckSpeed = 100; // Custom speed preserved
}
},
};
world.camera.addCustomNavigationMode(customMode);
world.camera.set("FastOrbit");Navigation Examples
Basic Mode Switching
// Default: Orbit mode is active after camera creation
// world.camera.mode.id === "Orbit"
// Switch to FirstPerson (validate projection first)
if (world.camera.projection.current === "Perspective") {
world.camera.set("FirstPerson");
}
// Switch to Plan mode
world.camera.set("Plan");
// Back to Orbit
world.camera.set("Orbit");Safe Mode Switching with Projection Validation
function setNavigationMode(
camera: OBC.OrthoPerspectiveCamera,
mode: OBC.NavModeID,
): void {
const isOrtho = camera.projection.current === "Orthographic";
const isFirstPerson = mode === "FirstPerson";
if (isOrtho && isFirstPerson) {
console.warn("FirstPerson is not compatible with orthographic projection.");
return;
}
camera.set(mode);
}Projection Switching
// Switch to orthographic
await world.camera.projection.set("Orthographic");
// Switch to perspective
await world.camera.projection.set("Perspective");
// Toggle
await world.camera.projection.toggle();
// Listen for changes
world.camera.projection.onChanged.add(() => {
const projection = world.camera.projection.current;
console.log("Now using:", projection);
// Update grid fade based on projection
grid.fade = projection === "Perspective";
});Fit Camera to Objects
// Fit to specific meshes
const meshes = [mesh1, mesh2, mesh3];
await world.camera.fit(meshes);
// Fit with more padding
await world.camera.fit(meshes, 2.5);
// Fit to all loaded fragment models
world.camera.fitToItems();Programmatic Camera Positioning
// Set camera position and look-at target
await world.camera.controls.setLookAt(
68, 23, -8.5, // camera position (x, y, z)
21.5, -5.5, 23, // look-at target (x, y, z)
);
// Animated transition (smooth)
await world.camera.controls.setLookAt(
68, 23, -8.5,
21.5, -5.5, 23,
true, // animate
);Floor Plan View Setup
// Complete floor plan viewing pattern
async function enterFloorPlanView(
camera: OBC.OrthoPerspectiveCamera,
grid: OBC.SimpleGrid,
centerX: number,
centerZ: number,
floorHeight: number,
viewHeight: number,
): Promise<void> {
// 1. Switch to Plan mode (disables rotation)
camera.set("Plan");
// 2. Switch to orthographic projection
await camera.projection.set("Orthographic");
// 3. Position camera above floor, looking straight down
await camera.controls.setLookAt(
centerX, viewHeight, centerZ,
centerX, floorHeight, centerZ,
);
// 4. Update grid for orthographic
grid.fade = false;
}
// Exit floor plan view
async function exitFloorPlanView(
camera: OBC.OrthoPerspectiveCamera,
grid: OBC.SimpleGrid,
): Promise<void> {
camera.set("Orbit");
await camera.projection.set("Perspective");
grid.fade = true;
}Custom Navigation Mode
// Define a custom "Fly" navigation mode
const flyMode: OBC.NavigationMode = {
id: "Fly",
enabled: false,
set(active: boolean) {
this.enabled = active;
if (active) {
const controls = world.camera.controls;
controls.minDistance = 0;
controls.maxDistance = Infinity;
controls.truckSpeed = 10;
// No rotation constraints — free flight
}
},
};
// Register and activate
world.camera.addCustomNavigationMode(flyMode);
world.camera.set("Fly");
// Switch back to built-in mode
world.camera.set("Orbit");Lock Camera During Animation
// Disable user input during programmatic movement
world.camera.setUserInput(false);
await world.camera.controls.setLookAt(
newX, newY, newZ,
targetX, targetY, targetZ,
true, // animate
);
// Re-enable after animation
world.camera.setUserInput(true);Camera Update Events with Fragments
// Update fragment LOD when camera moves
world.camera.controls.addEventListener("update", () => {
fragments.core.update();
});
// Update fragment camera reference when world camera changes
world.onCameraChanged.add((camera) => {
for (const [, model] of fragments.list) {
model.useCamera(camera.three);
}
fragments.core.update(true);
});UI Dropdown for Mode Selection (ThatOpen UI)
import * as BUI from "@thatopen/ui";
BUI.Manager.init();
const panel = BUI.Component.create<BUI.PanelSection>(() => {
return BUI.html`
<bim-panel active label="Camera Controls">
<bim-panel-section label="Navigation">
<bim-dropdown required label="Navigation Mode"
@change="${({ target }: { target: BUI.Dropdown }) => {
const selected = target.value[0] as OBC.NavModeID;
const isOrtho = world.camera.projection.current === "Orthographic";
if (isOrtho && selected === "FirstPerson") {
alert("FirstPerson is not compatible with orthographic!");
target.value[0] = world.camera.mode.id;
return;
}
world.camera.set(selected);
}}">
<bim-option checked label="Orbit"></bim-option>
<bim-option label="FirstPerson"></bim-option>
<bim-option label="Plan"></bim-option>
</bim-dropdown>
<bim-dropdown required label="Projection"
@change="${({ target }: { target: BUI.Dropdown }) => {
const selected = target.value[0] as OBC.CameraProjection;
const isFirstPerson = world.camera.mode.id === "FirstPerson";
if (selected === "Orthographic" && isFirstPerson) {
alert("Orthographic is not compatible with FirstPerson!");
target.value[0] = world.camera.projection.current;
return;
}
world.camera.projection.set(selected);
}}">
<bim-option checked label="Perspective"></bim-option>
<bim-option label="Orthographic"></bim-option>
</bim-dropdown>
<bim-checkbox label="Allow User Input" checked
@change="${({ target }: { target: BUI.Checkbox }) => {
world.camera.setUserInput(target.checked);
}}">
</bim-checkbox>
<bim-button label="Fit Model"
@click=${() => world.camera.fitToItems()}>
</bim-button>
</bim-panel-section>
</bim-panel>
`;
});OrthoPerspectiveCamera — API Reference
OrthoPerspectiveCamera
Extends SimpleCamera. Provides dual-projection camera with multiple navigation modes.
Properties
| Property | Type | Description |
|---|---|---|
projection | ProjectionManager | Manages perspective/orthographic switching |
threePersp | THREE.PerspectiveCamera | Perspective camera instance |
threeOrtho | THREE.OrthographicCamera | Orthographic camera instance |
three | THREE.Camera | Currently active camera (inherited from SimpleCamera) |
controls | CameraControls | Underlying camera-controls instance (inherited) |
mode | NavigationMode (getter) | Current active navigation mode |
Constructor
constructor(components: Components)Creates both perspective and orthographic cameras. Initializes the three built-in navigation modes (Orbit, FirstPerson, Plan). Sets Orbit as the default active mode.
Methods
set(mode: string): void
Switch the active navigation mode.
camera.set("Orbit");
camera.set("FirstPerson");
camera.set("Plan");
camera.set("CustomModeId"); // if registered- Deactivates the current mode by calling
currentMode.set(false). - Activates the new mode by calling
newMode.set(true). - ALWAYS switches modes synchronously.
fit(meshes: Iterable\<THREE.Mesh\>, offset?: number): Promise\<void\>
Frame the camera to show all provided meshes.
await camera.fit(meshes); // offset defaults to 1.5
await camera.fit(meshes, 2.0); // more padding- meshes: Iterable of THREE.Mesh objects to frame.
- offset: Padding multiplier (default: 1.5). Higher values give more
space around the objects.
fitToItems(): void
Convenience method to fit to all loaded fragment model meshes.
camera.fitToItems();setUserInput(active: boolean): void
Enable or disable all user camera interactions.
camera.setUserInput(false); // lock camera
camera.setUserInput(true); // unlock cameraWhen false: stores current mouse button assignments, then nullifies them. When true: restores previously stored mouse button assignments.
addCustomNavigationMode(mode: NavigationMode): void
Register a custom navigation mode.
camera.addCustomNavigationMode(myMode);The mode is stored in the internal _navigationModes map keyed by mode.id.
dispose(): void
Disposes both cameras, the projection manager, and all navigation modes. Called automatically when the parent World is disposed.
---
ProjectionManager
Manages switching between perspective and orthographic projections.
Properties
| Property | Type | Default | Description |
|---|---|---|---|
current | CameraProjection | "Perspective" | Active projection type |
onChanged | Event<THREE.Camera> | — | Fires after projection switch |
matchOrthoDistanceEnabled | boolean | false | Match ortho frustum to perspective distance |
Methods
set(projection: CameraProjection): Promise\<void\>
Switch to a specific projection.
await camera.projection.set("Perspective");
await camera.projection.set("Orthographic");ALWAYS await this method. It performs camera transition calculations.
toggle(): Promise\<void\>
Toggle between Perspective and Orthographic.
await camera.projection.toggle();---
NavigationMode Interface
interface NavigationMode {
readonly id: NavModeID; // Unique mode identifier
enabled: boolean; // Whether this mode is currently active
set(active: boolean): void; // Called to activate/deactivate
}When a mode is activated via set(true), it configures camera-controls settings (rotation speeds, mouse mappings, distance constraints). When deactivated via set(false), it should restore previous settings.
---
Type Aliases
type NavModeID = "Orbit" | "FirstPerson" | "Plan" | string;
type CameraProjection = "Perspective" | "Orthographic";NavModeID is extensible — custom modes can use any string identifier.
---
Built-in Mode Implementations
OrbitMode
| Setting | Value |
|---|---|
minDistance | 1 |
maxDistance | 300 |
truckSpeed | 2 |
Calculates orbit target from camera direction and distance. Standard 3D CAD-style orbit navigation.
FirstPersonMode
| Setting | Value |
|---|---|
minDistance | 1 |
maxDistance | 1 |
distance | 1 |
truckSpeed | 50 |
Mouse wheel mapped to dolly. Two-finger touch mapped to zoom-truck. REQUIRES perspective projection — falls back to Orbit if orthographic.
PlanMode
| Setting | Value |
|---|---|
| Azimuth rotation speed | 0 (disabled) |
| Polar rotation speed | 0 (disabled) |
| Left mouse | TRUCK (pan) |
| Single-touch | TOUCH_TRUCK |
| Dual-touch | TOUCH_ZOOM |
Stores original rotation speeds and mouse mappings on activation. Restores them on deactivation.
---
CameraControls (Underlying Library)
Key methods available via camera.controls:
// Position camera
await controls.setLookAt(posX, posY, posZ, targetX, targetY, targetZ, animate?);
// Move target only
await controls.moveTo(x, y, z, animate?);
// Zoom
await controls.dolly(distance, animate?);
await controls.zoom(zoomStep, animate?);
// Listen for updates
controls.addEventListener("update", callback);
controls.removeEventListener("update", callback);See camera-controls library documentation for full API.