123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910 |
- import Cartesian2 from "../Core/Cartesian2.js";
- import Cartesian3 from "../Core/Cartesian3.js";
- import Check from "../Core/Check.js";
- import Color from "../Core/Color.js";
- import defaultValue from "../Core/defaultValue.js";
- import defined from "../Core/defined.js";
- import destroyObject from "../Core/destroyObject.js";
- import Event from "../Core/Event.js";
- import JulianDate from "../Core/JulianDate.js";
- import CesiumMath from "../Core/Math.js";
- import Matrix4 from "../Core/Matrix4.js";
- import BillboardCollection from "./BillboardCollection.js";
- import CircleEmitter from "./CircleEmitter.js";
- import Particle from "./Particle.js";
- var defaultImageSize = new Cartesian2(1.0, 1.0);
- function ParticleSystem(options) {
- options = defaultValue(options, defaultValue.EMPTY_OBJECT);
-
- this.show = defaultValue(options.show, true);
-
- this.updateCallback = options.updateCallback;
-
- this.loop = defaultValue(options.loop, true);
-
- this.image = defaultValue(options.image, undefined);
- var emitter = options.emitter;
- if (!defined(emitter)) {
- emitter = new CircleEmitter(0.5);
- }
- this._emitter = emitter;
- this._bursts = options.bursts;
- this._modelMatrix = Matrix4.clone(
- defaultValue(options.modelMatrix, Matrix4.IDENTITY)
- );
- this._emitterModelMatrix = Matrix4.clone(
- defaultValue(options.emitterModelMatrix, Matrix4.IDENTITY)
- );
- this._matrixDirty = true;
- this._combinedMatrix = new Matrix4();
- this._startColor = Color.clone(
- defaultValue(options.color, defaultValue(options.startColor, Color.WHITE))
- );
- this._endColor = Color.clone(
- defaultValue(options.color, defaultValue(options.endColor, Color.WHITE))
- );
- this._startScale = defaultValue(
- options.scale,
- defaultValue(options.startScale, 1.0)
- );
- this._endScale = defaultValue(
- options.scale,
- defaultValue(options.endScale, 1.0)
- );
- this._emissionRate = defaultValue(options.emissionRate, 5.0);
- this._minimumSpeed = defaultValue(
- options.speed,
- defaultValue(options.minimumSpeed, 1.0)
- );
- this._maximumSpeed = defaultValue(
- options.speed,
- defaultValue(options.maximumSpeed, 1.0)
- );
- this._minimumParticleLife = defaultValue(
- options.particleLife,
- defaultValue(options.minimumParticleLife, 5.0)
- );
- this._maximumParticleLife = defaultValue(
- options.particleLife,
- defaultValue(options.maximumParticleLife, 5.0)
- );
- this._minimumMass = defaultValue(
- options.mass,
- defaultValue(options.minimumMass, 1.0)
- );
- this._maximumMass = defaultValue(
- options.mass,
- defaultValue(options.maximumMass, 1.0)
- );
- this._minimumImageSize = Cartesian2.clone(
- defaultValue(
- options.imageSize,
- defaultValue(options.minimumImageSize, defaultImageSize)
- )
- );
- this._maximumImageSize = Cartesian2.clone(
- defaultValue(
- options.imageSize,
- defaultValue(options.maximumImageSize, defaultImageSize)
- )
- );
- this._sizeInMeters = defaultValue(options.sizeInMeters, false);
- this._lifetime = defaultValue(options.lifetime, Number.MAX_VALUE);
- this._billboardCollection = undefined;
- this._particles = [];
-
- this._particlePool = [];
- this._previousTime = undefined;
- this._currentTime = 0.0;
- this._carryOver = 0.0;
- this._complete = new Event();
- this._isComplete = false;
- this._updateParticlePool = true;
- this._particleEstimate = 0;
- }
- Object.defineProperties(ParticleSystem.prototype, {
-
- emitter: {
- get: function () {
- return this._emitter;
- },
- set: function (value) {
-
- Check.defined("value", value);
-
- this._emitter = value;
- },
- },
-
- bursts: {
- get: function () {
- return this._bursts;
- },
- set: function (value) {
- this._bursts = value;
- this._updateParticlePool = true;
- },
- },
-
- modelMatrix: {
- get: function () {
- return this._modelMatrix;
- },
- set: function (value) {
-
- Check.defined("value", value);
-
- this._matrixDirty =
- this._matrixDirty || !Matrix4.equals(this._modelMatrix, value);
- Matrix4.clone(value, this._modelMatrix);
- },
- },
-
- emitterModelMatrix: {
- get: function () {
- return this._emitterModelMatrix;
- },
- set: function (value) {
-
- Check.defined("value", value);
-
- this._matrixDirty =
- this._matrixDirty || !Matrix4.equals(this._emitterModelMatrix, value);
- Matrix4.clone(value, this._emitterModelMatrix);
- },
- },
-
- startColor: {
- get: function () {
- return this._startColor;
- },
- set: function (value) {
-
- Check.defined("value", value);
-
- Color.clone(value, this._startColor);
- },
- },
-
- endColor: {
- get: function () {
- return this._endColor;
- },
- set: function (value) {
-
- Check.defined("value", value);
-
- Color.clone(value, this._endColor);
- },
- },
-
- startScale: {
- get: function () {
- return this._startScale;
- },
- set: function (value) {
-
- Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
-
- this._startScale = value;
- },
- },
-
- endScale: {
- get: function () {
- return this._endScale;
- },
- set: function (value) {
-
- Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
-
- this._endScale = value;
- },
- },
-
- emissionRate: {
- get: function () {
- return this._emissionRate;
- },
- set: function (value) {
-
- Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
-
- this._emissionRate = value;
- this._updateParticlePool = true;
- },
- },
-
- minimumSpeed: {
- get: function () {
- return this._minimumSpeed;
- },
- set: function (value) {
-
- Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
-
- this._minimumSpeed = value;
- },
- },
-
- maximumSpeed: {
- get: function () {
- return this._maximumSpeed;
- },
- set: function (value) {
-
- Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
-
- this._maximumSpeed = value;
- },
- },
-
- minimumParticleLife: {
- get: function () {
- return this._minimumParticleLife;
- },
- set: function (value) {
-
- Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
-
- this._minimumParticleLife = value;
- },
- },
-
- maximumParticleLife: {
- get: function () {
- return this._maximumParticleLife;
- },
- set: function (value) {
-
- Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
-
- this._maximumParticleLife = value;
- this._updateParticlePool = true;
- },
- },
-
- minimumMass: {
- get: function () {
- return this._minimumMass;
- },
- set: function (value) {
-
- Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
-
- this._minimumMass = value;
- },
- },
-
- maximumMass: {
- get: function () {
- return this._maximumMass;
- },
- set: function (value) {
-
- Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
-
- this._maximumMass = value;
- },
- },
-
- minimumImageSize: {
- get: function () {
- return this._minimumImageSize;
- },
- set: function (value) {
-
- Check.typeOf.object("value", value);
- Check.typeOf.number.greaterThanOrEquals("value.x", value.x, 0.0);
- Check.typeOf.number.greaterThanOrEquals("value.y", value.y, 0.0);
-
- this._minimumImageSize = value;
- },
- },
-
- maximumImageSize: {
- get: function () {
- return this._maximumImageSize;
- },
- set: function (value) {
-
- Check.typeOf.object("value", value);
- Check.typeOf.number.greaterThanOrEquals("value.x", value.x, 0.0);
- Check.typeOf.number.greaterThanOrEquals("value.y", value.y, 0.0);
-
- this._maximumImageSize = value;
- },
- },
-
- sizeInMeters: {
- get: function () {
- return this._sizeInMeters;
- },
- set: function (value) {
-
- Check.typeOf.bool("value", value);
-
- this._sizeInMeters = value;
- },
- },
-
- lifetime: {
- get: function () {
- return this._lifetime;
- },
- set: function (value) {
-
- Check.typeOf.number.greaterThanOrEquals("value", value, 0.0);
-
- this._lifetime = value;
- },
- },
-
- complete: {
- get: function () {
- return this._complete;
- },
- },
-
- isComplete: {
- get: function () {
- return this._isComplete;
- },
- },
- });
- function updateParticlePool(system) {
- var emissionRate = system._emissionRate;
- var life = system._maximumParticleLife;
- var burstAmount = 0;
- var bursts = system._bursts;
- if (defined(bursts)) {
- var length = bursts.length;
- for (var i = 0; i < length; ++i) {
- burstAmount += bursts[i].maximum;
- }
- }
- var billboardCollection = system._billboardCollection;
- var image = system.image;
- var particleEstimate = Math.ceil(emissionRate * life + burstAmount);
- var particles = system._particles;
- var particlePool = system._particlePool;
- var numToAdd = Math.max(
- particleEstimate - particles.length - particlePool.length,
- 0
- );
- for (var j = 0; j < numToAdd; ++j) {
- var particle = new Particle();
- particle._billboard = billboardCollection.add({
- image: image,
- });
- particlePool.push(particle);
- }
- system._particleEstimate = particleEstimate;
- }
- function getOrCreateParticle(system) {
-
- var particle = system._particlePool.pop();
- if (!defined(particle)) {
-
- particle = new Particle();
- }
- return particle;
- }
- function addParticleToPool(system, particle) {
- system._particlePool.push(particle);
- }
- function freeParticlePool(system) {
- var particles = system._particles;
- var particlePool = system._particlePool;
- var billboardCollection = system._billboardCollection;
- var numParticles = particles.length;
- var numInPool = particlePool.length;
- var estimate = system._particleEstimate;
- var start = numInPool - Math.max(estimate - numParticles - numInPool, 0);
- for (var i = start; i < numInPool; ++i) {
- var p = particlePool[i];
- billboardCollection.remove(p._billboard);
- }
- particlePool.length = start;
- }
- function removeBillboard(particle) {
- if (defined(particle._billboard)) {
- particle._billboard.show = false;
- }
- }
- function updateBillboard(system, particle) {
- var billboard = particle._billboard;
- if (!defined(billboard)) {
- billboard = particle._billboard = system._billboardCollection.add({
- image: particle.image,
- });
- }
- billboard.width = particle.imageSize.x;
- billboard.height = particle.imageSize.y;
- billboard.position = particle.position;
- billboard.sizeInMeters = system.sizeInMeters;
- billboard.show = true;
-
- var r = CesiumMath.lerp(
- particle.startColor.red,
- particle.endColor.red,
- particle.normalizedAge
- );
- var g = CesiumMath.lerp(
- particle.startColor.green,
- particle.endColor.green,
- particle.normalizedAge
- );
- var b = CesiumMath.lerp(
- particle.startColor.blue,
- particle.endColor.blue,
- particle.normalizedAge
- );
- var a = CesiumMath.lerp(
- particle.startColor.alpha,
- particle.endColor.alpha,
- particle.normalizedAge
- );
- billboard.color = new Color(r, g, b, a);
-
- billboard.scale = CesiumMath.lerp(
- particle.startScale,
- particle.endScale,
- particle.normalizedAge
- );
- }
- function addParticle(system, particle) {
- particle.startColor = Color.clone(system._startColor, particle.startColor);
- particle.endColor = Color.clone(system._endColor, particle.endColor);
- particle.startScale = system._startScale;
- particle.endScale = system._endScale;
- particle.image = system.image;
- particle.life = CesiumMath.randomBetween(
- system._minimumParticleLife,
- system._maximumParticleLife
- );
- particle.mass = CesiumMath.randomBetween(
- system._minimumMass,
- system._maximumMass
- );
- particle.imageSize.x = CesiumMath.randomBetween(
- system._minimumImageSize.x,
- system._maximumImageSize.x
- );
- particle.imageSize.y = CesiumMath.randomBetween(
- system._minimumImageSize.y,
- system._maximumImageSize.y
- );
-
- particle._normalizedAge = 0.0;
- particle._age = 0.0;
- var speed = CesiumMath.randomBetween(
- system._minimumSpeed,
- system._maximumSpeed
- );
- Cartesian3.multiplyByScalar(particle.velocity, speed, particle.velocity);
- system._particles.push(particle);
- }
- function calculateNumberToEmit(system, dt) {
-
- if (system._isComplete) {
- return 0;
- }
- dt = CesiumMath.mod(dt, system._lifetime);
-
- var v = dt * system._emissionRate;
- var numToEmit = Math.floor(v);
- system._carryOver += v - numToEmit;
- if (system._carryOver > 1.0) {
- numToEmit++;
- system._carryOver -= 1.0;
- }
-
- if (defined(system.bursts)) {
- var length = system.bursts.length;
- for (var i = 0; i < length; i++) {
- var burst = system.bursts[i];
- var currentTime = system._currentTime;
- if (defined(burst) && !burst._complete && currentTime > burst.time) {
- numToEmit += CesiumMath.randomBetween(burst.minimum, burst.maximum);
- burst._complete = true;
- }
- }
- }
- return numToEmit;
- }
- var rotatedVelocityScratch = new Cartesian3();
- ParticleSystem.prototype.update = function (frameState) {
- if (!this.show) {
- return;
- }
- if (!defined(this._billboardCollection)) {
- this._billboardCollection = new BillboardCollection();
- }
- if (this._updateParticlePool) {
- updateParticlePool(this);
- this._updateParticlePool = false;
- }
-
- var dt = 0.0;
- if (this._previousTime) {
- dt = JulianDate.secondsDifference(frameState.time, this._previousTime);
- }
- if (dt < 0.0) {
- dt = 0.0;
- }
- var particles = this._particles;
- var emitter = this._emitter;
- var updateCallback = this.updateCallback;
- var i;
- var particle;
-
- var length = particles.length;
- for (i = 0; i < length; ++i) {
- particle = particles[i];
- if (!particle.update(dt, updateCallback)) {
- removeBillboard(particle);
-
- addParticleToPool(this, particle);
- particles[i] = particles[length - 1];
- --i;
- --length;
- } else {
- updateBillboard(this, particle);
- }
- }
- particles.length = length;
- var numToEmit = calculateNumberToEmit(this, dt);
- if (numToEmit > 0 && defined(emitter)) {
-
- if (this._matrixDirty) {
- this._combinedMatrix = Matrix4.multiply(
- this.modelMatrix,
- this.emitterModelMatrix,
- this._combinedMatrix
- );
- this._matrixDirty = false;
- }
- var combinedMatrix = this._combinedMatrix;
- for (i = 0; i < numToEmit; i++) {
-
- particle = getOrCreateParticle(this);
-
- this._emitter.emit(particle);
-
- Cartesian3.add(
- particle.position,
- particle.velocity,
- rotatedVelocityScratch
- );
- Matrix4.multiplyByPoint(
- combinedMatrix,
- rotatedVelocityScratch,
- rotatedVelocityScratch
- );
-
- particle.position = Matrix4.multiplyByPoint(
- combinedMatrix,
- particle.position,
- particle.position
- );
-
- Cartesian3.subtract(
- rotatedVelocityScratch,
- particle.position,
- particle.velocity
- );
- Cartesian3.normalize(particle.velocity, particle.velocity);
-
- addParticle(this, particle);
- updateBillboard(this, particle);
- }
- }
- this._billboardCollection.update(frameState);
- this._previousTime = JulianDate.clone(frameState.time, this._previousTime);
- this._currentTime += dt;
- if (
- this._lifetime !== Number.MAX_VALUE &&
- this._currentTime > this._lifetime
- ) {
- if (this.loop) {
- this._currentTime = CesiumMath.mod(this._currentTime, this._lifetime);
- if (this.bursts) {
- var burstLength = this.bursts.length;
-
- for (i = 0; i < burstLength; i++) {
- this.bursts[i]._complete = false;
- }
- }
- } else {
- this._isComplete = true;
- this._complete.raiseEvent(this);
- }
- }
-
- if (frameState.frameNumber % 120 === 0) {
- freeParticlePool(this);
- }
- };
- ParticleSystem.prototype.isDestroyed = function () {
- return false;
- };
- ParticleSystem.prototype.destroy = function () {
- this._billboardCollection =
- this._billboardCollection && this._billboardCollection.destroy();
- return destroyObject(this);
- };
- export default ParticleSystem;
|