mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-14 12:29:30 -07:00
196 lines
4.0 KiB
JavaScript
196 lines
4.0 KiB
JavaScript
const makePassTexture = (regl, halfFloat) =>
|
|
regl.texture({
|
|
width: 1,
|
|
height: 1,
|
|
type: halfFloat ? "half float" : "uint8",
|
|
wrap: "clamp",
|
|
min: "linear",
|
|
mag: "linear",
|
|
});
|
|
|
|
const makePassFBO = (regl, halfFloat) =>
|
|
regl.framebuffer({ color: makePassTexture(regl, halfFloat) });
|
|
|
|
const makeDoubleBuffer = (regl, props) => {
|
|
const state = Array(2)
|
|
.fill()
|
|
.map(() =>
|
|
regl.framebuffer({
|
|
color: regl.texture(props),
|
|
depthStencil: false,
|
|
}),
|
|
);
|
|
return {
|
|
front: ({ tick }) => state[tick % 2],
|
|
back: ({ tick }) => state[(tick + 1) % 2],
|
|
};
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
let texture = regl.texture([[0]]);
|
|
let loaded = false;
|
|
const resource = {
|
|
texture: () => {
|
|
if (!loaded && url != null) {
|
|
console.warn(`texture still loading: ${url}`);
|
|
}
|
|
return texture;
|
|
},
|
|
width: () => {
|
|
if (!loaded && url != null) {
|
|
console.warn(`texture still loading: ${url}`);
|
|
}
|
|
return loaded ? texture.width : 1;
|
|
},
|
|
height: () => {
|
|
if (!loaded && url != null) {
|
|
console.warn(`texture still loading: ${url}`);
|
|
}
|
|
return loaded ? texture.height : 1;
|
|
},
|
|
loaded: (async () => {
|
|
if (url != null) {
|
|
const data = new Image();
|
|
data.crossOrigin = "anonymous";
|
|
let imageURL;
|
|
if (typeof cache.get(`import::${url}`) === "function") {
|
|
imageURL = (await cache.get(`import::${url}`)()).default;
|
|
} else {
|
|
imageURL = url;
|
|
}
|
|
data.src = imageURL;
|
|
await data.decode();
|
|
loaded = true;
|
|
if (mipmap) {
|
|
if (!isPowerOfTwo(data.width) || !isPowerOfTwo(data.height)) {
|
|
console.warn(`Can't mipmap a non-power-of-two image: ${url}`);
|
|
}
|
|
mipmap = false;
|
|
}
|
|
texture = regl.texture({
|
|
data,
|
|
mag: "linear",
|
|
min: mipmap ? "mipmap" : "linear",
|
|
flipY: true,
|
|
});
|
|
}
|
|
})(),
|
|
};
|
|
cache.set(key, resource);
|
|
return resource;
|
|
};
|
|
|
|
const loadText = (cache, url) => {
|
|
const key = url;
|
|
if (cache.has(key)) {
|
|
return cache.get(key);
|
|
}
|
|
let text = "";
|
|
let loaded = false;
|
|
const resource = {
|
|
text: () => {
|
|
if (!loaded) {
|
|
console.warn(`text still loading: ${url}`);
|
|
}
|
|
return text;
|
|
},
|
|
loaded: (async () => {
|
|
if (url != null) {
|
|
let textURL;
|
|
if (typeof cache.get(`import::${url}`) === "function") {
|
|
textURL = (await cache.get(`import::${url}`)()).default;
|
|
} else {
|
|
textURL = url;
|
|
}
|
|
text = await (await fetch(textURL)).text();
|
|
loaded = true;
|
|
}
|
|
})(),
|
|
};
|
|
cache.set(key, resource);
|
|
return resource;
|
|
};
|
|
|
|
const makeFullScreenQuad = (regl, uniforms = {}, context = {}) =>
|
|
regl({
|
|
vert: `
|
|
precision mediump float;
|
|
attribute vec2 aPosition;
|
|
varying vec2 vUV;
|
|
void main() {
|
|
vUV = 0.5 * (aPosition + 1.0);
|
|
gl_Position = vec4(aPosition, 0, 1);
|
|
}
|
|
`,
|
|
|
|
frag: `
|
|
precision mediump float;
|
|
varying vec2 vUV;
|
|
uniform sampler2D tex;
|
|
void main() {
|
|
gl_FragColor = texture2D(tex, vUV);
|
|
}
|
|
`,
|
|
|
|
attributes: {
|
|
aPosition: [-4, -4, 4, -4, 0, 4],
|
|
},
|
|
count: 3,
|
|
|
|
uniforms: {
|
|
...uniforms,
|
|
time: regl.context("time"),
|
|
tick: regl.context("tick"),
|
|
},
|
|
|
|
context,
|
|
|
|
depth: { enable: false },
|
|
});
|
|
|
|
const make1DTexture = (regl, rgbas) => {
|
|
const data = rgbas.map((rgba) => rgba.map((f) => Math.floor(f * 0xff))).flat();
|
|
return regl.texture({
|
|
data,
|
|
width: data.length / 4,
|
|
height: 1,
|
|
format: "rgba",
|
|
mag: "linear",
|
|
min: "linear",
|
|
});
|
|
};
|
|
|
|
const makePass = (outputs, ready, setSize, execute) => ({
|
|
outputs: outputs ?? {},
|
|
ready: ready ?? Promise.resolve(),
|
|
setSize: setSize ?? (() => {}),
|
|
execute: execute ?? (() => {}),
|
|
});
|
|
|
|
const makePipeline = (context, steps) =>
|
|
steps
|
|
.filter((f) => f != null)
|
|
.reduce(
|
|
(pipeline, f, i) => [...pipeline, f(context, i == 0 ? null : pipeline[i - 1].outputs)],
|
|
[],
|
|
);
|
|
|
|
export {
|
|
makePassTexture,
|
|
makePassFBO,
|
|
makeDoubleBuffer,
|
|
loadImage,
|
|
loadText,
|
|
makeFullScreenQuad,
|
|
make1DTexture,
|
|
makePass,
|
|
makePipeline,
|
|
};
|