Fix render order between particles and grass clusters
This commit is contained in:
parent
c2fdc464dd
commit
95f9fce3c4
10 changed files with 129 additions and 110 deletions
|
@ -304,6 +304,7 @@ set(SCENE_HEADERS
|
|||
src/engine/scene/pipeline/control.h
|
||||
src/engine/scene/pipeline/world.h
|
||||
src/engine/scene/scenegraph.h
|
||||
src/engine/scene/sceneleaf.h
|
||||
src/engine/scene/types.h)
|
||||
|
||||
set(SCENE_SOURCES
|
||||
|
|
|
@ -533,10 +533,11 @@ void Area::fill(SceneGraph &sceneGraph) {
|
|||
glm::vec3 baryPosition(getRandomBarycentric());
|
||||
glm::vec3 position(aabbTransform * glm::vec4(barycentricToCartesian(vertices[0], vertices[1], vertices[2], baryPosition), 1.0f));
|
||||
glm::vec2 lightmapUV(aabbNode->mesh()->mesh->getTriangleTexCoords2(face, baryPosition));
|
||||
GrassSceneNode::Cluster cluster;
|
||||
cluster.position = move(position);
|
||||
cluster.variant = getRandomGrassVariant();
|
||||
cluster.lightmapUV = move(lightmapUV);
|
||||
auto cluster = make_shared<GrassSceneNode::Cluster>();
|
||||
cluster->parent = grass.get();
|
||||
cluster->position = move(position);
|
||||
cluster->variant = getRandomGrassVariant();
|
||||
cluster->lightmapUV = move(lightmapUV);
|
||||
grass->addCluster(move(cluster));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,7 +144,7 @@ void EmitterSceneNode::doSpawnParticle() {
|
|||
glm::vec3 velocity((_velocity + random(0.0f, _randomVelocity)) * dir);
|
||||
|
||||
auto particle = make_shared<Particle>();
|
||||
particle->emitter = this;
|
||||
particle->parent = this;
|
||||
particle->position = move(position);
|
||||
particle->velocity = move(velocity);
|
||||
particle->frame = _frameStart;
|
||||
|
@ -191,7 +191,7 @@ void EmitterSceneNode::spawnLightningParticles() {
|
|||
glm::vec3 endToStart(segment.second - segment.first);
|
||||
glm::vec3 center(0.5f * (segment.first + segment.second));
|
||||
auto particle = make_shared<Particle>();
|
||||
particle->emitter = this;
|
||||
particle->parent = this;
|
||||
particle->position = move(center);
|
||||
particle->dir = _absTransform * glm::vec4(glm::normalize(endToStart), 0.0f);
|
||||
particle->size = glm::vec2(_lightningScale, glm::length(endToStart));
|
||||
|
@ -248,8 +248,11 @@ void EmitterSceneNode::detonate() {
|
|||
doSpawnParticle();
|
||||
}
|
||||
|
||||
void EmitterSceneNode::drawParticles(const vector<Particle *> &particles) {
|
||||
if (particles.empty()) return;
|
||||
void EmitterSceneNode::drawLeafs(const vector<shared_ptr<SceneLeaf>> &leafs, int count) {
|
||||
if (leafs.empty()) return;
|
||||
if (count == -1) {
|
||||
count = static_cast<int>(leafs.size());
|
||||
}
|
||||
|
||||
shared_ptr<ModelNode::Emitter> emitter(_modelNode->emitter());
|
||||
shared_ptr<Texture> texture(emitter->texture);
|
||||
|
@ -260,22 +263,22 @@ void EmitterSceneNode::drawParticles(const vector<Particle *> &particles) {
|
|||
uniforms.particles->gridSize = emitter->gridSize;
|
||||
uniforms.particles->render = static_cast<int>(emitter->renderMode);
|
||||
|
||||
for (size_t i = 0; i < particles.size(); ++i) {
|
||||
const Particle &particle = *particles[i];
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto particle = static_pointer_cast<Particle>(leafs[i]);
|
||||
|
||||
glm::mat4 transform(_absTransform);
|
||||
transform = glm::translate(transform, particles[i]->position);
|
||||
transform = glm::translate(transform, particle->position);
|
||||
if (emitter->renderMode == ModelNode::Emitter::RenderMode::MotionBlur) {
|
||||
transform = glm::scale(transform, glm::vec3((1.0f + kMotionBlurStrength * kProjectileSpeed) * particle.size.x, particle.size.y, 1.0f));
|
||||
transform = glm::scale(transform, glm::vec3((1.0f + kMotionBlurStrength * kProjectileSpeed) * particle->size.x, particle->size.y, 1.0f));
|
||||
} else {
|
||||
transform = glm::scale(transform, glm::vec3(particle.size, 1.0f));
|
||||
transform = glm::scale(transform, glm::vec3(particle->size, 1.0f));
|
||||
}
|
||||
|
||||
uniforms.particles->particles[i].transform = move(transform);
|
||||
uniforms.particles->particles[i].dir = glm::vec4(particle.dir, 1.0f);
|
||||
uniforms.particles->particles[i].color = glm::vec4(particle.color, particle.alpha);
|
||||
uniforms.particles->particles[i].size = glm::vec2(particle.size);
|
||||
uniforms.particles->particles[i].frame = particle.frame;
|
||||
uniforms.particles->particles[i].dir = glm::vec4(particle->dir, 1.0f);
|
||||
uniforms.particles->particles[i].color = glm::vec4(particle->color, particle->alpha);
|
||||
uniforms.particles->particles[i].size = glm::vec2(particle->size);
|
||||
uniforms.particles->particles[i].frame = particle->frame;
|
||||
}
|
||||
|
||||
Shaders::instance().activate(ShaderProgram::ParticleParticle, uniforms);
|
||||
|
@ -285,11 +288,11 @@ void EmitterSceneNode::drawParticles(const vector<Particle *> &particles) {
|
|||
|
||||
bool lighten = emitter->blendMode == ModelNode::Emitter::BlendMode::Lighten;
|
||||
if (lighten) {
|
||||
withLightenBlending([&particles]() {
|
||||
Meshes::instance().getBillboard()->drawInstanced(static_cast<int>(particles.size()));
|
||||
withLightenBlending([&count]() {
|
||||
Meshes::instance().getBillboard()->drawInstanced(count);
|
||||
});
|
||||
} else {
|
||||
Meshes::instance().getBillboard()->drawInstanced(static_cast<int>(particles.size()));
|
||||
Meshes::instance().getBillboard()->drawInstanced(count);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include "../../graphics/beziercurve.h"
|
||||
#include "../../graphics/model/modelnode.h"
|
||||
|
||||
#include "../sceneleaf.h"
|
||||
|
||||
#include "modelnodescenenode.h"
|
||||
|
||||
namespace reone {
|
||||
|
@ -33,8 +35,7 @@ class ModelSceneNode;
|
|||
|
||||
class EmitterSceneNode : public ModelNodeSceneNode {
|
||||
public:
|
||||
struct Particle {
|
||||
EmitterSceneNode *emitter { nullptr };
|
||||
struct Particle : public SceneLeaf {
|
||||
glm::vec3 position { 0.0f };
|
||||
glm::vec3 dir { 0.0f }; // used in Linked render mode
|
||||
glm::vec3 color { 1.0f };
|
||||
|
@ -49,7 +50,7 @@ public:
|
|||
EmitterSceneNode(const ModelSceneNode *model, std::shared_ptr<graphics::ModelNode> modelNode, SceneGraph *sceneGraph);
|
||||
|
||||
void update(float dt) override;
|
||||
void drawParticles(const std::vector<Particle *> &particles);
|
||||
void drawLeafs(const std::vector<std::shared_ptr<SceneLeaf>> &leafs, int count) override;
|
||||
|
||||
void detonate();
|
||||
|
||||
|
|
|
@ -48,11 +48,16 @@ void GrassSceneNode::clear() {
|
|||
_clusters.clear();
|
||||
}
|
||||
|
||||
void GrassSceneNode::addCluster(Cluster cluster) {
|
||||
void GrassSceneNode::addCluster(shared_ptr<Cluster> cluster) {
|
||||
_clusters.push_back(move(cluster));
|
||||
}
|
||||
|
||||
void GrassSceneNode::drawClusters(const vector<Cluster> &clusters) {
|
||||
void GrassSceneNode::drawLeafs(const vector<shared_ptr<SceneLeaf>> &leafs, int count) {
|
||||
if (leafs.empty()) return;
|
||||
if (count == -1) {
|
||||
count = static_cast<int>(leafs.size());
|
||||
}
|
||||
|
||||
setActiveTextureUnit(TextureUnits::diffuseMap);
|
||||
_texture->bind();
|
||||
|
||||
|
@ -66,15 +71,15 @@ void GrassSceneNode::drawClusters(const vector<Cluster> &clusters) {
|
|||
uniforms.combined.featureMask |= UniformFeatureFlags::lightmap;
|
||||
}
|
||||
|
||||
int numClusters = static_cast<int>(clusters.size());
|
||||
for (int i = 0; i < numClusters; ++i) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto cluster = static_pointer_cast<GrassSceneNode::Cluster>(leafs[i]);
|
||||
uniforms.grass->quadSize = _quadSize;
|
||||
uniforms.grass->clusters[i].positionVariant = glm::vec4(clusters[i].position, static_cast<float>(clusters[i].variant));
|
||||
uniforms.grass->clusters[i].lightmapUV = clusters[i].lightmapUV;
|
||||
uniforms.grass->clusters[i].positionVariant = glm::vec4(cluster->position, static_cast<float>(cluster->variant));
|
||||
uniforms.grass->clusters[i].lightmapUV = cluster->lightmapUV;
|
||||
}
|
||||
|
||||
Shaders::instance().activate(ShaderProgram::GrassGrass, uniforms);
|
||||
Meshes::instance().getGrass()->drawInstanced(numClusters);
|
||||
Meshes::instance().getGrass()->drawInstanced(count);
|
||||
}
|
||||
|
||||
} // namespace scene
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#include "../../graphics/texture/texture.h"
|
||||
|
||||
#include "../sceneleaf.h"
|
||||
|
||||
#include "scenenode.h"
|
||||
|
||||
namespace reone {
|
||||
|
@ -27,7 +29,7 @@ namespace scene {
|
|||
|
||||
class GrassSceneNode : public SceneNode {
|
||||
public:
|
||||
struct Cluster {
|
||||
struct Cluster : public SceneLeaf {
|
||||
glm::vec3 position { 0.0f };
|
||||
glm::vec2 lightmapUV { 0.0f };
|
||||
int variant { 0 };
|
||||
|
@ -36,17 +38,17 @@ public:
|
|||
GrassSceneNode(std::string name, glm::vec2 quadSize, std::shared_ptr<graphics::Texture> texture, std::shared_ptr<graphics::Texture> lightmap, SceneGraph *graph);
|
||||
|
||||
void clear();
|
||||
void addCluster(Cluster cluster);
|
||||
void addCluster(std::shared_ptr<Cluster> cluster);
|
||||
|
||||
void drawClusters(const std::vector<Cluster> &clusters);
|
||||
void drawLeafs(const std::vector<std::shared_ptr<SceneLeaf>> &leafs, int count) override;
|
||||
|
||||
const std::vector<Cluster> &clusters() const { return _clusters; }
|
||||
const std::vector<std::shared_ptr<Cluster>> &clusters() const { return _clusters; }
|
||||
|
||||
private:
|
||||
glm::vec2 _quadSize { 0.0f };
|
||||
std::shared_ptr<graphics::Texture> _texture;
|
||||
std::shared_ptr<graphics::Texture> _lightmap;
|
||||
std::vector<Cluster> _clusters;
|
||||
std::vector<std::shared_ptr<Cluster>> _clusters;
|
||||
};
|
||||
|
||||
} // namespace scene
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "../../graphics/aabb.h"
|
||||
|
||||
#include "../sceneleaf.h"
|
||||
#include "../types.h"
|
||||
|
||||
namespace reone {
|
||||
|
@ -44,6 +45,8 @@ public:
|
|||
virtual void update(float dt);
|
||||
virtual void draw();
|
||||
|
||||
virtual void drawLeafs(const std::vector<std::shared_ptr<SceneLeaf>> &leafs, int count = -1) {}
|
||||
|
||||
bool isVisible() const { return _visible; }
|
||||
bool isCullable() const { return _cullable; }
|
||||
bool isCulled() const { return _culled; }
|
||||
|
|
|
@ -74,8 +74,7 @@ void SceneGraph::update(float dt) {
|
|||
updateLighting();
|
||||
updateShadows(dt);
|
||||
prepareTransparentMeshes();
|
||||
prepareParticles();
|
||||
prepareGrass();
|
||||
prepareLeafs();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,78 +225,53 @@ void SceneGraph::prepareTransparentMeshes() {
|
|||
});
|
||||
}
|
||||
|
||||
void SceneGraph::prepareParticles() {
|
||||
void SceneGraph::prepareLeafs() {
|
||||
static glm::vec4 viewport(-1.0f, -1.0f, 1.0f, 1.0f);
|
||||
|
||||
// Extract particles from all emitters, sort them by depth
|
||||
vector<pair<EmitterSceneNode::Particle *, float>> particlesZ;
|
||||
vector<pair<shared_ptr<SceneLeaf>, float>> leafs;
|
||||
glm::vec3 cameraPos(_activeCamera->absoluteTransform()[3]);
|
||||
|
||||
// Add grass clusters
|
||||
for (auto &grass : _grass) {
|
||||
float grassDistance2 = kMaxGrassDistance * kMaxGrassDistance;
|
||||
size_t numLeafs = leafs.size();
|
||||
for (auto &cluster : grass->clusters()) {
|
||||
float distance2 = glm::distance2(cameraPos, cluster->position);
|
||||
if (distance2 <= grassDistance2) {
|
||||
glm::vec3 screen(glm::project(cluster->position, _activeCamera->view(), _activeCamera->projection(), viewport));
|
||||
if (screen.z >= 0.5f && glm::abs(screen.x) <= 1.0f && glm::abs(screen.y) <= 1.0f) {
|
||||
leafs.push_back(make_pair(cluster, screen.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add particles
|
||||
for (auto &emitter : _emitters) {
|
||||
glm::mat4 modelView(_activeCamera->view() * emitter->absoluteTransform());
|
||||
for (auto &particle : emitter->particles()) {
|
||||
glm::vec3 screen(glm::project(particle->position, modelView, _activeCamera->projection(), viewport));
|
||||
if (screen.z >= 0.5f && glm::abs(screen.x) <= 1.0f && glm::abs(screen.y) <= 1.0f) {
|
||||
particlesZ.push_back(make_pair(particle.get(), screen.z));
|
||||
leafs.push_back(make_pair(particle, screen.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
sort(particlesZ.begin(), particlesZ.end(), [](auto &left, auto &right) {
|
||||
return left.second > right.second;
|
||||
});
|
||||
|
||||
// Map (particle, Z) pairs to (emitter, particles)
|
||||
_particles.clear();
|
||||
EmitterSceneNode *emitter = nullptr;
|
||||
vector<EmitterSceneNode::Particle *> emitterParticles;
|
||||
for (auto &pair : particlesZ) {
|
||||
if (pair.first->emitter != emitter) {
|
||||
flushEmitterParticles(emitter, emitterParticles);
|
||||
emitter = pair.first->emitter;
|
||||
// Sort leafs back to front
|
||||
sort(leafs.begin(), leafs.end(), [](auto &left, auto &right) { return left.second > right.second; });
|
||||
|
||||
// Group leafs into buckets
|
||||
_leafs.clear();
|
||||
vector<shared_ptr<SceneLeaf>> nodeLeafs;
|
||||
for (auto &leafDepth : leafs) {
|
||||
if (!nodeLeafs.empty()) {
|
||||
_leafs.push_back(make_pair(nodeLeafs[0]->parent, nodeLeafs));
|
||||
nodeLeafs.clear();
|
||||
}
|
||||
emitterParticles.push_back(pair.first);
|
||||
nodeLeafs.push_back(leafDepth.first);
|
||||
}
|
||||
flushEmitterParticles(emitter, emitterParticles);
|
||||
}
|
||||
|
||||
void SceneGraph::flushEmitterParticles(EmitterSceneNode *emitter, vector<EmitterSceneNode::Particle *> &particles) {
|
||||
if (emitter && !particles.empty()) {
|
||||
_particles.push_back(make_pair(emitter, particles));
|
||||
particles.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneGraph::prepareGrass() {
|
||||
static glm::vec4 viewport(-1.0f, -1.0f, 1.0f, 1.0f);
|
||||
|
||||
_grassClusters.clear();
|
||||
|
||||
if (_activeCamera && !_grass.empty()) {
|
||||
glm::vec3 cameraPos(_activeCamera->absoluteTransform()[3]);
|
||||
float grassDistance2 = kMaxGrassDistance * kMaxGrassDistance;
|
||||
|
||||
for (auto &node : _grass) {
|
||||
vector<GrassSceneNode::Cluster> clusters;
|
||||
vector<pair<GrassSceneNode::Cluster, float>> clustersZ;
|
||||
for (auto &cluster : node->clusters()) {
|
||||
float distance2 = glm::distance2(cameraPos, cluster.position);
|
||||
if (distance2 <= grassDistance2) {
|
||||
glm::vec3 screen(glm::project(cluster.position, _activeCamera->view(), _activeCamera->projection(), viewport));
|
||||
if (screen.z >= 0.5f && glm::abs(screen.x) <= 1.0f && glm::abs(screen.y) <= 1.0f) {
|
||||
clustersZ.push_back(make_pair(cluster, screen.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
sort(clustersZ.begin(), clustersZ.end(), [](auto &left, auto &right) {
|
||||
return left.second > right.second;
|
||||
});
|
||||
int numClustersToErase = glm::max(0, static_cast<int>(clustersZ.size()) - kMaxGrassClusters);
|
||||
if (numClustersToErase > 0) {
|
||||
clustersZ.erase(clustersZ.begin(), clustersZ.begin() + numClustersToErase);
|
||||
}
|
||||
for (auto &cluster : clustersZ) {
|
||||
clusters.push_back(move(cluster.first));
|
||||
}
|
||||
_grassClusters.push_back(make_pair(node, move(clusters)));
|
||||
}
|
||||
if (!nodeLeafs.empty()) {
|
||||
_leafs.push_back(make_pair(nodeLeafs[0]->parent, nodeLeafs));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,14 +313,12 @@ void SceneGraph::draw(bool shadowPass) {
|
|||
|
||||
setBackFaceCullingEnabled(false);
|
||||
|
||||
// Render grass
|
||||
for (auto &grass : _grassClusters) {
|
||||
grass.first->drawClusters(grass.second);
|
||||
}
|
||||
|
||||
// Render particles
|
||||
for (auto &pair : _particles) {
|
||||
pair.first->drawParticles(pair.second);
|
||||
// Render particles and grass clusters
|
||||
for (auto &nodeLeaf : _leafs) {
|
||||
int count = nodeLeaf.first->type() == SceneNodeType::Grass && nodeLeaf.second.size() > kMaxGrassClusters ?
|
||||
kMaxGrassClusters :
|
||||
-1;
|
||||
nodeLeaf.first->drawLeafs(nodeLeaf.second, count);
|
||||
}
|
||||
|
||||
// Render lens flares
|
||||
|
|
|
@ -121,8 +121,7 @@ private:
|
|||
std::vector<LightSceneNode *> _lights;
|
||||
std::vector<EmitterSceneNode *> _emitters;
|
||||
std::vector<GrassSceneNode *> _grass;
|
||||
std::vector<std::pair<EmitterSceneNode *, std::vector<EmitterSceneNode::Particle *>>> _particles;
|
||||
std::vector<std::pair<GrassSceneNode *, std::vector<GrassSceneNode::Cluster>>> _grassClusters;
|
||||
std::vector<std::pair<SceneNode *, std::vector<std::shared_ptr<SceneLeaf>>>> _leafs;
|
||||
|
||||
uint32_t _textureId { 0 };
|
||||
bool _updateRoots { true };
|
||||
|
@ -157,10 +156,7 @@ private:
|
|||
void refreshShadowLight();
|
||||
|
||||
void prepareTransparentMeshes();
|
||||
void prepareParticles();
|
||||
void prepareGrass();
|
||||
|
||||
inline void flushEmitterParticles(EmitterSceneNode *emitter, std::vector<EmitterSceneNode::Particle *> &particles);
|
||||
void prepareLeafs();
|
||||
};
|
||||
|
||||
} // namespace scene
|
||||
|
|
35
src/engine/scene/sceneleaf.h
Normal file
35
src/engine/scene/sceneleaf.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021 The reone project contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace scene {
|
||||
|
||||
class SceneNode;
|
||||
|
||||
/**
|
||||
* Base class for emitter particles and grass clusters.
|
||||
*/
|
||||
struct SceneLeaf {
|
||||
SceneNode *parent { nullptr };
|
||||
};
|
||||
|
||||
} // namespace scene
|
||||
|
||||
} // namespace reone
|
Loading…
Reference in a new issue