three.tdl.particle.system
Version:
GPU based particle system for three.js. Heavily based on tdl library (https://github.com/greggman/tdl)
653 lines (402 loc) • 23.4 kB
JavaScript
import { Mesh, Matrix4, Vector3, InstancedBufferGeometry, InterleavedBuffer, NormalBlending, Vector4, BufferGeometry, InterleavedBufferAttribute, BufferAttribute, InstancedInterleavedBuffer, DynamicDrawUsage, ShaderMaterial, DoubleSide, FrontSide, DataTexture, RGBAFormat, LinearFilter } from 'three';
// source: https://github.com/greggman/tdl/blob/master/tdl/particles.js
// ported to three.js by fazeaction
var CORNERS_ = [
[ - 0.5, - 0.5 ],
[ + 0.5, - 0.5 ],
[ + 0.5, + 0.5 ],
[ - 0.5, + 0.5 ]
];
function createDefaultClock_ ( particleSystem ) {
return function () {
var now = particleSystem.now_;
var base = particleSystem.timeBase_;
return ( now.getTime() - base.getTime() ) / 1000.0;
}
}
var POSITION_START_TIME_IDX = 0;
var UV_LIFE_TIME_FRAME_START_IDX = 4;
var VELOCITY_START_SIZE_IDX = 8;
var ACCELERATION_END_SIZE_IDX = 12;
var SPIN_START_SPIN_SPEED_IDX = 16;
var ORIENTATION_IDX = 20;
var COLOR_MULT_IDX = 24;
var LAST_IDX = 28;
var singleParticleArray_ = new Float32Array( 4 * LAST_IDX );
// source: https://github.com/greggman/tdl/blob/master/tdl/particles.js
// ported to three.js by fazeaction
function ParticleSpec () {
this.numParticles = 1;
this.numFrames = 1;
this.frameDuration = 1;
this.frameStart = 0;
this.frameStartRange = 0;
this.timeRange = 99999999;
this.startTime = null;
this.lifeTime = 1;
this.lifeTimeRange = 0;
this.startSize = 1;
this.startSizeRange = 0;
this.endSize = 1;
this.endSizeRange = 0;
this.position = [ 0, 0, 0 ];
this.positionRange = [ 0, 0, 0 ];
this.velocity = [ 0, 0, 0 ];
this.velocityRange = [ 0, 0, 0 ];
this.acceleration = [ 0, 0, 0 ];
this.accelerationRange = [ 0, 0, 0 ];
this.spinStart = 0;
this.spinStartRange = 0;
this.spinSpeed = 0;
this.spinSpeedRange = 0;
this.colorMult = [ 1, 1, 1, 1 ];
this.colorMultRange = [ 0, 0, 0, 0 ];
this.worldVelocity = [ 0, 0, 0 ];
this.worldAcceleration = [ 0, 0, 0 ];
this.billboard = true;
this.orientation = [ 0, 0, 0, 1 ];
}
// source: https://github.com/greggman/tdl/blob/master/tdl/particles.js
class OneShot extends Mesh {
constructor(emitter, scene) {
super();
this.emitter_ = emitter.clone();
this.scene = scene;
this.world_ = new Matrix4();
this.tempWorld_ = new Matrix4();
this.timeOffset_ = 0;
this.visible_ = false;
// Remove the parent emitter from the particle system's drawable
// list (if it's still there) and add ourselves instead.
var particleSystem = emitter.particleSystem;
var idx = particleSystem.drawables_.indexOf(this.emitter_);
if (idx >= 0) {
particleSystem.drawables_.splice(idx, 1);
}
particleSystem.drawables_.push(this);
}
trigger ( opt_world ) {
if ( ! this.visible_ ) {
this.scene.add( this.emitter_ );
}
if ( opt_world ) {
this.emitter_.position.copy( new Vector3().fromArray( opt_world ) );
}
this.visible_ = true;
this.timeOffset_ = this.emitter_.timeSource_();
}
draw ( world, viewProjection, timeOffset ) {
if ( this.visible_ ) {
//this.tempWorld_.multiplyMatrices(this.world_, world);
this.emitter_.draw( this.world_, viewProjection, this.timeOffset_ );
}
}
}
var billboardParticleInstancedVertexShader = "\nuniform mat4 viewInverse;\nuniform vec3 worldVelocity;\nuniform vec3 worldAcceleration;\nuniform float timeRange;\nuniform float time;\nuniform float timeOffset;\nuniform float frameDuration;\nuniform float numFrames;\nattribute vec4 uvLifeTimeFrameStart;\nattribute float startTime;\nattribute vec4 velocityStartSize;\nattribute vec4 accelerationEndSize;\nattribute vec4 spinStartSpinSpeed;\nattribute vec4 colorMult;\nvarying vec2 outputTexcoord;\nvarying float outputPercentLife;\nvarying vec4 outputColorMult;\nvoid main() {\n float lifeTime = uvLifeTimeFrameStart.z;\n float frameStart = uvLifeTimeFrameStart.w;\n vec3 velocity = (modelMatrix * vec4(velocityStartSize.xyz,\n 0.)).xyz + worldVelocity;\n float startSize = velocityStartSize.w;\n vec3 acceleration = (modelMatrix * vec4(accelerationEndSize.xyz,\n 0)).xyz + worldAcceleration;\n float endSize = accelerationEndSize.w;\n float spinStart = spinStartSpinSpeed.x;\n float spinSpeed = spinStartSpinSpeed.y;\n float localTime = mod((time - timeOffset - startTime), timeRange);\n float percentLife = localTime / lifeTime;\n float frame = mod(floor(localTime / frameDuration + frameStart),\n numFrames);\n float uOffset = frame / numFrames;\n float u = uOffset + (uv.x + 0.5) * (1. / numFrames);\n outputTexcoord = vec2(u, uv.y + 0.5);\n outputColorMult = colorMult;\n vec3 basisX = viewInverse[0].xyz;\n vec3 basisZ = viewInverse[1].xyz;\n vec4 vertexWorld = modelMatrix * vec4(position, 1.0);\n float size = mix(startSize, endSize, percentLife);\n size = (percentLife < 0. || percentLife > 1.) ? 0. : size;\n float s = sin(spinStart + spinSpeed * localTime);\n float c = cos(spinStart + spinSpeed * localTime);\n vec2 rotatedPoint = vec2(uv.x * c + uv.y * s, -uv.x * s + uv.y * c);\n vec3 localPosition = vec3(basisX * rotatedPoint.x + basisZ * rotatedPoint.y) * size +\n velocity * localTime +\n acceleration * localTime * localTime +\n vertexWorld.xyz;\n outputPercentLife = percentLife;\n gl_Position = projectionMatrix * viewMatrix * vec4(localPosition, 1.);\n}";
var orientedParticleInstancedVertexShader = "\nuniform mat4 worldViewProjection;\nuniform mat4 world;\nuniform vec3 worldVelocity;\nuniform vec3 worldAcceleration;\nuniform float timeRange;\nuniform float time;\nuniform float timeOffset;\nuniform float frameDuration;\nuniform float numFrames;\nattribute vec3 offset;\nattribute vec4 uvLifeTimeFrameStart;attribute float startTime;attribute vec4 velocityStartSize;attribute vec4 accelerationEndSize;attribute vec4 spinStartSpinSpeed;attribute vec4 orientation;attribute vec4 colorMult;\nvarying vec2 outputTexcoord;\nvarying float outputPercentLife;\nvarying vec4 outputColorMult;\nvoid main() {\nfloat lifeTime = uvLifeTimeFrameStart.z;\nfloat frameStart = uvLifeTimeFrameStart.w;\nvec3 velocity = (world * vec4(velocityStartSize.xyz,\n 0.)).xyz + worldVelocity;\nfloat startSize = velocityStartSize.w;\nvec3 acceleration = (world * vec4(accelerationEndSize.xyz,\n 0)).xyz + worldAcceleration;\nfloat endSize = accelerationEndSize.w;\nfloat spinStart = spinStartSpinSpeed.x;\nfloat spinSpeed = spinStartSpinSpeed.y;\nfloat localTime = mod((time - timeOffset - startTime), timeRange);\nfloat percentLife = localTime / lifeTime;\nfloat frame = mod(floor(localTime / frameDuration + frameStart),\n numFrames);\nfloat uOffset = frame / numFrames;\nfloat u = uOffset + (uv.x + 0.5) * (1. / numFrames);\noutputTexcoord = vec2(u, uv.y + 0.5);\noutputColorMult = colorMult;\nfloat size = mix(startSize, endSize, percentLife);\nsize = (percentLife < 0. || percentLife > 1.) ? 0. : size;\nfloat s = sin(spinStart + spinSpeed * localTime);\nfloat c = cos(spinStart + spinSpeed * localTime);\nvec4 rotatedPoint = vec4((uv.x * c + uv.y * s) * size, 0.,\n (uv.x * s - uv.y * c) * size, 1.);\nvec3 center = velocity * localTime +\n acceleration * localTime * localTime +\n position +offset;\nvec4 q2 = orientation + orientation;\nvec4 qx = orientation.xxxw * q2.xyzx;\nvec4 qy = orientation.xyyw * q2.xyzy;\nvec4 qz = orientation.xxzw * q2.xxzz;\nmat4 localMatrix = mat4(\n (1.0 - qy.y) - qz.z,\n qx.y + qz.w,\n qx.z - qy.w,\n 0,\n qx.y - qz.w,\n (1.0 - qx.x) - qz.z,\n qy.z + qx.w,\n 0,\n qx.z + qy.w,\n qy.z - qx.w,\n (1.0 - qx.x) - qy.y,\n 0,\n center.x, center.y, center.z, 1);\nrotatedPoint = localMatrix * rotatedPoint;\noutputPercentLife = percentLife;\ngl_Position = projectionMatrix * modelViewMatrix * rotatedPoint;\n}";
var particleFragmentShader = "\n#ifdef GL_ES\nprecision mediump float;\n#endif\nuniform sampler2D rampSampler;\nuniform sampler2D colorSampler;\nvarying vec2 outputTexcoord;\nvarying float outputPercentLife;\nvarying vec4 outputColorMult;\nvoid main() {\n vec4 colorMult = texture2D(rampSampler, vec2(outputPercentLife, 0.5)) * outputColorMult;\n gl_FragColor = texture2D(colorSampler, outputTexcoord) * colorMult;\n}";
// source: https://github.com/greggman/tdl/blob/master/tdl/particles.js
class ParticleEmitter extends Mesh {
constructor( particleSystem, opt_texture, opt_clock ) {
super();
opt_clock = opt_clock || particleSystem.timeSource_;
//TODO make alternative to instanced buffer
//this.particleBuffer_ = new THREE.BufferGeometry();
//this.indexBuffer_ = [];
this.particleBuffer_ = new InstancedBufferGeometry();
this.interleavedBuffer = new InterleavedBuffer();
this.numParticles_ = 0;
this.rampTexture_ = particleSystem.defaultRampTexture;
this.colorTexture_ = opt_texture || particleSystem.defaultColorTexture;
this.particleSystem = particleSystem;
this.timeSource_ = opt_clock;
this.setState(NormalBlending);
}
setTranslation ( x, y, z ) {
this.position.x = x;
this.position.y = y;
this.position.z = z;
}
setState ( stateId ) {
this.blendFunc_ = stateId;
}
setColorRamp ( colorRamp ) {
var width = colorRamp.length / 4;
if (width % 1 != 0) {
throw 'colorRamp must have multiple of 4 entries';
}
if (this.rampTexture_ == this.particleSystem.defaultRampTexture) {
this.rampTexture_ = null;
}
this.rampTexture_ = this.particleSystem.createTextureFromFloats( width, 1, colorRamp, this.rampTexture_ );
}
validateParameters ( parameters ) {
var defaults = new ParticleSpec();
for ( var key in parameters ) {
if ( typeof defaults[ key ] === 'undefined' ) {
throw 'unknown particle parameter "' + key + '"';
}
}
for ( var key in defaults ) {
if ( typeof parameters[ key ] === 'undefined' ) {
parameters[ key ] = defaults[ key ];
}
}
}
createParticles_ ( firstParticleIndex, numParticles, parameters, opt_perParticleParamSetter ) {
var interleaveBufferData = this.interleavedBuffer.array;
this.billboard_ = parameters.billboard;
var random = this.particleSystem.randomFunction_;
var plusMinus = function ( range ) {
return ( random() - 0.5 ) * range * 2;
};
// TODO: change to not allocate.
var plusMinusVector = function ( range ) {
var v = [];
for (var ii = 0; ii < range.length; ++ ii) {
v.push( plusMinus( range[ ii ] ) );
}
return v;
};
for ( var ii = 0; ii < numParticles; ++ ii ) {
if ( opt_perParticleParamSetter ) {
opt_perParticleParamSetter( ii, parameters );
}
var pLifeTime = parameters.lifeTime;
var pStartTime = ( parameters.startTime === null ) ? ( ii * parameters.lifeTime / numParticles ) : parameters.startTime;
var pFrameStart = parameters.frameStart + plusMinus(parameters.frameStartRange);
var pPosition = new Vector3().addVectors( new Vector3().fromArray(parameters.position), new Vector3().fromArray(plusMinusVector(parameters.positionRange)));
var pVelocity = new Vector3().addVectors( new Vector3().fromArray(parameters.velocity), new Vector3().fromArray(plusMinusVector(parameters.velocityRange)));
var pAcceleration = new Vector3().addVectors( new Vector3().fromArray(parameters.acceleration), new Vector3().fromArray( plusMinusVector( parameters.accelerationRange )));
var pColorMult = new Vector4().addVectors( new Vector4().fromArray(parameters.colorMult), new Vector4().fromArray(plusMinusVector( parameters.colorMultRange )));
var pSpinStart = parameters.spinStart + plusMinus(parameters.spinStartRange);
var pSpinSpeed = parameters.spinSpeed + plusMinus(parameters.spinSpeedRange);
var pStartSize = parameters.startSize + plusMinus(parameters.startSizeRange);
var pEndSize = parameters.endSize + plusMinus(parameters.endSizeRange);
var pOrientation = new Vector4().fromArray(parameters.orientation);
for (var jj = 0; jj < 1; ++jj) {
var offset0 = LAST_IDX * jj + ( ii * LAST_IDX * 4 ) + ( firstParticleIndex * LAST_IDX * 4 );
var offset1 = offset0 + 1;
var offset2 = offset0 + 2;
var offset3 = offset0 + 3;
interleaveBufferData[POSITION_START_TIME_IDX + offset0] = pPosition.x;
interleaveBufferData[POSITION_START_TIME_IDX + offset1] = pPosition.y;
interleaveBufferData[POSITION_START_TIME_IDX + offset2] = pPosition.z;
interleaveBufferData[POSITION_START_TIME_IDX + offset3] = pStartTime;
interleaveBufferData[UV_LIFE_TIME_FRAME_START_IDX + offset0] = CORNERS_[jj][0];
interleaveBufferData[UV_LIFE_TIME_FRAME_START_IDX + offset1] = CORNERS_[jj][1];
interleaveBufferData[UV_LIFE_TIME_FRAME_START_IDX + offset2] = pLifeTime;
interleaveBufferData[UV_LIFE_TIME_FRAME_START_IDX + offset3] = pFrameStart;
interleaveBufferData[VELOCITY_START_SIZE_IDX + offset0] = pVelocity.x;
interleaveBufferData[VELOCITY_START_SIZE_IDX + offset1] = pVelocity.y;
interleaveBufferData[VELOCITY_START_SIZE_IDX + offset2] = pVelocity.z;
interleaveBufferData[VELOCITY_START_SIZE_IDX + offset3] = pStartSize;
interleaveBufferData[ACCELERATION_END_SIZE_IDX + offset0] = pAcceleration.x;
interleaveBufferData[ACCELERATION_END_SIZE_IDX + offset1] = pAcceleration.y;
interleaveBufferData[ACCELERATION_END_SIZE_IDX + offset2] = pAcceleration.z;
interleaveBufferData[ACCELERATION_END_SIZE_IDX + offset3] = pEndSize;
interleaveBufferData[SPIN_START_SPIN_SPEED_IDX + offset0] = pSpinStart;
interleaveBufferData[SPIN_START_SPIN_SPEED_IDX + offset1] = pSpinSpeed;
interleaveBufferData[SPIN_START_SPIN_SPEED_IDX + offset2] = 0;
interleaveBufferData[SPIN_START_SPIN_SPEED_IDX + offset3] = 0;
interleaveBufferData[ORIENTATION_IDX + offset0] = pOrientation.x;
interleaveBufferData[ORIENTATION_IDX + offset1] = pOrientation.y;
interleaveBufferData[ORIENTATION_IDX + offset2] = pOrientation.z;
interleaveBufferData[ORIENTATION_IDX + offset3] = pOrientation.w;
interleaveBufferData[COLOR_MULT_IDX + offset0] = pColorMult.x;
interleaveBufferData[COLOR_MULT_IDX + offset1] = pColorMult.y;
interleaveBufferData[COLOR_MULT_IDX + offset2] = pColorMult.z;
interleaveBufferData[COLOR_MULT_IDX + offset3] = pColorMult.w;
}
}
this.interleavedBuffer.needsUpdate = true;
this.material.uniforms.worldVelocity.value = new Vector3(parameters.worldVelocity[0], parameters.worldVelocity[1], parameters.worldVelocity[2]);
this.material.uniforms.worldAcceleration.value = new Vector3(parameters.worldAcceleration[0], parameters.worldAcceleration[1], parameters.worldAcceleration[2]);
this.material.uniforms.timeRange.value = parameters.timeRange;
this.material.uniforms.frameDuration.value = parameters.frameDuration;
this.material.uniforms.numFrames.value = parameters.numFrames;
this.material.uniforms.rampSampler.value = this.rampTexture_;
this.material.uniforms.colorSampler.value = this.colorTexture_;
this.material.blending = this.blendFunc_;
}
allocateParticles_ ( numParticles, parameters ) {
if ( this.numParticles_ != numParticles ) {
var numIndices = 6 * numParticles;
if (numIndices > 65536 && BufferGeometry.MaxIndex < 65536) {
throw "can't have more than 10922 particles per emitter";
}
var vertexBuffer = new InterleavedBuffer( new Float32Array([
// Front
0, 0, 0, 0, -0.5, -0.5, 0, 0,
0, 0, 0, 0, 0.5, -0.5, 0, 0,
0, 0, 0, 0, 0.5, 0.5, 0, 0,
0, 0, 0, 0, -0.5, 0.5, 0, 0
]), 8);
// Use vertexBuffer, starting at offset 0, 3 items in position attribute
var positions = new InterleavedBufferAttribute( vertexBuffer, 3, 0 );
this.particleBuffer_.setAttribute( 'position', positions );
// Use vertexBuffer, starting at offset 4, 2 items in uv attribute
var uvs = new InterleavedBufferAttribute( vertexBuffer, 2, 4 );
this.particleBuffer_.setAttribute( 'uv', uvs );
var indices = new Uint16Array([
0, 1, 2,
0, 2, 3
]);
this.particleBuffer_.setIndex( new BufferAttribute( indices, 1 ) );
this.numParticles_ = numParticles;
this.interleavedBuffer = new InstancedInterleavedBuffer( new Float32Array( numParticles * singleParticleArray_.byteLength ), LAST_IDX, 1 ).setUsage( DynamicDrawUsage );
this.particleBuffer_.setAttribute( 'position', new InterleavedBufferAttribute(this.interleavedBuffer, 3, POSITION_START_TIME_IDX));
this.particleBuffer_.setAttribute( 'startTime', new InterleavedBufferAttribute(this.interleavedBuffer, 1, 3));
this.particleBuffer_.setAttribute( 'uvLifeTimeFrameStart', new InterleavedBufferAttribute(this.interleavedBuffer, 4, UV_LIFE_TIME_FRAME_START_IDX));
this.particleBuffer_.setAttribute( 'velocityStartSize', new InterleavedBufferAttribute(this.interleavedBuffer, 4, VELOCITY_START_SIZE_IDX));
this.particleBuffer_.setAttribute( 'accelerationEndSize', new InterleavedBufferAttribute(this.interleavedBuffer, 4, ACCELERATION_END_SIZE_IDX));
this.particleBuffer_.setAttribute( 'spinStartSpinSpeed', new InterleavedBufferAttribute(this.interleavedBuffer, 4, SPIN_START_SPIN_SPEED_IDX));
this.particleBuffer_.setAttribute( 'orientation', new InterleavedBufferAttribute(this.interleavedBuffer, 4, ORIENTATION_IDX));
this.particleBuffer_.setAttribute( 'colorMult', new InterleavedBufferAttribute(this.interleavedBuffer, 4, COLOR_MULT_IDX));
this.particleBuffer_.computeBoundingSphere();
var uniforms = {
//world: { type: 'm4', value: this.matrixWorld },
viewInverse: { type: 'm4', value: this.particleSystem.camera.matrixWorld },
worldVelocity: { type: 'v3', value: null },
worldAcceleration: { type: 'v3', value: null },
timeRange: { type: 'f', value: null },
time: { type: 'f', value: null },
timeOffset: { type: 'f', value: null },
frameDuration: { type: 'f', value: null },
numFrames: { type: 'f', value: null },
rampSampler: { type: "t", value: this.rampTexture_ },
colorSampler: { type: "t", value: this.colorTexture_ }
};
var material = new ShaderMaterial({
uniforms: uniforms,
vertexShader: ( parameters.billboard ) ? billboardParticleInstancedVertexShader : orientedParticleInstancedVertexShader,
fragmentShader: particleFragmentShader,
side: (this.billboard_)? DoubleSide : FrontSide,
blending: this.blendFunc_,
depthTest: true,
depthWrite: false,
transparent: true
});
this.geometry = this.particleBuffer_;
this.material = material;
}
}
setParameters ( parameters, opt_perParticleParamSetter ) {
this.validateParameters ( parameters );
var numParticles = parameters.numParticles;
this.allocateParticles_ ( numParticles, parameters );
this.createParticles_ ( 0, numParticles, parameters, opt_perParticleParamSetter );
}
draw ( world, viewProjection, timeOffset ) {
var uniforms = this.material.uniforms;
uniforms.time.value = this.timeSource_();
uniforms.timeOffset.value = timeOffset;
}
createOneShot () {
return new OneShot( this, this.particleSystem.scene );
}
clone ( object ) {
if ( object === undefined ) object = this.particleSystem.createParticleEmitter( this.colorTexture_, this.timeSource_);
object.geometry = this.geometry;
object.material = this.material.clone();
object.material.uniforms.viewInverse.value = this.particleSystem.camera.matrixWorld;
object.material.uniforms.rampSampler.value = this.rampTexture_;
object.material.uniforms.colorSampler.value = this.colorTexture_;
super.copy( object );
return object;
}
}
// source: https://github.com/greggman/tdl/blob/master/tdl/particles.js
class Trail extends ParticleEmitter {
constructor( particleSystem, maxParticles, parameters, opt_texture, opt_perParticleParamSetter, opt_clock ) {
super(particleSystem, opt_texture, opt_clock);
this.allocateParticles_(maxParticles, parameters);
this.validateParameters(parameters);
this.parameters = parameters;
this.perParticleParamSetter = opt_perParticleParamSetter;
this.birthIndex_ = 0;
this.maxParticles_ = maxParticles;
}
birthParticles ( position ) {
var numParticles = this.parameters.numParticles;
this.parameters.startTime = this.timeSource_();
this.parameters.position = position;
while ( this.birthIndex_ + numParticles >= this.maxParticles_ ) {
var numParticlesToEnd = this.maxParticles_ - this.birthIndex_;
this.createParticles_( this.birthIndex_, numParticlesToEnd, this.parameters, this.perParticleParamSetter );
numParticles -= numParticlesToEnd;
this.birthIndex_ = 0;
}
this.createParticles_( this.birthIndex_, numParticles, this.parameters, this.perParticleParamSetter );
if ( this.birthIndex_ === 0 ) {
this.particleSystem.scene.add( this );
}
this.birthIndex_ += numParticles;
}
}
// source: https://github.com/greggman/tdl/blob/master/tdl/particles.js
class ParticleSystem {
constructor( scene, camera, opt_clock, opt_randomFunction ) {
this.scene = scene;
this.camera = camera;
this.drawables_ = [];
var pixelBase = [0, 0.20, 0.70, 1, 0.70, 0.20, 0, 0];
var pixels = [];
for (var yy = 0; yy < 8; ++yy) {
for (var xx = 0; xx < 8; ++xx) {
var pixel = pixelBase[xx] * pixelBase[yy];
pixels.push(pixel, pixel, pixel, pixel);
}
}
var colorTexture = this.createTextureFromFloats(8, 8, pixels);
var rampTexture = this.createTextureFromFloats(2, 1, [1, 1, 1, 1, 1, 1, 1, 0]);
this.now_ = new Date();
this.timeBase_ = new Date();
if (opt_clock) {
this.timeSource_ = opt_clock;
} else {
this.timeSource_ = createDefaultClock_(this);
}
this.randomFunction_ = opt_randomFunction || function () {
return Math.random();
};
this.defaultColorTexture = colorTexture;
this.defaultRampTexture = rampTexture;
}
createTextureFromFloats ( width, height, pixels, opt_texture ) {
var texture = null;
if ( opt_texture != null ) {
texture = opt_texture;
} else {
var data = new Uint8Array( pixels.length );
var t;
for ( var i = 0; i < pixels.length; i ++ ) {
t = pixels[ i ] * 255.;
data[ i ] = t;
}
texture = new DataTexture( data, width, height, RGBAFormat );
texture.minFilter = LinearFilter;
texture.magFilter = LinearFilter;
texture.needsUpdate = true;
return texture;
}
return texture;
}
createParticleEmitter ( opt_texture, opt_clock ) {
var emitter = new ParticleEmitter( this, opt_texture, opt_clock );
this.drawables_.push( emitter );
return emitter;
}
createTrail ( maxParticles, parameters, opt_texture, opt_perParticleParamSetter, opt_clock ) {
var trail = new Trail( this, maxParticles, parameters, opt_texture, opt_perParticleParamSetter, opt_clock );
this.drawables_.push( trail );
return trail;
}
draw ( viewProjection, world, viewInverse ) {
this.now_ = new Date();
for ( var ii = 0; ii < this.drawables_.length; ++ ii ) {
this.drawables_[ ii ].draw( world, viewProjection, 0 );
}
}
}
export { ACCELERATION_END_SIZE_IDX, COLOR_MULT_IDX, CORNERS_, LAST_IDX, ORIENTATION_IDX, OneShot, POSITION_START_TIME_IDX, ParticleEmitter, ParticleSpec, ParticleSystem, SPIN_START_SPIN_SPEED_IDX, Trail, UV_LIFE_TIME_FRAME_START_IDX, VELOCITY_START_SIZE_IDX, createDefaultClock_, singleParticleArray_ };