711 lines
20 KiB
JavaScript
711 lines
20 KiB
JavaScript
//import { initUiListeners, setupParamPanel, tick } from './game';
|
|
//import { initWorldGl, makeWorld } from './world';
|
|
import * as se3 from '../se3';
|
|
import {loadTexture, makeProgram} from '../gl';
|
|
import {makeFace, makeBufferFromFaces} from '../geometry';
|
|
|
|
const VSHADER = `
|
|
attribute vec3 aPosition;
|
|
attribute vec3 aNormal;
|
|
attribute vec2 aTextureCoord;
|
|
|
|
uniform mat4 uProjection;
|
|
uniform mat4 uModel;
|
|
uniform mat4 uView;
|
|
uniform vec3 uLightDirection;
|
|
uniform float uAmbiantLight;
|
|
|
|
varying highp vec2 vTextureCoord;
|
|
varying lowp vec3 vLighting;
|
|
varying lowp float vDistance;
|
|
|
|
void main() {
|
|
highp mat4 modelview = uView * uModel;
|
|
|
|
gl_Position = uProjection * modelview * vec4(aPosition, 1.0);
|
|
|
|
lowp vec3 normal = mat3(uModel) * aNormal;
|
|
lowp float diffuseAmount = max(dot(-uLightDirection, normal), 0.0);
|
|
lowp vec3 ambiant = uAmbiantLight * vec3(1.0, 1.0, 0.9);
|
|
vLighting = ambiant + vec3(1.0, 1.0, 1.0) * diffuseAmount;
|
|
|
|
vTextureCoord = aTextureCoord;
|
|
|
|
vDistance = length(modelview * vec4(aPosition, 1.0));
|
|
}
|
|
`;
|
|
|
|
const FSHADER = `
|
|
uniform sampler2D uSampler;
|
|
uniform lowp vec3 uFogColor;
|
|
|
|
varying highp vec2 vTextureCoord;
|
|
varying lowp vec3 vLighting;
|
|
varying lowp float vDistance;
|
|
|
|
void main() {
|
|
highp vec4 color = texture2D(uSampler, vTextureCoord);
|
|
if (color.a < 0.1) {
|
|
discard;
|
|
}
|
|
lowp float fogamount = 0.0; //smoothstep(80.0, 100.0, vDistance);
|
|
gl_FragColor = mix(vec4(vLighting * color.rgb, color.a), vec4(uFogColor, 1.0), fogamount);
|
|
}
|
|
`;
|
|
|
|
const kEpoch = 0;
|
|
|
|
async function initWorldGl(gl) {
|
|
const program = makeProgram(gl, VSHADER, FSHADER);
|
|
const texture = await loadTexture(gl, 'texture.png');
|
|
|
|
// load those ahead of time
|
|
const viewLoc = gl.getUniformLocation(program, 'uView');
|
|
const modelLoc = gl.getUniformLocation(program, 'uModel');
|
|
const projLoc = gl.getUniformLocation(program, 'uProjection');
|
|
const samplerLoc = gl.getUniformLocation(program, 'uSampler');
|
|
const fogColorLoc = gl.getUniformLocation(program, 'uFogColor');
|
|
const lightDirectionLoc = gl.getUniformLocation(program, 'uLightDirection');
|
|
const ambiantLoc = gl.getUniformLocation(program, 'uAmbiantLight');
|
|
|
|
const positionLoc = gl.getAttribLocation(program, 'aPosition');
|
|
const normalLoc = gl.getAttribLocation(program, 'aNormal');
|
|
const textureLoc = gl.getAttribLocation(program, 'aTextureCoord');
|
|
|
|
const setupScene = (sceneParams) => {
|
|
const {
|
|
projectionMatrix,
|
|
viewMatrix,
|
|
fogColor,
|
|
lightDirection,
|
|
ambiantLightAmount,
|
|
} = sceneParams;
|
|
|
|
gl.useProgram(program);
|
|
|
|
gl.uniformMatrix4fv(projLoc, false, new Float32Array(projectionMatrix));
|
|
gl.uniformMatrix4fv(viewLoc, false, new Float32Array(viewMatrix));
|
|
gl.uniform3fv(fogColorLoc, fogColor);
|
|
gl.uniform3fv(lightDirectionLoc, lightDirection);
|
|
gl.uniform1f(ambiantLoc, ambiantLightAmount);
|
|
|
|
// doing this here because it's the same for all world stuff
|
|
gl.uniformMatrix4fv(modelLoc, false, new Float32Array(se3.identity()));
|
|
gl.uniform1i(samplerLoc, 0);
|
|
|
|
gl.enableVertexAttribArray(positionLoc);
|
|
gl.enableVertexAttribArray(normalLoc);
|
|
gl.enableVertexAttribArray(textureLoc);
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
};
|
|
|
|
const drawObject = (objectParams) => {
|
|
const {
|
|
position,
|
|
orientation,
|
|
glBuffer,
|
|
numVertices,
|
|
} = objectParams;
|
|
|
|
gl.uniformMatrix4fv(modelLoc, false, new Float32Array(se3.product(
|
|
se3.translation(...position), orientation)));
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, glBuffer);
|
|
|
|
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 20, 0);
|
|
gl.vertexAttribPointer(normalLoc, 3, gl.BYTE, true, 20, 12);
|
|
gl.vertexAttribPointer(textureLoc, 2, gl.UNSIGNED_SHORT, true, 20, 16);
|
|
|
|
gl.drawArrays(gl.TRIANGLES, 0, numVertices);
|
|
};
|
|
|
|
return {
|
|
setupScene,
|
|
drawObject,
|
|
};
|
|
}
|
|
|
|
const ORBIT_VSHADER = `
|
|
attribute vec3 aPosition;
|
|
attribute vec2 aValue;
|
|
|
|
uniform mat4 uProjection;
|
|
uniform mat4 uModel;
|
|
uniform mat4 uView;
|
|
|
|
varying lowp vec2 vCoords;
|
|
|
|
void main() {
|
|
highp mat4 modelview = uView * uModel;
|
|
gl_Position = uProjection * modelview * vec4(aPosition, 1.0);
|
|
|
|
vCoords = aValue;
|
|
}
|
|
`;
|
|
|
|
const ORBIT_FSHADER = `
|
|
varying lowp vec2 vCoords;
|
|
|
|
void main() {
|
|
lowp float x = vCoords.x;
|
|
lowp float y = vCoords.y;
|
|
|
|
lowp float f = sqrt(x * x + y * y);
|
|
|
|
if (f > 1.00) {
|
|
discard;
|
|
} else if (f < 0.98) {
|
|
discard;
|
|
}
|
|
gl_FragColor = vec4(1, .5, 0, 0.5);
|
|
}
|
|
`;
|
|
|
|
function getOrbitDrawContext(gl) {
|
|
const program = makeProgram(gl, ORBIT_VSHADER, ORBIT_FSHADER);
|
|
|
|
// load those ahead of time
|
|
const viewLoc = gl.getUniformLocation(program, 'uView');
|
|
const modelLoc = gl.getUniformLocation(program, 'uModel');
|
|
const projLoc = gl.getUniformLocation(program, 'uProjection');
|
|
|
|
const positionLoc = gl.getAttribLocation(program, 'aPosition');
|
|
const valueLoc = gl.getAttribLocation(program, 'aValue');
|
|
|
|
const setupScene = (sceneParams) => {
|
|
const {
|
|
projectionMatrix,
|
|
viewMatrix,
|
|
} = sceneParams;
|
|
|
|
gl.useProgram(program);
|
|
|
|
gl.uniformMatrix4fv(projLoc, false, new Float32Array(projectionMatrix));
|
|
gl.uniformMatrix4fv(viewLoc, false, new Float32Array(viewMatrix));
|
|
|
|
// doing this here because it's the same for all world stuff
|
|
gl.uniformMatrix4fv(modelLoc, false, new Float32Array(se3.identity()));
|
|
|
|
gl.enableVertexAttribArray(positionLoc);
|
|
gl.enableVertexAttribArray(valueLoc);
|
|
};
|
|
|
|
const drawObject = (objectParams) => {
|
|
const {
|
|
position,
|
|
orientation,
|
|
value,
|
|
glBuffer,
|
|
} = objectParams;
|
|
|
|
gl.uniformMatrix4fv(modelLoc, false, new Float32Array(se3.product(
|
|
se3.translation(...position), orientation)));
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, glBuffer);
|
|
|
|
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 20, 0);
|
|
gl.vertexAttribPointer(valueLoc, 2, gl.FLOAT, false, 20, 12);
|
|
|
|
gl.disable(gl.CULL_FACE);
|
|
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
gl.enable(gl.CULL_FACE);
|
|
};
|
|
|
|
return {
|
|
setupScene,
|
|
drawObject,
|
|
};
|
|
}
|
|
|
|
function initUiListeners(canvas, context) {
|
|
const canvasClickHandler = () => {
|
|
canvas.requestPointerLock();
|
|
canvas.onclick = null;
|
|
// const clickListener = e => {
|
|
// switch(e.button) {
|
|
// case 0: // left click
|
|
// destroySelectedBlock(context);
|
|
// break;
|
|
// case 2: // right click
|
|
// makeDirBlock(context);
|
|
// break;
|
|
// }
|
|
// };
|
|
const clickListener = e => {};
|
|
const keyListener = e => {
|
|
if (e.type === 'keydown') {
|
|
if (e.repeat) return;
|
|
context.keys.add(e.code);
|
|
|
|
switch (e.code) {
|
|
case 'KeyF':
|
|
// context.flying = !context.flying;
|
|
return false;
|
|
case 'Space':
|
|
if (!context.flying) {
|
|
if (context.jumpAmount > 0) {
|
|
const amount = context.jumpForce;
|
|
context.camera.velocity[1] = amount;
|
|
context.jumpAmount -= 1;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
} else {
|
|
context.keys.delete(e.code);
|
|
}
|
|
};
|
|
const moveListener = e => {
|
|
context.camera.orientation[1] -= e.movementX / 500;
|
|
context.camera.orientation[0] -= e.movementY / 500;
|
|
|
|
context.camera.orientation[0] = Math.min(Math.max(
|
|
context.camera.orientation[0], -Math.PI / 2
|
|
), Math.PI / 2);
|
|
};
|
|
const changeListener = () => {
|
|
if (document.pointerLockElement === canvas) {
|
|
return;
|
|
}
|
|
document.removeEventListener('pointerdown', clickListener);
|
|
document.removeEventListener('pointerlockchange', changeListener);
|
|
document.removeEventListener('pointermove', moveListener);
|
|
document.removeEventListener('keydown', keyListener);
|
|
document.removeEventListener('keyup', keyListener);
|
|
canvas.onclick = canvasClickHandler;
|
|
};
|
|
document.addEventListener('pointerdown', clickListener);
|
|
document.addEventListener('pointerlockchange', changeListener);
|
|
document.addEventListener('pointermove', moveListener);
|
|
document.addEventListener('keydown', keyListener);
|
|
document.addEventListener('keyup', keyListener);
|
|
};
|
|
canvas.onclick = canvasClickHandler;
|
|
document.addEventListener('keydown', e => {
|
|
if (e.repeat) return;
|
|
switch (e.code) {
|
|
case 'F11':
|
|
canvas.requestFullscreen();
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
function handleInput(context) {
|
|
const move = (forward, right) => {
|
|
const dir = [right, 0, -forward, 1.0];
|
|
const ori = se3.roty(context.camera.orientation[1]);
|
|
const tf = se3.apply(ori, dir);
|
|
const maxSpeed = 8;
|
|
const airMovement = 0.08;
|
|
|
|
if (context.flying) {
|
|
context.camera.position[0] += tf[0] / 60;
|
|
context.camera.position[2] += tf[2] / 60;
|
|
}
|
|
if (context.isOnGround) {
|
|
context.camera.velocity[0] = tf[0];
|
|
context.camera.velocity[2] = tf[2];
|
|
} else {
|
|
const vel = context.camera.velocity;
|
|
|
|
vel[0] += tf[0] * airMovement;
|
|
vel[2] += tf[2] * airMovement;
|
|
|
|
const curVel = Math.sqrt(vel[0] * vel[0] + vel[2] * vel[2]);
|
|
|
|
if (curVel > maxSpeed) {
|
|
vel[0] *= maxSpeed / curVel;
|
|
vel[2] *= maxSpeed / curVel;
|
|
}
|
|
}
|
|
};
|
|
|
|
context.keys.forEach(key => {
|
|
switch (key) {
|
|
case 'KeyW':
|
|
move(8, 0.0);
|
|
return;
|
|
case 'KeyA':
|
|
move(0.0, -8);
|
|
return;
|
|
case 'KeyS':
|
|
move(-8, 0.0);
|
|
return;
|
|
case 'KeyD':
|
|
move(0.0, 8);
|
|
return;
|
|
|
|
case 'Space':
|
|
if (context.flying) {
|
|
context.camera.position[1] += 8 / 60;
|
|
}
|
|
return;
|
|
|
|
case 'ShiftLeft':
|
|
context.camera.position[1] -= 8 / 60;
|
|
return;
|
|
}
|
|
});
|
|
}
|
|
|
|
function updatePhysics(time, context) {
|
|
}
|
|
|
|
function updateGeometry(context, timeout_ms = 10) {
|
|
}
|
|
|
|
function normalizeAngle(theta) {
|
|
const twopi = 2 * Math.PI;
|
|
return theta - twopi * Math.floor((theta + twopi) / twopi);
|
|
}
|
|
|
|
/** Let's be honest I should clean this up.
|
|
*
|
|
* This is the part that solves Kepler's equation using Newton's method.
|
|
* For circular-ish orbits, one or two iterations are usually enough.
|
|
* More excentric orbits can take more (6 or 7?).
|
|
*
|
|
* For near-parabolic orbits (and some others?) it often fails to converge...
|
|
*/
|
|
function getCartesianState(orbit, mu, time) {
|
|
const {
|
|
excentricity: e,
|
|
semimajorAxis: a,
|
|
inclination: i,
|
|
ascendingNodeLongitude: Om,
|
|
periapsisArgument: w,
|
|
t0,
|
|
} = orbit;
|
|
let n = Math.sqrt(mu/(a**3));
|
|
if (a < 0) {
|
|
n = Math.sqrt(mu/-(a**3)); // mean motion
|
|
}
|
|
const M = n * (time - t0); // mean anomaly
|
|
|
|
// Newton's method
|
|
var E2 = 0;
|
|
var E = orbit.lastE || M;
|
|
let iterations = 0;
|
|
// a clever guess? https://link.springer.com/article/10.1023/A:1008200607490
|
|
// doesn't work at all.
|
|
|
|
while (Math.abs(E - E2) > 1e-10) {
|
|
if (e < 0.001) {
|
|
break;
|
|
}
|
|
E = E2;
|
|
if (e < 1) {
|
|
E2 = E - (E - e * Math.sin(E) - M) / (1 - e * Math.cos(E));
|
|
} else if (e > 1) {
|
|
E2 = E - (-E + e * Math.sinh(E) - M) / (e * Math.cosh(E) - 1);
|
|
} else {
|
|
E2 = E - (E + E*E*E/3 - M) / (1 + E*E);
|
|
}
|
|
|
|
iterations++;
|
|
if (iterations > 100) {
|
|
console.log('numerical instability');
|
|
return {};
|
|
}
|
|
}
|
|
orbit.lastE = E;
|
|
let nu;
|
|
if (e > 1) {
|
|
nu = 2 * Math.atan(Math.sqrt((e+1) / (e-1)) * Math.tanh(E/2));
|
|
} else {
|
|
nu = 2 * Math.atan(Math.sqrt((1+e) / (1-e)) * Math.tan(E/2));
|
|
}
|
|
const p = a * (1 - e**2);
|
|
const r = p / (1 + e * Math.cos(nu));// * ((a < 0) ? -1 : 1);
|
|
const rd = e * Math.sqrt(mu / p) * Math.sin(nu);
|
|
|
|
if (orbit.tf === undefined) {
|
|
orbit.tf = [se3.rotz(Om), se3.rotx(i), se3.rotz(w)].reduce(se3.product);
|
|
}
|
|
|
|
const tf = se3.product(orbit.tf, se3.rotz(nu));
|
|
const pos = se3.apply(tf, [r, 0, 0, 1]);
|
|
const vel = se3.apply(tf, [rd, Math.sqrt(p * mu) / r, 0, 1]);
|
|
|
|
return {
|
|
position: pos.slice(0, 3),
|
|
velocity: vel.slice(0, 3),
|
|
};
|
|
}
|
|
|
|
function getOrientation(body, time) {
|
|
return se3.rotxyz(
|
|
body.spin[0] * time,
|
|
body.spin[1] * time,
|
|
body.spin[2] * time,
|
|
);
|
|
}
|
|
|
|
function makeOrbitObject(context, orbit, parentPosition) {
|
|
const {gl} = context;
|
|
const position = parentPosition;
|
|
const glContext = context.orbitGlContext;
|
|
const orientation = [
|
|
se3.rotz(orbit.ascendingNodeLongitude),
|
|
se3.roty(-orbit.inclination),
|
|
se3.rotz(orbit.periapsisArgument),
|
|
].reduce(se3.product);
|
|
|
|
const buffer = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
|
|
const a = orbit.semimajorAxis;
|
|
const b = a * Math.sqrt(1 - orbit.excentricity**2);
|
|
|
|
const x = - orbit.semimajorAxis * orbit.excentricity;
|
|
|
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
|
x-a, -b, 0, -1, -1,
|
|
x-a, +b, 0, -1, +1,
|
|
x+a, -b, 0, +1, -1,
|
|
x+a, +b, 0, +1, +1,
|
|
]), gl.STATIC_DRAW);
|
|
|
|
const geometry = {
|
|
glBuffer: buffer,
|
|
numVertices: 4,
|
|
delete: () => gl.deleteBuffer(buffer),
|
|
};
|
|
|
|
return {
|
|
geometry,
|
|
orientation,
|
|
position,
|
|
glContext,
|
|
};
|
|
}
|
|
|
|
function getObjects(context, body, time, parentBody, parentPosition) {
|
|
const kGravitationalConstant = 6.674e-11;
|
|
const objects = [];
|
|
const {gl, glContext} = context;
|
|
let position = [0, 0, 0];
|
|
if (parentBody !== undefined) {
|
|
// const mu = kGravitationalConstant * parentBody.mass;
|
|
const mu = 10;
|
|
const coord = getCartesianPosition(body.orbit, mu, time);
|
|
position = [
|
|
parentPosition[0] + coord[0],
|
|
parentPosition[1] + coord[1],
|
|
parentPosition[2] + coord[2],
|
|
];
|
|
|
|
objects.push(makeOrbitObject(context, body.orbit, parentPosition));
|
|
}
|
|
|
|
objects.push({
|
|
geometry: makeBufferFromFaces(gl, body.geometry),
|
|
orientation: getOrientation(body, time),
|
|
position,
|
|
glContext,
|
|
});
|
|
|
|
if (body.children !== undefined) {
|
|
for (const child of body.children) {
|
|
objects.push(...getObjects(context, child, time, body, position));
|
|
}
|
|
}
|
|
|
|
return objects;
|
|
}
|
|
|
|
function draw(context, time) {
|
|
const {gl, camera, universe} = context;
|
|
const objects = getObjects(context, universe, time * 0.001);
|
|
|
|
gl.clearColor(...context.skyColor, 1.0);
|
|
gl.clearDepth(1.0);
|
|
gl.enable(gl.DEPTH_TEST);
|
|
gl.depthFunc(gl.LEQUAL);
|
|
|
|
gl.enable(gl.CULL_FACE);
|
|
gl.cullFace(gl.BACK);
|
|
gl.enable(gl.BLEND);
|
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
|
|
const camrot = camera.orientation;
|
|
const campos = camera.position;
|
|
const viewMatrix = se3.product(
|
|
se3.rotxyz(-camrot[0], -camrot[1], -camrot[2]),
|
|
se3.translation(-campos[0], -campos[1], -campos[2])
|
|
);
|
|
|
|
let lastGlContext;
|
|
|
|
for (const {position, orientation, geometry, glContext} of objects) {
|
|
if (glContext !== lastGlContext) {
|
|
glContext.setupScene({
|
|
projectionMatrix: context.projMatrix,
|
|
viewMatrix,
|
|
fogColor: context.skyColor,
|
|
lightDirection: context.lightDirection,
|
|
ambiantLightAmount: context.ambiantLight,
|
|
});
|
|
}
|
|
|
|
lastGlContext = glContext;
|
|
|
|
glContext.drawObject({
|
|
position,
|
|
orientation,
|
|
glBuffer: geometry.glBuffer,
|
|
numVertices: geometry.numVertices,
|
|
});
|
|
}
|
|
}
|
|
|
|
function tick(time, context) {
|
|
handleInput(context);
|
|
updatePhysics(time, context);
|
|
|
|
const campos = context.camera.position;
|
|
|
|
// world generation / geometry update
|
|
{
|
|
// frame time is typically 16.7ms, so this may lag a bit
|
|
let timeLeft = 10;
|
|
const start = performance.now();
|
|
updateGeometry(context, timeLeft);
|
|
}
|
|
|
|
draw(context, time);
|
|
|
|
const dt = (time - context.lastFrameTime) * 0.001;
|
|
context.lastFrameTime = time;
|
|
|
|
document.querySelector('#fps').textContent = `${1.0 / dt} fps`;
|
|
|
|
requestAnimationFrame(time => tick(time, context));
|
|
}
|
|
|
|
function makeCube(texture) {
|
|
return [
|
|
makeFace('-x', texture, [-0.5, 0, 0]),
|
|
makeFace('+x', texture, [+0.5, 0, 0]),
|
|
makeFace('-y', texture, [0, -0.5, 0]),
|
|
makeFace('+y', texture, [0, +0.5, 0]),
|
|
makeFace('-z', texture, [0, 0, -0.5]),
|
|
makeFace('+z', texture, [0, 0, +0.5]),
|
|
];
|
|
}
|
|
|
|
function makeObjects(gl) {
|
|
const texture = [0, 4];
|
|
const faces = [
|
|
makeFace('-x', texture, [-0.5, 0, 0]),
|
|
makeFace('+x', texture, [+0.5, 0, 0]),
|
|
makeFace('-y', texture, [0, -0.5, 0]),
|
|
makeFace('+y', texture, [0, +0.5, 0]),
|
|
makeFace('-z', texture, [0, 0, -0.5]),
|
|
makeFace('+z', texture, [0, 0, +0.5]),
|
|
];
|
|
return [
|
|
{
|
|
geometry: makeBufferFromFaces(gl, faces),
|
|
orientation: [0, 0, 0],
|
|
position: [0, 0, 0],
|
|
},
|
|
];
|
|
}
|
|
|
|
function makeSolarSystem(gl) {
|
|
return {
|
|
mass: 1.0,
|
|
spin: [0, 1.0, 0],
|
|
geometry: makeCube([0, 4]),
|
|
children: [
|
|
{
|
|
mass: 0.1,
|
|
spin: [0.2, 0.0, 0.0],
|
|
geometry: makeCube([0, 8]),
|
|
orbit: {
|
|
excentricity: 0,
|
|
semimajorAxis: 3,
|
|
inclination: 0,
|
|
ascendingNodeLongitude: 0,
|
|
periapsisArgument: 0,
|
|
trueAnomaly: 0,
|
|
},
|
|
},
|
|
{
|
|
mass: 0.1,
|
|
spin: [0.2, 0.0, 0.0],
|
|
geometry: makeCube([0, 1]),
|
|
orbit: {
|
|
excentricity: 0.8,
|
|
semimajorAxis: 5,
|
|
inclination: 0,
|
|
ascendingNodeLongitude: 0,
|
|
periapsisArgument: 0,
|
|
trueAnomaly: 0,
|
|
},
|
|
},
|
|
{
|
|
mass: 0.1,
|
|
spin: [0.0, 0.0, 1.0],
|
|
geometry: makeCube([9, 9]),
|
|
orbit: {
|
|
excentricity: 0.3,
|
|
semimajorAxis: 5,
|
|
inclination: 1.0,
|
|
ascendingNodeLongitude: 0,
|
|
periapsisArgument: 0,
|
|
trueAnomaly: 0,
|
|
},
|
|
},
|
|
],
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
const canvas = document.querySelector('#game');
|
|
// adjust canvas aspect ratio to that of the screen
|
|
canvas.height = screen.height / screen.width * canvas.width;
|
|
const gl = canvas.getContext('webgl');
|
|
|
|
if (gl === null) {
|
|
console.error('webgl not available')
|
|
return;
|
|
}
|
|
|
|
const context = {
|
|
gl,
|
|
projMatrix: se3.perspective(Math.PI / 3, canvas.clientWidth / canvas.clientHeight, 0.1, 100.0),
|
|
camera: {
|
|
position: [0.0, 0.0, 2.0],
|
|
orientation: [0.0, 0.0, 0.0],
|
|
velocity: [0, 0, 0],
|
|
},
|
|
keys: new Set(),
|
|
lightDirection: [-0.2, -0.5, 0.4],
|
|
skyColor: [0.10, 0.15, 0.2],
|
|
ambiantLight: 0.7,
|
|
blockSelectDistance: 8,
|
|
flying: true,
|
|
isOnGround: false,
|
|
gravity: -17,
|
|
jumpForce: 6.5,
|
|
// objects: makeObjects(gl),
|
|
universe: makeSolarSystem(gl),
|
|
};
|
|
|
|
context.glContext = await initWorldGl(gl);
|
|
context.orbitGlContext = getOrbitDrawContext(gl);
|
|
initUiListeners(canvas, context);
|
|
|
|
// setupParamPanel(context);
|
|
|
|
requestAnimationFrame(time => tick(time, context));
|
|
}
|
|
|
|
window.onload = main;
|