diff --git a/examples/example/entities/particlesTest.js b/examples/example/entities/particlesTest.js index 48d5ca246a..88e1c8d919 100644 --- a/examples/example/entities/particlesTest.js +++ b/examples/example/entities/particlesTest.js @@ -15,7 +15,7 @@ var box, particles, particleExample = -1, - NUM_PARTICLE_EXAMPLES = 6, + NUM_PARTICLE_EXAMPLES = 7, PARTICLE_RADIUS = 0.04; function onClickDownOnEntity(entityID) { @@ -24,7 +24,7 @@ switch (particleExample) { case 0: - print("Simple animation"); + print("Simple emitter"); Entities.editEntity(particles, { velocitySpread: { x: 0.0, y: 0.0, z: 0.0 }, accelerationSpread: { x: 0.0, y: 0.0, z: 0.0 }, @@ -71,12 +71,24 @@ }); break; case 5: - print("Stop animation"); + print("Alpha 0.5"); Entities.editEntity(particles, { velocitySpread: { x: 0.0, y: 0.0, z: 0.0 }, accelerationSpread: { x: 0.0, y: 0.0, z: 0.0 }, radiusStart: PARTICLE_RADIUS, radiusFinish: PARTICLE_RADIUS, + alpha: 0.5, + animationIsPlaying: true + }); + break; + case 6: + print("Stop emitting"); + Entities.editEntity(particles, { + velocitySpread: { x: 0.0, y: 0.0, z: 0.0 }, + accelerationSpread: { x: 0.0, y: 0.0, z: 0.0 }, + radiusStart: PARTICLE_RADIUS, + radiusFinish: PARTICLE_RADIUS, + alpha: 1.0, animationIsPlaying: false }); break; diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 424ea2b810..be9c28a80d 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -172,16 +172,17 @@ uint32_t toRGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return ((uint32_t)r | (uint32_t)g << 8 | (uint32_t)b << 16 | (uint32_t)a << 24); } -class PositionAndRadius { +class ParticleDetails { public: - PositionAndRadius(glm::vec3 position, float radius) : position(position), radius(radius) { } + ParticleDetails(glm::vec3 position, float radius, uint32_t rgba) : position(position), radius(radius), rgba(rgba) { } glm::vec3 position; float radius; + uint32_t rgba; }; static glm::vec3 zSortAxis; -static bool zSort(const PositionAndRadius& rhs, const PositionAndRadius& lhs) { +static bool zSort(const ParticleDetails& rhs, const ParticleDetails& lhs) { return glm::dot(rhs.position, ::zSortAxis) > glm::dot(lhs.position, ::zSortAxis); } @@ -190,22 +191,21 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { return; } + // make a copy of each particle's details + std::vector particleDetails; + particleDetails.reserve(getLivingParticleCount()); auto xcolor = getXColor(); - auto alpha = (uint8_t)(glm::clamp(getLocalRenderAlpha(), 0.0f, 1.0f) * 255.0f); - auto rgba = toRGBA(xcolor.red, xcolor.green, xcolor.blue, alpha); - - // make a copy of each particle position and radius - std::vector positionsAndRadiuses; - positionsAndRadiuses.reserve(getLivingParticleCount()); for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) { - positionsAndRadiuses.push_back(PositionAndRadius(_particlePositions[i], _particleRadiuses[i])); + auto alpha = (uint8_t)(glm::clamp(_particleAlphas[i] * getLocalRenderAlpha(), 0.0f, 1.0f) * 255.0f); + auto rgba = toRGBA(xcolor.red, xcolor.green, xcolor.blue, alpha); + particleDetails.push_back(ParticleDetails(_particlePositions[i], _particleRadiuses[i], rgba)); } // sort particles back to front // NOTE: this is view frustum might be one frame out of date. auto frustum = AbstractViewStateInterface::instance()->getCurrentViewFrustum(); ::zSortAxis = frustum->getDirection(); - qSort(positionsAndRadiuses.begin(), positionsAndRadiuses.end(), zSort); + qSort(particleDetails.begin(), particleDetails.end(), zSort); // allocate vertices _vertices.clear(); @@ -213,14 +213,14 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { // build vertices from particle positions and radiuses const glm::vec3 up = frustum->getUp(); const glm::vec3 right = frustum->getRight(); - for (auto&& particle : positionsAndRadiuses) { + for (auto&& particle : particleDetails) { glm::vec3 upOffset = up * particle.radius; glm::vec3 rightOffset = right * particle.radius; // generate corners of quad aligned to face the camera. - _vertices.emplace_back(particle.position + rightOffset + upOffset, glm::vec2(1.0f, 1.0f), rgba); - _vertices.emplace_back(particle.position - rightOffset + upOffset, glm::vec2(0.0f, 1.0f), rgba); - _vertices.emplace_back(particle.position - rightOffset - upOffset, glm::vec2(0.0f, 0.0f), rgba); - _vertices.emplace_back(particle.position + rightOffset - upOffset, glm::vec2(1.0f, 0.0f), rgba); + _vertices.emplace_back(particle.position + rightOffset + upOffset, glm::vec2(1.0f, 1.0f), particle.rgba); + _vertices.emplace_back(particle.position - rightOffset + upOffset, glm::vec2(0.0f, 1.0f), particle.rgba); + _vertices.emplace_back(particle.position - rightOffset - upOffset, glm::vec2(0.0f, 0.0f), particle.rgba); + _vertices.emplace_back(particle.position + rightOffset - upOffset, glm::vec2(1.0f, 0.0f), particle.rgba); } render::PendingChanges pendingChanges; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index a07eaacdb9..f838b7a17e 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -55,6 +55,7 @@ CONSTRUCT_PROPERTY(script, ENTITY_ITEM_DEFAULT_SCRIPT), CONSTRUCT_PROPERTY(scriptTimestamp, ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP), CONSTRUCT_PROPERTY(collisionSoundURL, ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL), CONSTRUCT_PROPERTY(color, ), +CONSTRUCT_PROPERTY(alpha, ENTITY_ITEM_DEFAULT_ALPHA), CONSTRUCT_PROPERTY(modelURL, ""), CONSTRUCT_PROPERTY(compoundShapeURL, ""), CONSTRUCT_PROPERTY(animationURL, ""), @@ -331,6 +332,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_SCRIPT_TIMESTAMP, scriptTimestamp); CHECK_PROPERTY_CHANGE(PROP_COLLISION_SOUND_URL, collisionSoundURL); CHECK_PROPERTY_CHANGE(PROP_COLOR, color); + CHECK_PROPERTY_CHANGE(PROP_ALPHA, alpha); CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_URL, animationURL); @@ -445,6 +447,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(angularDamping); COPY_PROPERTY_TO_QSCRIPTVALUE(visible); COPY_PROPERTY_TO_QSCRIPTVALUE(color); + COPY_PROPERTY_TO_QSCRIPTVALUE(alpha); COPY_PROPERTY_TO_QSCRIPTVALUE(modelURL); COPY_PROPERTY_TO_QSCRIPTVALUE(compoundShapeURL); COPY_PROPERTY_TO_QSCRIPTVALUE(animationURL); @@ -578,6 +581,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(angularDamping, float, setAngularDamping); COPY_PROPERTY_FROM_QSCRIPTVALUE(visible, bool, setVisible); COPY_PROPERTY_FROM_QSCRIPTVALUE(color, xColor, setColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE(alpha, float, setAlpha); COPY_PROPERTY_FROM_QSCRIPTVALUE(modelURL, QString, setModelURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(compoundShapeURL, QString, setCompoundShapeURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(animationURL, QString, setAnimationURL); @@ -915,11 +919,11 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType::Value command, Ent APPEND_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, properties.getStrokeWidths()); } - APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID()); APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha()); } if (propertyCount > 0) { int endOfEntityItemData = packetData->getUncompressedByteOffset(); @@ -1192,6 +1196,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACTION_DATA, QByteArray, setActionData); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); return valid; } @@ -1248,6 +1253,7 @@ void EntityItemProperties::markAllChanged() { _nameChanged = true; _visibleChanged = true; _colorChanged = true; + _alphaChanged = true; _modelURLChanged = true; _compoundShapeURLChanged = true; _animationURLChanged = true; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 95f9757f20..520e2c1224 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -103,6 +103,7 @@ public: DEFINE_PROPERTY(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64); DEFINE_PROPERTY_REF(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString); DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, xColor); + DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float); DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString); DEFINE_PROPERTY_REF(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString); DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, AnimationURL, animationURL, QString); @@ -294,6 +295,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, ScriptTimestamp, scriptTimestamp, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, CollisionSoundURL, collisionSoundURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Color, color, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Alpha, alpha, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ModelURL, modelURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, CompoundShapeURL, compoundShapeURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationURL, animationURL, ""); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index 0cdfe73040..a4aca0b344 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -27,6 +27,7 @@ const QString ENTITY_ITEM_DEFAULT_USER_DATA = QString(""); const QString ENTITY_ITEM_DEFAULT_MARKETPLACE_ID = QString(""); const QUuid ENTITY_ITEM_DEFAULT_SIMULATOR_ID = QUuid(); +const float ENTITY_ITEM_DEFAULT_ALPHA = 1.0f; const float ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA = 1.0f; const float ENTITY_ITEM_DEFAULT_GLOW_LEVEL = 0.0f; const bool ENTITY_ITEM_DEFAULT_VISIBLE = true; diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 7ea471d851..bfef04ab57 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -151,6 +151,8 @@ enum EntityPropertyList { PROP_RADIUS_START, PROP_RADIUS_FINISH, + PROP_ALPHA, // Supported by some derived classes + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index ff534ff03b..0f8df954bf 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -85,6 +85,7 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte _textures(DEFAULT_TEXTURES), _texturesChangedFlag(false), _shapeType(SHAPE_TYPE_NONE), + _alpha(ENTITY_ITEM_DEFAULT_ALPHA), _particleLifetimes(DEFAULT_MAX_PARTICLES, 0.0f), _particlePositions(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)), _particleVelocities(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)), @@ -93,6 +94,7 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte _radiusStarts(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS), _radiusMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS), _radiusFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS), + _particleAlphas(DEFAULT_MAX_PARTICLES, ENTITY_ITEM_DEFAULT_ALPHA), _timeUntilNextEmit(0.0f), _particleHeadIndex(0), _particleTailIndex(0), @@ -155,6 +157,7 @@ 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(alpha, getAlpha); COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationIsPlaying, getAnimationIsPlaying); COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFrameIndex, getAnimationFrameIndex); COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFPS, getAnimationFPS); @@ -181,6 +184,7 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha); SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationIsPlaying, setAnimationIsPlaying); SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFrameIndex, setAnimationFrameIndex); SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFPS, setAnimationFPS); @@ -266,10 +270,11 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); } - if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLE_RADIUS_ADDITIONS) { + if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLE_RADIUS_PROPERTIES) { READ_ENTITY_PROPERTY(PROP_RADIUS_SPREAD, float, setRadiusSpread); READ_ENTITY_PROPERTY(PROP_RADIUS_START, float, setRadiusStart); READ_ENTITY_PROPERTY(PROP_RADIUS_FINISH, float, setRadiusFinish); + READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); } return bytesRead; @@ -298,6 +303,7 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea requestedProperties += PROP_RADIUS_SPREAD; requestedProperties += PROP_RADIUS_START; requestedProperties += PROP_RADIUS_FINISH; + requestedProperties += PROP_ALPHA; return requestedProperties; } @@ -329,6 +335,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_RADIUS_SPREAD, getRadiusSpread()); APPEND_ENTITY_PROPERTY(PROP_RADIUS_START, getRadiusStart()); APPEND_ENTITY_PROPERTY(PROP_RADIUS_FINISH, getRadiusFinish()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } bool ParticleEffectEntityItem::isAnimatingSomething() const { @@ -584,6 +591,9 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { integrateParticle(i, timeLeftInFrame); extendBounds(_particlePositions[i]); + // Alpha + _particleAlphas[i] = _alpha; + _particleTailIndex = (_particleTailIndex + 1) % _maxParticles; // overflow! move head forward by one. @@ -612,6 +622,7 @@ void ParticleEffectEntityItem::setMaxParticles(quint32 maxParticles) { _radiusStarts.resize(_maxParticles); _radiusMiddles.resize(_maxParticles); _radiusFinishes.resize(_maxParticles); + _particleAlphas.resize(_maxParticles); // effectively clear all particles and start emitting new ones from scratch. _particleHeadIndex = 0; diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index cac0af39a4..81d88e5a41 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -56,6 +56,9 @@ public: _color[BLUE_INDEX] = value.blue; } + void setAlpha(float alpha) { _alpha = alpha; } + float getAlpha() const { return _alpha; } + void updateShapeType(ShapeType type); virtual ShapeType getShapeType() const { return _shapeType; } @@ -160,6 +163,7 @@ protected: // the properties of this entity rgbColor _color; + float _alpha; quint32 _maxParticles; float _lifespan; float _emitRate; @@ -187,6 +191,7 @@ protected: QVector _radiusStarts; QVector _radiusMiddles; QVector _radiusFinishes; + QVector _particleAlphas; float _timeUntilNextEmit; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 8ac183da6f..3a2013d367 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -67,7 +67,7 @@ PacketVersion versionForPacketType(PacketType::Value packetType) { case EntityAdd: case EntityEdit: case EntityData: - return VERSION_ENTITIES_PARTICLE_RADIUS_ADDITIONS; + return VERSION_ENTITIES_PARTICLE_ALPHA_PROPERTIES; case AvatarData: return 13; default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index a68d3adbad..e63f7f410b 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -145,6 +145,7 @@ const PacketVersion VERSION_ENTITIES_POLYLINE = 37; const PacketVersion VERSION_OCTREE_CENTERED_ORIGIN = 38; const PacketVersion VERSION_ENTITIES_PARTICLE_MODIFICATIONS = 39; const PacketVersion VERSION_ENTITIES_POLYVOX_NEIGHBORS = 40; -const PacketVersion VERSION_ENTITIES_PARTICLE_RADIUS_ADDITIONS = 41; +const PacketVersion VERSION_ENTITIES_PARTICLE_RADIUS_PROPERTIES = 41; +const PacketVersion VERSION_ENTITIES_PARTICLE_ALPHA_PROPERTIES = 42; #endif // hifi_PacketHeaders_h