UNPKG

three.tdl.particle.system

Version:

GPU based particle system for three.js. Heavily based on tdl library (https://github.com/greggman/tdl)

2 lines (1 loc) 14.7 kB
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("three")):"function"==typeof define&&define.amd?define(["exports","three"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).THREE_TDL_ParticleSystem={},t.THREE)}(this,(function(t,e){"use strict";var r=[[-.5,-.5],[.5,-.5],[.5,.5],[-.5,.5]];function i(t){return function(){var e=t.now_,r=t.timeBase_;return(e.getTime()-r.getTime())/1e3}}var a=12,n=16,o=20,l=24,s=28,c=new Float32Array(112);function u(){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=!0,this.orientation=[0,0,0,1]}class m extends e.Mesh{constructor(t,r){super(),this.emitter_=t.clone(),this.scene=r,this.world_=new e.Matrix4,this.tempWorld_=new e.Matrix4,this.timeOffset_=0,this.visible_=!1;var i=t.particleSystem,a=i.drawables_.indexOf(this.emitter_);a>=0&&i.drawables_.splice(a,1),i.drawables_.push(this)}trigger(t){this.visible_||this.scene.add(this.emitter_),t&&this.emitter_.position.copy((new e.Vector3).fromArray(t)),this.visible_=!0,this.timeOffset_=this.emitter_.timeSource_()}draw(t,e,r){this.visible_&&this.emitter_.draw(this.world_,e,this.timeOffset_)}}class f extends e.Mesh{constructor(t,r,i){super(),i=i||t.timeSource_,this.particleBuffer_=new e.InstancedBufferGeometry,this.interleavedBuffer=new e.InterleavedBuffer,this.numParticles_=0,this.rampTexture_=t.defaultRampTexture,this.colorTexture_=r||t.defaultColorTexture,this.particleSystem=t,this.timeSource_=i,this.setState(e.NormalBlending)}setTranslation(t,e,r){this.position.x=t,this.position.y=e,this.position.z=r}setState(t){this.blendFunc_=t}setColorRamp(t){var e=t.length/4;if(e%1!=0)throw"colorRamp must have multiple of 4 entries";this.rampTexture_==this.particleSystem.defaultRampTexture&&(this.rampTexture_=null),this.rampTexture_=this.particleSystem.createTextureFromFloats(e,1,t,this.rampTexture_)}validateParameters(t){var e=new u;for(var r in t)if(void 0===e[r])throw'unknown particle parameter "'+r+'"';for(var r in e)void 0===t[r]&&(t[r]=e[r])}createParticles_(t,i,c,u){var m=this.interleavedBuffer.array;this.billboard_=c.billboard;for(var f=this.particleSystem.randomFunction_,h=function(t){return(f()-.5)*t*2},d=function(t){for(var e=[],r=0;r<t.length;++r)e.push(h(t[r]));return e},p=0;p<i;++p){u&&u(p,c);for(var v=c.lifeTime,S=null===c.startTime?p*c.lifeTime/i:c.startTime,y=c.frameStart+h(c.frameStartRange),_=(new e.Vector3).addVectors((new e.Vector3).fromArray(c.position),(new e.Vector3).fromArray(d(c.positionRange))),w=(new e.Vector3).addVectors((new e.Vector3).fromArray(c.velocity),(new e.Vector3).fromArray(d(c.velocityRange))),x=(new e.Vector3).addVectors((new e.Vector3).fromArray(c.acceleration),(new e.Vector3).fromArray(d(c.accelerationRange))),T=(new e.Vector4).addVectors((new e.Vector4).fromArray(c.colorMult),(new e.Vector4).fromArray(d(c.colorMultRange))),b=c.spinStart+h(c.spinStartRange),z=c.spinSpeed+h(c.spinSpeedRange),g=c.startSize+h(c.startSizeRange),A=c.endSize+h(c.endSizeRange),P=(new e.Vector4).fromArray(c.orientation),I=0;I<1;++I){var B=s*I+p*s*4+t*s*4,R=B+1,F=B+2,M=B+3;m[0+B]=_.x,m[0+R]=_.y,m[0+F]=_.z,m[0+M]=S,m[4+B]=r[I][0],m[4+R]=r[I][1],m[4+F]=v,m[4+M]=y,m[8+B]=w.x,m[8+R]=w.y,m[8+F]=w.z,m[8+M]=g,m[a+B]=x.x,m[a+R]=x.y,m[a+F]=x.z,m[a+M]=A,m[n+B]=b,m[n+R]=z,m[n+F]=0,m[n+M]=0,m[o+B]=P.x,m[o+R]=P.y,m[o+F]=P.z,m[o+M]=P.w,m[l+B]=T.x,m[l+R]=T.y,m[l+F]=T.z,m[l+M]=T.w}}this.interleavedBuffer.needsUpdate=!0,this.material.uniforms.worldVelocity.value=new e.Vector3(c.worldVelocity[0],c.worldVelocity[1],c.worldVelocity[2]),this.material.uniforms.worldAcceleration.value=new e.Vector3(c.worldAcceleration[0],c.worldAcceleration[1],c.worldAcceleration[2]),this.material.uniforms.timeRange.value=c.timeRange,this.material.uniforms.frameDuration.value=c.frameDuration,this.material.uniforms.numFrames.value=c.numFrames,this.material.uniforms.rampSampler.value=this.rampTexture_,this.material.uniforms.colorSampler.value=this.colorTexture_,this.material.blending=this.blendFunc_}allocateParticles_(t,r){if(this.numParticles_!=t){if(6*t>65536&&e.BufferGeometry.MaxIndex<65536)throw"can't have more than 10922 particles per emitter";var i=new e.InterleavedBuffer(new Float32Array([0,0,0,0,-.5,-.5,0,0,0,0,0,0,.5,-.5,0,0,0,0,0,0,.5,.5,0,0,0,0,0,0,-.5,.5,0,0]),8),u=new e.InterleavedBufferAttribute(i,3,0);this.particleBuffer_.setAttribute("position",u);var m=new e.InterleavedBufferAttribute(i,2,4);this.particleBuffer_.setAttribute("uv",m);var f=new Uint16Array([0,1,2,0,2,3]);this.particleBuffer_.setIndex(new e.BufferAttribute(f,1)),this.numParticles_=t,this.interleavedBuffer=new e.InstancedInterleavedBuffer(new Float32Array(t*c.byteLength),s,1).setUsage(e.DynamicDrawUsage),this.particleBuffer_.setAttribute("position",new e.InterleavedBufferAttribute(this.interleavedBuffer,3,0)),this.particleBuffer_.setAttribute("startTime",new e.InterleavedBufferAttribute(this.interleavedBuffer,1,3)),this.particleBuffer_.setAttribute("uvLifeTimeFrameStart",new e.InterleavedBufferAttribute(this.interleavedBuffer,4,4)),this.particleBuffer_.setAttribute("velocityStartSize",new e.InterleavedBufferAttribute(this.interleavedBuffer,4,8)),this.particleBuffer_.setAttribute("accelerationEndSize",new e.InterleavedBufferAttribute(this.interleavedBuffer,4,a)),this.particleBuffer_.setAttribute("spinStartSpinSpeed",new e.InterleavedBufferAttribute(this.interleavedBuffer,4,n)),this.particleBuffer_.setAttribute("orientation",new e.InterleavedBufferAttribute(this.interleavedBuffer,4,o)),this.particleBuffer_.setAttribute("colorMult",new e.InterleavedBufferAttribute(this.interleavedBuffer,4,l)),this.particleBuffer_.computeBoundingSphere();var h={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_}},d=new e.ShaderMaterial({uniforms:h,vertexShader:r.billboard?"\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\t\tfloat lifeTime = uvLifeTimeFrameStart.z;\n\t\tfloat frameStart = uvLifeTimeFrameStart.w;\n\t\tvec3 velocity = (modelMatrix * vec4(velocityStartSize.xyz,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t 0.)).xyz + worldVelocity;\n\t\tfloat startSize = velocityStartSize.w;\n\t\tvec3 acceleration = (modelMatrix * vec4(accelerationEndSize.xyz,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t 0)).xyz + worldAcceleration;\n\t\tfloat endSize = accelerationEndSize.w;\n\t\tfloat spinStart = spinStartSpinSpeed.x;\n\t\tfloat spinSpeed = spinStartSpinSpeed.y;\n\t\tfloat localTime = mod((time - timeOffset - startTime), timeRange);\n\t\tfloat percentLife = localTime / lifeTime;\n\t\tfloat frame = mod(floor(localTime / frameDuration + frameStart),\n\t\t\t\t\t\t\t\t\t\t numFrames);\n\t\tfloat uOffset = frame / numFrames;\n\t\tfloat u = uOffset + (uv.x + 0.5) * (1. / numFrames);\n\t\toutputTexcoord = vec2(u, uv.y + 0.5);\n\t\toutputColorMult = colorMult;\n\t\tvec3 basisX = viewInverse[0].xyz;\n\t\tvec3 basisZ = viewInverse[1].xyz;\n\t\tvec4 vertexWorld = modelMatrix * vec4(position, 1.0);\n\t\tfloat size = mix(startSize, endSize, percentLife);\n\t\tsize = (percentLife < 0. || percentLife > 1.) ? 0. : size;\n\t\tfloat s = sin(spinStart + spinSpeed * localTime);\n\t\tfloat c = cos(spinStart + spinSpeed * localTime);\n\t\tvec2 rotatedPoint = vec2(uv.x * c + uv.y * s, -uv.x * s + uv.y * c);\n\t\tvec3 localPosition = vec3(basisX * rotatedPoint.x + basisZ * rotatedPoint.y) * size +\n\t\t\t\t\t\t\t\t\t\t\t\tvelocity * localTime +\n\t\t\t\t\t\t\t\t\t\t\t\tacceleration * localTime * localTime +\n\t\t\t\t\t\t\t\t\t\t\t\tvertexWorld.xyz;\n\t\toutputPercentLife = percentLife;\n\t\tgl_Position = projectionMatrix * viewMatrix * vec4(localPosition, 1.);\n}":"\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\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t0.)).xyz + worldVelocity;\nfloat startSize = velocityStartSize.w;\nvec3 acceleration = (world * vec4(accelerationEndSize.xyz,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t0)).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\t\t\t\t\t\t\t\t\tnumFrames);\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\t\t\t\t\t\t\t\t\t\t\t\t (uv.x * s - uv.y * c) * size, 1.);\nvec3 center = velocity * localTime +\n\t\t\t\t\t\t\tacceleration * localTime * localTime +\n\t\t\t\t\t\t\tposition +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\t\t(1.0 - qy.y) - qz.z,\n\t\tqx.y + qz.w,\n\t\tqx.z - qy.w,\n\t\t0,\n\t\tqx.y - qz.w,\n\t\t(1.0 - qx.x) - qz.z,\n\t\tqy.z + qx.w,\n\t\t0,\n\t\tqx.z + qy.w,\n\t\tqy.z - qx.w,\n\t\t(1.0 - qx.x) - qy.y,\n\t\t0,\n\t\tcenter.x, center.y, center.z, 1);\nrotatedPoint = localMatrix * rotatedPoint;\noutputPercentLife = percentLife;\ngl_Position = projectionMatrix * modelViewMatrix * rotatedPoint;\n}",fragmentShader:"\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\t\tvec4 colorMult = texture2D(rampSampler, vec2(outputPercentLife, 0.5)) * outputColorMult;\n\t\tgl_FragColor = texture2D(colorSampler, outputTexcoord) * colorMult;\n}",side:this.billboard_?e.DoubleSide:e.FrontSide,blending:this.blendFunc_,depthTest:!0,depthWrite:!1,transparent:!0});this.geometry=this.particleBuffer_,this.material=d}}setParameters(t,e){this.validateParameters(t);var r=t.numParticles;this.allocateParticles_(r,t),this.createParticles_(0,r,t,e)}draw(t,e,r){var i=this.material.uniforms;i.time.value=this.timeSource_(),i.timeOffset.value=r}createOneShot(){return new m(this,this.particleSystem.scene)}clone(t){return void 0===t&&(t=this.particleSystem.createParticleEmitter(this.colorTexture_,this.timeSource_)),t.geometry=this.geometry,t.material=this.material.clone(),t.material.uniforms.viewInverse.value=this.particleSystem.camera.matrixWorld,t.material.uniforms.rampSampler.value=this.rampTexture_,t.material.uniforms.colorSampler.value=this.colorTexture_,super.copy(t),t}}class h extends f{constructor(t,e,r,i,a,n){super(t,i,n),this.allocateParticles_(e,r),this.validateParameters(r),this.parameters=r,this.perParticleParamSetter=a,this.birthIndex_=0,this.maxParticles_=e}birthParticles(t){var e=this.parameters.numParticles;for(this.parameters.startTime=this.timeSource_(),this.parameters.position=t;this.birthIndex_+e>=this.maxParticles_;){var r=this.maxParticles_-this.birthIndex_;this.createParticles_(this.birthIndex_,r,this.parameters,this.perParticleParamSetter),e-=r,this.birthIndex_=0}this.createParticles_(this.birthIndex_,e,this.parameters,this.perParticleParamSetter),0===this.birthIndex_&&this.particleSystem.scene.add(this),this.birthIndex_+=e}}t.ACCELERATION_END_SIZE_IDX=a,t.COLOR_MULT_IDX=l,t.CORNERS_=r,t.LAST_IDX=s,t.ORIENTATION_IDX=o,t.OneShot=m,t.POSITION_START_TIME_IDX=0,t.ParticleEmitter=f,t.ParticleSpec=u,t.ParticleSystem=class{constructor(t,e,r,a){this.scene=t,this.camera=e,this.drawables_=[];for(var n=[0,.2,.7,1,.7,.2,0,0],o=[],l=0;l<8;++l)for(var s=0;s<8;++s){var c=n[s]*n[l];o.push(c,c,c,c)}var u=this.createTextureFromFloats(8,8,o),m=this.createTextureFromFloats(2,1,[1,1,1,1,1,1,1,0]);this.now_=new Date,this.timeBase_=new Date,this.timeSource_=r||i(this),this.randomFunction_=a||function(){return Math.random()},this.defaultColorTexture=u,this.defaultRampTexture=m}createTextureFromFloats(t,r,i,a){var n=null;if(null==a){for(var o,l=new Uint8Array(i.length),s=0;s<i.length;s++)o=255*i[s],l[s]=o;return(n=new e.DataTexture(l,t,r,e.RGBAFormat)).minFilter=e.LinearFilter,n.magFilter=e.LinearFilter,n.needsUpdate=!0,n}return n=a}createParticleEmitter(t,e){var r=new f(this,t,e);return this.drawables_.push(r),r}createTrail(t,e,r,i,a){var n=new h(this,t,e,r,i,a);return this.drawables_.push(n),n}draw(t,e,r){this.now_=new Date;for(var i=0;i<this.drawables_.length;++i)this.drawables_[i].draw(e,t,0)}},t.SPIN_START_SPIN_SPEED_IDX=n,t.Trail=h,t.UV_LIFE_TIME_FRAME_START_IDX=4,t.VELOCITY_START_SIZE_IDX=8,t.createDefaultClock_=i,t.singleParticleArray_=c,Object.defineProperty(t,"__esModule",{value:!0})}));