/** * three.quarks v0.15.6 build Fri Jan 17 2025 * https://quarks.art * Copyright 2025 Alchemist0823 , MIT */ 'use strict'; var three = require('three'); var quarks_core = require('quarks.core'); var soft_fragment = ` #ifdef SOFT_PARTICLES /* #ifdef LOGDEPTH float distSample = linearize_depth_log(sampleDepth, near, far); #else float distSample = ortho ? linearize_depth_ortho(sampleDepth, near, far) : linearize_depth(sampleDepth, near, far); #endif */ vec2 p2 = projPosition.xy / projPosition.w; p2 = 0.5 * p2 + 0.5; float readDepth = texture2D(depthTexture, p2.xy).r; float viewDepth = linearize_depth(readDepth); float softParticlesFade = saturate(SOFT_INV_FADE_DISTANCE * ((viewDepth - SOFT_NEAR_FADE) - linearDepth)); gl_FragColor *= softParticlesFade; //gl_FragColor = vec4(softParticlesFade , 0, 0, 1); #endif `; var soft_pars_fragment = ` #ifdef SOFT_PARTICLES uniform sampler2D depthTexture; uniform vec4 projParams; uniform vec2 softParams; varying vec4 projPosition; varying float linearDepth; #define SOFT_NEAR_FADE softParams.x #define SOFT_INV_FADE_DISTANCE softParams.y #define zNear projParams.x #define zFar projParams.y float linearize_depth(float d) { return (zFar * zNear) / (zFar - d * (zFar - zNear)); } #endif `; var soft_pars_vertex = ` #ifdef SOFT_PARTICLES varying vec4 projPosition; varying float linearDepth; #endif `; var soft_vertex = ` #ifdef SOFT_PARTICLES projPosition = gl_Position; linearDepth = -mvPosition.z; #endif `; var tile_fragment = ` #ifdef USE_MAP vec4 texelColor = texture2D( map, vUv); #ifdef TILE_BLEND texelColor = mix( texelColor, texture2D( map, vUvNext ), vUvBlend ); #endif diffuseColor *= texelColor; #endif `; var tile_pars_fragment = ` #if defined( USE_UV ) || defined( USE_ANISOTROPY ) \tvarying vec2 vUv; #ifdef TILE_BLEND varying vec2 vUvNext; varying float vUvBlend; #endif #endif #ifdef USE_MAP \tuniform mat3 mapTransform; \tvarying vec2 vMapUv; #ifdef TILE_BLEND varying vec2 vMapUvNext; #endif #endif #ifdef USE_ALPHAMAP \tuniform mat3 alphaMapTransform; \tvarying vec2 vAlphaMapUv; #endif #ifdef USE_LIGHTMAP \tuniform mat3 lightMapTransform; \tvarying vec2 vLightMapUv; #endif #ifdef USE_AOMAP \tuniform mat3 aoMapTransform; \tvarying vec2 vAoMapUv; #endif #ifdef USE_BUMPMAP \tuniform mat3 bumpMapTransform; \tvarying vec2 vBumpMapUv; #endif #ifdef USE_NORMALMAP \tuniform mat3 normalMapTransform; \tvarying vec2 vNormalMapUv; #endif #ifdef USE_DISPLACEMENTMAP \tuniform mat3 displacementMapTransform; \tvarying vec2 vDisplacementMapUv; #endif #ifdef USE_EMISSIVEMAP \tuniform mat3 emissiveMapTransform; \tvarying vec2 vEmissiveMapUv; #endif #ifdef USE_METALNESSMAP \tuniform mat3 metalnessMapTransform; \tvarying vec2 vMetalnessMapUv; #endif #ifdef USE_ROUGHNESSMAP \tuniform mat3 roughnessMapTransform; \tvarying vec2 vRoughnessMapUv; #endif #ifdef USE_ANISOTROPYMAP \tuniform mat3 anisotropyMapTransform; \tvarying vec2 vAnisotropyMapUv; #endif #ifdef USE_CLEARCOATMAP \tuniform mat3 clearcoatMapTransform; \tvarying vec2 vClearcoatMapUv; #endif #ifdef USE_CLEARCOAT_NORMALMAP \tuniform mat3 clearcoatNormalMapTransform; \tvarying vec2 vClearcoatNormalMapUv; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP \tuniform mat3 clearcoatRoughnessMapTransform; \tvarying vec2 vClearcoatRoughnessMapUv; #endif #ifdef USE_SHEEN_COLORMAP \tuniform mat3 sheenColorMapTransform; \tvarying vec2 vSheenColorMapUv; #endif #ifdef USE_SHEEN_ROUGHNESSMAP \tuniform mat3 sheenRoughnessMapTransform; \tvarying vec2 vSheenRoughnessMapUv; #endif #ifdef USE_IRIDESCENCEMAP \tuniform mat3 iridescenceMapTransform; \tvarying vec2 vIridescenceMapUv; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP \tuniform mat3 iridescenceThicknessMapTransform; \tvarying vec2 vIridescenceThicknessMapUv; #endif #ifdef USE_SPECULARMAP \tuniform mat3 specularMapTransform; \tvarying vec2 vSpecularMapUv; #endif #ifdef USE_SPECULAR_COLORMAP \tuniform mat3 specularColorMapTransform; \tvarying vec2 vSpecularColorMapUv; #endif #ifdef USE_SPECULAR_INTENSITYMAP \tuniform mat3 specularIntensityMapTransform; \tvarying vec2 vSpecularIntensityMapUv; #endif #ifdef USE_TRANSMISSIONMAP \tuniform mat3 transmissionMapTransform; \tvarying vec2 vTransmissionMapUv; #endif #ifdef USE_THICKNESSMAP \tuniform mat3 thicknessMapTransform; \tvarying vec2 vThicknessMapUv; #endif `; var tile_pars_vertex = ` #ifdef UV_TILE attribute float uvTile; uniform vec2 tileCount; mat3 makeTileTransform(float uvTile) { float col = mod(uvTile, tileCount.x); float row = (tileCount.y - floor(uvTile / tileCount.x) - 1.0); return mat3( 1.0 / tileCount.x, 0.0, 0.0, 0.0, 1.0 / tileCount.y, 0.0, col / tileCount.x, row / tileCount.y, 1.0); } #else mat3 makeTileTransform(float uvTile) { return mat3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0); } #endif #if defined( USE_UV ) || defined( USE_ANISOTROPY ) \tvarying vec2 vUv; #ifdef TILE_BLEND varying vec2 vUvNext; varying float vUvBlend; #endif #endif #ifdef USE_MAP \tuniform mat3 mapTransform; \tvarying vec2 vMapUv; #ifdef TILE_BLEND varying vec2 vMapUvNext; #endif #endif #ifdef USE_ALPHAMAP \tuniform mat3 alphaMapTransform; \tvarying vec2 vAlphaMapUv; #endif #ifdef USE_LIGHTMAP \tuniform mat3 lightMapTransform; \tvarying vec2 vLightMapUv; #endif #ifdef USE_AOMAP \tuniform mat3 aoMapTransform; \tvarying vec2 vAoMapUv; #endif #ifdef USE_BUMPMAP \tuniform mat3 bumpMapTransform; \tvarying vec2 vBumpMapUv; #endif #ifdef USE_NORMALMAP \tuniform mat3 normalMapTransform; \tvarying vec2 vNormalMapUv; #endif #ifdef USE_DISPLACEMENTMAP \tuniform mat3 displacementMapTransform; \tvarying vec2 vDisplacementMapUv; #endif #ifdef USE_EMISSIVEMAP \tuniform mat3 emissiveMapTransform; \tvarying vec2 vEmissiveMapUv; #endif #ifdef USE_METALNESSMAP \tuniform mat3 metalnessMapTransform; \tvarying vec2 vMetalnessMapUv; #endif #ifdef USE_ROUGHNESSMAP \tuniform mat3 roughnessMapTransform; \tvarying vec2 vRoughnessMapUv; #endif #ifdef USE_ANISOTROPYMAP \tuniform mat3 anisotropyMapTransform; \tvarying vec2 vAnisotropyMapUv; #endif #ifdef USE_CLEARCOATMAP \tuniform mat3 clearcoatMapTransform; \tvarying vec2 vClearcoatMapUv; #endif #ifdef USE_CLEARCOAT_NORMALMAP \tuniform mat3 clearcoatNormalMapTransform; \tvarying vec2 vClearcoatNormalMapUv; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP \tuniform mat3 clearcoatRoughnessMapTransform; \tvarying vec2 vClearcoatRoughnessMapUv; #endif #ifdef USE_SHEEN_COLORMAP \tuniform mat3 sheenColorMapTransform; \tvarying vec2 vSheenColorMapUv; #endif #ifdef USE_SHEEN_ROUGHNESSMAP \tuniform mat3 sheenRoughnessMapTransform; \tvarying vec2 vSheenRoughnessMapUv; #endif #ifdef USE_IRIDESCENCEMAP \tuniform mat3 iridescenceMapTransform; \tvarying vec2 vIridescenceMapUv; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP \tuniform mat3 iridescenceThicknessMapTransform; \tvarying vec2 vIridescenceThicknessMapUv; #endif #ifdef USE_SPECULARMAP \tuniform mat3 specularMapTransform; \tvarying vec2 vSpecularMapUv; #endif #ifdef USE_SPECULAR_COLORMAP \tuniform mat3 specularColorMapTransform; \tvarying vec2 vSpecularColorMapUv; #endif #ifdef USE_SPECULAR_INTENSITYMAP \tuniform mat3 specularIntensityMapTransform; \tvarying vec2 vSpecularIntensityMapUv; #endif #ifdef USE_TRANSMISSIONMAP \tuniform mat3 transmissionMapTransform; \tvarying vec2 vTransmissionMapUv; #endif #ifdef USE_THICKNESSMAP \tuniform mat3 thicknessMapTransform; \tvarying vec2 vThicknessMapUv; #endif `; var tile_vertex = ` #ifdef UV_TILE mat3 tileTransform = makeTileTransform(floor(uvTile)); #ifdef TILE_BLEND mat3 nextTileTransform = makeTileTransform(ceil(uvTile)); vUvBlend = fract(uvTile); #endif #else mat3 tileTransform = makeTileTransform(0.0); #endif #if defined( USE_UV ) || defined( USE_ANISOTROPY ) vUv = (tileTransform *vec3( uv, 1 )).xy; #if defined( TILE_BLEND ) && defined( UV_TILE ) vUvNext = (nextTileTransform *vec3( uv, 1 )).xy; #endif #endif #ifdef USE_MAP vMapUv = ( tileTransform * (mapTransform * vec3( MAP_UV, 1 ) )).xy; #if defined( TILE_BLEND ) && defined( UV_TILE ) vMapUvNext = (nextTileTransform * (mapTransform * vec3( MAP_UV, 1 ))).xy; #endif #endif #ifdef USE_ALPHAMAP vAlphaMapUv = ( tileTransform * (alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) )).xy; #endif #ifdef USE_LIGHTMAP vLightMapUv = ( tileTransform * (lightMapTransform * vec3( LIGHTMAP_UV, 1 ) )).xy; #endif #ifdef USE_AOMAP vAoMapUv = ( tileTransform * (aoMapTransform * vec3( AOMAP_UV, 1 ) )).xy; #endif #ifdef USE_BUMPMAP vBumpMapUv = ( tileTransform * (bumpMapTransform * vec3( BUMPMAP_UV, 1 ) )).xy; #endif #ifdef USE_NORMALMAP vNormalMapUv = ( tileTransform * (normalMapTransform * vec3( NORMALMAP_UV, 1 ) )).xy; #endif #ifdef USE_DISPLACEMENTMAP vDisplacementMapUv = ( tileTransform * (displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) )).xy; #endif #ifdef USE_EMISSIVEMAP vEmissiveMapUv = ( tileTransform * (emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) )).xy; #endif #ifdef USE_METALNESSMAP vMetalnessMapUv = ( tileTransform * (metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) )).xy; #endif #ifdef USE_ROUGHNESSMAP vRoughnessMapUv = ( tileTransform * (roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) )).xy; #endif #ifdef USE_ANISOTROPYMAP vAnisotropyMapUv = ( tileTransform * (anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) )).xy; #endif #ifdef USE_CLEARCOATMAP vClearcoatMapUv = ( tileTransform * (clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) )).xy; #endif #ifdef USE_CLEARCOAT_NORMALMAP vClearcoatNormalMapUv = ( tileTransform * (clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) )).xy; #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP vClearcoatRoughnessMapUv = ( tileTransform * (clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) )).xy; #endif #ifdef USE_IRIDESCENCEMAP vIridescenceMapUv = ( tileTransform * (iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) )).xy; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP vIridescenceThicknessMapUv = ( tileTransform * (iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) )).xy; #endif #ifdef USE_SHEEN_COLORMAP vSheenColorMapUv = ( tileTransform * (sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) )).xy; #endif #ifdef USE_SHEEN_ROUGHNESSMAP vSheenRoughnessMapUv = ( tileTransform * (sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) )).xy; #endif #ifdef USE_SPECULARMAP vSpecularMapUv = ( tileTransform * (specularMapTransform * vec3( SPECULARMAP_UV, 1 ) )).xy; #endif #ifdef USE_SPECULAR_COLORMAP vSpecularColorMapUv = ( tileTransform * (specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) )).xy; #endif #ifdef USE_SPECULAR_INTENSITYMAP vSpecularIntensityMapUv = ( tileTransform * (specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) )).xy; #endif #ifdef USE_TRANSMISSIONMAP vTransmissionMapUv = ( tileTransform * transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) )).xy; #endif #ifdef USE_THICKNESSMAP vThicknessMapUv = ( tileTransform * thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) )).xy; #endif `; const ShaderChunk = three.ShaderChunk; function registerShaderChunks() { ShaderChunk['tile_pars_vertex'] = tile_pars_vertex; ShaderChunk['tile_vertex'] = tile_vertex; ShaderChunk['tile_pars_fragment'] = tile_pars_fragment; ShaderChunk['tile_fragment'] = tile_fragment; ShaderChunk['soft_pars_vertex'] = soft_pars_vertex; ShaderChunk['soft_vertex'] = soft_vertex; ShaderChunk['soft_pars_fragment'] = soft_pars_fragment; ShaderChunk['soft_fragment'] = soft_fragment; } class ParticleEmitter extends three.Object3D { constructor(system) { super(); this.type = 'ParticleEmitter'; this.system = system; } clone() { const system = this.system.clone(); system.emitter.copy(this, true); return system.emitter; } dispose() { } extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } toJSON(meta, options = {}) { const children = this.children; this.children = this.children.filter((child) => child.type !== 'ParticleSystemPreview'); const data = super.toJSON(meta); this.children = children; if (this.system !== null) data.object.ps = this.system.toJSON(meta, options); return data; } } exports.RenderMode = void 0; (function (RenderMode) { RenderMode[RenderMode["BillBoard"] = 0] = "BillBoard"; RenderMode[RenderMode["StretchedBillBoard"] = 1] = "StretchedBillBoard"; RenderMode[RenderMode["Mesh"] = 2] = "Mesh"; RenderMode[RenderMode["Trail"] = 3] = "Trail"; RenderMode[RenderMode["HorizontalBillBoard"] = 4] = "HorizontalBillBoard"; RenderMode[RenderMode["VerticalBillBoard"] = 5] = "VerticalBillBoard"; })(exports.RenderMode || (exports.RenderMode = {})); class VFXBatch extends three.Mesh { constructor(settings) { super(); this.type = 'VFXBatch'; this.maxParticles = 1000; this.systems = new Set(); const layers = new three.Layers(); layers.mask = settings.layers.mask; const newMat = settings.material.clone(); newMat.defines = {}; Object.assign(newMat.defines, settings.material.defines); this.settings = { instancingGeometry: settings.instancingGeometry, renderMode: settings.renderMode, renderOrder: settings.renderOrder, material: newMat, uTileCount: settings.uTileCount, vTileCount: settings.vTileCount, blendTiles: settings.blendTiles, softParticles: settings.softParticles, softNearFade: settings.softNearFade, softFarFade: settings.softFarFade, layers: layers, }; this.frustumCulled = false; this.renderOrder = this.settings.renderOrder; } addSystem(system) { this.systems.add(system); } removeSystem(system) { this.systems.delete(system); } applyDepthTexture(depthTexture) { const uniform = this.material.uniforms['depthTexture']; if (uniform) { if (uniform.value !== depthTexture) { uniform.value = depthTexture; this.material.needsUpdate = true; } } } } const UP = new quarks_core.Vector3(0, 0, 1); const tempQ = new quarks_core.Quaternion(); const tempV = new quarks_core.Vector3(); const tempV2 = new quarks_core.Vector3(); new quarks_core.Vector3(); const PREWARM_FPS = 60; const DEFAULT_GEOMETRY = new three.PlaneGeometry(1, 1, 1, 1); class ParticleSystem { set time(time) { this.emissionState.time = time; } get time() { return this.emissionState.time; } get layers() { return this.rendererSettings.layers; } get texture() { return this.rendererSettings.material.map; } set texture(texture) { this.rendererSettings.material.map = texture; this.neededToUpdateRender = true; } get material() { return this.rendererSettings.material; } set material(material) { this.rendererSettings.material = material; this.neededToUpdateRender = true; } get uTileCount() { return this.rendererSettings.uTileCount; } set uTileCount(u) { this.rendererSettings.uTileCount = u; this.neededToUpdateRender = true; } get vTileCount() { return this.rendererSettings.vTileCount; } set vTileCount(v) { this.rendererSettings.vTileCount = v; this.neededToUpdateRender = true; } get blendTiles() { return this.rendererSettings.blendTiles; } set blendTiles(v) { this.rendererSettings.blendTiles = v; this.neededToUpdateRender = true; } get softParticles() { return this.rendererSettings.softParticles; } set softParticles(v) { this.rendererSettings.softParticles = v; this.neededToUpdateRender = true; } get softNearFade() { return this.rendererSettings.softNearFade; } set softNearFade(v) { this.rendererSettings.softNearFade = v; this.neededToUpdateRender = true; } get softFarFade() { return this.rendererSettings.softFarFade; } set softFarFade(v) { this.rendererSettings.softFarFade = v; this.neededToUpdateRender = true; } get instancingGeometry() { return this.rendererSettings.instancingGeometry; } set instancingGeometry(geometry) { this.restart(); this.particles.length = 0; this.rendererSettings.instancingGeometry = geometry; this.neededToUpdateRender = true; } get renderMode() { return this.rendererSettings.renderMode; } set renderMode(renderMode) { if ((this.rendererSettings.renderMode != exports.RenderMode.Trail && renderMode === exports.RenderMode.Trail) || (this.rendererSettings.renderMode == exports.RenderMode.Trail && renderMode !== exports.RenderMode.Trail)) { this.restart(); this.particles.length = 0; } if (this.rendererSettings.renderMode !== renderMode) { switch (renderMode) { case exports.RenderMode.Trail: this.rendererEmitterSettings = { startLength: new quarks_core.ConstantValue(30), followLocalOrigin: false, }; break; case exports.RenderMode.Mesh: this.rendererEmitterSettings = { geometry: new three.PlaneGeometry(1, 1), }; this.startRotation = new quarks_core.AxisAngleGenerator(new quarks_core.Vector3(0, 1, 0), new quarks_core.ConstantValue(0)); break; case exports.RenderMode.StretchedBillBoard: this.rendererEmitterSettings = { speedFactor: 0, lengthFactor: 2 }; if (this.rendererSettings.renderMode === exports.RenderMode.Mesh) { this.startRotation = new quarks_core.ConstantValue(0); } break; case exports.RenderMode.BillBoard: case exports.RenderMode.VerticalBillBoard: case exports.RenderMode.HorizontalBillBoard: this.rendererEmitterSettings = {}; if (this.rendererSettings.renderMode === exports.RenderMode.Mesh) { this.startRotation = new quarks_core.ConstantValue(0); } break; } } this.rendererSettings.renderMode = renderMode; this.neededToUpdateRender = true; } get renderOrder() { return this.rendererSettings.renderOrder; } set renderOrder(renderOrder) { this.rendererSettings.renderOrder = renderOrder; this.neededToUpdateRender = true; } get blending() { return this.rendererSettings.material.blending; } set blending(blending) { this.rendererSettings.material.blending = blending; this.neededToUpdateRender = true; } constructor(parameters) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1; this.temp = new quarks_core.Vector3(); this.travelDistance = 0; this.normalMatrix = new quarks_core.Matrix3(); this.memory = []; this.listeners = {}; this.firstTimeUpdate = true; this.autoDestroy = parameters.autoDestroy === undefined ? false : parameters.autoDestroy; this.duration = (_a = parameters.duration) !== null && _a !== void 0 ? _a : 1; this.looping = parameters.looping === undefined ? true : parameters.looping; this.prewarm = parameters.prewarm === undefined ? false : parameters.prewarm; this.startLife = (_b = parameters.startLife) !== null && _b !== void 0 ? _b : new quarks_core.ConstantValue(5); this.startSpeed = (_c = parameters.startSpeed) !== null && _c !== void 0 ? _c : new quarks_core.ConstantValue(0); this.startRotation = (_d = parameters.startRotation) !== null && _d !== void 0 ? _d : new quarks_core.ConstantValue(0); this.startSize = (_e = parameters.startSize) !== null && _e !== void 0 ? _e : new quarks_core.ConstantValue(1); this.startColor = (_f = parameters.startColor) !== null && _f !== void 0 ? _f : new quarks_core.ConstantColor(new quarks_core.Vector4(1, 1, 1, 1)); this.emissionOverTime = (_g = parameters.emissionOverTime) !== null && _g !== void 0 ? _g : new quarks_core.ConstantValue(10); this.emissionOverDistance = (_h = parameters.emissionOverDistance) !== null && _h !== void 0 ? _h : new quarks_core.ConstantValue(0); this.emissionBursts = (_j = parameters.emissionBursts) !== null && _j !== void 0 ? _j : []; this.onlyUsedByOther = (_k = parameters.onlyUsedByOther) !== null && _k !== void 0 ? _k : false; this.emitterShape = (_l = parameters.shape) !== null && _l !== void 0 ? _l : new quarks_core.SphereEmitter(); this.behaviors = (_m = parameters.behaviors) !== null && _m !== void 0 ? _m : new Array(); this.worldSpace = (_o = parameters.worldSpace) !== null && _o !== void 0 ? _o : false; this.rendererEmitterSettings = (_p = parameters.rendererEmitterSettings) !== null && _p !== void 0 ? _p : {}; if (parameters.renderMode === exports.RenderMode.StretchedBillBoard) { const stretchedBillboardSettings = this.rendererEmitterSettings; if (parameters.speedFactor !== undefined) { stretchedBillboardSettings.speedFactor = parameters.speedFactor; } stretchedBillboardSettings.speedFactor = (_q = stretchedBillboardSettings.speedFactor) !== null && _q !== void 0 ? _q : 0; stretchedBillboardSettings.lengthFactor = (_r = stretchedBillboardSettings.lengthFactor) !== null && _r !== void 0 ? _r : 0; } this.rendererSettings = { instancingGeometry: (_s = parameters.instancingGeometry) !== null && _s !== void 0 ? _s : DEFAULT_GEOMETRY, renderMode: (_t = parameters.renderMode) !== null && _t !== void 0 ? _t : exports.RenderMode.BillBoard, renderOrder: (_u = parameters.renderOrder) !== null && _u !== void 0 ? _u : 0, material: parameters.material, uTileCount: (_v = parameters.uTileCount) !== null && _v !== void 0 ? _v : 1, vTileCount: (_w = parameters.vTileCount) !== null && _w !== void 0 ? _w : 1, blendTiles: (_x = parameters.blendTiles) !== null && _x !== void 0 ? _x : false, softParticles: (_y = parameters.softParticles) !== null && _y !== void 0 ? _y : false, softNearFade: (_z = parameters.softNearFade) !== null && _z !== void 0 ? _z : 0, softFarFade: (_0 = parameters.softFarFade) !== null && _0 !== void 0 ? _0 : 0, layers: (_1 = parameters.layers) !== null && _1 !== void 0 ? _1 : new three.Layers(), }; this.neededToUpdateRender = true; this.particles = new Array(); this.startTileIndex = parameters.startTileIndex || new quarks_core.ConstantValue(0); this.emitter = new ParticleEmitter(this); this.paused = false; this.particleNum = 0; this.emissionState = { isBursting: false, burstParticleIndex: 0, burstParticleCount: 0, burstIndex: 0, burstWaveIndex: 0, time: 0, waitEmiting: 0, travelDistance: 0, }; this.emissionBursts.forEach((burst) => burst.count.startGen(this.memory)); this.emissionOverDistance.startGen(this.memory); this.emitEnded = false; this.markForDestroy = false; this.prewarmed = false; } pause() { this.paused = true; } play() { this.paused = false; } stop() { this.restart(); this.pause(); } spawn(count, emissionState, matrix) { tempQ.setFromRotationMatrix(matrix); const translation = tempV; const quaternion = tempQ; const scale = tempV2; matrix.decompose(translation, quaternion, scale); for (let i = 0; i < count; i++) { emissionState.burstParticleIndex = i; this.particleNum++; while (this.particles.length < this.particleNum) { if (this.rendererSettings.renderMode === exports.RenderMode.Trail) { this.particles.push(new quarks_core.TrailParticle()); } else { this.particles.push(new quarks_core.SpriteParticle()); } } const particle = this.particles[this.particleNum - 1]; particle.reset(); particle.speedModifier = 1; this.startColor.startGen(particle.memory); this.startColor.genColor(particle.memory, particle.startColor, this.emissionState.time); particle.color.copy(particle.startColor); this.startSpeed.startGen(particle.memory); particle.startSpeed = this.startSpeed.genValue(particle.memory, emissionState.time / this.duration); this.startLife.startGen(particle.memory); particle.life = this.startLife.genValue(particle.memory, emissionState.time / this.duration); particle.age = 0; this.startSize.startGen(particle.memory); if (this.startSize.type === "vec3function") { this.startSize.genValue(particle.memory, particle.startSize, emissionState.time / this.duration); } else { const size = this.startSize.genValue(particle.memory, emissionState.time / this.duration); particle.startSize.set(size, size, size); } this.startTileIndex.startGen(particle.memory); particle.uvTile = this.startTileIndex.genValue(particle.memory); particle.size.copy(particle.startSize); if (this.rendererSettings.renderMode === exports.RenderMode.Mesh || this.rendererSettings.renderMode === exports.RenderMode.BillBoard || this.rendererSettings.renderMode === exports.RenderMode.VerticalBillBoard || this.rendererSettings.renderMode === exports.RenderMode.HorizontalBillBoard || this.rendererSettings.renderMode === exports.RenderMode.StretchedBillBoard) { const sprite = particle; this.startRotation.startGen(particle.memory); if (this.rendererSettings.renderMode === exports.RenderMode.Mesh) { if (!(sprite.rotation instanceof quarks_core.Quaternion)) { sprite.rotation = new quarks_core.Quaternion(); } if (this.startRotation.type === 'rotation') { this.startRotation.genValue(particle.memory, sprite.rotation, 1, emissionState.time / this.duration); } else { sprite.rotation.setFromAxisAngle(UP, this.startRotation.genValue(sprite.memory, (emissionState.time / this.duration))); } } else { if (this.startRotation.type === 'rotation') { sprite.rotation = 0; } else { sprite.rotation = this.startRotation.genValue(sprite.memory, emissionState.time / this.duration); } } } else if (this.rendererSettings.renderMode === exports.RenderMode.Trail) { const trail = particle; this.rendererEmitterSettings.startLength.startGen(trail.memory); trail.length = this.rendererEmitterSettings.startLength.genValue(trail.memory, emissionState.time / this.duration); } this.emitterShape.initialize(particle, emissionState); if (this.rendererSettings.renderMode === exports.RenderMode.Trail && this.rendererEmitterSettings.followLocalOrigin) { const trail = particle; trail.localPosition = new quarks_core.Vector3().copy(trail.position); } if (this.worldSpace) { particle.position.applyMatrix4(matrix); particle.startSize.multiply(scale).abs(); particle.size.copy(particle.startSize); particle.velocity.multiply(scale).applyMatrix3(this.normalMatrix); if (particle.rotation && particle.rotation instanceof quarks_core.Quaternion) { particle.rotation.multiplyQuaternions(tempQ, particle.rotation); } } else { if (this.onlyUsedByOther) { particle.parentMatrix = matrix; } } for (let j = 0; j < this.behaviors.length; j++) { this.behaviors[j].initialize(particle, this); } } } endEmit() { this.emitEnded = true; if (this.autoDestroy) { this.markForDestroy = true; } this.fire({ type: "emitEnd", particleSystem: this }); } dispose() { if (this._renderer) this._renderer.deleteSystem(this); this.emitter.dispose(); if (this.emitter.parent) this.emitter.parent.remove(this.emitter); this.fire({ type: "destroy", particleSystem: this }); } restart() { this.memory.length = 0; this.paused = false; this.particleNum = 0; this.emissionState.isBursting = false; this.emissionState.burstIndex = 0; this.emissionState.burstWaveIndex = 0; this.emissionState.time = 0; this.emissionState.waitEmiting = 0; this.behaviors.forEach((behavior) => { behavior.reset(); }); this.emitEnded = false; this.markForDestroy = false; this.prewarmed = false; this.emissionBursts.forEach((burst) => burst.count.startGen(this.memory)); this.emissionOverDistance.startGen(this.memory); } update(delta) { if (this.paused) return; let currentParent = this.emitter; while (currentParent.parent) { currentParent = currentParent.parent; } if (currentParent.type !== 'Scene') { this.dispose(); return; } if (this.firstTimeUpdate) { this.firstTimeUpdate = false; this.emitter.updateWorldMatrix(true, false); } if (this.emitEnded && this.particleNum === 0) { if (this.markForDestroy && this.emitter.parent) this.dispose(); return; } if (this.looping && this.prewarm && !this.prewarmed) { this.prewarmed = true; for (let i = 0; i < this.duration * PREWARM_FPS; i++) { this.update(1.0 / PREWARM_FPS); } } if (delta > 0.1) { delta = 0.1; } if (this.neededToUpdateRender) { if (this._renderer) this._renderer.updateSystem(this); this.neededToUpdateRender = false; } if (!this.onlyUsedByOther) { this.emit(delta, this.emissionState, this.emitter.matrixWorld); } this.emitterShape.update(this, delta); for (let j = 0; j < this.behaviors.length; j++) { this.behaviors[j].frameUpdate(delta); for (let i = 0; i < this.particleNum; i++) { if (!this.particles[i].died) { this.behaviors[j].update(this.particles[i], delta); } } } for (let i = 0; i < this.particleNum; i++) { if (this.rendererEmitterSettings.followLocalOrigin && this.particles[i].localPosition) { this.particles[i].position.copy(this.particles[i].localPosition); if (this.particles[i].parentMatrix) { this.particles[i].position.applyMatrix4(this.particles[i].parentMatrix); } else { this.particles[i].position.applyMatrix4(this.emitter.matrixWorld); } } else { this.particles[i].position.addScaledVector(this.particles[i].velocity, delta * this.particles[i].speedModifier); } this.particles[i].age += delta; } if (this.rendererSettings.renderMode === exports.RenderMode.Trail) { for (let i = 0; i < this.particleNum; i++) { const particle = this.particles[i]; particle.update(); } } for (let i = 0; i < this.particleNum; i++) { const particle = this.particles[i]; if (particle.died && (!(particle instanceof quarks_core.TrailParticle) || particle.previous.length === 0)) { this.particles[i] = this.particles[this.particleNum - 1]; this.particles[this.particleNum - 1] = particle; this.particleNum--; i--; this.fire({ type: "particleDied", particleSystem: this, particle: particle }); } } } emit(delta, emissionState, emitterMatrix) { if (emissionState.time > this.duration) { if (this.looping) { emissionState.time -= this.duration; emissionState.burstIndex = 0; this.behaviors.forEach((behavior) => { behavior.reset(); }); } else { if (!this.emitEnded && !this.onlyUsedByOther) { this.endEmit(); } } } this.normalMatrix.getNormalMatrix(emitterMatrix); const totalSpawn = Math.ceil(emissionState.waitEmiting); this.spawn(totalSpawn, emissionState, emitterMatrix); emissionState.waitEmiting -= totalSpawn; while (emissionState.burstIndex < this.emissionBursts.length && this.emissionBursts[emissionState.burstIndex].time <= emissionState.time) { if (Math.random() < this.emissionBursts[emissionState.burstIndex].probability) { const count = this.emissionBursts[emissionState.burstIndex].count.genValue(this.memory, this.time); emissionState.isBursting = true; emissionState.burstParticleCount = count; this.spawn(count, emissionState, emitterMatrix); emissionState.isBursting = false; } emissionState.burstIndex++; } if (!this.emitEnded) { emissionState.waitEmiting += delta * this.emissionOverTime.genValue(this.memory, emissionState.time / this.duration); if (emissionState.previousWorldPos != undefined) { this.temp.set(emitterMatrix.elements[12], emitterMatrix.elements[13], emitterMatrix.elements[14]); emissionState.travelDistance += emissionState.previousWorldPos.distanceTo(this.temp); const emitPerMeter = this.emissionOverDistance.genValue(this.memory, emissionState.time / this.duration); if (emissionState.travelDistance * emitPerMeter > 0) { const count = Math.floor(emissionState.travelDistance * emitPerMeter); emissionState.travelDistance -= count / emitPerMeter; emissionState.waitEmiting += count; } } } if (emissionState.previousWorldPos === undefined) emissionState.previousWorldPos = new quarks_core.Vector3(); emissionState.previousWorldPos.set(emitterMatrix.elements[12], emitterMatrix.elements[13], emitterMatrix.elements[14]); emissionState.time += delta; } toJSON(meta, options = {}) { var _a; const isRootObject = meta === undefined || typeof meta === 'string'; if (isRootObject) { meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {}, skeletons: {}, animations: {}, nodes: {}, }; } meta.materials[this.rendererSettings.material.uuid] = this.rendererSettings.material.toJSON(meta); if (options.useUrlForImage) { if (((_a = this.texture) === null || _a === void 0 ? void 0 : _a.source) !== undefined) { const image = this.texture.source; meta.images[image.uuid] = { uuid: image.uuid, url: this.texture.image.url, }; } } let rendererSettingsJSON; if (this.renderMode === exports.RenderMode.Trail) { rendererSettingsJSON = { startLength: this.rendererEmitterSettings.startLength.toJSON(), followLocalOrigin: this.rendererEmitterSettings.followLocalOrigin, }; } else if (this.renderMode === exports.RenderMode.Mesh) { rendererSettingsJSON = {}; } else if (this.renderMode === exports.RenderMode.StretchedBillBoard) { rendererSettingsJSON = { speedFactor: this.rendererEmitterSettings.speedFactor, lengthFactor: this.rendererEmitterSettings.lengthFactor, }; } else { rendererSettingsJSON = {}; } const geometry = this.rendererSettings.instancingGeometry; if (meta.geometries && !meta.geometries[geometry.uuid]) { meta.geometries[geometry.uuid] = geometry.toJSON(); } return { version: '3.0', autoDestroy: this.autoDestroy, looping: this.looping, prewarm: this.prewarm, duration: this.duration, shape: this.emitterShape.toJSON(), startLife: this.startLife.toJSON(), startSpeed: this.startSpeed.toJSON(), startRotation: this.startRotation.toJSON(), startSize: this.startSize.toJSON(), startColor: this.startColor.toJSON(), emissionOverTime: this.emissionOverTime.toJSON(), emissionOverDistance: this.emissionOverDistance.toJSON(), emissionBursts: this.emissionBursts.map((burst) => ({ time: burst.time, count: burst.count.toJSON(), probability: burst.probability, interval: burst.interval, cycle: burst.cycle, })), onlyUsedByOther: this.onlyUsedByOther, instancingGeometry: this.rendererSettings.instancingGeometry.uuid, renderOrder: this.renderOrder, renderMode: this.renderMode, rendererEmitterSettings: rendererSettingsJSON, material: this.rendererSettings.material.uuid, layers: this.layers.mask, startTileIndex: this.startTileIndex.toJSON(), uTileCount: this.uTileCount, vTileCount: this.vTileCount, blendTiles: this.blendTiles, softParticles: this.rendererSettings.softParticles, softFarFade: this.rendererSettings.softFarFade, softNearFade: this.rendererSettings.softNearFade, behaviors: this.behaviors.map((behavior) => behavior.toJSON()), worldSpace: this.worldSpace, }; } static fromJSON(json, meta, dependencies) { var _a, _b; const shape = quarks_core.EmitterFromJSON(json.shape, meta); let rendererEmitterSettings; if (json.renderMode === exports.RenderMode.Trail) { const trailSettings = json.rendererEmitterSettings; rendererEmitterSettings = { startLength: trailSettings.startLength != undefined ? quarks_core.ValueGeneratorFromJSON(trailSettings.startLength) : new quarks_core.ConstantValue(30), followLocalOrigin: trailSettings.followLocalOrigin, }; } else if (json.renderMode === exports.RenderMode.Mesh) { rendererEmitterSettings = {}; } else if (json.renderMode === exports.RenderMode.StretchedBillBoard) { rendererEmitterSettings = json.rendererEmitterSettings; if (json.speedFactor != undefined) { rendererEmitterSettings.speedFactor = json.speedFactor; } } else { rendererEmitterSettings = {}; } const layers = new three.Layers(); if (json.layers) { layers.mask = json.layers; } const ps = new ParticleSystem({ autoDestroy: json.autoDestroy, looping: json.looping, prewarm: json.prewarm, duration: json.duration, shape: shape, startLife: quarks_core.ValueGeneratorFromJSON(json.startLife), startSpeed: quarks_core.ValueGeneratorFromJSON(json.startSpeed), startRotation: quarks_core.GeneratorFromJSON(json.startRotation), startSize: quarks_core.GeneratorFromJSON(json.startSize), startColor: quarks_core.ColorGeneratorFromJSON(json.startColor), emissionOverTime: quarks_core.ValueGeneratorFromJSON(json.emissionOverTime), emissionOverDistance: quarks_core.ValueGeneratorFromJSON(json.emissionOverDistance), emissionBursts: (_a = json.emissionBursts) === null || _a === void 0 ? void 0 : _a.map((burst) => { var _a, _b, _c; return ({ time: burst.time, count: typeof burst.count === 'number' ? new quarks_core.ConstantValue(burst.count) : quarks_core.ValueGeneratorFromJSON(burst.count), probability: (_a = burst.probability) !== null && _a !== void 0 ? _a : 1, interval: (_b = burst.interval) !== null && _b !== void 0 ? _b : 0.1, cycle: (_c = burst.cycle) !== null && _c !== void 0 ? _c : 1, }); }), onlyUsedByOther: json.onlyUsedByOther, instancingGeometry: meta.geometries[json.instancingGeometry], renderMode: json.renderMode, rendererEmitterSettings: rendererEmitterSettings, renderOrder: json.renderOrder, layers: layers, material: json.material ? meta.materials[json.material] : json.texture ? new three.MeshBasicMaterial({ map: meta.textures[json.texture], transparent: (_b = json.transparent) !== null && _b !== void 0 ? _b : true, blending: json.blending, side: three.DoubleSide, }) : new three.MeshBasicMaterial({ color: 0xffffff, transparent: true, blending: three.AdditiveBlending, side: three.DoubleSide, }), startTileIndex: typeof json.startTileIndex === 'number' ? new quarks_core.ConstantValue(json.startTileIndex) : quarks_core.ValueGeneratorFromJSON(json.startTileIndex), uTileCount: json.uTileCount, vTileCount: json.vTileCount, blendTiles: json.blendTiles, softParticles: json.softParticles, softFarFade: json.softFarFade, softNearFade: json.softNearFade, behaviors: [], worldSpace: json.worldSpace, }); ps.behaviors = json.behaviors.map((behaviorJson) => { const behavior = quarks_core.BehaviorFromJSON(behaviorJson, ps); if (behavior.type === 'EmitSubParticleSystem') { dependencies[behaviorJson.subParticleSystem] = behavior; } return behavior; }); return ps; } addBehavior(behavior) { this.behaviors.push(behavior); } getRendererSettings() { return this.rendererSettings; } addEventListener(event, callback) { if (!this.listeners[event]) this.listeners[event] = []; this.listeners[event].push(callback); } removeAllEventListeners(event) { if (this.listeners[event]) this.listeners[event] = []; } removeEventListener(event, callback) { if (this.listeners[event]) { const index = this.listeners[event].indexOf(callback); if (index !== -1) { this.listeners[event].splice(index, 1); } } } fire(event) { if (this.listeners[event.type]) { this.listeners[event.type].forEach(callback => callback(event)); } } clone() { const newEmissionBursts = []; for (const emissionBurst of this.emissionBursts) { const newEmissionBurst = {}; Object.assign(newEmissionBurst, emissionBurst); newEmissionBursts.push(newEmissionBurst); } const newBehaviors = []; for (const behavior of this.behaviors) { newBehaviors.push(behavior.clone()); } let rendererEmitterSettings; if (this.renderMode === exports.RenderMode.Trail) { rendererEmitterSettings = { startLength: this.rendererEmitterSettings.startLength.clone(), followLocalOrigin: this.rendererEmitterSettings.followLocalOrigin, }; } else if (this.renderMode === exports.RenderMode.StretchedBillBoard) { rendererEmitterSettings = { lengthFactor: this.rendererEmitterSettings.lengthFactor, speedFactor: this.rendererEmitterSettings.speedFactor, }; } else { rendererEmitterSettings = {}; } const layers = new three.Layers(); layers.mask = this.layers.mask; return new ParticleSystem({ autoDestroy: this.autoDestroy, looping: this.looping, duration: this.duration, shape: this.emitterShape.clone(), startLife: this.startLife.clone(), startSpeed: this.startSpeed.clone(), startRotation: this.startRotation.clone(), startSize: this.startSize.clone(), startColor: this.startColor.clone(), emissionOverTime: this.emissionOverTime.clone(), emissionOverDistance: this.emissionOverDistance.clone(), emissionBursts: newEmissionBursts, onlyUsedByOther: this.onlyUsedByOther, instancingGeometry: this.rendererSettings.instancingGeometry, renderMode: this.renderMode, renderOrder: this.renderOrder, rendererEmitterSettings: rendererEmitterSettings, material: this.rendererSettings.material, startTileIndex: this.startTileIndex, uTileCount: this.uTileCount, vTileCount: this.vTileCount, blendTiles: this.blendTiles, softParticles: this.softParticles, softFarFade: this.softFarFade, softNearFade: this.softNearFade, behaviors: newBehaviors, worldSpace: this.worldSpace, layers: layers, }); } } var particle_frag = ` #include #include #include #include #include #include #include #include void main() { #include vec3 outgoingLight = vec3( 0.0 ); vec4 diffuseColor = vColor; #include #include #include outgoingLight = diffuseColor.rgb; #ifdef USE_COLOR_AS_ALPHA gl_FragColor = vec4( outgoingLight, diffuseColor.r ); #else gl_FragColor = vec4( outgoingLight, diffuseColor.a ); #endif #include #include } `; var particle_physics_frag = ` #define STANDARD #ifdef PHYSICAL #define IOR #define USE_SPECULAR #endif uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; #ifdef IOR uniform float ior; #endif #ifdef USE_SPECULAR uniform float specularIntensity; uniform vec3 specularColor; #ifdef USE_SPECULAR_COLORMAP uniform sampler2D specularColorMap; #endif #ifdef USE_SPECULAR_INTENSITYMAP uniform sampler2D specularIntensityMap; #endif #endif #ifdef USE_CLEARCOAT uniform float clearcoat; uniform float clearcoatRoughness; #endif #ifdef USE_DISPERSION uniform float dispersion; #endif #ifdef USE_IRIDESCENCE uniform float iridescence; uniform float iridescenceIOR; uniform float iridescenceThicknessMinimum; uniform float iridescenceThicknessMaximum; #endif #ifdef USE_SHEEN uniform vec3 sheenColor; uniform float sheenRoughness; #ifdef USE_SHEEN_COLORMAP uniform sampler2D sheenColorMap; #endif #ifdef USE_SHEEN_ROUGHNESSMAP uniform sampler2D sheenRoughnessMap; #endif #endif #ifdef USE_ANISOTROPY uniform vec2 anisotropyVector; #ifdef USE_ANISOTROPYMAP uniform sampler2D anisotropyMap; #endif #endif varying vec3 vViewPosition; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void main() { vec4 diffuseColor = vec4( diffuse, opacity ); #include ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); vec3 totalEmissiveRadiance = emissive; #include #include #include #include #include #include #include #include #include #include #include #include #include // accumulation #include #include #include #include // modulation #include vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; #include vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; #ifdef USE_SHEEN // Sheen energy compensation approximation calculation can be found at the end of // https://drive.google.com/file/d/1T0D1VSyR4AllqIJTQAraEIzjlb5h4FKH/view?usp=sharing float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecularDirect + sheenSpecularIndirect; #endif #ifdef USE_CLEARCOAT float dotNVcc = saturate( dot( geometryClearcoatNormal, geometryViewDir ) ); vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + ( clearcoatSpecularDirect + clearcoatSpecularIndirect ) * material.clearcoat; #endif #include #include #include #include #include #include }`; var particle_vert = ` #include #include #include #include #include #include attribute vec3 offset; attribute float rotation; attribute vec3 size; void main() { vec2 alignedPosition = position.xy * size.xy; vec2 rotatedPosition; rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; #ifdef HORIZONTAL vec4 mvPosition = modelMatrix * vec4( offset, 1.0 ); mvPosition.x += rotatedPosition.x; mvPosition.z -= rotatedPosition.y; mvPosition = viewMatrix * mvPosition; #elif defined(VERTICAL) vec4 mvPosition = modelMatrix * vec4( offset, 1.0 ); mvPosition.y += rotatedPosition.y; mvPosition = viewMatrix * mvPosition; mvPosition.x += rotatedPosition.x; #else vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 ); mvPosition.xy += rotatedPosition; #endif vColor = color; gl_Position = projectionMatrix * mvPosition; #include #include #include #include } `; var local_particle_vert = ` #include #include #include #include #include #include attribute vec3 offset; attribute vec4 rotation; attribute vec3 size; // attribute vec4 color; void main() { float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z; float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2; float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2; float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2; float sx = size.x, sy = size.y, sz = size.z; mat4 matrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0, // 1. column ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0, // 2. column ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0, // 3. column offset.x, offset.y, offset.z, 1.0); vec4 mvPosition = modelViewMatrix * (matrix * vec4( position, 1.0 )); vColor = color; gl_Position = projectionMatrix * mvPosition; #include #include #include #include } `; var local_particle_physics_vert = ` #define STANDARD varying vec3 vViewPosition; #ifdef USE_TRANSMISSION varying vec3 vWorldPosition; #endif #include attribute vec3 offset; attribute vec4 rotation; attribute vec3 size; #include #include #include #include #include #include #include #include #include #include void main() { #include float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z; float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2; float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2; float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2; float sx = size.x, sy = size.y, sz = size.z; mat4 particleMatrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0, // 1. column ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0, // 2. column ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0, // 3. column offset.x, offset.y, offset.z, 1.0); #include #include #include #include #include #include #include #include // replace defaultnormal_vertex vec3 transformedNormal = objectNormal; mat3 m = mat3( particleMatrix ); transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) ); transformedNormal = m * transformedNormal; transformedNormal = normalMatrix * transformedNormal; #ifdef FLIP_SIDED transformedNormal = - transformedNormal; #endif #ifdef USE_TANGENT vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz; #ifdef FLIP_SIDED transformedTangent = - transformedTangent; #endif #endif #include #include #include #include #include // replace include vec4 mvPosition = vec4( transformed, 1.0 ); mvPosition = modelViewMatrix * (particleMatrix * mvPosition); gl_Position = projectionMatrix * mvPosition; #include #include vViewPosition = - mvPosition.xyz; #include #include #include #ifdef USE_TRANSMISSION vWorldPosition = worldPosition.xyz; #endif } `; var stretched_bb_particle_vert = ` #include #include #include #include #include #include attribute vec3 offset; attribute float rotation; attribute vec3 size; attribute vec4 velocity; uniform float speedFactor; void main() { float lengthFactor = velocity.w; float avgSize = (size.x + size.y) * 0.5; #ifdef USE_SKEW vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 ); vec3 viewVelocity = normalMatrix * velocity.xyz; vec3 scaledPos = vec3(position.xy * size.xy, position.z); float vlength = length(viewVelocity); vec3 projVelocity = dot(scaledPos, viewVelocity) * viewVelocity / vlength; mvPosition.xyz += scaledPos + projVelocity * (speedFactor / avgSize + lengthFactor / vlength); #else vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 ); vec3 viewVelocity = normalMatrix * velocity.xyz; float vlength = length(viewVelocity); mvPosition.xyz += position.y * normalize(cross(mvPosition.xyz, viewVelocity)) * avgSize; // switch the cross to match unity implementation mvPosition.xyz -= (position.x + 0.5) * viewVelocity * (1.0 + lengthFactor / vlength) * avgSize; // minus position.x to match unity implementation #endif vColor = color; gl_Position = projectionMatrix * mvPosition; #include #include #include #include } `; function getMaterialUVChannelName(value) { if (value === 0) return 'uv'; return `uv${value}`; } class ParticleMeshStandardMaterial extends three.MeshStandardMaterial { constructor(parameters) { super(parameters); } onBeforeCompile(parameters, renderer) { super.onBeforeCompile(parameters, renderer); parameters.vertexShader = local_particle_physics_vert; parameters.fragmentShader = particle_physics_frag; } } class ParticleMeshPhysicsMaterial extends three.MeshPhysicalMaterial { constructor(parameters) { super(parameters); } onBeforeCompile(parameters, renderer) { super.onBeforeCompile(parameters, renderer); parameters.vertexShader = local_particle_physics_vert; parameters.fragmentShader = particle_physics_frag; } } class SpriteBatch extends VFXBatch { constructor(settings) { super(settings); this.vector_ = new quarks_core.Vector3(); this.vector2_ = new quarks_core.Vector3(); this.vector3_ = new quarks_core.Vector3(); this.quaternion_ = new quarks_core.Quaternion(); this.quaternion2_ = new quarks_core.Quaternion(); this.quaternion3_ = new quarks_core.Quaternion(); this.rotationMat_ = new quarks_core.Matrix3(); this.rotationMat2_ = new quarks_core.Matrix3(); this.maxParticles = 1000; this.setupBuffers(); this.rebuildMaterial(); } buildExpandableBuffers() { this.offsetBuffer = new three.InstancedBufferAttribute(new Float32Array(this.maxParticles * 3), 3); this.offsetBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setAttribute('offset', this.offsetBuffer); this.colorBuffer = new three.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4); this.colorBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setAttribute('color', this.colorBuffer); if (this.settings.renderMode === exports.RenderMode.Mesh) { this.rotationBuffer = new three.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4); this.rotationBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setAttribute('rotation', this.rotationBuffer); } else if (this.settings.renderMode === exports.RenderMode.BillBoard || this.settings.renderMode === exports.RenderMode.HorizontalBillBoard || this.settings.renderMode === exports.RenderMode.VerticalBillBoard || this.settings.renderMode === exports.RenderMode.StretchedBillBoard) { this.rotationBuffer = new three.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1); this.rotationBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setAttribute('rotation', this.rotationBuffer); } this.sizeBuffer = new three.InstancedBufferAttribute(new Float32Array(this.maxParticles * 3), 3); this.sizeBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setAttribute('size', this.sizeBuffer); this.uvTileBuffer = new three.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1); this.uvTileBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setAttribute('uvTile', this.uvTileBuffer); if (this.settings.renderMode === exports.RenderMode.StretchedBillBoard) { this.velocityBuffer = new three.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4); this.velocityBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setAttribute('velocity', this.velocityBuffer); } } setupBuffers() { if (this.geometry) this.geometry.dispose(); this.geometry = new three.InstancedBufferGeometry(); this.geometry.setIndex(this.settings.instancingGeometry.getIndex()); if (this.settings.instancingGeometry.hasAttribute('normal')) { this.geometry.setAttribute('normal', this.settings.instancingGeometry.getAttribute('normal')); } this.geometry.setAttribute('position', this.settings.instancingGeometry.getAttribute('position')); this.geometry.setAttribute('uv', this.settings.instancingGeometry.getAttribute('uv')); this.buildExpandableBuffers(); } expandBuffers(target) { while (target >= this.maxParticles) { this.maxParticles *= 2; } this.setupBuffers(); } rebuildMaterial() { this.layers.mask = this.settings.layers.mask; const uniforms = {}; const defines = {}; if (this.settings.material.type !== 'MeshStandardMaterial' && this.settings.material.type !== 'MeshPhysicalMaterial') { uniforms['map'] = new three.Uniform(this.settings.material.map); } if (this.settings.material.alphaTest) { defines['USE_ALPHATEST'] = ''; uniforms['alphaTest'] = new three.Uniform(this.settings.material.alphaTest); } defines['USE_UV'] = ''; const uTileCount = this.settings.uTileCount; const vTileCount = this.settings.vTileCount; if (uTileCount > 1 || vTileCount > 1) { defines['UV_TILE'] = ''; uniforms['tileCount'] = new three.Uniform(new quarks_core.Vector2(uTileCount, vTileCount)); } if (this.settings.material.defines && this.settings.material.defines['USE_COLOR_AS_ALPHA'] !== undefined) { defines['USE_COLOR_AS_ALPHA'] = ''; } if (this.settings.material.normalMap) { defines['USE_NORMALMAP'] = ''; defines['NORMALMAP_UV'] = getMaterialUVChannelName(this.settings.material.normalMap.channel); uniforms['normalMapTransform'] = new three.Uniform(new quarks_core.Matrix3().copy(this.settings.material.normalMap.matrix)); } if (this.settings.material.map) { defines['USE_MAP'] = ''; if (this.settings.blendTiles) defines['TILE_BLEND'] = ''; defines['MAP_UV'] = getMaterialUVChannelName(this.settings.material.map.channel); uniforms['mapTransform'] = new three.Uniform(new quarks_core.Matrix3().copy(this.settings.material.map.matrix)); } defines['USE_COLOR_ALPHA'] = ''; let onBeforeRender; if (this.settings.softParticles) { defines['SOFT_PARTICLES'] = ''; const nearFade = this.settings.softNearFade; const invFadeDistance = 1.0 / (this.settings.softFarFade - this.settings.softNearFade); uniforms['softParams'] = new three.Uniform(new quarks_core.Vector2(nearFade, invFadeDistance)); uniforms['depthTexture'] = new three.Uniform(null); const projParams = (uniforms['projParams'] = new three.Uniform(new quarks_core.Vector4())); onBeforeRender = (_renderer, _scene, camera) => { projParams.value.set(camera.near, camera.far, 0, 0); }; } let needLights = false; if (this.settings.renderMode === exports.RenderMode.BillBoard || this.settings.renderMode === exports.RenderMode.VerticalBillBoard || this.settings.renderMode === exports.RenderMode.HorizontalBillBoard || this.settings.renderMode === exports.RenderMode.Mesh) { let vertexShader; let fragmentShader; if (this.settings.renderMode === exports.RenderMode.Mesh) { if (this.settings.material.type === 'MeshStandardMaterial' || this.settings.material.type === 'MeshPhysicalMaterial') { defines['USE_COLOR'] = ''; vertexShader = local_particle_physics_vert; fragmentShader = particle_physics_frag; needLights = true; } else { vertexShader = local_particle_vert; fragmentShader = particle_frag; } } else { vertexShader = particle_vert; fragmentShader = particle_frag; } if (this.settings.renderMode === exports.RenderMode.VerticalBillBoard) { defines['VERTICAL'] = ''; } else if (this.settings.renderMode === exports.RenderMode.HorizontalBillBoard) { defines['HORIZONTAL'] = ''; } let specialMats = false; if (this.settings.renderMode === exports.RenderMode.Mesh) { if (this.settings.material.type === 'MeshStandardMaterial') { this.material = new ParticleMeshStandardMaterial({}); this.material.copy(this.settings.material); this.material.uniforms = uniforms; this.material.defines = defines; specialMats = true; } else if (this.settings.material.type === 'MeshPhysicalMaterial') { this.material = new ParticleMeshPhysicsMaterial({}); this.material.copy(this.settings.material); this.material.uniforms = uniforms; this.material.defines = defines; specialMats = true; } } if (!specialMats) { this.material = new three.ShaderMaterial({ uniforms: uniforms, defines: defines, vertexShader: vertexShader, fragmentShader: fragmentShader, transparent: this.settings.material.transparent, depthWrite: !this.settings.material.transparent, blending: this.settings.material.blending, blendDst: this.settings.material.blendDst, blendSrc: this.settings.material.blendSrc, blendEquation: this.settings.material.blendEquation, premultipliedAlpha: this.settings.material.premultipliedAlpha, side: this.settings.material.side, alphaTest: this.settings.material.alphaTest, depthTest: this.settings.material.depthTest, lights: needLights, }); } } else if (this.settings.renderMode === exports.RenderMode.StretchedBillBoard) { uniforms['speedFactor'] = new three.Uniform(1.0); this.material = new three.ShaderMaterial({ uniforms: uniforms, defines: defines, vertexShader: stretched_bb_particle_vert, fragmentShader: particle_frag, transparent: this.settings.material.transparent, depthWrite: !this.settings.material.transparent, blending: this.settings.material.blending, blendDst: this.settings.material.blendDst, blendSrc: this.settings.material.blendSrc, blendEquation: this.settings.material.blendEquation, premultipliedAlpha: this.settings.material.premultipliedAlpha, side: this.settings.material.side, alphaTest: this.settings.material.alphaTest, depthTest: this.settings.material.depthTest, }); } else { throw new Error('render mode unavailable'); } if (this.material && onBeforeRender) { this.material.onBeforeRender = onBeforeRender; } } update() { let index = 0; let particleCount = 0; this.systems.forEach((system) => { particleCount += system.particleNum; }); if (particleCount > this.maxParticles) { this.expandBuffers(particleCount); } this.systems.forEach((system) => { if (system.emitter.updateMatrixWorld) { system.emitter.updateWorldMatrix(true, false); system.emitter.updateMatrixWorld(true); } const particles = system.particles; const particleNum = system.particleNum; const rotation = this.quaternion2_; const translation = this.vector2_; const scale = this.vector3_; system.emitter.matrixWorld.decompose(translation, rotation, scale); this.rotationMat_.setFromMatrix4(system.emitter.matrixWorld); for (let j = 0; j < particleNum; j++, index++) { const particle = particles[j]; if (this.settings.renderMode === exports.RenderMode.Mesh) { let q; if (system.worldSpace) { q = particle.rotation; } else { let parentQ; if (particle.parentMatrix) { parentQ = this.quaternion3_.setFromRotationMatrix(particle.parentMatrix); } else { parentQ = rotation; } q = this.quaternion_; q.copy(parentQ).multiply(particle.rotation); } this.rotationBuffer.setXYZW(index, q.x, q.y, q.z, q.w); } else if (this.settings.renderMode === exports.RenderMode.StretchedBillBoard || this.settings.renderMode === exports.RenderMode.VerticalBillBoard || this.settings.renderMode === exports.RenderMode.HorizontalBillBoard || this.settings.renderMode === exports.RenderMode.BillBoard) { this.rotationBuffer.setX(index, particle.rotation); } let vec; if (system.worldSpace) { vec = particle.position; } else { vec = this.vector_; if (particle.parentMatrix) { vec.copy(particle.position).applyMatrix4(particle.parentMatrix); } else { vec.copy(particle.position).applyMatrix4(system.emitter.matrixWorld); } } this.offsetBuffer.setXYZ(index, vec.x, vec.y, vec.z); this.colorBuffer.setXYZW(index, particle.color.x, particle.color.y, particle.color.z, particle.color.w); if (system.worldSpace) { this.sizeBuffer.setXYZ(index, particle.size.x, particle.size.y, particle.size.z); } else { if (particle.parentMatrix) { this.sizeBuffer.setXYZ(index, particle.size.x, particle.size.y, particle.size.z); } else { this.sizeBuffer.setXYZ(index, particle.size.x * Math.abs(scale.x), particle.size.y * Math.abs(scale.y), particle.size.z * Math.abs(scale.z)); } } this.uvTileBuffer.setX(index, particle.uvTile); if (this.settings.renderMode === exports.RenderMode.StretchedBillBoard && this.velocityBuffer) { let speedFactor = system.rendererEmitterSettings.speedFactor; if (speedFactor === 0) speedFactor = 0.001; const lengthFactor = system.rendererEmitterSettings.lengthFactor; let vec; if (system.worldSpace) { vec = particle.velocity; } else { vec = this.vector_; if (particle.parentMatrix) { this.rotationMat2_.setFromMatrix4(particle.parentMatrix); vec.copy(particle.velocity).applyMatrix3(this.rotationMat2_); } else { vec.copy(particle.velocity).applyMatrix3(this.rotationMat_); } } this.velocityBuffer.setXYZW(index, vec.x * speedFactor, vec.y * speedFactor, vec.z * speedFactor, lengthFactor); } } }); this.geometry.instanceCount = index; if (index > 0) { this.offsetBuffer.clearUpdateRanges(); this.offsetBuffer.addUpdateRange(0, index * 3); this.offsetBuffer.needsUpdate = true; this.sizeBuffer.clearUpdateRanges(); this.sizeBuffer.addUpdateRange(0, index * 3); this.sizeBuffer.needsUpdate = true; this.colorBuffer.clearUpdateRanges(); this.colorBuffer.addUpdateRange(0, index * 4); this.colorBuffer.needsUpdate = true; this.uvTileBuffer.clearUpdateRanges(); this.uvTileBuffer.addUpdateRange(0, index); this.uvTileBuffer.needsUpdate = true; if (this.settings.renderMode === exports.RenderMode.StretchedBillBoard && this.velocityBuffer) { this.velocityBuffer.clearUpdateRanges(); this.velocityBuffer.addUpdateRange(0, index * 4); this.velocityBuffer.needsUpdate = true; } if (this.settings.renderMode === exports.RenderMode.Mesh) { this.rotationBuffer.clearUpdateRanges(); this.rotationBuffer.addUpdateRange(0, index * 4); this.rotationBuffer.needsUpdate = true; } else if (this.settings.renderMode === exports.RenderMode.StretchedBillBoard || this.settings.renderMode === exports.RenderMode.HorizontalBillBoard || this.settings.renderMode === exports.RenderMode.VerticalBillBoard || this.settings.renderMode === exports.RenderMode.BillBoard) { this.rotationBuffer.clearUpdateRanges(); this.rotationBuffer.addUpdateRange(0, index); this.rotationBuffer.needsUpdate = true; } } } dispose() { this.geometry.dispose(); } } var trail_frag = ` #include #include #include #include #include #include uniform sampler2D alphaMap; uniform float useAlphaMap; uniform float visibility; uniform float alphaTest; varying vec4 vColor; void main() { #include #include vec4 diffuseColor = vColor; #ifdef USE_MAP #include #ifndef USE_COLOR_AS_ALPHA #endif #endif if( useAlphaMap == 1. ) diffuseColor.a *= texture2D( alphaMap, vUv).a; if( diffuseColor.a < alphaTest ) discard; gl_FragColor = diffuseColor; #include #include }`; var trail_vert = ` #include #include #include #include #include #include attribute vec3 previous; attribute vec3 next; attribute float side; attribute float width; uniform vec2 resolution; uniform float lineWidth; uniform float sizeAttenuation; vec2 fix(vec4 i, float aspect) { vec2 res = i.xy / i.w; res.x *= aspect; return res; } void main() { #include float aspect = resolution.x / resolution.y; vColor = color; mat4 m = projectionMatrix * modelViewMatrix; vec4 finalPosition = m * vec4( position, 1.0 ); vec4 prevPos = m * vec4( previous, 1.0 ); vec4 nextPos = m * vec4( next, 1.0 ); vec2 currentP = fix( finalPosition, aspect ); vec2 prevP = fix( prevPos, aspect ); vec2 nextP = fix( nextPos, aspect ); float w = lineWidth * width; vec2 dir; if( nextP == currentP ) dir = normalize( currentP - prevP ); else if( prevP == currentP ) dir = normalize( nextP - currentP ); else { vec2 dir1 = normalize( currentP - prevP ); vec2 dir2 = normalize( nextP - currentP ); dir = normalize( dir1 + dir2 ); vec2 perp = vec2( -dir1.y, dir1.x ); vec2 miter = vec2( -dir.y, dir.x ); //w = clamp( w / dot( miter, perp ), 0., 4., * lineWidth * width ); } //vec2 normal = ( cross( vec3( dir, 0. ) vec3( 0., 0., 1. ) ) ).xy; vec4 normal = vec4( -dir.y, dir.x, 0., 1. ); normal.xy *= .5 * w; normal *= projectionMatrix; if( sizeAttenuation == 0. ) { normal.xy *= finalPosition.w; normal.xy /= ( vec4( resolution, 0., 1. ) * projectionMatrix ).xy; } finalPosition.xy += normal.xy * side; gl_Position = finalPosition; #include #include vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); #include }`; class TrailBatch extends VFXBatch { constructor(settings) { super(settings); this.vector_ = new quarks_core.Vector3(); this.vector2_ = new quarks_core.Vector3(); this.vector3_ = new quarks_core.Vector3(); this.quaternion_ = new quarks_core.Quaternion(); this.maxParticles = 10000; this.setupBuffers(); this.rebuildMaterial(); } setupBuffers() { if (this.geometry) this.geometry.dispose(); this.geometry = new three.BufferGeometry(); this.indexBuffer = new three.BufferAttribute(new Uint32Array(this.maxParticles * 6), 1); this.indexBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setIndex(this.indexBuffer); this.positionBuffer = new three.BufferAttribute(new Float32Array(this.maxParticles * 6), 3); this.positionBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setAttribute('position', this.positionBuffer); this.previousBuffer = new three.BufferAttribute(new Float32Array(this.maxParticles * 6), 3); this.previousBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setAttribute('previous', this.previousBuffer); this.nextBuffer = new three.BufferAttribute(new Float32Array(this.maxParticles * 6), 3); this.nextBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setAttribute('next', this.nextBuffer); this.widthBuffer = new three.BufferAttribute(new Float32Array(this.maxParticles * 2), 1); this.widthBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setAttribute('width', this.widthBuffer); this.sideBuffer = new three.BufferAttribute(new Float32Array(this.maxParticles * 2), 1); this.sideBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setAttribute('side', this.sideBuffer); this.uvBuffer = new three.BufferAttribute(new Float32Array(this.maxParticles * 4), 2); this.uvBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setAttribute('uv', this.uvBuffer); this.colorBuffer = new three.BufferAttribute(new Float32Array(this.maxParticles * 8), 4); this.colorBuffer.setUsage(three.DynamicDrawUsage); this.geometry.setAttribute('color', this.colorBuffer); } expandBuffers(target) { while (target >= this.maxParticles) { this.maxParticles *= 2; } this.setupBuffers(); } rebuildMaterial() { this.layers.mask = this.settings.layers.mask; const uniforms = { lineWidth: { value: 1 }, map: { value: null }, useMap: { value: 0 }, alphaMap: { value: null }, useAlphaMap: { value: 0 }, resolution: { value: new quarks_core.Vector2(1, 1) }, sizeAttenuation: { value: 1 }, visibility: { value: 1 }, alphaTest: { value: 0 }, }; const defines = {}; defines['USE_UV'] = ''; defines['USE_COLOR_ALPHA'] = ''; if (this.settings.material.map) { defines['USE_MAP'] = ''; defines['MAP_UV'] = getMaterialUVChannelName(this.settings.material.map.channel); uniforms['map'] = new three.Uniform(this.settings.material.map); uniforms['mapTransform'] = new three.Uniform(new quarks_core.Matrix3().copy(this.settings.material.map.matrix)); } if (this.settings.material.defines && this.settings.material.defines['USE_COLOR_AS_ALPHA'] !== undefined) { defines['USE_COLOR_AS_ALPHA'] = ''; } if (this.settings.renderMode === exports.RenderMode.Trail) { this.material = new three.ShaderMaterial({ uniforms: uniforms, defines: defines, vertexShader: trail_vert, fragmentShader: trail_frag, transparent: this.settings.material.transparent, depthWrite: !this.settings.material.transparent, side: this.settings.material.side, blending: this.settings.material.blending || three.AdditiveBlending, blendDst: this.settings.material.blendDst, blendSrc: this.settings.material.blendSrc, blendEquation: this.settings.material.blendEquation, premultipliedAlpha: this.settings.material.premultipliedAlpha, }); } else { throw new Error('render mode unavailable'); } } update() { let index = 0; let triangles = 0; let particleCount = 0; this.systems.forEach((system) => { for (let j = 0; j < system.particleNum; j++) { particleCount += system.particles[j].previous.length * 2; } }); if (particleCount > this.maxParticles) { this.expandBuffers(particleCount); } this.systems.forEach((system) => { if (system.emitter.updateMatrixWorld) { system.emitter.updateWorldMatrix(true, false); system.emitter.updateMatrixWorld(true); } const rotation = this.quaternion_; const translation = this.vector2_; const scale = this.vector3_; system.emitter.matrixWorld.decompose(translation, rotation, scale); const particles = system.particles; const particleNum = system.particleNum; const uTileCount = this.settings.uTileCount; const vTileCount = this.settings.vTileCount; const tileWidth = 1 / uTileCount; const tileHeight = 1 / vTileCount; for (let j = 0; j < particleNum; j++) { const particle = particles[j]; const col = particle.uvTile % vTileCount; const row = Math.floor(particle.uvTile / vTileCount + 0.001); const iter = particle.previous.values(); let curIter = iter.next(); let previous = curIter.value; let current = previous; if (!curIter.done) curIter = iter.next(); let next; if (curIter.value !== undefined) { next = curIter.value; } else { next = current; } for (let i = 0; i < particle.previous.length; i++, index += 2) { this.positionBuffer.setXYZ(index, current.position.x, current.position.y, current.position.z); this.positionBuffer.setXYZ(index + 1, current.position.x, current.position.y, current.position.z); if (system.worldSpace) { this.positionBuffer.setXYZ(index, current.position.x, current.position.y, current.position.z); this.positionBuffer.setXYZ(index + 1, current.position.x, current.position.y, current.position.z); } else { if (particle.parentMatrix) { this.vector_.copy(current.position).applyMatrix4(particle.parentMatrix); } else { this.vector_.copy(current.position).applyMatrix4(system.emitter.matrixWorld); } this.positionBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z); this.positionBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z); } if (system.worldSpace) { this.previousBuffer.setXYZ(index, previous.position.x, previous.position.y, previous.position.z); this.previousBuffer.setXYZ(index + 1, previous.position.x, previous.position.y, previous.position.z); } else { if (particle.parentMatrix) { this.vector_.copy(previous.position).applyMatrix4(particle.parentMatrix); } else { this.vector_.copy(previous.position).applyMatrix4(system.emitter.matrixWorld); } this.previousBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z); this.previousBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z); } if (system.worldSpace) { this.nextBuffer.setXYZ(index, next.position.x, next.position.y, next.position.z); this.nextBuffer.setXYZ(index + 1, next.position.x, next.position.y, next.position.z); } else { if (particle.parentMatrix) { this.vector_.copy(next.position).applyMatrix4(particle.parentMatrix); } else { this.vector_.copy(next.position).applyMatrix4(system.emitter.matrixWorld); } this.nextBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z); this.nextBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z); } this.sideBuffer.setX(index, -1); this.sideBuffer.setX(index + 1, 1); if (system.worldSpace) { this.widthBuffer.setX(index, current.size); this.widthBuffer.setX(index + 1, current.size); } else { if (particle.parentMatrix) { this.widthBuffer.setX(index, current.size); this.widthBuffer.setX(index + 1, current.size); } else { const objectScale = (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z)) / 3; this.widthBuffer.setX(index, current.size * objectScale); this.widthBuffer.setX(index + 1, current.size * objectScale); } } this.uvBuffer.setXY(index, (i / particle.previous.length + col) * tileWidth, (vTileCount - row - 1) * tileHeight); this.uvBuffer.setXY(index + 1, (i / particle.previous.length + col) * tileWidth, (vTileCount - row) * tileHeight); this.colorBuffer.setXYZW(index, current.color.x, current.color.y, current.color.z, current.color.w); this.colorBuffer.setXYZW(index + 1, current.color.x, current.color.y, current.color.z, current.color.w); if (i + 1 < particle.previous.length) { this.indexBuffer.setX(triangles * 3, index); this.indexBuffer.setX(triangles * 3 + 1, index + 1); this.indexBuffer.setX(triangles * 3 + 2, index + 2); triangles++; this.indexBuffer.setX(triangles * 3, index + 2); this.indexBuffer.setX(triangles * 3 + 1, index + 1); this.indexBuffer.setX(triangles * 3 + 2, index + 3); triangles++; } previous = current; current = next; if (!curIter.done) { curIter = iter.next(); if (curIter.value !== undefined) { next = curIter.value; } } } } }); this.positionBuffer.clearUpdateRanges(); this.positionBuffer.addUpdateRange(0, index * 3); this.positionBuffer.needsUpdate = true; this.previousBuffer.clearUpdateRanges(); this.previousBuffer.addUpdateRange(0, index * 3); this.previousBuffer.needsUpdate = true; this.nextBuffer.clearUpdateRanges(); this.nextBuffer.addUpdateRange(0, index * 3); this.nextBuffer.needsUpdate = true; this.sideBuffer.clearUpdateRanges(); this.sideBuffer.addUpdateRange(0, index); this.sideBuffer.needsUpdate = true; this.widthBuffer.clearUpdateRanges(); this.widthBuffer.addUpdateRange(0, index); this.widthBuffer.needsUpdate = true; this.uvBuffer.clearUpdateRanges(); this.uvBuffer.addUpdateRange(0, index * 2); this.uvBuffer.needsUpdate = true; this.colorBuffer.clearUpdateRanges(); this.colorBuffer.addUpdateRange(0, index * 4); this.colorBuffer.needsUpdate = true; this.indexBuffer.clearUpdateRanges(); this.indexBuffer.addUpdateRange(0, triangles * 3); this.indexBuffer.needsUpdate = true; this.geometry.setDrawRange(0, triangles * 3); } dispose() { this.geometry.dispose(); } } class MeshSurfaceEmitter { get geometry() { return this._geometry; } set geometry(geometry) { this._geometry = geometry; if (geometry === undefined) { return; } if (typeof geometry === 'string') { return; } const tri = new three.Triangle(); this._triangleIndexToArea.length = 0; let area = 0; if (!geometry.getIndex()) { return; } const array = geometry.getIndex().array; const triCount = array.length / 3; this._triangleIndexToArea.push(0); for (let i = 0; i < triCount; i++) { tri.setFromAttributeAndIndices(geometry.getAttribute('position'), array[i * 3], array[i * 3 + 1], array[i * 3 + 2]); area += tri.getArea(); this._triangleIndexToArea.push(area); } geometry.userData.triangleIndexToArea = this._triangleIndexToArea; } constructor(geometry) { this.type = 'mesh_surface'; this._triangleIndexToArea = []; this._tempA = new three.Vector3(); this._tempB = new three.Vector3(); this._tempC = new three.Vector3(); if (!geometry) { return; } this.geometry = geometry; } initialize(p) { const geometry = this._geometry; if (!geometry || geometry.getIndex() === null) { p.position.set(0, 0, 0); p.velocity.set(0, 0, 1).multiplyScalar(p.startSpeed); return; } const triCount = this._triangleIndexToArea.length - 1; let left = 0, right = triCount; const target = Math.random() * this._triangleIndexToArea[triCount]; while (left + 1 < right) { const mid = Math.floor((left + right) / 2); if (target < this._triangleIndexToArea[mid]) { right = mid; } else { left = mid; } } let u1 = Math.random(); let u2 = Math.random(); if (u1 + u2 > 1) { u1 = 1 - u1; u2 = 1 - u2; } const index1 = geometry.getIndex().array[left * 3]; const index2 = geometry.getIndex().array[left * 3 + 1]; const index3 = geometry.getIndex().array[left * 3 + 2]; const positionBuffer = geometry.getAttribute('position'); this._tempA.fromBufferAttribute(positionBuffer, index1); this._tempB.fromBufferAttribute(positionBuffer, index2); this._tempC.fromBufferAttribute(positionBuffer, index3); this._tempB.sub(this._tempA); this._tempC.sub(this._tempA); this._tempA.addScaledVector(this._tempB, u1).addScaledVector(this._tempC, u2); p.position.copy(this._tempA); this._tempA.copy(this._tempB).cross(this._tempC).normalize(); p.velocity.copy(this._tempA).normalize().multiplyScalar(p.startSpeed); } toJSON() { return { type: 'mesh_surface', mesh: this._geometry ? this._geometry.uuid : '', }; } static fromJSON(json, meta) { return new MeshSurfaceEmitter(meta.geometries[json.geometry]); } clone() { return new MeshSurfaceEmitter(this._geometry); } update(system, delta) { } } quarks_core.loadPlugin({ id: "three.quarks", initialize: () => { }, emitterShapes: [{ type: 'mesh_surface', params: [['geometry', ['geometry']]], constructor: MeshSurfaceEmitter, loadJSON: MeshSurfaceEmitter.fromJSON, }], behaviors: [], }); class BatchedRenderer extends three.Object3D { constructor() { super(); this.batches = []; this.systemToBatchIndex = new Map(); this.type = 'BatchedRenderer'; this.depthTexture = null; } static equals(a, b) { return (a.material.side === b.material.side && a.material.blending === b.material.blending && a.material.blendSrc === b.material.blendSrc && a.material.blendDst === b.material.blendDst && a.material.blendEquation === b.material.blendEquation && a.material.premultipliedAlpha === b.material.premultipliedAlpha && a.material.transparent === b.material.transparent && a.material.depthTest === b.material.depthTest && a.material.type === b.material.type && a.material.alphaTest === b.material.alphaTest && a.material.map === b.material.map && a.renderMode === b.renderMode && a.blendTiles === b.blendTiles && a.softParticles === b.softParticles && a.softFarFade === b.softFarFade && a.softNearFade === b.softNearFade && a.uTileCount === b.uTileCount && a.vTileCount === b.vTileCount && a.instancingGeometry === b.instancingGeometry && a.renderOrder === b.renderOrder && a.layers.mask === b.layers.mask); } addSystem(system) { system._renderer = this; const settings = system.getRendererSettings(); for (let i = 0; i < this.batches.length; i++) { if (BatchedRenderer.equals(this.batches[i].settings, settings)) { this.batches[i].addSystem(system); this.systemToBatchIndex.set(system, i); return; } } let batch; switch (settings.renderMode) { case exports.RenderMode.Trail: batch = new TrailBatch(settings); break; case exports.RenderMode.Mesh: case exports.RenderMode.BillBoard: case exports.RenderMode.VerticalBillBoard: case exports.RenderMode.HorizontalBillBoard: case exports.RenderMode.StretchedBillBoard: batch = new SpriteBatch(settings); break; } if (this.depthTexture) { batch.applyDepthTexture(this.depthTexture); } batch.addSystem(system); this.batches.push(batch); this.systemToBatchIndex.set(system, this.batches.length - 1); this.add(batch); } deleteSystem(system) { const batchIndex = this.systemToBatchIndex.get(system); if (batchIndex != undefined) { this.batches[batchIndex].removeSystem(system); this.systemToBatchIndex.delete(system); } } setDepthTexture(depthTexture) { this.depthTexture = depthTexture; for (const batch of this.batches) { batch.applyDepthTexture(depthTexture); } } updateSystem(system) { this.deleteSystem(system); this.addSystem(system); } update(delta) { this.systemToBatchIndex.forEach((value, ps) => { ps.update(delta); }); for (let i = 0; i < this.batches.length; i++) { this.batches[i].update(); } } } const BatchedParticleRenderer = BatchedRenderer; class QuarksLoader extends three.ObjectLoader { constructor(manager) { super(manager); } linkReference(object) { const objectsMap = {}; object.traverse(function (child) { objectsMap[child.uuid] = child; }); object.traverse(function (child) { if (child.type === 'ParticleEmitter') { const system = child.system; system.emitterShape; for (let i = 0; i < system.behaviors.length; i++) { if (system.behaviors[i] instanceof quarks_core.EmitSubParticleSystem) { system.behaviors[i].subParticleSystem = objectsMap[system.behaviors[i].subParticleSystem]; } } } }); } parse(json, onLoad) { const object = super.parse(json, onLoad); this.linkReference(object); return object; } parseObject(data, geometries, materials, textures, animations) { let object; function getGeometry(name) { if (geometries[name] === undefined) { console.warn('THREE.ObjectLoader: Undefined geometry', name); } return geometries[name]; } function getMaterial(name) { if (name === undefined) return undefined; if (Array.isArray(name)) { const array = []; for (let i = 0, l = name.length; i < l; i++) { const uuid = name[i]; if (materials[uuid] === undefined) { console.warn('THREE.ObjectLoader: Undefined material', uuid); } array.push(materials[uuid]); } return array; } if (materials[name] === undefined) { console.warn('THREE.ObjectLoader: Undefined material', name); } return materials[name]; } function getTexture(uuid) { if (textures[uuid] === undefined) { console.warn('THREE.ObjectLoader: Undefined texture', uuid); } return textures[uuid]; } let geometry, material; const meta = { textures: textures, geometries: geometries, materials: materials, }; const dependencies = {}; switch (data.type) { case 'ParticleEmitter': object = ParticleSystem.fromJSON(data.ps, meta, dependencies).emitter; break; case 'Scene': object = new three.Scene(); if (data.background !== undefined) { if (Number.isInteger(data.background)) { object.background = new three.Color(data.background); } else { object.background = getTexture(data.background); } } if (data.environment !== undefined) { object.environment = getTexture(data.environment); } if (data.fog !== undefined) { if (data.fog.type === 'Fog') { object.fog = new three.Fog(data.fog.color, data.fog.near, data.fog.far); } else if (data.fog.type === 'FogExp2') { object.fog = new three.FogExp2(data.fog.color, data.fog.density); } if (data.fog.name !== '') { object.fog.name = data.fog.name; } } if (data.backgroundBlurriness !== undefined) object.backgroundBlurriness = data.backgroundBlurriness; if (data.backgroundIntensity !== undefined) object.backgroundIntensity = data.backgroundIntensity; if (data.backgroundRotation !== undefined) object.backgroundRotation.fromArray(data.backgroundRotation); if (data.environmentIntensity !== undefined) object.environmentIntensity = data.environmentIntensity; if (data.environmentRotation !== undefined) object.environmentRotation.fromArray(data.environmentRotation); break; case 'PerspectiveCamera': object = new three.PerspectiveCamera(data.fov, data.aspect, data.near, data.far); if (data.focus !== undefined) object.focus = data.focus; if (data.zoom !== undefined) object.zoom = data.zoom; if (data.filmGauge !== undefined) object.filmGauge = data.filmGauge; if (data.filmOffset !== undefined) object.filmOffset = data.filmOffset; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case 'OrthographicCamera': object = new three.OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far); if (data.zoom !== undefined) object.zoom = data.zoom; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case 'AmbientLight': object = new three.AmbientLight(data.color, data.intensity); break; case 'DirectionalLight': object = new three.DirectionalLight(data.color, data.intensity); break; case 'PointLight': object = new three.PointLight(data.color, data.intensity, data.distance, data.decay); break; case 'RectAreaLight': object = new three.RectAreaLight(data.color, data.intensity, data.width, data.height); break; case 'SpotLight': object = new three.SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay); break; case 'HemisphereLight': object = new three.HemisphereLight(data.color, data.groundColor, data.intensity); break; case 'LightProbe': object = new three.LightProbe().fromJSON(data); break; case 'SkinnedMesh': geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new three.SkinnedMesh(geometry, material); if (data.bindMode !== undefined) object.bindMode = data.bindMode; if (data.bindMatrix !== undefined) object.bindMatrix.fromArray(data.bindMatrix); if (data.skeleton !== undefined) object.skeleton = data.skeleton; break; case 'Mesh': geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new three.Mesh(geometry, material); break; case 'InstancedMesh': { geometry = getGeometry(data.geometry); material = getMaterial(data.material); const count = data.count; const instanceMatrix = data.instanceMatrix; const instanceColor = data.instanceColor; object = new three.InstancedMesh(geometry, material, count); object.instanceMatrix = new three.InstancedBufferAttribute(new Float32Array(instanceMatrix.array), 16); if (instanceColor !== undefined) object.instanceColor = new three.InstancedBufferAttribute(new Float32Array(instanceColor.array), instanceColor.itemSize); break; } case 'BatchedMesh': geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new three.BatchedMesh(data.maxGeometryCount, data.maxVertexCount, data.maxIndexCount, material); object.geometry = geometry; object.perObjectFrustumCulled = data.perObjectFrustumCulled; object.sortObjects = data.sortObjects; object._drawRanges = data.drawRanges; object._reservedRanges = data.reservedRanges; object._visibility = data.visibility; object._active = data.active; object._bounds = data.bounds.map((bound) => { const box = new three.Box3(); box.min.fromArray(bound.boxMin); box.max.fromArray(bound.boxMax); const sphere = new three.Sphere(); sphere.radius = bound.sphereRadius; sphere.center.fromArray(bound.sphereCenter); return { boxInitialized: bound.boxInitialized, box: box, sphereInitialized: bound.sphereInitialized, sphere: sphere, }; }); object._maxGeometryCount = data.maxGeometryCount; object._maxVertexCount = data.maxVertexCount; object._maxIndexCount = data.maxIndexCount; object._geometryInitialized = data.geometryInitialized; object._geometryCount = data.geometryCount; object._matricesTexture = getTexture(data.matricesTexture.uuid); break; case 'LOD': object = new three.LOD(); break; case 'Line': object = new three.Line(getGeometry(data.geometry), getMaterial(data.material)); break; case 'LineLoop': object = new three.LineLoop(getGeometry(data.geometry), getMaterial(data.material)); break; case 'LineSegments': object = new three.LineSegments(getGeometry(data.geometry), getMaterial(data.material)); break; case 'PointCloud': case 'Points': object = new three.Points(getGeometry(data.geometry), getMaterial(data.material)); break; case 'Sprite': object = new three.Sprite(getMaterial(data.material)); break; case 'Group': object = new three.Group(); break; case 'Bone': object = new three.Bone(); break; default: object = new three.Object3D(); } object.uuid = data.uuid; if (data.name !== undefined) object.name = data.name; if (data.matrix !== undefined) { object.matrix.fromArray(data.matrix); if (data.matrixAutoUpdate !== undefined) object.matrixAutoUpdate = data.matrixAutoUpdate; if (object.matrixAutoUpdate) { object.matrix.decompose(object.position, object.quaternion, object.scale); if (isNaN(object.quaternion.x)) { object.quaternion.set(0, 0, 0, 1); } } } else { if (data.position !== undefined) object.position.fromArray(data.position); if (data.rotation !== undefined) object.rotation.fromArray(data.rotation); if (data.quaternion !== undefined) object.quaternion.fromArray(data.quaternion); if (data.scale !== undefined) object.scale.fromArray(data.scale); } if (data.up !== undefined) object.up.fromArray(data.up); if (data.castShadow !== undefined) object.castShadow = data.castShadow; if (data.receiveShadow !== undefined) object.receiveShadow = data.receiveShadow; if (data.shadow) { if (data.shadow.bias !== undefined) object.shadow.bias = data.shadow.bias; if (data.shadow.normalBias !== undefined) object.normalBias = data.shadow.normalBias; if (data.shadow.radius !== undefined) object.radius = data.shadow.radius; if (data.shadow.mapSize !== undefined) object.mapSize.fromArray(data.shadow.mapSize); if (data.shadow.camera !== undefined) { object.camera = this.parseObject(data.shadow.camera); } } if (data.visible !== undefined) object.visible = data.visible; if (data.frustumCulled !== undefined) object.frustumCulled = data.frustumCulled; if (data.renderOrder !== undefined) object.renderOrder = data.renderOrder; if (data.userData !== undefined) object.userData = data.userData; if (data.layers !== undefined) object.layers.mask = data.layers; if (data.children !== undefined) { const children = data.children; for (let i = 0; i < children.length; i++) { object.add(this.parseObject(children[i], geometries, materials, textures, animations)); } } if (data.animations !== undefined) { const objectAnimations = data.animations; for (let i = 0; i < objectAnimations.length; i++) { const uuid = objectAnimations[i]; object.animations.push(animations[uuid]); } } if (data.type === 'LOD') { if (data.autoUpdate !== undefined) object.autoUpdate = data.autoUpdate; const levels = data.levels; for (let l = 0; l < levels.length; l++) { const level = levels[l]; const child = object.getObjectByProperty('uuid', level.object); if (child !== undefined) { object.addLevel(child, level.distance); } } } return object; } } class QuarksUtil { static runOnAllParticleEmitters(obj, func) { obj.traverse((child) => { if (child.type === 'ParticleEmitter') { func(child); } }); if (obj.type === 'ParticleEmitter') { func(obj); } } static addToBatchRenderer(obj, batchRenderer) { QuarksUtil.runOnAllParticleEmitters(obj, (ps) => { batchRenderer.addSystem(ps.system); }); } static play(obj) { QuarksUtil.runOnAllParticleEmitters(obj, (ps) => { ps.system.play(); }); } static stop(obj) { QuarksUtil.runOnAllParticleEmitters(obj, (ps) => { ps.system.stop(); }); } static setAutoDestroy(obj, value) { QuarksUtil.runOnAllParticleEmitters(obj, (ps) => { ps.system.autoDestroy = value; }); } static endEmit(obj) { QuarksUtil.runOnAllParticleEmitters(obj, (ps) => { ps.system.endEmit(); }); } static restart(obj) { QuarksUtil.runOnAllParticleEmitters(obj, (ps) => { ps.system.restart(); }); } static pause(obj) { QuarksUtil.runOnAllParticleEmitters(obj, (ps) => { ps.system.pause(); }); } } registerShaderChunks(); console.log('%c Particle system powered by three.quarks. https://quarks.art/', 'font-size: 14px; font-weight: bold;'); exports.BatchedParticleRenderer = BatchedParticleRenderer; exports.BatchedRenderer = BatchedRenderer; exports.MeshSurfaceEmitter = MeshSurfaceEmitter; exports.ParticleEmitter = ParticleEmitter; exports.ParticleMeshPhysicsMaterial = ParticleMeshPhysicsMaterial; exports.ParticleMeshStandardMaterial = ParticleMeshStandardMaterial; exports.ParticleSystem = ParticleSystem; exports.QuarksLoader = QuarksLoader; exports.QuarksUtil = QuarksUtil; exports.SpriteBatch = SpriteBatch; exports.TrailBatch = TrailBatch; exports.VFXBatch = VFXBatch; exports.registerShaderChunks = registerShaderChunks; Object.keys(quarks_core).forEach(function (k) { if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, { enumerable: true, get: function () { return quarks_core[k]; } }); });