Initial work on perspective camera control for the 3D demo

This commit is contained in:
Patrick Walton 2017-09-02 13:41:08 -07:00
parent 7360a41a60
commit 049b8eba97
2 changed files with 96 additions and 10 deletions

View File

@ -12,6 +12,7 @@ import * as glmatrix from 'gl-matrix';
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
import {DemoAppController} from "./app-controller";
import {PerspectiveCamera} from "./camera";
import {mat4, vec2} from "gl-matrix";
import {PathfinderMeshData} from "./meshes";
import {ShaderMap, ShaderProgramSource} from "./shader-loader";
@ -19,7 +20,6 @@ import {BUILTIN_FONT_URI, TextLayout, PathfinderGlyph} from "./text";
import {PathfinderError, panic, unwrapNull} from "./utils";
import {PathfinderDemoView, Timings} from "./view";
import SSAAStrategy from "./ssaa-strategy";
import { OrthographicCamera } from "./camera";
const TEXT: string = "Lorem ipsum dolor sit amet";
@ -27,6 +27,10 @@ const FONT: string = 'open-sans';
const PIXELS_PER_UNIT: number = 1.0;
const FOV: number = 45.0;
const NEAR_CLIP_PLANE: number = 0.01;
const FAR_CLIP_PLANE: number = 1000.0;
const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = {
none: NoAAStrategy,
ssaa: SSAAStrategy,
@ -82,9 +86,8 @@ class ThreeDView extends PathfinderDemoView {
this.appController = appController;
this.camera = new OrthographicCamera(this.canvas);
this.camera.onPan = () => this.setDirty();
this.camera.onZoom = () => this.setDirty();
this.camera = new PerspectiveCamera(this.canvas);
this.camera.onChange = () => this.setDirty();
}
uploadPathMetadata(pathCount: number) {
@ -140,9 +143,13 @@ class ThreeDView extends PathfinderDemoView {
protected get worldTransform() {
const transform = glmatrix.mat4.create();
const translation = this.camera.translation;
glmatrix.mat4.fromTranslation(transform, [translation[0], translation[1], 0]);
glmatrix.mat4.scale(transform, transform, [this.camera.scale, this.camera.scale, 1.0]);
glmatrix.mat4.perspective(transform,
FOV / 180.0 * Math.PI,
this.canvas.width / this.canvas.height,
NEAR_CLIP_PLANE,
FAR_CLIP_PLANE);
glmatrix.mat4.translate(transform, transform, this.camera.translation);
glmatrix.mat4.mul(transform, transform, this.camera.rotationMatrix);
return transform;
}
@ -150,7 +157,7 @@ class ThreeDView extends PathfinderDemoView {
private appController: ThreeDController;
camera: OrthographicCamera;
camera: PerspectiveCamera;
}
class ThreeDGlyph extends PathfinderGlyph {

View File

@ -11,7 +11,12 @@
import * as glmatrix from 'gl-matrix';
import {PathfinderView} from "./view";
const SCALE_FACTOR: number = 1.0 / 100.0;
const ORTHOGRAPHIC_ZOOM_SPEED: number = 1.0 / 100.0;
const PERSPECTIVE_MOVEMENT_SPEED: number = 10.0;
const PERSPECTIVE_ROTATION_SPEED: number = 1.0 / 300.0;
const MOVEMENT_INTERVAL_DELAY: number = 10;
export abstract class Camera {
constructor(canvas: HTMLCanvasElement) {
@ -49,7 +54,7 @@ export class OrthographicCamera extends Camera {
glmatrix.vec2.sub(absoluteTranslation, this.translation, mouseLocation);
glmatrix.vec2.scale(absoluteTranslation, absoluteTranslation, 1.0 / this.scale);
this.scale *= 1.0 - event.deltaY * window.devicePixelRatio * SCALE_FACTOR;
this.scale *= 1.0 - event.deltaY * window.devicePixelRatio * ORTHOGRAPHIC_ZOOM_SPEED;
glmatrix.vec2.scale(absoluteTranslation, absoluteTranslation, this.scale);
glmatrix.vec2.add(this.translation, absoluteTranslation, mouseLocation);
@ -73,3 +78,77 @@ export class OrthographicCamera extends Camera {
translation: glmatrix.vec2;
scale: number;
}
export class PerspectiveCamera extends Camera {
constructor(canvas: HTMLCanvasElement) {
super(canvas);
this.translation = glmatrix.vec3.create();
this.rotation = glmatrix.vec2.create();
this.movementDelta = glmatrix.vec3.create();
this.movementInterval = null;
this.canvas.addEventListener('mousedown', event => this.onMouseDown(event), false);
this.canvas.addEventListener('mouseup', event => this.onMouseUp(event), false);
this.canvas.addEventListener('mousemove', event => this.onMouseMove(event), false);
this.onChange = null;
}
private onMouseDown(event: MouseEvent): void {
if (document.pointerLockElement !== this.canvas) {
this.canvas.requestPointerLock();
return;
}
this.movementDelta = glmatrix.vec3.fromValues(PERSPECTIVE_MOVEMENT_SPEED, 0.0, 0.0);
if (event.button !== 1)
this.movementDelta[0] = -this.movementDelta[0];
this.movementInterval = window.setInterval(() => this.move(), MOVEMENT_INTERVAL_DELAY);
}
private onMouseUp(event: MouseEvent): void {
if (this.movementInterval != null) {
window.clearInterval(this.movementInterval);
this.movementDelta = glmatrix.vec3.create();
}
}
private move() {
const delta = glmatrix.vec3.clone(this.movementDelta);
glmatrix.vec3.transformMat4(delta, delta, this.rotationMatrix);
glmatrix.vec3.add(this.translation, this.translation, delta);
if (this.onChange != null)
this.onChange();
}
private onMouseMove(event: MouseEvent): void {
if (document.pointerLockElement !== this.canvas)
return;
this.rotation[0] += event.movementY * PERSPECTIVE_ROTATION_SPEED;
this.rotation[1] += event.movementX * PERSPECTIVE_ROTATION_SPEED;
if (this.onChange != null)
this.onChange();
}
get rotationMatrix(): glmatrix.mat4 {
const matrix = glmatrix.mat4.create();
glmatrix.mat4.fromXRotation(matrix, this.rotation[0]);
glmatrix.mat4.rotateY(matrix, matrix, this.rotation[1]);
return matrix;
}
onChange: (() => void) | null;
translation: glmatrix.vec3;
/// Pitch and yaw Euler angles.
rotation: glmatrix.vec2;
private movementDelta: glmatrix.vec3;
private movementInterval: number | null;
}