diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e6c80dc..2520e920 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/src/engine/game/object/area.cpp b/src/engine/game/object/area.cpp index f9622b77..2d585317 100644 --- a/src/engine/game/object/area.cpp +++ b/src/engine/game/object/area.cpp @@ -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(); + cluster->parent = grass.get(); + cluster->position = move(position); + cluster->variant = getRandomGrassVariant(); + cluster->lightmapUV = move(lightmapUV); grass->addCluster(move(cluster)); } } diff --git a/src/engine/scene/node/emitternode.cpp b/src/engine/scene/node/emitternode.cpp index 7797b55e..8e2bd112 100644 --- a/src/engine/scene/node/emitternode.cpp +++ b/src/engine/scene/node/emitternode.cpp @@ -144,7 +144,7 @@ void EmitterSceneNode::doSpawnParticle() { glm::vec3 velocity((_velocity + random(0.0f, _randomVelocity)) * dir); auto particle = make_shared(); - 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->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 &particles) { - if (particles.empty()) return; +void EmitterSceneNode::drawLeafs(const vector> &leafs, int count) { + if (leafs.empty()) return; + if (count == -1) { + count = static_cast(leafs.size()); + } shared_ptr emitter(_modelNode->emitter()); shared_ptr texture(emitter->texture); @@ -260,22 +263,22 @@ void EmitterSceneNode::drawParticles(const vector &particles) { uniforms.particles->gridSize = emitter->gridSize; uniforms.particles->render = static_cast(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(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 &particles) { bool lighten = emitter->blendMode == ModelNode::Emitter::BlendMode::Lighten; if (lighten) { - withLightenBlending([&particles]() { - Meshes::instance().getBillboard()->drawInstanced(static_cast(particles.size())); + withLightenBlending([&count]() { + Meshes::instance().getBillboard()->drawInstanced(count); }); } else { - Meshes::instance().getBillboard()->drawInstanced(static_cast(particles.size())); + Meshes::instance().getBillboard()->drawInstanced(count); } } diff --git a/src/engine/scene/node/emitternode.h b/src/engine/scene/node/emitternode.h index e7f10b73..dc1022d9 100644 --- a/src/engine/scene/node/emitternode.h +++ b/src/engine/scene/node/emitternode.h @@ -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 modelNode, SceneGraph *sceneGraph); void update(float dt) override; - void drawParticles(const std::vector &particles); + void drawLeafs(const std::vector> &leafs, int count) override; void detonate(); diff --git a/src/engine/scene/node/grassnode.cpp b/src/engine/scene/node/grassnode.cpp index fe80bbbc..360cdde5 100644 --- a/src/engine/scene/node/grassnode.cpp +++ b/src/engine/scene/node/grassnode.cpp @@ -48,11 +48,16 @@ void GrassSceneNode::clear() { _clusters.clear(); } -void GrassSceneNode::addCluster(Cluster cluster) { +void GrassSceneNode::addCluster(shared_ptr cluster) { _clusters.push_back(move(cluster)); } -void GrassSceneNode::drawClusters(const vector &clusters) { +void GrassSceneNode::drawLeafs(const vector> &leafs, int count) { + if (leafs.empty()) return; + if (count == -1) { + count = static_cast(leafs.size()); + } + setActiveTextureUnit(TextureUnits::diffuseMap); _texture->bind(); @@ -66,15 +71,15 @@ void GrassSceneNode::drawClusters(const vector &clusters) { uniforms.combined.featureMask |= UniformFeatureFlags::lightmap; } - int numClusters = static_cast(clusters.size()); - for (int i = 0; i < numClusters; ++i) { + for (int i = 0; i < count; ++i) { + auto cluster = static_pointer_cast(leafs[i]); uniforms.grass->quadSize = _quadSize; - uniforms.grass->clusters[i].positionVariant = glm::vec4(clusters[i].position, static_cast(clusters[i].variant)); - uniforms.grass->clusters[i].lightmapUV = clusters[i].lightmapUV; + uniforms.grass->clusters[i].positionVariant = glm::vec4(cluster->position, static_cast(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 diff --git a/src/engine/scene/node/grassnode.h b/src/engine/scene/node/grassnode.h index 7c691816..5b381617 100644 --- a/src/engine/scene/node/grassnode.h +++ b/src/engine/scene/node/grassnode.h @@ -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 texture, std::shared_ptr lightmap, SceneGraph *graph); void clear(); - void addCluster(Cluster cluster); + void addCluster(std::shared_ptr cluster); - void drawClusters(const std::vector &clusters); + void drawLeafs(const std::vector> &leafs, int count) override; - const std::vector &clusters() const { return _clusters; } + const std::vector> &clusters() const { return _clusters; } private: glm::vec2 _quadSize { 0.0f }; std::shared_ptr _texture; std::shared_ptr _lightmap; - std::vector _clusters; + std::vector> _clusters; }; } // namespace scene diff --git a/src/engine/scene/node/scenenode.h b/src/engine/scene/node/scenenode.h index 60f77157..d63dee79 100644 --- a/src/engine/scene/node/scenenode.h +++ b/src/engine/scene/node/scenenode.h @@ -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> &leafs, int count = -1) {} + bool isVisible() const { return _visible; } bool isCullable() const { return _cullable; } bool isCulled() const { return _culled; } diff --git a/src/engine/scene/scenegraph.cpp b/src/engine/scene/scenegraph.cpp index 3d0dc750..02021452 100644 --- a/src/engine/scene/scenegraph.cpp +++ b/src/engine/scene/scenegraph.cpp @@ -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> particlesZ; + vector, 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 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> 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 &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 clusters; - vector> 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(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 diff --git a/src/engine/scene/scenegraph.h b/src/engine/scene/scenegraph.h index 63d85768..349b6ceb 100644 --- a/src/engine/scene/scenegraph.h +++ b/src/engine/scene/scenegraph.h @@ -121,8 +121,7 @@ private: std::vector _lights; std::vector _emitters; std::vector _grass; - std::vector>> _particles; - std::vector>> _grassClusters; + std::vector>>> _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 &particles); + void prepareLeafs(); }; } // namespace scene diff --git a/src/engine/scene/sceneleaf.h b/src/engine/scene/sceneleaf.h new file mode 100644 index 00000000..46f9ae1c --- /dev/null +++ b/src/engine/scene/sceneleaf.h @@ -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 . + */ + +#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