Disable back-face culling for particles, grass and etc.

This commit is contained in:
Vsevolod Kremianskii 2021-05-16 14:08:25 +07:00
parent 81349e79f0
commit c4985f8684
8 changed files with 87 additions and 132 deletions

View file

@ -308,7 +308,6 @@ set(SCENE_HEADERS
set(SCENE_SOURCES
src/engine/scene/node/cameranode.cpp
src/engine/scene/node/emitternode.cpp
src/engine/scene/node/emitternode_particle.cpp
src/engine/scene/node/grassnode.cpp
src/engine/scene/node/lightnode.cpp
src/engine/scene/node/meshnode.cpp

View file

@ -64,13 +64,13 @@ void StateManager::withDepthTest(const function<void()> &block) {
}
void StateManager::setDepthTestEnabled(bool enabled) {
if (_depthTestEnabled != enabled) {
if (_depthTest != enabled) {
if (enabled) {
glEnable(GL_DEPTH_TEST);
} else {
glDisable(GL_DEPTH_TEST);
}
_depthTestEnabled = enabled;
_depthTest = enabled;
}
}
@ -107,9 +107,20 @@ void StateManager::withLightenBlending(const function<void()> &block) {
}
void StateManager::withBackFaceCulling(const function<void()> &block) {
glEnable(GL_CULL_FACE);
setBackFaceCullingEnabled(true);
block();
glDisable(GL_CULL_FACE);
setBackFaceCullingEnabled(false);
}
void StateManager::setBackFaceCullingEnabled(bool enabled) {
if (_backFaceCulling != enabled) {
if (enabled) {
glEnable(GL_CULL_FACE);
} else {
glDisable(GL_CULL_FACE);
}
_backFaceCulling = enabled;
}
}
void StateManager::setActiveTextureUnit(int n) {

View file

@ -39,14 +39,15 @@ public:
void withLightenBlending(const std::function<void()> &block);
void withBackFaceCulling(const std::function<void()> &block);
void setDepthTestEnabled(bool enabled);
void setBackFaceCullingEnabled(bool enabled);
void setActiveTextureUnit(int n);
private:
bool _depthTest { false };
bool _backFaceCulling { false };
int _textureUnit { 0 };
bool _depthTestEnabled { false };
uint32_t _polygonMode { 0 };
void setDepthTestEnabled(bool enabled);
};

View file

@ -77,9 +77,6 @@ EmitterSceneNode::EmitterSceneNode(const ModelSceneNode *model, shared_ptr<Model
}
void EmitterSceneNode::update(float dt) {
shared_ptr<CameraSceneNode> camera(_sceneGraph->activeCamera());
if (!camera) return;
removeExpiredParticles(dt);
spawnParticles(dt);
@ -89,19 +86,16 @@ void EmitterSceneNode::update(float dt) {
}
void EmitterSceneNode::removeExpiredParticles(float dt) {
for (auto it = _particles.begin(); it != _particles.end(); ) {
auto &particle = (*it);
if (isParticleExpired(*particle)) {
it = _particles.erase(it);
} else {
++it;
}
}
auto expiredParticles = find_if(_particles.begin(), _particles.end(), [this](auto &particle) { return isParticleExpired(*particle); });
_particles.erase(expiredParticles, _particles.end());
}
bool EmitterSceneNode::isParticleExpired(Particle &particle) const {
return _lifeExpectancy != -1.0f && particle.lifetime >= _lifeExpectancy;
}
void EmitterSceneNode::spawnParticles(float dt) {
shared_ptr<ModelNode::Emitter> emitter(_modelNode->emitter());
switch (emitter->updateMode) {
case ModelNode::Emitter::UpdateMode::Fountain:
if (_birthrate != 0.0f) {
@ -194,6 +188,61 @@ void EmitterSceneNode::detonate() {
doSpawnParticle();
}
void EmitterSceneNode::initParticle(Particle &particle) {
if (_fps > 0.0f) {
particle.animLength = (_frameEnd - _frameStart + 1) / _fps;
}
particle.frame = _frameStart;
}
void EmitterSceneNode::updateParticle(Particle &particle, float dt) {
shared_ptr<ModelNode::Emitter> emitter(_modelNode->emitter());
if (_lifeExpectancy != -1.0f) {
particle.lifetime = glm::min(particle.lifetime + dt, _lifeExpectancy);
} else if (particle.lifetime == particle.animLength) {
particle.lifetime = 0.0f;
} else {
particle.lifetime = glm::min(particle.lifetime + dt, particle.animLength);
}
if (!isParticleExpired(particle)) {
particle.position.z += particle.velocity * dt;
updateParticleAnimation(particle, dt);
}
}
template <class T>
static T interpolateConstraints(const EmitterSceneNode::Constraints<T> &constraints, float t) {
T result;
if (t < 0.5f) {
float tt = 2.0f * t;
result = (1.0f - tt) * constraints.start + tt * constraints.mid;
} else {
float tt = 2.0f * (t - 0.5f);
result = (1.0f - tt) * constraints.mid + tt * constraints.end;
}
return result;
}
void EmitterSceneNode::updateParticleAnimation(Particle &particle, float dt) {
shared_ptr<ModelNode::Emitter> emitter(_modelNode->emitter());
float maturity;
if (_lifeExpectancy != -1.0f) {
maturity = particle.lifetime / static_cast<float>(_lifeExpectancy);
} else if (particle.animLength > 0.0f) {
maturity = particle.lifetime / particle.animLength;
} else {
maturity = 0.0f;
}
particle.frame = static_cast<int>(glm::ceil(_frameStart + maturity * (_frameEnd - _frameStart)));
particle.size = interpolateConstraints(_particleSize, maturity);
particle.color = interpolateConstraints(_color, maturity);
particle.alpha = interpolateConstraints(_alpha, maturity);
}
} // namespace scene
} // namespace reone

View file

@ -17,10 +17,6 @@
#pragma once
#include <vector>
#include "glm/vec3.hpp"
#include "../../common/timer.h"
#include "../../graphics/model/modelnode.h"
@ -56,12 +52,6 @@ public:
EmitterSceneNode(const ModelSceneNode *model, std::shared_ptr<graphics::ModelNode> modelNode, SceneGraph *sceneGraph);
void update(float dt) override;
/**
* Renders the specified particles.
*
* @param particles subset of this emitters particles
*/
void drawParticles(const std::vector<Particle *> &particles);
void detonate();
@ -94,15 +84,11 @@ private:
void removeExpiredParticles(float dt);
void doSpawnParticle();
// Particles
void initParticle(Particle &particle);
void updateParticle(Particle &particle, float dt);
void updateParticleAnimation(Particle &particle, float dt);
bool isParticleExpired(Particle &particle) const;
// END Particles
};
} // namespace scene

View file

@ -1,93 +0,0 @@
/*
* 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/>.
*/
#include "emitternode.h"
using namespace std;
using namespace reone::graphics;
namespace reone {
namespace scene {
void EmitterSceneNode::initParticle(Particle &particle) {
shared_ptr<ModelNode::Emitter> emitter(_modelNode->emitter());
if (_fps > 0) {
particle.animLength = (_frameEnd - _frameStart + 1) / static_cast<float>(_fps);
}
particle.frame = _frameStart;
}
void EmitterSceneNode::updateParticle(Particle &particle, float dt) {
shared_ptr<ModelNode::Emitter> emitter(_modelNode->emitter());
if (_lifeExpectancy != -1) {
particle.lifetime = glm::min(particle.lifetime + dt, static_cast<float>(_lifeExpectancy));
} else if (particle.lifetime == particle.animLength) {
particle.lifetime = 0.0f;
} else {
particle.lifetime = glm::min(particle.lifetime + dt, particle.animLength);
}
if (!isParticleExpired(particle)) {
particle.position.z += particle.velocity * dt;
updateParticleAnimation(particle, dt);
}
}
template <class T>
static T interpolateConstraints(const EmitterSceneNode::Constraints<T> &constraints, float t) {
T result;
if (t < 0.5f) {
float tt = 2.0f * t;
result = (1.0f - tt) * constraints.start + tt * constraints.mid;
} else {
float tt = 2.0f * (t - 0.5f);
result = (1.0f - tt) * constraints.mid + tt * constraints.end;
}
return result;
}
void EmitterSceneNode::updateParticleAnimation(Particle &particle, float dt) {
shared_ptr<ModelNode::Emitter> emitter(_modelNode->emitter());
float maturity;
if (_lifeExpectancy != -1) {
maturity = particle.lifetime / static_cast<float>(_lifeExpectancy);
} else if (particle.animLength > 0.0f) {
maturity = particle.lifetime / particle.animLength;
} else {
maturity = 0.0f;
}
particle.frame = static_cast<int>(glm::ceil(_frameStart + maturity * (_frameEnd - _frameStart)));
particle.size = interpolateConstraints(_particleSize, maturity);
particle.color = interpolateConstraints(_color, maturity);
particle.alpha = interpolateConstraints(_alpha, maturity);
}
bool EmitterSceneNode::isParticleExpired(Particle &particle) const {
shared_ptr<ModelNode::Emitter> emitter(_modelNode->emitter());
return _lifeExpectancy != -1 && particle.lifetime >= _lifeExpectancy;
}
} // namespace scene
} // namespace reone

View file

@ -136,11 +136,8 @@ void WorldRenderPipeline::render() {
computeLightSpaceMatrices();
StateManager::instance().withBackFaceCulling([this]() {
drawShadows();
drawGeometry();
});
drawShadows();
drawGeometry();
applyHorizontalBlur();
applyVerticalBlur();
drawResult();

View file

@ -22,6 +22,7 @@
#include "glm/gtx/transform.hpp"
#include "../graphics/mesh/meshes.h"
#include "../graphics/statemanager.h"
#include "node/cameranode.h"
#include "node/emitternode.h"
@ -311,6 +312,8 @@ void SceneGraph::draw(bool shadowPass) {
return;
}
StateManager::instance().setBackFaceCullingEnabled(true);
// Render opaque meshes
for (auto &mesh : _opaqueMeshes) {
mesh->drawSingle(false);
@ -334,6 +337,8 @@ void SceneGraph::draw(bool shadowPass) {
mesh->drawSingle(false);
}
StateManager::instance().setBackFaceCullingEnabled(false);
// Render grass
for (auto &grass : _grassClusters) {
grass.first->drawClusters(grass.second);