mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-14 12:29:30 -07:00
Exploring ways to preserve the vanilla JS browser demo without compromising on the bundle. Experimenting with embedding images in the bundle as data URIs
This commit is contained in:
5
TODO.txt
5
TODO.txt
@@ -1,4 +1,9 @@
|
||||
TODO:
|
||||
TTF --> MSDF
|
||||
Isolate fun stuff from core
|
||||
Separate configs
|
||||
Separate assets
|
||||
|
||||
|
||||
Live config update roadmap
|
||||
Modify regl pass
|
||||
|
||||
92
js/Matrix.js
92
js/Matrix.js
@@ -1,8 +1,87 @@
|
||||
import highPassFrag from "../shaders/glsl/bloomPass.highPass.frag.glsl";
|
||||
import blurFrag from "../shaders/glsl/bloomPass.blur.frag.glsl";
|
||||
import combineFrag from "../shaders/glsl/bloomPass.combine.frag.glsl";
|
||||
import imagePassFrag from "../shaders/glsl/imagePass.frag.glsl";
|
||||
import mirrorPassFrag from "../shaders/glsl/mirrorPass.frag.glsl";
|
||||
import palettePassFrag from "../shaders/glsl/palettePass.frag.glsl";
|
||||
import rainPassIntro from "../shaders/glsl/rainPass.intro.frag.glsl";
|
||||
import rainPassRaindrop from "../shaders/glsl/rainPass.raindrop.frag.glsl";
|
||||
import rainPassSymbol from "../shaders/glsl/rainPass.symbol.frag.glsl";
|
||||
import rainPassEffect from "../shaders/glsl/rainPass.effect.frag.glsl";
|
||||
import rainPassVert from "../shaders/glsl/rainPass.vert.glsl";
|
||||
import rainPassFrag from "../shaders/glsl/rainPass.frag.glsl";
|
||||
import stripePassFrag from "../shaders/glsl/stripePass.frag.glsl";
|
||||
import msdfCoptic from "../assets/coptic_msdf.png";
|
||||
import msdfGothic from "../assets/gothic_msdf.png";
|
||||
import msdfMatrixCode from "../assets/matrixcode_msdf.png";
|
||||
import msdfRes from "../assets/resurrections_msdf.png";
|
||||
// import megacity from "../assets/megacity_msdf.png";
|
||||
import msdfResGlint from "../assets/resurrections_glint_msdf.png";
|
||||
// import msdfHuberfishA from "../assets/huberfish_a_msdf.png";
|
||||
// import msdfHuberfishD from "../assets/huberfish_d_msdf.png";
|
||||
// import msdfGtargTenretni from "../assets/gtarg_tenretniolleh_msdf.png";
|
||||
// import msdfGtargAlienText from "../assets/gtarg_alientext_msdf.png";
|
||||
// import msdfNeoMatrixology from "../assets/neomatrixology_msdf.png";
|
||||
// import texSand from "../assets/sand.png";
|
||||
// import texPixels from "../assets/pixel_grid.png";
|
||||
import texMesh from "../assets/mesh.png";
|
||||
import texMetal from "../assets/metal.png";
|
||||
import bloomBlurShader from "../shaders/wgsl/bloomBlur.wgsl";
|
||||
import bloomCombineShader from "../shaders/wgsl/bloomCombine.wgsl";
|
||||
import endPassShader from "../shaders/wgsl/endPass.wgsl";
|
||||
import imagePassShader from "../shaders/wgsl/imagePass.wgsl";
|
||||
import mirrorPassShader from "../shaders/wgsl/mirrorPass.wgsl";
|
||||
import palettePassShader from "../shaders/wgsl/palettePass.wgsl";
|
||||
import rainPassShader from "../shaders/wgsl/rainPass.wgsl";
|
||||
import stripePassShader from "../shaders/wgsl/stripePass.wgsl";
|
||||
|
||||
const inclusion = [
|
||||
highPassFrag,
|
||||
blurFrag,
|
||||
combineFrag,
|
||||
imagePassFrag,
|
||||
mirrorPassFrag,
|
||||
palettePassFrag,
|
||||
rainPassIntro,
|
||||
rainPassRaindrop,
|
||||
rainPassSymbol,
|
||||
rainPassEffect,
|
||||
rainPassVert,
|
||||
rainPassFrag,
|
||||
stripePassFrag,
|
||||
msdfCoptic,
|
||||
msdfGothic,
|
||||
msdfMatrixCode,
|
||||
msdfRes,
|
||||
// megacity,
|
||||
msdfResGlint,
|
||||
// msdfHuberfishA,
|
||||
// msdfHuberfishD,
|
||||
// msdfGtargTenretni,
|
||||
// msdfGtargAlienText,
|
||||
// msdfNeoMatrixology,
|
||||
// texSand,
|
||||
// texPixels,
|
||||
texMesh,
|
||||
texMetal,
|
||||
bloomBlurShader,
|
||||
bloomCombineShader,
|
||||
endPassShader,
|
||||
imagePassShader,
|
||||
mirrorPassShader,
|
||||
palettePassShader,
|
||||
rainPassShader,
|
||||
stripePassShader,
|
||||
].reduce((i, s) => s.length + i, 0);
|
||||
if (inclusion === 0) console.log("!");
|
||||
|
||||
import React, { useEffect, useState, useRef, memo } from "react";
|
||||
// import { init as initRain, formulate as refreshRain, destroy as destroyRain } from "./regl/main";
|
||||
import { init as initRain, formulate as refreshRain, destroy as destroyRain } from "./webgpu/main";
|
||||
import * as reglRenderer from "./regl/main";
|
||||
import * as webgpuRenderer from "./webgpu/main";
|
||||
import makeConfig from "./utils/config";
|
||||
|
||||
console.log(webgpuRenderer.init, webgpuRenderer.formulate, webgpuRenderer.destroy);
|
||||
|
||||
/**
|
||||
* @typedef {object} Colour
|
||||
* @property {"hsl"|"rgb"} space
|
||||
@@ -18,7 +97,7 @@ import makeConfig from "./utils/config";
|
||||
* "classic" | "megacity" | "neomatrixology" | "operator" |
|
||||
* "nightmare" | "paradise" | "resurrections" | "trinity" |
|
||||
* "morpheus" | "bugs" | "palimpsest" | "twilight" |
|
||||
* "holoplay" | "3d" | "throwback" | "updated" |
|
||||
* "3d" | "throwback" | "updated" |
|
||||
* "1999" | "2003" | "2021" | string /* custom * /
|
||||
* ),
|
||||
* font?: keyof typeof fonts, // "matrixcode", …
|
||||
@@ -36,7 +115,6 @@ import makeConfig from "./utils/config";
|
||||
* renderer?: "regl" | "three" | string,
|
||||
* suppressWarnings?: boolean,
|
||||
* useHalfFloat?: boolean,
|
||||
* useHoloplay?: boolean,
|
||||
* isometric?: boolean,
|
||||
*
|
||||
* /* ------------- glyph appearance ------------- * /
|
||||
@@ -119,12 +197,12 @@ export const Matrix = memo((props) => {
|
||||
canvas.style.width = "100%";
|
||||
canvas.style.height = "100%";
|
||||
const init = async () => {
|
||||
setRain(await initRain(canvas));
|
||||
setRain(await reglRenderer.init(canvas));
|
||||
};
|
||||
init();
|
||||
|
||||
return () => {
|
||||
destroyRain(rain);
|
||||
reglRenderer.destroy(rain);
|
||||
setRain(null);
|
||||
};
|
||||
}, []);
|
||||
@@ -134,7 +212,7 @@ export const Matrix = memo((props) => {
|
||||
return;
|
||||
}
|
||||
const refresh = async () => {
|
||||
await refreshRain(rain, makeConfig({ ...rest }));
|
||||
await reglRenderer.formulate(rain, makeConfig({ ...rest }));
|
||||
};
|
||||
refresh();
|
||||
}, [props, rain]);
|
||||
|
||||
20
js/fetchLibraries.js
Normal file
20
js/fetchLibraries.js
Normal file
@@ -0,0 +1,20 @@
|
||||
export default async () => {
|
||||
let glMatrix, createREGL;
|
||||
|
||||
try {
|
||||
glMatrix = await import("gl-matrix");
|
||||
createREGL = (await import("regl")).default;
|
||||
} catch {
|
||||
const loadJS = (src) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const tag = document.createElement("script");
|
||||
[tag.onload, tag.onerror, tag.src] = [resolve, reject, src];
|
||||
document.body.appendChild(tag);
|
||||
});
|
||||
await Promise.all([loadJS("lib/regl.min.js"), loadJS("lib/gl-matrix.js")]);
|
||||
glMatrix = globalThis.glMatrix;
|
||||
createREGL = globalThis.createREGL;
|
||||
}
|
||||
|
||||
return { glMatrix, createREGL };
|
||||
};
|
||||
@@ -14,7 +14,6 @@ const versions = [
|
||||
"paradise",
|
||||
"resurrections",
|
||||
"operator",
|
||||
"holoplay",
|
||||
"throwback",
|
||||
"updated",
|
||||
"1999",
|
||||
|
||||
12
js/main.js
12
js/main.js
@@ -1,4 +1,4 @@
|
||||
import makeConfig from "./config.js";
|
||||
import makeConfig from "./utils/config.js";
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
document.body.appendChild(canvas);
|
||||
@@ -27,6 +27,12 @@ document.body.onload = async () => {
|
||||
const useWebGPU = (await supportsWebGPU()) && ["webgpu"].includes(config.renderer?.toLowerCase());
|
||||
const solution = import(`./${useWebGPU ? "webgpu" : "regl"}/main.js`);
|
||||
|
||||
const initialize = async (canvas, config) => {
|
||||
const { init, formulate } = await solution;
|
||||
const rain = await init(canvas);
|
||||
await formulate(rain, config);
|
||||
};
|
||||
|
||||
if (isRunningSwiftShader() && !config.suppressWarnings) {
|
||||
const notice = document.createElement("notice");
|
||||
notice.innerHTML = `<div class="notice">
|
||||
@@ -41,11 +47,11 @@ document.body.onload = async () => {
|
||||
config.suppressWarnings = true;
|
||||
urlParams.set("suppressWarnings", true);
|
||||
history.replaceState({}, "", "?" + unescape(urlParams.toString()));
|
||||
(await solution).default(canvas, config);
|
||||
await initialize(canvas, config);
|
||||
canvas.style.display = "unset";
|
||||
document.body.removeChild(notice);
|
||||
});
|
||||
} else {
|
||||
(await solution).default(canvas, config);
|
||||
await initialize(canvas, config);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { makePassFBO, makePass } from "./utils";
|
||||
import highPassFrag from "../../shaders/glsl/bloomPass.highPass.frag.glsl";
|
||||
import blurFrag from "../../shaders/glsl/bloomPass.blur.frag.glsl";
|
||||
import combineFrag from "../../shaders/glsl/bloomPass.combine.frag.glsl";
|
||||
import { loadText, makePassFBO, makePass } from "./utils.js";
|
||||
|
||||
// The bloom pass is basically an added high-pass blur.
|
||||
// The blur approximation is the sum of a pyramid of downscaled, blurred textures.
|
||||
@@ -20,7 +17,7 @@ const resizePyramid = (pyramid, vw, vh, scale) =>
|
||||
fbo.resize(Math.floor((vw * scale) / 2 ** index), Math.floor((vh * scale) / 2 ** index)),
|
||||
);
|
||||
|
||||
export default ({ regl, config }, inputs) => {
|
||||
export default ({ regl, cache, config }, inputs) => {
|
||||
const { bloomStrength, bloomSize, highPassThreshold } = config;
|
||||
const enabled = bloomSize > 0 && bloomStrength > 0;
|
||||
|
||||
@@ -39,6 +36,7 @@ export default ({ regl, config }, inputs) => {
|
||||
const output = makePassFBO(regl, config.useHalfFloat);
|
||||
|
||||
// The high pass restricts the blur to bright things in our input texture.
|
||||
const highPassFrag = loadText(cache, "shaders/glsl/bloomPass.highPass.frag.glsl");
|
||||
const highPass = regl({
|
||||
frag: regl.prop("frag"),
|
||||
uniforms: {
|
||||
@@ -53,6 +51,7 @@ export default ({ regl, config }, inputs) => {
|
||||
// by blurring them all, this basic blur approximates a more complex gaussian:
|
||||
// https://web.archive.org/web/20191124072602/https://software.intel.com/en-us/articles/compute-shader-hdr-and-bloom
|
||||
|
||||
const blurFrag = loadText(cache, "shaders/glsl/bloomPass.blur.frag.glsl");
|
||||
const blur = regl({
|
||||
frag: regl.prop("frag"),
|
||||
uniforms: {
|
||||
@@ -65,6 +64,7 @@ export default ({ regl, config }, inputs) => {
|
||||
});
|
||||
|
||||
// The pyramid of textures gets flattened (summed) into a final blurry "bloom" texture
|
||||
const combineFrag = loadText(cache, "shaders/glsl/bloomPass.combine.frag.glsl");
|
||||
const combine = regl({
|
||||
frag: regl.prop("frag"),
|
||||
uniforms: {
|
||||
@@ -79,7 +79,7 @@ export default ({ regl, config }, inputs) => {
|
||||
primary: inputs.primary,
|
||||
bloom: output,
|
||||
},
|
||||
Promise.all([highPassFrag.loaded, blurFrag.loaded]),
|
||||
Promise.all([highPassFrag.loaded, blurFrag.loaded, combineFrag.loaded]),
|
||||
(w, h) => {
|
||||
// The blur pyramids can be lower resolution than the screen.
|
||||
resizePyramid(highPassPyramid, w, h, bloomSize);
|
||||
@@ -98,14 +98,14 @@ export default ({ regl, config }, inputs) => {
|
||||
const vBlurFBO = vBlurPyramid[i];
|
||||
highPass({
|
||||
fbo: highPassFBO,
|
||||
frag: highPassFrag,
|
||||
frag: highPassFrag.text(),
|
||||
tex: i === 0 ? inputs.primary : highPassPyramid[i - 1],
|
||||
});
|
||||
blur({ fbo: hBlurFBO, frag: blurFrag, tex: highPassFBO, direction: [1, 0] });
|
||||
blur({ fbo: vBlurFBO, frag: blurFrag, tex: hBlurFBO, direction: [0, 1] });
|
||||
blur({ fbo: hBlurFBO, frag: blurFrag.text(), tex: highPassFBO, direction: [1, 0] });
|
||||
blur({ fbo: vBlurFBO, frag: blurFrag.text(), tex: hBlurFBO, direction: [0, 1] });
|
||||
}
|
||||
|
||||
combine({ frag: combineFrag });
|
||||
combine({ frag: combineFrag.text() });
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { loadImage, loadText, makePassFBO, makePass } from "./utils.js";
|
||||
import imagePassFrag from "../../shaders/glsl/imagePass.frag.glsl";
|
||||
|
||||
// Multiplies the rendered rain and bloom by a loaded in image
|
||||
|
||||
@@ -10,6 +9,7 @@ export default ({ regl, cache, config }, inputs) => {
|
||||
const output = makePassFBO(regl, config.useHalfFloat);
|
||||
const bgURL = "bgURL" in config ? config.bgURL : defaultBGURL;
|
||||
const background = loadImage(cache, regl, bgURL);
|
||||
const imagePassFrag = loadText(cache, "shaders/glsl/imagePass.frag.glsl");
|
||||
const render = regl({
|
||||
frag: regl.prop("frag"),
|
||||
uniforms: {
|
||||
@@ -23,11 +23,11 @@ export default ({ regl, cache, config }, inputs) => {
|
||||
{
|
||||
primary: output,
|
||||
},
|
||||
Promise.all([background.loaded]),
|
||||
Promise.all([background.loaded, imagePassFrag.loaded]),
|
||||
(w, h) => output.resize(w, h),
|
||||
(shouldRender) => {
|
||||
if (shouldRender) {
|
||||
render({ frag: imagePassFrag });
|
||||
render({ frag: imagePassFrag.text() });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { makeFullScreenQuad, makePipeline } from "./utils.js";
|
||||
import createREGL from "regl";
|
||||
import fetchLibraries from "../fetchLibraries.js";
|
||||
import makeRain from "./rainPass.js";
|
||||
import makeBloomPass from "./bloomPass.js";
|
||||
import makePalettePass from "./palettePass.js";
|
||||
@@ -21,7 +21,13 @@ const effects = {
|
||||
mirror: makeMirrorPass,
|
||||
};
|
||||
|
||||
let createREGL, glMatrix;
|
||||
|
||||
export const init = async (canvas) => {
|
||||
const libraries = await fetchLibraries();
|
||||
createREGL = libraries.createREGL;
|
||||
glMatrix = libraries.glMatrix;
|
||||
|
||||
const resize = () => {
|
||||
const devicePixelRatio = window.devicePixelRatio ?? 1;
|
||||
canvas.width = Math.ceil(canvas.clientWidth * devicePixelRatio * rain.resolution);
|
||||
@@ -74,18 +80,12 @@ export const formulate = async (rain, config) => {
|
||||
}
|
||||
|
||||
const cameraTex = regl.texture(cameraCanvas);
|
||||
const lkg = await getLKG(config.useHoloplay, true);
|
||||
|
||||
// All this takes place in a full screen quad.
|
||||
const fullScreenQuad = makeFullScreenQuad(regl);
|
||||
const effectName = config.effect in effects ? config.effect : "palette";
|
||||
const context = { regl, cache, config, lkg, cameraTex, cameraAspectRatio };
|
||||
const pipeline = makePipeline(context, [
|
||||
makeRain,
|
||||
makeBloomPass,
|
||||
effects[effectName],
|
||||
makeQuiltPass,
|
||||
]);
|
||||
const context = { regl, cache, config, cameraTex, cameraAspectRatio, glMatrix };
|
||||
const pipeline = makePipeline(context, [makeRain, makeBloomPass, effects[effectName]]);
|
||||
|
||||
const screenUniforms = { tex: pipeline[pipeline.length - 1].outputs.primary };
|
||||
const drawToScreen = regl({ uniforms: screenUniforms });
|
||||
@@ -150,7 +150,7 @@ export const formulate = async (rain, config) => {
|
||||
rain.tick = tick;
|
||||
};
|
||||
|
||||
export const destroy = ({ regl, resize, doubleClick, cache, tick, canvas }) => {
|
||||
export const destroy = ({ regl, cache, resize, doubleClick, tick, canvas }) => {
|
||||
window.removeEventListener("resize", resize);
|
||||
window.removeEventListener("dblclick", doubleClick);
|
||||
cache.clear();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { loadText, makePassFBO, makePass } from "./utils.js";
|
||||
import mirrorPassFrag from "../../shaders/glsl/mirrorPass.frag.glsl";
|
||||
|
||||
let start;
|
||||
const numClicks = 5;
|
||||
@@ -14,8 +13,9 @@ window.onclick = (e) => {
|
||||
index = (index + 1) % numClicks;
|
||||
};
|
||||
|
||||
export default ({ regl, config, cameraTex, cameraAspectRatio }, inputs) => {
|
||||
export default ({ regl, cache, config, cameraTex, cameraAspectRatio }, inputs) => {
|
||||
const output = makePassFBO(regl, config.useHalfFloat);
|
||||
const mirrorPassFrag = loadText(cache, "shaders/glsl/mirrorPass.frag.glsl");
|
||||
const render = regl({
|
||||
frag: regl.prop("frag"),
|
||||
uniforms: {
|
||||
@@ -36,14 +36,14 @@ export default ({ regl, config, cameraTex, cameraAspectRatio }, inputs) => {
|
||||
{
|
||||
primary: output,
|
||||
},
|
||||
null, // No async loading, glsl bundled and loaded into memory at document load
|
||||
Promise.all([mirrorPassFrag.loaded]),
|
||||
(w, h) => {
|
||||
output.resize(w, h);
|
||||
aspectRatio = w / h;
|
||||
},
|
||||
(shouldRender) => {
|
||||
if (shouldRender) {
|
||||
render({ frag: mirrorPassFrag });
|
||||
render({ frag: mirrorPassFrag.text() });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import colorToRGB from "../utils/colorToRGB";
|
||||
import { make1DTexture, makePassFBO, makePass } from "./utils.js";
|
||||
import palettePassFrag from "../../shaders/glsl/palettePass.frag.glsl";
|
||||
import colorToRGB from "../utils/colorToRGB.js";
|
||||
import { loadText, make1DTexture, makePassFBO, makePass } from "./utils.js";
|
||||
|
||||
// Maps the brightness of the rendered rain and bloom to colors
|
||||
// in a 1D gradient palette texture generated from the passed-in color sequence
|
||||
@@ -55,7 +54,7 @@ const makePalette = (regl, entries) => {
|
||||
// won't persist across subsequent frames. This is a safe trick
|
||||
// in screen space.
|
||||
|
||||
export default ({ regl, config }, inputs) => {
|
||||
export default ({ regl, cache, config }, inputs) => {
|
||||
const output = makePassFBO(regl, config.useHalfFloat);
|
||||
const paletteTex = makePalette(regl, config.palette);
|
||||
const {
|
||||
@@ -67,6 +66,7 @@ export default ({ regl, config }, inputs) => {
|
||||
ditherMagnitude,
|
||||
} = config;
|
||||
|
||||
const palettePassFrag = loadText(cache, "shaders/glsl/palettePass.frag.glsl");
|
||||
const render = regl({
|
||||
frag: regl.prop("frag"),
|
||||
|
||||
@@ -92,7 +92,7 @@ export default ({ regl, config }, inputs) => {
|
||||
(w, h) => output.resize(w, h),
|
||||
(shouldRender) => {
|
||||
if (shouldRender) {
|
||||
render({ frag: palettePassFrag });
|
||||
render({ frag: palettePassFrag.text() });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import { loadImage, makePassFBO, makeDoubleBuffer, makePass } from "./utils.js";
|
||||
import { mat4, vec3 } from "gl-matrix";
|
||||
import rainPassIntro from "../../shaders/glsl/rainPass.intro.frag.glsl";
|
||||
import rainPassRaindrop from "../../shaders/glsl/rainPass.raindrop.frag.glsl";
|
||||
import rainPassSymbol from "../../shaders/glsl/rainPass.symbol.frag.glsl";
|
||||
import rainPassEffect from "../../shaders/glsl/rainPass.effect.frag.glsl";
|
||||
import rainPassVert from "../../shaders/glsl/rainPass.vert.glsl";
|
||||
import rainPassFrag from "../../shaders/glsl/rainPass.frag.glsl";
|
||||
import { loadImage, loadText, makePassFBO, makeDoubleBuffer, makePass } from "./utils.js";
|
||||
|
||||
const extractEntries = (src, keys) =>
|
||||
Object.fromEntries(Array.from(Object.entries(src)).filter(([key]) => keys.includes(key)));
|
||||
@@ -37,7 +30,8 @@ const blVert = [1, 0];
|
||||
const brVert = [1, 1];
|
||||
const quadVertices = [tlVert, trVert, brVert, tlVert, brVert, blVert];
|
||||
|
||||
export default ({ regl, cache, config, lkg }) => {
|
||||
export default ({ regl, cache, config, glMatrix }) => {
|
||||
const { mat4, vec3 } = glMatrix;
|
||||
// The volumetric mode multiplies the number of columns
|
||||
// to reach the desired density, and then overlaps them
|
||||
const volumetric = config.volumetric;
|
||||
@@ -69,6 +63,7 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
};
|
||||
|
||||
const introDoubleBuffer = makeComputeDoubleBuffer(regl, 1, numColumns);
|
||||
const rainPassIntro = loadText(cache, "shaders/glsl/rainPass.intro.frag.glsl");
|
||||
|
||||
const introUniforms = {
|
||||
...commonUniforms,
|
||||
@@ -85,6 +80,7 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
});
|
||||
|
||||
const raindropDoubleBuffer = makeComputeDoubleBuffer(regl, numRows, numColumns);
|
||||
const rainPassRaindrop = loadText(cache, "shaders/glsl/rainPass.raindrop.frag.glsl");
|
||||
|
||||
const raindropUniforms = {
|
||||
...commonUniforms,
|
||||
@@ -108,6 +104,7 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
});
|
||||
|
||||
const symbolDoubleBuffer = makeComputeDoubleBuffer(regl, numRows, numColumns);
|
||||
const rainPassSymbol = loadText(cache, "shaders/glsl/rainPass.symbol.frag.glsl");
|
||||
|
||||
const symbolUniforms = {
|
||||
...commonUniforms,
|
||||
@@ -125,6 +122,7 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
});
|
||||
|
||||
const effectDoubleBuffer = makeComputeDoubleBuffer(regl, numRows, numColumns);
|
||||
const rainPassEffect = loadText(cache, "shaders/glsl/rainPass.effect.frag.glsl");
|
||||
|
||||
const effectUniforms = {
|
||||
...commonUniforms,
|
||||
@@ -224,8 +222,6 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
screenSize: regl.prop("screenSize"),
|
||||
},
|
||||
|
||||
viewport: regl.prop("viewport"),
|
||||
|
||||
attributes: {
|
||||
aPosition: quadPositions,
|
||||
aCorner: Array(numQuads).fill(quadVertices),
|
||||
@@ -237,7 +233,6 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
|
||||
// Camera and transform math for the volumetric mode
|
||||
const screenSize = [1, 1];
|
||||
//const { mat4, vec3 } = glMatrix;
|
||||
const transform = mat4.create();
|
||||
if (volumetric && config.isometric) {
|
||||
mat4.rotateX(transform, transform, (Math.PI * 1) / 8);
|
||||
@@ -253,7 +248,17 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
{
|
||||
primary: output,
|
||||
},
|
||||
Promise.all([glyphMSDF.loaded, glintMSDF.loaded, baseTexture.loaded, glintTexture.loaded]),
|
||||
Promise.all([
|
||||
glyphMSDF.loaded,
|
||||
glintMSDF.loaded,
|
||||
baseTexture.loaded,
|
||||
glintTexture.loaded,
|
||||
rainPassIntro.loaded,
|
||||
rainPassRaindrop.loaded,
|
||||
rainPassSymbol.loaded,
|
||||
rainPassVert.loaded,
|
||||
rainPassFrag.loaded,
|
||||
]),
|
||||
(w, h) => {
|
||||
output.resize(w, h);
|
||||
const aspectRatio = w / h;
|
||||
@@ -276,10 +281,10 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
[screenSize[0], screenSize[1]] = aspectRatio > 1 ? [1, aspectRatio] : [1 / aspectRatio, 1];
|
||||
},
|
||||
(shouldRender) => {
|
||||
intro({ frag: rainPassIntro });
|
||||
raindrop({ frag: rainPassRaindrop });
|
||||
symbol({ frag: rainPassSymbol });
|
||||
effect({ frag: rainPassEffect });
|
||||
intro({ frag: rainPassIntro.text() });
|
||||
raindrop({ frag: rainPassRaindrop.text() });
|
||||
symbol({ frag: rainPassSymbol.text() });
|
||||
effect({ frag: rainPassEffect.text() });
|
||||
|
||||
if (shouldRender) {
|
||||
regl.clear({
|
||||
@@ -292,8 +297,8 @@ export default ({ regl, cache, config, lkg }) => {
|
||||
transform,
|
||||
camera,
|
||||
screenSize,
|
||||
vert: rainPassVert,
|
||||
frag: rainPassFrag,
|
||||
vert: rainPassVert.text(),
|
||||
frag: rainPassFrag.text(),
|
||||
glyphTransform: [1, 0, 0, 1],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import colorToRGB from "../utils/colorToRGB";
|
||||
import { make1DTexture, makePassFBO, makePass } from "./utils";
|
||||
import stripePassFrag from "../../shaders/glsl/stripePass.frag.glsl";
|
||||
import colorToRGB from "../utils/colorToRGB.js";
|
||||
import { loadText, make1DTexture, makePassFBO, makePass } from "./utils.js";
|
||||
|
||||
// Multiplies the rendered rain and bloom by a 1D gradient texture
|
||||
// generated from the passed-in color sequence
|
||||
@@ -28,7 +27,7 @@ const prideStripeColors = [
|
||||
.map((color) => Array(2).fill(color))
|
||||
.flat();
|
||||
|
||||
export default ({ regl, config }, inputs) => {
|
||||
export default ({ regl, cache, config }, inputs) => {
|
||||
const output = makePassFBO(regl, config.useHalfFloat);
|
||||
|
||||
const {
|
||||
@@ -52,6 +51,8 @@ export default ({ regl, config }, inputs) => {
|
||||
stripeColors.map((color) => [...colorToRGB(color), 1]),
|
||||
);
|
||||
|
||||
const stripePassFrag = loadText(cache, "shaders/glsl/stripePass.frag.glsl");
|
||||
|
||||
const render = regl({
|
||||
frag: regl.prop("frag"),
|
||||
|
||||
@@ -73,11 +74,11 @@ export default ({ regl, config }, inputs) => {
|
||||
{
|
||||
primary: output,
|
||||
},
|
||||
null,
|
||||
stripePassFrag.loaded,
|
||||
(w, h) => output.resize(w, h),
|
||||
(shouldRender) => {
|
||||
if (shouldRender) {
|
||||
render({ frag: stripePassFrag });
|
||||
render({ frag: stripePassFrag.text() });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -29,7 +29,6 @@ const makeDoubleBuffer = (regl, props) => {
|
||||
const isPowerOfTwo = (x) => Math.log2(x) % 1 == 0;
|
||||
|
||||
const loadImage = (cache, regl, url, mipmap) => {
|
||||
|
||||
const key = `${url}_${mipmap}`;
|
||||
if (cache.has(key)) {
|
||||
return cache.get(key);
|
||||
|
||||
@@ -1,84 +1,71 @@
|
||||
import msdfCoptic from "../../assets/coptic_msdf.png";
|
||||
import msdfGothic from "../../assets/gothic_msdf.png";
|
||||
import msdfMatrixCode from "../../assets/matrixcode_msdf.png";
|
||||
import msdfRes from "../../assets/resurrections_msdf.png";
|
||||
import megacity from "../../assets/megacity_msdf.png";
|
||||
import msdfResGlint from "../../assets/resurrections_glint_msdf.png";
|
||||
import msdfHuberfishA from "../../assets/huberfish_a_msdf.png";
|
||||
import msdfHuberfishD from "../../assets/huberfish_d_msdf.png";
|
||||
import msdfGtargTenretni from "../../assets/gtarg_tenretniolleh_msdf.png";
|
||||
import msdfGtargAlienText from "../../assets/gtarg_alientext_msdf.png";
|
||||
import msdfNeoMatrixology from "../../assets/neomatrixology_msdf.png";
|
||||
|
||||
import texSand from "../../assets/sand.png";
|
||||
import texPixels from "../../assets/pixel_grid.png";
|
||||
import texMesh from "../../assets/mesh.png";
|
||||
import texMetal from "../../assets/metal.png";
|
||||
|
||||
const fonts = {
|
||||
coptic: {
|
||||
// The script the Gnostic codices were written in
|
||||
glyphMSDFURL: msdfCoptic,
|
||||
glyphMSDFURL: "assets/coptic_msdf.png",
|
||||
glyphSequenceLength: 32,
|
||||
glyphTextureGridSize: [8, 8],
|
||||
},
|
||||
gothic: {
|
||||
// The script the Codex Argenteus was written in
|
||||
glyphMSDFURL: msdfGothic,
|
||||
glyphMSDFURL: "assets/gothic_msdf.png",
|
||||
glyphSequenceLength: 27,
|
||||
glyphTextureGridSize: [8, 8],
|
||||
},
|
||||
matrixcode: {
|
||||
// The glyphs seen in the film trilogy
|
||||
glyphMSDFURL: msdfMatrixCode,
|
||||
glyphMSDFURL: "assets/matrixcode_msdf.png",
|
||||
glyphSequenceLength: 57,
|
||||
glyphTextureGridSize: [8, 8],
|
||||
},
|
||||
/*
|
||||
megacity: {
|
||||
// The glyphs seen in the film trilogy
|
||||
glyphMSDFURL: megacity,
|
||||
glyphMSDFURL: "assets/megacity_msdf.png",
|
||||
glyphSequenceLength: 64,
|
||||
glyphTextureGridSize: [8, 8],
|
||||
},
|
||||
*/
|
||||
resurrections: {
|
||||
// The glyphs seen in the film trilogy
|
||||
glyphMSDFURL: msdfRes,
|
||||
glintMSDFURL: msdfResGlint,
|
||||
glyphMSDFURL: "assets/resurrections_msdf.png",
|
||||
glintMSDFURL: "assets/resurrections_glint_msdf.png",
|
||||
glyphSequenceLength: 135,
|
||||
glyphTextureGridSize: [13, 12],
|
||||
},
|
||||
/*
|
||||
huberfishA: {
|
||||
glyphMSDFURL: msdfHuberfishA,
|
||||
glyphMSDFURL: "assets/huberfish_a_msdf.png",
|
||||
glyphSequenceLength: 34,
|
||||
glyphTextureGridSize: [6, 6],
|
||||
},
|
||||
huberfishD: {
|
||||
glyphMSDFURL: msdfHuberfishD,
|
||||
glyphMSDFURL: "assets/huberfish_d_msdf.png",
|
||||
glyphSequenceLength: 34,
|
||||
glyphTextureGridSize: [6, 6],
|
||||
},
|
||||
gtarg_tenretniolleh: {
|
||||
glyphMSDFURL: msdfGtargTenretni,
|
||||
glyphMSDFURL: "assets/gtarg_tenretniolleh_msdf.png",
|
||||
glyphSequenceLength: 36,
|
||||
glyphTextureGridSize: [6, 6],
|
||||
},
|
||||
gtarg_alientext: {
|
||||
glyphMSDFURL: msdfGtargAlienText,
|
||||
glyphMSDFURL: "assets/gtarg_alientext_msdf.png",
|
||||
glyphSequenceLength: 38,
|
||||
glyphTextureGridSize: [8, 5],
|
||||
},
|
||||
neomatrixology: {
|
||||
glyphMSDFURL: msdfNeoMatrixology,
|
||||
glyphMSDFURL: "assets/neomatrixology_msdf.png",
|
||||
glyphSequenceLength: 12,
|
||||
glyphTextureGridSize: [4, 4],
|
||||
},
|
||||
*/
|
||||
};
|
||||
|
||||
const textureURLs = {
|
||||
sand: texSand,
|
||||
pixels: texPixels,
|
||||
mesh: texMesh,
|
||||
metal: texMetal,
|
||||
// sand: "assets/sand.png",
|
||||
// pixels: "assets/pixel_grid.png",
|
||||
mesh: "assets/mesh.png",
|
||||
metal: "assets/metal.png",
|
||||
};
|
||||
|
||||
const hsl = (...values) => ({ space: "hsl", values });
|
||||
@@ -149,6 +136,7 @@ const defaults = {
|
||||
|
||||
const versions = {
|
||||
classic: {},
|
||||
/*
|
||||
megacity: {
|
||||
font: "megacity",
|
||||
animationSpeed: 0.5,
|
||||
@@ -167,6 +155,7 @@ const versions = {
|
||||
cursorColor: hsl(0.167, 1, 0.75),
|
||||
cursorIntensity: 2,
|
||||
},
|
||||
*/
|
||||
operator: {
|
||||
cursorColor: hsl(0.375, 1, 0.66),
|
||||
cursorIntensity: 3,
|
||||
@@ -279,6 +268,7 @@ const versions = {
|
||||
raindropLength: 0.3,
|
||||
density: 0.75,
|
||||
},
|
||||
/*
|
||||
morpheus: {
|
||||
font: "resurrections",
|
||||
glintTexture: "mesh",
|
||||
@@ -368,7 +358,7 @@ const versions = {
|
||||
// { color: hsl(0.1, 1.0, 0.9), at: 1.0 },
|
||||
],
|
||||
},
|
||||
|
||||
*/
|
||||
["3d"]: {
|
||||
volumetric: true,
|
||||
fallSpeed: 0.5,
|
||||
|
||||
@@ -6,8 +6,6 @@ import {
|
||||
makeBindGroup,
|
||||
makePass,
|
||||
} from "./utils.js";
|
||||
import bloomBlurShader from "../../shaders/wgsl/bloomBlur.wgsl";
|
||||
import bloomCombineShader from "../../shaders/wgsl/bloomCombine.wgsl";
|
||||
|
||||
// const makePyramid = makeComputeTarget;
|
||||
|
||||
@@ -56,8 +54,8 @@ export default ({ config, device }) => {
|
||||
}
|
||||
|
||||
const assets = [
|
||||
loadShader(device, bloomBlurShader),
|
||||
loadShader(device, bloomCombineShader),
|
||||
loadShader(device, "shaders/wgsl/bloomBlur.wgsl"),
|
||||
loadShader(device, "shaders/wgsl/bloomCombine.wgsl"),
|
||||
];
|
||||
|
||||
const linearSampler = device.createSampler({
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { loadShader, makeBindGroup, makePass } from "./utils.js";
|
||||
|
||||
import endPassShader from "../../shaders/wgsl/endPass.wgsl";
|
||||
|
||||
// Eventually, WebGPU will allow the output of the final pass in the pipeline to be copied to the canvas texture.
|
||||
// Until then, this render pass does the job.
|
||||
|
||||
@@ -23,7 +21,7 @@ export default ({ device, canvasFormat, canvasContext }) => {
|
||||
let renderPipeline;
|
||||
let renderBindGroup;
|
||||
|
||||
const assets = [loadShader(device, endPassShader)];
|
||||
const assets = [loadShader(device, "shaders/wgsl/endPass.wgsl")];
|
||||
|
||||
const loaded = (async () => {
|
||||
const [imageShader] = await Promise.all(assets);
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
makeBindGroup,
|
||||
makePass,
|
||||
} from "./utils.js";
|
||||
import imagePassShader from "../../shaders/wgsl/imagePass.wgsl";
|
||||
|
||||
// Multiplies the rendered rain and bloom by a loaded in image
|
||||
|
||||
@@ -16,7 +15,10 @@ const defaultBGURL =
|
||||
|
||||
export default ({ config, cache, device }) => {
|
||||
const bgURL = "bgURL" in config ? config.bgURL : defaultBGURL;
|
||||
const assets = [loadTexture(device, cache, bgURL), loadShader(device, imagePassShader)];
|
||||
const assets = [
|
||||
loadTexture(device, cache, bgURL),
|
||||
loadShader(device, "shaders/wgsl/imagePass.wgsl"),
|
||||
];
|
||||
|
||||
const linearSampler = device.createSampler({
|
||||
magFilter: "linear",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { structs } from "../../lib/gpu-buffer.js";
|
||||
import { makeUniformBuffer, makePipeline } from "./utils.js";
|
||||
import fetchLibraries from "../fetchLibraries.js";
|
||||
|
||||
import makeRain from "./rainPass.js";
|
||||
import makeBloomPass from "./bloomPass.js";
|
||||
@@ -23,7 +24,12 @@ const effects = {
|
||||
mirror: makeMirrorPass,
|
||||
};
|
||||
|
||||
let glMatrix;
|
||||
|
||||
export const init = async (canvas) => {
|
||||
const libraries = await fetchLibraries();
|
||||
glMatrix = libraries.glMatrix;
|
||||
|
||||
const resize = () => {
|
||||
const devicePixelRatio = window.devicePixelRatio ?? 1;
|
||||
canvas.width = Math.ceil(canvas.clientWidth * devicePixelRatio * rain.resolution);
|
||||
@@ -50,7 +56,16 @@ export const init = async (canvas) => {
|
||||
const device = await adapter.requestDevice();
|
||||
|
||||
const cache = new Map();
|
||||
const rain = { canvas, resize, doubleClick, cache, canvasContext, adapter, device, resolution: 1 };
|
||||
const rain = {
|
||||
canvas,
|
||||
resize,
|
||||
doubleClick,
|
||||
cache,
|
||||
canvasContext,
|
||||
adapter,
|
||||
device,
|
||||
resolution: 1,
|
||||
};
|
||||
|
||||
window.addEventListener("dblclick", doubleClick);
|
||||
window.addEventListener("resize", resize);
|
||||
@@ -103,6 +118,7 @@ export const formulate = async (rain, config) => {
|
||||
cameraTex,
|
||||
cameraAspectRatio,
|
||||
cameraSize,
|
||||
glMatrix,
|
||||
};
|
||||
|
||||
const effectName = config.effect in effects ? config.effect : "palette";
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
makeBindGroup,
|
||||
makePass,
|
||||
} from "./utils.js";
|
||||
import mirrorPassShader from "../../shaders/wgsl/mirrorPass.wgsl";
|
||||
|
||||
let start;
|
||||
const numTouches = 5;
|
||||
@@ -26,7 +25,7 @@ window.onclick = (e) => {
|
||||
};
|
||||
|
||||
export default ({ config, device, cameraTex, cameraAspectRatio, timeBuffer }) => {
|
||||
const assets = [loadShader(device, mirrorPassShader)];
|
||||
const assets = [loadShader(device, "shaders/wgsl/mirrorPass.wgsl")];
|
||||
|
||||
const linearSampler = device.createSampler({
|
||||
magFilter: "linear",
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
makeComputeTarget,
|
||||
makePass,
|
||||
} from "./utils.js";
|
||||
import palettePassShader from "../../shaders/wgsl/palettePass.wgsl";
|
||||
|
||||
// Maps the brightness of the rendered rain and bloom to colors
|
||||
// in a linear gradient buffer generated from the passed-in color sequence
|
||||
@@ -87,7 +86,7 @@ export default ({ config, device, timeBuffer }) => {
|
||||
let output;
|
||||
let screenSize;
|
||||
|
||||
const assets = [loadShader(device, palettePassShader)];
|
||||
const assets = [loadShader(device, "shaders/wgsl/palettePass.wgsl")];
|
||||
|
||||
const loaded = (async () => {
|
||||
const [paletteShader] = await Promise.all(assets);
|
||||
|
||||
@@ -7,8 +7,6 @@ import {
|
||||
makeBindGroup,
|
||||
makePass,
|
||||
} from "./utils.js";
|
||||
import { mat2, mat4, vec2, vec3 } from "gl-matrix";
|
||||
import rainPassShader from "../../shaders/wgsl/rainPass.wgsl";
|
||||
|
||||
const rippleTypes = {
|
||||
box: 0,
|
||||
@@ -35,13 +33,14 @@ const makeConfigBuffer = (device, configUniforms, config, density, gridSize, gly
|
||||
return makeUniformBuffer(device, configUniforms, configData);
|
||||
};
|
||||
|
||||
export default ({ config, cache, device, timeBuffer }) => {
|
||||
export default ({ config, glMatrix, cache, device, timeBuffer }) => {
|
||||
const { mat2, mat4, vec2, vec3 } = glMatrix;
|
||||
const assets = [
|
||||
loadTexture(device, cache, config.glyphMSDFURL),
|
||||
loadTexture(device, cache, config.glintMSDFURL),
|
||||
loadTexture(device, cache, config.baseTextureURL, false, true),
|
||||
loadTexture(device, cache, config.glintTextureURL, false, true),
|
||||
loadShader(device, rainPassShader),
|
||||
loadShader(device, "shaders/wgsl/rainPass.wgsl"),
|
||||
];
|
||||
|
||||
// The volumetric mode multiplies the number of columns
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
makeComputeTarget,
|
||||
makePass,
|
||||
} from "./utils.js";
|
||||
import stripePassShader from "../../shaders/wgsl/stripePass.wgsl";
|
||||
|
||||
// Multiplies the rendered rain and bloom by a 1D gradient texture
|
||||
// generated from the passed-in color sequence
|
||||
@@ -69,7 +68,7 @@ export default ({ config, device, timeBuffer }) => {
|
||||
let output;
|
||||
let screenSize;
|
||||
|
||||
const assets = [loadShader(device, stripePassShader)];
|
||||
const assets = [loadShader(device, "shaders/wgsl/stripePass.wgsl")];
|
||||
|
||||
const loaded = (async () => {
|
||||
const [stripeShader] = await Promise.all(assets);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const loadTexture = async (device, cache, url) => {
|
||||
|
||||
const key = url;
|
||||
if (cache.has(key)) {
|
||||
return cache.get(key);
|
||||
@@ -63,9 +62,9 @@ const makeComputeTarget = (device, size, mipLevelCount = 1) =>
|
||||
GPUTextureUsage.STORAGE_BINDING,
|
||||
});
|
||||
|
||||
const loadShader = async (device, code /*text*/) => {
|
||||
// const response = await fetch(url);
|
||||
// const code = await response.text();
|
||||
const loadShader = async (device, url) => {
|
||||
const response = await fetch(url);
|
||||
const code = await response.text();
|
||||
return {
|
||||
code,
|
||||
module: device.createShaderModule({ code }),
|
||||
|
||||
116
package-lock.json
generated
116
package-lock.json
generated
@@ -19,10 +19,12 @@
|
||||
"@babel/preset-react": "^7.22.5",
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"@rollup/plugin-commonjs": "^28.0.3",
|
||||
"@rollup/plugin-image": "^3.0.3",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@rollup/plugin-url": "^8.0.2",
|
||||
"babel-loader": "^9.1.3",
|
||||
"copy-webpack-plugin": "^13.0.0",
|
||||
"html-webpack-plugin": "^5.5.3",
|
||||
"prettier": "^3.5.3",
|
||||
"raw-loader": "^4.0.2",
|
||||
@@ -1966,6 +1968,28 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-image": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-image/-/plugin-image-3.0.3.tgz",
|
||||
"integrity": "sha512-qXWQwsXpvD4trSb8PeFPFajp8JLpRtqqOeNYRUKnEQNHm7e5UP7fuSRcbjQAJ7wDZBbnJvSdY5ujNBQd9B1iFg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.0.1",
|
||||
"mini-svg-data-uri": "^1.4.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-node-resolve": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz",
|
||||
@@ -3406,6 +3430,43 @@
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/copy-webpack-plugin": {
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.0.tgz",
|
||||
"integrity": "sha512-FgR/h5a6hzJqATDGd9YG41SeDViH+0bkHn6WNXCi5zKAZkeESeSxLySSsFLHqLEVCh0E+rITmCf0dusXWYukeQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"glob-parent": "^6.0.1",
|
||||
"normalize-path": "^3.0.0",
|
||||
"schema-utils": "^4.2.0",
|
||||
"serialize-javascript": "^6.0.2",
|
||||
"tinyglobby": "^0.2.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/copy-webpack-plugin/node_modules/glob-parent": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/core-js-compat": {
|
||||
"version": "3.32.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.32.0.tgz",
|
||||
@@ -5190,6 +5251,16 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/mini-svg-data-uri": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
|
||||
"integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mini-svg-data-uri": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/minimalistic-assert": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
@@ -6711,6 +6782,51 @@
|
||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
|
||||
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.4.4",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/fdir": {
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
|
||||
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"picomatch": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/picomatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
|
||||
@@ -26,10 +26,12 @@
|
||||
"@babel/preset-react": "^7.22.5",
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"@rollup/plugin-commonjs": "^28.0.3",
|
||||
"@rollup/plugin-image": "^3.0.3",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@rollup/plugin-url": "^8.0.2",
|
||||
"babel-loader": "^9.1.3",
|
||||
"copy-webpack-plugin": "^13.0.0",
|
||||
"html-webpack-plugin": "^5.5.3",
|
||||
"prettier": "^3.5.3",
|
||||
"raw-loader": "^4.0.2",
|
||||
|
||||
@@ -6,6 +6,7 @@ import url from "@rollup/plugin-url";
|
||||
import { visualizer } from "rollup-plugin-visualizer"; // <- size report
|
||||
import terser from "@rollup/plugin-terser";
|
||||
import { string } from "rollup-plugin-string";
|
||||
import image from "@rollup/plugin-image";
|
||||
|
||||
export default {
|
||||
input: "js/Matrix.js",
|
||||
@@ -14,7 +15,8 @@ export default {
|
||||
peerDepsExternal(), // auto-exclude peerDeps
|
||||
nodeResolve(), // so Rollup can find deps in node_modules
|
||||
string({ include: ["**/*.glsl"] }),
|
||||
url({ include: ["**/*.png"], limit: 0 }),
|
||||
string({ include: ["**/*.wgsl"] }),
|
||||
image({ include: ["**/*.png"], limit: 0 }),
|
||||
babel({
|
||||
exclude: "node_modules/**", // transpile JSX
|
||||
babelHelpers: "bundled",
|
||||
@@ -34,6 +36,7 @@ export default {
|
||||
],
|
||||
output: [
|
||||
{
|
||||
inlineDynamicImports: true,
|
||||
file: "dist/index.cjs.js",
|
||||
format: "cjs",
|
||||
exports: "named",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const webpack = require("webpack");
|
||||
const path = require("path");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const CopyPlugin = require("copy-webpack-plugin");
|
||||
|
||||
module.exports = {
|
||||
mode: "development",
|
||||
@@ -12,19 +13,14 @@ module.exports = {
|
||||
exclude: /node_modules/,
|
||||
use: ["babel-loader"],
|
||||
},
|
||||
// {
|
||||
// test: /\.css$/,
|
||||
// use: ["style-loader", "css-loader"],
|
||||
// },
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ["style-loader", "css-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.(png|j?g|svg|gif)?$/,
|
||||
test: /\.(png|jpe?g|svg|glsl|wgsl)?$/,
|
||||
type: "asset/resource",
|
||||
},
|
||||
{
|
||||
test: /\.(glsl|frag|vert|wgsl)$/i,
|
||||
exclude: /node_modules/,
|
||||
use: ["raw-loader"],
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
@@ -41,6 +37,12 @@ module.exports = {
|
||||
template: path.resolve(__dirname, "public/index.html"),
|
||||
filename: "index.html",
|
||||
}),
|
||||
new CopyPlugin({
|
||||
patterns: [
|
||||
{ from: "assets", to: "assets" },
|
||||
{ from: "shaders", to: "shaders" },
|
||||
],
|
||||
}),
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
],
|
||||
devServer: {
|
||||
|
||||
Reference in New Issue
Block a user