skycraft: smoother ship controls

This commit is contained in:
Paul Mathieu 2022-11-10 14:09:52 -08:00
parent c709b1308e
commit f86697aad8
6 changed files with 163 additions and 30 deletions

View File

@ -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]];

View File

@ -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));
}

View File

@ -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

View File

@ -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,

View File

@ -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,
}

124
skycraft/quat.ts Normal file
View File

@ -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,
};
}