Collisions, gravity, walking, jumping.
This commit is contained in:
parent
9a859ac6c2
commit
44f484896f
13
geometry.js
13
geometry.js
@ -174,17 +174,4 @@ export function makeBufferFromFaces(gl, faces) {
|
|||||||
numVertices,
|
numVertices,
|
||||||
delete: () => gl.deleteBuffer(glBuffer),
|
delete: () => gl.deleteBuffer(glBuffer),
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function makeGrassCube(gl) {
|
|
||||||
faces = [
|
|
||||||
makeFace('+y', [0, 15], [0.0, 0.5, 0.0]),
|
|
||||||
makeFace('-y', [2, 15], [0.0, -0.5, 0.0]),
|
|
||||||
makeFace('-x', [1, 15], [-0.5, 0.0, 0.0]),
|
|
||||||
makeFace('+x', [1, 15], [0.5, 0.0, 0.0]),
|
|
||||||
makeFace('-z', [1, 15], [0.0, 0.0, -0.5]),
|
|
||||||
makeFace('+z', [1, 15], [0.0, 0.0, 0.5]),
|
|
||||||
];
|
|
||||||
|
|
||||||
return makeBuffersFromFraces(gl, faces);
|
|
||||||
}
|
}
|
153
index.js
153
index.js
@ -1,4 +1,4 @@
|
|||||||
import { makeBufferFromFaces, makeFace, makeGrassCube } from "./geometry";
|
import { makeBufferFromFaces, makeFace} from "./geometry";
|
||||||
import { loadTexture, makeProgram } from "./gl";
|
import { loadTexture, makeProgram } from "./gl";
|
||||||
import * as se3 from './se3';
|
import * as se3 from './se3';
|
||||||
|
|
||||||
@ -112,6 +112,8 @@ function draw(gl, params, objects) {
|
|||||||
gl.uniform1i(samplerLoc, 0);
|
gl.uniform1i(samplerLoc, 0);
|
||||||
|
|
||||||
gl.drawArrays(gl.TRIANGLES, 0, geometry.numVertices);
|
gl.drawArrays(gl.TRIANGLES, 0, geometry.numVertices);
|
||||||
|
// gl.bindTexture(gl.TEXTURE_2D, null);
|
||||||
|
// gl.drawArrays(gl.LINE_STRIP, 0, geometry.numVertices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,8 +128,24 @@ function handleKeys(params) {
|
|||||||
const ori = se3.rotxyz(...camori);
|
const ori = se3.rotxyz(...camori);
|
||||||
const tf = se3.apply(ori, dir);
|
const tf = se3.apply(ori, dir);
|
||||||
|
|
||||||
params.camera.position[0] += tf[0];
|
if (params.flying) {
|
||||||
params.camera.position[2] += tf[2];
|
params.camera.position[0] += tf[0];
|
||||||
|
params.camera.position[2] += tf[2];
|
||||||
|
}
|
||||||
|
if (params.isOnGround) {
|
||||||
|
params.camera.velocity[0] = tf[0];
|
||||||
|
params.camera.velocity[2] = tf[2];
|
||||||
|
} else {
|
||||||
|
params.camera.velocity[0] += tf[0] / 60;
|
||||||
|
params.camera.velocity[2] += tf[2] / 60;
|
||||||
|
|
||||||
|
if (Math.abs(params.camera.velocity[0]) > Math.abs(tf[0])) {
|
||||||
|
params.camera.velocity[0] = tf[0];
|
||||||
|
}
|
||||||
|
if (Math.abs(params.camera.velocity[2]) > Math.abs(tf[2])) {
|
||||||
|
params.camera.velocity[2] = tf[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
params.keys.forEach(key => {
|
params.keys.forEach(key => {
|
||||||
@ -146,7 +164,14 @@ function handleKeys(params) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case 'Space':
|
case 'Space':
|
||||||
params.camera.position[1] += 0.1;
|
if(params.flying) {
|
||||||
|
params.camera.position[1] += 0.1;
|
||||||
|
} else {
|
||||||
|
if (params.jumpAmount > 0) {
|
||||||
|
params.camera.velocity[1] = 5.0 / 60;
|
||||||
|
params.jumpAmount -= 5.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
case 'ControlLeft':
|
case 'ControlLeft':
|
||||||
params.camera.position[1] -= 0.1;
|
params.camera.position[1] -= 0.1;
|
||||||
@ -178,8 +203,27 @@ function getObjects(world, z, x, program, texture) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updatePhysics(params) {
|
||||||
|
params.camera.velocity[1] -= 9.8 / 60 / 60;
|
||||||
|
|
||||||
|
const oldPos = params.camera.position;
|
||||||
|
const targetPos = params.flying ? oldPos : params.camera.position.map((v, i) => v + params.camera.velocity[i]);
|
||||||
|
const {isOnGround, newPos} = checkCollision(oldPos, targetPos, params.world);
|
||||||
|
params.camera.position = newPos;
|
||||||
|
params.camera.velocity = newPos.map((v, i) => v - oldPos[i]);
|
||||||
|
if (isOnGround) {
|
||||||
|
params.jumpAmount = 20;
|
||||||
|
params.camera.velocity = params.camera.velocity.map(v => v * 0.7);
|
||||||
|
}
|
||||||
|
params.isOnGround = isOnGround;
|
||||||
|
}
|
||||||
|
|
||||||
function tick(time, gl, params) {
|
function tick(time, gl, params) {
|
||||||
|
handleKeys(params);
|
||||||
|
updatePhysics(params);
|
||||||
|
|
||||||
const campos = params.camera.position;
|
const campos = params.camera.position;
|
||||||
|
|
||||||
// expensive stuff, can take several cycles
|
// expensive stuff, can take several cycles
|
||||||
try {
|
try {
|
||||||
let timeLeft = 10;
|
let timeLeft = 10;
|
||||||
@ -198,8 +242,6 @@ function tick(time, gl, params) {
|
|||||||
const objects = getObjects(params.world, campos[2], campos[0], params.program, params.texture);
|
const objects = getObjects(params.world, campos[2], campos[0], params.program, params.texture);
|
||||||
draw(gl, params, objects);
|
draw(gl, params, objects);
|
||||||
|
|
||||||
handleKeys(params);
|
|
||||||
|
|
||||||
const dt = (time - stuff.lastFrameTime) * 0.001;
|
const dt = (time - stuff.lastFrameTime) * 0.001;
|
||||||
stuff.lastFrameTime = time;
|
stuff.lastFrameTime = time;
|
||||||
|
|
||||||
@ -296,19 +338,31 @@ function makeChunk(z, x) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function blockLookup(world, x, y, z) {
|
function blockLookup(world, x, y, z) {
|
||||||
const chunki = Math.floor(z / 16);
|
const midx = x + 0.5;
|
||||||
const chunkj = Math.floor(x / 16);
|
const midy = y + 0.5;
|
||||||
|
const midz = z + 0.5;
|
||||||
|
|
||||||
|
const chunki = Math.floor(midz / 16);
|
||||||
|
const chunkj = Math.floor(midx / 16);
|
||||||
|
|
||||||
const chunk = world.chunkMap.get(chunki, chunkj);
|
const chunk = world.chunkMap.get(chunki, chunkj);
|
||||||
if (chunk === undefined) {
|
if (chunk === undefined) {
|
||||||
return BlockType.STONE;
|
return {
|
||||||
|
type: BlockType.STONE,
|
||||||
|
x, y, z,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const i = Math.floor(z - chunki * 16);
|
const i = Math.floor(midz - chunki * 16);
|
||||||
const j = Math.floor(x - chunkj * 16);
|
const j = Math.floor(midx - chunkj * 16);
|
||||||
const k = Math.floor(y);
|
const k = Math.floor(midy);
|
||||||
|
|
||||||
return chunk.data[256 * (16*i + j) + k];
|
return {
|
||||||
|
type: chunk.data[256 * (16*i + j) + k],
|
||||||
|
x: j,
|
||||||
|
y: k,
|
||||||
|
z: i,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeChunkBuffer(gl, data, z0, x0, lookup) {
|
function makeChunkBuffer(gl, data, z0, x0, lookup) {
|
||||||
@ -419,7 +473,7 @@ function generateMissingChunks(world, z, x, timeLimit = 10000) {
|
|||||||
invalidateChunkGeometry(world, i + 1, j);
|
invalidateChunkGeometry(world, i + 1, j);
|
||||||
invalidateChunkGeometry(world, i, j - 1);
|
invalidateChunkGeometry(world, i, j - 1);
|
||||||
invalidateChunkGeometry(world, i, j + 1);
|
invalidateChunkGeometry(world, i, j + 1);
|
||||||
|
|
||||||
if (performance.now() - start > timeLimit) {
|
if (performance.now() - start > timeLimit) {
|
||||||
throw 'timesup';
|
throw 'timesup';
|
||||||
}
|
}
|
||||||
@ -462,7 +516,7 @@ function updateWorldGeometry(gl, world, z, x, timeLimit = 10000) {
|
|||||||
|
|
||||||
const chunkz = 16 * i;
|
const chunkz = 16 * i;
|
||||||
const chunkx = 16 * j;
|
const chunkx = 16 * j;
|
||||||
const lookup = (i, j, k) => blockLookup(world, j + chunkx, k, i + chunkz);
|
const lookup = (i, j, k) => blockLookup(world, j + chunkx, k, i + chunkz).type;
|
||||||
|
|
||||||
chunk.buffer = makeChunkBuffer(gl, chunk.data, chunk.position.z, chunk.position.x, lookup);
|
chunk.buffer = makeChunkBuffer(gl, chunk.data, chunk.position.z, chunk.position.x, lookup);
|
||||||
|
|
||||||
@ -477,23 +531,63 @@ function updateWorldGeometry(gl, world, z, x, timeLimit = 10000) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function checkCollision(curPos, newPos, world) {
|
function checkCollision(curPos, newPos, world) {
|
||||||
// [ ] get a BB for the player at newPos
|
// I guess Steve is about 1.7 m tall?
|
||||||
// [ ] check it against world
|
// he also has a 60x60 cm axis-aligned square section '^_^
|
||||||
// [ ] need a struct to access world @ x, y, z
|
// box is centered around the camera
|
||||||
// [ ] need to check all 8 corners of the BB
|
const steveBB = [
|
||||||
// [ ] if it collides, figure out by how much and return a safe newPos
|
[-0.3, 0.2, -0.3],
|
||||||
|
[-0.3, 0.2, 0.3],
|
||||||
|
[0.3, 0.2, -0.3],
|
||||||
|
[0.3, 0.2, 0.3],
|
||||||
|
|
||||||
return safeNewPos;
|
[-0.3, -1.5, -0.3],
|
||||||
|
[-0.3, -1.5, 0.3],
|
||||||
|
[0.3, -1.5, -0.3],
|
||||||
|
[0.3, -1.5, 0.3],
|
||||||
|
];
|
||||||
|
|
||||||
|
const translate = (v, pos) => v.map((el, i) => el + pos[i]);
|
||||||
|
|
||||||
|
let dp = newPos.map((x, i) => x - curPos[i]);
|
||||||
|
let isOnGround = false;
|
||||||
|
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
const newSteve = v => v.map((x, j) => i === j ? x + newPos[j] : x + curPos[j]);
|
||||||
|
for (const point of steveBB.map(newSteve)) {
|
||||||
|
const block = blockLookup(world, ...point);
|
||||||
|
if (block.type !== BlockType.AIR) {
|
||||||
|
if (i === 1 && dp[i] < 0) {
|
||||||
|
isOnGround = true;
|
||||||
|
console.log(`on ground`);
|
||||||
|
}
|
||||||
|
dp[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
const newSteve = v => v.map((x, j) => curPos[j] + x + dp[j]);
|
||||||
|
for (const point of steveBB.map(newSteve)) {
|
||||||
|
const block = blockLookup(world, ...point);
|
||||||
|
if (block.type !== BlockType.AIR) {
|
||||||
|
dp[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
newPos: translate(curPos, dp),
|
||||||
|
isOnGround,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stuff I need to do:
|
// Stuff I need to do:
|
||||||
// [x] a skybox
|
// [x] a skybox
|
||||||
// [x] a movable camera
|
// [x] a movable camera
|
||||||
// [ ] some kind of gravity
|
// [x] some kind of gravity
|
||||||
// [ ] collision detection
|
// [x] collision detection
|
||||||
// [x] more blocks
|
// [x] more blocks
|
||||||
// [ ] ability to mine & place
|
// [ ] ability to mine & place
|
||||||
// [ ] generating & loading of more chunks
|
// [x] generating & loading of more chunks
|
||||||
// [x] distance fog
|
// [x] distance fog
|
||||||
// [ ] different biomes (with different noise stats)
|
// [ ] different biomes (with different noise stats)
|
||||||
// [ ] non-flowy water
|
// [ ] non-flowy water
|
||||||
@ -519,6 +613,7 @@ async function main() {
|
|||||||
camera: {
|
camera: {
|
||||||
position: [0.0, 70.5, 0.0],
|
position: [0.0, 70.5, 0.0],
|
||||||
orientation: [0.0, Math.PI, 0.0],
|
orientation: [0.0, Math.PI, 0.0],
|
||||||
|
velocity: [0, 0, 0],
|
||||||
},
|
},
|
||||||
keys: new Set(),
|
keys: new Set(),
|
||||||
world: makeWorld(),
|
world: makeWorld(),
|
||||||
@ -526,6 +621,8 @@ async function main() {
|
|||||||
program,
|
program,
|
||||||
lightDirection: [-0.2, -0.5, 0.4],
|
lightDirection: [-0.2, -0.5, 0.4],
|
||||||
ambiantLight: 0.7,
|
ambiantLight: 0.7,
|
||||||
|
flying: false,
|
||||||
|
isOnGround: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
document.querySelector('#lightx').oninput = e => {
|
document.querySelector('#lightx').oninput = e => {
|
||||||
@ -558,6 +655,12 @@ async function main() {
|
|||||||
const keyListener = e => {
|
const keyListener = e => {
|
||||||
if (e.type === 'keydown') {
|
if (e.type === 'keydown') {
|
||||||
params.keys.add(e.code);
|
params.keys.add(e.code);
|
||||||
|
|
||||||
|
switch (e.code) {
|
||||||
|
case 'KeyF':
|
||||||
|
params.flying = !params.flying;
|
||||||
|
console.log(`flying: ${params.flying}`)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
params.keys.delete(e.code);
|
params.keys.delete(e.code);
|
||||||
}
|
}
|
||||||
@ -583,4 +686,4 @@ async function main() {
|
|||||||
requestAnimationFrame(time => tick(time, gl, params));
|
requestAnimationFrame(time => tick(time, gl, params));
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onload = main;
|
window.onload = main;
|
||||||
|
Loading…
Reference in New Issue
Block a user