//// ParticleEffectEntityItem.cpp// libraries/entities/src//// Some starter code for a particle simulation entity, which could ideally be used for a variety of effects.// This is some really early and rough stuff here. It was enough for now to just get it up and running in the interface.//// Todo's and other notes:// - The simulation should restart when the AnimationLoop's max frame is reached (or passed), but there doesn't seem// to be a good way to set that max frame to something reasonable right now.// - There seems to be a bug whereby entities on the edge of screen will just pop off or on. This is probably due// to my lack of understanding of how entities in the octree are picked for rendering. I am updating the entity// dimensions based on the bounds of the sim, but maybe I need to update a dirty flag or something.// - This should support some kind of pre-roll of the simulation.// - Just to get this out the door, I just did forward Euler integration. There are better ways.// - Gravity always points along the Y axis. Support an actual gravity vector.// - Add the ability to add arbitrary forces to the simulation.// - Add drag.// - Add some kind of support for collisions.// - There's no synchronization of the simulation across clients at all. In fact, it's using rand() under the hood, so// there's no gaurantee that different clients will see simulations that look anything like the other.//// Created by Jason Rickwald on 3/2/15.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "ParticleEffectEntityItem.h" #include <glm/gtx/transform.hpp>#include <QtCore/QJsonDocument> #include <ByteCountCoding.h>#include <GeometryUtil.h>#include <Interpolate.h> #include "EntityTree.h"#include "EntityTreeElement.h"#include "EntitiesLogging.h"#include "EntityScriptingInterface.h" using namespace particle; template <typename T>bool operator==(const Range<T>& a, const Range<T>& b) { return (a.start == b.start && a.finish == b.finish);} template <typename T>bool operator==(const Gradient<T>& a, const Gradient<T>& b) { return (a.target == b.target && a.spread == b.spread);} template <typename T>bool operator==(const RangeGradient<T>& a, const RangeGradient<T>& b) { return (a.gradient == b.gradient && a.range == b.range);} template <typename T>bool operator!=(const Range<T>& a, const Range<T>& b) { return !(a == b);} template <typename T>bool operator!=(const Gradient<T>& a, const Gradient<T>& b) { return !(a == b);} template <typename T>bool operator!=(const RangeGradient<T>& a, const RangeGradient<T>& b) { return !(a == b);} bool operator==(const EmitProperties& a, const EmitProperties& b) { return (a.rate == b.rate) && (a.speed == b.speed) && (a.acceleration == b.acceleration) && (a.orientation == b.orientation) && (a.dimensions == b.dimensions) && (a.shouldTrail == b.shouldTrail);} bool operator!=(const EmitProperties& a, const EmitProperties& b) { return !(a == b);} bool operator==(const Properties& a, const Properties& b) { return (a.color == b.color) && (a.alpha == b.alpha) && (a.radiusStart == b.radiusStart) && (a.radius == b.radius) && (a.spin == b.spin) && (a.rotateWithEntity == b.rotateWithEntity) && (a.lifespan == b.lifespan) && (a.maxParticles == b.maxParticles) && (a.emission == b.emission) && (a.polar == b.polar) && (a.azimuth == b.azimuth) && (a.textures == b.textures);} bool operator!=(const Properties& a, const Properties& b) { return !(a == b);} bool Properties::valid() const { if (glm::any(glm::isnan(emission.orientation))) { qCWarning(entities) << "Bad particle data"; return false; } return (alpha.gradient.target == glm::clamp(alpha.gradient.target, MINIMUM_ALPHA, MAXIMUM_ALPHA)) && (alpha.range.start == glm::clamp(alpha.range.start, MINIMUM_ALPHA, MAXIMUM_ALPHA)) && (alpha.range.finish == glm::clamp(alpha.range.finish, MINIMUM_ALPHA, MAXIMUM_ALPHA)) && (alpha.gradient.spread == glm::clamp(alpha.gradient.spread, MINIMUM_ALPHA, MAXIMUM_ALPHA)) && (radiusStart == glm::clamp(radiusStart, MINIMUM_EMIT_RADIUS_START, MAXIMUM_EMIT_RADIUS_START)) && (radius.gradient.target == glm::clamp(radius.gradient.target, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) && (radius.range.start == glm::clamp(radius.range.start, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) && (radius.range.finish == glm::clamp(radius.range.finish, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) && (radius.gradient.spread == glm::clamp(radius.gradient.spread, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) && (spin.gradient.target == glm::clamp(spin.gradient.target, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) && (spin.range.start == glm::clamp(spin.range.start, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) && (spin.range.finish == glm::clamp(spin.range.finish, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) && (spin.gradient.spread == glm::clamp(spin.gradient.spread, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) && (lifespan == glm::clamp(lifespan, MINIMUM_LIFESPAN, MAXIMUM_LIFESPAN)) && (maxParticles == glm::clamp(maxParticles, MINIMUM_MAX_PARTICLES, MAXIMUM_MAX_PARTICLES)) && (emission.rate == glm::clamp(emission.rate, MINIMUM_EMIT_RATE, MAXIMUM_EMIT_RATE)) && (emission.speed.target == glm::clamp(emission.speed.target, MINIMUM_EMIT_SPEED, MAXIMUM_EMIT_SPEED)) && (emission.speed.spread == glm::clamp(emission.speed.spread, MINIMUM_EMIT_SPEED, MAXIMUM_EMIT_SPEED)) && (emission.acceleration.target == glm::clamp(emission.acceleration.target, vec3(MINIMUM_EMIT_ACCELERATION), vec3(MAXIMUM_EMIT_ACCELERATION))) && (emission.acceleration.spread == glm::clamp(emission.acceleration.spread, vec3(MINIMUM_ACCELERATION_SPREAD), vec3(MAXIMUM_ACCELERATION_SPREAD)) && (emission.dimensions == glm::clamp(emission.dimensions, vec3(MINIMUM_EMIT_DIMENSION), vec3(MAXIMUM_EMIT_DIMENSION))) && (polar.start == glm::clamp(polar.start, MINIMUM_POLAR, MAXIMUM_POLAR)) && (polar.finish == glm::clamp(polar.finish, MINIMUM_POLAR, MAXIMUM_POLAR)) && (azimuth.start == glm::clamp(azimuth.start, MINIMUM_AZIMUTH, MAXIMUM_AZIMUTH)) && (azimuth.finish == glm::clamp(azimuth.finish, MINIMUM_AZIMUTH, MAXIMUM_AZIMUTH)));} bool Properties::emitting() const { return emission.rate > 0.0f && lifespan > 0.0f && polar.start <= polar.finish; } uint64_t Properties::emitIntervalUsecs() const { if (emission.rate > 0.0f) { return (uint64_t)(USECS_PER_SECOND / emission.rate); } return 0;} EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { std::shared_ptr<ParticleEffectEntityItem> entity(new ParticleEffectEntityItem(entityID), [](ParticleEffectEntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity;} // our non-pure virtual subclass for now...ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID){ _type = EntityTypes::ParticleEffect; _visuallyReady = false;} void ParticleEffectEntityItem::setAlpha(float alpha) { alpha = glm::clamp(alpha, MINIMUM_ALPHA, MAXIMUM_ALPHA); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.alpha.gradient.target != alpha; _particleProperties.alpha.gradient.target = alpha; });} float ParticleEffectEntityItem::getAlpha() const { return resultWithReadLock<float>([&] { return _particleProperties.alpha.gradient.target; });} void ParticleEffectEntityItem::setAlphaStart(float alphaStart) { alphaStart = glm::isnan(alphaStart) ? alphaStart : glm::clamp(alphaStart, MINIMUM_ALPHA, MAXIMUM_ALPHA); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.alpha.range.start != alphaStart; _particleProperties.alpha.range.start = alphaStart; });} float ParticleEffectEntityItem::getAlphaStart() const { return resultWithReadLock<float>([&] { return _particleProperties.alpha.range.start; });} void ParticleEffectEntityItem::setAlphaFinish(float alphaFinish) { alphaFinish = glm::isnan(alphaFinish) ? alphaFinish : glm::clamp(alphaFinish, MINIMUM_ALPHA, MAXIMUM_ALPHA); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.alpha.range.finish != alphaFinish; _particleProperties.alpha.range.finish = alphaFinish; });} float ParticleEffectEntityItem::getAlphaFinish() const { return resultWithReadLock<float>([&] { return _particleProperties.alpha.range.finish; });} void ParticleEffectEntityItem::setAlphaSpread(float alphaSpread) { alphaSpread = glm::clamp(alphaSpread, MINIMUM_ALPHA, MAXIMUM_ALPHA); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.alpha.gradient.spread != alphaSpread; _particleProperties.alpha.gradient.spread = alphaSpread; });} float ParticleEffectEntityItem::getAlphaSpread() const { return resultWithReadLock<float>([&] { return _particleProperties.alpha.gradient.spread; });} void ParticleEffectEntityItem::setLifespan(float lifespan) { lifespan = glm::clamp(lifespan, MINIMUM_LIFESPAN, MAXIMUM_LIFESPAN); bool changed; withWriteLock([&] { changed = _particleProperties.lifespan != lifespan; _needsRenderUpdate |= changed; _particleProperties.lifespan = lifespan; }); if (changed) { computeAndUpdateDimensions(); }} float ParticleEffectEntityItem::getLifespan() const { return resultWithReadLock<float>([&] { return _particleProperties.lifespan; });} void ParticleEffectEntityItem::setEmitRate(float emitRate) { emitRate = glm::clamp(emitRate, MINIMUM_EMIT_RATE, MAXIMUM_EMIT_RATE); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.emission.rate != emitRate; _particleProperties.emission.rate = emitRate; });} float ParticleEffectEntityItem::getEmitRate() const { return resultWithReadLock<float>([&] { return _particleProperties.emission.rate; });} void ParticleEffectEntityItem::setEmitSpeed(float emitSpeed) { emitSpeed = glm::clamp(emitSpeed, MINIMUM_EMIT_SPEED, MAXIMUM_EMIT_SPEED); bool changed; withWriteLock([&] { changed = _particleProperties.emission.speed.target != emitSpeed; _needsRenderUpdate |= changed; _particleProperties.emission.speed.target = emitSpeed; }); if (changed) { computeAndUpdateDimensions(); }} float ParticleEffectEntityItem::getEmitSpeed() const { return resultWithReadLock<float>([&] { return _particleProperties.emission.speed.target; });} void ParticleEffectEntityItem::setSpeedSpread(float speedSpread) { speedSpread = glm::clamp(speedSpread, MINIMUM_EMIT_SPEED, MAXIMUM_EMIT_SPEED); bool changed; withWriteLock([&] { changed = _particleProperties.emission.speed.spread != speedSpread; _needsRenderUpdate |= changed; _particleProperties.emission.speed.spread = speedSpread; }); if (changed) { computeAndUpdateDimensions(); }} float ParticleEffectEntityItem::getSpeedSpread() const { return resultWithReadLock<float>([&] { return _particleProperties.emission.speed.spread; });} void ParticleEffectEntityItem::setEmitOrientation(const glm::quat& emitOrientation_) { auto emitOrientation = glm::normalize(emitOrientation_); bool changed; withWriteLock([&] { changed = _particleProperties.emission.orientation != emitOrientation; _needsRenderUpdate |= changed; _particleProperties.emission.orientation = emitOrientation; }); if (changed) { computeAndUpdateDimensions(); }} glm::quat ParticleEffectEntityItem::getEmitOrientation() const { return resultWithReadLock<glm::quat>([&] { return _particleProperties.emission.orientation; });} void ParticleEffectEntityItem::setEmitDimensions(const glm::vec3& emitDimensions_) { auto emitDimensions = glm::clamp(emitDimensions_, vec3(MINIMUM_EMIT_DIMENSION), vec3(MAXIMUM_EMIT_DIMENSION)); bool changed; withWriteLock([&] { changed = _particleProperties.emission.dimensions != emitDimensions; _needsRenderUpdate |= changed; _particleProperties.emission.dimensions = emitDimensions; }); if (changed) { computeAndUpdateDimensions(); }} glm::vec3 ParticleEffectEntityItem::getEmitDimensions() const { return resultWithReadLock<glm::vec3>([&] { return _particleProperties.emission.dimensions; });} void ParticleEffectEntityItem::setEmitRadiusStart(float emitRadiusStart) { emitRadiusStart = glm::clamp(emitRadiusStart, MINIMUM_EMIT_RADIUS_START, MAXIMUM_EMIT_RADIUS_START); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.radiusStart != emitRadiusStart; _particleProperties.radiusStart = emitRadiusStart; });} float ParticleEffectEntityItem::getEmitRadiusStart() const { return resultWithReadLock<float>([&] { return _particleProperties.radiusStart; });} void ParticleEffectEntityItem::setPolarStart(float polarStart) { polarStart = glm::clamp(polarStart, MINIMUM_POLAR, MAXIMUM_POLAR); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.polar.start != polarStart; _particleProperties.polar.start = polarStart; });} float ParticleEffectEntityItem::getPolarStart() const { return resultWithReadLock<float>([&] { return _particleProperties.polar.start; });} void ParticleEffectEntityItem::setPolarFinish(float polarFinish) { polarFinish = glm::clamp(polarFinish, MINIMUM_POLAR, MAXIMUM_POLAR); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.polar.finish != polarFinish; _particleProperties.polar.finish = polarFinish; });} float ParticleEffectEntityItem::getPolarFinish() const { return resultWithReadLock<float>([&] { return _particleProperties.polar.finish; });} void ParticleEffectEntityItem::setAzimuthStart(float azimuthStart) { azimuthStart = glm::clamp(azimuthStart, MINIMUM_AZIMUTH, MAXIMUM_AZIMUTH); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.azimuth.start != azimuthStart; _particleProperties.azimuth.start = azimuthStart; });} float ParticleEffectEntityItem::getAzimuthStart() const { return resultWithReadLock<float>([&] { return _particleProperties.azimuth.start; });} void ParticleEffectEntityItem::setAzimuthFinish(float azimuthFinish) { azimuthFinish = glm::clamp(azimuthFinish, MINIMUM_AZIMUTH, MAXIMUM_AZIMUTH); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.azimuth.finish != azimuthFinish; _particleProperties.azimuth.finish = azimuthFinish; });} float ParticleEffectEntityItem::getAzimuthFinish() const { return resultWithReadLock<float>([&] { return _particleProperties.azimuth.finish; });} void ParticleEffectEntityItem::setEmitAcceleration(const glm::vec3& emitAcceleration_) { auto emitAcceleration = glm::clamp(emitAcceleration_, vec3(MINIMUM_EMIT_ACCELERATION), vec3(MAXIMUM_EMIT_ACCELERATION)); bool changed; withWriteLock([&] { changed = _particleProperties.emission.acceleration.target != emitAcceleration; _needsRenderUpdate |= changed; _particleProperties.emission.acceleration.target = emitAcceleration; }); if (changed) { computeAndUpdateDimensions(); }} glm::vec3 ParticleEffectEntityItem::getEmitAcceleration() const { return resultWithReadLock<glm::vec3>([&] { return _particleProperties.emission.acceleration.target; });} void ParticleEffectEntityItem::setAccelerationSpread(const glm::vec3& accelerationSpread_){ auto accelerationSpread = glm::clamp(accelerationSpread_, vec3(MINIMUM_ACCELERATION_SPREAD), vec3(MAXIMUM_ACCELERATION_SPREAD)); bool changed; withWriteLock([&] { changed = _particleProperties.emission.acceleration.spread != accelerationSpread; _needsRenderUpdate |= changed; _particleProperties.emission.acceleration.spread = accelerationSpread; }); if (changed) { computeAndUpdateDimensions(); }} glm::vec3 ParticleEffectEntityItem::getAccelerationSpread() const { return resultWithReadLock<glm::vec3>([&] { return _particleProperties.emission.acceleration.spread; });} void ParticleEffectEntityItem::setParticleRadius(float particleRadius) { particleRadius = glm::clamp(particleRadius, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS); bool changed; withWriteLock([&] { changed = _particleProperties.radius.gradient.target != particleRadius; _needsRenderUpdate |= changed; _particleProperties.radius.gradient.target = particleRadius; }); if (changed) { computeAndUpdateDimensions(); }} float ParticleEffectEntityItem::getParticleRadius() const { return resultWithReadLock<float>([&] { return _particleProperties.radius.gradient.target; });} void ParticleEffectEntityItem::setRadiusStart(float radiusStart) { radiusStart = glm::isnan(radiusStart) ? radiusStart : glm::clamp(radiusStart, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS); bool changed; withWriteLock([&] { changed = _particleProperties.radius.range.start != radiusStart; _needsRenderUpdate |= changed; _particleProperties.radius.range.start = radiusStart; }); if (changed) { computeAndUpdateDimensions(); }}Lines 1–500 of 881. Load more
// // ParticleEffectEntityItem.cpp // libraries/entities/src // // Some starter code for a particle simulation entity, which could ideally be used for a variety of effects. // This is some really early and rough stuff here. It was enough for now to just get it up and running in the interface. // // Todo's and other notes: // - The simulation should restart when the AnimationLoop's max frame is reached (or passed), but there doesn't seem // to be a good way to set that max frame to something reasonable right now. // - There seems to be a bug whereby entities on the edge of screen will just pop off or on. This is probably due // to my lack of understanding of how entities in the octree are picked for rendering. I am updating the entity // dimensions based on the bounds of the sim, but maybe I need to update a dirty flag or something. // - This should support some kind of pre-roll of the simulation. // - Just to get this out the door, I just did forward Euler integration. There are better ways. // - Gravity always points along the Y axis. Support an actual gravity vector. // - Add the ability to add arbitrary forces to the simulation. // - Add drag. // - Add some kind of support for collisions. // - There's no synchronization of the simulation across clients at all. In fact, it's using rand() under the hood, so // there's no gaurantee that different clients will see simulations that look anything like the other. // // Created by Jason Rickwald on 3/2/15. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "ParticleEffectEntityItem.h" #include <glm/gtx/transform.hpp> #include <QtCore/QJsonDocument> #include <ByteCountCoding.h> #include <GeometryUtil.h> #include <Interpolate.h> #include "EntityTree.h" #include "EntityTreeElement.h" #include "EntitiesLogging.h" #include "EntityScriptingInterface.h" using namespace particle; template <typename T> bool operator==(const Range<T>& a, const Range<T>& b) { return (a.start == b.start && a.finish == b.finish); } template <typename T> bool operator==(const Gradient<T>& a, const Gradient<T>& b) { return (a.target == b.target && a.spread == b.spread); } template <typename T> bool operator==(const RangeGradient<T>& a, const RangeGradient<T>& b) { return (a.gradient == b.gradient && a.range == b.range); } template <typename T> bool operator!=(const Range<T>& a, const Range<T>& b) { return !(a == b); } template <typename T> bool operator!=(const Gradient<T>& a, const Gradient<T>& b) { return !(a == b); } template <typename T> bool operator!=(const RangeGradient<T>& a, const RangeGradient<T>& b) { return !(a == b); } bool operator==(const EmitProperties& a, const EmitProperties& b) { return (a.rate == b.rate) && (a.speed == b.speed) && (a.acceleration == b.acceleration) && (a.orientation == b.orientation) && (a.dimensions == b.dimensions) && (a.shouldTrail == b.shouldTrail); } bool operator!=(const EmitProperties& a, const EmitProperties& b) { return !(a == b); } bool operator==(const Properties& a, const Properties& b) { return (a.color == b.color) && (a.alpha == b.alpha) && (a.radiusStart == b.radiusStart) && (a.radius == b.radius) && (a.spin == b.spin) && (a.rotateWithEntity == b.rotateWithEntity) && (a.lifespan == b.lifespan) && (a.maxParticles == b.maxParticles) && (a.emission == b.emission) && (a.polar == b.polar) && (a.azimuth == b.azimuth) && (a.textures == b.textures); } bool operator!=(const Properties& a, const Properties& b) { return !(a == b); } bool Properties::valid() const { if (glm::any(glm::isnan(emission.orientation))) { qCWarning(entities) << "Bad particle data"; return false; } return (alpha.gradient.target == glm::clamp(alpha.gradient.target, MINIMUM_ALPHA, MAXIMUM_ALPHA)) && (alpha.range.start == glm::clamp(alpha.range.start, MINIMUM_ALPHA, MAXIMUM_ALPHA)) && (alpha.range.finish == glm::clamp(alpha.range.finish, MINIMUM_ALPHA, MAXIMUM_ALPHA)) && (alpha.gradient.spread == glm::clamp(alpha.gradient.spread, MINIMUM_ALPHA, MAXIMUM_ALPHA)) && (radiusStart == glm::clamp(radiusStart, MINIMUM_EMIT_RADIUS_START, MAXIMUM_EMIT_RADIUS_START)) && (radius.gradient.target == glm::clamp(radius.gradient.target, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) && (radius.range.start == glm::clamp(radius.range.start, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) && (radius.range.finish == glm::clamp(radius.range.finish, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) && (radius.gradient.spread == glm::clamp(radius.gradient.spread, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS)) && (spin.gradient.target == glm::clamp(spin.gradient.target, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) && (spin.range.start == glm::clamp(spin.range.start, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) && (spin.range.finish == glm::clamp(spin.range.finish, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) && (spin.gradient.spread == glm::clamp(spin.gradient.spread, MINIMUM_PARTICLE_SPIN, MAXIMUM_PARTICLE_SPIN)) && (lifespan == glm::clamp(lifespan, MINIMUM_LIFESPAN, MAXIMUM_LIFESPAN)) && (maxParticles == glm::clamp(maxParticles, MINIMUM_MAX_PARTICLES, MAXIMUM_MAX_PARTICLES)) && (emission.rate == glm::clamp(emission.rate, MINIMUM_EMIT_RATE, MAXIMUM_EMIT_RATE)) && (emission.speed.target == glm::clamp(emission.speed.target, MINIMUM_EMIT_SPEED, MAXIMUM_EMIT_SPEED)) && (emission.speed.spread == glm::clamp(emission.speed.spread, MINIMUM_EMIT_SPEED, MAXIMUM_EMIT_SPEED)) && (emission.acceleration.target == glm::clamp(emission.acceleration.target, vec3(MINIMUM_EMIT_ACCELERATION), vec3(MAXIMUM_EMIT_ACCELERATION))) && (emission.acceleration.spread == glm::clamp(emission.acceleration.spread, vec3(MINIMUM_ACCELERATION_SPREAD), vec3(MAXIMUM_ACCELERATION_SPREAD)) && (emission.dimensions == glm::clamp(emission.dimensions, vec3(MINIMUM_EMIT_DIMENSION), vec3(MAXIMUM_EMIT_DIMENSION))) && (polar.start == glm::clamp(polar.start, MINIMUM_POLAR, MAXIMUM_POLAR)) && (polar.finish == glm::clamp(polar.finish, MINIMUM_POLAR, MAXIMUM_POLAR)) && (azimuth.start == glm::clamp(azimuth.start, MINIMUM_AZIMUTH, MAXIMUM_AZIMUTH)) && (azimuth.finish == glm::clamp(azimuth.finish, MINIMUM_AZIMUTH, MAXIMUM_AZIMUTH))); } bool Properties::emitting() const { return emission.rate > 0.0f && lifespan > 0.0f && polar.start <= polar.finish; } uint64_t Properties::emitIntervalUsecs() const { if (emission.rate > 0.0f) { return (uint64_t)(USECS_PER_SECOND / emission.rate); } return 0; } EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { std::shared_ptr<ParticleEffectEntityItem> entity(new ParticleEffectEntityItem(entityID), [](ParticleEffectEntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity; } // our non-pure virtual subclass for now... ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { _type = EntityTypes::ParticleEffect; _visuallyReady = false; } void ParticleEffectEntityItem::setAlpha(float alpha) { alpha = glm::clamp(alpha, MINIMUM_ALPHA, MAXIMUM_ALPHA); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.alpha.gradient.target != alpha; _particleProperties.alpha.gradient.target = alpha; }); } float ParticleEffectEntityItem::getAlpha() const { return resultWithReadLock<float>([&] { return _particleProperties.alpha.gradient.target; }); } void ParticleEffectEntityItem::setAlphaStart(float alphaStart) { alphaStart = glm::isnan(alphaStart) ? alphaStart : glm::clamp(alphaStart, MINIMUM_ALPHA, MAXIMUM_ALPHA); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.alpha.range.start != alphaStart; _particleProperties.alpha.range.start = alphaStart; }); } float ParticleEffectEntityItem::getAlphaStart() const { return resultWithReadLock<float>([&] { return _particleProperties.alpha.range.start; }); } void ParticleEffectEntityItem::setAlphaFinish(float alphaFinish) { alphaFinish = glm::isnan(alphaFinish) ? alphaFinish : glm::clamp(alphaFinish, MINIMUM_ALPHA, MAXIMUM_ALPHA); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.alpha.range.finish != alphaFinish; _particleProperties.alpha.range.finish = alphaFinish; }); } float ParticleEffectEntityItem::getAlphaFinish() const { return resultWithReadLock<float>([&] { return _particleProperties.alpha.range.finish; }); } void ParticleEffectEntityItem::setAlphaSpread(float alphaSpread) { alphaSpread = glm::clamp(alphaSpread, MINIMUM_ALPHA, MAXIMUM_ALPHA); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.alpha.gradient.spread != alphaSpread; _particleProperties.alpha.gradient.spread = alphaSpread; }); } float ParticleEffectEntityItem::getAlphaSpread() const { return resultWithReadLock<float>([&] { return _particleProperties.alpha.gradient.spread; }); } void ParticleEffectEntityItem::setLifespan(float lifespan) { lifespan = glm::clamp(lifespan, MINIMUM_LIFESPAN, MAXIMUM_LIFESPAN); bool changed; withWriteLock([&] { changed = _particleProperties.lifespan != lifespan; _needsRenderUpdate |= changed; _particleProperties.lifespan = lifespan; }); if (changed) { computeAndUpdateDimensions(); } } float ParticleEffectEntityItem::getLifespan() const { return resultWithReadLock<float>([&] { return _particleProperties.lifespan; }); } void ParticleEffectEntityItem::setEmitRate(float emitRate) { emitRate = glm::clamp(emitRate, MINIMUM_EMIT_RATE, MAXIMUM_EMIT_RATE); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.emission.rate != emitRate; _particleProperties.emission.rate = emitRate; }); } float ParticleEffectEntityItem::getEmitRate() const { return resultWithReadLock<float>([&] { return _particleProperties.emission.rate; }); } void ParticleEffectEntityItem::setEmitSpeed(float emitSpeed) { emitSpeed = glm::clamp(emitSpeed, MINIMUM_EMIT_SPEED, MAXIMUM_EMIT_SPEED); bool changed; withWriteLock([&] { changed = _particleProperties.emission.speed.target != emitSpeed; _needsRenderUpdate |= changed; _particleProperties.emission.speed.target = emitSpeed; }); if (changed) { computeAndUpdateDimensions(); } } float ParticleEffectEntityItem::getEmitSpeed() const { return resultWithReadLock<float>([&] { return _particleProperties.emission.speed.target; }); } void ParticleEffectEntityItem::setSpeedSpread(float speedSpread) { speedSpread = glm::clamp(speedSpread, MINIMUM_EMIT_SPEED, MAXIMUM_EMIT_SPEED); bool changed; withWriteLock([&] { changed = _particleProperties.emission.speed.spread != speedSpread; _needsRenderUpdate |= changed; _particleProperties.emission.speed.spread = speedSpread; }); if (changed) { computeAndUpdateDimensions(); } } float ParticleEffectEntityItem::getSpeedSpread() const { return resultWithReadLock<float>([&] { return _particleProperties.emission.speed.spread; }); } void ParticleEffectEntityItem::setEmitOrientation(const glm::quat& emitOrientation_) { auto emitOrientation = glm::normalize(emitOrientation_); bool changed; withWriteLock([&] { changed = _particleProperties.emission.orientation != emitOrientation; _needsRenderUpdate |= changed; _particleProperties.emission.orientation = emitOrientation; }); if (changed) { computeAndUpdateDimensions(); } } glm::quat ParticleEffectEntityItem::getEmitOrientation() const { return resultWithReadLock<glm::quat>([&] { return _particleProperties.emission.orientation; }); } void ParticleEffectEntityItem::setEmitDimensions(const glm::vec3& emitDimensions_) { auto emitDimensions = glm::clamp(emitDimensions_, vec3(MINIMUM_EMIT_DIMENSION), vec3(MAXIMUM_EMIT_DIMENSION)); bool changed; withWriteLock([&] { changed = _particleProperties.emission.dimensions != emitDimensions; _needsRenderUpdate |= changed; _particleProperties.emission.dimensions = emitDimensions; }); if (changed) { computeAndUpdateDimensions(); } } glm::vec3 ParticleEffectEntityItem::getEmitDimensions() const { return resultWithReadLock<glm::vec3>([&] { return _particleProperties.emission.dimensions; }); } void ParticleEffectEntityItem::setEmitRadiusStart(float emitRadiusStart) { emitRadiusStart = glm::clamp(emitRadiusStart, MINIMUM_EMIT_RADIUS_START, MAXIMUM_EMIT_RADIUS_START); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.radiusStart != emitRadiusStart; _particleProperties.radiusStart = emitRadiusStart; }); } float ParticleEffectEntityItem::getEmitRadiusStart() const { return resultWithReadLock<float>([&] { return _particleProperties.radiusStart; }); } void ParticleEffectEntityItem::setPolarStart(float polarStart) { polarStart = glm::clamp(polarStart, MINIMUM_POLAR, MAXIMUM_POLAR); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.polar.start != polarStart; _particleProperties.polar.start = polarStart; }); } float ParticleEffectEntityItem::getPolarStart() const { return resultWithReadLock<float>([&] { return _particleProperties.polar.start; }); } void ParticleEffectEntityItem::setPolarFinish(float polarFinish) { polarFinish = glm::clamp(polarFinish, MINIMUM_POLAR, MAXIMUM_POLAR); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.polar.finish != polarFinish; _particleProperties.polar.finish = polarFinish; }); } float ParticleEffectEntityItem::getPolarFinish() const { return resultWithReadLock<float>([&] { return _particleProperties.polar.finish; }); } void ParticleEffectEntityItem::setAzimuthStart(float azimuthStart) { azimuthStart = glm::clamp(azimuthStart, MINIMUM_AZIMUTH, MAXIMUM_AZIMUTH); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.azimuth.start != azimuthStart; _particleProperties.azimuth.start = azimuthStart; }); } float ParticleEffectEntityItem::getAzimuthStart() const { return resultWithReadLock<float>([&] { return _particleProperties.azimuth.start; }); } void ParticleEffectEntityItem::setAzimuthFinish(float azimuthFinish) { azimuthFinish = glm::clamp(azimuthFinish, MINIMUM_AZIMUTH, MAXIMUM_AZIMUTH); withWriteLock([&] { _needsRenderUpdate |= _particleProperties.azimuth.finish != azimuthFinish; _particleProperties.azimuth.finish = azimuthFinish; }); } float ParticleEffectEntityItem::getAzimuthFinish() const { return resultWithReadLock<float>([&] { return _particleProperties.azimuth.finish; }); } void ParticleEffectEntityItem::setEmitAcceleration(const glm::vec3& emitAcceleration_) { auto emitAcceleration = glm::clamp(emitAcceleration_, vec3(MINIMUM_EMIT_ACCELERATION), vec3(MAXIMUM_EMIT_ACCELERATION)); bool changed; withWriteLock([&] { changed = _particleProperties.emission.acceleration.target != emitAcceleration; _needsRenderUpdate |= changed; _particleProperties.emission.acceleration.target = emitAcceleration; }); if (changed) { computeAndUpdateDimensions(); } } glm::vec3 ParticleEffectEntityItem::getEmitAcceleration() const { return resultWithReadLock<glm::vec3>([&] { return _particleProperties.emission.acceleration.target; }); } void ParticleEffectEntityItem::setAccelerationSpread(const glm::vec3& accelerationSpread_){ auto accelerationSpread = glm::clamp(accelerationSpread_, vec3(MINIMUM_ACCELERATION_SPREAD), vec3(MAXIMUM_ACCELERATION_SPREAD)); bool changed; withWriteLock([&] { changed = _particleProperties.emission.acceleration.spread != accelerationSpread; _needsRenderUpdate |= changed; _particleProperties.emission.acceleration.spread = accelerationSpread; }); if (changed) { computeAndUpdateDimensions(); } } glm::vec3 ParticleEffectEntityItem::getAccelerationSpread() const { return resultWithReadLock<glm::vec3>([&] { return _particleProperties.emission.acceleration.spread; }); } void ParticleEffectEntityItem::setParticleRadius(float particleRadius) { particleRadius = glm::clamp(particleRadius, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS); bool changed; withWriteLock([&] { changed = _particleProperties.radius.gradient.target != particleRadius; _needsRenderUpdate |= changed; _particleProperties.radius.gradient.target = particleRadius; }); if (changed) { computeAndUpdateDimensions(); } } float ParticleEffectEntityItem::getParticleRadius() const { return resultWithReadLock<float>([&] { return _particleProperties.radius.gradient.target; }); } void ParticleEffectEntityItem::setRadiusStart(float radiusStart) { radiusStart = glm::isnan(radiusStart) ? radiusStart : glm::clamp(radiusStart, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS); bool changed; withWriteLock([&] { changed = _particleProperties.radius.range.start != radiusStart; _needsRenderUpdate |= changed; _particleProperties.radius.range.start = radiusStart; }); if (changed) { computeAndUpdateDimensions(); } }