A few things

- webgl boilerplate
- shader ambiant + diffuse lighting
- fractal perlin noise chunk generation
- super primitive world generation (stone, dirt, grass blocks)
- WASD controls + mouse pointer capture

On the menu:
- collisions
- gravity
- mine & place
- more stuff
This commit is contained in:
Paul Mathieu 2021-12-08 09:24:19 -08:00
commit 49e814d1bc
9 changed files with 835 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules/

127
geometry.js Normal file
View File

@ -0,0 +1,127 @@
import * as se3 from './se3';
/** Makes a square with constant z = 0.
*
* Face is oriented towards z+.
*/
function makeSquare() {
const vertices = [
// triangle 0
0.5, 0.5 , 0.0,
-0.5, 0.5, 0.0,
-0.5, -0.5, 0.0,
// triangle 1
0.5, 0.5, 0.0,
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
];
// normals toward z+
const normals = [].concat(...Array(6).fill([0.0, 0.0, 1.0]));
const textures = [
0.99, 0.99,
0.01, 0.99,
0.01, 0.01,
0.99, 0.99,
0.01, 0.01,
0.99, 0.01,
];
return {
vertices,
normals,
textures,
};
}
function makeZFace(normal, texture, transform) {
const textMul = 0.0625;
const textOff = texture.map(x => x * textMul);
const vertices = [];
const textures = [];
const sq = makeSquare();
for (let i = 0; i < 6; i++) {
vertices.push(
se3.product(
transform,
sq.vertices.slice(3 * i, 3 * (i + 1)).concat([1.0])
).slice(0, 3));
textures.push(sq.textures.slice(2 * i, 2 * (i + 1)).map((v, j) => textMul * v + textOff[j]));
}
const normals = [].concat(...Array(6).fill(normal));
return {
vertices: [].concat(...vertices),
normals,
textures: [].concat(...textures),
};
}
export function makeFace(which, texture, centerPos) {
const rot = {
'-x': se3.roty(-Math.PI / 2),
'+x': se3.roty(Math.PI / 2),
'-y': se3.rotx(Math.PI / 2),
'+y': se3.rotx(-Math.PI / 2),
'-z': se3.roty(Math.PI),
'+z': se3.identity(),
}[which];
const normal = {
'+x': [1.0, 0.0, 0.0],
'-x': [-1.0, 0.0, 0.0],
'+y': [0.0, 1.0, 0.0],
'-y': [0.0, -1.0, 0.0],
'+z': [0.0, 0.0, 1.0],
'-z': [0.0, 0.0, -1.0],
}[which];
const tf = se3.product(se3.translation(...centerPos), rot);
return makeZFace(normal, texture, tf);
}
export function makeBuffersFromFraces(gl, faces) {
vertices = [].concat(...faces.map(f => f.vertices));
normals = [].concat(...faces.map(f => f.normals));
textures = [].concat(...faces.map(f => f.textures));
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
const textureBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, textureBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textures), gl.STATIC_DRAW);
return {
vertexBuffer,
normalBuffer,
textureBuffer,
numVertices: vertices.length / 3,
};
}
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);
}

49
gl.js Normal file
View 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;
}

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<html>
<head>
<title>WebMinecraft</title>
<script src="app.js"></script>
</head>
<body>
<canvas id="game" width="1024" height="768"></canvas>
<div id="fps"></div>
</body>
</html>

420
index.js Normal file
View File

@ -0,0 +1,420 @@
import { makeBuffersFromFraces, makeFace, makeGrassCube } from "./geometry";
import { loadTexture, makeProgram } from "./gl";
import * as se3 from './se3';
const TEST_VSHADER = `
attribute vec3 aPosition;
attribute vec3 aNormal;
attribute vec2 aTextureCoord;
uniform mat4 uProjection;
uniform mat4 uModel;
uniform mat4 uView;
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(modelview) * aNormal;
lowp vec3 lightDirection = - normalize(mat3(uView) * vec3(1.0, -0.3, -0.8));
lowp float diffuseAmount = max(dot(lightDirection, normal), 0.0);
lowp vec3 ambiant = vec3(0.7, 0.7, 0.2);
vLighting = ambiant + vec3(1.0, 1.0, 1.0) * diffuseAmount;
vTextureCoord = aTextureCoord;
vDistance = length(modelview * vec4(aPosition, 1.0));
}
`;
const TEST_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);
lowp float fogamount = smoothstep(30.0, 90.0, vDistance);
gl_FragColor = vec4(mix(vLighting * color.rgb, uFogColor, fogamount), color.a);
}
`;
/** Draw.
*
* @param {WebGLRenderingContext} gl
*/
function draw(gl, params, objects) {
gl.clearColor(0.6, 0.8, 1.0, 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.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const camrot = params.camera.orientation;
const campos = params.camera.position;
const viewMat = se3.product(
se3.rotxyz(-camrot[0], -camrot[1], -camrot[2]),
se3.translation(-campos[0], -campos[1], -campos[2])
);
for (const {program, texture, position, orientation, geometry} of objects) {
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 positionLoc = gl.getAttribLocation(program, 'aPosition');
const normalLoc = gl.getAttribLocation(program, 'aNormal');
const textureLoc = gl.getAttribLocation(program, 'aTextureCoord');
const mvMatrix = se3.product(se3.translation(...position), se3.rotxyz(...orientation));
gl.useProgram(program);
gl.uniformMatrix4fv(viewLoc, false, new Float32Array(viewMat));
gl.uniformMatrix4fv(modelLoc, false, new Float32Array(mvMatrix));
gl.uniformMatrix4fv(projLoc, false, new Float32Array(params.projMatrix));
gl.uniform3f(fogColorLoc, 0.6, 0.8, 1.0);
gl.bindBuffer(gl.ARRAY_BUFFER, geometry.vertexBuffer);
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, geometry.normalBuffer);
gl.vertexAttribPointer(normalLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(normalLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, geometry.textureBuffer);
gl.vertexAttribPointer(textureLoc, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(textureLoc);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(samplerLoc, 0);
gl.drawArrays(gl.TRIANGLES, 0, geometry.numVertices);
}
}
const stuff = {
lastFrameTime: 0,
};
function handleKeys(params) {
const move = (forward, right) => {
const dir = [right, 0, -forward, 1.0];
const camori = params.camera.orientation;
const ori = se3.rotxyz(...camori);
const tf = se3.apply(ori, dir);
params.camera.position[0] += tf[0];
params.camera.position[2] += tf[2];
};
params.keys.forEach(key => {
switch (key) {
case 'KeyW':
move(0.1, 0.0);
return;
case 'KeyA':
move(0.0, -0.1);
return;
case 'KeyS':
move(-0.1, 0.0);
return;
case 'KeyD':
move(0.0, 0.1);
return;
case 'Space':
params.camera.position[1] += 0.1;
return;
case 'ControlLeft':
params.camera.position[1] -= 0.1;
return;
}
});
}
function tick(time, gl, params, objects) {
draw(gl, params, objects);
handleKeys(params);
const dt = (time - stuff.lastFrameTime) * 0.001;
stuff.lastFrameTime = time;
document.querySelector('#fps').textContent = `${1.0 / dt} fps`;
requestAnimationFrame(time => tick(time, gl, params, objects));
}
const BlockType = {
AIR: 1,
DIRT: 2,
GRASS: 3,
STONE: 4,
};
function ghettoPerlinNoise(seed, x, y, gridSize = 16) {
const dot = (vx, vy) => vx[0] * vy[0] + vx[1] * vy[1];
// super ghetto random
const xorshift = (x) => {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
return x;
};
const randGrad = (x0, y0) => {
const rand = xorshift(1337 * x0 + seed + 80085 * y0);
return [Math.sin(rand), Math.cos(rand)];
};
const interpol = (a, b, x) => a + (x*x*(3 - 2*x)) * (b - a);
const x0 = Math.floor(x / gridSize);
const y0 = Math.floor(y / gridSize);
const sx = x / gridSize - x0;
const sy = y / gridSize - y0;
const n0 = dot(randGrad(x0, y0), [sx, sy]);
const n1 = dot(randGrad(x0 + 1, y0), [sx - 1, sy]);
const n2 = dot(randGrad(x0, y0 + 1), [sx, sy - 1]);
const n3 = dot(randGrad(x0 + 1, y0 + 1), [sx - 1, sy - 1]);
return interpol(interpol(n0, n1, sx), interpol(n2, n3, sx), sy);
}
function makeTerrain(x, y) {
const seed = 1337;
const fractalNoise = (x, y) => (
ghettoPerlinNoise(seed, x, y, 8) * 0.06
+ ghettoPerlinNoise(seed, x, y, 16) * 0.125
+ ghettoPerlinNoise(seed, x, y, 32) * 0.25
+ ghettoPerlinNoise(seed, x, y, 64) * 0.5
);
const terrain = Array(16);
for (let i = 0; i < 16; i++) {
for (let j = 0; j < 16; j++) {
terrain[i * 16 + j] = fractalNoise(x + i, y + j);
}
}
return terrain;
}
function makeChunk(x, y) {
const terrain = makeTerrain(x, y);
const chunk = new Uint8Array(16 * 16 * 256);
for (let i = 0; i < 16; i++) {
for (let j = 0; j < 16; j++) {
const height = Math.floor(64 + 64 * terrain[i * 16 + j]);
// everything above is air
// that block is grass
// everything below is dirt
const offset = i * (16 * 256) + j * 256;
const stoneHeight = Math.min(62, height);
chunk.set(Array(stoneHeight).fill(BlockType.STONE), offset);
if (stoneHeight < height) {
chunk.set(Array(height - 1 - stoneHeight).fill(BlockType.DIRT), offset + stoneHeight);
chunk[offset + height - 1] = BlockType.GRASS;
}
chunk.set(Array(256 - height).fill(BlockType.AIR), offset + height);
}
}
return chunk;
}
function makeChunkMesh(chunk, z0, x0) {
const blockPos = i => {
const x = Math.floor(i / (16 * 256));
const y = Math.floor((i - 16 * 258 * x) / 256);
const z = i - 256 * (16*x + y);
return [x, y, z];
};
const blockIndex = (i, j, k) => 256 * (i*16 +j) + k;
const sideNeighbors = (i, j) => {
return [
{pos: [i - 1, j], dir: '-z'},
{pos: [i + 1, j], dir: '+z'},
{pos: [i, j - 1], dir: '-x'},
{pos: [i, j + 1], dir: '+x'},
].filter(v => v.pos.every(i => i >= 0 && i < 16));
};
const faceDir = (dir, x, y, z) => {
return {
'-x': [x - 0.5, y, z],
'+x': [x + 0.5, y, z],
'-z': [x, y, z - 0.5],
'+z': [x, y, z + 0.5],
}[dir];
};
const faces = [];
for (let i = 0; i < 16; i++) {
for (let j = 0; j < 16; j++) {
for (let k = 0; k < 256; k++) {
const bi = blockIndex(i, j, k);
const upTexture = (() => {
switch (chunk[bi - 1]) {
case BlockType.GRASS: return [0, 15];
case BlockType.DIRT: return [2, 15];
case BlockType.STONE: return [3, 15];
}
})();
if (chunk[bi] == BlockType.AIR) {
faces.push(makeFace('+y', upTexture, [x0 + j, k - 0.5, z0 + i]));
break;
}
const sideTexture = (() => {
switch (chunk[bi]) {
case BlockType.GRASS: return [1, 15];
case BlockType.DIRT: return [2, 15];
case BlockType.STONE: return [3, 15];
}
})();
for ({ pos, dir } of sideNeighbors(i, j)) {
if (chunk[blockIndex(...pos, k)] == BlockType.AIR) {
faces.push(makeFace(dir, sideTexture, faceDir(dir, x0 + j, k, z0 + i)));
}
}
if (i == 0) {
faces.push(makeFace('-z', sideTexture, faceDir('-z', x0 + j, k, z0 + i)));
}
if (i == 15) {
faces.push(makeFace('+z', sideTexture, faceDir('+z', x0 + j, k, z0 + i)));
}
if (j == 0) {
faces.push(makeFace('-x', sideTexture, faceDir('-x', x0 + j, k, z0 + i)));
}
if (j == 15) {
faces.push(makeFace('+x', sideTexture, faceDir('+x', x0 + j, k, z0 + i)));
}
}
}
}
return faces;
}
function makeWorldBuffers(gl) {
const chunkFaces = [].concat(
makeChunkMesh(makeChunk(0, -16), 0, -16),
makeChunkMesh(makeChunk(16, -16), 16, -16),
makeChunkMesh(makeChunk(0, 0), 0, 0),
makeChunkMesh(makeChunk(16, 0), 16, 0),
makeChunkMesh(makeChunk(16, 16), 16, 16),
makeChunkMesh(makeChunk(0, 16), 0, 16),
);
return makeBuffersFromFraces(gl, chunkFaces);
}
// Stuff I need to do:
// [x] a skybox
// [x] a movable camera
// [ ] some kind of gravity
// [ ] collision detection
// [x] more blocks
// [ ] ability to mine & place
// [ ] generating & loading of more chunks
// [x] distance fog
// [ ] different biomes (with different noise stats)
// [ ] non-flowy water
// [ ] flowy water
// [ ] ALIGN CHUNK WITH WORLD COORDS
// [x] better controls
async function main() {
const canvas = document.querySelector('#game');
const gl = canvas.getContext('webgl');
if (gl === null) {
console.error('webgl not available')
return;
}
const program = makeProgram(gl, TEST_VSHADER, TEST_FSHADER);
const params = {
projMatrix: se3.perspective(Math.PI / 3, canvas.clientWidth / canvas.clientHeight, 0.1, 100.0),
camera: {
position: [0.0, 70.5, 0.0],
orientation: [0.0, Math.PI, 0.0],
},
keys: new Set()
}
const texture = await loadTexture(gl, 'texture.png');
const objects = [
{
program,
position: [0.0, 0.0, 0.0],
orientation: [0.0, 0.0, 0.0],
geometry: makeWorldBuffers(gl),
texture,
},
];
canvas.onclick = e => {
canvas.requestPointerLock();
const keyListener = e => {
if (e.type === 'keydown') {
params.keys.add(e.code);
} else {
params.keys.delete(e.code);
}
};
const moveListener = e => {
params.camera.orientation[1] -= e.movementX / 500;
params.camera.orientation[0] -= e.movementY / 500;
};
const changeListener = () => {
if (document.pointerLockElement === canvas) {
return;
}
document.removeEventListener('pointerlockchange', changeListener);
document.removeEventListener('pointermove', moveListener);
document.removeEventListener('keydown', keyListener);
document.removeEventListener('keyup', keyListener);
};
document.addEventListener('pointerlockchange', changeListener);
document.addEventListener('pointermove', moveListener);
document.addEventListener('keydown', keyListener);
document.addEventListener('keyup', keyListener);
};
requestAnimationFrame(time => tick(time, gl, params, objects));
}
window.onload = main;

12
package.json Normal file
View File

@ -0,0 +1,12 @@
{
"name": "wmc",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"esbuild": "^0.14.2"
},
"scripts": {
"serve": "esbuild --outfile=app.js index.js --bundle --servedir=."
}
}

102
se3.js Normal file
View File

@ -0,0 +1,102 @@
// 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 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 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,
];
};

BIN
texture.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

111
yarn.lock Normal file
View File

@ -0,0 +1,111 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
esbuild-android-arm64@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.2.tgz#256b7cf2f9d382a2a92a4ff4e13187587c9b7c6a"
integrity sha512-hEixaKMN3XXCkoe+0WcexO4CcBVU5DCSUT+7P8JZiWZCbAjSkc9b6Yz2X5DSfQmRCtI/cQRU6TfMYrMQ5NBfdw==
esbuild-darwin-64@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.2.tgz#891a59ce6bc3aded0265f982469b3eb9571b92f8"
integrity sha512-Uq8t0cbJQkxkQdbUfOl2wZqZ/AtLZjvJulR1HHnc96UgyzG9YlCLSDMiqjM+NANEy7/zzvwKJsy3iNC9wwqLJA==
esbuild-darwin-arm64@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.2.tgz#ab834fffa9c612b2901ca1e77e4695d4d8aa63a2"
integrity sha512-619MSa17sr7YCIrUj88KzQu2ESA4jKYtIYfLU/smX6qNgxQt3Y/gzM4s6sgJ4fPQzirvmXgcHv1ZNQAs/Xh48A==
esbuild-freebsd-64@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.2.tgz#f7fc87a83f02de27d5a48472571efa1a432ae86d"
integrity sha512-aP6FE/ZsChZpUV6F3HE3x1Pz0paoYXycJ7oLt06g0G9dhJKknPawXCqQg/WMyD+ldCEZfo7F1kavenPdIT/SGQ==
esbuild-freebsd-arm64@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.2.tgz#bc8758420431106751f3180293cac0b5bc4ce2ee"
integrity sha512-LSm98WTb1QIhyS83+Po0KTpZNdd2XpVpI9ua5rLWqKWbKeNRFwOsjeiuwBaRNc+O32s9oC2ZMefETxHBV6VNkQ==
esbuild-linux-32@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.2.tgz#0cc2dcd816d6d66e255bc7aeac139b1d04246812"
integrity sha512-8VxnNEyeUbiGflTKcuVc5JEPTqXfsx2O6ABwUbfS1Hp26lYPRPC7pKQK5Dxa0MBejGc50jy7YZae3EGQUQ8EkQ==
esbuild-linux-64@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.2.tgz#c790f739aa75b15c153609ea3457153fbe4db93d"
integrity sha512-4bzMS2dNxOJoFIiHId4w+tqQzdnsch71JJV1qZnbnErSFWcR9lRgpSqWnTTFtv6XM+MvltRzSXC5wQ7AEBY6Hg==
esbuild-linux-arm64@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.2.tgz#96858a1f89ad30274dec780d0e3dd8b5691c6b0c"
integrity sha512-RlIVp0RwJrdtasDF1vTFueLYZ8WuFzxoQ1OoRFZOTyJHCGCNgh7xJIC34gd7B7+RT0CzLBB4LcM5n0LS+hIoww==
esbuild-linux-arm@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.2.tgz#03e193225afa9b1215d2ec6efe8edf0c03eeed6f"
integrity sha512-PaylahvMHhH8YMfJPMKEqi64qA0Su+d4FNfHKvlKes/2dUe4QxgbwXT9oLVgy8iJdcFMrO7By4R8fS8S0p8aVQ==
esbuild-linux-mips64le@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.2.tgz#972f218d2cb5125237376d40ad60a6e5356a782c"
integrity sha512-Fdwrq2roFnO5oetIiUQQueZ3+5soCxBSJswg3MvYaXDomj47BN6oAWMZgLrFh1oVrtWrxSDLCJBenYdbm2s+qQ==
esbuild-linux-ppc64le@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.2.tgz#20b71622ac09142b0e523f633af0829def7fed6b"
integrity sha512-vxptskw8JfCDD9QqpRO0XnsM1osuWeRjPaXX1TwdveLogYsbdFtcuiuK/4FxGiNMUr1ojtnCS2rMPbY8puc5NA==
esbuild-netbsd-64@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.2.tgz#dbd6a25117902ef67aa11d8779dd9c6bca7fbe82"
integrity sha512-I8+LzYK5iSNpspS9eCV9sW67Rj8FgMHimGri4mKiGAmN0pNfx+hFX146rYtzGtewuxKtTsPywWteHx+hPRLDsw==
esbuild-openbsd-64@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.2.tgz#3c5f199eed459b2f88865548394c0b77383d9ca4"
integrity sha512-120HgMe9elidWUvM2E6mMf0csrGwx8sYDqUIJugyMy1oHm+/nT08bTAVXuwYG/rkMIqsEO9AlMxuYnwR6En/3Q==
esbuild-sunos-64@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.2.tgz#900a681db6b76c6a7f60fc28d2bfe5b11698641c"
integrity sha512-Q3xcf9Uyfra9UuCFxoLixVvdigo0daZaKJ97TL2KNA4bxRUPK18wwGUk3AxvgDQZpRmg82w9PnkaNYo7a+24ow==
esbuild-windows-32@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.2.tgz#61e0ba5bd95b277a55d2b997ac4c04dfe2559220"
integrity sha512-TW7O49tPsrq+N1sW8mb3m24j/iDGa4xzAZH4wHWwoIzgtZAYPKC0hpIhufRRG/LA30bdMChO9pjJZ5mtcybtBQ==
esbuild-windows-64@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.2.tgz#6ab59ef721ff75c682a1c8ae0570dabb637abddb"
integrity sha512-Rym6ViMNmi1E2QuQMWy0AFAfdY0wGwZD73BnzlsQBX5hZBuy/L+Speh7ucUZ16gwsrMM9v86icZUDrSN/lNBKg==
esbuild-windows-arm64@0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.2.tgz#aca2a4f83d2f0d1592ad4be832ed0045fc888cda"
integrity sha512-ZrLbhr0vX5Em/P1faMnHucjVVWPS+m3tktAtz93WkMZLmbRJevhiW1y4CbulBd2z0MEdXZ6emDa1zFHq5O5bSA==
esbuild@^0.14.2:
version "0.14.2"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.2.tgz#9c1e1a652549cc33e44885eea42ea2cc6267edc2"
integrity sha512-l076A6o/PIgcyM24s0dWmDI/b8RQf41uWoJu9I0M71CtW/YSw5T5NUeXxs5lo2tFQD+O4CW4nBHJXx3OY5NpXg==
optionalDependencies:
esbuild-android-arm64 "0.14.2"
esbuild-darwin-64 "0.14.2"
esbuild-darwin-arm64 "0.14.2"
esbuild-freebsd-64 "0.14.2"
esbuild-freebsd-arm64 "0.14.2"
esbuild-linux-32 "0.14.2"
esbuild-linux-64 "0.14.2"
esbuild-linux-arm "0.14.2"
esbuild-linux-arm64 "0.14.2"
esbuild-linux-mips64le "0.14.2"
esbuild-linux-ppc64le "0.14.2"
esbuild-netbsd-64 "0.14.2"
esbuild-openbsd-64 "0.14.2"
esbuild-sunos-64 "0.14.2"
esbuild-windows-32 "0.14.2"
esbuild-windows-64 "0.14.2"
esbuild-windows-arm64 "0.14.2"