skycraft: orbit computatino still borken

But we got ourselves a spaceship!
Improved the camera a bit. Now we can see the ship's orbit.
This commit is contained in:
Paul Mathieu 2022-11-08 00:33:51 -08:00
parent 2fccb1fb7c
commit 216ef470c5
4 changed files with 166 additions and 47 deletions

View File

@ -3,43 +3,8 @@
import * as se3 from '../se3'; import * as se3 from '../se3';
import {loadTexture, makeProgram} from '../gl'; import {loadTexture, makeProgram} from '../gl';
import {makeFace, makeBufferFromFaces} from '../geometry'; import {makeFace, makeBufferFromFaces} from '../geometry';
import { loadStlModel } from './stl';
const linalg = { import * as linalg from './linalg';
cross: (a, b) => {
return [
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0],
];
},
diff: (a, b) => {
return [
a[0] - b[0],
a[1] - b[1],
a[2] - b[2],
];
},
add: (a, b) => {
return [
a[0] + b[0],
a[1] + b[1],
a[2] + b[2],
];
},
norm: a => {
return Math.sqrt(a[0]**2 + a[1]**2 + a[2]**2);
},
dot: (a, b) => {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
},
scale: (a, s) => {
return [
a[0] * s,
a[1] * s,
a[2] * s,
];
},
};
const VSHADER = ` const VSHADER = `
attribute vec3 aPosition; attribute vec3 aPosition;
@ -462,7 +427,7 @@ function closeToPlanet(context) {
} }
function makeSun(seed) { function makeSun(seed) {
const radius = 79; const radius = 7;
const radiusChunks = Math.floor(radius / CHUNKSIZE); const radiusChunks = Math.floor(radius / CHUNKSIZE);
const chunks = []; const chunks = [];
@ -756,6 +721,9 @@ function handleInput(context) {
case 'KeyD': case 'KeyD':
move(0.0, 0.5); move(0.0, 0.5);
return; return;
case 'KeyR':
context.timeOffset += 1;
return;
} }
}); });
} }
@ -998,6 +966,7 @@ function getCartesianState(orbit, mu, time) {
const rd = e * Math.sqrt(mu / p) * Math.sin(nu); const rd = e * Math.sqrt(mu / p) * Math.sin(nu);
if (orbit.tf === undefined) { if (orbit.tf === undefined) {
// FIXME: this is actually borken. :/
orbit.tf = [se3.rotz(Om), se3.rotx(i), se3.rotz(w)].reduce(se3.product); orbit.tf = [se3.rotz(Om), se3.rotx(i), se3.rotz(w)].reduce(se3.product);
} }
@ -1023,11 +992,14 @@ function makeOrbitObject(context, orbit, parentPosition) {
const {gl} = context; const {gl} = context;
const position = parentPosition; const position = parentPosition;
const glContext = context.orbitGlContext; const glContext = context.orbitGlContext;
const orientation = [ const orientation = orbit.tf;
se3.rotz(orbit.ascendingNodeLongitude),
se3.rotx(orbit.inclination), // FIXME: currently borken.
se3.rotz(orbit.periapsisArgument), // const orientation = [
].reduce(se3.product); // se3.rotz(orbit.ascendingNodeLongitude),
// se3.rotx(orbit.inclination),
// se3.rotz(orbit.periapsisArgument),
// ].reduce(se3.product);
const buffer = gl.createBuffer(); const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
@ -1062,7 +1034,7 @@ const kGravitationalConstant = 6.674e-11;
function getObjects(context, body, parentPosition) { function getObjects(context, body, parentPosition) {
const objects = []; const objects = [];
const {gl, glContext} = context; const {gl, glContext, player} = context;
const {position, orientation} = body; const {position, orientation} = body;
if (body.glBuffer === undefined) { if (body.glBuffer === undefined) {
@ -1078,6 +1050,19 @@ function getObjects(context, body, parentPosition) {
if (parentPosition !== undefined) { if (parentPosition !== undefined) {
const orbitObject = makeOrbitObject(context, body.orbit, parentPosition); const orbitObject = makeOrbitObject(context, body.orbit, parentPosition);
objects.push(orbitObject); objects.push(orbitObject);
} else {
const shipOrientation = [
se3.rotationOnly(player.tf),
se3.rotationOnly(context.camera.tf),
se3.rotxyz(-Math.PI / 2, 0, Math.PI / 2),
].reduce(se3.product);
const shipPos = player.position;
objects.push({
geometry: makeBufferFromFaces(gl, context.spaceship),
orientation: shipOrientation,
position: shipPos,
glContext,
});
} }
if (body.children !== undefined) { if (body.children !== undefined) {
@ -1108,7 +1093,12 @@ function draw(context) {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const viewMatrix = se3.inverse(se3.product(context.player.tf, context.camera.tf)); const viewMatrix = se3.inverse([
context.player.tf, // player position & orientation
context.camera.tf, // camera orientation relative to player
se3.translation(0, 1, 4), // step back from the player
].reduce(se3.product));
let lastGlContext; let lastGlContext;
for (const {position, orientation, geometry, glContext} of objects) { for (const {position, orientation, geometry, glContext} of objects) {
@ -1135,7 +1125,8 @@ function draw(context) {
function tick(time, context) { function tick(time, context) {
handleInput(context); handleInput(context);
updatePhysics(time * 0.001, context); const simTime = time * 0.001 + context.timeOffset;
updatePhysics(simTime, context);
const campos = context.player.position; const campos = context.player.position;
@ -1198,6 +1189,15 @@ async function main() {
return; return;
} }
// TODO
// [ ] loading bar
// [ ] spaceship
// [ ] landing
// [ ] huge planets
// [ ] lighting
const modelPromise = loadStlModel('spaceship.stl');
const context = { const context = {
gl, gl,
projMatrix: se3.perspective(Math.PI / 3, canvas.clientWidth / canvas.clientHeight, 0.1, 10000.0), projMatrix: se3.perspective(Math.PI / 3, canvas.clientWidth / canvas.clientHeight, 0.1, 10000.0),
@ -1213,7 +1213,7 @@ async function main() {
keys: new Set(), keys: new Set(),
lightDirection: [-0.2, -0.5, 0.4], lightDirection: [-0.2, -0.5, 0.4],
skyColor: [0.10, 0.15, 0.2], skyColor: [0.10, 0.15, 0.2],
ambiantLight: 0.7, ambiantLight: 0.4,
blockSelectDistance: 8, blockSelectDistance: 8,
flying: true, flying: true,
isOnGround: false, isOnGround: false,
@ -1221,6 +1221,7 @@ async function main() {
jumpForce: 6.5, jumpForce: 6.5,
// objects: makeObjects(gl), // objects: makeObjects(gl),
universe: getSolarSystem(0), universe: getSolarSystem(0),
timeOffset: 0,
}; };
context.glContext = await initWorldGl(gl); context.glContext = await initWorldGl(gl);
@ -1228,6 +1229,10 @@ async function main() {
initUiListeners(canvas, context); initUiListeners(canvas, context);
// setupParamPanel(context); // setupParamPanel(context);
const starshipGeom = await modelPromise;
console.log(`loaded ${starshipGeom.length} triangles`);
context.spaceship = starshipGeom;
requestAnimationFrame(time => tick(time, context)); requestAnimationFrame(time => tick(time, context));
} }

38
skycraft/linalg.js Normal file
View File

@ -0,0 +1,38 @@
export function cross(a, b) {
return [
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0],
];
}
export function diff(a, b) {
return [
a[0] - b[0],
a[1] - b[1],
a[2] - b[2],
];
}
export function add(a, b) {
return [
a[0] + b[0],
a[1] + b[1],
a[2] + b[2],
];
}
export function norm(a) {
return Math.sqrt(a[0] ** 2 + a[1] ** 2 + a[2] ** 2);
}
export function dot(a, b) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
export function scale(a, s) {
return [
a[0] * s,
a[1] * s,
a[2] * s,
];
}

BIN
skycraft/spaceship.stl Normal file

Binary file not shown.

76
skycraft/stl.js Normal file
View File

@ -0,0 +1,76 @@
import * as linalg from './linalg';
function parseTriangle(triangleData) {
const dv = new DataView(triangleData.buffer);
function data(idx) {
const offset = 4 * idx;
return dv.getFloat32(offset, /*littleEndian=*/true);
}
const attributeByteCount = dv.getUint16(48, /*littleEndian=*/true);
console.assert(attributeByteCount === 0);
const x = [data(3), data(4), data(5)];
const y = [data(6), data(7), data(8)];
const z = [data(9), data(10), data(11)];
const n = linalg.cross(linalg.diff(y, x), linalg.diff(z, y));
return {
vertices: [x, y, z],
normals: new Array(3).fill(linalg.scale(n, 1 / linalg.norm(n))),
textures: new Array(3).fill([0, 0]),
}; // no normals, default texture
}
export async function loadStlModel(url) {
const stlDataStream = (await fetch(url)).body;
const triangles = [];
return new Promise((resolve, reject) => {
let bytesReceived = 0;
let gotHeader = false;
const partial = new Uint8Array(50); // each triangle is 50 bytes
let partialOffset = 0;
const reader = stlDataStream.getReader();
function pump() {
reader.read().then(({ done, value }) => {
if (done) {
return resolve(triangles);
}
const skipped = bytesReceived;
bytesReceived += value.length;
let inputOffset = 0;
if (!gotHeader) {
if (bytesReceived < 84) { // header + triangle count
return pump();
} else {
gotHeader = true;
inputOffset = 84 - skipped;
}
}
// parse triangle data
while (true) {
const spaceLeft = 50 - partialOffset;
console.assert(spaceLeft > 0);
const leftToCopy = value.length - inputOffset;
const toCopy = value.subarray(inputOffset, inputOffset + Math.min(leftToCopy, spaceLeft));
partial.set(toCopy, partialOffset);
if (leftToCopy < spaceLeft) {
break;
}
// parse a triangle!
triangles.push(parseTriangle(partial));
partialOffset = 0;
inputOffset += toCopy.length;
}
pump();
});
}
pump();
});
}