881 lines · 30.9 KB · Text
RawDownload
////  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.