diff --git a/examples/example/entities/particlesTest.js b/examples/example/entities/particlesTest.js new file mode 100644 index 0000000000..0d1ea60005 --- /dev/null +++ b/examples/example/entities/particlesTest.js @@ -0,0 +1,170 @@ +// +// particlesTest.js +// examples/example/entities +// +// Created by David Rowe on 2 Sep 2015. +// Copyright 2015 High Fidelity, Inc. +// +// Click on the box entity to display different particle effects. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function () { + var box, + particles, + particleExample = -1, + NUM_PARTICLE_EXAMPLES = 11, + PARTICLE_RADIUS = 0.04; + + function onClickDownOnEntity(entityID) { + if (entityID === box || entityID === particles) { + particleExample = (particleExample + 1) % NUM_PARTICLE_EXAMPLES; + + switch (particleExample) { + case 0: + 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 }, + radiusSpread: 0.0, + animationIsPlaying: true + }); + break; + case 1: + print("Velocity spread"); + Entities.editEntity(particles, { + velocitySpread: { x: 0.1, y: 0.0, z: 0.1 } + }); + break; + case 2: + print("Acceleration spread"); + Entities.editEntity(particles, { + velocitySpread: { x: 0.0, y: 0.0, z: 0.0 }, + accelerationSpread: { x: 0.0, y: 0.1, z: 0.0 } + }); + break; + case 3: + print("Radius spread"); + Entities.editEntity(particles, { + accelerationSpread: { x: 0.0, y: 0.0, z: 0.0 }, + radiusSpread: 0.035 + }); + break; + case 4: + print("Radius start and finish"); + Entities.editEntity(particles, { + radiusSpread: 0.0, + radiusStart: 0.0, + radiusFinish: 0.0 + }); + break; + case 5: + print("Alpha 0.5"); + Entities.editEntity(particles, { + radiusStart: PARTICLE_RADIUS, + radiusFinish: PARTICLE_RADIUS, + alpha: 0.5 + }); + break; + case 6: + print("Alpha spread"); + Entities.editEntity(particles, { + alpha: 0.5, + alphaSpread: 0.5 + }); + break; + case 7: + print("Alpha start and finish"); + Entities.editEntity(particles, { + alphaSpread: 0.0, + alpha: 1.0, + alphaStart: 0.0, + alphaFinish: 0.0 + }); + break; + case 8: + print("Color spread"); + Entities.editEntity(particles, { + alpha: 1.0, + alphaStart: 1.0, + alphaFinish: 1.0, + color: { red: 128, green: 128, blue: 128 }, + colorSpread: { red: 128, green: 0, blue: 0 } + }); + break; + case 9: + print("Color start and finish"); + Entities.editEntity(particles, { + color: { red: 255, green: 255, blue: 255 }, + colorSpread: { red: 0, green: 0, blue: 0 }, + colorStart: { red: 255, green: 0, blue: 0 }, + colorFinish: { red: 0, green: 255, blue: 0 } + }); + break; + case 10: + print("Stop emitting"); + Entities.editEntity(particles, { + colorStart: { red: 255, green: 255, blue: 255 }, + colorFinish: { red: 255, green: 255, blue: 255 }, + animationIsPlaying: false + }); + break; + } + } + } + + function setUp() { + var spawnPoint = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation()))), + animation = { + fps: 30, + frameIndex: 0, + running: true, + firstFrame: 0, + lastFrame: 30, + loop: true + }; + + box = Entities.addEntity({ + type: "Box", + position: spawnPoint, + dimensions: { x: 0.3, y: 0.3, z: 0.3 }, + color: { red: 128, green: 128, blue: 128 }, + lifetime: 3600 // 1 hour; just in case + }); + + particles = Entities.addEntity({ + type: "ParticleEffect", + position: spawnPoint, + particleRadius: PARTICLE_RADIUS, + radiusSpread: 0.0, + emitRate: 2.0, + emitVelocity: { x: 0.0, y: 1.0, z: 0.0 }, + velocitySpread: { x: 0.0, y: 0.0, z: 0.0 }, + emitAcceleration: { x: 0.0, y: -0.3, z: 0.0 }, + accelerationSpread: { x: 0.0, y: 0.0, z: 0.0 }, + textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + color: { red: 255, green: 255, blue: 255 }, + lifespan: 5.0, + visible: true, + locked: false, + animationSettings: animation, + animationIsPlaying: false, + lifetime: 3600 // 1 hour; just in case + }); + + Entities.clickDownOnEntity.connect(onClickDownOnEntity); + + print("Click on the box to cycle through particle examples"); + } + + function tearDown() { + Entities.clickDownOnEntity.disconnect(onClickDownOnEntity); + Entities.deleteEntity(particles); + Entities.deleteEntity(box); + } + + setUp(); + Script.scriptEnding.connect(tearDown); +}()); \ No newline at end of file diff --git a/examples/particles.js b/examples/particles.js index fc1a936a72..ad232dd781 100644 --- a/examples/particles.js +++ b/examples/particles.js @@ -43,7 +43,7 @@ emitVelocity: {x: 0, y: 5, z: 0}, velocitySpread: {x: 2, y: 0, z: 2}, emitAcceleration: {x: 0, y: -9.8, z: 0}, - textures: "https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png", + textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", color: color, lifespan: 1.0, visible: true, diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index f683083ed1..40648fe26f 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -168,50 +168,59 @@ void RenderableParticleEffectEntityItem::update(const quint64& now) { updateRenderItem(); } -static glm::vec3 zSortAxis; -static bool zSort(const glm::vec3& rhs, const glm::vec3& lhs) { - return glm::dot(rhs, ::zSortAxis) > glm::dot(lhs, ::zSortAxis); -} - 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 ParticleDetails { +public: + 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 ParticleDetails& rhs, const ParticleDetails& lhs) { + return glm::dot(rhs.position, ::zSortAxis) > glm::dot(lhs.position, ::zSortAxis); +} + void RenderableParticleEffectEntityItem::updateRenderItem() { if (!_scene) { return; } - float particleRadius = getParticleRadius(); - 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 - std::vector positions; - positions.reserve(getLivingParticleCount()); + // make a copy of each particle's details + std::vector particleDetails; + particleDetails.reserve(getLivingParticleCount()); for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) { - positions.push_back(_particlePositions[i]); + auto xcolor = _particleColors[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(positions.begin(), positions.end(), zSort); + qSort(particleDetails.begin(), particleDetails.end(), zSort); // allocate vertices _vertices.clear(); - // build vertices from particle positions - const glm::vec3 upOffset = frustum->getUp() * particleRadius; - const glm::vec3 rightOffset = frustum->getRight() * particleRadius; - for (auto&& pos : positions) { + // build vertices from particle positions and radiuses + const glm::vec3 up = frustum->getUp(); + const glm::vec3 right = frustum->getRight(); + 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(pos + rightOffset + upOffset, glm::vec2(1.0f, 1.0f), rgba); - _vertices.emplace_back(pos - rightOffset + upOffset, glm::vec2(0.0f, 1.0f), rgba); - _vertices.emplace_back(pos - rightOffset - upOffset, glm::vec2(0.0f, 0.0f), rgba); - _vertices.emplace_back(pos + 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 89284b2123..eec872153d 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -55,6 +55,13 @@ 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(colorSpread, ParticleEffectEntityItem::DEFAULT_COLOR_SPREAD), +CONSTRUCT_PROPERTY(colorStart, ParticleEffectEntityItem::DEFAULT_COLOR), +CONSTRUCT_PROPERTY(colorFinish, ParticleEffectEntityItem::DEFAULT_COLOR), +CONSTRUCT_PROPERTY(alpha, ENTITY_ITEM_DEFAULT_ALPHA), +CONSTRUCT_PROPERTY(alphaSpread, ParticleEffectEntityItem::DEFAULT_ALPHA_SPREAD), +CONSTRUCT_PROPERTY(alphaStart, ParticleEffectEntityItem::DEFAULT_ALPHA_START), +CONSTRUCT_PROPERTY(alphaFinish, ParticleEffectEntityItem::DEFAULT_ALPHA_FINISH), CONSTRUCT_PROPERTY(modelURL, ""), CONSTRUCT_PROPERTY(compoundShapeURL, ""), CONSTRUCT_PROPERTY(animationURL, ""), @@ -89,6 +96,9 @@ CONSTRUCT_PROPERTY(velocitySpread, ParticleEffectEntityItem::DEFAULT_VELOCITY_SP CONSTRUCT_PROPERTY(emitAcceleration, ParticleEffectEntityItem::DEFAULT_EMIT_ACCELERATION), CONSTRUCT_PROPERTY(accelerationSpread, ParticleEffectEntityItem::DEFAULT_ACCELERATION_SPREAD), CONSTRUCT_PROPERTY(particleRadius, ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS), +CONSTRUCT_PROPERTY(radiusSpread, ParticleEffectEntityItem::DEFAULT_RADIUS_SPREAD), +CONSTRUCT_PROPERTY(radiusStart, ParticleEffectEntityItem::DEFAULT_RADIUS_START), +CONSTRUCT_PROPERTY(radiusFinish, ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH), CONSTRUCT_PROPERTY(marketplaceID, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID), CONSTRUCT_PROPERTY(keyLightColor, ZoneEntityItem::DEFAULT_KEYLIGHT_COLOR), CONSTRUCT_PROPERTY(keyLightIntensity, ZoneEntityItem::DEFAULT_KEYLIGHT_INTENSITY), @@ -328,6 +338,13 @@ 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_COLOR_SPREAD, colorSpread); + CHECK_PROPERTY_CHANGE(PROP_COLOR_START, colorStart); + CHECK_PROPERTY_CHANGE(PROP_COLOR_FINISH, colorFinish); + CHECK_PROPERTY_CHANGE(PROP_ALPHA, alpha); + CHECK_PROPERTY_CHANGE(PROP_ALPHA_SPREAD, alphaSpread); + CHECK_PROPERTY_CHANGE(PROP_ALPHA_START, alphaStart); + CHECK_PROPERTY_CHANGE(PROP_ALPHA_FINISH, alphaFinish); CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_URL, animationURL); @@ -362,6 +379,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_EMIT_ACCELERATION, emitAcceleration); CHECK_PROPERTY_CHANGE(PROP_ACCELERATION_SPREAD, accelerationSpread); CHECK_PROPERTY_CHANGE(PROP_PARTICLE_RADIUS, particleRadius); + CHECK_PROPERTY_CHANGE(PROP_RADIUS_SPREAD, radiusSpread); + CHECK_PROPERTY_CHANGE(PROP_RADIUS_START, radiusStart); + CHECK_PROPERTY_CHANGE(PROP_RADIUS_FINISH, radiusFinish); CHECK_PROPERTY_CHANGE(PROP_MARKETPLACE_ID, marketplaceID); CHECK_PROPERTY_CHANGE(PROP_NAME, name); CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_COLOR, keyLightColor); @@ -439,6 +459,13 @@ 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(colorSpread); + COPY_PROPERTY_TO_QSCRIPTVALUE(colorStart); + COPY_PROPERTY_TO_QSCRIPTVALUE(colorFinish); + COPY_PROPERTY_TO_QSCRIPTVALUE(alpha); + COPY_PROPERTY_TO_QSCRIPTVALUE(alphaSpread); + COPY_PROPERTY_TO_QSCRIPTVALUE(alphaStart); + COPY_PROPERTY_TO_QSCRIPTVALUE(alphaFinish); COPY_PROPERTY_TO_QSCRIPTVALUE(modelURL); COPY_PROPERTY_TO_QSCRIPTVALUE(compoundShapeURL); COPY_PROPERTY_TO_QSCRIPTVALUE(animationURL); @@ -471,6 +498,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(emitAcceleration); COPY_PROPERTY_TO_QSCRIPTVALUE(accelerationSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(particleRadius); + COPY_PROPERTY_TO_QSCRIPTVALUE(radiusSpread); + COPY_PROPERTY_TO_QSCRIPTVALUE(radiusStart); + COPY_PROPERTY_TO_QSCRIPTVALUE(radiusFinish); COPY_PROPERTY_TO_QSCRIPTVALUE(marketplaceID); COPY_PROPERTY_TO_QSCRIPTVALUE(name); COPY_PROPERTY_TO_QSCRIPTVALUE(collisionSoundURL); @@ -569,6 +599,13 @@ 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(colorSpread, xColor, setColorSpread); + COPY_PROPERTY_FROM_QSCRIPTVALUE(colorStart, xColor, setColorStart); + COPY_PROPERTY_FROM_QSCRIPTVALUE(colorFinish, xColor, setColorFinish); + COPY_PROPERTY_FROM_QSCRIPTVALUE(alpha, float, setAlpha); + COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaSpread, float, setAlphaSpread); + COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaStart, float, setAlphaStart); + COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaFinish, float, setAlphaFinish); COPY_PROPERTY_FROM_QSCRIPTVALUE(modelURL, QString, setModelURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(compoundShapeURL, QString, setCompoundShapeURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(animationURL, QString, setAnimationURL); @@ -600,6 +637,9 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(emitAcceleration, glmVec3, setEmitAcceleration); COPY_PROPERTY_FROM_QSCRIPTVALUE(accelerationSpread, glmVec3, setAccelerationSpread); COPY_PROPERTY_FROM_QSCRIPTVALUE(particleRadius, float, setParticleRadius); + COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusSpread, float, setRadiusSpread); + COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusStart, float, setRadiusStart); + COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusFinish, float, setRadiusFinish); COPY_PROPERTY_FROM_QSCRIPTVALUE(marketplaceID, QString, setMarketplaceID); COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName); COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionSoundURL, QString, setCollisionSoundURL); @@ -850,7 +890,15 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType::Value command, Ent APPEND_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, properties.getEmitAcceleration()); APPEND_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, properties.getAccelerationSpread()); APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, properties.getParticleRadius()); - + APPEND_ENTITY_PROPERTY(PROP_RADIUS_SPREAD, properties.getRadiusSpread()); + APPEND_ENTITY_PROPERTY(PROP_RADIUS_START, properties.getRadiusStart()); + APPEND_ENTITY_PROPERTY(PROP_RADIUS_FINISH, properties.getRadiusFinish()); + APPEND_ENTITY_PROPERTY(PROP_COLOR_SPREAD, properties.getColorSpread()); + APPEND_ENTITY_PROPERTY(PROP_COLOR_START, properties.getColorStart()); + APPEND_ENTITY_PROPERTY(PROP_COLOR_FINISH, properties.getColorFinish()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, properties.getAlphaSpread()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, properties.getAlphaStart()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, properties.getAlphaFinish()); } if (properties.getType() == EntityTypes::Zone) { @@ -901,11 +949,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(); @@ -1126,6 +1174,15 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ACCELERATION, glm::vec3, setEmitAcceleration); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACCELERATION_SPREAD, glm::vec3, setAccelerationSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_RADIUS, float, setParticleRadius); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_SPREAD, float, setRadiusSpread); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_START, float, setRadiusStart); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_FINISH, float, setRadiusFinish); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_SPREAD, xColor, setColorSpread); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_START, xColor, setColorStart); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_FINISH, xColor, setColorFinish); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_SPREAD, float, setAlphaSpread); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_START, float, setAlphaStart); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_FINISH, float, setAlphaFinish); } if (properties.getType() == EntityTypes::Zone) { @@ -1175,6 +1232,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; } @@ -1231,6 +1289,7 @@ void EntityItemProperties::markAllChanged() { _nameChanged = true; _visibleChanged = true; _colorChanged = true; + _alphaChanged = true; _modelURLChanged = true; _compoundShapeURLChanged = true; _animationURLChanged = true; @@ -1264,6 +1323,15 @@ void EntityItemProperties::markAllChanged() { _emitAccelerationChanged = true; _accelerationSpreadChanged = true; _particleRadiusChanged = true; + _radiusSpreadChanged = true; + _radiusStartChanged = true; + _radiusFinishChanged = true; + _colorSpreadChanged = true; + _colorStartChanged = true; + _colorFinishChanged = true; + _alphaSpreadChanged = true; + _alphaStartChanged = true; + _alphaFinishChanged = true; _marketplaceIDChanged = true; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 942dc3edb1..2731ab19d5 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -103,6 +103,13 @@ 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_REF(PROP_COLOR_SPREAD, ColorSpread, colorSpread, xColor); + DEFINE_PROPERTY_REF(PROP_COLOR_START, ColorStart, colorStart, xColor); + DEFINE_PROPERTY_REF(PROP_COLOR_FINISH, ColorFinish, colorFinish, xColor); + DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float); + DEFINE_PROPERTY(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float); + DEFINE_PROPERTY(PROP_ALPHA_START, AlphaStart, alphaStart, float); + DEFINE_PROPERTY(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, 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); @@ -136,6 +143,9 @@ public: DEFINE_PROPERTY(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, glm::vec3); DEFINE_PROPERTY(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, glm::vec3); DEFINE_PROPERTY(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float); + DEFINE_PROPERTY(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float); + DEFINE_PROPERTY(PROP_RADIUS_START, RadiusStart, radiusStart, float); + DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float); DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString); DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor); DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float); @@ -291,6 +301,13 @@ 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, ColorSpread, colorSpread, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ColorStart, colorStart, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ColorFinish, colorFinish, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, Alpha, alpha, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AlphaSpread, alphaSpread, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AlphaStart, alphaStart, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AlphaFinish, alphaFinish, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ModelURL, modelURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, CompoundShapeURL, compoundShapeURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationURL, animationURL, ""); @@ -322,6 +339,9 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitAcceleration, emitAcceleration, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, AccelerationSpread, accelerationSpread, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParticleRadius, particleRadius, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, RadiusSpread, radiusSpread, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, RadiusStart, radiusStart, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, RadiusFinish, radiusFinish, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, MarketplaceID, marketplaceID, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundMode, backgroundMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, ""); 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 d929bdbef5..d4f880ed8f 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -146,6 +146,21 @@ enum EntityPropertyList { PROP_Y_P_NEIGHBOR_ID, // used by PolyVox PROP_Z_P_NEIGHBOR_ID, // used by PolyVox + // Used by particles + PROP_RADIUS_SPREAD, + PROP_RADIUS_START, + PROP_RADIUS_FINISH, + + PROP_ALPHA, // Supported by some derived classes + + //Used by particles + PROP_COLOR_SPREAD, + PROP_COLOR_START, + PROP_COLOR_FINISH, + PROP_ALPHA_SPREAD, + PROP_ALPHA_START, + PROP_ALPHA_FINISH, + //////////////////////////////////////////////////////////////////////////////////////////////////// // 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 7dab825adc..dce4445336 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -43,6 +43,11 @@ #include "ParticleEffectEntityItem.h" const xColor ParticleEffectEntityItem::DEFAULT_COLOR = { 255, 255, 255 }; +const xColor ParticleEffectEntityItem::DEFAULT_COLOR_SPREAD = { 0, 0, 0 }; +const float ParticleEffectEntityItem::DEFAULT_ALPHA = 1.0f; +const float ParticleEffectEntityItem::DEFAULT_ALPHA_SPREAD = 0.0f; +const float ParticleEffectEntityItem::DEFAULT_ALPHA_START = DEFAULT_ALPHA; +const float ParticleEffectEntityItem::DEFAULT_ALPHA_FINISH = DEFAULT_ALPHA; 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; @@ -54,6 +59,9 @@ const glm::vec3 ParticleEffectEntityItem::DEFAULT_VELOCITY_SPREAD(3.0f, 0.0f, 3. const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_ACCELERATION(0.0f, -9.8f, 0.0f); const glm::vec3 ParticleEffectEntityItem::DEFAULT_ACCELERATION_SPREAD(0.0f, 0.0f, 0.0f); const float ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS = 0.025f; +const float ParticleEffectEntityItem::DEFAULT_RADIUS_SPREAD = 0.0f; +const float ParticleEffectEntityItem::DEFAULT_RADIUS_START = DEFAULT_PARTICLE_RADIUS; +const float ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH = DEFAULT_PARTICLE_RADIUS; const QString ParticleEffectEntityItem::DEFAULT_TEXTURES = ""; @@ -72,16 +80,42 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte _emitAcceleration(DEFAULT_EMIT_ACCELERATION), _accelerationSpread(DEFAULT_ACCELERATION_SPREAD), _particleRadius(DEFAULT_PARTICLE_RADIUS), + _radiusSpread(DEFAULT_RADIUS_SPREAD), + _radiusStart(DEFAULT_RADIUS_START), + _radiusFinish(DEFAULT_RADIUS_FINISH), _lastAnimated(usecTimestampNow()), _animationLoop(), _animationSettings(), _textures(DEFAULT_TEXTURES), _texturesChangedFlag(false), _shapeType(SHAPE_TYPE_NONE), + _colorSpread(DEFAULT_COLOR_SPREAD), + _colorStart(DEFAULT_COLOR), + _colorFinish(DEFAULT_COLOR), + _isColorStartInitialized(false), + _isColorFinishInitialized(false), + _alpha(DEFAULT_ALPHA), + _alphaSpread(DEFAULT_ALPHA_SPREAD), + _alphaStart(DEFAULT_ALPHA_START), + _alphaFinish(DEFAULT_ALPHA_FINISH), + _isAlphaStartInitialized(false), + _isAlphaFinishInitialized(false), _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)), _particleAccelerations(DEFAULT_MAX_PARTICLES, glm::vec3(0.0f, 0.0f, 0.0f)), + _particleRadiuses(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS), + _radiusStarts(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS), + _radiusMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS), + _radiusFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_PARTICLE_RADIUS), + _particleColors(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR), + _colorStarts(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR), + _colorMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR), + _colorFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_COLOR), + _particleAlphas(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA), + _alphaStarts(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA), + _alphaMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA), + _alphaFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA), _timeUntilNextEmit(0.0f), _particleHeadIndex(0), _particleTailIndex(0), @@ -97,10 +131,6 @@ ParticleEffectEntityItem::~ParticleEffectEntityItem() { } -void ParticleEffectEntityItem::setLifespan(float lifespan) { - _lifespan = lifespan; -} - void ParticleEffectEntityItem::setEmitVelocity(const glm::vec3& emitVelocity) { _emitVelocity = emitVelocity; computeAndUpdateDimensions(); @@ -111,7 +141,6 @@ void ParticleEffectEntityItem::setVelocitySpread(const glm::vec3& velocitySpread computeAndUpdateDimensions(); } - void ParticleEffectEntityItem::setEmitAcceleration(const glm::vec3& emitAcceleration) { _emitAcceleration = emitAcceleration; computeAndUpdateDimensions(); @@ -122,10 +151,6 @@ void ParticleEffectEntityItem::setAccelerationSpread(const glm::vec3& accelerati computeAndUpdateDimensions(); } -void ParticleEffectEntityItem::setParticleRadius(float particleRadius) { - _particleRadius = particleRadius; -} - void ParticleEffectEntityItem::computeAndUpdateDimensions() { const float time = _lifespan * 1.1f; // add 10% extra time to account for incremental timer accumulation error @@ -153,6 +178,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); @@ -163,9 +189,19 @@ EntityItemProperties ParticleEffectEntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifespan, getLifespan); COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitRate, getEmitRate); COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitVelocity, getEmitVelocity); - + COPY_ENTITY_PROPERTY_TO_PROPERTIES(velocitySpread, getVelocitySpread); COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitAcceleration, getEmitAcceleration); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(accelerationSpread, getAccelerationSpread); COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleRadius, getParticleRadius); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(radiusSpread, getRadiusSpread); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(radiusStart, getRadiusStart); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(radiusFinish, getRadiusFinish); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(colorSpread, getColorSpread); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(colorStart, getColorStart); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(colorFinish, getColorFinish); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaSpread, getAlphaSpread); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaStart, getAlphaStart); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaFinish, getAlphaFinish); COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); return properties; @@ -175,6 +211,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); @@ -185,11 +222,20 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifespan, setLifespan); SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitRate, setEmitRate); SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitVelocity, setEmitVelocity); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocitySpread, setVelocitySpread); SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitAcceleration, setEmitAcceleration); SET_ENTITY_PROPERTY_FROM_PROPERTIES(accelerationSpread, setAccelerationSpread); SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleRadius, setParticleRadius); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(radiusSpread, setRadiusSpread); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(radiusStart, setRadiusStart); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(radiusFinish, setRadiusFinish); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(colorSpread, setColorSpread); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(colorStart, setColorStart); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(colorFinish, setColorFinish); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaSpread, setAlphaSpread); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaStart, setAlphaStart); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaFinish, setAlphaFinish); SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocitySpread, setVelocitySpread); if (somethingChanged) { bool wantDebug = false; @@ -257,6 +303,22 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); } + 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); + } + + if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLE_COLOR_PROPERTIES) { + READ_ENTITY_PROPERTY(PROP_COLOR_SPREAD, xColor, setColorSpread); + READ_ENTITY_PROPERTY(PROP_COLOR_START, xColor, setColorStart); + READ_ENTITY_PROPERTY(PROP_COLOR_FINISH, xColor, setColorFinish); + READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); + READ_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, float, setAlphaSpread); + READ_ENTITY_PROPERTY(PROP_ALPHA_START, float, setAlphaStart); + READ_ENTITY_PROPERTY(PROP_ALPHA_FINISH, float, setAlphaFinish); + } + return bytesRead; } @@ -280,6 +342,16 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea requestedProperties += PROP_PARTICLE_RADIUS; requestedProperties += PROP_TEXTURES; requestedProperties += PROP_VELOCITY_SPREAD; + requestedProperties += PROP_RADIUS_SPREAD; + requestedProperties += PROP_RADIUS_START; + requestedProperties += PROP_RADIUS_FINISH; + requestedProperties += PROP_COLOR_SPREAD; + requestedProperties += PROP_COLOR_START; + requestedProperties += PROP_COLOR_FINISH; + requestedProperties += PROP_ALPHA; + requestedProperties += PROP_ALPHA_SPREAD; + requestedProperties += PROP_ALPHA_START; + requestedProperties += PROP_ALPHA_FINISH; return requestedProperties; } @@ -308,6 +380,16 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, getParticleRadius()); APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures()); APPEND_ENTITY_PROPERTY(PROP_VELOCITY_SPREAD, getVelocitySpread()); + 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_COLOR_SPREAD, getColorSpread()); + APPEND_ENTITY_PROPERTY(PROP_COLOR_START, getColorStart()); + APPEND_ENTITY_PROPERTY(PROP_COLOR_FINISH, getColorFinish()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, getAlphaSpread()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, getAlphaStart()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, getAlphaFinish()); } bool ParticleEffectEntityItem::isAnimatingSomething() const { @@ -477,6 +559,27 @@ QString ParticleEffectEntityItem::getAnimationSettings() const { return jsonByteString; } +void ParticleEffectEntityItem::updateRadius(quint32 index, float age) { + _particleRadiuses[index] = interpolate(_radiusStarts[index], _radiusMiddles[index], _radiusFinishes[index], age); +} + +void ParticleEffectEntityItem::updateColor(quint32 index, float age) { + _particleColors[index].red = + (int)glm::clamp(interpolate(_colorStarts[index].red, _colorMiddles[index].red, _colorFinishes[index].red, age), + 0.0f, 255.0f); + _particleColors[index].green = + (int)glm::clamp(interpolate(_colorStarts[index].green, _colorMiddles[index].green, _colorFinishes[index].green, age), + 0.0f, 255.0f); + _particleColors[index].blue = + (int)glm::clamp(interpolate(_colorStarts[index].blue, _colorMiddles[index].blue, _colorFinishes[index].blue, age), + 0.0f, 255.0f); +} + +void ParticleEffectEntityItem::updateAlpha(quint32 index, float age) { + _particleAlphas[index] = glm::clamp(interpolate(_alphaStarts[index], _alphaMiddles[index], _alphaFinishes[index], age), + 0.0f, 1.0f); +} + void ParticleEffectEntityItem::extendBounds(const glm::vec3& point) { _particleMinBound.x = glm::min(_particleMinBound.x, point.x); _particleMinBound.y = glm::min(_particleMinBound.y, point.y); @@ -509,12 +612,16 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { _particleHeadIndex = (_particleHeadIndex + 1) % _maxParticles; } else { + float age = (1.0f - _particleLifetimes[i] / _lifespan); // 0.0 .. 1.0 + updateRadius(i, age); + updateColor(i, age); + updateAlpha(i, age); integrateParticle(i, deltaTime); extendBounds(_particlePositions[i]); } } - // emit new particles, but only if animaiton is playing + // emit new particles, but only if animation is playing if (getAnimationIsPlaying()) { float timeLeftInFrame = deltaTime; @@ -527,12 +634,24 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { quint32 i = _particleTailIndex; _particleLifetimes[i] = _lifespan; - + // Radius + if (_radiusSpread == 0.0f) { + _radiusStarts[i] = getRadiusStart(); + _radiusMiddles[i] =_particleRadius; + _radiusFinishes[i] = getRadiusFinish(); + } else { + float spreadMultiplier = 1.0f + (2.0f * randFloat() - 1) * _radiusSpread / _particleRadius; + _radiusStarts[i] = spreadMultiplier * getRadiusStart(); + _radiusMiddles[i] = spreadMultiplier * _particleRadius; + _radiusFinishes[i] = spreadMultiplier * getRadiusFinish(); + } + updateRadius(i, 0.0f); + + // Velocity and acceleration glm::vec3 spreadOffset; spreadOffset.x = -_velocitySpread.x + randFloat() * (_velocitySpread.x * 2.0f); spreadOffset.y = -_velocitySpread.y + randFloat() * (_velocitySpread.y * 2.0f); spreadOffset.z = -_velocitySpread.z + randFloat() * (_velocitySpread.z * 2.0f); - // set initial conditions _particlePositions[i] = getPosition(); @@ -547,6 +666,48 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { integrateParticle(i, timeLeftInFrame); extendBounds(_particlePositions[i]); + // Color + if (_colorSpread == xColor{ 0, 0, 0 }) { + _colorStarts[i] = getColorStart(); + _colorMiddles[i] = getXColor(); + _colorFinishes[i] = getColorFinish(); + } else { + xColor startColor = getColorStart(); + xColor middleColor = getXColor(); + xColor finishColor = getColorFinish(); + + float spread = 2.0f * randFloat() - 1.0f; + float spreadMultiplierRed = 1.0f + spread * (float)_colorSpread.red / (float)middleColor.red; + float spreadMultiplierGreen = 1.0f + spread * (float)_colorSpread.green / (float)middleColor.green; + float spreadMultiplierBlue = 1.0f + spread * (float)_colorSpread.blue / (float)middleColor.blue; + + _colorStarts[i].red = (int)glm::clamp(spreadMultiplierRed * (float)startColor.red, 0.0f, 255.0f); + _colorStarts[i].green = (int)glm::clamp(spreadMultiplierGreen * (float)startColor.green, 0.0f, 255.0f); + _colorStarts[i].blue = (int)glm::clamp(spreadMultiplierBlue * (float)startColor.blue, 0.0f, 255.0f); + + _colorMiddles[i].red = (int)glm::clamp(spreadMultiplierRed * (float)middleColor.red, 0.0f, 255.0f); + _colorMiddles[i].green = (int)glm::clamp(spreadMultiplierGreen * (float)middleColor.green, 0.0f, 255.0f); + _colorMiddles[i].blue = (int)glm::clamp(spreadMultiplierBlue * (float)middleColor.blue, 0.0f, 255.0f); + + _colorFinishes[i].red = (int)glm::clamp(spreadMultiplierRed * (float)finishColor.red, 0.0f, 255.0f); + _colorFinishes[i].green = (int)glm::clamp(spreadMultiplierGreen * (float)finishColor.green, 0.0f, 255.0f); + _colorFinishes[i].blue = (int)glm::clamp(spreadMultiplierBlue * (float)finishColor.blue, 0.0f, 255.0f); + } + updateColor(i, 0.0f); + + // Alpha + if (_alphaSpread == 0.0f) { + _alphaStarts[i] = getAlphaStart(); + _alphaMiddles[i] = _alpha; + _alphaFinishes[i] = getAlphaFinish(); + } else { + float spreadMultiplier = 1.0f + (2.0f * randFloat() - 1) * _alphaSpread / _alpha; + _alphaStarts[i] = spreadMultiplier * getAlphaStart(); + _alphaMiddles[i] = spreadMultiplier * _alpha; + _alphaFinishes[i] = spreadMultiplier * getAlphaFinish(); + } + updateAlpha(i, 0.0f); + _particleTailIndex = (_particleTailIndex + 1) % _maxParticles; // overflow! move head forward by one. @@ -571,8 +732,20 @@ void ParticleEffectEntityItem::setMaxParticles(quint32 maxParticles) { _particleLifetimes.resize(_maxParticles); _particlePositions.resize(_maxParticles); _particleVelocities.resize(_maxParticles); + _particleRadiuses.resize(_maxParticles); + _radiusStarts.resize(_maxParticles); + _radiusMiddles.resize(_maxParticles); + _radiusFinishes.resize(_maxParticles); + _particleColors.resize(_maxParticles); + _colorStarts.resize(_maxParticles); + _colorMiddles.resize(_maxParticles); + _colorFinishes.resize(_maxParticles); + _particleAlphas.resize(_maxParticles); + _alphaStarts.resize(_maxParticles); + _alphaMiddles.resize(_maxParticles); + _alphaFinishes.resize(_maxParticles); - // effectivly clear all particles and start emitting new ones from scratch. + // effectively clear all particles and start emitting new ones from scratch. _particleHeadIndex = 0; _particleTailIndex = 0; _timeUntilNextEmit = 0.0f; @@ -587,3 +760,42 @@ quint32 ParticleEffectEntityItem::getLivingParticleCount() const { return (_maxParticles - _particleHeadIndex) + _particleTailIndex; } } + +float ParticleEffectEntityItem::cubicInterpolate(float y0, float y1, float y2, float y3, float u) { + float a0, a1, a2, a3, uu; + uu = u * u; + a0 = y3 - y2 - y0 + y1; + a1 = y0 - y1 - a0; + a2 = y2 - y0; + a3 = y1; + + return (a0 * u * uu + a1 * uu + a2 * u + a3); +} + +float ParticleEffectEntityItem::interpolate(float start, float middle, float finish, float age) { + float y0, y1, y2, y3, u; + + if (age <= 0.5f) { + if (start == middle) { + return middle; + } + + y1 = start; + y2 = middle; + y3 = finish; + y0 = 2.0f * y1 - y2; + u = 2.0f * age; + } else { + if (middle == finish) { + return middle; + } + + y0 = start; + y1 = middle; + y2 = finish; + y3 = 2.0f * y2 - y1; + u = 2.0f * age - 1.0f; + } + + return cubicInterpolate(y0, y1, y2, y3, u); +} diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 4ed9216e85..4e3c4b57b9 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -56,6 +56,36 @@ public: _color[BLUE_INDEX] = value.blue; } + bool _isColorStartInitialized; + void setColorStart(const xColor& colorStart) { _colorStart = colorStart; _isColorStartInitialized = true; } + xColor getColorStart() const { return _isColorStartInitialized ? _colorStart : getXColor(); } + + bool _isColorFinishInitialized; + void setColorFinish(const xColor& colorFinish) { _colorFinish = colorFinish; _isColorFinishInitialized = true; } + xColor getColorFinish() const { return _isColorFinishInitialized ? _colorFinish : getXColor(); } + + static const xColor DEFAULT_COLOR_SPREAD; + void setColorSpread(const xColor& colorSpread) { _colorSpread = colorSpread; } + xColor getColorSpread() const { return _colorSpread; } + + static const float DEFAULT_ALPHA; + void setAlpha(float alpha) { _alpha = alpha; } + float getAlpha() const { return _alpha; } + + static const float DEFAULT_ALPHA_START; + bool _isAlphaStartInitialized; + void setAlphaStart(float alphaStart) { _alphaStart = alphaStart; _isAlphaStartInitialized = true; } + float getAlphaStart() const { return _isAlphaStartInitialized ? _alphaStart : _alpha; } + + static const float DEFAULT_ALPHA_FINISH; + bool _isAlphaFinishInitialized; + void setAlphaFinish(float alphaFinish) { _alphaFinish = alphaFinish; _isAlphaFinishInitialized = true; } + float getAlphaFinish() const { return _isAlphaFinishInitialized ? _alphaFinish : _alpha; } + + static const float DEFAULT_ALPHA_SPREAD; + void setAlphaSpread(float alphaSpread) { _alphaSpread = alphaSpread; } + float getAlphaSpread() const { return _alphaSpread; } + void updateShapeType(ShapeType type); virtual ShapeType getShapeType() const { return _shapeType; } @@ -91,7 +121,7 @@ public: quint32 getMaxParticles() const { return _maxParticles; } static const float DEFAULT_LIFESPAN; - void setLifespan(float lifespan); + void setLifespan(float lifespan) { _lifespan = lifespan; } float getLifespan() const { return _lifespan; } static const float DEFAULT_EMIT_RATE; @@ -102,12 +132,10 @@ public: void setEmitVelocity(const glm::vec3& emitVelocity); const glm::vec3& getEmitVelocity() const { return _emitVelocity; } - static const glm::vec3 DEFAULT_VELOCITY_SPREAD; void setVelocitySpread(const glm::vec3& velocitySpread); const glm::vec3& getVelocitySpread() const { return _velocitySpread; } - static const glm::vec3 DEFAULT_EMIT_ACCELERATION; void setEmitAcceleration(const glm::vec3& emitAcceleration); const glm::vec3& getEmitAcceleration() const { return _emitAcceleration; } @@ -117,9 +145,23 @@ public: const glm::vec3& getAccelerationSpread() const { return _accelerationSpread; } static const float DEFAULT_PARTICLE_RADIUS; - void setParticleRadius(float particleRadius); + void setParticleRadius(float particleRadius) { _particleRadius = particleRadius; } float getParticleRadius() const { return _particleRadius; } - + + static const float DEFAULT_RADIUS_START; + bool _isRadiusStartInitialized; + void setRadiusStart(float radiusStart) { _radiusStart = radiusStart; _isRadiusStartInitialized = true; } + float getRadiusStart() const { return _isRadiusStartInitialized ? _radiusStart : _particleRadius; } + + static const float DEFAULT_RADIUS_FINISH; + bool _isRadiusFinishInitialized; + void setRadiusFinish(float radiusFinish) { _radiusFinish = radiusFinish; _isRadiusFinishInitialized = true; } + float getRadiusFinish() const { return _isRadiusFinishInitialized ? _radiusFinish : _particleRadius; } + + static const float DEFAULT_RADIUS_SPREAD; + void setRadiusSpread(float radiusSpread) { _radiusSpread = radiusSpread; } + float getRadiusSpread() const { return _radiusSpread; } + void computeAndUpdateDimensions(); @@ -141,12 +183,22 @@ protected: bool isAnimatingSomething() const; void stepSimulation(float deltaTime); + void updateRadius(quint32 index, float age); + void updateColor(quint32 index, float age); + void updateAlpha(quint32 index, float age); void extendBounds(const glm::vec3& point); void integrateParticle(quint32 index, float deltaTime); quint32 getLivingParticleCount() const; // the properties of this entity rgbColor _color; + xColor _colorStart; + xColor _colorFinish; + xColor _colorSpread; + float _alpha; + float _alphaStart; + float _alphaFinish; + float _alphaSpread; quint32 _maxParticles; float _lifespan; float _emitRate; @@ -155,6 +207,9 @@ protected: glm::vec3 _emitAcceleration; glm::vec3 _accelerationSpread; float _particleRadius; + float _radiusStart; + float _radiusFinish; + float _radiusSpread; quint64 _lastAnimated; AnimationLoop _animationLoop; QString _animationSettings; @@ -167,9 +222,22 @@ protected: QVector _particlePositions; QVector _particleVelocities; QVector _particleAccelerations; + QVector _particleRadiuses; + QVector _radiusStarts; + QVector _radiusMiddles; + QVector _radiusFinishes; + QVector _particleColors; + QVector _colorStarts; + QVector _colorMiddles; + QVector _colorFinishes; + QVector _particleAlphas; + QVector _alphaStarts; + QVector _alphaMiddles; + QVector _alphaFinishes; + float _timeUntilNextEmit; - // particle arrays are a ring buffer, use these indicies + // particle arrays are a ring buffer, use these indices // to keep track of the living particles. quint32 _particleHeadIndex; quint32 _particleTailIndex; @@ -177,6 +245,10 @@ protected: // bounding volume glm::vec3 _particleMaxBound; glm::vec3 _particleMinBound; + +private: + float cubicInterpolate(float y0, float y1, float y2, float y3, float u); + float interpolate(float start, float middle, float finish, float age); }; #endif // hifi_ParticleEffectEntityItem_h diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index e9eca409e6..bb120ce198 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_POLYVOX_NEIGHBORS; + return VERSION_ENTITIES_PARTICLE_COLOR_PROPERTIES; case AvatarData: return 13; default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index d22ee59cad..eb48bc8019 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -145,5 +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_PROPERTIES = 41; +const PacketVersion VERSION_ENTITIES_PARTICLE_COLOR_PROPERTIES = 42; #endif // hifi_PacketHeaders_h