Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

modular-particle-system-webgl-renderer

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

modular-particle-system-webgl-renderer - npm Package Compare versions

Comparing version 0.0.1 to 0.0.2

index.iife.js

591

index.js

@@ -1,337 +0,334 @@

var ParticleRenderer = (function (exports) {
'use strict';
// WebGL 2 renderer for "modular-particle-system" developed by Niilo Keinänen 2022
// WebGL 2 renderer for "modular-particle-system" developed by Niilo Keinänen 2022
const defaultOpts = {
autoUpdate: true,
};
const defaultOpts = {
autoUpdate: true,
};
/**
* NOTE: This is buffer size, not amount of particles! Actual max particles count is considerably less than this
*/
const maxParticlesDataBufferSize = 500000;
/**
* NOTE: This is buffer size, not amount of particles! Actual max particles count is considerably less than this
*/
const maxParticlesDataBufferSize = 500000;
export const ParticleRenderer = (opts) => {
const { particleSystem, container, textures, autoUpdate } = Object.assign(
{},
defaultOpts,
opts
);
const ParticleRenderer = (opts) => {
const { particleSystem, container, textures, autoUpdate } = Object.assign(
{},
defaultOpts,
opts
);
// #region Init
if (!particleSystem) {
throw new Error(`particleSystem parameter must be supplied!`);
}
// #region Init
if (!particleSystem) {
throw new Error(`particleSystem parameter must be supplied!`);
}
if (!container) {
throw new Error(`container parameter must be supplied!`);
}
if (!container) {
throw new Error(`container parameter must be supplied!`);
}
const canvas = document.createElement("canvas");
container.append(canvas);
const canvas = document.createElement("canvas");
container.append(canvas);
const gl = canvas.getContext("webgl2");
const gl = canvas.getContext("webgl2");
// #region Init shaders and other static render resources
// #region Init shaders and other static render resources
gl.enable(gl.BLEND);
gl.blendFuncSeparate(
gl.SRC_ALPHA,
gl.ONE_MINUS_SRC_ALPHA,
gl.ONE,
gl.ONE_MINUS_SRC_ALPHA
);
gl.enable(gl.BLEND);
gl.blendFuncSeparate(
gl.SRC_ALPHA,
gl.ONE_MINUS_SRC_ALPHA,
gl.ONE,
gl.ONE_MINUS_SRC_ALPHA
);
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSrc);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
const infoLog = gl.getShaderInfoLog(vertexShader);
console.error(`Vertex shader compilation error\n${infoLog}`);
}
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSrc);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
const infoLog = gl.getShaderInfoLog(vertexShader);
console.error(`Vertex shader compilation error\n${infoLog}`);
}
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSrc);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
const infoLog = gl.getShaderInfoLog(fragmentShader);
console.error(`Fragment shader compilation error\n${infoLog}`);
}
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSrc);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
const infoLog = gl.getShaderInfoLog(fragmentShader);
console.error(`Fragment shader compilation error\n${infoLog}`);
}
const shader = gl.createProgram();
gl.attachShader(shader, vertexShader);
gl.attachShader(shader, fragmentShader);
gl.linkProgram(shader);
if (!gl.getProgramParameter(shader, gl.LINK_STATUS)) {
console.error(`Shader program link error\n${gl.getProgramInfoLog(shader)}`);
}
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
gl.useProgram(shader);
const shader = gl.createProgram();
gl.attachShader(shader, vertexShader);
gl.attachShader(shader, fragmentShader);
gl.linkProgram(shader);
if (!gl.getProgramParameter(shader, gl.LINK_STATUS)) {
console.error(`Shader program link error\n${gl.getProgramInfoLog(shader)}`);
}
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
gl.useProgram(shader);
const locViewportSize = gl.getUniformLocation(shader, "uViewportSizePx");
const locTexture = gl.getUniformLocation(shader, "uTexture");
const locViewportSize = gl.getUniformLocation(shader, "uViewportSizePx");
const locTexture = gl.getUniformLocation(shader, "uTexture");
const vertexBufferRectangle = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBufferRectangle);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
-0.5, -0.5, 0.0, 0.0, -0.5, 0.5, 0.0, 1.0, 0.5, 0.5, 1.0, 1.0, 0.5, -0.5,
1.0, 0.0,
]),
gl.STATIC_DRAW
);
gl.enableVertexAttribArray(locGeometry);
gl.vertexAttribDivisor(locGeometry, 0);
gl.vertexAttribPointer(
locGeometry,
2,
gl.FLOAT,
false,
4 * Float32Array.BYTES_PER_ELEMENT,
0
);
gl.enableVertexAttribArray(locTexCoord);
gl.vertexAttribDivisor(locTexCoord, 0);
gl.vertexAttribPointer(
locTexCoord,
2,
gl.FLOAT,
false,
4 * Float32Array.BYTES_PER_ELEMENT,
2 * Float32Array.BYTES_PER_ELEMENT
);
const vertexBufferRectangle = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBufferRectangle);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
-0.5, -0.5, 0.0, 0.0, -0.5, 0.5, 0.0, 1.0, 0.5, 0.5, 1.0, 1.0, 0.5, -0.5,
1.0, 0.0,
]),
gl.STATIC_DRAW
);
gl.enableVertexAttribArray(locGeometry);
gl.vertexAttribDivisor(locGeometry, 0);
gl.vertexAttribPointer(
locGeometry,
2,
gl.FLOAT,
false,
4 * Float32Array.BYTES_PER_ELEMENT,
0
);
gl.enableVertexAttribArray(locTexCoord);
gl.vertexAttribDivisor(locTexCoord, 0);
gl.vertexAttribPointer(
locTexCoord,
2,
gl.FLOAT,
false,
4 * Float32Array.BYTES_PER_ELEMENT,
2 * Float32Array.BYTES_PER_ELEMENT
);
const particlesDataBuffer = gl.createBuffer();
let particlesDataBufferSize = 10000;
let particlesData = new Float32Array(particlesDataBufferSize);
gl.bindBuffer(gl.ARRAY_BUFFER, particlesDataBuffer);
gl.bufferData(gl.ARRAY_BUFFER, particlesData, gl.STREAM_DRAW);
const particlesDataBuffer = gl.createBuffer();
let particlesDataBufferSize = 10000;
let particlesData = new Float32Array(particlesDataBufferSize);
gl.bindBuffer(gl.ARRAY_BUFFER, particlesDataBuffer);
gl.bufferData(gl.ARRAY_BUFFER, particlesData, gl.STREAM_DRAW);
// #endregion
// #endregion
// #region Load texture sources
const loadedTextures = [];
if (textures) {
const keys = Object.keys(textures);
keys.forEach((key) => {
const value = textures[key];
if (!value) {
return;
}
console.time(`load texture ${key}`);
try {
if (value instanceof HTMLImageElement) {
const loadImage = () => {
const index = loadedTextures.length;
value.removeEventListener("load", loadImage);
const texture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0 + index);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
// #region Load texture sources
const loadedTextures = [];
if (textures) {
const keys = Object.keys(textures);
keys.forEach((key) => {
const value = textures[key];
if (!value) {
return;
}
console.time(`load texture ${key}`);
try {
if (value instanceof HTMLImageElement) {
const loadImage = () => {
const index = loadedTextures.length;
value.removeEventListener("load", loadImage);
const texture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0 + index);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
const level = 0;
const internalFormat = gl.RGBA;
const srcFormat = gl.RGBA;
const srcType = gl.UNSIGNED_BYTE;
const pxData = value;
gl.texImage2D(
gl.TEXTURE_2D,
level,
internalFormat,
srcFormat,
srcType,
pxData
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
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
);
loadedTextures.push({ texture, index, name: key });
console.timeEnd(`load texture ${key}`);
const level = 0;
const internalFormat = gl.RGBA;
const srcFormat = gl.RGBA;
const srcType = gl.UNSIGNED_BYTE;
const pxData = value;
gl.texImage2D(
gl.TEXTURE_2D,
level,
internalFormat,
srcFormat,
srcType,
pxData
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
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
);
loadedTextures.push({ texture, index, name: key });
console.timeEnd(`load texture ${key}`);
// Select active texture for rendering.
let desiredTextureIndex = undefined;
particleSystem.effects.forEach((effect) => {
effect.textures.forEach((texture) => {
const i = loadedTextures.findIndex(
(item) => item.name === texture
);
if (i >= 0) {
desiredTextureIndex = i;
}
});
// Select active texture for rendering.
let desiredTextureIndex = undefined;
particleSystem.effects.forEach((effect) => {
effect.textures.forEach((texture) => {
const i = loadedTextures.findIndex(
(item) => item.name === texture
);
if (i >= 0) {
desiredTextureIndex = i;
}
});
if (desiredTextureIndex >= 0) {
gl.uniform1i(locTexture, desiredTextureIndex);
}
};
if (value.complete) {
loadImage();
} else {
value.addEventListener("load", loadImage);
});
if (desiredTextureIndex >= 0) {
gl.uniform1i(locTexture, desiredTextureIndex);
}
};
if (value.complete) {
loadImage();
} else {
value.addEventListener("load", loadImage);
}
} catch (e) {
console.error(`Couldn't load texture ${key}\n\t${e.message}`);
}
});
}
// #endregion
} catch (e) {
console.error(`Couldn't load texture ${key}\n\t${e.message}`);
}
});
}
// #endregion
const render = () => {
const bounds = container.getBoundingClientRect();
const width = Math.floor(bounds.width);
const height = Math.floor(bounds.height);
canvas.width = width;
canvas.height = height;
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
const render = () => {
const bounds = container.getBoundingClientRect();
const width = Math.floor(bounds.width);
const height = Math.floor(bounds.height);
canvas.width = width;
canvas.height = height;
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
gl.viewport(0, 0, width, height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.viewport(0, 0, width, height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const particles = particleSystem.effects
.map((effect) => effect.particles)
.flat();
const particleCount = particles.length;
const attributesPerParticle = 8;
const particlesDataSize = attributesPerParticle * particleCount;
const particles = particleSystem.effects
.map((effect) => effect.particles)
.flat();
const particleCount = particles.length;
const attributesPerParticle = 8;
const particlesDataSize = attributesPerParticle * particleCount;
if (
particlesDataSize > particlesDataBufferSize &&
particlesDataBufferSize < maxParticlesDataBufferSize
) {
let increasedSize = Math.max(
particlesDataSize,
particlesDataBufferSize * 2
if (
particlesDataSize > particlesDataBufferSize &&
particlesDataBufferSize < maxParticlesDataBufferSize
) {
let increasedSize = Math.max(
particlesDataSize,
particlesDataBufferSize * 2
);
if (increasedSize > maxParticlesDataBufferSize) {
console.warn(
`Maximum particle data buffer size exceeded - some particles might not be rendered!`
);
if (increasedSize > maxParticlesDataBufferSize) {
console.warn(
`Maximum particle data buffer size exceeded - some particles might not be rendered!`
);
increasedSize = maxParticlesDataBufferSize;
}
console.info(
`Increasing particle data buffer size ${particlesDataBufferSize} -> ${increasedSize}`
);
particlesData = new Float32Array(increasedSize);
particlesDataBufferSize = increasedSize;
increasedSize = maxParticlesDataBufferSize;
}
const renderedParticlesCount = Math.min(
particleCount,
particlesDataBufferSize / attributesPerParticle
console.info(
`Increasing particle data buffer size ${particlesDataBufferSize} -> ${increasedSize}`
);
for (let i = 0; i < renderedParticlesCount; i += 1) {
const particle = particles[i];
const dataPos = i * attributesPerParticle;
particlesData[dataPos + 0] = particle.position.x;
particlesData[dataPos + 1] = -particle.position.y;
particlesData[dataPos + 2] = particle.color.r;
particlesData[dataPos + 3] = particle.color.g;
particlesData[dataPos + 4] = particle.color.b;
particlesData[dataPos + 5] = particle.alpha;
particlesData[dataPos + 6] = particle.rotation;
particlesData[dataPos + 7] = particle.scale;
}
particlesData = new Float32Array(increasedSize);
particlesDataBufferSize = increasedSize;
}
gl.uniform2f(locViewportSize, width, height);
const renderedParticlesCount = Math.min(
particleCount,
particlesDataBufferSize / attributesPerParticle
);
for (let i = 0; i < renderedParticlesCount; i += 1) {
const particle = particles[i];
const dataPos = i * attributesPerParticle;
particlesData[dataPos + 0] = particle.position.x;
particlesData[dataPos + 1] = -particle.position.y;
particlesData[dataPos + 2] = particle.color.r;
particlesData[dataPos + 3] = particle.color.g;
particlesData[dataPos + 4] = particle.color.b;
particlesData[dataPos + 5] = particle.alpha;
particlesData[dataPos + 6] = particle.rotation;
particlesData[dataPos + 7] = particle.scale;
}
gl.bufferData(gl.ARRAY_BUFFER, particlesData, gl.STREAM_DRAW);
gl.uniform2f(locViewportSize, width, height);
gl.enableVertexAttribArray(locPos);
gl.vertexAttribDivisor(locPos, 1);
gl.vertexAttribPointer(
locPos,
2,
gl.FLOAT,
false,
attributesPerParticle * Float32Array.BYTES_PER_ELEMENT,
0 * Float32Array.BYTES_PER_ELEMENT
);
gl.bufferData(gl.ARRAY_BUFFER, particlesData, gl.STREAM_DRAW);
gl.enableVertexAttribArray(locColor);
gl.vertexAttribDivisor(locColor, 1);
gl.vertexAttribPointer(
locColor,
4,
gl.FLOAT,
false,
attributesPerParticle * Float32Array.BYTES_PER_ELEMENT,
2 * Float32Array.BYTES_PER_ELEMENT
);
gl.enableVertexAttribArray(locPos);
gl.vertexAttribDivisor(locPos, 1);
gl.vertexAttribPointer(
locPos,
2,
gl.FLOAT,
false,
attributesPerParticle * Float32Array.BYTES_PER_ELEMENT,
0 * Float32Array.BYTES_PER_ELEMENT
);
gl.enableVertexAttribArray(locRotation);
gl.vertexAttribDivisor(locRotation, 1);
gl.vertexAttribPointer(
locRotation,
1,
gl.FLOAT,
false,
attributesPerParticle * Float32Array.BYTES_PER_ELEMENT,
6 * Float32Array.BYTES_PER_ELEMENT
);
gl.enableVertexAttribArray(locColor);
gl.vertexAttribDivisor(locColor, 1);
gl.vertexAttribPointer(
locColor,
4,
gl.FLOAT,
false,
attributesPerParticle * Float32Array.BYTES_PER_ELEMENT,
2 * Float32Array.BYTES_PER_ELEMENT
);
gl.enableVertexAttribArray(locScale);
gl.vertexAttribDivisor(locScale, 1);
gl.vertexAttribPointer(
locScale,
1,
gl.FLOAT,
false,
attributesPerParticle * Float32Array.BYTES_PER_ELEMENT,
7 * Float32Array.BYTES_PER_ELEMENT
);
gl.enableVertexAttribArray(locRotation);
gl.vertexAttribDivisor(locRotation, 1);
gl.vertexAttribPointer(
locRotation,
1,
gl.FLOAT,
false,
attributesPerParticle * Float32Array.BYTES_PER_ELEMENT,
6 * Float32Array.BYTES_PER_ELEMENT
);
gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, renderedParticlesCount);
};
gl.enableVertexAttribArray(locScale);
gl.vertexAttribDivisor(locScale, 1);
gl.vertexAttribPointer(
locScale,
1,
gl.FLOAT,
false,
attributesPerParticle * Float32Array.BYTES_PER_ELEMENT,
7 * Float32Array.BYTES_PER_ELEMENT
);
particleSystem.update(0);
gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, renderedParticlesCount);
};
let tPrev = window.performance.now();
let rFrame = undefined;
const frame = () => {
const tNow = window.performance.now();
if (autoUpdate) {
const tDelta = Math.min(tNow - tPrev, 10000);
particleSystem.update(tDelta / 1000);
}
render();
tPrev = tNow;
rFrame = requestAnimationFrame(frame);
};
particleSystem.update(0);
let tPrev = window.performance.now();
let rFrame = undefined;
const frame = () => {
const tNow = window.performance.now();
if (autoUpdate) {
const tDelta = Math.min(tNow - tPrev, 10000);
particleSystem.update(tDelta / 1000);
}
render();
tPrev = tNow;
rFrame = requestAnimationFrame(frame);
};
rFrame = requestAnimationFrame(frame);
const rendererHandle = {
destroy: () => {
if (rFrame) {
cancelAnimationFrame(rFrame);
rFrame = undefined;
container.removeChild(canvas);
}
},
};
return rendererHandle;
const rendererHandle = {
destroy: () => {
if (rFrame) {
cancelAnimationFrame(rFrame);
rFrame = undefined;
container.removeChild(canvas);
}
},
};
return rendererHandle;
};
const locGeometry = 0;
const locTexCoord = 1;
const locPos = 2;
const locColor = 3;
const locRotation = 4;
const locScale = 5;
const locGeometry = 0;
const locTexCoord = 1;
const locPos = 2;
const locColor = 3;
const locRotation = 4;
const locScale = 5;
const vertexShaderSrc = `#version 300 es
const vertexShaderSrc = `#version 300 es
precision mediump float;

@@ -362,3 +359,3 @@ precision mediump int;

const fragmentShaderSrc = `#version 300 es
const fragmentShaderSrc = `#version 300 es
precision mediump float;

@@ -374,9 +371,1 @@ precision mediump int;

}`;
exports.ParticleRenderer = ParticleRenderer;
Object.defineProperty(exports, '__esModule', { value: true });
return exports;
})({});
{
"name": "modular-particle-system-webgl-renderer",
"version": "0.0.1",
"version": "0.0.2",
"description": "",

@@ -10,3 +10,3 @@ "type": "module",

"minify": "rm -rf min && mkdir min && uglifyjs src/renderer.js --output min/renderer.js",
"build": "rm -rf build && mkdir build && rollup src/renderer.js --file build/index.js --format iife --name ParticleRenderer",
"build": "rm -rf build && mkdir build && rollup src/renderer.js --file build/index.iife.js --format iife --name ParticleRenderer && cp src/renderer.js build/index.js",
"release:build": "rm -rf release && mkdir release && npm run build && cp -r LICENSE README.md package.json build/** release",

@@ -18,2 +18,4 @@ "release": "npm run release:build && cd release && npm publish"

"license": "MIT",
"main": "index.js",
"browser": "index.iife.js",
"devDependencies": {

@@ -20,0 +22,0 @@ "@babel/core": "^7.17.9",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc