codepens/firefly-flow/dist/script.js

1171 lines
42 KiB
JavaScript

function _defineProperty(obj, key, value) {if (key in obj) {Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });} else {obj[key] = value;}return obj;}function _classPrivateFieldSet(receiver, privateMap, value) {var descriptor = privateMap.get(receiver);if (!descriptor) {throw new TypeError("attempted to set private field on non-instance");}if (descriptor.set) {descriptor.set.call(receiver, value);} else {if (!descriptor.writable) {throw new TypeError("attempted to set read only private field");}descriptor.value = value;}return value;}function _classPrivateFieldGet(receiver, privateMap) {var descriptor = privateMap.get(receiver);if (!descriptor) {throw new TypeError("attempted to get private field on non-instance");}if (descriptor.get) {return descriptor.get.call(receiver);}return descriptor.value;}function _classStaticPrivateFieldSpecGet(receiver, classConstructor, descriptor) {if (receiver !== classConstructor) {throw new TypeError("Private static access of wrong provenance");}if (descriptor.get) {return descriptor.get.call(receiver);}return descriptor.value;}import { Vec2, Vec3, Mat2, Mat3, Mat4, Quat } from 'https://cdn.skypack.dev/wtc-math';
import gifJs from 'https://cdn.skypack.dev/gif.js';
console.clear();
const setup = function () {
// Simulation dimensions
const px = Math.min(window.devicePixelRatio, 2);
const dimensions = [window.innerWidth, window.innerHeight];
const texturesize = 128;
const particles = Math.pow(texturesize, 2);
const textureArraySize = particles * 4;
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
const renderer = new Renderer(canvas, { width: dimensions[0], height: dimensions[1], alpha: true, premultipliedAlpha: false, preserveDrawingBuffer: true });
const ctx = renderer.ctx;
let drawing = new Float32Array([-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0]);
const ants = new Float32Array(particles * 2).fill(0);
const references = new Float32Array(particles * 2).fill(0);
const positionData = new Float32Array(particles * 4).fill(0);
const velocityData = new Float32Array(particles * 4).fill(0);
for (let i = 0; i < ants.length; i += 2) {
const index = i / 2;
const tindex = i * 2;
ants[i] = index % texturesize; // x position
ants[i + 1] = Math.floor(index / texturesize); // y position
references[i] = ants[i] / texturesize; // x position of the texture particle representing this ant
references[i + 1] = ants[i + 1] / texturesize; // y position of the texture particle representing this ant
positionData[tindex] = Math.random() * (window.innerWidth * px + 40);
positionData[tindex + 1] = Math.random() * (window.innerHeight * px + 40);
positionData[tindex + 2] = 0;
positionData[tindex + 3] = 1;
velocityData[tindex] = Math.random() - .5;
velocityData[tindex + 1] = Math.random() - .5;
velocityData[tindex + 2] = 0;
velocityData[tindex + 3] = 1;
}
// for (let i = 0; i < textureArraySize; i += 4) {
// positionData[i] = Math.random();
// positionData[i + 1] = Math.random();
// positionData[i + 2] = 0;
// positionData[i + 3] = 1;
// }
const positionBuffer = new FrameBuffer(renderer, 'position', {
width: texturesize,
height: texturesize,
tiling: Texture.IMAGETYPE_TILE,
texdepth: FrameBuffer.TEXTYPE_FLOAT,
pxRatio: 1,
data: positionData });
const velocityBuffer = new FrameBuffer(renderer, 'velocity', {
width: texturesize,
height: texturesize,
tiling: Texture.IMAGETYPE_TILE,
texdepth: FrameBuffer.TEXTYPE_FLOAT,
pxRatio: 1,
data: velocityData });
const blurBuffer = new FrameBuffer(renderer, 'blur', {
width: window.innerWidth,
height: window.innerHeight,
tiling: Texture.IMAGETYPE_REGULAR,
texdepth: FrameBuffer.TEXTYPE_FLOAT,
pxRatio: Math.min(window.devicePixelRatio, 2) });
const primaryBuffer = new FrameBuffer(renderer, 'prime', {
width: window.innerWidth,
height: window.innerHeight,
tiling: Texture.IMAGETYPE_REGULAR,
pxRatio: Math.min(window.devicePixelRatio, 2) });
const drawBuffer = new Buffer(ctx, drawing);
const antBuffer = new Buffer(ctx, ants, {
attributes: [{
name: 'ants',
numComponents: 2 }] });
const referenceBuffer = new Buffer(ctx, references, {
attributes: [{
name: 'reference',
numComponents: 2 }] });
const vertexShader_buffer = document.getElementById('vertexShader_buffer').innerText;
const vertexShader_particle = document.getElementById('vertexShader_particle').innerText;
const programPosition = new Program(ctx, vertexShader_buffer, document.getElementById('fragmentShader_position').innerText, {
renderType: Program.RENDER_STRIP });
const programVelocity = new Program(ctx, vertexShader_buffer, document.getElementById('fragmentShader_velocity').innerText, {
renderType: Program.RENDER_STRIP });
const programBlur = new Program(ctx, vertexShader_buffer, document.getElementById('fragmentShader_blur').innerText, {
renderType: Program.RENDER_STRIP });
const programMain = new Program(ctx, vertexShader_particle, document.getElementById('fragmentShader_particle').innerText, {
// clearColour: [.15,.1,.05, 1.],
clearColour: [.9, .9, .9, 0.],
renderType: Program.RENDER_POINTS,
blending: Renderer.BLENDING_NORMAL,
depthTesting: false,
transparent: false,
premultiplied: false });
const programOutput = new Program(ctx, vertexShader_buffer, document.getElementById('fragmentShader_output').innerText, {
clearColour: [.0, .0, .0, 1.],
renderType: Program.RENDER_STRIP });
const time = new Uniform(ctx, 'time', Uniform.TYPE_FLOAT, 100);
const uDelta = new Uniform(ctx, 'delta', Uniform.TYPE_FLOAT, 100);
const mouse = new Uniform(ctx, 'mouse', Uniform.TYPE_V2, [0., 0.]);
const screen = new Uniform(ctx, 'screen', Uniform.TYPE_V2, [window.innerWidth * px, window.innerHeight * px]);
const noise = new Texture(ctx, 'noise', {
textureType: Texture.IMAGETYPE_TILE,
url: 'https://assets.codepen.io/982762/noise.png' });
noise.preload().then(n => {
requestAnimationFrame(run);
});
let pointerdown = false;
let lastPos = new Vec2();
window.addEventListener('pointerdown', e => {
pointerdown = true;
lastPos = new Vec2(e.x, e.y);
});
window.addEventListener('pointerup', e => {
pointerdown = false;
});
window.addEventListener('pointermove', e => {
if (pointerdown) {
let newPos = new Vec2(e.x, e.y);
mouse.value = newPos.array;
}
});
let playing = true;
const setPlaying = value => {
playing = value;
};
let autoTransitionTimer = 0;
let timeToTransition = 0;
const setupValues = i => {
dimensions[0] = window.innerWidth;
dimensions[1] = window.innerHeight;
time.value = -10000;
};
setupValues(0);
let timeout;
window.addEventListener('resize', () => {
clearTimeout(timeout);
timeout = setTimeout(() => {
dimensions[0] = window.innerWidth;
dimensions[1] = window.innerHeight;
renderer.resize(dimensions[0], dimensions[1]);
blurBuffer.resize(dimensions[0], dimensions[1]);
primaryBuffer.resize(dimensions[0], dimensions[1]);
screen.value = [dimensions[0] * px, dimensions[1] * px];
}, 100);
});
let then = 0;
const run = delta => {
let now = Date.now() / 1000;
let _delta = now - then;
then = now;
if (_delta > 1000) {
requestAnimationFrame(run);
return;
}
if (playing) {
uDelta.value = Math.min(_delta, 0.5);
time.value += _delta * .05;
renderer.setViewport([velocityBuffer.width, velocityBuffer.height]);
// window.renderer = renderer;
// console.log(renderer.uniformResolution.value)
renderer.setupProgram(programVelocity, [drawBuffer], [], [time, mouse, velocityBuffer, positionBuffer, uDelta, screen]);
velocityBuffer.render(4);
renderer.setupProgram(programPosition, [drawBuffer], [], [time, mouse, velocityBuffer, positionBuffer, uDelta, screen]);
positionBuffer.render(4);
renderer.setViewport();
renderer.setupProgram(programMain, [referenceBuffer], [], [time, mouse, velocityBuffer, positionBuffer, screen, blurBuffer]);
primaryBuffer.render(particles);
renderer.setViewport();
renderer.setupProgram(programBlur, [drawBuffer], [], [time, primaryBuffer]);
blurBuffer.render(4);
renderer.setViewport();
renderer.setupProgram(programOutput, [drawBuffer], [], [time, primaryBuffer, blurBuffer]);
renderer.render(4);
requestAnimationFrame(run);
}
};
};
// Determine whether a number is a power of 2
function powerOf2(v) {
return v && !(v & v - 1);
}
// Return the next greatest power of 2
function nextPow2(v) {
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
// Update a provided image to the nearest power of 2 in size.
const pow2Image = c => {
const newWidth = powerOf2(c.width) ? c.width : nextPow2(c.width);
const newHeight = powerOf2(c.height) ? c.height : nextPow2(c.height);
const _c = document.createElement('canvas');
const ctx = _c.getContext('2d');
_c.width = newWidth;
_c.height = newHeight;
ctx.drawImage(c, 0, 0, newWidth, newHeight);
return _c;
};
const asyncImageLoad = function (img, src) {
return new Promise((resolve, reject) => {
img.onload = () => resolve(img);
img.onerror = reject;
img.src = src;
});
};
const glEnumToString = function () {
const haveEnumsForType = {};
const enums = {};
function addEnums(gl) {
const type = gl.constructor.name;
if (!haveEnumsForType[type]) {
for (const key in gl) {
if (typeof gl[key] === 'number') {
const existing = enums[gl[key]];
enums[gl[key]] = existing ? `${existing} | ${key}` : key;
}
}
haveEnumsForType[type] = true;
}
}
return function glEnumToString(gl, value) {
addEnums(gl);
return enums[value] || (typeof value === 'number' ? `0x${value.toString(16)}` : value);
};
}();
const addExtensions = ctx => {
// Set up the extensions
ctx.getExtension('OES_standard_derivatives');
ctx.getExtension('EXT_shader_texture_lod');
ctx.getExtension('OES_texture_float');
ctx.getExtension('WEBGL_color_buffer_float');
ctx.getExtension('OES_texture_float_linear');
ctx.getExtension('EXT_color_buffer_float');
};
function createContext(c, opt_attribs, params) {
const ctx = c.getContext("webgl", params) || this._el.getContext("experimental-webgl", params);
addExtensions(ctx);
return ctx;
}
const quatToMat4 = q => {
if (q.array) q = q.array; // This just transforms a provided vector into to an array.
if (q instanceof Array && q.length >= 4) {
const [x, y, z, w] = q;
const [x2, y2, z2] = q.map(x => x * 2.);
const xx = x * x2,
yx = y * x2,
yy = y * y2,
zx = z * x2,
zy = z * y2,
zz = z * z2,
wx = w * x2,
wy = w * y2,
wz = w * z2;
return new Mat4(
1 - yy - zz, yx - wz, zx + wy, 0,
yx + wz, 1 - xx - zz, zy - wx, 0,
zx - wy, zy + wx, 1 - xx - yy, 0,
0, 0, 0, 1);
}
};var _blending = new WeakMap();var _blendingEnabled = new WeakMap();var _buffers = new WeakMap();
class Renderer {
constructor(canvas, options) {_defineProperty(this, "isWebgl2", false);_blending.set(this, { writable: true, value: void 0 });_blendingEnabled.set(this, { writable: true, value: false });_buffers.set(this, { writable: true, value: [] });
options = Object.assign({}, _classStaticPrivateFieldSpecGet(Renderer, Renderer, _defaultOptions), options);
this.width = options.width;
this.height = options.height;
this.pxRatio = options.pxRatio;
this.clearing = options.clearing;
this.depthTesting = options.depthTesting;
this.canvas = canvas || document.createElement('canvas');
this.canvas.width = this.width * this.pxRatio;
this.canvas.height = this.height * this.pxRatio;
this.premultipliedAlpha = options.premultipliedAlpha;
this.ctx = this.canvas.getContext("webgl", options) || this.canvas.getContext("experimental-webgl", options);
this.ctx.viewportWidth = this.canvas.width;
this.ctx.viewportHeight = this.canvas.height;
this.uniformResolution = new Uniform(this.ctx, 'resolution', Uniform.TYPE_V2, [this.canvas.width, this.canvas.height]);
this.addExtensions();
}
resize(w, h, ratio) {
this.width = w;
this.height = h;
this.pxRatio = ratio || this.pxRatio;
this.canvas.width = this.width * this.pxRatio;
this.canvas.height = this.height * this.pxRatio;
this.ctx.viewportWidth = this.canvas.width;
this.ctx.viewportHeight = this.canvas.height;
this.uniformResolution = new Uniform(this.ctx, 'resolution', Uniform.TYPE_V2, [this.canvas.width, this.canvas.height]);
}
setViewport(dimensions) {
let w = this.width * this.pxRatio;
let h = this.height * this.pxRatio;
if (dimensions) {
w = dimensions[0];
h = dimensions[1];
}
this.ctx.viewport(0, 0, w, h);
this.uniformResolution = new Uniform(this.ctx, 'resolution', Uniform.TYPE_V2, [w, h]);
}
addExtensions() {
this.ctx.getExtension('OES_standard_derivatives');
this.ctx.getExtension('EXT_shader_texture_lod');
this.ctx.getExtension('OES_texture_float');
this.ctx.getExtension('WEBGL_color_buffer_float');
this.ctx.getExtension('OES_texture_float_linear');
this.ctx.getExtension('EXT_color_buffer_float');
}
linkBuffer(buffer) {
let hasBuffer = false;
_classPrivateFieldGet(this, _buffers).forEach(b => {
if (buffer === b) hasBuffer = true;
});
if (!hasBuffer) {
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, buffer.buffer);
this.ctx.bufferData(
this.ctx.ARRAY_BUFFER,
buffer.data,
buffer.drawType);
}
buffer.link(this.currentProgram.program);
}
setupProgram(program, buffers, attributes, uniforms) {
this.currentProgram = program;
this.ctx.useProgram(program.program);
this.premultiplied = program.premultiplied;
this.depthTesting = program.depthTesting;
if (program.blending === Program.BLENDING_NORMAL && program.transparent === false) {
this.blending = Program.BLENDING_OFF;
} else {
this.blending = program.blending;
}
this.clearColour = program.clearColour;
const a = this.clearColour[3];
// console.log('prem', this.premultipliedAlpha)
if (this.premultipliedAlpha) this.clearColour = this.clearColour.map((c, i) => c * a);
this.ctx.clearColor(...this.clearColour);
// TODO: Unlink unused buffers during this setup phase as well.
buffers.forEach(buffer => {
this.linkBuffer(buffer);
});
// this.ctx.enable(ctx.DEPTH_TEST);
if (this.depthTesting) this.ctx.enable(this.ctx.DEPTH_TEST);else
this.ctx.disable(this.ctx.DEPTH_TEST);
uniforms.forEach(uniform => {
uniform.bind(program.program);
});
this.uniformResolution.bind(program.program);
}
render(points, buffer) {
this.ctx.bindFramebuffer(this.ctx.FRAMEBUFFER, (buffer === null || buffer === void 0 ? void 0 : buffer.fb) || null);
if (this.clearing) {
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT);
if (this.depthTesting) this.ctx.clear(this.ctx.DEPTH_BUFFER_BIT);
}
switch (this.currentProgram.renderType) {
case Program.RENDER_TRIANGLES:
this.ctx.drawArrays(this.ctx.TRIANGLES, 0, points);
break;
case Program.RENDER_STRIP:
this.ctx.drawArrays(this.ctx.TRIANGLE_STRIP, 0, points);
break;
case Program.RENDER_LINES:
this.ctx.drawArrays(this.ctx.LINE_STRIP, 0, points);
break;
case Program.RENDER_LINELOOP:
this.ctx.drawArrays(this.ctx.LINE_LOOP, 0, points);
break;
case Program.RENDER_POINTS:
this.ctx.drawArrays(this.ctx.POINTS, 0, points);
break;}
}
/* SETTERS AND GETTERS */
get blending() {
return _classPrivateFieldGet(this, _blending) || Program.BLENDING_NORMAL;
}
set blending(blending) {
if (blending === Renderer.BLENDING_DEBUG) {
if (!this.breakLog) {
console.log(blending, Renderer.BLENDING_OFF, this.premultiplied);
this.breakLog = true;
}
_classPrivateFieldSet(this, _blending, blending);
this.ctx.enable(this.ctx.BLEND);
this.ctx.blendFuncSeparate(this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA, this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA);
return;
}
_classPrivateFieldSet(this, _blending, blending);
if (blending === Renderer.BLENDING_OFF) {
this.ctx.disable(this.ctx.BLEND);
_classPrivateFieldSet(this, _blendingEnabled, false);
return;
}
if (_classPrivateFieldGet(this, _blendingEnabled) === false) {
this.ctx.enable(this.ctx.BLEND);
// this.ctx.alphaFunc(this.ctx.GL_GREATER, 0.5);
// this.ctx.enable(this.ctx.GL_ALPHA_TEST);
_classPrivateFieldSet(this, _blendingEnabled, true);
}
if (this.premultiplied) {
switch (this.blending) {
case Renderer.BLENDING_NORMAL:
this.ctx.blendFuncSeparate(this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA, this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA);
break;
case Renderer.BLENDING_ADDITIVE:
this.ctx.blendFunc(this.ctx.ONE, this.ctx.ONE);
break;
case Renderer.BLENDING_SUBTRACTIVE:
this.ctx.blendFuncSeparate(this.ctx.ZERO, this.ctx.ZERO, this.ctx.ONE_MINUS_SRC_COLOR, this.ctx.ONE_MINUS_SRC_ALPHA);
break;
case Renderer.BLENDING_MULTIPLY:
this.ctx.blendFuncSeparate(this.ctx.ZERO, this.ctx.SRC_COLOR, this.ctx.ZERO, this.ctx.SRC_ALPHA);
break;}
} else {
switch (this.blending) {
case Renderer.BLENDING_NORMAL:
this.ctx.blendFuncSeparate(this.ctx.SRC_ALPHA, this.ctx.ONE_MINUS_SRC_ALPHA, this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA);
break;
case Renderer.BLENDING_ADDITIVE:
this.ctx.blendFunc(this.ctx.SRC_ALPHA, this.ctx.ONE);
break;
case Renderer.BLENDING_SUBTRACTIVE:
this.ctx.blendFunc(this.ctx.ZERO, this.ctx.ONE_MINUS_SRC_COLOR);
break;
case Renderer.BLENDING_MULTIPLY:
this.ctx.blendFunc(this.ctx.ZERO, this.ctx.SRC_COLOR);
break;}
}
}}var _defaultOptions = { writable: true, value: { width: 512, height: 512, pxRatio: Math.min(window.devicePixelRatio, 2), clearing: true, depthTesting: true, premultipliedAlpha: true } };_defineProperty(Renderer, "BLENDING_DEBUG", -1);_defineProperty(Renderer, "BLENDING_NORMAL", 1);_defineProperty(Renderer, "BLENDING_ADDITIVE", 2);_defineProperty(Renderer, "BLENDING_SUBTRACTIVE", 4);_defineProperty(Renderer, "BLENDING_MULTIPLY", 8);_defineProperty(Renderer, "BLENDING_OFF", 16);
class Buffer {
constructor(ctx, data, options) {
this.ctx = ctx;
this.name = name;
options = Object.assign({}, _classStaticPrivateFieldSpecGet(Buffer, Buffer, _defaults), options);
this.attributes = options.attributes.map(a => Object.assign({}, _classStaticPrivateFieldSpecGet(Buffer, Buffer, _defaultAttribute), a));
this.normalized = options.normalized;
this.drawType = options.drawType;
this.type = options.type;
if (data instanceof Array) data = new Float32Array(data);
this.data = data;
this.buffer = ctx.createBuffer();
}
link(program, hasBuffer = false) {
let location = this.ctx.getAttribLocation(program, `a_${this.name}`);
this.attributes.forEach(attribute => {
const location = this.ctx.getAttribLocation(program, `a_${attribute.name}`);
this.ctx.vertexAttribPointer(location, attribute.numComponents, this.type, this.normalized, attribute.stride, attribute.offset);
this.ctx.enableVertexAttribArray(location);
});
}
get length() {
return this.data.length;
}}var _defaultAttribute = { writable: true, value: { numComponents: 2, offset: 0, stride: 0 } };var _defaults = { writable: true, value: { attributes: [{ name: 'position' }], normalized: false, drawType: window.WebGLRenderingContext.STATIC_DRAW, type: window.WebGLRenderingContext.FLOAT } };var _vShader = new WeakMap();var _fShader = new WeakMap();var _p = new WeakMap();var _renderType = new WeakMap();
class Program {
constructor(ctx, vertexShaderSource, fragmentShaderSource, options = {}) {_vShader.set(this, { writable: true, value: void 0 });_fShader.set(this, { writable: true, value: void 0 });_p.set(this, { writable: true, value: void 0 });_renderType.set(this, { writable: true, value: void 0 });
options = Object.assign({}, _classStaticPrivateFieldSpecGet(Program, Program, _defaultOptions2), options);
this.ctx = ctx;
this.renderType = options.renderType;
this.clearColour = options.clearColour;
this.blending = options.blending;
this.premultiplied = options.premultiplied;
this.transparent = options.transparent;
this.depthTesting = options.depthTesting;
// Create the shaders
this.vShader = Program.createShaderOfType(this.ctx, this.ctx.VERTEX_SHADER, vertexShaderSource);
this.fShader = Program.createShaderOfType(this.ctx, this.ctx.FRAGMENT_SHADER, fragmentShaderSource);
// Create the program and link the shaders
_classPrivateFieldSet(this, _p, this.ctx.createProgram());
this.ctx.attachShader(_classPrivateFieldGet(this, _p), this.vShader);
this.ctx.attachShader(_classPrivateFieldGet(this, _p), this.fShader);
this.ctx.linkProgram(_classPrivateFieldGet(this, _p));
// Check the result of linking
var linked = this.ctx.getProgramParameter(_classPrivateFieldGet(this, _p), this.ctx.LINK_STATUS);
if (!linked) {
var error = this.ctx.getProgramInfoLog(_classPrivateFieldGet(this, _p));
console.log('Failed to link program: ' + error);
this.ctx.deleteProgram(_classPrivateFieldGet(this, _p));
this.ctx.deleteShader(this.fShader);
this.ctx.deleteShader(this.vShader);
}
}
get program() {
return _classPrivateFieldGet(this, _p);
}
/* SETTERS AND GETTERS */
set renderType(value) {
if ([
Program.RENDER_TRIANGLES,
Program.RENDER_STRIP,
Program.RENDER_LINES,
Program.RENDER_LINELOOP,
Program.RENDER_POINTS].
indexOf(value) > -1) _classPrivateFieldSet(this, _renderType, value);
}
get renderType() {
return _classPrivateFieldGet(this, _renderType);
}
/**
* Static Methods
*/
/**
* Create a shader of a given type given a context, type and source.
*
* @static
* @param {WebGLContext} ctx The context under which to create the shader
* @param {WebGLShaderType} type The shader type, vertex or fragment
* @param {string} source The shader source.
* @return {WebGLShader} The created shader
*/
static createShaderOfType(ctx, type, source) {
const shader = ctx.createShader(type);
ctx.shaderSource(shader, source);
ctx.compileShader(shader);
// Check the compile status
const compiled = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS);
if (!compiled) {
// Something went wrong during compilation; get the error
const lastError = ctx.getShaderInfoLog(shader);
console.error(`${Program.addLineNumbersWithError(source, lastError)}\nError compiling ${glEnumToString(ctx, type)}: ${lastError}`);
ctx.deleteShader(shader);
return null;
}
return shader;
}
static addLineNumbersWithError(src, log = '') {
console.log(src);
const errorRE = /ERROR:\s*\d+:(\d+)/gi;
// Note: Error message formats are not defined by any spec so this may or may not work.
const matches = [...log.matchAll(errorRE)];
const lineNoToErrorMap = new Map(matches.map((m, ndx) => {
const lineNo = parseInt(m[1]);
const next = matches[ndx + 1];
const end = next ? next.index : log.length;
const msg = log.substring(m.index, end);
return [lineNo - 1, msg];
}));
return src.split('\n').map((line, lineNo) => {
const err = lineNoToErrorMap.get(lineNo);
return `${lineNo + 1}: ${line}${err ? `\n\n^^^ ${err}` : ''}`;
}).join('\n');
}}_defineProperty(Program, "RENDER_TRIANGLES", 0);_defineProperty(Program, "RENDER_STRIP", 1);_defineProperty(Program, "RENDER_LINES", 2);_defineProperty(Program, "RENDER_LINELOOP", 4);_defineProperty(Program, "RENDER_POINTS", 8);var _defaultOptions2 = { writable: true, value: { renderType: Program.RENDER_TRIANGLES, clearColour: [1.0, 1.0, 1.0, 1.0], blending: Renderer.BLENDING_OFF, premultiplied: true, transparent: false, depthTesting: true } };var _prefix = new WeakMap();
class Uniform {
constructor(ctx, name, type, value) {_prefix.set(this, { writable: true, value: 'u' });
this.ctx = ctx;
this.name = name;
this.type = type;
this.value = value;
}
prebind() {
}
bind(program) {
this.prebind(program);
const location = this.ctx.getUniformLocation(program, `${_classPrivateFieldGet(this, _prefix)}_${this.name}`);
switch (this.type) {
case Uniform.TYPE_INT:
if (!isNaN(this.value)) this.ctx.uniform1i(location, this.value);
break;
case Uniform.TYPE_FLOAT:
if (!isNaN(this.value)) this.ctx.uniform1f(location, this.value);
break;
case Uniform.TYPE_V2:
if (this.value instanceof Array && this.value.length === 2.) this.ctx.uniform2fv(location, this.value);
break;
case Uniform.TYPE_V3:
if (this.value instanceof Array && this.value.length === 3.) this.ctx.uniform3fv(location, this.value);
break;
case Uniform.TYPE_V4:
if (this.value instanceof Array && this.value.length === 4.) this.ctx.uniform4fv(location, this.value);
break;
case Uniform.TYPE_BOOL:
if (!isNaN(this.value)) this.ctx.uniform1i(location, this.value);
break;
case Uniform.TYPE_M2:
if (this.value instanceof Array && this.value.length === 4.) this.ctx.uniformMatrix2fv(location, false, this.value);
case Uniform.TYPE_M3:
if (this.value instanceof Array && this.value.length === 9.) this.ctx.uniformMatrix3fv(location, false, this.value);
case Uniform.TYPE_M4:
if (this.value instanceof Array && this.value.length === 16.) this.ctx.uniformMatrix4fv(location, false, this.value);
break;}
}}_defineProperty(Uniform, "TYPE_INT", 0);_defineProperty(Uniform, "TYPE_FLOAT", 1);_defineProperty(Uniform, "TYPE_V2", 2);_defineProperty(Uniform, "TYPE_V3", 3);_defineProperty(Uniform, "TYPE_V4", 4);_defineProperty(Uniform, "TYPE_BOOL", 5);_defineProperty(Uniform, "TYPE_M2", 6);_defineProperty(Uniform, "TYPE_M3", 7);_defineProperty(Uniform, "TYPE_M4", 8);var _prefix2 = new WeakMap();
class Texture extends Uniform {
constructor(ctx, name, options) {
super(ctx, name, 0, null);_prefix2.set(this, { writable: true, value: 's' });
options = Object.assign({}, _classStaticPrivateFieldSpecGet(Texture, Texture, _defaultOptions3), options);
this.textureType = options.textureType;
this.minFilter = options.minFilter;
this.magFilter = options.magFilter;
this.makePowerOf2 = options.makePowerOf2;
this.generateMipMap = options.generateMipMap;
this.url = options.url;
this.data = options.data;
this.value = Texture.masteri++;
}
async preload() {
const store = {};
const img = new Image();
img.crossOrigin = "anonymous";
await asyncImageLoad(img, this.url);
if (this.makePowerOf2) this.image = pow2Image(img);else
this.image = img;
// this.loadTexture(gl, n, store);
return this;
}
prebind(program) {
if (!this.image) return;
this.ctx.activeTexture(this.ctx.TEXTURE0 + this.value);
const texture = this.ctx.createTexture(); // Create the texture object
this.ctx.pixelStorei(this.ctx.UNPACK_FLIP_Y_WEBGL, true);
this.ctx.bindTexture(this.ctx.TEXTURE_2D, texture);
// Set the parameters based on the passed type
// In WebGL images are wrapped by default, so we don't need to check for that
if (this.textureType === Texture.IMAGETYPE_MIRROR) {
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.MIRRORED_REPEAT);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.MIRRORED_REPEAT);
} else if (this.textureType === Texture.IMAGETYPE_REGULAR) {
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.CLAMP_TO_EDGE);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.CLAMP_TO_EDGE);
}
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MIN_FILTER, this.minFilter);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MAG_FILTER, this.magFilter);
// Upload the image into the texture.
if (this.data) {
this.ctx.texImage2D(this.ctx.TEXTURE_2D, 0, this.ctx.RGBA, this.ctx.RGBA, this.ctx.UNSIGNED_BYTE, this.data);
} else {
this.ctx.texImage2D(this.ctx.TEXTURE_2D, 0, this.ctx.RGBA, this.ctx.RGBA, this.ctx.UNSIGNED_BYTE, this.image);
}
if (this.generateMipMap) this.ctx.generateMipmap(this.ctx.TEXTURE_2D);
}}var _defaultOptions3 = { writable: true, value: { textureType: 0, minFilter: window.WebGLRenderingContext.LINEAR, magFilter: window.WebGLRenderingContext.LINEAR, makePowerOf2: false, generateMipMap: false } };_defineProperty(Texture, "masteri", 0);_defineProperty(Texture, "IMAGETYPE_REGULAR", 0);_defineProperty(Texture, "IMAGETYPE_TILE", 1);_defineProperty(Texture, "IMAGETYPE_MIRROR", 2);var _fb = new WeakMap();var _fb2 = new WeakMap();var _activeFB = new WeakMap();var _name = new WeakMap();var _width = new WeakMap();var _height = new WeakMap();var _pxRatio = new WeakMap();var _tiling = new WeakMap();var _texdepth = new WeakMap();var _data = new WeakMap();
class FrameBuffer {
constructor(renderer, name, options) {_fb.set(this, { writable: true, value: void 0 });_fb2.set(this, { writable: true, value: void 0 });_activeFB.set(this, { writable: true, value: void 0 });_name.set(this, { writable: true, value: void 0 });_width.set(this, { writable: true, value: void 0 });_height.set(this, { writable: true, value: void 0 });_pxRatio.set(this, { writable: true, value: void 0 });_tiling.set(this, { writable: true, value: Texture.IMAGETYPE_REGULAR });_texdepth.set(this, { writable: true, value: FrameBuffer.TEXTYPE_HALF_FLOAT_OES });_data.set(this, { writable: true, value: void 0 });
options = Object.assign({}, _classStaticPrivateFieldSpecGet(FrameBuffer, FrameBuffer, _defaultOptions4), options);
this.width = options.width;
this.height = options.height;
this.pxRatio = options.pxRatio;
this.tiling = options.tiling;
this.texdepth = options.texdepth;
this.depthTesting = options.depthTesting;
_classPrivateFieldSet(this, _name, name);
this.value = Texture.masteri++;
this.ctx = renderer.ctx;
this.renderer = renderer;
this.data = options.data;
_classPrivateFieldSet(this, _fb, this.createFrameBuffer());
_classPrivateFieldSet(this, _fb2, this.createFrameBuffer());
_classPrivateFieldSet(this, _activeFB, _classPrivateFieldGet(this, _fb));
}
resize(width, height) {
this.width = width;
this.height = height;
_classPrivateFieldSet(this, _fb, this.createFrameBuffer());
_classPrivateFieldSet(this, _fb2, this.createFrameBuffer());
_classPrivateFieldSet(this, _activeFB, _classPrivateFieldGet(this, _fb));
}
createFrameBuffer() {
const targetTexture = this.ctx.createTexture();
this.ctx.bindTexture(this.ctx.TEXTURE_2D, targetTexture);
{
// define size and format of level 0
const level = 0;
let internalFormat = this.ctx.RGBA;
const border = 0;
let format = this.ctx.RGBA;
let t;
if (this.texdepth === FrameBuffer.TEXTYPE_FLOAT) {
const e = this.ctx.getExtension('OES_texture_float');
t = this.ctx.FLOAT;
// internalFormat = this.ctx.FLOAT;
// format = this.ctx.FLOAT;
} else if (this.texdepth & FrameBuffer.TEXTYPE_HALF_FLOAT_OES) {
// t = gl.renderer.isWebgl2 ? e.HALF_FLOAT : e.HALF_FLOAT_OES;
// gl.renderer.extensions['OES_texture_half_float'] ? gl.renderer.extensions['OES_texture_half_float'].HALF_FLOAT_OES :
// gl.UNSIGNED_BYTE;
const e = this.ctx.getExtension('OES_texture_half_float');
t = this.renderer.isWebgl2 ? this.ctx.HALF_FLOAT : e.HALF_FLOAT_OES;
// format = gl.RGBA;
if (this.renderer.isWebgl2) {
internalFormat = this.ctx.RGBA16F;
}
// internalFormat = gl.RGB32F;
// format = gl.RGB32F;
// window.gl = gl
// t = e.HALF_FLOAT_OES;
} else {
t = this.ctx.UNSIGNED_BYTE;
}
const type = t;
const data = this.data;
this.ctx.texImage2D(this.ctx.TEXTURE_2D, level, internalFormat,
this.width * this.pxRatio, this.height * this.pxRatio, border,
format, type, data);
// gl.generateMipmap(gl.TEXTURE_2D);
// set the filtering so we don't need mips
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MIN_FILTER, this.ctx.NEAREST);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MAG_FILTER, this.ctx.NEAREST);
// Set the parameters based on the passed type
if (this.tiling === Texture.IMAGETYPE_TILE) {
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.REPEAT);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.REPEAT);
} else if (this.tiling === Texture.IMAGETYPE_MIRROR) {
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.MIRRORED_REPEAT);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.MIRRORED_REPEAT);
} else if (this.tiling === Texture.IMAGETYPE_REGULAR) {
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.CLAMP_TO_EDGE);
this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.CLAMP_TO_EDGE);
}
}
// Create and bind the framebuffer
const fb = this.ctx.createFramebuffer();
this.ctx.bindFramebuffer(this.ctx.FRAMEBUFFER, fb);
if (this.depthTesting) {
var ext = this.ctx.getExtension('WEBGL_depth_texture');
let depth = this.ctx.createTexture();
this.ctx.bindTexture(this.ctx.TEXTURE_2D, depth);
this.ctx.texImage2D(
this.ctx.TEXTURE_2D, 0, this.ctx.DEPTH_COMPONENT, this.width * this.pxRatio, this.height * this.pxRatio, 0, this.ctx.DEPTH_COMPONENT, this.ctx.UNSIGNED_SHORT, null);
this.ctx.framebufferTexture2D(this.ctx.FRAMEBUFFER, this.ctx.DEPTH_ATTACHMENT, this.ctx.TEXTURE_2D, depth, 0);
}
// attach the texture as the first color attachment
const attachmentPoint = this.ctx.COLOR_ATTACHMENT0;
const level = 0;
this.ctx.framebufferTexture2D(
this.ctx.FRAMEBUFFER,
attachmentPoint,
this.ctx.TEXTURE_2D,
targetTexture,
level);
return {
fb: fb,
frameTexture: targetTexture };
}
bind() {
// find the active texture based on the index
let uniform = this.ctx.getUniformLocation(this.renderer.currentProgram.program, `b_${_classPrivateFieldGet(this, _name)}`);
// Set the texture unit to the uniform
this.ctx.uniform1i(uniform, this.value);
this.ctx.activeTexture(this.ctx.TEXTURE0 + this.value);
// Bind the texture
this.ctx.bindTexture(this.ctx.TEXTURE_2D, _classPrivateFieldGet(this, _activeFB).frameTexture);
}
render(n) {
this.bind();
// Finally, ping-pong the texture
_classPrivateFieldSet(this, _activeFB, _classPrivateFieldGet(this, _activeFB) === _classPrivateFieldGet(this, _fb) ? _classPrivateFieldGet(this, _fb2) : _classPrivateFieldGet(this, _fb));
// this.renderer.render(n, this.#activeFB);
this.renderer.render(n, _classPrivateFieldGet(this, _activeFB), [this.width, this.height]);
}
set data(value) {
if (value instanceof Float32Array) _classPrivateFieldSet(this, _data, value);
}
get data() {
return _classPrivateFieldGet(this, _data) || null;
}
set width(value) {
if (value > 0) _classPrivateFieldSet(this, _width, value);
}
get width() {
return _classPrivateFieldGet(this, _width) || 1;
}
set height(value) {
if (value > 0) _classPrivateFieldSet(this, _height, value);
}
get height() {
return _classPrivateFieldGet(this, _height) || 1;
}
set pxRatio(value) {
if (value > 0) _classPrivateFieldSet(this, _pxRatio, value);
}
get pxRatio() {
return _classPrivateFieldGet(this, _pxRatio) || 1;
}
set tiling(value) {
if ([Texture.IMAGETYPE_REGULAR, Texture.IMAGETYPE_TILE, Texture.IMAGETYPE_MIRROR].indexOf(value) > -1) _classPrivateFieldSet(this, _tiling, value);
}
get tiling() {
return _classPrivateFieldGet(this, _tiling);
}
set texdepth(value) {
if ([FrameBuffer.TEXTYPE_FLOAT, FrameBuffer.TEXTYPE_UNSIGNED_BYTE, FrameBuffer.TEXTYPE_HALF_FLOAT_OES].indexOf(value) > -1) _classPrivateFieldSet(this, _texdepth, value);
}
get texdepth() {
return _classPrivateFieldGet(this, _texdepth);
}}var _defaultOptions4 = { writable: true, value: { width: 512, height: 512, pxRatio: Math.min(window.devicePixelRatio, 2), tiling: Texture.IMAGETYPE_REGULAR, texdepth: FrameBuffer.TEXTYPE_HALF_FLOAT_OES, data: null, depthTesting: false } };_defineProperty(FrameBuffer, "TEXTYPE_FLOAT", 0);_defineProperty(FrameBuffer, "TEXTYPE_UNSIGNED_BYTE", 1);_defineProperty(FrameBuffer, "TEXTYPE_HALF_FLOAT_OES", 2);var _fov = new WeakMap();var _aspect = new WeakMap();var _near = new WeakMap();var _far = new WeakMap();var _pos = new WeakMap();var _target = new WeakMap();var _up = new WeakMap();var _updateDebounce = new WeakMap();var _model = new WeakMap();var _view = new WeakMap();var _proj = new WeakMap();var _MVP = new WeakMap();var _u_model = new WeakMap();var _u_view = new WeakMap();var _u_proj = new WeakMap();var _u_MVP = new WeakMap();var _q = new WeakMap();var _name2 = new WeakMap();
class Camera {
constructor(renderer, name, options) {_fov.set(this, { writable: true, value: void 0 });_aspect.set(this, { writable: true, value: void 0 });_near.set(this, { writable: true, value: void 0 });_far.set(this, { writable: true, value: void 0 });_pos.set(this, { writable: true, value: void 0 });_target.set(this, { writable: true, value: void 0 });_up.set(this, { writable: true, value: void 0 });_updateDebounce.set(this, { writable: true, value: void 0 });_model.set(this, { writable: true, value: void 0 });_view.set(this, { writable: true, value: void 0 });_proj.set(this, { writable: true, value: void 0 });_MVP.set(this, { writable: true, value: void 0 });_u_model.set(this, { writable: true, value: void 0 });_u_view.set(this, { writable: true, value: void 0 });_u_proj.set(this, { writable: true, value: void 0 });_u_MVP.set(this, { writable: true, value: void 0 });_q.set(this, { writable: true, value: void 0 });_name2.set(this, { writable: true, value: void 0 });
options = Object.assign({}, _classStaticPrivateFieldSpecGet(Camera, Camera, _defaultOptions5), options);
this.renderer = renderer;
this.ctx = renderer.ctx;
this.fov = options.fov;
this.aspect = options.aspect;
this.near = options.near;
this.far = options.far;
this.pos = options.pos;
this.target = options.target;
this.up = options.up;
this.q = new Quat();
this.name = name;
this.update(true);
}
set q(value) {
if (value instanceof Quat) {
_classPrivateFieldSet(this, _q, value);
_classPrivateFieldSet(this, _model, quatToMat4(_classPrivateFieldGet(this, _q)));
_classPrivateFieldSet(this, _u_model, new Uniform(this.ctx, 'm_model', Uniform.TYPE_M4, _classPrivateFieldGet(this, _model).array));
}
}
get q() {
return _classPrivateFieldGet(this, _q) || new Quat();
}
update(nt = false) {
clearTimeout(_classPrivateFieldGet(this, _updateDebounce));
// this.#updateDebounce = setTimeout(() => {
_classPrivateFieldSet(this, _model, new Mat4());
_classPrivateFieldSet(this, _view, Mat4.lookAt(this.pos, this.target, this.up));
_classPrivateFieldSet(this, _proj, Mat4.perspective(this.fov, this.aspect, this.near, this.far));
_classPrivateFieldSet(this, _MVP, _classPrivateFieldGet(this, _proj).multiplyNew(_classPrivateFieldGet(this, _view)).multiply(_classPrivateFieldGet(this, _model)));
_classPrivateFieldSet(this, _u_view, new Uniform(this.ctx, 'm_view', Uniform.TYPE_M4, _classPrivateFieldGet(this, _view).array));
_classPrivateFieldSet(this, _u_proj, new Uniform(this.ctx, 'm_proj', Uniform.TYPE_M4, _classPrivateFieldGet(this, _proj).array));
_classPrivateFieldSet(this, _u_MVP, new Uniform(this.ctx, 'm_MVP', Uniform.TYPE_M4, _classPrivateFieldGet(this, _MVP).array));
this.setup = true;
// }, nt ? 0 : 50);
}
set name(value) {
if (typeof value === 'string') _classPrivateFieldSet(this, _name2, value);
}
get name() {
return _classPrivateFieldGet(this, _name2) || 'camera';
}
set fov(value) {
if (!isNaN(value)) _classPrivateFieldSet(this, _fov, value);
}
get fov() {
return _classPrivateFieldGet(this, _fov);
}
set aspect(value) {
if (!isNaN(value)) _classPrivateFieldSet(this, _aspect, value);
}
get aspect() {
return _classPrivateFieldGet(this, _aspect);
}
set near(value) {
if (!isNaN(value)) _classPrivateFieldSet(this, _near, value);
}
get near() {
return _classPrivateFieldGet(this, _near);
}
set far(value) {
if (!isNaN(value)) _classPrivateFieldSet(this, _far, value);
}
get far() {
return _classPrivateFieldGet(this, _far);
}
set pos(value) {
if (value instanceof Vec3) _classPrivateFieldSet(this, _pos, value);
}
get pos() {
return _classPrivateFieldGet(this, _pos);
}
set target(value) {
if (value instanceof Vec3) _classPrivateFieldSet(this, _target, value);
}
get target() {
return _classPrivateFieldGet(this, _target);
}
set up(value) {
if (value instanceof Vec3) _classPrivateFieldSet(this, _up, value);
}
get up() {
return _classPrivateFieldGet(this, _up);
}
get u_model() {
return _classPrivateFieldGet(this, _u_model);
}
get u_view() {
return _classPrivateFieldGet(this, _u_view);
}
get u_proj() {
return _classPrivateFieldGet(this, _u_proj);
}
get u_MVP() {
return _classPrivateFieldGet(this, _u_MVP);
}
get uniforms() {
return [this.u_model, this.u_view, this.u_proj, this.u_MVP];
}}var _defaultOptions5 = { writable: true, value: { fov: 30 * Math.PI / 180, aspect: window.innerWidth / window.innerHeight, near: .5, far: 100, pos: new Vec3(3, 1, -5), target: new Vec3(0, 0, 0), up: new Vec3(0, 1, 0) } };
setup();