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:
parent
2fccb1fb7c
commit
216ef470c5
@ -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
38
skycraft/linalg.js
Normal 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
BIN
skycraft/spaceship.stl
Normal file
Binary file not shown.
76
skycraft/stl.js
Normal file
76
skycraft/stl.js
Normal 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();
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user