UNPKG

playcanvas

Version:

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

222 lines (221 loc) 6.3 kB
import { Curve } from "../../../core/math/curve.js"; import { CurveSet } from "../../../core/math/curve-set.js"; import { Vec3 } from "../../../core/math/vec3.js"; import { Asset } from "../../asset/asset.js"; import { ComponentSystem } from "../system.js"; import { ParticleSystemComponent } from "./component.js"; import { ParticleSystemComponentData } from "./data.js"; import { particleChunksGLSL } from "../../../scene/shader-lib/glsl/collections/particle-chunks-glsl.js"; import { particleChunksWGSL } from "../../../scene/shader-lib/wgsl/collections/particle-chunks-wgsl.js"; import { SHADERLANGUAGE_GLSL, SHADERLANGUAGE_WGSL } from "../../../platform/graphics/constants.js"; import { ShaderChunks } from "../../../scene/shader-lib/shader-chunks.js"; const _schema = [ "enabled", "autoPlay", "numParticles", "lifetime", "rate", "rate2", "startAngle", "startAngle2", "loop", "preWarm", "lighting", "halfLambert", "intensity", "depthWrite", "noFog", "depthSoftening", "sort", "blendType", "stretch", "alignToMotion", "emitterShape", "emitterExtents", "emitterExtentsInner", "emitterRadius", "emitterRadiusInner", "initialVelocity", "wrap", "wrapBounds", "localSpace", "screenSpace", "colorMapAsset", "normalMapAsset", "mesh", "meshAsset", "renderAsset", "orientation", "particleNormal", "localVelocityGraph", "localVelocityGraph2", "velocityGraph", "velocityGraph2", "rotationSpeedGraph", "rotationSpeedGraph2", "radialSpeedGraph", "radialSpeedGraph2", "scaleGraph", "scaleGraph2", "colorGraph", "colorGraph2", "alphaGraph", "alphaGraph2", "colorMap", "normalMap", "animTilesX", "animTilesY", "animStartFrame", "animNumFrames", "animNumAnimations", "animIndex", "randomizeAnimIndex", "animSpeed", "animLoop", "layers" ]; class ParticleSystemComponentSystem extends ComponentSystem { constructor(app) { super(app); this.id = "particlesystem"; this.ComponentType = ParticleSystemComponent; this.DataType = ParticleSystemComponentData; this.schema = _schema; this.propertyTypes = { emitterExtents: "vec3", emitterExtentsInner: "vec3", particleNormal: "vec3", wrapBounds: "vec3", localVelocityGraph: "curveset", localVelocityGraph2: "curveset", velocityGraph: "curveset", velocityGraph2: "curveset", colorGraph: "curveset", colorGraph2: "curveset", alphaGraph: "curve", alphaGraph2: "curve", rotationSpeedGraph: "curve", rotationSpeedGraph2: "curve", radialSpeedGraph: "curve", radialSpeedGraph2: "curve", scaleGraph: "curve", scaleGraph2: "curve" }; this.on("beforeremove", this.onBeforeRemove, this); this.app.systems.on("update", this.onUpdate, this); ShaderChunks.get(app.graphicsDevice, SHADERLANGUAGE_GLSL).add(particleChunksGLSL); ShaderChunks.get(app.graphicsDevice, SHADERLANGUAGE_WGSL).add(particleChunksWGSL); } initializeComponentData(component, _data, properties) { const data = {}; properties = []; const types = this.propertyTypes; if (_data.mesh instanceof Asset || typeof _data.mesh === "number") { _data.meshAsset = _data.mesh; delete _data.mesh; } for (const prop in _data) { if (_data.hasOwnProperty(prop)) { properties.push(prop); data[prop] = _data[prop]; } if (types[prop] === "vec3") { if (Array.isArray(data[prop])) { data[prop] = new Vec3(data[prop][0], data[prop][1], data[prop][2]); } } else if (types[prop] === "curve") { if (!(data[prop] instanceof Curve)) { const t = data[prop].type; data[prop] = new Curve(data[prop].keys); data[prop].type = t; } } else if (types[prop] === "curveset") { if (!(data[prop] instanceof CurveSet)) { const t = data[prop].type; data[prop] = new CurveSet(data[prop].keys); data[prop].type = t; } } if (data.layers && Array.isArray(data.layers)) { data.layers = data.layers.slice(0); } } super.initializeComponentData(component, data, properties); } cloneComponent(entity, clone) { const source = entity.particlesystem.data; const schema = this.schema; const data = {}; for (let i = 0, len = schema.length; i < len; i++) { const prop = schema[i]; let sourceProp = source[prop]; if (sourceProp instanceof Vec3 || sourceProp instanceof Curve || sourceProp instanceof CurveSet) { sourceProp = sourceProp.clone(); data[prop] = sourceProp; } else if (prop === "layers") { data.layers = source.layers.slice(0); } else { if (sourceProp !== null && sourceProp !== void 0) { data[prop] = sourceProp; } } } return this.addComponent(clone, data); } onUpdate(dt) { const components = this.store; const stats = this.app.stats.particles; const composition = this.app.scene.layers; for (let i = 0; i < composition.layerList.length; i++) { composition.layerList[i].requiresLightCube = false; } for (const id in components) { if (components.hasOwnProperty(id)) { const component = components[id]; const entity = component.entity; const data = component.data; if (data.enabled && entity.enabled) { const emitter = entity.particlesystem.emitter; if (!emitter?.meshInstance.visible) continue; if (emitter.lighting) { const layers = data.layers; for (let i = 0; i < layers.length; i++) { const layer = composition.getLayerById(layers[i]); if (layer) { layer.requiresLightCube = true; } } } if (!data.paused) { let numSteps = 0; emitter.simTime += dt; if (emitter.simTime >= emitter.fixedTimeStep) { numSteps = Math.floor(emitter.simTime / emitter.fixedTimeStep); emitter.simTime -= numSteps * emitter.fixedTimeStep; } if (numSteps) { numSteps = Math.min(numSteps, emitter.maxSubSteps); for (let i = 0; i < numSteps; i++) { emitter.addTime(emitter.fixedTimeStep, false); } stats._updatesPerFrame += numSteps; stats._frameTime += emitter._addTimeTime; emitter._addTimeTime = 0; } emitter.finishFrame(); } } } } } onBeforeRemove(entity, component) { component.onBeforeRemove(); } destroy() { super.destroy(); this.app.systems.off("update", this.onUpdate, this); } } export { ParticleSystemComponentSystem };