mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-06-16 17:20:30 +02:00
294 lines
11 KiB
C++
294 lines
11 KiB
C++
//
|
|
// RenderableParticleEffectEntityItem.cpp
|
|
// interface/src
|
|
//
|
|
// Created by Jason Rickwald on 3/2/15.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
|
|
#include <glm/gtx/quaternion.hpp>
|
|
|
|
#include <DependencyManager.h>
|
|
#include <DeferredLightingEffect.h>
|
|
#include <PerfStat.h>
|
|
#include <GeometryCache.h>
|
|
#include <AbstractViewStateInterface.h>
|
|
#include "EntitiesRendererLogging.h"
|
|
|
|
#include "RenderableParticleEffectEntityItem.h"
|
|
|
|
#include "untextured_particle_vert.h"
|
|
#include "untextured_particle_frag.h"
|
|
#include "textured_particle_vert.h"
|
|
#include "textured_particle_frag.h"
|
|
|
|
static const size_t VERTEX_PER_PARTICLE = 4;
|
|
|
|
class ParticlePayloadData {
|
|
public:
|
|
template<typename T>
|
|
struct InterpolationData {
|
|
T start;
|
|
T middle;
|
|
T finish;
|
|
T spread;
|
|
};
|
|
struct ParticleUniforms {
|
|
InterpolationData<float> radius;
|
|
InterpolationData<glm::vec4> 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<ParticlePayloadData>;
|
|
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<ParticlePrimitive>;
|
|
|
|
ParticlePayloadData() {
|
|
ParticleUniforms uniforms;
|
|
_uniformBuffer = std::make_shared<Buffer>(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(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(const AABox& bound) { _bound = bound; }
|
|
|
|
BufferPointer getParticleBuffer() { return _particleBuffer; }
|
|
const BufferPointer& getParticleBuffer() const { return _particleBuffer; }
|
|
|
|
const ParticleUniforms& getParticleUniforms() const { return _uniformBuffer.get<ParticleUniforms>(); }
|
|
ParticleUniforms& editParticleUniforms() { return _uniformBuffer.edit<ParticleUniforms>(); }
|
|
|
|
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);
|
|
|
|
gpu::Batch& batch = *args->_batch;
|
|
batch.setPipeline(_pipeline);
|
|
|
|
if (_texture) {
|
|
batch.setResourceTexture(0, _texture);
|
|
}
|
|
|
|
batch.setModelTransform(_modelTransform);
|
|
batch.setUniformBuffer(0, _uniformBuffer);
|
|
batch.setInputFormat(_vertexFormat);
|
|
batch.setInputBuffer(0, _particleBuffer, 0, sizeof(ParticlePrimitive));
|
|
|
|
auto numParticles = _particleBuffer->getSize() / sizeof(ParticlePrimitive);
|
|
batch.drawInstanced(numParticles, gpu::TRIANGLE_STRIP, VERTEX_PER_PARTICLE);
|
|
}
|
|
|
|
protected:
|
|
Transform _modelTransform;
|
|
AABox _bound;
|
|
PipelinePointer _pipeline;
|
|
FormatPointer _vertexFormat { std::make_shared<Format>() };
|
|
BufferPointer _particleBuffer { std::make_shared<Buffer>() };
|
|
BufferView _uniformBuffer;
|
|
TexturePointer _texture;
|
|
bool _visibleFlag = true;
|
|
};
|
|
|
|
namespace render {
|
|
template <>
|
|
const ItemKey payloadGetKey(const ParticlePayloadData::Pointer& payload) {
|
|
if (payload->getVisibleFlag()) {
|
|
return ItemKey::Builder::transparentShape();
|
|
} else {
|
|
return ItemKey::Builder().withInvisible().build();
|
|
}
|
|
}
|
|
|
|
template <>
|
|
const Item::Bound payloadGetBound(const ParticlePayloadData::Pointer& payload) {
|
|
return payload->getBound();
|
|
}
|
|
|
|
template <>
|
|
void payloadRender(const ParticlePayloadData::Pointer& payload, RenderArgs* args) {
|
|
payload->render(args);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
|
return std::make_shared<RenderableParticleEffectEntityItem>(entityID, properties);
|
|
}
|
|
|
|
RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
|
ParticleEffectEntityItem(entityItemID, properties) {
|
|
|
|
// lazy creation of particle system pipeline
|
|
if (!_untexturedPipeline || !_texturedPipeline) {
|
|
createPipelines();
|
|
}
|
|
}
|
|
|
|
bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self,
|
|
render::ScenePointer scene,
|
|
render::PendingChanges& pendingChanges) {
|
|
|
|
_scene = scene;
|
|
_renderItemId = _scene->allocateID();
|
|
auto particlePayloadData = std::make_shared<ParticlePayloadData>();
|
|
particlePayloadData->setPipeline(_untexturedPipeline);
|
|
auto renderPayload = std::make_shared<ParticlePayloadData::Payload>(particlePayloadData);
|
|
render::Item::Status::Getters statusGetters;
|
|
makeEntityItemStatusGetters(shared_from_this(), statusGetters);
|
|
renderPayload->addStatusGetters(statusGetters);
|
|
pendingChanges.resetItem(_renderItemId, renderPayload);
|
|
return true;
|
|
}
|
|
|
|
void RenderableParticleEffectEntityItem::removeFromScene(EntityItemPointer self,
|
|
render::ScenePointer scene,
|
|
render::PendingChanges& pendingChanges) {
|
|
pendingChanges.removeItem(_renderItemId);
|
|
_scene = nullptr;
|
|
};
|
|
|
|
void RenderableParticleEffectEntityItem::update(const quint64& now) {
|
|
ParticleEffectEntityItem::update(now);
|
|
|
|
if (_texturesChangedFlag) {
|
|
if (_textures.isEmpty()) {
|
|
_texture.clear();
|
|
} else {
|
|
// for now use the textures string directly.
|
|
// Eventually we'll want multiple textures in a map or array.
|
|
_texture = DependencyManager::get<TextureCache>()->getTexture(_textures);
|
|
}
|
|
_texturesChangedFlag = false;
|
|
}
|
|
|
|
updateRenderItem();
|
|
}
|
|
|
|
static glm::vec4 toGlm(const xColor& color, float alpha) {
|
|
return glm::vec4((float)color.red / 255.0f, (float)color.green / 255.0f, (float)color.blue / 255.0f, alpha);
|
|
}
|
|
|
|
void RenderableParticleEffectEntityItem::updateRenderItem() {
|
|
if (!_scene) {
|
|
return;
|
|
}
|
|
using ParticleUniforms = ParticlePayloadData::ParticleUniforms;
|
|
using ParticlePrimitive = ParticlePayloadData::ParticlePrimitive;
|
|
using ParticlePrimitives = ParticlePayloadData::ParticlePrimitives;
|
|
|
|
// 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>();
|
|
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<ParticlePayloadData>(_renderItemId, [=](ParticlePayloadData& payload) {
|
|
// 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) {
|
|
return;
|
|
}
|
|
memcpy(particleBuffer->editData(), particlePrimitives->data(), numBytes);
|
|
|
|
// Update transform and bounds
|
|
payload.setModelTransform(transform);
|
|
payload.setBound(bounds);
|
|
|
|
if (_texture && _texture->isLoaded()) {
|
|
payload.setTexture(_texture->getGPUTexture());
|
|
payload.setPipeline(_texturedPipeline);
|
|
} else {
|
|
payload.setTexture(nullptr);
|
|
payload.setPipeline(_untexturedPipeline);
|
|
}
|
|
});
|
|
|
|
_scene->enqueuePendingChanges(pendingChanges);
|
|
}
|
|
|
|
void RenderableParticleEffectEntityItem::createPipelines() {
|
|
if (!_untexturedPipeline) {
|
|
auto state = std::make_shared<gpu::State>();
|
|
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);
|
|
|
|
auto vertShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(untextured_particle_vert)));
|
|
auto fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(untextured_particle_frag)));
|
|
|
|
auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vertShader, fragShader));
|
|
_untexturedPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state));
|
|
}
|
|
if (!_texturedPipeline) {
|
|
auto state = std::make_shared<gpu::State>();
|
|
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);
|
|
|
|
auto vertShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(textured_particle_vert)));
|
|
auto fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_frag)));
|
|
|
|
auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vertShader, fragShader));
|
|
_texturedPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state));
|
|
|
|
}
|
|
}
|