Move a few things around
- common files to a new 'wmc-common' package in common/ - skycraft now puts all serving files into static/
This commit is contained in:
131
common/geometry.js
Normal file
131
common/geometry.js
Normal file
@@ -0,0 +1,131 @@
|
||||
import { memoize } from "./memoize";
|
||||
|
||||
function _makeTextureFace(texture) {
|
||||
const textureWidth = 16;
|
||||
const texturesPerAtlas = 16;
|
||||
const textMul = 1 / texturesPerAtlas;
|
||||
const halfPixel = 1 / texturesPerAtlas / textureWidth / 2;
|
||||
const textOff = texture.map(x => x * textMul + halfPixel);
|
||||
const a = textMul / textureWidth * (textureWidth - 1);
|
||||
const textuv = [
|
||||
[1, 1],
|
||||
[0, 1],
|
||||
[0, 0],
|
||||
[1, 1],
|
||||
[0, 0],
|
||||
[1, 0],
|
||||
];
|
||||
|
||||
const textures = textuv.map(uv => uv.map((x, j) => a * x + textOff[j]));
|
||||
|
||||
return textures;
|
||||
}
|
||||
|
||||
const makeTextureFace = memoize(_makeTextureFace);
|
||||
|
||||
function makePZFace(texture) {
|
||||
return {
|
||||
vertices: [[0.5, 0.5, 0], [-0.5, 0.5, 0], [-0.5, -0.5, 0], [0.5, 0.5, 0], [-0.5, -0.5, 0], [0.5, -0.5, 0]],
|
||||
normals: Array(6).fill([0.0, 0.0, 1.0]),
|
||||
textures: makeTextureFace(texture),
|
||||
};
|
||||
}
|
||||
function makeNZFace(texture) {
|
||||
return {
|
||||
vertices: [[-0.5, 0.5, 0.0], [0.5, 0.5, 0.0], [0.5, -0.5, 0.0], [-0.5, 0.5, 0.0], [0.5, -0.5, 0.0], [-0.5, -0.5, 0.0]],
|
||||
normals: Array(6).fill([0.0, 0.0, -1.0]),
|
||||
textures: makeTextureFace(texture),
|
||||
};
|
||||
}
|
||||
function makePXFace(texture) {
|
||||
return {
|
||||
vertices: [[0, 0.5, -0.5], [0, 0.5, 0.5], [0, -0.5, 0.5], [0, 0.5, -0.5], [0, -0.5, 0.5], [0, -0.5, -0.5]],
|
||||
normals: Array(6).fill([1.0, 0.0, 0.0]),
|
||||
textures: makeTextureFace(texture),
|
||||
};
|
||||
}
|
||||
function makeNXFace(texture) {
|
||||
return {
|
||||
vertices: [[0, 0.5, 0.5], [0, 0.5, -0.5], [0, -0.5, -0.5], [0, 0.5, 0.5], [0, -0.5, -0.5], [0, -0.5, 0.5]],
|
||||
normals: Array(6).fill([-1.0, 0.0, 0.0]),
|
||||
textures: makeTextureFace(texture),
|
||||
};
|
||||
}
|
||||
function makePYFace(texture) {
|
||||
return {
|
||||
vertices: [[0.5, 0, -0.5], [-0.5, 0, -0.5], [-0.5, 0, 0.5], [0.5, 0, -0.5], [-0.5, 0, 0.5], [0.5, 0, 0.5]],
|
||||
normals: Array(6).fill([0.0, 1.0, 0.0]),
|
||||
textures: makeTextureFace(texture),
|
||||
};
|
||||
}
|
||||
function makeNYFace(texture) {
|
||||
return {
|
||||
vertices: [[0.5, 0, 0.5], [-0.5, 0, 0.5], [-0.5, 0, -0.5], [0.5, 0, 0.5], [-0.5, 0, -0.5], [0.5, 0, -0.5]],
|
||||
normals: Array(6).fill([0.0, -1.0, 0.0]),
|
||||
textures: makeTextureFace(texture),
|
||||
};
|
||||
}
|
||||
|
||||
function translateFace(face, x, y, z) {
|
||||
return {
|
||||
normals: face.normals,
|
||||
textures: face.textures,
|
||||
vertices: face.vertices.map(([vx, vy, vz]) => [vx + x, vy + y, vz + z]),
|
||||
};
|
||||
}
|
||||
|
||||
export function makeFace(which, texture, centerPos) {
|
||||
switch(which) {
|
||||
case '-x': return translateFace(makeNXFace(texture), ...centerPos);
|
||||
case '+x': return translateFace(makePXFace(texture), ...centerPos);
|
||||
case '-y': return translateFace(makeNYFace(texture), ...centerPos);
|
||||
case '+y': return translateFace(makePYFace(texture), ...centerPos);
|
||||
case '-z': return translateFace(makeNZFace(texture), ...centerPos);
|
||||
case '+z': return translateFace(makePZFace(texture), ...centerPos);
|
||||
}
|
||||
|
||||
throw Error('unknown face');
|
||||
}
|
||||
|
||||
/** Packs all those faces into one big buffer. */
|
||||
export function makeBufferFromFaces(gl, faces) {
|
||||
const numVertices = faces.map(f => f.vertices).reduce((count, vertices) => count + vertices.length, 0);
|
||||
|
||||
// 3 * float32 + 3 * byte (padded to 4) + 2 * short
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/vertexAttribPointer#examples
|
||||
const vertexSize = 3 * 4 + 4 + 4;
|
||||
|
||||
const glBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, glBuffer);
|
||||
|
||||
const buffer = new Uint8Array(numVertices * vertexSize);
|
||||
const dv = new DataView(buffer.buffer);
|
||||
let offset = 0;
|
||||
|
||||
faces.forEach(face => {
|
||||
for (let i = 0; i < face.vertices.length; i++) {
|
||||
const position = face.vertices[i];
|
||||
dv.setFloat32(offset + 0, position[0], true);
|
||||
dv.setFloat32(offset + 4, position[1], true);
|
||||
dv.setFloat32(offset + 8, position[2], true);
|
||||
offset += 12;
|
||||
const normal = face.normals[i];
|
||||
dv.setInt8(offset + 0, normal[0] * 0x7f);
|
||||
dv.setInt8(offset + 1, normal[1] * 0x7f);
|
||||
dv.setInt8(offset + 2, normal[2] * 0x7f);
|
||||
offset += 4;
|
||||
const texture = face.textures[i];
|
||||
dv.setUint16(offset + 0, texture[0] * 0xffff, true);
|
||||
dv.setUint16(offset + 2, texture[1] * 0xffff, true);
|
||||
offset += 4;
|
||||
}
|
||||
});
|
||||
|
||||
gl.bufferData(gl.ARRAY_BUFFER, buffer, gl.STATIC_DRAW);
|
||||
|
||||
return {
|
||||
glBuffer,
|
||||
numVertices,
|
||||
delete: () => gl.deleteBuffer(glBuffer),
|
||||
};
|
||||
}
|
49
common/gl.js
Normal file
49
common/gl.js
Normal file
@@ -0,0 +1,49 @@
|
||||
function makeShader(gl, type, source) {
|
||||
const shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
throw Error(`Shader compiler error: ${gl.getShaderInfoLog(shader)}`);
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
export function makeProgram(gl, vertexShader, fragmentShader) {
|
||||
const vshader = makeShader(gl, gl.VERTEX_SHADER, vertexShader);
|
||||
const fshader = makeShader(gl, gl.FRAGMENT_SHADER, fragmentShader);
|
||||
|
||||
const shaderProg = gl.createProgram();
|
||||
gl.attachShader(shaderProg, vshader);
|
||||
gl.attachShader(shaderProg, fshader);
|
||||
gl.linkProgram(shaderProg);
|
||||
|
||||
if (!gl.getProgramParameter(shaderProg, gl.LINK_STATUS)) {
|
||||
throw Error(`Shader linker error: ${gl.getProgramInfoLog(shaderProg)}`);
|
||||
}
|
||||
|
||||
return shaderProg;
|
||||
}
|
||||
|
||||
function getImage(url) {
|
||||
return new Promise(resolve => {
|
||||
const image = new Image();
|
||||
image.onload = () => resolve(image);
|
||||
image.src = url;
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadTexture(gl, url) {
|
||||
const image = await getImage(url);
|
||||
const texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
|
||||
return texture;
|
||||
}
|
12
common/memoize.js
Normal file
12
common/memoize.js
Normal file
@@ -0,0 +1,12 @@
|
||||
export function memoize(f) {
|
||||
const memo = {};
|
||||
|
||||
function g(...args) {
|
||||
if (!(args in memo)) {
|
||||
memo[args] = f(...args);
|
||||
}
|
||||
return memo[args];
|
||||
}
|
||||
|
||||
return g;
|
||||
}
|
8
common/package.json
Normal file
8
common/package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "wmc-common",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.14.2"
|
||||
}
|
||||
}
|
139
common/se3.js
Normal file
139
common/se3.js
Normal file
@@ -0,0 +1,139 @@
|
||||
// all column-major, *sigh*
|
||||
export function identity() {
|
||||
return [
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
];
|
||||
}
|
||||
|
||||
export function rotx(rad) {
|
||||
const s = Math.sin(rad);
|
||||
const c = Math.cos(rad);
|
||||
|
||||
return [
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
0.0, c, s, 0.0,
|
||||
0.0, -s, c, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0,
|
||||
];
|
||||
}
|
||||
|
||||
export function roty(rad) {
|
||||
const s = Math.sin(rad);
|
||||
const c = Math.cos(rad);
|
||||
|
||||
return [
|
||||
c, 0.0, -s, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
s, 0.0, c, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0,
|
||||
];
|
||||
}
|
||||
|
||||
export function rotz(rad) {
|
||||
const s = Math.sin(rad);
|
||||
const c = Math.cos(rad);
|
||||
|
||||
return [
|
||||
c, s, 0.0, 0.0,
|
||||
-s, c, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0,
|
||||
];
|
||||
}
|
||||
|
||||
export function apply(mat, vect) {
|
||||
const indices = [0, 1, 2, 3];
|
||||
const sum = v => v.reduce((a, b) => a + b);
|
||||
return [
|
||||
sum(indices.map(i => mat[4*i + 0] * vect[i])),
|
||||
sum(indices.map(i => mat[4*i + 1] * vect[i])),
|
||||
sum(indices.map(i => mat[4*i + 2] * vect[i])),
|
||||
sum(indices.map(i => mat[4*i + 3] * vect[i])),
|
||||
];
|
||||
}
|
||||
|
||||
export function rotxyz(x, y, z) {
|
||||
return [rotx(x), roty(y), rotz(z)].reduce(product);
|
||||
}
|
||||
|
||||
export function rotationOnly(m) {
|
||||
return [
|
||||
m[0], m[1], m[2], 0.0,
|
||||
m[4], m[5], m[6], 0.0,
|
||||
m[8], m[9], m[10], 0.0,
|
||||
0, 0, 0, 1,
|
||||
];
|
||||
}
|
||||
|
||||
export function translation(x, y, z) {
|
||||
return [
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
x, y, z, 1.0,
|
||||
];
|
||||
}
|
||||
|
||||
export function orientationOnly(m) {
|
||||
return [
|
||||
m[0], m[1], m[2], 0,
|
||||
m[4], m[5], m[6], 0,
|
||||
m[8], m[9], m[10], 0,
|
||||
0, 0, 0, 1,
|
||||
];
|
||||
}
|
||||
|
||||
export function inverse(m) {
|
||||
const t = apply(m, [0, 0, 0, 1]);
|
||||
const r = orientationOnly(m);
|
||||
const newR = [
|
||||
m[0], m[4], m[8], 0,
|
||||
m[1], m[5], m[9], 0,
|
||||
m[2], m[6], m[10], 0,
|
||||
0, 0, 0, 1,
|
||||
];
|
||||
const newT = apply(newR, t);
|
||||
const out = newR;
|
||||
|
||||
out[12] = -newT[0];
|
||||
out[13] = -newT[1];
|
||||
out[14] = -newT[2];
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
export function product(a, b) {
|
||||
const c = (i, j) => (
|
||||
a[4 * 0 + i] * b[4 * j + 0] +
|
||||
a[4 * 1 + i] * b[4 * j + 1] +
|
||||
a[4 * 2 + i] * b[4 * j + 2] +
|
||||
a[4 * 3 + i] * b[4 * j + 3]
|
||||
);
|
||||
return [
|
||||
c(0, 0), c(1, 0), c(2, 0), c(3, 0),
|
||||
c(0, 1), c(1, 1), c(2, 1), c(3, 1),
|
||||
c(0, 2), c(1, 2), c(2, 2), c(3, 2),
|
||||
c(0, 3), c(1, 3), c(2, 3), c(3, 3),
|
||||
];
|
||||
}
|
||||
|
||||
export function ortho(aspectRatio) {
|
||||
const out = identity();
|
||||
out[0] = 1 / aspectRatio;
|
||||
return out;
|
||||
}
|
||||
|
||||
export function perspective(fov, aspectRatio, near, far) {
|
||||
const f = 1 / Math.tan(fov / 2);
|
||||
const rangeInv = 1 / (near - far);
|
||||
|
||||
return [
|
||||
f / aspectRatio, 0.0, 0.0, 0.0,
|
||||
0.0, f, 0.0, 0.0,
|
||||
0.0, 0.0, (near + far) * rangeInv, -1,
|
||||
0.0, 0.0, near * far * rangeInv * 2, 0,
|
||||
];
|
||||
};
|
Reference in New Issue
Block a user