Kinesim/vendor/threex/threex.sparks.js
Amit Kumar Nandi c51788eb87 Forgejo Up
2024-10-03 06:46:07 +05:30

237 lines
6.8 KiB
JavaScript

// This THREEx helper makes it even easier to use spark.js with three.js
// * FIXME This is currently only with WebGL
//
// # Code
//
var THREEx = THREEx || {};
THREEx.Sparks = function(opts)
{
opts = opts || {};
this._maxParticles = opts.maxParticles || console.assert(false);
this._texture = opts.texture || this._buildDefaultTexture();
var counter = opts.counter || console.assert(false);
var vertexIndexPool = {
__pools: [],
// Get a new Vector
get: function() {
if( this.__pools.length > 0 ) return this.__pools.pop();
console.assert(false, "pool ran out!")
return null;
},
// Release a vector back into the pool
add: function(v){ this.__pools.push(v); }
};
var particles = new THREE.Geometry();
var vertices = particles.vertices;
for ( i = 0; i < this._maxParticles; i++ ) {
var position = new THREE.Vector3(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY);
vertices.push(new THREE.Vertex(position));
vertexIndexPool.add(i);
}
// to handle window resize
this._$onWindowResize = this._onWindowResize.bind(this);
window.addEventListener('resize', this._$onWindowResize, false);
var attributes = this._attributes = {
size : { type: 'f', value: [] },
aColor : { type: 'c', value: [] }
};
var uniforms = this._uniforms = {
texture : { type: "t", texture: this._texture },
color : { type: "c", value: new THREE.Color(0xffffff) },
sizeRatio : { type: "f", value: this._computeSizeRatio() }
};
// fill attributes array
var valuesSize = this._attributes.size.value;
var valuesColor = this._attributes.aColor.value;
for(var v = 0; v < particles.vertices.length; v++ ){
valuesSize[v] = 99;
valuesColor[v] = new THREE.Color( 0x000000 );
}
var material = new THREE.ShaderMaterial( {
uniforms : this._uniforms,
attributes : this._attributes,
vertexShader : THREEx.Sparks.vertexShaderText,
fragmentShader : THREEx.Sparks.fragmentShaderText,
blending : THREE.AdditiveBlending,
depthWrite : false,
transparent : true
});
this._group = new THREE.ParticleSystem( particles, material );
//this._group.dynamic = true;
//this._group.sortParticles = true; // TODO is this needed ?
//// EMITTER STUFF
var setTargetParticle = function() {
var vertexIdx = vertexIndexPool.get();
var target = {
vertexIdx : vertexIdx,
size : function(value){ valuesSize[vertexIdx] = value; },
color : function(){ return valuesColor[vertexIdx]; }
};
return target;
};
var onParticleCreated = function(particle) {
var vertexIdx = particle.target.vertexIdx;
// copy particle position into three.js geometry
vertices[vertexIdx].position = particle.position;
};
var onParticleDead = function(particle) {
var vertexIdx = particle.target.vertexIdx;
// Hide the particle
valuesColor[vertexIdx].setHex( 0x000000 );
vertices[vertexIdx].position.set(Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY);
// Mark particle system as available by returning to pool
vertexIndexPool.add( vertexIdx );
};
var emitter = this._emitter = new SPARKS.Emitter(counter);
emitter.addInitializer(new SPARKS.Target(null, setTargetParticle));
emitter.addCallback("created" , onParticleCreated );
emitter.addCallback("dead" , onParticleDead );
}
THREEx.Sparks.prototype.destroy = function()
{
window.removeEventListener('resize', this._$onWindowResize);
if( this._emitter.isRunning() ) this._emitter.stop();
}
//////////////////////////////////////////////////////////////////////////////////
// //
//////////////////////////////////////////////////////////////////////////////////
THREEx.Sparks.prototype.container = function()
{
return this._group;
}
THREEx.Sparks.prototype.emitter = function()
{
return this._emitter;
}
THREEx.Sparks.prototype.update = function()
{
this._group.geometry.__dirtyVertices = true;
this._group.geometry.__dirtyColors = true;
this._attributes.size.needsUpdate = true;
this._attributes.aColor.needsUpdate = true;
}
//////////////////////////////////////////////////////////////////////////////////
// handle window resize //
//////////////////////////////////////////////////////////////////////////////////
THREEx.Sparks.prototype._onWindowResize = function()
{
this._uniforms.sizeRatio.value = this._computeSizeRatio();
this._uniforms.sizeRatio.needsUpdate = true;
}
THREEx.Sparks.prototype._computeSizeRatio = function()
{
return window.innerHeight / 1024;
}
//////////////////////////////////////////////////////////////////////////////////
// Shader Text //
//////////////////////////////////////////////////////////////////////////////////
THREEx.Sparks.vertexShaderText = [
"attribute float size;",
"attribute vec4 aColor;",
"uniform float sizeRatio;",
"varying vec4 vColor;",
"void main() {",
"vec4 mvPosition= modelViewMatrix * vec4( position, 1.0 );",
"gl_PointSize = size * sizeRatio * ( 150.0 / length( mvPosition.xyz ) );",
"gl_Position = projectionMatrix * mvPosition;",
"vColor = aColor;",
"}"
].join('\n');
THREEx.Sparks.fragmentShaderText = [
"uniform vec3 color;",
"uniform sampler2D texture;",
"varying vec4 vColor;",
"void main() {",
"vec4 outColor = texture2D( texture, gl_PointCoord );",
"gl_FragColor = outColor * vec4( color * vColor.xyz, 1.0 );",
"}"
].join('\n');
//////////////////////////////////////////////////////////////////////////////////
// Texture //
//////////////////////////////////////////////////////////////////////////////////
THREEx.Sparks.prototype._buildDefaultTexture = function(size)
{
size = size || 128;
var canvas = document.createElement( 'canvas' );
var context = canvas.getContext( '2d' );
canvas.width = canvas.height = size;
var gradient = context.createRadialGradient( canvas.width/2, canvas.height /2, 0, canvas.width /2, canvas.height /2, canvas.width /2 );
gradient.addColorStop( 0 , 'rgba(255,255,255,1)' );
gradient.addColorStop( 0.2, 'rgba(255,255,255,1)' );
gradient.addColorStop( 0.4, 'rgba(128,128,128,1)' );
gradient.addColorStop( 1 , 'rgba(0,0,0,1)' );
context.beginPath();
context.arc(size/2, size/2, size/2, 0, Math.PI*2, false);
context.closePath();
context.fillStyle = gradient;
//context.fillStyle = 'rgba(128,128,128,1)';
context.fill();
var texture = new THREE.Texture( canvas );
texture.needsUpdate = true;
return texture;
}
//////////////////////////////////////////////////////////////////////////////////
// Custom initializer TODO put it elsewhere //
//////////////////////////////////////////////////////////////////////////////////
THREEx.Sparks.ColorSizeInitializer = function(color, size){
this._color = color;
this._size = size;
}
THREEx.Sparks.ColorSizeInitializer.prototype.initialize = function(emitter, particle)
{
if( this._color !== undefined ) particle.target.color().copy(this._color);
if( this._size !== undefined ) particle.target.size(this._size);
}