diff --git a/skycraft/chunk.ts b/skycraft/chunk.ts index 8397c29..0b41bb5 100644 --- a/skycraft/chunk.ts +++ b/skycraft/chunk.ts @@ -138,7 +138,7 @@ function faceTexture(type: number, dir: direction) { function* makeChunkFaces(chunk) { const cs = CHUNKSIZE; - function faceCenter(pos: linalg.vec3, dir: direction) { + function faceCenter(pos: linalg.Vec3, dir: direction) { switch (dir) { case '-x': return [pos[0] - 0.5, pos[1], pos[2]]; case '+x': return [pos[0] + 0.5, pos[1], pos[2]]; diff --git a/skycraft/draw.ts b/skycraft/draw.ts index b0bc6ff..bef27c4 100644 --- a/skycraft/draw.ts +++ b/skycraft/draw.ts @@ -259,7 +259,7 @@ function getObjects(context, body, parentPosition = undefined) { } else { const shipOrientation = [ se3.rotationOnly(player.tf), - se3.rotationOnly(context.camera.tf), + //se3.rotationOnly(context.camera.tf), se3.rotxyz(-Math.PI / 2, Math.PI / 2, Math.PI / 2), ].reduce(se3.product); const shipPos = player.position; @@ -280,7 +280,7 @@ function getObjects(context, body, parentPosition = undefined) { return objects; } -function sunDirection(position: linalg.vec3) { +function sunDirection(position: linalg.Vec3) { return linalg.scale(position, 1/linalg.norm(position)); } diff --git a/skycraft/index.ts b/skycraft/index.ts index 38a28e6..912e87c 100644 --- a/skycraft/index.ts +++ b/skycraft/index.ts @@ -6,6 +6,7 @@ import * as se3 from '../se3'; import { computeOrbit, findSoi, getCartesianState, updateBodyPhysics } from './orbit'; import { getBodyGeometry } from './chunk'; import { draw, getOrbitDrawContext, initWorldGl } from './draw'; +import * as quat from './quat'; const kEpoch = 0; @@ -175,12 +176,13 @@ function initUiListeners(canvas: HTMLCanvasElement, context) { } }; const moveListener = e => { - context.camera.orientation[0] -= e.movementY / 500; - context.camera.orientation[1] -= e.movementX / 500; - context.camera.tf = se3.product( - se3.roty(context.camera.orientation[1]), - se3.rotx(context.camera.orientation[0]), - ); +// context.camera.orientation[0] -= e.movementY / 500; +// context.camera.orientation[1] -= e.movementX / 500; + context.camera.tf =[ + context.camera.tf, + se3.roty(-e.movementX / 500), + se3.rotx(-e.movementY / 500), + ].reduce(se3.product); }; const changeListener = () => { if (document.pointerLockElement === canvas) { @@ -224,11 +226,8 @@ function handleInput(context) { if (context.flying) { context.player.tf = [ context.player.tf, - context.camera.tf, se3.translation(...dir), ].reduce(se3.product); - context.camera.tf = se3.identity(); - context.camera.orientation = [0, 0, 0]; } else { const vel = context.player.velocity; const dv = linalg.scale(se3.apply(tf, dir), 1/dir[3]); @@ -258,6 +257,17 @@ function handleInput(context) { }); } +function slerp(current: linalg.Mat4, target: linalg.Mat4, maxVelocity: number) : linalg.Mat4 { + const q0 = quat.mat2Quat(current); + const q1 = quat.mat2Quat(target); + + const dq = quat.diff(q1, q0); + const maxt = maxVelocity / quat.norm(dq); + + const q = quat.normalize(quat.add(q0, quat.scale(dq, Math.min(1, maxt)))); + return quat.quat2Mat(q); +} + function updatePhysics(time: number, context) { const {player} = context; const dt = time - (context.lastTime || 0); @@ -267,6 +277,10 @@ function updatePhysics(time: number, context) { updateBodyPhysics(time, context.universe); + const dr = slerp(se3.identity(), context.camera.tf, 0.007); + player.tf = se3.product(player.tf, dr); + context.camera.tf = se3.product(context.camera.tf, se3.inverse(dr)); + if (!context.flying) { if (context.orbit === undefined) { const newPos = linalg.add(player.position, linalg.scale(player.velocity, dt)); @@ -293,7 +307,6 @@ function updatePhysics(time: number, context) { } } } - } function updateGeometry(context, timeout_ms = 10) { @@ -304,8 +317,6 @@ function tick(time: number, context) { const simTime = time * 0.001 + context.timeOffset; updatePhysics(simTime, context); - const campos = context.player.position; - // world generation / geometry update { // frame time is typically 16.7ms, so this may lag a bit diff --git a/skycraft/linalg.ts b/skycraft/linalg.ts index 70bdfaf..32b4ca3 100644 --- a/skycraft/linalg.ts +++ b/skycraft/linalg.ts @@ -1,6 +1,7 @@ -export type vec3 = [number, number, number]; +export type Vec3 = [number, number, number]; +export type Mat4 = number[]; -export function cross(a: vec3, b: vec3) : vec3 { +export function cross(a: Vec3, b: Vec3) : Vec3 { return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], @@ -8,7 +9,7 @@ export function cross(a: vec3, b: vec3) : vec3 { ]; } -export function diff(a: vec3, b: vec3) : vec3 { +export function diff(a: Vec3, b: Vec3) : Vec3 { return [ a[0] - b[0], a[1] - b[1], @@ -16,7 +17,7 @@ export function diff(a: vec3, b: vec3) : vec3 { ]; } -export function add(a: vec3, b: vec3) : vec3 { +export function add(a: Vec3, b: Vec3) : Vec3 { return [ a[0] + b[0], a[1] + b[1], @@ -24,14 +25,14 @@ export function add(a: vec3, b: vec3) : vec3 { ]; } -export function norm(a: vec3) : number { +export function norm(a: Vec3) : number { return Math.sqrt(a[0] ** 2 + a[1] ** 2 + a[2] ** 2); } -export function dot(a: vec3, b: vec3) : number { +export function dot(a: Vec3, b: Vec3) : number { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; } -export function scale(a: vec3, s: number) : vec3 { +export function scale(a: Vec3, s: number) : Vec3 { return [ a[0] * s, a[1] * s, diff --git a/skycraft/orbit.ts b/skycraft/orbit.ts index 80add15..5c7837f 100644 --- a/skycraft/orbit.ts +++ b/skycraft/orbit.ts @@ -1,9 +1,6 @@ import * as se3 from '../se3'; import * as linalg from './linalg'; -import {vec3} from './linalg'; - -type mat4 = number[]; -type vec4 = [number, number, number, number]; +import {Vec3} from './linalg'; interface Orbit { excentricity: number, @@ -18,13 +15,13 @@ interface Orbit { } interface Body { - position: vec3, - velocity: vec3, - orientation: vec4, + position: Vec3, + velocity: Vec3, + orientation: Vec3, children: Body[], mass: number, orbit: Orbit, - spin: vec3, + spin: Vec3, name: string, } diff --git a/skycraft/quat.ts b/skycraft/quat.ts new file mode 100644 index 0000000..6592285 --- /dev/null +++ b/skycraft/quat.ts @@ -0,0 +1,124 @@ +import {Mat4} from './linalg'; +import * as se3 from '../se3'; + +export interface Quat { + x: number, + y: number, + z: number, + w: number, +} + +export function mat2Quat(m: Mat4) : Quat { + const q : Quat = {}; + + if (m[0 * 4 + 0] + m[1 * 4 + 1] + m[2 * 4 + 2] > 0.0) { + const t = + m[0 * 4 + 0] + m[1 * 4 + 1] + m[2 * 4 + 2] + 1.0; + const s = 0.5 / Math.sqrt(t); + q.w = s * t; + q.z = (m[1 * 4 + 0] - m[0 * 4 + 1]) * s; + q.y = (m[0 * 4 + 2] - m[2 * 4 + 0]) * s; + q.x = (m[2 * 4 + 1] - m[1 * 4 + 2]) * s; + } else if (m[0 * 4 + 0] > m[1 * 4 + 1] && m[0 * 4 + 0] > m[2 * 4 + 2]) { + const t = + m[0 * 4 + 0] - m[1 * 4 + 1] - m[2 * 4 + 2] + 1.0; + const s = 0.5 / Math.sqrt(t); + q.x = s * t; + q.y = (m[1 * 4 + 0] + m[0 * 4 + 1]) * s; + q.z = (m[0 * 4 + 2] + m[2 * 4 + 0]) * s; + q.w = (m[2 * 4 + 1] - m[1 * 4 + 2]) * s; + } else if (m[1 * 4 + 1] > m[2 * 4 + 2]) { + const t = - m[0 * 4 + 0] + m[1 * 4 + 1] - m[2 * 4 + 2] + 1.0; + const s = 0.5 / Math.sqrt(t); + q.y = s * t; + q.x = (m[1 * 4 + 0] + m[0 * 4 + 1]) * s; + q.w = (m[0 * 4 + 2] - m[2 * 4 + 0]) * s; + q.z = (m[2 * 4 + 1] + m[1 * 4 + 2]) * s; + } else { + const t = - m[0 * 4 + 0] - m[1 * 4 + 1] + m[2 * 4 + 2] + 1.0; + const s = 0.5 / Math.sqrt(t); + q.z = s * t; + q.w = (m[1 * 4 + 0] - m[0 * 4 + 1]) * s; + q.x = (m[0 * 4 + 2] + m[2 * 4 + 0]) * s; + q.y = (m[2 * 4 + 1] + m[1 * 4 + 2]) * s; + } + + return q; +} + +export function quat2Mat(q: Quat): Mat4 { + const m: Mat4 = se3.identity(); + + const x2 = q.x + q.x; + const y2 = q.y + q.y; + const z2 = q.z + q.z; + { + const xx2 = q.x * x2; + const yy2 = q.y * y2; + const zz2 = q.z * z2; + m[0 * 4 + 0] = 1.0 - yy2 - zz2; + m[1 * 4 + 1] = 1.0 - xx2 - zz2; + m[2 * 4 + 2] = 1.0 - xx2 - yy2; + } + { + const yz2 = q.y * z2; + const wx2 = q.w * x2; + m[1 * 4 + 2] = yz2 - wx2; + m[2 * 4 + 1] = yz2 + wx2; + } + { + const xy2 = q.x * y2; + const wz2 = q.w * z2; + m[0 * 4 + 1] = xy2 - wz2; + m[1 * 4 + 0] = xy2 + wz2; + } + { + const xz2 = q.x * z2; + const wy2 = q.w * y2; + m[2 * 4 + 0] = xz2 - wy2; + m[0 * 4 + 2] = xz2 + wy2; + } + + return m; +} + +export function normalize(q: Quat) : Quat { + const n = norm(q); + if (n < 1e-10) { + return {x: 0, y: 0, z: 0, w: 1}; + } + return { + x: q.x / n, + y: q.y / n, + z: q.z / n, + w: q.w / n, + }; +} + +export function diff(q0: Quat, q1: Quat) { + return { + x: q0.x - q1.x, + y: q0.y - q1.y, + z: q0.z - q1.z, + w: q0.w - q1.w, + }; +} +export function norm(q: Quat) { + return Math.sqrt(q.x**2 + q.y**2 + q.z**2 + q.w**2); +} + +export function add(q0: Quat, q1: Quat) { + return { + x: q0.x + q1.x, + y: q0.y + q1.y, + z: q0.z + q1.z, + w: q0.w + q1.w, + }; +} + +export function scale(q: Quat, a: number): Quat { + return { + x: a * q.x, + y: a * q.y, + z: a * q.z, + w: a * q.w, + }; +} \ No newline at end of file