Skip to content

Commit

Permalink
added back zoom slowing and pointer move only occurs on canvas
Browse files Browse the repository at this point in the history
  • Loading branch information
kpal81xd committed Apr 18, 2024
1 parent 6c7223c commit 7ec9258
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 102 deletions.
22 changes: 8 additions & 14 deletions src/cameras/base-camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ type PointerMoveEvent = PointerEvent & {
const LOOK_MAX_ANGLE = 90;

abstract class BaseCamera {
target: HTMLElement = document.documentElement;

entity: Entity = new Entity();

sceneSize: number = 100;
Expand All @@ -30,11 +32,8 @@ abstract class BaseCamera {

protected _angles: Vec3 = new Vec3();

protected _zoom: number = 0;

protected _focusDist: number = 0;

constructor(options: Record<string, any> = {}) {
constructor(target: HTMLElement, options: Record<string, any> = {}) {
this.target = target;
this.sceneSize = options.sceneSize ?? this.sceneSize;
this.lookSensitivity = options.lookSensitivity ?? this.lookSensitivity;
this.lookDamping = options.lookDamping ?? this.lookDamping;
Expand All @@ -45,14 +44,6 @@ abstract class BaseCamera {
this._onPointerUp = this._onPointerUp.bind(this);
}

abstract get point(): Vec3

abstract get start(): Vec3

get dir() {
return this._dir;
}

private _smoothLook(dt: number) {
const lerpRate = 1 - Math.pow(this.lookDamping, dt * 1000);
this._angles.x = math.lerp(this._angles.x, this._dir.x, lerpRate);
Expand All @@ -76,13 +67,16 @@ abstract class BaseCamera {
protected abstract _onPointerUp(event: PointerEvent): void

protected _look(event: PointerMoveEvent) {
if (event.target !== this.target) {
return;
}
const movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
const movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
this._dir.x = math.clamp(this._dir.x - movementY * this.lookSensitivity, -LOOK_MAX_ANGLE, LOOK_MAX_ANGLE);
this._dir.y -= movementX * this.lookSensitivity;
}

abstract focus(point: Vec3, start?: Vec3, dir?: Vec2, snap?: boolean): void
abstract focus(point: Vec3, start?: Vec3): void

attach(camera: Entity) {
this._camera = camera;
Expand Down
71 changes: 35 additions & 36 deletions src/cameras/multi-camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,24 @@ class MultiCamera extends BaseCamera {

wheelSpeed: number = 0.005;

zoomThreshold: number = 0.01;
zoomAbsMin: number = 0.01;

zoomExp: number = 0.5;
zoomScaleMax: number = 10;

zoomSpeedMin: number = 0.01;

moveSpeed: number = 2;

sprintSpeed: number = 4;

crouchSpeed: number = 1;

private _zoomDist: number = 0;

private _zoomStart: number = 0;

private _cameraDist: number = 0;

private _pointerEvents: Map<number, PointerEvent> = new Map();

private _lastPinchDist: number = -1;
Expand All @@ -60,15 +68,15 @@ class MultiCamera extends BaseCamera {
crouch: false
};

constructor(options: Record<string, any> = {}) {
super(options);
constructor(target: HTMLElement, options: Record<string, any> = {}) {
super(target, options);

this.mousePanSpeed = options.mousePanSpeed ?? this.mousePanSpeed;
this.mobilePanSpeed = options.mobilePanSpeed ?? this.mobilePanSpeed;
this.pinchSpeed = options.pinchSpeed ?? this.pinchSpeed;
this.wheelSpeed = options.wheelSpeed ?? this.wheelSpeed;
this.zoomThreshold = options.zoomThreshold ?? this.zoomThreshold;
this.zoomExp = options.zoomExp ?? this.zoomExp;
this.zoomAbsMin = options.zoomAbsMin ?? this.zoomAbsMin;
this.zoomScaleMax = options.zoomScaleMax ?? this.zoomScaleMax;
this.moveSpeed = options.moveSpeed ?? this.moveSpeed;
this.sprintSpeed = options.sprintSpeed ?? this.sprintSpeed;
this.crouchSpeed = options.crouchSpeed ?? this.crouchSpeed;
Expand All @@ -78,14 +86,6 @@ class MultiCamera extends BaseCamera {
this._onKeyUp = this._onKeyUp.bind(this);
}

get point() {
return this._origin;
}

get start() {
return this._camera.getPosition();
}

protected _onPointerDown(event: PointerEvent) {
if (!this._camera) {
return;
Expand All @@ -101,7 +101,7 @@ class MultiCamera extends BaseCamera {
this._panning = true;
}
if (event.button === 2) {
this._zoom = this._focusDist;
this._zoomDist = this._cameraDist;
this._origin.copy(this._camera.getPosition());
this._position.copy(this._origin);
this._camera.setLocalPosition(0, 0, 0);
Expand Down Expand Up @@ -133,7 +133,7 @@ class MultiCamera extends BaseCamera {
// pinch zoom
const pinchDist = this._getPinchDist();
if (this._lastPinchDist > 0) {
this._focusZoom(this._lastPinchDist - pinchDist);
this._zoom(this._lastPinchDist - pinchDist);
}
this._lastPinchDist = pinchDist;
}
Expand All @@ -150,7 +150,7 @@ class MultiCamera extends BaseCamera {
this._panning = false;
}
if (event.button === 2) {
tmpV1.copy(this.entity.forward).mulScalar(this._zoom);
tmpV1.copy(this.entity.forward).mulScalar(this._zoomDist);
this._origin.add(tmpV1);
this._position.add(tmpV1);
this._flying = false;
Expand All @@ -159,7 +159,7 @@ class MultiCamera extends BaseCamera {

private _onWheel(event: WheelEvent) {
event.preventDefault();
this._focusZoom(event.deltaY);
this._zoom(event.deltaY);
}

private _onKeyDown(event: KeyboardEvent) {
Expand Down Expand Up @@ -273,12 +273,15 @@ class MultiCamera extends BaseCamera {
this._lastPosition.copy(pos);
}

private _focusZoom(delta: number) {
const zoomMult = delta * this.sceneSize * this.wheelSpeed;
this._zoom = Math.max(this._zoom + zoomMult, this.zoomThreshold);
private _zoom(delta: number) {
const min = this.zoomAbsMin;
const max = this.zoomScaleMax * this.sceneSize;
const speed = math.clamp(this._zoomDist / (max - min), this.zoomSpeedMin, 1);
this._zoomDist += (delta * this.wheelSpeed * this.sceneSize * speed);
this._zoomDist = math.clamp(this._zoomDist, min, max);
}

focus(point: Vec3, start?: Vec3, dir?: Vec2, snap?: boolean) {
focus(point: Vec3, start?: Vec3) {
if (!this._camera) {
return;
}
Expand All @@ -288,23 +291,19 @@ class MultiCamera extends BaseCamera {
}

tmpV1.sub2(start, point);
if (dir) {
this._dir.copy(dir);
} else {
const elev = Math.atan2(tmpV1.y, tmpV1.z) * math.RAD_TO_DEG;
const azim = Math.atan2(tmpV1.x, tmpV1.z) * math.RAD_TO_DEG;
this._dir.set(-elev, -azim);
}
const elev = Math.atan2(tmpV1.y, tmpV1.z) * math.RAD_TO_DEG;
const azim = Math.atan2(tmpV1.x, tmpV1.z) * math.RAD_TO_DEG;
this._dir.set(-elev, -azim);

this._origin.copy(point);
this._camera.setPosition(start);

if (snap) {
this._angles.set(this._dir.x, this._dir.y, 0);
this._position.copy(this._origin);
}
this._zoomDist = tmpV1.length();
this._zoomStart = this._zoomDist;
}

this._zoom = tmpV1.length();
resetZoom() {
this._zoomDist = this._zoomStart;
}

attach(camera: Entity) {
Expand Down Expand Up @@ -343,8 +342,8 @@ class MultiCamera extends BaseCamera {
}

if (!this._flying) {
this._focusDist = math.lerp(this._focusDist, this._zoom, 1 - Math.pow(this.moveDamping, dt * 1000));
this._camera.setLocalPosition(0, 0, this._focusDist);
this._cameraDist = math.lerp(this._cameraDist, this._zoomDist, 1 - Math.pow(this.moveDamping, dt * 1000));
this._camera.setLocalPosition(0, 0, this._cameraDist);
}

this._move(dt);
Expand Down
108 changes: 56 additions & 52 deletions src/viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ import { MorphTargetData, File, HierarchyNode } from './types';
import { DebugLines } from './debug-lines';
import { Multiframe } from './multiframe';
import { ReadDepth } from './read-depth';
import { BaseCamera } from './cameras/base-camera';
import { MultiCamera } from './cameras/multi-camera';
import { PngExporter } from './png-exporter';
import { ProjectiveSkybox } from './projective-skybox';
Expand All @@ -88,9 +87,9 @@ const defaultSceneBounds = new BoundingBox(new Vec3(0, 1, 0), new Vec3(1, 1, 1))
const vec = new Vec3();
const bbox = new BoundingBox();

const FOCUS_SECTOR_MULT = 0.25;
const FOCUS_SCALE_MULT = 0.25;
const FOCUS_START_DIR = new Vec3(0, 1, 3);
const FOCUS_SECTOR_MULT = 0.5;
const FOCUS_SCALE_MULT = 1;
const FOCUS_START_DIR = new Vec3(0, 1, 5);

class Viewer {
canvas: HTMLCanvasElement;
Expand Down Expand Up @@ -155,7 +154,7 @@ class Viewer {

canvasResize = true;

controlCamera: BaseCamera;
multiCamera: MultiCamera;

constructor(canvas: HTMLCanvasElement, graphicsDevice: GraphicsDevice, observer: Observer, skyboxUrls: Map<string, string>) {
this.canvas = canvas;
Expand Down Expand Up @@ -234,14 +233,15 @@ class Viewer {
camera.camera.requestSceneColorMap(true);

// create camera controls
this.controlCamera = new MultiCamera();
app.root.addChild(this.controlCamera.entity);
this.controlCamera.attach(camera);
this.multiCamera = new MultiCamera(canvas);
app.root.addChild(this.multiCamera.entity);
this.multiCamera.attach(camera);

app.keyboard.on(EVENT_KEYDOWN, (event) => {
switch (event.key) {
case KEY_F: {
this.focusSelection(false);
this.multiCamera.resetZoom();
break;
}
}
Expand Down Expand Up @@ -380,7 +380,7 @@ class Viewer {
this.camera.getWorldTransform().transformPoint(this.cursorWorld, this.cursorWorld); // world space

// focus on cursor
this.controlCamera.focus(this.cursorWorld);
this.multiCamera.focus(this.cursorWorld);
}
});

Expand Down Expand Up @@ -701,6 +701,52 @@ class Viewer {
};
}

private getFocusPosition(bbox: BoundingBox) {
const focus = new Vec3();
if (this.initialCameraFocus) {
focus.copy(this.initialCameraFocus);
this.initialCameraFocus = null;
} else {
const entityAsset = this.entityAssets[0];
const splatData = entityAsset?.asset?.resource?.splatData;
if (splatData) {
splatData.calcFocalPoint(focus);
entityAsset.entity.getWorldTransform().transformPoint(focus, focus);
} else {
focus.copy(bbox.center);
}
}
return focus;
}

private focusSelection(calcStart = true) {
const camera = this.camera.camera;

// calculate scene bounding box
this.calcSceneBounds(bbox, this.selectedNode as Entity);

// calculate the camera focus point
const focus = this.getFocusPosition(bbox);

const sceneSize = bbox.halfExtents.length();
let start: Vec3 | null = null;
if (calcStart) {
start = new Vec3();
if (this.initialCameraPosition) {
start.copy(this.initialCameraPosition);
this.initialCameraPosition = null;
} else {
start.copy(focus);
const scale = FOCUS_SCALE_MULT / Math.sin(FOCUS_SECTOR_MULT * camera.fov * math.DEG_TO_RAD);
start.add(vec.copy(FOCUS_START_DIR).normalize().mulScalar(sceneSize * scale));
}
}

// focus orbit camera on object and set focus and sceneSize
this.multiCamera.sceneSize = sceneSize;
this.multiCamera.focus(focus, start);
}

destroyRenderTargets() {
const rt = this.camera.camera.renderTarget;
if (rt) {
Expand Down Expand Up @@ -873,48 +919,6 @@ class Viewer {
}
}

focusSelection(calcStart = true) {
const camera = this.camera.camera;

// calculate scene bounding box
this.calcSceneBounds(bbox, this.selectedNode as Entity);

const sceneSize = bbox.halfExtents.length();

// calculate the camera focus point
const focus = new Vec3();
if (this.initialCameraFocus) {
focus.copy(this.initialCameraFocus);
this.initialCameraFocus = null;
} else {
const entityAsset = this.entityAssets[0];
const splatData = entityAsset?.asset?.resource?.splatData;
if (splatData) {
splatData.calcFocalPoint(focus);
entityAsset.entity.getWorldTransform().transformPoint(focus, focus);
} else {
focus.copy(bbox.center);
}
}

let start: Vec3 | null = null;
if (calcStart) {
start = new Vec3();
if (this.initialCameraPosition) {
start.copy(this.initialCameraPosition);
this.initialCameraPosition = null;
} else {
start.copy(focus);
const scale = FOCUS_SCALE_MULT / Math.tan(FOCUS_SECTOR_MULT * camera.fov * math.DEG_TO_RAD);
start.add(vec.copy(FOCUS_START_DIR).mulScalar(sceneSize * scale));
}
}

// focus orbit camera on object and set focus and sceneSize
this.controlCamera.sceneSize = sceneSize;
this.controlCamera.focus(focus, start);
}

// adjust camera clipping planes to fit the scene
fitCameraClipPlanes() {
if (this.xrMode?.active) {
Expand Down Expand Up @@ -1410,7 +1414,7 @@ class Viewer {
update(deltaTime: number) {
// update the orbit camera
if (!this.xrMode?.active) {
this.controlCamera.update(deltaTime);
this.multiCamera.update(deltaTime);
}

const maxdiff = (a: Mat4, b: Mat4) => {
Expand Down

0 comments on commit 7ec9258

Please sign in to comment.