From 30b77d420022334a5031c544fb9d1963bd79e3ad Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 3 Mar 2015 22:07:12 -0800 Subject: [PATCH 01/14] Don't crash if avatar model at start-up or loaded is skeletonless --- interface/src/avatar/MyAvatar.cpp | 6 ++++++ interface/src/avatar/SkeletonModel.cpp | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9c83942439..9213d7416f 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -195,6 +195,12 @@ void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("skeleton"); _skeletonModel.simulate(deltaTime); } + + if (!_skeletonModel.hasSkeleton()) { + // All the simulation that can be done has been done + return; + } + { PerformanceTimer perfTimer("attachments"); simulateAttachments(deltaTime); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index fa9846fd7d..03baa334e5 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -721,7 +721,8 @@ void SkeletonModel::buildShapes() { } const FBXGeometry& geometry = _geometry->getFBXGeometry(); - if (geometry.joints.isEmpty()) { + if (geometry.joints.isEmpty() || geometry.rootJointIndex == -1) { + // rootJointIndex == -1 if the avatar model has no skeleton return; } From aad3f1dfd9c71fb8feb3bb0fd7f5e2a451d97578 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 3 Mar 2015 22:07:45 -0800 Subject: [PATCH 02/14] Automatically switch to default model if avatar model is skeletonless --- interface/src/Application.cpp | 19 +++++++++++++++++++ interface/src/Application.h | 2 ++ interface/src/avatar/MyAvatar.cpp | 1 - interface/src/avatar/SkeletonModel.cpp | 5 +++++ interface/src/avatar/SkeletonModel.h | 8 +++++++- 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6484e364bc..f1d9dcbef2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -490,6 +490,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(nodeList.data(), SIGNAL(dataReceived(const quint8, const int)), bandwidthRecorder.data(), SLOT(updateInboundData(const quint8, const int))); + connect(&_myAvatar->getSkeletonModel(), &SkeletonModel::skeletonLoaded, + this, &Application::checkSkeleton, Qt::QueuedConnection); + // check first run... if (_firstRun.get()) { qDebug() << "This is a first run..."; @@ -4006,3 +4009,19 @@ void Application::notifyPacketVersionMismatch() { msgBox.exec(); } } + +void Application::checkSkeleton() { + if (_myAvatar->getSkeletonModel().isActive() && !_myAvatar->getSkeletonModel().hasSkeleton()) { + qDebug() << "MyAvatar model has no skeleton"; + + QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded..."; + QMessageBox msgBox; + msgBox.setText(message); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); + + _myAvatar->setSkeletonModelURL(DEFAULT_BODY_MODEL_URL); + _myAvatar->sendIdentityPacket(); + } +} diff --git a/interface/src/Application.h b/interface/src/Application.h index 91a5f7547b..81025245fa 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -584,6 +584,8 @@ private: QTimer _settingsTimer; GLCanvas* _glWidget = new GLCanvas(); // our GLCanvas has a couple extra features + + void checkSkeleton(); }; #endif // hifi_Application_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9213d7416f..c2a957dc72 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 03baa334e5..fbb622b30a 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -81,6 +81,8 @@ void SkeletonModel::setJointStates(QVector states) { if (_enableShapes) { buildShapes(); } + + emit skeletonLoaded(); } const float PALM_PRIORITY = DEFAULT_PRIORITY; @@ -1007,3 +1009,6 @@ void SkeletonModel::renderJointCollisionShapes(float alpha) { glPopMatrix(); } +bool SkeletonModel::hasSkeleton() { + return isActive() ? _geometry->getFBXGeometry().rootJointIndex != -1 : false; +} diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index d28d1a8aef..74d0ed0324 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -123,7 +123,13 @@ public: void resetShapePositionsToDefaultPose(); // DEBUG method void renderRagdoll(); - + + bool hasSkeleton(); + +signals: + + void skeletonLoaded(); + protected: void buildShapes(); From 8686e090c9fd32a3aeec629fe7677017012a4989 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 3 Mar 2015 22:30:45 -0800 Subject: [PATCH 03/14] Don't upload skeletonless avatar model to HiFi servers --- interface/src/ModelUploader.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index ad73d119a3..32973b001d 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -177,6 +177,22 @@ bool ModelUploader::zip() { } QByteArray fbxContents = fbx.readAll(); FBXGeometry geometry = readFBX(fbxContents, QVariantHash()); + + // Make sure that a skeleton model has a skeleton + if (_modelType == SKELETON_MODEL) { + if (geometry.rootJointIndex == -1) { + + QString message = "Your selected skeleton model has no skeleton.\n\nThe upload will be canceled."; + QMessageBox msgBox; + msgBox.setWindowTitle("Model Upload"); + msgBox.setText(message); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Warning); + msgBox.exec(); + + return false; + } + } // make sure we have some basic mappings populateBasicMapping(mapping, filename, geometry); From 3522357c8c10fba5866aaecec408b7db61cda230 Mon Sep 17 00:00:00 2001 From: Jason <2billbrasky@gmail.com> Date: Wed, 4 Mar 2015 16:06:06 -0800 Subject: [PATCH 04/14] 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. --- .../src/EntityTreeRenderer.cpp | 2 + .../RenderableParticleEffectEntityItem.cpp | 88 +++ .../src/RenderableParticleEffectEntityItem.h | 29 + .../entities/src/EntityItemProperties.cpp | 57 ++ libraries/entities/src/EntityItemProperties.h | 26 +- libraries/entities/src/EntityTypes.cpp | 2 + libraries/entities/src/EntityTypes.h | 3 +- .../entities/src/ParticleEffectEntityItem.cpp | 522 ++++++++++++++++++ .../entities/src/ParticleEffectEntityItem.h | 181 ++++++ 9 files changed, 908 insertions(+), 2 deletions(-) create mode 100644 libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp create mode 100644 libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h create mode 100644 libraries/entities/src/ParticleEffectEntityItem.cpp create mode 100644 libraries/entities/src/ParticleEffectEntityItem.h diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 46f9ff6f55..3db4ffb2ac 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -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 diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp new file mode 100644 index 0000000000..a10f59287c --- /dev/null +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -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 + +#include + +#include +#include +#include + +#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(); +}; diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h new file mode 100644 index 0000000000..837c878a45 --- /dev/null +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -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 + +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 diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index f3f84876ba..87b59b0392 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -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 { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 2391bcde84..d5a3c32809 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -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"; diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index fd9484e0d6..d3ffc4247c 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -23,6 +23,7 @@ #include "ModelEntityItem.h" #include "SphereEntityItem.h" #include "TextEntityItem.h" +#include "ParticleEffectEntityItem.h" QMap EntityTypes::_typeToNameMap; QMap 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) { diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 8ed407f11d..e1f8e876bb 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -35,7 +35,8 @@ public: Sphere, Light, Text, - LAST = Text + ParticleEffect, + LAST = ParticleEffect } EntityType; static const QString& getEntityTypeName(EntityType entityType); diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp new file mode 100644 index 0000000000..cf84f7be75 --- /dev/null +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -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 +#include + +#include + +#include +#include + +#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); +} + diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h new file mode 100644 index 0000000000..1306620bc6 --- /dev/null +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -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 +#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 + From 1b7fdf5d16b76581ee5e29f688c8b4a8e57a5e6d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Mar 2015 18:49:52 +0100 Subject: [PATCH 05/14] Improve disk cache usage Now we always load from the disk cache if the file is in there. Then we check the last modidied date from the network file against the one in the cache. If needed, we redownload. --- libraries/networking/src/ResourceCache.cpp | 43 +++++++++++++++++++++- libraries/networking/src/ResourceCache.h | 6 ++- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 7eadb0a3dd..658f32aba1 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -12,9 +12,10 @@ #include #include +#include +#include #include #include -#include #include @@ -314,13 +315,51 @@ void Resource::handleReplyTimeout() { "received" << _bytesReceived << "total" << _bytesTotal); } +void Resource::maybeRefresh() { + if (Q_LIKELY(NetworkAccessManager::getInstance().cache())) { + QNetworkReply* reply = qobject_cast(sender()); + QVariant variant = reply->header(QNetworkRequest::LastModifiedHeader); + QNetworkCacheMetaData metaData = NetworkAccessManager::getInstance().cache()->metaData(_url); + if (variant.isValid() && variant.canConvert() && metaData.isValid()) { + QDateTime lastModified = variant.value(); + QDateTime lastModifiedOld = metaData.lastModified(); + if (lastModified.isValid() && lastModifiedOld.isValid() && + lastModifiedOld == lastModified) { + // We don't need to update, return + return; + } + } + qDebug() << "Loaded" << _url.fileName() << "from the disk cache but the network version is newer, refreshing."; + refresh(); + } +} + void Resource::makeRequest() { _reply = NetworkAccessManager::getInstance().get(_request); connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64))); connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError())); connect(_reply, SIGNAL(finished()), SLOT(handleReplyFinished())); - + + if (_reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool()) { + // If the file as been updated since it was cached, refresh it + QNetworkRequest request(_request); + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); + request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false); + QNetworkReply* reply = NetworkAccessManager::getInstance().head(request); + connect(reply, &QNetworkReply::finished, this, &Resource::maybeRefresh); + } else { + if (Q_LIKELY(NetworkAccessManager::getInstance().cache())) { + QNetworkCacheMetaData metaData = NetworkAccessManager::getInstance().cache()->metaData(_url); + if (metaData.expirationDate().isNull() || metaData.expirationDate() <= QDateTime::currentDateTime()) { + // If the expiration date is NULL or in the past, + // put one far enough away that it won't be an issue. + metaData.setExpirationDate(QDateTime::currentDateTime().addYears(100)); + NetworkAccessManager::getInstance().cache()->updateMetaData(metaData); + } + } + } + _replyTimer = new QTimer(this); connect(_replyTimer, SIGNAL(timeout()), SLOT(handleReplyTimeout())); _replyTimer->setSingleShot(true); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 62fca71ea5..11b091a9e3 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -149,7 +149,7 @@ public: /// For loading resources, returns the load progress. float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; } - + /// Refreshes the resource. void refresh(); @@ -169,6 +169,10 @@ signals: protected slots: void attemptRequest(); + + /// Refreshes the resource if the last modified date on the network + /// is greater than the last modified date in the cache. + void maybeRefresh(); protected: From 9d7144dab04921ab7428f4e52ec083485f42dec8 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Mar 2015 18:52:48 +0100 Subject: [PATCH 06/14] Bumped disk cache size to 10GB --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6484e364bc..96874f8593 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -141,7 +141,7 @@ using namespace std; static unsigned STARFIELD_NUM_STARS = 50000; static unsigned STARFIELD_SEED = 1; -const qint64 MAXIMUM_CACHE_SIZE = 10737418240; // 10GB +const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES; // 10GB static QTimer* locationUpdateTimer = NULL; static QTimer* balanceUpdateTimer = NULL; From 53f23d8452ed15cd1107d99f6c6632cd361e3e85 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Mar 2015 21:08:14 +0100 Subject: [PATCH 07/14] Added disk cache editor --- interface/src/ui/DiskCacheEditor.cpp | 148 +++++++++++++++++++++++++++ interface/src/ui/DiskCacheEditor.h | 46 +++++++++ 2 files changed, 194 insertions(+) create mode 100644 interface/src/ui/DiskCacheEditor.cpp create mode 100644 interface/src/ui/DiskCacheEditor.h diff --git a/interface/src/ui/DiskCacheEditor.cpp b/interface/src/ui/DiskCacheEditor.cpp new file mode 100644 index 0000000000..d1987462e3 --- /dev/null +++ b/interface/src/ui/DiskCacheEditor.cpp @@ -0,0 +1,148 @@ +// +// DiskCacheEditor.cpp +// +// +// Created by Clement on 3/4/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "DiskCacheEditor.h" + +DiskCacheEditor::DiskCacheEditor(QWidget* parent) : QObject(parent) { + +} + +QWindow* DiskCacheEditor::windowHandle() { + return (_dialog) ? _dialog->windowHandle() : nullptr; +} + +void DiskCacheEditor::toggle() { + qDebug() << "DiskCacheEditor::toggle()"; + if (!_dialog) { + makeDialog(); + } + + if (!_dialog->isActiveWindow()) { + _dialog->show(); + _dialog->raise(); + _dialog->activateWindow(); + } else { + _dialog->close(); + } +} + +void DiskCacheEditor::makeDialog() { + _dialog = new QDialog(static_cast(parent())); + Q_CHECK_PTR(_dialog); + _dialog->setAttribute(Qt::WA_DeleteOnClose); + _dialog->setWindowTitle("Disk Cache Editor"); + + QGridLayout* layout = new QGridLayout(_dialog); + Q_CHECK_PTR(layout); + _dialog->setLayout(layout); + + + QLabel* path = new QLabel("Path : ", _dialog); + Q_CHECK_PTR(path); + path->setAlignment(Qt::AlignRight); + layout->addWidget(path, 0, 0); + + QLabel* size = new QLabel("Max Size : ", _dialog); + Q_CHECK_PTR(size); + size->setAlignment(Qt::AlignRight); + layout->addWidget(size, 1, 0); + + QLabel* maxSize = new QLabel("Current Size : ", _dialog); + Q_CHECK_PTR(maxSize); + maxSize->setAlignment(Qt::AlignRight); + layout->addWidget(maxSize, 2, 0); + + + _path = new QLabel(_dialog); + Q_CHECK_PTR(_path); + _path->setAlignment(Qt::AlignLeft); + layout->addWidget(_path, 0, 1, 1, 3); + + _size = new QLabel(_dialog); + Q_CHECK_PTR(_size); + _size->setAlignment(Qt::AlignLeft); + layout->addWidget(_size, 1, 1, 1, 3); + + _maxSize = new QLabel(_dialog); + Q_CHECK_PTR(_maxSize); + _maxSize->setAlignment(Qt::AlignLeft); + layout->addWidget(_maxSize, 2, 1, 1, 3); + + refresh(); + + + QPushButton* refreshCacheButton = new QPushButton(_dialog); + Q_CHECK_PTR(refreshCacheButton); + refreshCacheButton->setText("Refresh"); + refreshCacheButton->setToolTip("Reload the cache stats."); + connect(refreshCacheButton, SIGNAL(clicked()), SLOT(refresh())); + layout->addWidget(refreshCacheButton, 3, 2); + + QPushButton* clearCacheButton = new QPushButton(_dialog); + Q_CHECK_PTR(clearCacheButton); + clearCacheButton->setText("Clear"); + clearCacheButton->setToolTip("Erases the entire content of the disk cache."); + connect(clearCacheButton, SIGNAL(clicked()), SLOT(clear())); + layout->addWidget(clearCacheButton, 3, 3); +} + +void DiskCacheEditor::refresh() { + static const std::function stringify = [](qint64 number) { + static const QStringList UNITS = QStringList() << "B" << "KB" << "MB" << "GB"; + static const qint64 CHUNK = 1024; + QString unit; + int i = 0; + for (i = 0; i < 4; ++i) { + if (number / CHUNK > 0) { + number /= CHUNK; + } else { + break; + } + } + return QString("%0 %1").arg(number).arg(UNITS[i]); + }; + QNetworkDiskCache* cache = qobject_cast(NetworkAccessManager::getInstance().cache()); + + if (_path) { + _path->setText(cache->cacheDirectory()); + } + if (_size) { + _size->setText(stringify(cache->cacheSize())); + } + if (_maxSize) { + _maxSize->setText(stringify(cache->maximumCacheSize())); + } +} + +void DiskCacheEditor::clear() { + QMessageBox::StandardButton buttonClicked = + QMessageBox::question(_dialog, "Clearing disk cache", + "You are about to erase all the content of the disk cache," + "are you sure you want to do that?"); + if (buttonClicked == QMessageBox::Yes) { + QNetworkDiskCache* cache = qobject_cast(NetworkAccessManager::getInstance().cache()); + if (cache) { + qDebug() << "DiskCacheEditor::clear(): Clearing disk cache."; + cache->clear(); + } + } + refresh(); +} diff --git a/interface/src/ui/DiskCacheEditor.h b/interface/src/ui/DiskCacheEditor.h new file mode 100644 index 0000000000..5d673c4285 --- /dev/null +++ b/interface/src/ui/DiskCacheEditor.h @@ -0,0 +1,46 @@ +// +// DiskCacheEditor.h +// +// +// Created by Clement on 3/4/15. +// Copyright 2015 High Fidelity, Inc. +// +// 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_DiskCacheEditor_h +#define hifi_DiskCacheEditor_h + +#include +#include + +class QDialog; +class QLabel; +class QWindow; + +class DiskCacheEditor : public QObject { + Q_OBJECT + +public: + DiskCacheEditor(QWidget* parent = nullptr); + + QWindow* windowHandle(); + +public slots: + void toggle(); + +private slots: + void refresh(); + void clear(); + +private: + void makeDialog(); + + QPointer _dialog; + QPointer _path; + QPointer _size; + QPointer _maxSize; +}; + +#endif // hifi_DiskCacheEditor_h \ No newline at end of file From 908e946ac5c606929137c17090975898d5faa967 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Mar 2015 21:09:23 +0100 Subject: [PATCH 08/14] Hook up disk cache to the dialog manager --- interface/src/ui/DialogsManager.cpp | 7 ++++++- interface/src/ui/DialogsManager.h | 7 +++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 2452369c18..ad963e8af3 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -20,6 +20,7 @@ #include "AttachmentsDialog.h" #include "BandwidthDialog.h" #include "CachesSizeDialog.h" +#include "DiskCacheEditor.h" #include "HMDToolsDialog.h" #include "LodToolsDialog.h" #include "LoginDialog.h" @@ -37,6 +38,11 @@ void DialogsManager::toggleAddressBar() { } } +void DialogsManager::toggleDiskCacheEditor() { + maybeCreateDialog(_diskCacheEditor); + _diskCacheEditor->toggle(); +} + void DialogsManager::toggleLoginDialog() { maybeCreateDialog(_loginDialog); _loginDialog->toggleQAction(); @@ -61,7 +67,6 @@ void DialogsManager::octreeStatsDetails() { } void DialogsManager::cachesSizeDialog() { - qDebug() << "Caches size:" << _cachesSizeDialog.isNull(); if (!_cachesSizeDialog) { maybeCreateDialog(_cachesSizeDialog); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index e2da71bbd8..897215cbff 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -24,8 +24,9 @@ class QAction; class AddressBarDialog; class AnimationsDialog; class AttachmentsDialog; -class CachesSizeDialog; class BandwidthDialog; +class CachesSizeDialog; +class DiskCacheEditor; class LodToolsDialog; class LoginDialog; class OctreeStatsDialog; @@ -45,6 +46,7 @@ public: public slots: void toggleAddressBar(); + void toggleDiskCacheEditor(); void toggleLoginDialog(); void showLoginDialog(); void octreeStatsDetails(); @@ -73,7 +75,7 @@ private: member = new T(parent); Q_CHECK_PTR(member); - if (_hmdToolsDialog) { + if (_hmdToolsDialog && member->windowHandle()) { _hmdToolsDialog->watchWindow(member->windowHandle()); } } @@ -84,6 +86,7 @@ private: QPointer _attachmentsDialog; QPointer _bandwidthDialog; QPointer _cachesSizeDialog; + QPointer _diskCacheEditor; QPointer _ircInfoBox; QPointer _hmdToolsDialog; QPointer _lodToolsDialog; From f4d8ee39dcae0587138dc48681af3063834c47df Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Mar 2015 21:10:02 +0100 Subject: [PATCH 09/14] Menu item for cache editor --- interface/src/Application.cpp | 2 +- interface/src/Menu.cpp | 2 ++ interface/src/Menu.h | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 96874f8593..3454557fe9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -445,7 +445,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache"); networkAccessManager.setCache(cache); - + ResourceCache::setRequestLimit(3); _window->setCentralWidget(_glWidget); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 59d6734b2a..ea63b9879f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -436,6 +436,8 @@ Menu::Menu() { SLOT(disable(bool))); addActionToQMenuAndActionHash(networkMenu, MenuOption::CachesSize, 0, dialogsManager.data(), SLOT(cachesSizeDialog())); + addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0, + dialogsManager.data(), SLOT(toggleDiskCacheEditor())); QMenu* timingMenu = developerMenu->addMenu("Timing and Stats"); QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index fe1035d54b..3166e1fa37 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -125,7 +125,7 @@ namespace MenuOption { const QString BookmarkLocation = "Bookmark Location"; const QString Bookmarks = "Bookmarks"; const QString CascadedShadows = "Cascaded"; - const QString CachesSize = "Caches Size"; + const QString CachesSize = "RAM Caches Size"; const QString Chat = "Chat..."; const QString ChatCircling = "Chat Circling"; const QString CollideAsRagdoll = "Collide With Self (Ragdoll)"; @@ -143,6 +143,7 @@ namespace MenuOption { const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD"; const QString DisableLightEntities = "Disable Light Entities"; const QString DisableNackPackets = "Disable NACK Packets"; + const QString DiskCacheEditor = "Disk Cache Editor"; const QString DisplayHands = "Show Hand Info"; const QString DisplayHandTargets = "Show Hand Targets"; const QString DisplayModelBounds = "Display Model Bounds"; From 7690301e582ec54d9947fefc86f6294313cdfba6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Mar 2015 21:49:22 +0100 Subject: [PATCH 10/14] Include functional --- interface/src/ui/DiskCacheEditor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/ui/DiskCacheEditor.cpp b/interface/src/ui/DiskCacheEditor.cpp index d1987462e3..38fd1cb383 100644 --- a/interface/src/ui/DiskCacheEditor.cpp +++ b/interface/src/ui/DiskCacheEditor.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include #include From 7a5669f14ec2de834f0c7a7efa64a0286e90cd8d Mon Sep 17 00:00:00 2001 From: Jason <2billbrasky@gmail.com> Date: Fri, 6 Mar 2015 15:52:21 -0800 Subject: [PATCH 11/14] Interview project updates. Tried to address all of Brad's notes, most of which were related to matching the coding style for the project. Also used GeometryCache instead of making direct calls to OpenGL to do drawing, took a different approach to seeding rand(), updated the packet version, and fixed a bug that I noticed in the setting of the dimensions for the particle effect entity. --- .../RenderableParticleEffectEntityItem.cpp | 88 +-- .../src/RenderableParticleEffectEntityItem.h | 9 +- .../entities/src/EntityItemProperties.cpp | 106 +-- libraries/entities/src/EntityItemProperties.h | 46 +- .../entities/src/ParticleEffectEntityItem.cpp | 704 +++++++++--------- .../entities/src/ParticleEffectEntityItem.h | 210 +++--- libraries/networking/src/PacketHeaders.cpp | 2 +- libraries/networking/src/PacketHeaders.h | 1 + 8 files changed, 581 insertions(+), 585 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index a10f59287c..9cb8c3912e 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -15,11 +15,17 @@ #include #include #include +#include #include "RenderableParticleEffectEntityItem.h" EntityItem* RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - return new RenderableParticleEffectEntityItem(entityID, properties); + return new RenderableParticleEffectEntityItem(entityID, properties); +} + +RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : + ParticleEffectEntityItem(entityItemID, properties) { + _cacheID = DependencyManager::get()->allocateID(); } void RenderableParticleEffectEntityItem::render(RenderArgs* args) { @@ -28,11 +34,45 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) { glm::vec3 position = getPositionInMeters(); glm::vec3 center = getCenterInMeters(); glm::quat rotation = getRotation(); - float pa_rad = getParticleRadius(); + float pa_rad = getParticleRadius(); const float MAX_COLOR = 255.0f; - glm::vec4 sphereColor(getColor()[RED_INDEX] / MAX_COLOR, getColor()[GREEN_INDEX] / MAX_COLOR, + glm::vec4 paColor(getColor()[RED_INDEX] / MAX_COLOR, getColor()[GREEN_INDEX] / MAX_COLOR, getColor()[BLUE_INDEX] / MAX_COLOR, getLocalRenderAlpha()); + + // 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. + + QVector* pointVec = new QVector(_paCount * VERTS_PER_PARTICLE); + quint32 paIter = _paHead; + while (_paLife[paIter] > 0.0f) { + int j = paIter * XYZ_STRIDE; + + pointVec->append(glm::vec3(_paPosition[j] - pa_rad, _paPosition[j + 1] + pa_rad, _paPosition[j + 2])); + pointVec->append(glm::vec3(_paPosition[j] + pa_rad, _paPosition[j + 1] + pa_rad, _paPosition[j + 2])); + pointVec->append(glm::vec3(_paPosition[j] + pa_rad, _paPosition[j + 1] - pa_rad, _paPosition[j + 2])); + pointVec->append(glm::vec3(_paPosition[j] - pa_rad, _paPosition[j + 1] - pa_rad, _paPosition[j + 2])); + + pointVec->append(glm::vec3(_paPosition[j] + pa_rad, _paPosition[j + 1] + pa_rad, _paPosition[j + 2])); + pointVec->append(glm::vec3(_paPosition[j] - pa_rad, _paPosition[j + 1] + pa_rad, _paPosition[j + 2])); + pointVec->append(glm::vec3(_paPosition[j] - pa_rad, _paPosition[j + 1] - pa_rad, _paPosition[j + 2])); + pointVec->append(glm::vec3(_paPosition[j] + pa_rad, _paPosition[j + 1] - pa_rad, _paPosition[j + 2])); + + pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] + pa_rad, _paPosition[j + 2] - pa_rad)); + pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] + pa_rad, _paPosition[j + 2] + pa_rad)); + pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] - pa_rad, _paPosition[j + 2] + pa_rad)); + pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] - pa_rad, _paPosition[j + 2] - pa_rad)); + + pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] + pa_rad, _paPosition[j + 2] + pa_rad)); + pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] + pa_rad, _paPosition[j + 2] - pa_rad)); + pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] - pa_rad, _paPosition[j + 2] - pa_rad)); + pointVec->append(glm::vec3(_paPosition[j], _paPosition[j + 1] - pa_rad, _paPosition[j + 2] + pa_rad)); + + paIter = (paIter + 1) % _maxParticles; + } + + DependencyManager::get()->updateVertices(_cacheID, *pointVec, paColor); glPushMatrix(); glTranslatef(position.x, position.y, position.z); @@ -44,45 +84,9 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) { 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(); + DependencyManager::get()->renderVertices(gpu::QUADS, _cacheID); glPopMatrix(); glPopMatrix(); + + delete pointVec; }; diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index 837c878a45..74b29574c3 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -16,13 +16,12 @@ class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem { public: static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties); - - RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - ParticleEffectEntityItem(entityItemID, properties) - { } - + RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); virtual void render(RenderArgs* args); +protected: + int _cacheID; + const int VERTS_PER_PARTICLE = 16; }; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 87b59b0392..3a2bacf9b1 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -67,13 +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), + 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), @@ -246,13 +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); + 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; } @@ -312,13 +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); + 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(); @@ -397,13 +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); + 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(); } @@ -582,15 +582,15 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem 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 (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(); @@ -814,15 +814,15 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int 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); - } + 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; } @@ -901,13 +901,13 @@ void EntityItemProperties::markAllChanged() { _backgroundColorChanged = true; _shapeTypeChanged = true; - _maxParticlesChanged = true; - _lifespanChanged = true; - _emitRateChanged = true; - _emitDirectionChanged = true; - _emitStrengthChanged = true; - _localGravityChanged = true; - _particleRadiusChanged = true; + _maxParticlesChanged = true; + _lifespanChanged = true; + _emitRateChanged = true; + _emitDirectionChanged = true; + _emitStrengthChanged = true; + _localGravityChanged = true; + _particleRadiusChanged = true; } AACube EntityItemProperties::getMaximumAACubeInTreeUnits() const { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index d5a3c32809..15527288ab 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -84,14 +84,14 @@ enum EntityPropertyList { 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, + // 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_PARTICLE_RADIUS, @@ -119,7 +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 + friend class ParticleEffectEntityItem; // TODO: consider removing this friend relationship and use public methods public: EntityItemProperties(); virtual ~EntityItemProperties(); @@ -192,13 +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); + 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); } @@ -321,13 +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_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"; diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index cf84f7be75..31b002b23f 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -57,466 +57,456 @@ const float ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS = 0.025f; EntityItem* ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - return new ParticleEffectEntityItem(entityID, 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(); + 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. + _paLife = new float[_maxParticles]; + _paPosition = new float[_maxParticles * XYZ_STRIDE]; // x,y,z + _paVelocity = new float[_maxParticles * XYZ_STRIDE]; // x,y,z + _paXmax = _paYmax = _paZmax = 1.0f; + _paXmin = _paYmin = _paZmin = -1.0f; + _randSeed = (unsigned int) glm::abs(_lifespan + _emitRate + _localGravity + getPosition().x + getPosition().y + getPosition().z); + resetSimulation(); + _lastAnimated = usecTimestampNow(); } -ParticleEffectEntityItem::~ParticleEffectEntityItem() -{ - free(pa_life); - free(pa_position); - free(pa_velocity); +ParticleEffectEntityItem::~ParticleEffectEntityItem() { + delete [] _paLife; + delete [] _paPosition; + delete [] _paVelocity; } 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); + 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; + return properties; } bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class + 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); + 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; + 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) { + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { - int bytesRead = 0; - const unsigned char* dataAt = data; + int bytesRead = 0; + const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY_COLOR(PROP_COLOR, _color); + 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); + // 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); - } + 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); + 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; + 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; + 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; + return requestedProperties; } void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { + 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 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; + return getAnimationIsPlaying() && + getAnimationFPS() != 0.0f; } bool ParticleEffectEntityItem::needsToCallUpdate() const { - return isAnimatingSomething() ? true : EntityItem::needsToCallUpdate(); + 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; - } + // 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) { + stepSimulation(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. + resetSimulation(); + stepSimulation((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); + // update the dimensions + glm::vec3 dims; + dims.x = glm::max(glm::abs(_paXmin), glm::abs(_paXmax)) * 2.0; + dims.y = glm::max(glm::abs(_paYmin), glm::abs(_paYmax)) * 2.0; + dims.z = glm::max(glm::abs(_paZmin), glm::abs(_paZmax)) * 2.0; + setDimensionsInMeters(dims); - EntityItem::update(now); // let our base class handle it's updates... + 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); + 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; - } + 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(); - } + if (isAnimatingSomething()) { + qDebug() << "ParticleEffectEntityItem::setAnimationFrameIndex()"; + qDebug() << " value:" << value; + qDebug() << " was:" << _animationLoop.getFrameIndex(); + } #endif - _animationLoop.setFrameIndex(value); + _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 + // 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); - } + 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(); + 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); - } + if (isAnimatingSomething()) { + qDebug() << "ParticleEffectEntityItem::setAnimationSettings() calling setAnimationFrameIndex()..."; + qDebug() << " settings:" << value; + qDebug() << " settingsMap[frameIndex]:" << settingsMap["frameIndex"]; + qDebug(" frameIndex: %20.5f", frameIndex); + } #endif - setAnimationFrameIndex(frameIndex); - } + setAnimationFrameIndex(frameIndex); + } - if (settingsMap.contains("running")) { - bool running = settingsMap["running"].toBool(); - if (running != getAnimationIsPlaying()) { - setAnimationIsPlaying(running); - } - } + 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("firstFrame")) { + float firstFrame = settingsMap["firstFrame"].toFloat(); + setAnimationFirstFrame(firstFrame); + } - if (settingsMap.contains("lastFrame")) { - float lastFrame = settingsMap["lastFrame"].toFloat(); - setAnimationLastFrame(lastFrame); - } + 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("loop")) { + bool loop = settingsMap["loop"].toBool(); + setAnimationLoop(loop); + } - if (settingsMap.contains("hold")) { - bool hold = settingsMap["hold"].toBool(); - setAnimationHold(hold); - } + if (settingsMap.contains("hold")) { + bool hold = settingsMap["hold"].toBool(); + setAnimationHold(hold); + } - if (settingsMap.contains("startAutomatically")) { - bool startAutomatically = settingsMap["startAutomatically"].toBool(); - setAnimationStartAutomatically(startAutomatically); - } + if (settingsMap.contains("startAutomatically")) { + bool startAutomatically = settingsMap["startAutomatically"].toBool(); + setAnimationStartAutomatically(startAutomatically); + } - _animationSettings = value; - _dirtyFlags |= EntityItem::DIRTY_UPDATEABLE; + _animationSettings = value; + _dirtyFlags |= EntityItem::DIRTY_UPDATEABLE; } void ParticleEffectEntityItem::setAnimationIsPlaying(bool value) { - _dirtyFlags |= EntityItem::DIRTY_UPDATEABLE; - _animationLoop.setRunning(value); + _dirtyFlags |= EntityItem::DIRTY_UPDATEABLE; + _animationLoop.setRunning(value); } void ParticleEffectEntityItem::setAnimationFPS(float value) { - _dirtyFlags |= EntityItem::DIRTY_UPDATEABLE; - _animationLoop.setFPS(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; + // 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(); + QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8()); + QJsonObject settingsAsJsonObject = settingsAsJson.object(); + QVariantMap settingsMap = settingsAsJsonObject.toVariantMap(); - QVariant fpsValue(getAnimationFPS()); - settingsMap["fps"] = fpsValue; + QVariant fpsValue(getAnimationFPS()); + settingsMap["fps"] = fpsValue; - QVariant frameIndexValue(getAnimationFrameIndex()); - settingsMap["frameIndex"] = frameIndexValue; + QVariant frameIndexValue(getAnimationFrameIndex()); + settingsMap["frameIndex"] = frameIndexValue; - QVariant runningValue(getAnimationIsPlaying()); - settingsMap["running"] = runningValue; + QVariant runningValue(getAnimationIsPlaying()); + settingsMap["running"] = runningValue; - QVariant firstFrameValue(getAnimationFirstFrame()); - settingsMap["firstFrame"] = firstFrameValue; + QVariant firstFrameValue(getAnimationFirstFrame()); + settingsMap["firstFrame"] = firstFrameValue; - QVariant lastFrameValue(getAnimationLastFrame()); - settingsMap["lastFrame"] = lastFrameValue; + QVariant lastFrameValue(getAnimationLastFrame()); + settingsMap["lastFrame"] = lastFrameValue; - QVariant loopValue(getAnimationLoop()); - settingsMap["loop"] = loopValue; + QVariant loopValue(getAnimationLoop()); + settingsMap["loop"] = loopValue; - QVariant holdValue(getAnimationHold()); - settingsMap["hold"] = holdValue; + QVariant holdValue(getAnimationHold()); + settingsMap["hold"] = holdValue; - QVariant startAutomaticallyValue(getAnimationStartAutomatically()); - settingsMap["startAutomatically"] = startAutomaticallyValue; + QVariant startAutomaticallyValue(getAnimationStartAutomatically()); + settingsMap["startAutomatically"] = startAutomaticallyValue; - settingsAsJsonObject = QJsonObject::fromVariantMap(settingsMap); - QJsonDocument newDocument(settingsAsJsonObject); - QByteArray jsonByteArray = newDocument.toJson(QJsonDocument::Compact); - QString jsonByteString(jsonByteArray); - return jsonByteString; + 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; +void ParticleEffectEntityItem::stepSimulation(float deltaTime) { + _paXmin = _paYmin = _paZmin = -1.0; + _paXmax = _paYmax = _paZmax = 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; + // update particles + quint32 updateIter = _paHead; + while (_paLife[updateIter] > 0.0f) { + _paLife[updateIter] -= deltaTime; + if (_paLife[updateIter] <= 0.0f) { + _paLife[updateIter] = -1.0f; + _paHead = (_paHead + 1) % _maxParticles; + _paCount--; + } + else { + // DUMB FORWARD EULER just to get it done + int j = updateIter * XYZ_STRIDE; + _paPosition[j] += _paVelocity[j] * deltaTime; + _paPosition[j+1] += _paVelocity[j+1] * deltaTime; + _paPosition[j+2] += _paVelocity[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]); + _paXmin = glm::min(_paXmin, _paPosition[j]); + _paYmin = glm::min(_paYmin, _paPosition[j+1]); + _paZmin = glm::min(_paZmin, _paPosition[j+2]); + _paXmax = glm::max(_paXmax, _paPosition[j]); + _paYmax = glm::max(_paYmax, _paPosition[j + 1]); + _paZmax = glm::max(_paZmax, _paPosition[j + 2]); - // massless particles - pa_velocity[j + 1] += deltaTime * _localGravity; - } - updateIter = (updateIter + 1) % _maxParticles; - } + // massless particles + _paVelocity[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; + // emit new particles + quint32 emitIdx = updateIter; + _partialEmit += ((float)_emitRate) * deltaTime; + quint32 birthed = (quint32)_partialEmit; + _partialEmit -= (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; + for (quint32 i = 0; i < birthed; i++) { + if (_paLife[emitIdx] < 0.0f) { + int j = emitIdx * XYZ_STRIDE; + _paLife[emitIdx] = _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; + _paVelocity[j] = (_emitDirection.x * _emitStrength) + randOffset.x; + _paVelocity[j + 1] = (_emitDirection.y * _emitStrength) + randOffset.y; + _paVelocity[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; + // DUMB FORWARD EULER just to get it done + _paPosition[j] += _paVelocity[j] * deltaTime; + _paPosition[j + 1] += _paVelocity[j + 1] * deltaTime; + _paPosition[j + 2] += _paVelocity[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]); + _paXmin = glm::min(_paXmin, _paPosition[j]); + _paYmin = glm::min(_paYmin, _paPosition[j + 1]); + _paZmin = glm::min(_paZmin, _paPosition[j + 2]); + _paXmax = glm::max(_paXmax, _paPosition[j]); + _paYmax = glm::max(_paYmax, _paPosition[j + 1]); + _paZmax = glm::max(_paZmax, _paPosition[j + 2]); - // massless particles - pa_velocity[j + 1] += deltaTime * _localGravity; + // massless particles + // and simple gravity down + _paVelocity[j + 1] += deltaTime * _localGravity; - pa_emit_idx = (pa_emit_idx + 1) % _maxParticles; - pa_count++; - } - else - break; - } + emitIdx = (emitIdx + 1) % _maxParticles; + _paCount++; + } + 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; +void ParticleEffectEntityItem::resetSimulation() { + for (int i = 0; i < _maxParticles; i++) { + int j = i * XYZ_STRIDE; + _paLife[i] = -1.0f; + _paPosition[j] = 0.0f; + _paPosition[j+1] = 0.0f; + _paPosition[j+2] = 0.0f; + _paVelocity[j] = 0.0f; + _paVelocity[j+1] = 0.0f; + _paVelocity[j+2] = 0.0f; + } + _paCount = 0; + _paHead = 0; + _partialEmit = 0.0f; - srand((unsigned int) this); + srand(_randSeed); } diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 1306620bc6..b00eb94685 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -39,141 +39,143 @@ class ParticleEffectEntityItem : public EntityItem { public: - - static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties); - ParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); - virtual ~ParticleEffectEntityItem(); + 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); + 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 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 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 int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData); - virtual void update(const quint64& now); - virtual bool needsToCallUpdate() const; + 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; } + 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 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; } + void updateShapeType(ShapeType type); + virtual ShapeType getShapeType() const { return _shapeType; } - virtual void debugDump() const; + virtual void debugDump() const; - static const float DEFAULT_ANIMATION_FRAME_INDEX; - void setAnimationFrameIndex(float value); - void setAnimationSettings(const QString& value); + 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 bool DEFAULT_ANIMATION_IS_PLAYING; + void setAnimationIsPlaying(bool value); - static const float DEFAULT_ANIMATION_FPS; - void setAnimationFPS(float 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 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 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 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 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(); } + 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 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_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 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 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_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_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; } + 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; + 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(); + bool isAnimatingSomething() const; + void stepSimulation(float deltaTime); + void resetSimulation(); - // 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; + // 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; + // all the internals of running the particle sim + const quint32 XYZ_STRIDE = 3; + float* _paLife; + float* _paPosition; + float* _paVelocity; + float _partialEmit; + quint32 _paCount; + quint32 _paHead; + float _paXmin; + float _paXmax; + float _paYmin; + float _paYmax; + float _paZmin; + float _paZmax; + unsigned int _randSeed; }; diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 2eee540fff..0aaa27e17f 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -72,7 +72,7 @@ PacketVersion versionForPacketType(PacketType type) { return 1; case PacketTypeEntityAddOrEdit: case PacketTypeEntityData: - return VERSION_MODEL_ENTITIES_SUPPORT_SHAPE_TYPE; + return VERSION_ENTITIES_HAS_PARTICLES; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index e593955b51..8e15c64b4b 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -129,5 +129,6 @@ const PacketVersion VERSION_ENTITIES_HAVE_USER_DATA = 6; const PacketVersion VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME = 7; const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SHAPE_TYPE = 8; const PacketVersion VERSION_OCTREE_HAS_FILE_BREAKS = 1; +const PacketVersion VERSION_ENTITIES_HAS_PARTICLES = 10; #endif // hifi_PacketHeaders_h From cfcc4679e13ba13f351dd2890c5833f3ee9264e4 Mon Sep 17 00:00:00 2001 From: Jason <2billbrasky@gmail.com> Date: Fri, 6 Mar 2015 15:57:38 -0800 Subject: [PATCH 12/14] Interview project updates (2). Oops, I forgot to convert tabs-to-spaces on EntityTreeRenderer. Committing it now. --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 3db4ffb2ac..ce946af93c 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -54,7 +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) + REGISTER_ENTITY_TYPE_WITH_FACTORY(ParticleEffect, RenderableParticleEffectEntityItem::factory) _currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID _currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID From d112c455e2f1c7d43750a5a320535a1568b004cd Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 6 Mar 2015 17:49:31 -0800 Subject: [PATCH 13/14] Add hmdDefaults.js script --- examples/hmdDefaults.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 examples/hmdDefaults.js diff --git a/examples/hmdDefaults.js b/examples/hmdDefaults.js new file mode 100644 index 0000000000..0096b11777 --- /dev/null +++ b/examples/hmdDefaults.js @@ -0,0 +1,16 @@ +// +// hmdDefaults.js +// examples +// +// Created by David Rowe on 6 Mar 2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.load("progress.js"); +Script.load("lobby.js"); +Script.load("notifications.js"); +Script.load("controllers/oculus/goTo.js"); +//Script.load("scripts.js"); // Not created yet From 7088c0287aeea192a7b1fd5ad6ff99fc0584d474 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 6 Mar 2015 18:29:22 -0800 Subject: [PATCH 14/14] different mixamo detection --- interface/src/ModelUploader.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index a2b729ad39..6740ea9634 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -314,14 +314,16 @@ void ModelUploader::populateBasicMapping(QVariantHash& mapping, QString filename // mixamo blendshapes - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will // be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file bool likelyMixamoFile = geometry.applicationName == "mixamo.com" || - (geometry.blendshapeChannelNames.contains("BrowsDown_Left") && + (geometry.blendshapeChannelNames.contains("Facial_Blends") && geometry.blendshapeChannelNames.contains("BrowsDown_Right") && geometry.blendshapeChannelNames.contains("MouthOpen") && - geometry.blendshapeChannelNames.contains("TongueUp") && - geometry.blendshapeChannelNames.contains("MouthWhistle_NarrowAdjust_Left") && - geometry.blendshapeChannelNames.contains("NoseScrunch_Left") && + geometry.blendshapeChannelNames.contains("Blink_Left") && + geometry.blendshapeChannelNames.contains("Blink_Right") && geometry.blendshapeChannelNames.contains("Squint_Right")); - + +qDebug() << "likelyMixamoFile:" << likelyMixamoFile; +qDebug() << "geometry.blendshapeChannelNames:" << geometry.blendshapeChannelNames; + if (!mapping.contains(BLENDSHAPE_FIELD) && likelyMixamoFile) { QVariantHash blendshapes; blendshapes.insertMulti("BrowsD_L", QVariantList() << "BrowsDown_Left" << 1.0);