UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

161 lines (160 loc) 8.6 kB
import { math } from "../../core/math/math.js"; import { Mat3 } from "../../core/math/mat3.js"; import { Mat4 } from "../../core/math/mat4.js"; import { Vec3 } from "../../core/math/vec3.js"; import { drawQuadWithShader } from "../graphics/quad-render-utils.js"; import { EMITTERSHAPE_BOX } from "../constants.js"; const spawnMatrix3 = new Mat3(); const emitterMatrix3 = new Mat3(); const emitterMatrix3Inv = new Mat3(); class ParticleGPUUpdater { constructor(emitter, gd) { this._emitter = emitter; this.frameRandomUniform = new Float32Array(3); this.emitterPosUniform = new Float32Array(3); this.emitterScaleUniform = new Float32Array([1, 1, 1]); this.worldBoundsMulUniform = new Float32Array(3); this.worldBoundsAddUniform = new Float32Array(3); this.inBoundsSizeUniform = new Float32Array(3); this.inBoundsCenterUniform = new Float32Array(3); this.constantParticleTexIN = gd.scope.resolve("particleTexIN"); this.constantParticleTexOUT = gd.scope.resolve("particleTexOUT"); this.constantEmitterPos = gd.scope.resolve("emitterPos"); this.constantEmitterScale = gd.scope.resolve("emitterScale"); this.constantSpawnBounds = gd.scope.resolve("spawnBounds"); this.constantSpawnPosInnerRatio = gd.scope.resolve("spawnPosInnerRatio"); this.constantSpawnBoundsSphere = gd.scope.resolve("spawnBoundsSphere"); this.constantSpawnBoundsSphereInnerRatio = gd.scope.resolve("spawnBoundsSphereInnerRatio"); this.constantInitialVelocity = gd.scope.resolve("initialVelocity"); this.constantFrameRandom = gd.scope.resolve("frameRandom"); this.constantDelta = gd.scope.resolve("delta"); this.constantRate = gd.scope.resolve("rate"); this.constantRateDiv = gd.scope.resolve("rateDiv"); this.constantLifetime = gd.scope.resolve("lifetime"); this.constantGraphSampleSize = gd.scope.resolve("graphSampleSize"); this.constantGraphNumSamples = gd.scope.resolve("graphNumSamples"); this.constantInternalTex0 = gd.scope.resolve("internalTex0"); this.constantInternalTex1 = gd.scope.resolve("internalTex1"); this.constantInternalTex2 = gd.scope.resolve("internalTex2"); this.constantInternalTex3 = gd.scope.resolve("internalTex3"); this.constantEmitterMatrix = gd.scope.resolve("emitterMatrix"); this.constantEmitterMatrixInv = gd.scope.resolve("emitterMatrixInv"); this.constantNumParticles = gd.scope.resolve("numParticles"); this.constantNumParticlesPot = gd.scope.resolve("numParticlesPot"); this.constantLocalVelocityDivMult = gd.scope.resolve("localVelocityDivMult"); this.constantVelocityDivMult = gd.scope.resolve("velocityDivMult"); this.constantRotSpeedDivMult = gd.scope.resolve("rotSpeedDivMult"); this.constantSeed = gd.scope.resolve("seed"); this.constantStartAngle = gd.scope.resolve("startAngle"); this.constantStartAngle2 = gd.scope.resolve("startAngle2"); this.constantOutBoundsMul = gd.scope.resolve("outBoundsMul"); this.constantOutBoundsAdd = gd.scope.resolve("outBoundsAdd"); this.constantInBoundsSize = gd.scope.resolve("inBoundsSize"); this.constantInBoundsCenter = gd.scope.resolve("inBoundsCenter"); this.constantMaxVel = gd.scope.resolve("maxVel"); this.constantFaceTangent = gd.scope.resolve("faceTangent"); this.constantFaceBinorm = gd.scope.resolve("faceBinorm"); this.constantRadialSpeedDivMult = gd.scope.resolve("radialSpeedDivMult"); } _setInputBounds() { this.inBoundsSizeUniform[0] = this._emitter.prevWorldBoundsSize.x; this.inBoundsSizeUniform[1] = this._emitter.prevWorldBoundsSize.y; this.inBoundsSizeUniform[2] = this._emitter.prevWorldBoundsSize.z; this.constantInBoundsSize.setValue(this.inBoundsSizeUniform); this.inBoundsCenterUniform[0] = this._emitter.prevWorldBoundsCenter.x; this.inBoundsCenterUniform[1] = this._emitter.prevWorldBoundsCenter.y; this.inBoundsCenterUniform[2] = this._emitter.prevWorldBoundsCenter.z; this.constantInBoundsCenter.setValue(this.inBoundsCenterUniform); } randomize() { this.frameRandomUniform[0] = Math.random(); this.frameRandomUniform[1] = Math.random(); this.frameRandomUniform[2] = Math.random(); } // This shouldn't change emitter state, only read from it update(device, spawnMatrix, extentsInnerRatioUniform, delta, isOnStop) { const emitter = this._emitter; device.setDrawStates(); this.randomize(); this.constantRadialSpeedDivMult.setValue(emitter.material.getParameter("radialSpeedDivMult").data); this.constantGraphSampleSize.setValue(1 / emitter.precision); this.constantGraphNumSamples.setValue(emitter.precision); this.constantNumParticles.setValue(emitter.numParticles); this.constantNumParticlesPot.setValue(emitter.numParticlesPot); this.constantInternalTex0.setValue(emitter.internalTex0); this.constantInternalTex1.setValue(emitter.internalTex1); this.constantInternalTex2.setValue(emitter.internalTex2); this.constantInternalTex3.setValue(emitter.internalTex3); const node = emitter.meshInstance.node; const emitterScale = node === null ? Vec3.ONE : node.localScale; if (emitter.pack8) { this.worldBoundsMulUniform[0] = emitter.worldBoundsMul.x; this.worldBoundsMulUniform[1] = emitter.worldBoundsMul.y; this.worldBoundsMulUniform[2] = emitter.worldBoundsMul.z; this.constantOutBoundsMul.setValue(this.worldBoundsMulUniform); this.worldBoundsAddUniform[0] = emitter.worldBoundsAdd.x; this.worldBoundsAddUniform[1] = emitter.worldBoundsAdd.y; this.worldBoundsAddUniform[2] = emitter.worldBoundsAdd.z; this.constantOutBoundsAdd.setValue(this.worldBoundsAddUniform); this._setInputBounds(); let maxVel = emitter.maxVel * Math.max(Math.max(emitterScale.x, emitterScale.y), emitterScale.z); maxVel = Math.max(maxVel, 1); this.constantMaxVel.setValue(maxVel); } const emitterPos = node === null || emitter.localSpace ? Vec3.ZERO : node.getPosition(); const emitterMatrix = node === null ? Mat4.IDENTITY : node.getWorldTransform(); if (emitter.emitterShape === EMITTERSHAPE_BOX) { spawnMatrix3.setFromMat4(spawnMatrix); this.constantSpawnBounds.setValue(spawnMatrix3.data); this.constantSpawnPosInnerRatio.setValue(extentsInnerRatioUniform); } else { this.constantSpawnBoundsSphere.setValue(emitter.emitterRadius); this.constantSpawnBoundsSphereInnerRatio.setValue(emitter.emitterRadius === 0 ? 0 : emitter.emitterRadiusInner / emitter.emitterRadius); } this.constantInitialVelocity.setValue(emitter.initialVelocity); emitterMatrix3.setFromMat4(emitterMatrix); emitterMatrix3Inv.invertMat4(emitterMatrix); this.emitterPosUniform[0] = emitterPos.x; this.emitterPosUniform[1] = emitterPos.y; this.emitterPosUniform[2] = emitterPos.z; this.constantEmitterPos.setValue(this.emitterPosUniform); this.constantFrameRandom.setValue(this.frameRandomUniform); this.constantDelta.setValue(delta); this.constantRate.setValue(emitter.rate); this.constantRateDiv.setValue(emitter.rate2 - emitter.rate); this.constantStartAngle.setValue(emitter.startAngle * math.DEG_TO_RAD); this.constantStartAngle2.setValue(emitter.startAngle2 * math.DEG_TO_RAD); this.constantSeed.setValue(emitter.seed); this.constantLifetime.setValue(emitter.lifetime); this.emitterScaleUniform[0] = emitterScale.x; this.emitterScaleUniform[1] = emitterScale.y; this.emitterScaleUniform[2] = emitterScale.z; this.constantEmitterScale.setValue(this.emitterScaleUniform); this.constantEmitterMatrix.setValue(emitterMatrix3.data); this.constantEmitterMatrixInv.setValue(emitterMatrix3Inv.data); this.constantLocalVelocityDivMult.setValue(emitter.localVelocityUMax); this.constantVelocityDivMult.setValue(emitter.velocityUMax); this.constantRotSpeedDivMult.setValue(emitter.rotSpeedUMax[0]); let texIN = emitter.swapTex ? emitter.particleTexOUT : emitter.particleTexIN; texIN = emitter.beenReset ? emitter.particleTexStart : texIN; const texOUT = emitter.swapTex ? emitter.particleTexIN : emitter.particleTexOUT; this.constantParticleTexIN.setValue(texIN); drawQuadWithShader( device, emitter.swapTex ? emitter.rtParticleTexIN : emitter.rtParticleTexOUT, !isOnStop ? emitter.loop ? emitter.shaderParticleUpdateRespawn : emitter.shaderParticleUpdateNoRespawn : emitter.shaderParticleUpdateOnStop ); emitter.material.setParameter("particleTexOUT", texIN); emitter.material.setParameter("particleTexIN", texOUT); emitter.beenReset = false; emitter.swapTex = !emitter.swapTex; emitter.prevWorldBoundsSize.copy(emitter.worldBoundsSize); emitter.prevWorldBoundsCenter.copy(emitter.worldBounds.center); if (emitter.pack8) { this._setInputBounds(); } } } export { ParticleGPUUpdater };