mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 11:45:36 +02:00
High Fidelity interview project -- Jason Rickwald
For my project, I decided to add a new entity -- a Particle Effect. This is really rudimentary to start out with, but you could see where it's headed.
This commit is contained in:
parent
cfa6352c3a
commit
3522357c8c
9 changed files with 908 additions and 2 deletions
|
@ -32,6 +32,7 @@
|
|||
#include "RenderableModelEntityItem.h"
|
||||
#include "RenderableSphereEntityItem.h"
|
||||
#include "RenderableTextEntityItem.h"
|
||||
#include "RenderableParticleEffectEntityItem.h"
|
||||
|
||||
|
||||
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
||||
|
@ -53,6 +54,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
|||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::factory)
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory)
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Text, RenderableTextEntityItem::factory)
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(ParticleEffect, RenderableParticleEffectEntityItem::factory)
|
||||
|
||||
_currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
|
||||
_currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// RenderableParticleEffectEntityItem.cpp
|
||||
// interface/src
|
||||
//
|
||||
// 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 <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <gpu/GPUConfig.h>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "RenderableParticleEffectEntityItem.h"
|
||||
|
||||
EntityItem* RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new RenderableParticleEffectEntityItem(entityID, properties);
|
||||
}
|
||||
|
||||
void RenderableParticleEffectEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableParticleEffectEntityItem::render");
|
||||
assert(getType() == EntityTypes::ParticleEffect);
|
||||
glm::vec3 position = getPositionInMeters();
|
||||
glm::vec3 center = getCenterInMeters();
|
||||
glm::quat rotation = getRotation();
|
||||
float pa_rad = getParticleRadius();
|
||||
|
||||
const float MAX_COLOR = 255.0f;
|
||||
glm::vec4 sphereColor(getColor()[RED_INDEX] / MAX_COLOR, getColor()[GREEN_INDEX] / MAX_COLOR,
|
||||
getColor()[BLUE_INDEX] / MAX_COLOR, getLocalRenderAlpha());
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
|
||||
const int SLICES = 8;
|
||||
const int STACKS = 5;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glColor4f(sphereColor.r, sphereColor.g, sphereColor.b, sphereColor.a);
|
||||
|
||||
// Right now we're just iterating over particles and rendering as a cross of four quads.
|
||||
// This is pretty dumb, it was quick enough to code up. Really, there should be many
|
||||
// rendering modes, including the all-important textured billboards.
|
||||
|
||||
quint32 paiter = pa_head;
|
||||
while (pa_life[paiter] > 0.0f)
|
||||
{
|
||||
int j = paiter * 3;
|
||||
|
||||
glVertex3f(pa_position[j] - pa_rad, pa_position[j + 1] + pa_rad, pa_position[j + 2]);
|
||||
glVertex3f(pa_position[j] + pa_rad, pa_position[j + 1] + pa_rad, pa_position[j + 2]);
|
||||
glVertex3f(pa_position[j] + pa_rad, pa_position[j + 1] - pa_rad, pa_position[j + 2]);
|
||||
glVertex3f(pa_position[j] - pa_rad, pa_position[j + 1] - pa_rad, pa_position[j + 2]);
|
||||
|
||||
glVertex3f(pa_position[j] + pa_rad, pa_position[j + 1] + pa_rad, pa_position[j + 2]);
|
||||
glVertex3f(pa_position[j] - pa_rad, pa_position[j + 1] + pa_rad, pa_position[j + 2]);
|
||||
glVertex3f(pa_position[j] - pa_rad, pa_position[j + 1] - pa_rad, pa_position[j + 2]);
|
||||
glVertex3f(pa_position[j] + pa_rad, pa_position[j + 1] - pa_rad, pa_position[j + 2]);
|
||||
|
||||
glVertex3f(pa_position[j], pa_position[j + 1] + pa_rad, pa_position[j + 2] - pa_rad);
|
||||
glVertex3f(pa_position[j], pa_position[j + 1] + pa_rad, pa_position[j + 2] + pa_rad);
|
||||
glVertex3f(pa_position[j], pa_position[j + 1] - pa_rad, pa_position[j + 2] + pa_rad);
|
||||
glVertex3f(pa_position[j], pa_position[j + 1] - pa_rad, pa_position[j + 2] - pa_rad);
|
||||
|
||||
glVertex3f(pa_position[j], pa_position[j + 1] + pa_rad, pa_position[j + 2] + pa_rad);
|
||||
glVertex3f(pa_position[j], pa_position[j + 1] + pa_rad, pa_position[j + 2] - pa_rad);
|
||||
glVertex3f(pa_position[j], pa_position[j + 1] - pa_rad, pa_position[j + 2] - pa_rad);
|
||||
glVertex3f(pa_position[j], pa_position[j + 1] - pa_rad, pa_position[j + 2] + pa_rad);
|
||||
|
||||
paiter = (paiter + 1) % _maxParticles;
|
||||
}
|
||||
|
||||
glEnd();
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// RenderableParticleEffectEntityItem.h
|
||||
// interface/src/entities
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_RenderableParticleEffectEntityItem_h
|
||||
#define hifi_RenderableParticleEffectEntityItem_h
|
||||
|
||||
#include <ParticleEffectEntityItem.h>
|
||||
|
||||
class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
ParticleEffectEntityItem(entityItemID, properties)
|
||||
{ }
|
||||
|
||||
virtual void render(RenderArgs* args);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_RenderableParticleEffectEntityItem_h
|
|
@ -23,6 +23,7 @@
|
|||
#include "EntityItemPropertiesDefaults.h"
|
||||
#include "ModelEntityItem.h"
|
||||
#include "TextEntityItem.h"
|
||||
#include "ParticleEffectEntityItem.h"
|
||||
|
||||
|
||||
EntityItemProperties::EntityItemProperties() :
|
||||
|
@ -66,6 +67,13 @@ EntityItemProperties::EntityItemProperties() :
|
|||
CONSTRUCT_PROPERTY(textColor, TextEntityItem::DEFAULT_TEXT_COLOR),
|
||||
CONSTRUCT_PROPERTY(backgroundColor, TextEntityItem::DEFAULT_BACKGROUND_COLOR),
|
||||
CONSTRUCT_PROPERTY(shapeType, SHAPE_TYPE_NONE),
|
||||
CONSTRUCT_PROPERTY(maxParticles, ParticleEffectEntityItem::DEFAULT_MAX_PARTICLES),
|
||||
CONSTRUCT_PROPERTY(lifespan, ParticleEffectEntityItem::DEFAULT_LIFESPAN),
|
||||
CONSTRUCT_PROPERTY(emitRate, ParticleEffectEntityItem::DEFAULT_EMIT_RATE),
|
||||
CONSTRUCT_PROPERTY(emitDirection, ParticleEffectEntityItem::DEFAULT_EMIT_DIRECTION),
|
||||
CONSTRUCT_PROPERTY(emitStrength, ParticleEffectEntityItem::DEFAULT_EMIT_STRENGTH),
|
||||
CONSTRUCT_PROPERTY(localGravity, ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY),
|
||||
CONSTRUCT_PROPERTY(particleRadius, ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS),
|
||||
|
||||
_id(UNKNOWN_ENTITY_ID),
|
||||
_idSet(false),
|
||||
|
@ -238,6 +246,13 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_TEXT_COLOR, textColor);
|
||||
CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_COLOR, backgroundColor);
|
||||
CHECK_PROPERTY_CHANGE(PROP_SHAPE_TYPE, shapeType);
|
||||
CHECK_PROPERTY_CHANGE(PROP_MAX_PARTICLES, maxParticles);
|
||||
CHECK_PROPERTY_CHANGE(PROP_LIFESPAN, lifespan);
|
||||
CHECK_PROPERTY_CHANGE(PROP_EMIT_RATE, emitRate);
|
||||
CHECK_PROPERTY_CHANGE(PROP_EMIT_DIRECTION, emitDirection);
|
||||
CHECK_PROPERTY_CHANGE(PROP_EMIT_STRENGTH, emitStrength);
|
||||
CHECK_PROPERTY_CHANGE(PROP_LOCAL_GRAVITY, localGravity);
|
||||
CHECK_PROPERTY_CHANGE(PROP_PARTICLE_RADIUS, particleRadius);
|
||||
|
||||
return changedProperties;
|
||||
}
|
||||
|
@ -297,6 +312,13 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(textColor, getTextColor());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(backgroundColor, getBackgroundColor());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(shapeType, getShapeTypeAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(maxParticles);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(lifespan);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(emitRate);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(emitDirection);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(emitStrength);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(localGravity);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(particleRadius);
|
||||
|
||||
// Sitting properties support
|
||||
QScriptValue sittingPoints = engine->newObject();
|
||||
|
@ -375,6 +397,13 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(textColor, setTextColor);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(backgroundColor, setBackgroundColor);
|
||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(shapeType, ShapeType);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(maxParticles, setMaxParticles);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(lifespan, setLifespan);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(emitRate, setEmitRate);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(emitDirection, setEmitDirection);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(emitStrength, setEmitStrength);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(localGravity, setLocalGravity);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(particleRadius, setParticleRadius);
|
||||
|
||||
_lastEdited = usecTimestampNow();
|
||||
}
|
||||
|
@ -552,6 +581,16 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
APPEND_ENTITY_PROPERTY(PROP_EXPONENT, appendValue, properties.getExponent());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CUTOFF, appendValue, properties.getCutoff());
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::ParticleEffect) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, appendValue, properties.getMaxParticles());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LIFESPAN, appendValue, properties.getLifespan());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_RATE, appendValue, properties.getEmitRate());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_DIRECTION, appendValue, properties.getEmitDirection());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, appendValue, properties.getEmitStrength());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, appendValue, properties.getLocalGravity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, appendValue, properties.getParticleRadius());
|
||||
}
|
||||
}
|
||||
if (propertyCount > 0) {
|
||||
int endOfEntityItemData = packetData->getUncompressedByteOffset();
|
||||
|
@ -774,6 +813,16 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EXPONENT, float, setExponent);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CUTOFF, float, setCutoff);
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::ParticleEffect) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_PARTICLES, float, setMaxParticles);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFESPAN, float, setLifespan);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_RATE, float, setEmitRate);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_DIRECTION, glm::vec3, setEmitDirection);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_STRENGTH, float, setEmitStrength);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCAL_GRAVITY, float, setLocalGravity);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_RADIUS, float, setParticleRadius);
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
@ -851,6 +900,14 @@ void EntityItemProperties::markAllChanged() {
|
|||
_textColorChanged = true;
|
||||
_backgroundColorChanged = true;
|
||||
_shapeTypeChanged = true;
|
||||
|
||||
_maxParticlesChanged = true;
|
||||
_lifespanChanged = true;
|
||||
_emitRateChanged = true;
|
||||
_emitDirectionChanged = true;
|
||||
_emitStrengthChanged = true;
|
||||
_localGravityChanged = true;
|
||||
_particleRadiusChanged = true;
|
||||
}
|
||||
|
||||
AACube EntityItemProperties::getMaximumAACubeInTreeUnits() const {
|
||||
|
|
|
@ -83,9 +83,18 @@ enum EntityPropertyList {
|
|||
PROP_ANIMATION_SETTINGS,
|
||||
PROP_USER_DATA,
|
||||
PROP_SHAPE_TYPE,
|
||||
|
||||
// used by ParticleEffect entities
|
||||
PROP_MAX_PARTICLES,
|
||||
PROP_LIFESPAN,
|
||||
PROP_EMIT_RATE,
|
||||
PROP_EMIT_DIRECTION,
|
||||
PROP_EMIT_STRENGTH,
|
||||
PROP_LOCAL_GRAVITY,
|
||||
PROP_PARTICLE_RADIUS,
|
||||
|
||||
// NOTE: add new properties ABOVE this line and then modify PROP_LAST_ITEM below
|
||||
PROP_LAST_ITEM = PROP_SHAPE_TYPE,
|
||||
PROP_LAST_ITEM = PROP_PARTICLE_RADIUS,
|
||||
|
||||
// These properties of TextEntity piggy back off of properties of ModelEntities, the type doesn't matter
|
||||
// since the derived class knows how to interpret it's own properties and knows the types it expects
|
||||
|
@ -110,6 +119,7 @@ class EntityItemProperties {
|
|||
friend class SphereEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||
friend class LightEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||
friend class TextEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||
friend class ParticleEffectEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||
public:
|
||||
EntityItemProperties();
|
||||
virtual ~EntityItemProperties();
|
||||
|
@ -182,6 +192,13 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, xColor);
|
||||
DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, xColor);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_SHAPE_TYPE, ShapeType, shapeType, ShapeType);
|
||||
DEFINE_PROPERTY(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32);
|
||||
DEFINE_PROPERTY(PROP_LIFESPAN, Lifespan, lifespan, float);
|
||||
DEFINE_PROPERTY(PROP_EMIT_RATE, EmitRate, emitRate, float);
|
||||
DEFINE_PROPERTY_REF(PROP_EMIT_DIRECTION, EmitDirection, emitDirection, glm::vec3);
|
||||
DEFINE_PROPERTY(PROP_EMIT_STRENGTH, EmitStrength, emitStrength, float);
|
||||
DEFINE_PROPERTY(PROP_LOCAL_GRAVITY, LocalGravity, localGravity, float);
|
||||
DEFINE_PROPERTY(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float);
|
||||
|
||||
public:
|
||||
float getMaxDimension() const { return glm::max(_dimensions.x, _dimensions.y, _dimensions.z); }
|
||||
|
@ -304,6 +321,13 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
|||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, TextColor, textColor, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundColor, backgroundColor, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ShapeType, shapeType, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, MaxParticles, maxParticles, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Lifespan, lifespan, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitRate, emitRate, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitDirection, emitDirection, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitStrength, emitStrength, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, LocalGravity, localGravity, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParticleRadius, particleRadius, "");
|
||||
|
||||
debug << " last edited:" << properties.getLastEdited() << "\n";
|
||||
debug << " edited ago:" << properties.getEditedAgo() << "\n";
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "ModelEntityItem.h"
|
||||
#include "SphereEntityItem.h"
|
||||
#include "TextEntityItem.h"
|
||||
#include "ParticleEffectEntityItem.h"
|
||||
|
||||
QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap;
|
||||
QMap<QString, EntityTypes::EntityType> EntityTypes::_nameToTypeMap;
|
||||
|
@ -37,6 +38,7 @@ REGISTER_ENTITY_TYPE(Box)
|
|||
REGISTER_ENTITY_TYPE(Sphere)
|
||||
REGISTER_ENTITY_TYPE(Light)
|
||||
REGISTER_ENTITY_TYPE(Text)
|
||||
REGISTER_ENTITY_TYPE(ParticleEffect)
|
||||
|
||||
|
||||
const QString& EntityTypes::getEntityTypeName(EntityType entityType) {
|
||||
|
|
|
@ -35,7 +35,8 @@ public:
|
|||
Sphere,
|
||||
Light,
|
||||
Text,
|
||||
LAST = Text
|
||||
ParticleEffect,
|
||||
LAST = ParticleEffect
|
||||
} EntityType;
|
||||
|
||||
static const QString& getEntityTypeName(EntityType entityType);
|
||||
|
|
522
libraries/entities/src/ParticleEffectEntityItem.cpp
Normal file
522
libraries/entities/src/ParticleEffectEntityItem.cpp
Normal file
|
@ -0,0 +1,522 @@
|
|||
//
|
||||
// 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 controls for spread (which is currently hard-coded) and varying emission strength (not currently implemented).
|
||||
// - Add drag.
|
||||
// - Add some kind of support for collisions.
|
||||
// - For simplicity, I'm currently just rendering each particle as a cross of four axis-aligned quads. Really, we'd
|
||||
// want multiple render modes, including (the most important) textured billboards (always facing camera). Also, these
|
||||
// should support animated textures.
|
||||
// - 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.
|
||||
// - MORE?
|
||||
//
|
||||
// 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 <glm/gtx/transform.hpp>
|
||||
#include <QtCore/QJsonDocument>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <ByteCountCoding.h>
|
||||
#include <GeometryUtil.h>
|
||||
|
||||
#include "EntityTree.h"
|
||||
#include "EntityTreeElement.h"
|
||||
#include "ParticleEffectEntityItem.h"
|
||||
|
||||
const float ParticleEffectEntityItem::DEFAULT_ANIMATION_FRAME_INDEX = 0.0f;
|
||||
const bool ParticleEffectEntityItem::DEFAULT_ANIMATION_IS_PLAYING = false;
|
||||
const float ParticleEffectEntityItem::DEFAULT_ANIMATION_FPS = 30.0f;
|
||||
const quint32 ParticleEffectEntityItem::DEFAULT_MAX_PARTICLES = 1000;
|
||||
const float ParticleEffectEntityItem::DEFAULT_LIFESPAN = 3.0f;
|
||||
const float ParticleEffectEntityItem::DEFAULT_EMIT_RATE = 15.0f;
|
||||
const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_DIRECTION(0.0f, 1.0f, 0.0f);
|
||||
const float ParticleEffectEntityItem::DEFAULT_EMIT_STRENGTH = 25.0f;
|
||||
const float ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY = -9.8f;
|
||||
const float ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS = 0.025f;
|
||||
|
||||
|
||||
EntityItem* ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new ParticleEffectEntityItem(entityID, properties);
|
||||
}
|
||||
|
||||
// our non-pure virtual subclass for now...
|
||||
ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
EntityItem(entityItemID, properties)
|
||||
{
|
||||
_type = EntityTypes::ParticleEffect;
|
||||
_maxParticles = DEFAULT_MAX_PARTICLES;
|
||||
_lifespan = DEFAULT_LIFESPAN;
|
||||
_emitRate = DEFAULT_EMIT_RATE;
|
||||
_emitDirection = DEFAULT_EMIT_DIRECTION;
|
||||
_emitStrength = DEFAULT_EMIT_STRENGTH;
|
||||
_localGravity = DEFAULT_LOCAL_GRAVITY;
|
||||
_particleRadius = DEFAULT_PARTICLE_RADIUS;
|
||||
setProperties(properties);
|
||||
// this is a pretty dumb thing to do, and it should probably be changed to use a more dynamic
|
||||
// data structure in the future. I'm just trying to get some code out the door for now (and it's
|
||||
// at least time efficient (though not space efficient).
|
||||
// Also, this being a real-time application, it's doubtful we'll ever have millions of particles
|
||||
// to keep track of, so this really isn't all that bad.
|
||||
pa_life = (float*) malloc(sizeof(float) * _maxParticles);
|
||||
pa_position = (float*)malloc(sizeof(float) * _maxParticles * 3); // x,y,z
|
||||
pa_velocity = (float*)malloc(sizeof(float) * _maxParticles * 3); // x,y,z
|
||||
pa_xmax = pa_ymax = pa_zmax = 1.0f;
|
||||
pa_xmin = pa_ymin = pa_zmin = -1.0f;
|
||||
resetSim();
|
||||
_lastAnimated = usecTimestampNow();
|
||||
}
|
||||
|
||||
ParticleEffectEntityItem::~ParticleEffectEntityItem()
|
||||
{
|
||||
free(pa_life);
|
||||
free(pa_position);
|
||||
free(pa_velocity);
|
||||
}
|
||||
|
||||
EntityItemProperties ParticleEffectEntityItem::getProperties() const {
|
||||
EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationIsPlaying, getAnimationIsPlaying);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFrameIndex, getAnimationFrameIndex);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFPS, getAnimationFPS);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(glowLevel, getGlowLevel);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationSettings, getAnimationSettings);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxParticles, getMaxParticles);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifespan, getLifespan);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitRate, getEmitRate);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitDirection, getEmitDirection);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitStrength, getEmitStrength);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(localGravity, getLocalGravity);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleRadius, getParticleRadius);
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationIsPlaying, setAnimationIsPlaying);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFrameIndex, setAnimationFrameIndex);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFPS, setAnimationFPS);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationSettings, setAnimationSettings);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, updateShapeType);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxParticles, setMaxParticles);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifespan, setLifespan);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitRate, setEmitRate);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitDirection, setEmitDirection);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitStrength, setEmitStrength);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(localGravity, setLocalGravity);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleRadius, setParticleRadius);
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
int elapsed = now - getLastEdited();
|
||||
qDebug() << "ParticleEffectEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
|
||||
"now=" << now << " getLastEdited()=" << getLastEdited();
|
||||
}
|
||||
setLastEdited(properties.getLastEdited());
|
||||
}
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {
|
||||
|
||||
int bytesRead = 0;
|
||||
const unsigned char* dataAt = data;
|
||||
|
||||
READ_ENTITY_PROPERTY_COLOR(PROP_COLOR, _color);
|
||||
|
||||
// Because we're using AnimationLoop which will reset the frame index if you change it's running state
|
||||
// we want to read these values in the order they appear in the buffer, but call our setters in an
|
||||
// order that allows AnimationLoop to preserve the correct frame rate.
|
||||
float animationFPS = getAnimationFPS();
|
||||
float animationFrameIndex = getAnimationFrameIndex();
|
||||
bool animationIsPlaying = getAnimationIsPlaying();
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, animationFPS);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, animationFrameIndex);
|
||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, animationIsPlaying);
|
||||
|
||||
if (propertyFlags.getHasProperty(PROP_ANIMATION_PLAYING)) {
|
||||
if (animationIsPlaying != getAnimationIsPlaying()) {
|
||||
setAnimationIsPlaying(animationIsPlaying);
|
||||
}
|
||||
}
|
||||
if (propertyFlags.getHasProperty(PROP_ANIMATION_FPS)) {
|
||||
setAnimationFPS(animationFPS);
|
||||
}
|
||||
if (propertyFlags.getHasProperty(PROP_ANIMATION_FRAME_INDEX)) {
|
||||
setAnimationFrameIndex(animationFrameIndex);
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY_STRING(PROP_ANIMATION_SETTINGS, setAnimationSettings);
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_SHAPE_TYPE, ShapeType, updateShapeType);
|
||||
READ_ENTITY_PROPERTY(PROP_MAX_PARTICLES, quint32, _maxParticles);
|
||||
READ_ENTITY_PROPERTY(PROP_LIFESPAN, float, _lifespan);
|
||||
READ_ENTITY_PROPERTY(PROP_EMIT_RATE, float, _emitRate);
|
||||
READ_ENTITY_PROPERTY_SETTER(PROP_EMIT_DIRECTION, glm::vec3, setEmitDirection);
|
||||
READ_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, float, _emitStrength);
|
||||
READ_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, float, _localGravity);
|
||||
READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, _particleRadius);
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
||||
EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
|
||||
requestedProperties += PROP_COLOR;
|
||||
requestedProperties += PROP_ANIMATION_FPS;
|
||||
requestedProperties += PROP_ANIMATION_FRAME_INDEX;
|
||||
requestedProperties += PROP_ANIMATION_PLAYING;
|
||||
requestedProperties += PROP_ANIMATION_SETTINGS;
|
||||
requestedProperties += PROP_SHAPE_TYPE;
|
||||
requestedProperties += PROP_MAX_PARTICLES;
|
||||
requestedProperties += PROP_LIFESPAN;
|
||||
requestedProperties += PROP_EMIT_RATE;
|
||||
requestedProperties += PROP_EMIT_DIRECTION;
|
||||
requestedProperties += PROP_EMIT_STRENGTH;
|
||||
requestedProperties += PROP_LOCAL_GRAVITY;
|
||||
requestedProperties += PROP_PARTICLE_RADIUS;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const {
|
||||
|
||||
bool successPropertyFits = true;
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, getAnimationFPS());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, getAnimationFrameIndex());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, appendValue, getAnimationIsPlaying());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, appendValue, getAnimationSettings());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, appendValue, (uint32_t)getShapeType());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, appendValue, getMaxParticles());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LIFESPAN, appendValue, getLifespan());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_RATE, appendValue, getEmitRate());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_DIRECTION, appendValue, getEmitDirection());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, appendValue, getEmitStrength());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, appendValue, getLocalGravity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, appendValue, getParticleRadius());
|
||||
}
|
||||
|
||||
bool ParticleEffectEntityItem::isAnimatingSomething() const {
|
||||
return getAnimationIsPlaying() &&
|
||||
getAnimationFPS() != 0.0f;
|
||||
}
|
||||
|
||||
bool ParticleEffectEntityItem::needsToCallUpdate() const {
|
||||
return isAnimatingSomething() ? true : EntityItem::needsToCallUpdate();
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::update(const quint64& now) {
|
||||
// only advance the frame index if we're playing
|
||||
if (getAnimationIsPlaying()) {
|
||||
float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND;
|
||||
_lastAnimated = now;
|
||||
float lastFrame = _animationLoop.getFrameIndex();
|
||||
_animationLoop.simulate(deltaTime);
|
||||
float curFrame = _animationLoop.getFrameIndex();
|
||||
if (curFrame > lastFrame)
|
||||
{
|
||||
stepSim(deltaTime);
|
||||
}
|
||||
else if (curFrame < lastFrame)
|
||||
{
|
||||
// we looped around, so restart the sim and only sim up to the point
|
||||
// since the beginning of the frame range.
|
||||
resetSim();
|
||||
stepSim((curFrame - _animationLoop.getFirstFrame()) / _animationLoop.getFPS());
|
||||
}
|
||||
}
|
||||
else {
|
||||
_lastAnimated = now;
|
||||
}
|
||||
|
||||
// update the dimensions
|
||||
glm::vec3 dims;
|
||||
dims.x = glm::max(glm::abs(pa_xmin), glm::abs(pa_xmax));
|
||||
dims.y = glm::max(glm::abs(pa_xmin), glm::abs(pa_xmax));
|
||||
dims.z = glm::max(glm::abs(pa_xmin), glm::abs(pa_xmax));
|
||||
setDimensionsInMeters(dims);
|
||||
|
||||
EntityItem::update(now); // let our base class handle it's updates...
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::debugDump() const {
|
||||
quint64 now = usecTimestampNow();
|
||||
qDebug() << "PA EFFECT EntityItem id:" << getEntityItemID() << "---------------------------------------------";
|
||||
qDebug() << " color:" << _color[0] << "," << _color[1] << "," << _color[2];
|
||||
qDebug() << " position:" << debugTreeVector(_position);
|
||||
qDebug() << " dimensions:" << debugTreeVector(_dimensions);
|
||||
qDebug() << " getLastEdited:" << debugTime(getLastEdited(), now);
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::updateShapeType(ShapeType type) {
|
||||
if (type != _shapeType) {
|
||||
_shapeType = type;
|
||||
_dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS;
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setAnimationFrameIndex(float value) {
|
||||
#ifdef WANT_DEBUG
|
||||
if (isAnimatingSomething()) {
|
||||
qDebug() << "ParticleEffectEntityItem::setAnimationFrameIndex()";
|
||||
qDebug() << " value:" << value;
|
||||
qDebug() << " was:" << _animationLoop.getFrameIndex();
|
||||
}
|
||||
#endif
|
||||
_animationLoop.setFrameIndex(value);
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setAnimationSettings(const QString& value) {
|
||||
// the animations setting is a JSON string that may contain various animation settings.
|
||||
// if it includes fps, frameIndex, or running, those values will be parsed out and
|
||||
// will over ride the regular animation settings
|
||||
|
||||
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
|
||||
QJsonObject settingsAsJsonObject = settingsAsJson.object();
|
||||
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
|
||||
if (settingsMap.contains("fps")) {
|
||||
float fps = settingsMap["fps"].toFloat();
|
||||
setAnimationFPS(fps);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("frameIndex")) {
|
||||
float frameIndex = settingsMap["frameIndex"].toFloat();
|
||||
#ifdef WANT_DEBUG
|
||||
if (isAnimatingSomething()) {
|
||||
qDebug() << "ParticleEffectEntityItem::setAnimationSettings() calling setAnimationFrameIndex()...";
|
||||
qDebug() << " settings:" << value;
|
||||
qDebug() << " settingsMap[frameIndex]:" << settingsMap["frameIndex"];
|
||||
qDebug(" frameIndex: %20.5f", frameIndex);
|
||||
}
|
||||
#endif
|
||||
|
||||
setAnimationFrameIndex(frameIndex);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("running")) {
|
||||
bool running = settingsMap["running"].toBool();
|
||||
if (running != getAnimationIsPlaying()) {
|
||||
setAnimationIsPlaying(running);
|
||||
}
|
||||
}
|
||||
|
||||
if (settingsMap.contains("firstFrame")) {
|
||||
float firstFrame = settingsMap["firstFrame"].toFloat();
|
||||
setAnimationFirstFrame(firstFrame);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("lastFrame")) {
|
||||
float lastFrame = settingsMap["lastFrame"].toFloat();
|
||||
setAnimationLastFrame(lastFrame);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("loop")) {
|
||||
bool loop = settingsMap["loop"].toBool();
|
||||
setAnimationLoop(loop);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("hold")) {
|
||||
bool hold = settingsMap["hold"].toBool();
|
||||
setAnimationHold(hold);
|
||||
}
|
||||
|
||||
if (settingsMap.contains("startAutomatically")) {
|
||||
bool startAutomatically = settingsMap["startAutomatically"].toBool();
|
||||
setAnimationStartAutomatically(startAutomatically);
|
||||
}
|
||||
|
||||
_animationSettings = value;
|
||||
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setAnimationIsPlaying(bool value) {
|
||||
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
|
||||
_animationLoop.setRunning(value);
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setAnimationFPS(float value) {
|
||||
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
|
||||
_animationLoop.setFPS(value);
|
||||
}
|
||||
|
||||
QString ParticleEffectEntityItem::getAnimationSettings() const {
|
||||
// the animations setting is a JSON string that may contain various animation settings.
|
||||
// if it includes fps, frameIndex, or running, those values will be parsed out and
|
||||
// will over ride the regular animation settings
|
||||
QString value = _animationSettings;
|
||||
|
||||
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
|
||||
QJsonObject settingsAsJsonObject = settingsAsJson.object();
|
||||
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
|
||||
|
||||
QVariant fpsValue(getAnimationFPS());
|
||||
settingsMap["fps"] = fpsValue;
|
||||
|
||||
QVariant frameIndexValue(getAnimationFrameIndex());
|
||||
settingsMap["frameIndex"] = frameIndexValue;
|
||||
|
||||
QVariant runningValue(getAnimationIsPlaying());
|
||||
settingsMap["running"] = runningValue;
|
||||
|
||||
QVariant firstFrameValue(getAnimationFirstFrame());
|
||||
settingsMap["firstFrame"] = firstFrameValue;
|
||||
|
||||
QVariant lastFrameValue(getAnimationLastFrame());
|
||||
settingsMap["lastFrame"] = lastFrameValue;
|
||||
|
||||
QVariant loopValue(getAnimationLoop());
|
||||
settingsMap["loop"] = loopValue;
|
||||
|
||||
QVariant holdValue(getAnimationHold());
|
||||
settingsMap["hold"] = holdValue;
|
||||
|
||||
QVariant startAutomaticallyValue(getAnimationStartAutomatically());
|
||||
settingsMap["startAutomatically"] = startAutomaticallyValue;
|
||||
|
||||
settingsAsJsonObject = QJsonObject::fromVariantMap(settingsMap);
|
||||
QJsonDocument newDocument(settingsAsJsonObject);
|
||||
QByteArray jsonByteArray = newDocument.toJson(QJsonDocument::Compact);
|
||||
QString jsonByteString(jsonByteArray);
|
||||
return jsonByteString;
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::stepSim(float deltaTime)
|
||||
{
|
||||
pa_xmin = pa_ymin = pa_zmin = -1.0;
|
||||
pa_xmax = pa_ymax = pa_zmax = 1.0;
|
||||
|
||||
// update particles
|
||||
quint32 updateIter = pa_head;
|
||||
while (pa_life[updateIter] > 0.0f)
|
||||
{
|
||||
pa_life[updateIter] -= deltaTime;
|
||||
if (pa_life[updateIter] <= 0.0f)
|
||||
{
|
||||
pa_life[updateIter] = -1.0f;
|
||||
pa_head = (pa_head + 1) % _maxParticles;
|
||||
pa_count--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// DUMB FORWARD EULER just to get it done
|
||||
int j = updateIter * 3;
|
||||
pa_position[j] += pa_velocity[j] * deltaTime;
|
||||
pa_position[j+1] += pa_velocity[j+1] * deltaTime;
|
||||
pa_position[j+2] += pa_velocity[j+2] * deltaTime;
|
||||
|
||||
pa_xmin = glm::min(pa_xmin, pa_position[j]);
|
||||
pa_ymin = glm::min(pa_ymin, pa_position[j+1]);
|
||||
pa_zmin = glm::min(pa_zmin, pa_position[j+2]);
|
||||
pa_xmax = glm::max(pa_xmax, pa_position[j]);
|
||||
pa_ymax = glm::max(pa_ymax, pa_position[j + 1]);
|
||||
pa_zmax = glm::max(pa_zmax, pa_position[j + 2]);
|
||||
|
||||
// massless particles
|
||||
pa_velocity[j + 1] += deltaTime * _localGravity;
|
||||
}
|
||||
updateIter = (updateIter + 1) % _maxParticles;
|
||||
}
|
||||
|
||||
// emit new particles
|
||||
quint32 pa_emit_idx = updateIter;
|
||||
partial_emit += ((float)_emitRate) * deltaTime;
|
||||
quint32 birthed = (quint32)partial_emit;
|
||||
partial_emit -= (float)birthed;
|
||||
glm::vec3 randOffset;
|
||||
|
||||
for (quint32 i = 0; i < birthed; i++)
|
||||
{
|
||||
if (pa_life[pa_emit_idx] < 0.0f)
|
||||
{
|
||||
int j = pa_emit_idx * 3;
|
||||
pa_life[pa_emit_idx] = _lifespan;
|
||||
randOffset.x = (((double)rand() / (double)RAND_MAX) - 0.5) * 0.25 * _emitStrength;
|
||||
randOffset.y = (((double)rand() / (double)RAND_MAX) - 0.5) * 0.25 * _emitStrength;
|
||||
randOffset.z = (((double)rand() / (double)RAND_MAX) - 0.5) * 0.25 * _emitStrength;
|
||||
pa_velocity[j] = (_emitDirection.x * _emitStrength) + randOffset.x;
|
||||
pa_velocity[j + 1] = (_emitDirection.y * _emitStrength) + randOffset.y;
|
||||
pa_velocity[j + 2] = (_emitDirection.z * _emitStrength) + randOffset.z;
|
||||
|
||||
// DUMB FORWARD EULER just to get it done
|
||||
pa_position[j] += pa_velocity[j] * deltaTime;
|
||||
pa_position[j + 1] += pa_velocity[j + 1] * deltaTime;
|
||||
pa_position[j + 2] += pa_velocity[j + 2] * deltaTime;
|
||||
|
||||
pa_xmin = glm::min(pa_xmin, pa_position[j]);
|
||||
pa_ymin = glm::min(pa_ymin, pa_position[j + 1]);
|
||||
pa_zmin = glm::min(pa_zmin, pa_position[j + 2]);
|
||||
pa_xmax = glm::max(pa_xmax, pa_position[j]);
|
||||
pa_ymax = glm::max(pa_ymax, pa_position[j + 1]);
|
||||
pa_zmax = glm::max(pa_zmax, pa_position[j + 2]);
|
||||
|
||||
// massless particles
|
||||
pa_velocity[j + 1] += deltaTime * _localGravity;
|
||||
|
||||
pa_emit_idx = (pa_emit_idx + 1) % _maxParticles;
|
||||
pa_count++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::resetSim()
|
||||
{
|
||||
for (int i = 0; i < _maxParticles; i++)
|
||||
{
|
||||
int j = i * 3;
|
||||
pa_life[i] = -1.0f;
|
||||
pa_position[j] = 0.0f;
|
||||
pa_position[j+1] = 0.0f;
|
||||
pa_position[j+2] = 0.0f;
|
||||
pa_velocity[j] = 0.0f;
|
||||
pa_velocity[j+1] = 0.0f;
|
||||
pa_velocity[j+2] = 0.0f;
|
||||
}
|
||||
pa_count = 0;
|
||||
pa_head = 0;
|
||||
partial_emit = 0.0f;
|
||||
|
||||
srand((unsigned int) this);
|
||||
}
|
||||
|
181
libraries/entities/src/ParticleEffectEntityItem.h
Normal file
181
libraries/entities/src/ParticleEffectEntityItem.h
Normal file
|
@ -0,0 +1,181 @@
|
|||
//
|
||||
// ParticleEffectEntityItem.h
|
||||
// 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 controls for spread (which is currently hard-coded) and varying emission strength (not currently implemented).
|
||||
// - Add drag.
|
||||
// - Add some kind of support for collisions.
|
||||
// - For simplicity, I'm currently just rendering each particle as a cross of four axis-aligned quads. Really, we'd
|
||||
// want multiple render modes, including (the most important) textured billboards (always facing camera). Also, these
|
||||
// should support animated textures.
|
||||
// - 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.
|
||||
// - MORE?
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_ParticleEffectEntityItem_h
|
||||
#define hifi_ParticleEffectEntityItem_h
|
||||
|
||||
#include <AnimationLoop.h>
|
||||
#include "EntityItem.h"
|
||||
|
||||
class ParticleEffectEntityItem : public EntityItem {
|
||||
public:
|
||||
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
ParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
|
||||
virtual ~ParticleEffectEntityItem();
|
||||
|
||||
ALLOW_INSTANTIATION // This class can be instantiated
|
||||
|
||||
// methods for getting/setting all properties of this entity
|
||||
virtual EntityItemProperties getProperties() const;
|
||||
virtual bool setProperties(const EntityItemProperties& properties);
|
||||
|
||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const;
|
||||
|
||||
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
|
||||
|
||||
virtual void update(const quint64& now);
|
||||
virtual bool needsToCallUpdate() const;
|
||||
|
||||
const rgbColor& getColor() const { return _color; }
|
||||
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
|
||||
|
||||
void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); }
|
||||
void setColor(const xColor& value) {
|
||||
_color[RED_INDEX] = value.red;
|
||||
_color[GREEN_INDEX] = value.green;
|
||||
_color[BLUE_INDEX] = value.blue;
|
||||
}
|
||||
|
||||
void updateShapeType(ShapeType type);
|
||||
virtual ShapeType getShapeType() const { return _shapeType; }
|
||||
|
||||
virtual void debugDump() const;
|
||||
|
||||
static const float DEFAULT_ANIMATION_FRAME_INDEX;
|
||||
void setAnimationFrameIndex(float value);
|
||||
void setAnimationSettings(const QString& value);
|
||||
|
||||
static const bool DEFAULT_ANIMATION_IS_PLAYING;
|
||||
void setAnimationIsPlaying(bool value);
|
||||
|
||||
static const float DEFAULT_ANIMATION_FPS;
|
||||
void setAnimationFPS(float value);
|
||||
|
||||
void setAnimationLoop(bool loop) { _animationLoop.setLoop(loop); }
|
||||
bool getAnimationLoop() const { return _animationLoop.getLoop(); }
|
||||
|
||||
void setAnimationHold(bool hold) { _animationLoop.setHold(hold); }
|
||||
bool getAnimationHold() const { return _animationLoop.getHold(); }
|
||||
|
||||
void setAnimationStartAutomatically(bool startAutomatically) { _animationLoop.setStartAutomatically(startAutomatically); }
|
||||
bool getAnimationStartAutomatically() const { return _animationLoop.getStartAutomatically(); }
|
||||
|
||||
void setAnimationFirstFrame(float firstFrame) { _animationLoop.setFirstFrame(firstFrame); }
|
||||
float getAnimationFirstFrame() const { return _animationLoop.getFirstFrame(); }
|
||||
|
||||
void setAnimationLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); }
|
||||
float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); }
|
||||
|
||||
static const quint32 DEFAULT_MAX_PARTICLES;
|
||||
void setMaxParticles(quint32 maxParticles) { _maxParticles = maxParticles; }
|
||||
quint32 getMaxParticles() const { return _maxParticles; }
|
||||
|
||||
static const float DEFAULT_LIFESPAN;
|
||||
void setLifespan(float lifespan) { _lifespan = lifespan; }
|
||||
float getLifespan() const { return _lifespan; }
|
||||
|
||||
static const float DEFAULT_EMIT_RATE;
|
||||
void setEmitRate(float emitRate) { _emitRate = emitRate; }
|
||||
float getEmitRate() const { return _emitRate; }
|
||||
|
||||
static const glm::vec3 DEFAULT_EMIT_DIRECTION;
|
||||
void setEmitDirection(glm::vec3 emitDirection) { _emitDirection = emitDirection; }
|
||||
const glm::vec3& getEmitDirection() const { return _emitDirection; }
|
||||
|
||||
static const float DEFAULT_EMIT_STRENGTH;
|
||||
void setEmitStrength(float emitStrength) { _emitStrength = emitStrength; }
|
||||
float getEmitStrength() const { return _emitStrength; }
|
||||
|
||||
static const float DEFAULT_LOCAL_GRAVITY;
|
||||
void setLocalGravity(float localGravity) { _localGravity = localGravity; }
|
||||
float getLocalGravity() const { return _localGravity; }
|
||||
|
||||
static const float DEFAULT_PARTICLE_RADIUS;
|
||||
void setParticleRadius(float particleRadius) { _particleRadius = particleRadius; }
|
||||
float getParticleRadius() const { return _particleRadius; }
|
||||
|
||||
bool getAnimationIsPlaying() const { return _animationLoop.isRunning(); }
|
||||
float getAnimationFrameIndex() const { return _animationLoop.getFrameIndex(); }
|
||||
float getAnimationFPS() const { return _animationLoop.getFPS(); }
|
||||
QString getAnimationSettings() const;
|
||||
|
||||
protected:
|
||||
|
||||
bool isAnimatingSomething() const;
|
||||
void stepSim(float deltaTime);
|
||||
void resetSim();
|
||||
|
||||
// the properties of this entity
|
||||
rgbColor _color;
|
||||
quint32 _maxParticles;
|
||||
float _lifespan;
|
||||
float _emitRate;
|
||||
glm::vec3 _emitDirection;
|
||||
float _emitStrength;
|
||||
float _localGravity;
|
||||
float _particleRadius;
|
||||
quint64 _lastAnimated;
|
||||
AnimationLoop _animationLoop;
|
||||
QString _animationSettings;
|
||||
ShapeType _shapeType = SHAPE_TYPE_NONE;
|
||||
|
||||
// all the internals of running the particle sim
|
||||
float* pa_life;
|
||||
float* pa_position;
|
||||
float* pa_velocity;
|
||||
float partial_emit;
|
||||
quint32 pa_count;
|
||||
quint32 pa_head;
|
||||
float pa_xmin;
|
||||
float pa_xmax;
|
||||
float pa_ymin;
|
||||
float pa_ymax;
|
||||
float pa_zmin;
|
||||
float pa_zmax;
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_ParticleEffectEntityItem_h
|
||||
|
Loading…
Reference in a new issue