diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index edbfb6158c..520069abdf 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -23,43 +23,73 @@ #include "untextured_particle_frag.h" #include "textured_particle_vert.h" #include "textured_particle_frag.h" -#include "textured_particle_alpha_discard_frag.h" -class ParticlePayload { + +class ParticlePayloadData { public: - typedef render::Payload Payload; - typedef Payload::DataPointer Pointer; - typedef RenderableParticleEffectEntityItem::Vertex Vertex; + static const size_t VERTEX_PER_PARTICLE = 4; - ParticlePayload(EntityItemPointer entity) : - _entity(entity), - _vertexFormat(std::make_shared()), - _vertexBuffer(std::make_shared()), - _indexBuffer(std::make_shared()) { - - _vertexFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element::VEC3F_XYZ, 0); - _vertexFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), offsetof(Vertex, uv)); - _vertexFormat->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::COLOR_RGBA_32, offsetof(Vertex, rgba)); + template + struct InterpolationData { + T start; + T middle; + T finish; + T spread; + }; + struct ParticleUniforms { + InterpolationData radius; + InterpolationData color; // rgba + float lifespan; + }; + + struct ParticlePrimitive { + ParticlePrimitive(glm::vec3 xyzIn, glm::vec2 uvIn) : xyz(xyzIn), uv(uvIn) {} + glm::vec3 xyz; // Position + glm::vec2 uv; // Lifetime + seed + }; + + using Payload = render::Payload; + using Pointer = Payload::DataPointer; + using PipelinePointer = gpu::PipelinePointer; + using FormatPointer = gpu::Stream::FormatPointer; + using BufferPointer = gpu::BufferPointer; + using TexturePointer = gpu::TexturePointer; + using Format = gpu::Stream::Format; + using Buffer = gpu::Buffer; + using BufferView = gpu::BufferView; + using ParticlePrimitives = std::vector; + + ParticlePayloadData() { + ParticleUniforms uniforms; + _uniformBuffer = std::make_shared(sizeof(ParticleUniforms), (const gpu::Byte*) &uniforms); + + _vertexFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element::VEC3F_XYZ, + offsetof(ParticlePrimitive, xyz), gpu::Stream::PER_INSTANCE); + _vertexFormat->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::VEC2F_UV, + offsetof(ParticlePrimitive, uv), gpu::Stream::PER_INSTANCE); } - void setPipeline(gpu::PipelinePointer pipeline) { _pipeline = pipeline; } - const gpu::PipelinePointer& getPipeline() const { return _pipeline; } + void setPipeline(PipelinePointer pipeline) { _pipeline = pipeline; } + const PipelinePointer& getPipeline() const { return _pipeline; } const Transform& getModelTransform() const { return _modelTransform; } void setModelTransform(const Transform& modelTransform) { _modelTransform = modelTransform; } const AABox& getBound() const { return _bound; } - void setBound(AABox& bound) { _bound = bound; } + void setBound(const AABox& bound) { _bound = bound; } - gpu::BufferPointer getVertexBuffer() { return _vertexBuffer; } - const gpu::BufferPointer& getVertexBuffer() const { return _vertexBuffer; } + BufferPointer getParticleBuffer() { return _particleBuffer; } + const BufferPointer& getParticleBuffer() const { return _particleBuffer; } + + const ParticleUniforms& getParticleUniforms() const { return _uniformBuffer.get(); } + ParticleUniforms& editParticleUniforms() { return _uniformBuffer.edit(); } - gpu::BufferPointer getIndexBuffer() { return _indexBuffer; } - const gpu::BufferPointer& getIndexBuffer() const { return _indexBuffer; } - - void setTexture(gpu::TexturePointer texture) { _texture = texture; } - const gpu::TexturePointer& getTexture() const { return _texture; } + void setTexture(TexturePointer texture) { _texture = texture; } + const TexturePointer& getTexture() const { return _texture; } + bool getVisibleFlag() const { return _visibleFlag; } + void setVisibleFlag(bool visibleFlag) { _visibleFlag = visibleFlag; } + void render(RenderArgs* args) const { assert(_pipeline); @@ -71,30 +101,29 @@ public: } batch.setModelTransform(_modelTransform); + batch.setUniformBuffer(0, _uniformBuffer); batch.setInputFormat(_vertexFormat); - batch.setInputBuffer(0, _vertexBuffer, 0, sizeof(Vertex)); - batch.setIndexBuffer(gpu::UINT16, _indexBuffer, 0); + batch.setInputBuffer(0, _particleBuffer, 0, sizeof(ParticlePrimitive)); - auto numIndices = _indexBuffer->getSize() / sizeof(uint16_t); - batch.drawIndexed(gpu::TRIANGLES, numIndices); + auto numParticles = _particleBuffer->getSize() / sizeof(ParticlePrimitive); + batch.drawInstanced(numParticles, gpu::TRIANGLE_STRIP, VERTEX_PER_PARTICLE); } - - EntityItemPointer _entity; protected: Transform _modelTransform; AABox _bound; - gpu::PipelinePointer _pipeline; - gpu::Stream::FormatPointer _vertexFormat; - gpu::BufferPointer _vertexBuffer; - gpu::BufferPointer _indexBuffer; - gpu::TexturePointer _texture; + PipelinePointer _pipeline; + FormatPointer _vertexFormat { std::make_shared() }; + BufferPointer _particleBuffer { std::make_shared() }; + BufferView _uniformBuffer; + TexturePointer _texture; + bool _visibleFlag = true; }; namespace render { template <> - const ItemKey payloadGetKey(const ParticlePayload::Pointer& payload) { - if (payload->_entity->getVisible()) { + const ItemKey payloadGetKey(const ParticlePayloadData::Pointer& payload) { + if (payload->getVisibleFlag()) { return ItemKey::Builder::transparentShape(); } else { return ItemKey::Builder().withInvisible().build(); @@ -102,16 +131,15 @@ namespace render { } template <> - const Item::Bound payloadGetBound(const ParticlePayload::Pointer& payload) { + const Item::Bound payloadGetBound(const ParticlePayloadData::Pointer& payload) { return payload->getBound(); } template <> - void payloadRender(const ParticlePayload::Pointer& payload, RenderArgs* args) { - if (payload->_entity->getVisible()) { + void payloadRender(const ParticlePayloadData::Pointer& payload, RenderArgs* args) { + if (payload->getVisibleFlag()) { payload->render(args); } - } } @@ -119,7 +147,7 @@ namespace render { EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer entity{ new RenderableParticleEffectEntityItem(entityID) }; + auto entity = std::make_shared(entityID); entity->setProperties(properties); return entity; } @@ -127,7 +155,7 @@ EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const EntityItemID& entityItemID) : ParticleEffectEntityItem(entityItemID) { // lazy creation of particle system pipeline - if (!_untexturedPipeline && !_texturedPipeline) { + if (!_untexturedPipeline || !_texturedPipeline) { createPipelines(); } } @@ -135,18 +163,15 @@ RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const Ent bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges) { - - auto particlePayload = - std::shared_ptr(new ParticlePayload(getThisPointer())); - particlePayload->setPipeline(_untexturedPipeline); - _renderItemId = scene->allocateID(); - auto renderData = ParticlePayload::Pointer(particlePayload); - auto renderPayload = render::PayloadPointer(new ParticlePayload::Payload(renderData)); + _scene = scene; + _renderItemId = _scene->allocateID(); + auto particlePayloadData = std::make_shared(); + particlePayloadData->setPipeline(_untexturedPipeline); + auto renderPayload = std::make_shared(particlePayloadData); render::Item::Status::Getters statusGetters; makeEntityItemStatusGetters(getThisPointer(), statusGetters); renderPayload->addStatusGetters(statusGetters); pendingChanges.resetItem(_renderItemId, renderPayload); - _scene = scene; return true; } @@ -174,141 +199,71 @@ void RenderableParticleEffectEntityItem::update(const quint64& now) { updateRenderItem(); } -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; } - - // make a copy of each particle's details - std::vector particleDetails; - particleDetails.reserve(getLivingParticleCount()); - for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) { - 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)); + if (!getVisible()) { + render::PendingChanges pendingChanges; + pendingChanges.updateItem(_renderItemId, [](ParticlePayloadData& payload) { + payload.setVisibleFlag(false); + }); + + _scene->enqueuePendingChanges(pendingChanges); + return; } - - // sort particles back to front - // NOTE: this is view frustum might be one frame out of date. - auto frustum = AbstractViewStateInterface::instance()->getCurrentViewFrustum(); + using ParticleUniforms = ParticlePayloadData::ParticleUniforms; + using ParticlePrimitive = ParticlePayloadData::ParticlePrimitive; + using ParticlePrimitives = ParticlePayloadData::ParticlePrimitives; - // No need to sort if we're doing additive blending - if (_additiveBlending != true) { - ::zSortAxis = frustum->getDirection(); - qSort(particleDetails.begin(), particleDetails.end(), zSort); - } - - - - // allocate vertices - _vertices.clear(); - - // build vertices from particle positions and radiuses - glm::vec3 dir = frustum->getDirection(); - for (auto&& particle : particleDetails) { - glm::vec3 right = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), dir)); - glm::vec3 up = glm::normalize(glm::cross(right, dir)); - - 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), 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); + // Fill in Uniforms structure + ParticleUniforms particleUniforms; + particleUniforms.radius.start = getRadiusStart(); + particleUniforms.radius.middle = getParticleRadius(); + particleUniforms.radius.finish = getRadiusFinish(); + particleUniforms.radius.spread = getRadiusSpread(); + particleUniforms.color.start = toGlm(getColorStart(), getAlphaStart()); + particleUniforms.color.middle = toGlm(getXColor(), getAlpha()); + particleUniforms.color.finish = toGlm(getColorFinish(), getAlphaFinish()); + particleUniforms.color.spread = toGlm(getColorSpread(), getAlphaSpread()); + particleUniforms.lifespan = getLifespan(); + + // Build particle primitives + auto particlePrimitives = std::make_shared(); + particlePrimitives->reserve(_particles.size()); // Reserve space + for (auto& particle : _particles) { + particlePrimitives->emplace_back(particle.position, glm::vec2(particle.lifetime, particle.seed)); } + + auto bounds = getAABox(); + auto position = getPosition(); + auto rotation = getRotation(); + Transform transform; + transform.setTranslation(position); + transform.setRotation(rotation); render::PendingChanges pendingChanges; - pendingChanges.updateItem(_renderItemId, [this](ParticlePayload& payload) { - // update vertex buffer - auto vertexBuffer = payload.getVertexBuffer(); - size_t numBytes = sizeof(Vertex) * _vertices.size(); - + pendingChanges.updateItem(_renderItemId, [=](ParticlePayloadData& payload) { + payload.setVisibleFlag(true); + + // Update particle uniforms + memcpy(&payload.editParticleUniforms(), &particleUniforms, sizeof(ParticleUniforms)); + + // Update particle buffer + auto particleBuffer = payload.getParticleBuffer(); + size_t numBytes = sizeof(ParticlePrimitive) * particlePrimitives->size(); + particleBuffer->resize(numBytes); if (numBytes == 0) { - vertexBuffer->resize(0); - auto indexBuffer = payload.getIndexBuffer(); - indexBuffer->resize(0); return; } + memcpy(particleBuffer->editData(), particlePrimitives->data(), numBytes); - vertexBuffer->resize(numBytes); - gpu::Byte* data = vertexBuffer->editData(); - memcpy(data, &(_vertices[0]), numBytes); + // Update transform and bounds + payload.setModelTransform(transform); + payload.setBound(bounds); - // FIXME, don't update index buffer if num particles has not changed. - // update index buffer - auto indexBuffer = payload.getIndexBuffer(); - const size_t NUM_VERTS_PER_PARTICLE = 4; - const size_t NUM_INDICES_PER_PARTICLE = 6; - auto numQuads = (_vertices.size() / NUM_VERTS_PER_PARTICLE); - numBytes = sizeof(uint16_t) * numQuads * NUM_INDICES_PER_PARTICLE; - indexBuffer->resize(numBytes); - data = indexBuffer->editData(); - auto indexPtr = reinterpret_cast(data); - for (size_t i = 0; i < numQuads; ++i) { - indexPtr[i * NUM_INDICES_PER_PARTICLE + 0] = i * NUM_VERTS_PER_PARTICLE + 0; - indexPtr[i * NUM_INDICES_PER_PARTICLE + 1] = i * NUM_VERTS_PER_PARTICLE + 1; - indexPtr[i * NUM_INDICES_PER_PARTICLE + 2] = i * NUM_VERTS_PER_PARTICLE + 3; - indexPtr[i * NUM_INDICES_PER_PARTICLE + 3] = i * NUM_VERTS_PER_PARTICLE + 1; - indexPtr[i * NUM_INDICES_PER_PARTICLE + 4] = i * NUM_VERTS_PER_PARTICLE + 2; - indexPtr[i * NUM_INDICES_PER_PARTICLE + 5] = i * NUM_VERTS_PER_PARTICLE + 3; - } - - // update transform - glm::quat rot = getRotation(); - glm::vec3 pos = getPosition(); - Transform t; - t.setRotation(rot); - payload.setModelTransform(t); - - // transform _particleMinBound and _particleMaxBound corners into world coords - glm::vec3 d = _particleMaxBound - _particleMinBound; - const size_t NUM_BOX_CORNERS = 8; - glm::vec3 corners[NUM_BOX_CORNERS] = { - pos + rot * (_particleMinBound + glm::vec3(0.0f, 0.0f, 0.0f)), - pos + rot * (_particleMinBound + glm::vec3(d.x, 0.0f, 0.0f)), - pos + rot * (_particleMinBound + glm::vec3(0.0f, d.y, 0.0f)), - pos + rot * (_particleMinBound + glm::vec3(d.x, d.y, 0.0f)), - pos + rot * (_particleMinBound + glm::vec3(0.0f, 0.0f, d.z)), - pos + rot * (_particleMinBound + glm::vec3(d.x, 0.0f, d.z)), - pos + rot * (_particleMinBound + glm::vec3(0.0f, d.y, d.z)), - pos + rot * (_particleMinBound + glm::vec3(d.x, d.y, d.z)) - }; - glm::vec3 min(FLT_MAX, FLT_MAX, FLT_MAX); - glm::vec3 max = -min; - for (size_t i = 0; i < NUM_BOX_CORNERS; i++) { - min.x = std::min(min.x, corners[i].x); - min.y = std::min(min.y, corners[i].y); - min.z = std::min(min.z, corners[i].z); - max.x = std::max(max.x, corners[i].x); - max.y = std::max(max.y, corners[i].y); - max.z = std::max(max.z, corners[i].z); - } - AABox bound(min, max - min); - payload.setBound(bound); - - bool textured = _texture && _texture->isLoaded(); - if (textured) { + if (_texture && _texture->isLoaded()) { payload.setTexture(_texture->getGPUTexture()); payload.setPipeline(_texturedPipeline); } else { @@ -321,46 +276,29 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { } void RenderableParticleEffectEntityItem::createPipelines() { - bool writeToDepthBuffer = false; - gpu::State::BlendArg destinationColorBlendArg; - if (_additiveBlending) { - destinationColorBlendArg = gpu::State::ONE; - } - else { - destinationColorBlendArg = gpu::State::INV_SRC_ALPHA; - writeToDepthBuffer = true; - } if (!_untexturedPipeline) { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, writeToDepthBuffer, gpu::LESS_EQUAL); - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, - destinationColorBlendArg, gpu::State::FACTOR_ALPHA, - gpu::State::BLEND_OP_ADD, gpu::State::ONE); + state->setDepthTest(true, false, gpu::LESS_EQUAL); + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + auto vertShader = gpu::Shader::createVertex(std::string(untextured_particle_vert)); auto fragShader = gpu::Shader::createPixel(std::string(untextured_particle_frag)); + auto program = gpu::Shader::createProgram(vertShader, fragShader); _untexturedPipeline = gpu::Pipeline::create(program, state); } if (!_texturedPipeline) { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); - + state->setDepthTest(true, false, gpu::LESS_EQUAL); + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - bool writeToDepthBuffer = !_additiveBlending; - state->setDepthTest(true, writeToDepthBuffer, gpu::LESS_EQUAL); - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, - destinationColorBlendArg, gpu::State::FACTOR_ALPHA, - gpu::State::BLEND_OP_ADD, gpu::State::ONE); auto vertShader = gpu::Shader::createVertex(std::string(textured_particle_vert)); - gpu::ShaderPointer fragShader; - if (_additiveBlending) { - fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag)); - } - else { - //If we are sorting and have no additive blending, we want to discard pixels with low alpha to avoid inter-particle entity artifacts - fragShader = gpu::Shader::createPixel(std::string(textured_particle_alpha_discard_frag)); - } + auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag)); + auto program = gpu::Shader::createProgram(vertShader, fragShader); _texturedPipeline = gpu::Pipeline::create(program, state); } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index b024ddb991..0c1cc50a98 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -16,7 +16,7 @@ #include "RenderableEntityItem.h" class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem { -friend class ParticlePayload; + friend class ParticlePayloadData; public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderableParticleEffectEntityItem(const EntityItemID& entityItemID); @@ -29,23 +29,14 @@ public: virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges) override; protected: - render::ItemID _renderItemId; - - struct Vertex { - Vertex(glm::vec3 xyzIn, glm::vec2 uvIn, uint32_t rgbaIn) : xyz(xyzIn), uv(uvIn), rgba(rgbaIn) {} - glm::vec3 xyz; - glm::vec2 uv; - uint32_t rgba; - }; - void createPipelines(); - - std::vector _vertices; + + render::ScenePointer _scene; + render::ItemID _renderItemId; + + NetworkTexturePointer _texture; gpu::PipelinePointer _untexturedPipeline; gpu::PipelinePointer _texturedPipeline; - - render::ScenePointer _scene; - NetworkTexturePointer _texture; }; diff --git a/libraries/entities-renderer/src/textured_particle.slf b/libraries/entities-renderer/src/textured_particle.slf index 58a3103323..e139c7cc01 100644 --- a/libraries/entities-renderer/src/textured_particle.slf +++ b/libraries/entities-renderer/src/textured_particle.slf @@ -11,12 +11,11 @@ uniform sampler2D colorMap; -in vec4 _color; -in vec2 _texCoord0; +in vec4 varColor; +in vec2 varTexcoord; out vec4 outFragColor; void main(void) { - vec4 color = texture(colorMap, _texCoord0); - outFragColor = color * _color; + outFragColor = texture(colorMap, varTexcoord.xy) * varColor; } diff --git a/libraries/entities-renderer/src/textured_particle.slv b/libraries/entities-renderer/src/textured_particle.slv index c741af559d..1e9275ec72 100644 --- a/libraries/entities-renderer/src/textured_particle.slv +++ b/libraries/entities-renderer/src/textured_particle.slv @@ -10,21 +10,84 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include gpu/Inputs.slh@> - <@include gpu/Transform.slh@> <$declareStandardTransform()$> -out vec4 _color; -out vec2 _texCoord0; +struct Radii { + float start; + float middle; + float finish; + float spread; +}; +struct Colors { + vec4 start; + vec4 middle; + vec4 finish; + vec4 spread; +}; + +struct ParticleUniforms { + Radii radius; + Colors color; + float lifespan; +}; + +uniform particleBuffer { + ParticleUniforms particle; +}; + +in vec3 inPosition; +in vec2 inColor; // This is actual Lifetime + Seed + +out vec4 varColor; +out vec2 varTexcoord; + +const int NUM_VERTICES_PER_PARTICLE = 4; +const vec4 UNIT_QUAD[NUM_VERTICES_PER_PARTICLE] = vec4[NUM_VERTICES_PER_PARTICLE]( + vec4(-1.0, -1.0, 0.0, 0.0), + vec4(1.0, -1.0, 0.0, 0.0), + vec4(-1.0, 1.0, 0.0, 0.0), + vec4(1.0, 1.0, 0.0, 0.0) +); + +float bezierInterpolate(float y1, float y2, float y3, float u) { + // https://en.wikipedia.org/wiki/Bezier_curve + return (1.0 - u) * (1.0 - u) * y1 + 2.0 * (1.0 - u) * u * y2 + u * u * y3; +} + +vec4 interpolate3Vec4(vec4 y1, vec4 y2, vec4 y3, float u) { + return vec4(bezierInterpolate(y1.x, y2.x, y3.x, u), + bezierInterpolate(y1.y, y2.y, y3.y, u), + bezierInterpolate(y1.z, y2.z, y3.z, u), + bezierInterpolate(y1.w, y2.w, y3.w, u)); +} + void main(void) { - // pass along the color & uvs to fragment shader - _color = inColor; - _texCoord0 = inTexCoord0.xy; - TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, inPosition, gl_Position)$> + + // Which icon are we dealing with ? + int particleID = gl_VertexID / NUM_VERTICES_PER_PARTICLE; + // Which quad vertex pos? + int twoTriID = gl_VertexID - particleID * NUM_VERTICES_PER_PARTICLE; + + // Particle properties + float age = inColor.x / particle.lifespan; + float seed = inColor.y; + + // Pass the texcoord and the z texcoord is representing the texture icon + varTexcoord = vec2((UNIT_QUAD[twoTriID].xy + 1.0) * 0.5); + varColor = interpolate3Vec4(particle.color.start, particle.color.middle, particle.color.finish, age); + + // anchor point in eye space + float radius = bezierInterpolate(particle.radius.start, particle.radius.middle, particle.radius.finish , age); + vec4 quadPos = radius * UNIT_QUAD[twoTriID]; + + vec4 anchorPoint; + <$transformModelToEyePos(cam, obj, inPosition, anchorPoint)$> + + vec4 eyePos = anchorPoint + quadPos; + <$transformEyeToClipPos(cam, eyePos, gl_Position)$> } diff --git a/libraries/entities-renderer/src/textured_particle_alpha_discard.slf b/libraries/entities-renderer/src/textured_particle_alpha_discard.slf deleted file mode 100644 index 389744449a..0000000000 --- a/libraries/entities-renderer/src/textured_particle_alpha_discard.slf +++ /dev/null @@ -1,25 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// fragment shader -// -// 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 -// - -uniform sampler2D colorMap; - -in vec4 _color; -in vec2 _texCoord0; - -out vec4 outFragColor; - -void main(void) { - vec4 color = texture(colorMap, _texCoord0); - if (color.a < 0.1) { - discard; - } - outFragColor = color * _color; -} diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 33e7d1ca90..5600c85650 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -40,9 +40,6 @@ #include "EntityScriptingInterface.h" #include "ParticleEffectEntityItem.h" -const glm::vec3 X_AXIS = glm::vec3(1.0f, 0.0f, 0.0f); -const glm::vec3 Z_AXIS = glm::vec3(0.0f, 0.0f, 1.0f); - const float SCRIPT_MAXIMUM_PI = 3.1416f; // Round up so that reasonable property values work const xColor ParticleEffectEntityItem::DEFAULT_COLOR = { 255, 255, 255 }; @@ -66,8 +63,8 @@ const float ParticleEffectEntityItem::DEFAULT_EMIT_SPEED = 5.0f; const float ParticleEffectEntityItem::MINIMUM_EMIT_SPEED = 0.0f; const float ParticleEffectEntityItem::MAXIMUM_EMIT_SPEED = 1000.0f; // Approx mach 3 const float ParticleEffectEntityItem::DEFAULT_SPEED_SPREAD = 1.0f; -const glm::quat ParticleEffectEntityItem::DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, X_AXIS); // Vertical -const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_DIMENSIONS = glm::vec3(0.0f, 0.0f, 0.0f); // Emit from point +const glm::quat ParticleEffectEntityItem::DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_X); // Vertical +const glm::vec3 ParticleEffectEntityItem::DEFAULT_EMIT_DIMENSIONS = Vectors::ZERO; // Emit from point const float ParticleEffectEntityItem::MINIMUM_EMIT_DIMENSION = 0.0f; const float ParticleEffectEntityItem::MAXIMUM_EMIT_DIMENSION = (float)TREE_SCALE; const float ParticleEffectEntityItem::DEFAULT_EMIT_RADIUS_START = 1.0f; // Emit from surface (when emitDimensions > 0) @@ -106,36 +103,12 @@ EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID // our non-pure virtual subclass for now... ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID), - _lastSimulated(usecTimestampNow()), - _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), - _particleMaxBound(glm::vec3(1.0f, 1.0f, 1.0f)), - _particleMinBound(glm::vec3(-1.0f, -1.0f, -1.0f)) , - _additiveBlending(DEFAULT_ADDITIVE_BLENDING) + _lastSimulated(usecTimestampNow()) { - _type = EntityTypes::ParticleEffect; setColor(DEFAULT_COLOR); } -ParticleEffectEntityItem::~ParticleEffectEntityItem() { -} - - void ParticleEffectEntityItem::setAlpha(float alpha) { if (MINIMUM_ALPHA <= alpha && alpha <= MAXIMUM_ALPHA) { _alpha = alpha; @@ -310,8 +283,8 @@ void ParticleEffectEntityItem::setRadiusSpread(float radiusSpread) { void ParticleEffectEntityItem::computeAndUpdateDimensions() { const float time = _lifespan * 1.1f; // add 10% extra time to account for incremental timer accumulation error - glm::vec3 velocity = _emitSpeed * (_emitOrientation * Z_AXIS); - glm::vec3 velocitySpread = _speedSpread * (_emitOrientation * Z_AXIS); + glm::vec3 velocity = _emitSpeed * (_emitOrientation * Vectors::UNIT_Z); + glm::vec3 velocitySpread = _speedSpread * (_emitOrientation * Vectors::UNIT_Z); glm::vec3 maxVelocity = glm::abs(velocity) + velocitySpread; glm::vec3 maxAccleration = glm::abs(_acceleration) + _accelerationSpread; @@ -578,7 +551,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, bool ParticleEffectEntityItem::isEmittingParticles() const { // keep emitting if there are particles still alive. - return (getIsEmitting() || getLivingParticleCount() > 0); + return (getIsEmitting() || !_particles.empty()); } bool ParticleEffectEntityItem::needsToCallUpdate() const { @@ -613,62 +586,25 @@ void ParticleEffectEntityItem::updateShapeType(ShapeType type) { } } -void ParticleEffectEntityItem::updateRadius(quint32 index, float age) { - _particleRadiuses[index] = Interpolate::interpolate3Points(_radiusStarts[index], _radiusMiddles[index], - _radiusFinishes[index], age); -} - -void ParticleEffectEntityItem::updateColor(quint32 index, float age) { - _particleColors[index].red = (int)Interpolate::interpolate3Points(_colorStarts[index].red, _colorMiddles[index].red, - _colorFinishes[index].red, age); - _particleColors[index].green = (int)Interpolate::interpolate3Points(_colorStarts[index].green, _colorMiddles[index].green, - _colorFinishes[index].green, age); - _particleColors[index].blue = (int)Interpolate::interpolate3Points(_colorStarts[index].blue, _colorMiddles[index].blue, - _colorFinishes[index].blue, age); -} - -void ParticleEffectEntityItem::updateAlpha(quint32 index, float age) { - _particleAlphas[index] = Interpolate::interpolate3Points(_alphaStarts[index], _alphaMiddles[index], - _alphaFinishes[index], age); -} - -void ParticleEffectEntityItem::extendBounds(const glm::vec3& point) { - _particleMinBound.x = glm::min(_particleMinBound.x, point.x); - _particleMinBound.y = glm::min(_particleMinBound.y, point.y); - _particleMinBound.z = glm::min(_particleMinBound.z, point.z); - _particleMaxBound.x = glm::max(_particleMaxBound.x, point.x); - _particleMaxBound.y = glm::max(_particleMaxBound.y, point.y); - _particleMaxBound.z = glm::max(_particleMaxBound.z, point.z); -} - -void ParticleEffectEntityItem::integrateParticle(quint32 index, float deltaTime) { - glm::vec3 accel = _particleAccelerations[index]; - glm::vec3 atSquared = (0.5f * deltaTime * deltaTime) * accel; - glm::vec3 at = accel * deltaTime; - _particlePositions[index] += _particleVelocities[index] * deltaTime + atSquared; - _particleVelocities[index] += at; +void ParticleEffectEntityItem::integrateParticle(Particle& particle, float deltaTime) { + glm::vec3 atSquared = (0.5f * deltaTime * deltaTime) * particle.acceleration; + glm::vec3 at = particle.acceleration * deltaTime; + particle.position += particle.velocity * deltaTime + atSquared; + particle.velocity += at; } void ParticleEffectEntityItem::stepSimulation(float deltaTime) { - - _particleMinBound = glm::vec3(-1.0f, -1.0f, -1.0f); - _particleMaxBound = glm::vec3(1.0f, 1.0f, 1.0f); - // update particles between head and tail - for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) { - _particleLifetimes[i] += deltaTime; + for (Particle& particle : _particles) { + particle.lifetime += deltaTime; // if particle has died. - if (_particleLifetimes[i] >= _lifespan || _lifespan < EPSILON) { + if (particle.lifetime >= _lifespan) { // move head forward - _particleHeadIndex = (_particleHeadIndex + 1) % _maxParticles; + _particles.pop_front(); } else { - float age = _particleLifetimes[i] / _lifespan; // 0.0 .. 1.0 - updateRadius(i, age); - updateColor(i, age); - updateAlpha(i, age); - integrateParticle(i, deltaTime); - extendBounds(_particlePositions[i]); + // Otherwise update it + integrateParticle(particle, deltaTime); } } @@ -677,192 +613,104 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { float timeLeftInFrame = deltaTime; while (_timeUntilNextEmit < timeLeftInFrame) { - - timeLeftInFrame -= _timeUntilNextEmit; - _timeUntilNextEmit = 1.0f / _emitRate; - - // emit a new particle at tail index. - quint32 i = _particleTailIndex; - _particleLifetimes[i] = 0.0f; - - // Radius - if (_radiusSpread == 0.0f) { - _radiusStarts[i] = getRadiusStart(); - _radiusMiddles[i] =_particleRadius; - _radiusFinishes[i] = getRadiusFinish(); - } else { - float spreadMultiplier; - if (_particleRadius > 0.0f) { - spreadMultiplier = 1.0f + randFloatInRange(-1.0f, 1.0f) * _radiusSpread / _particleRadius; - } else { - spreadMultiplier = 1.0f; - } - _radiusStarts[i] = - glm::clamp(spreadMultiplier * getRadiusStart(), MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS); - _radiusMiddles[i] = - glm::clamp(spreadMultiplier * _particleRadius, MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS); - _radiusFinishes[i] = - glm::clamp(spreadMultiplier * getRadiusFinish(), MINIMUM_PARTICLE_RADIUS, MAXIMUM_PARTICLE_RADIUS); - } - updateRadius(i, 0.0f); - - // Position, velocity, and acceleration - if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) { - // Emit along z-axis from position - _particlePositions[i] = getPosition(); - _particleVelocities[i] = - (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * Z_AXIS); - _particleAccelerations[i] = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread; - - } else { - // Emit around point or from ellipsoid - // - Distribute directions evenly around point - // - Distribute points relatively evenly over ellipsoid surface - // - Distribute points relatively evenly within ellipsoid volume - - float elevationMinZ = sin(PI_OVER_TWO - _polarFinish); - float elevationMaxZ = sin(PI_OVER_TWO - _polarStart); - float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat()); - - float azimuth; - if (_azimuthFinish >= _azimuthStart) { - azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat(); - } else { - azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * randFloat(); - } - - glm::vec3 emitDirection; - - if (_emitDimensions == glm::vec3()) { - // Point - emitDirection = glm::quat(glm::vec3(PI_OVER_TWO - elevation, 0.0f, azimuth)) * Z_AXIS; - - _particlePositions[i] = getPosition(); - } else { - // Ellipsoid - float radiusScale = 1.0f; - if (_emitRadiusStart < 1.0f) { - float emitRadiusStart = glm::max(_emitRadiusStart, EPSILON); // Avoid math complications at center - float randRadius = - emitRadiusStart + randFloatInRange(0.0f, MAXIMUM_EMIT_RADIUS_START - emitRadiusStart); - radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f); - } - - glm::vec3 radiuses = radiusScale * 0.5f * _emitDimensions; - float x = radiuses.x * glm::cos(elevation) * glm::cos(azimuth); - float y = radiuses.y * glm::cos(elevation) * glm::sin(azimuth); - float z = radiuses.z * glm::sin(elevation); - glm::vec3 emitPosition = glm::vec3(x, y, z); - emitDirection = glm::normalize(glm::vec3( - radiuses.x > 0.0f ? x / (radiuses.x * radiuses.x) : 0.0f, - radiuses.y > 0.0f ? y / (radiuses.y * radiuses.y) : 0.0f, - radiuses.z > 0.0f ? z / (radiuses.z * radiuses.z) : 0.0f - )); - - _particlePositions[i] = getPosition() + _emitOrientation * emitPosition; - } - - _particleVelocities[i] = - (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * emitDirection); - _particleAccelerations[i] = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread; - } - 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 = randFloatInRange(-1.0f, 1.0f); - float spreadMultiplierRed = - middleColor.red > 0 ? 1.0f + spread * (float)_colorSpread.red / (float)middleColor.red : 1.0f; - float spreadMultiplierGreen = - middleColor.green > 0 ? 1.0f + spread * (float)_colorSpread.green / (float)middleColor.green : 1.0f; - float spreadMultiplierBlue = - middleColor.blue > 0 ? 1.0f + spread * (float)_colorSpread.blue / (float)middleColor.blue : 1.0f; - - _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 + randFloatInRange(-1.0f, 1.0f) * _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. // because the case of head == tail indicates an empty array, not a full one. // This can drop an existing older particle, but this is by design, newer particles are a higher priority. - if (_particleTailIndex == _particleHeadIndex) { - _particleHeadIndex = (_particleHeadIndex + 1) % _maxParticles; + if (_particles.size() >= _maxParticles) { + _particles.pop_front(); } + + // emit a new particle at tail index. + _particles.push_back(createParticle()); + auto particle = _particles.back(); + particle.lifetime += timeLeftInFrame; + + // Initialize it + integrateParticle(particle, deltaTime); + + // Advance in frame + timeLeftInFrame -= _timeUntilNextEmit; + _timeUntilNextEmit = 1.0f / _emitRate; } _timeUntilNextEmit -= timeLeftInFrame; } } +ParticleEffectEntityItem::Particle ParticleEffectEntityItem::createParticle() { + Particle particle; + + particle.seed = randFloatInRange(0.0f, 1.0f); + + // Position, velocity, and acceleration + if (_polarStart == 0.0f && _polarFinish == 0.0f && _emitDimensions.z == 0.0f) { + // Emit along z-axis from position + particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * Vectors::UNIT_Z); + particle.acceleration = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread; + + } else { + // Emit around point or from ellipsoid + // - Distribute directions evenly around point + // - Distribute points relatively evenly over ellipsoid surface + // - Distribute points relatively evenly within ellipsoid volume + + float elevationMinZ = sin(PI_OVER_TWO - _polarFinish); + float elevationMaxZ = sin(PI_OVER_TWO - _polarStart); + float elevation = asin(elevationMinZ + (elevationMaxZ - elevationMinZ) * randFloat()); + + float azimuth; + if (_azimuthFinish >= _azimuthStart) { + azimuth = _azimuthStart + (_azimuthFinish - _azimuthStart) * randFloat(); + } else { + azimuth = _azimuthStart + (TWO_PI + _azimuthFinish - _azimuthStart) * randFloat(); + } + + glm::vec3 emitDirection; + + if (_emitDimensions == Vectors::ZERO) { + // Point + emitDirection = glm::quat(glm::vec3(PI_OVER_TWO - elevation, 0.0f, azimuth)) * Vectors::UNIT_Z; + } else { + // Ellipsoid + float radiusScale = 1.0f; + if (_emitRadiusStart < 1.0f) { + float emitRadiusStart = glm::max(_emitRadiusStart, EPSILON); // Avoid math complications at center + float randRadius = + emitRadiusStart + randFloatInRange(0.0f, MAXIMUM_EMIT_RADIUS_START - emitRadiusStart); + radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f); + } + + glm::vec3 radii = radiusScale * 0.5f * _emitDimensions; + float x = radii.x * glm::cos(elevation) * glm::cos(azimuth); + float y = radii.y * glm::cos(elevation) * glm::sin(azimuth); + float z = radii.z * glm::sin(elevation); + glm::vec3 emitPosition = glm::vec3(x, y, z); + emitDirection = glm::normalize(glm::vec3( + radii.x > 0.0f ? x / (radii.x * radii.x) : 0.0f, + radii.y > 0.0f ? y / (radii.y * radii.y) : 0.0f, + radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f + )); + + particle.position = _emitOrientation * emitPosition; + } + + particle.velocity = (_emitSpeed + randFloatInRange(-1.0f, 1.0f) * _speedSpread) * (_emitOrientation * emitDirection); + particle.acceleration = _emitAcceleration + randFloatInRange(-1.0f, 1.0f) * _accelerationSpread; + } + + return particle; +} + void ParticleEffectEntityItem::setMaxParticles(quint32 maxParticles) { if (_maxParticles != maxParticles && MINIMUM_MAX_PARTICLES <= maxParticles && maxParticles <= MAXIMUM_MAX_PARTICLES) { _maxParticles = maxParticles; - // TODO: try to do something smart here and preserve the state of existing particles. - - // resize vectors - _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); + // Pop all the overflowing oldest particles + while (_particles.size() > _maxParticles) { + _particles.pop_front(); + } // effectively clear all particles and start emitting new ones from scratch. - _particleHeadIndex = 0; - _particleTailIndex = 0; _timeUntilNextEmit = 0.0f; } } - -// because particles are in a ring buffer, this isn't trivial -quint32 ParticleEffectEntityItem::getLivingParticleCount() const { - if (_particleTailIndex >= _particleHeadIndex) { - return _particleTailIndex - _particleHeadIndex; - } else { - return (_maxParticles - _particleHeadIndex) + _particleTailIndex; - } -} diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index d6dd257f7e..c35a45baeb 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -11,19 +11,17 @@ #ifndef hifi_ParticleEffectEntityItem_h #define hifi_ParticleEffectEntityItem_h -#include +#include #include "EntityItem.h" class ParticleEffectEntityItem : public EntityItem { public: + ALLOW_INSTANTIATION // This class can be instantiated static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); ParticleEffectEntityItem(const EntityItemID& entityItemID); - virtual ~ParticleEffectEntityItem(); - - ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of this entity virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; @@ -218,16 +216,27 @@ public: virtual bool supportsDetailedRayIntersection() const { return false; } protected: + struct Particle; + using Particles = std::deque; bool isAnimatingSomething() const; + + Particle createParticle(); 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 + void integrateParticle(Particle& particle, float deltaTime); + + struct Particle { + float seed { 0.0f }; + float lifetime { 0.0f }; + glm::vec3 position { Vectors::ZERO }; + glm::vec3 velocity { Vectors::ZERO }; + glm::vec3 acceleration { Vectors::ZERO }; + }; + + // Particles container + Particles _particles; + + // Particles properties rgbColor _color; xColor _colorStart = DEFAULT_COLOR; xColor _colorFinish = DEFAULT_COLOR; @@ -236,63 +245,42 @@ protected: float _alphaStart = DEFAULT_ALPHA_START; float _alphaFinish = DEFAULT_ALPHA_FINISH; float _alphaSpread = DEFAULT_ALPHA_SPREAD; - quint32 _maxParticles = DEFAULT_MAX_PARTICLES; - float _lifespan = DEFAULT_LIFESPAN; - float _emitRate = DEFAULT_EMIT_RATE; - float _emitSpeed = DEFAULT_EMIT_SPEED; - float _speedSpread = DEFAULT_SPEED_SPREAD; - glm::quat _emitOrientation = DEFAULT_EMIT_ORIENTATION; - glm::vec3 _emitDimensions = DEFAULT_EMIT_DIMENSIONS; - float _emitRadiusStart = DEFAULT_EMIT_RADIUS_START; - float _polarStart = DEFAULT_POLAR_START; - float _polarFinish = DEFAULT_POLAR_FINISH; - float _azimuthStart = DEFAULT_AZIMUTH_START; - float _azimuthFinish = DEFAULT_AZIMUTH_FINISH; - glm::vec3 _emitAcceleration = DEFAULT_EMIT_ACCELERATION; - glm::vec3 _accelerationSpread = DEFAULT_ACCELERATION_SPREAD; float _particleRadius = DEFAULT_PARTICLE_RADIUS; float _radiusStart = DEFAULT_RADIUS_START; float _radiusFinish = DEFAULT_RADIUS_FINISH; float _radiusSpread = DEFAULT_RADIUS_SPREAD; + float _lifespan = DEFAULT_LIFESPAN; + + // Emiter properties + quint32 _maxParticles = DEFAULT_MAX_PARTICLES; + + float _emitRate = DEFAULT_EMIT_RATE; + float _emitSpeed = DEFAULT_EMIT_SPEED; + float _speedSpread = DEFAULT_SPEED_SPREAD; + + glm::quat _emitOrientation = DEFAULT_EMIT_ORIENTATION; + glm::vec3 _emitDimensions = DEFAULT_EMIT_DIMENSIONS; + float _emitRadiusStart = DEFAULT_EMIT_RADIUS_START; + glm::vec3 _emitAcceleration = DEFAULT_EMIT_ACCELERATION; + glm::vec3 _accelerationSpread = DEFAULT_ACCELERATION_SPREAD; + + float _polarStart = DEFAULT_POLAR_START; + float _polarFinish = DEFAULT_POLAR_FINISH; + float _azimuthStart = DEFAULT_AZIMUTH_START; + float _azimuthFinish = DEFAULT_AZIMUTH_FINISH; + + quint64 _lastSimulated { 0 }; + bool _isEmitting { true }; - quint64 _lastSimulated; - bool _isEmitting = true; + QString _textures { DEFAULT_TEXTURES }; + bool _texturesChangedFlag { false }; + ShapeType _shapeType { SHAPE_TYPE_NONE }; + + float _timeUntilNextEmit { 0.0f }; - QString _textures = DEFAULT_TEXTURES; - bool _texturesChangedFlag = false; - ShapeType _shapeType = SHAPE_TYPE_NONE; - - // all the internals of running the particle sim - QVector _particleLifetimes; - 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 = 0.0f; - - // particle arrays are a ring buffer, use these indices - // to keep track of the living particles. - quint32 _particleHeadIndex = 0; - quint32 _particleTailIndex = 0; - - // bounding volume - glm::vec3 _particleMaxBound; - glm::vec3 _particleMinBound; - - bool _additiveBlending; + + bool _additiveBlending { DEFAULT_ADDITIVE_BLENDING }; }; #endif // hifi_ParticleEffectEntityItem_h diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index 5a0ab93ec5..19fa94524e 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -154,7 +154,7 @@ GLBackend::GLShader* compileShader(const Shader& shader) { qCWarning(gpulogging) << "GLShader::compileShader - failed to compile the gl shader object:"; qCWarning(gpulogging) << temp; - + /* filestream.open("debugshader.glsl.info.txt"); if (filestream.is_open()) { diff --git a/libraries/gpu/src/gpu/Stream.cpp b/libraries/gpu/src/gpu/Stream.cpp index 183f2137fb..ff765f3250 100644 --- a/libraries/gpu/src/gpu/Stream.cpp +++ b/libraries/gpu/src/gpu/Stream.cpp @@ -77,16 +77,6 @@ bool Stream::Format::setAttribute(Slot slot, Slot channel, Frequency frequency) return true; } - -BufferStream::BufferStream() : - _buffers(), - _offsets(), - _strides() -{} - -BufferStream::~BufferStream() { -} - void BufferStream::addBuffer(const BufferPointer& buffer, Offset offset, Offset stride) { _buffers.push_back(buffer); _offsets.push_back(offset); diff --git a/libraries/gpu/src/gpu/Stream.h b/libraries/gpu/src/gpu/Stream.h index 4fff3b651d..d47a877669 100644 --- a/libraries/gpu/src/gpu/Stream.h +++ b/libraries/gpu/src/gpu/Stream.h @@ -50,7 +50,7 @@ public: // Frequency describer enum Frequency { PER_VERTEX = 0, - PER_INSTANCE, + PER_INSTANCE = 1, }; // The attribute description @@ -124,10 +124,7 @@ typedef std::vector< Offset > Offsets; // A Buffer Stream can be assigned to the Batch to set several stream channels in one call class BufferStream { public: - typedef Offsets Strides; - - BufferStream(); - ~BufferStream(); + using Strides = Offsets; void clear() { _buffers.clear(); _offsets.clear(); _strides.clear(); } void addBuffer(const BufferPointer& buffer, Offset offset, Offset stride); diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index e67fbf7f66..9a866ca4d0 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -86,7 +86,6 @@ TransformCamera getTransformCamera() { } <@endfunc@> - <@func transformModelToWorldPos(objectTransform, modelPos, worldPos)@> { // transformModelToWorldPos <$worldPos$> = (<$objectTransform$>._model * <$modelPos$>); @@ -136,7 +135,22 @@ TransformCamera getTransformCamera() { <@func transformClipToEyeDir(cameraTransform, clipPos, eyeDir)@> { // transformClipToEyeDir - <$eyeDir$> = vec3(<$cameraTransform$>._projectionInverse * vec4(<$clipPos$>.xyz, 1.0)); + <$eyeDir$> = vec3(<$cameraTransform$>._projectionInverse * vec4(<$clipPos$>.xyz, 0.0)); + } +<@endfunc@> + +<@func $transformModelToEyePos(cameraTransform, objectTransform, modelPos, eyePos)@> + + { // transformModelToEyePos + vec4 _worldpos = (<$objectTransform$>._model * vec4(<$modelPos$>.xyz, 1.0)); + <$eyePos$> = (<$cameraTransform$>._view * _worldpos); + } +<@endfunc@> + +<@func transformEyeToClipPos(cameraTransform, eyePos, clipPos)@> + { // transformEyeToClipPos + <$clipPos$> = <$cameraTransform$>._projection * vec4(<$eyePos$>.xyz, 1.0); } <@endfunc@> diff --git a/libraries/model/src/model/Light.cpp b/libraries/model/src/model/Light.cpp index 823be727a4..917e3bed7e 100755 --- a/libraries/model/src/model/Light.cpp +++ b/libraries/model/src/model/Light.cpp @@ -18,7 +18,7 @@ Light::Light() : _transform() { // only if created from nothing shall we create the Buffer to store the properties Schema schema; - _schemaBuffer = gpu::BufferView(std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema)); + _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema); } Light::Light(const Light& light) : diff --git a/libraries/model/src/model/Light.h b/libraries/model/src/model/Light.h index efc02e3de4..de7a846a25 100755 --- a/libraries/model/src/model/Light.h +++ b/libraries/model/src/model/Light.h @@ -112,8 +112,6 @@ public: Vec4 _shadow{0.0f}; Vec4 _control{0.0f, 0.0f, 0.0f, 0.0f}; - - Schema() {} }; const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; } diff --git a/libraries/shared/src/AABox.cpp b/libraries/shared/src/AABox.cpp index b514e171eb..7c001c570e 100644 --- a/libraries/shared/src/AABox.cpp +++ b/libraries/shared/src/AABox.cpp @@ -42,9 +42,48 @@ glm::vec3 AABox::calcCenter() const { return center; } -void AABox::scale(float scale) { - _corner = _corner * scale; - _scale = _scale * scale; +void AABox::rotate(const glm::quat& rotation) { + auto minimum = _corner; + auto maximum = _corner + _scale; + + glm::vec3 bottomLeftNear(minimum.x, minimum.y, minimum.z); + glm::vec3 bottomRightNear(maximum.x, minimum.y, minimum.z); + glm::vec3 bottomLeftFar(minimum.x, minimum.y, maximum.z); + glm::vec3 bottomRightFar(maximum.x, minimum.y, maximum.z); + glm::vec3 topLeftNear(minimum.x, maximum.y, minimum.z); + glm::vec3 topRightNear(maximum.x, maximum.y, minimum.z); + glm::vec3 topLeftFar(minimum.x, maximum.y, maximum.z); + glm::vec3 topRightFar(maximum.x, maximum.y, maximum.z); + + glm::vec3 bottomLeftNearRotated = rotation * bottomLeftNear; + glm::vec3 bottomRightNearRotated = rotation * bottomRightNear; + glm::vec3 bottomLeftFarRotated = rotation * bottomLeftFar; + glm::vec3 bottomRightFarRotated = rotation * bottomRightFar; + glm::vec3 topLeftNearRotated = rotation * topLeftNear; + glm::vec3 topRightNearRotated = rotation * topRightNear; + glm::vec3 topLeftFarRotated = rotation * topLeftFar; + glm::vec3 topRightFarRotated = rotation * topRightFar; + + minimum = glm::min(bottomLeftNearRotated, + glm::min(bottomRightNearRotated, + glm::min(bottomLeftFarRotated, + glm::min(bottomRightFarRotated, + glm::min(topLeftNearRotated, + glm::min(topRightNearRotated, + glm::min(topLeftFarRotated, + topRightFarRotated))))))); + + maximum = glm::max(bottomLeftNearRotated, + glm::max(bottomRightNearRotated, + glm::max(bottomLeftFarRotated, + glm::max(bottomRightFarRotated, + glm::max(topLeftNearRotated, + glm::max(topRightNearRotated, + glm::max(topLeftFarRotated, + topRightFarRotated))))))); + + _corner = minimum; + _scale = maximum - minimum; } glm::vec3 AABox::getVertex(BoxVertex vertex) const { diff --git a/libraries/shared/src/AABox.h b/libraries/shared/src/AABox.h index 1f5923dbd8..8de0462138 100644 --- a/libraries/shared/src/AABox.h +++ b/libraries/shared/src/AABox.h @@ -34,13 +34,18 @@ public: AABox(const glm::vec3& corner, const glm::vec3& dimensions); AABox(); ~AABox() {}; - + void setBox(const glm::vec3& corner, const glm::vec3& scale); void setBox(const glm::vec3& corner, float scale); glm::vec3 getVertexP(const glm::vec3& normal) const; glm::vec3 getVertexN(const glm::vec3& normal) const; - void scale(float scale); + + void shiftBy(const glm::vec3& delta) { _corner += delta; } + void rotate(const glm::quat& rotation); + void scale(float scale) { _corner *= scale; _scale *= scale; } + void scale(const glm::vec3& scale) { _corner *= scale; _scale *= scale; } + const glm::vec3& getCorner() const { return _corner; } const glm::vec3& getScale() const { return _scale; } const glm::vec3& getDimensions() const { return _scale; } diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 257ef2f00e..101412bbf7 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -360,6 +360,10 @@ QSize fromGlm(const glm::ivec2 & v) { return QSize(v.x, v.y); } +vec4 toGlm(const xColor& color, float alpha) { + return vec4((float)color.red / 255.0f, (float)color.green / 255.0f, (float)color.blue / 255.0f, alpha); +} + QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) { QRectF result(pos.x, pos.y, size.x, size.y); return result; diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 7daf1b7e43..25ded54e93 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -156,6 +156,7 @@ vec2 toGlm(const QPointF& pt); vec3 toGlm(const xColor& color); vec4 toGlm(const QColor& color); ivec4 toGlm(const QRect& rect); +vec4 toGlm(const xColor& color, float alpha); QSize fromGlm(const glm::ivec2 & v); QMatrix4x4 fromGlm(const glm::mat4 & m);