2014-07-13 18:56:17 +02:00

1777 lines
66 KiB
C++

//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2009 Joerg Henrichs
//
// 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, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "graphics/irr_driver.hpp"
#include "config/user_config.hpp"
#include "graphics/callbacks.hpp"
#include "graphics/camera.hpp"
#include "graphics/glwrap.hpp"
#include "graphics/lens_flare.hpp"
#include "graphics/light.hpp"
#include "graphics/lod_node.hpp"
#include "graphics/material_manager.hpp"
#include "graphics/particle_kind_manager.hpp"
#include "graphics/per_camera_node.hpp"
#include "graphics/post_processing.hpp"
#include "graphics/referee.hpp"
#include "graphics/rtts.hpp"
#include "graphics/screenquad.hpp"
#include "graphics/shaders.hpp"
#include "graphics/stkmeshscenenode.hpp"
#include "graphics/stkinstancedscenenode.hpp"
#include "graphics/wind.hpp"
#include "io/file_manager.hpp"
#include "items/item.hpp"
#include "items/item_manager.hpp"
#include "modes/world.hpp"
#include "physics/physics.hpp"
#include "tracks/track.hpp"
#include "utils/constants.hpp"
#include "utils/helpers.hpp"
#include "utils/log.hpp"
#include "utils/profiler.hpp"
#include <algorithm>
#include <limits>
#define MAX2(a, b) ((a) > (b) ? (a) : (b))
#define MIN2(a, b) ((a) > (b) ? (b) : (a))
void IrrDriver::renderGLSL(float dt)
{
World *world = World::getWorld(); // Never NULL.
Track *track = world->getTrack();
// Overrides
video::SOverrideMaterial &overridemat = m_video_driver->getOverrideMaterial();
overridemat.EnablePasses = scene::ESNRP_SOLID | scene::ESNRP_TRANSPARENT;
overridemat.EnableFlags = 0;
if (m_wireframe)
{
overridemat.Material.Wireframe = 1;
overridemat.EnableFlags |= video::EMF_WIREFRAME;
}
if (m_mipviz)
{
overridemat.Material.MaterialType = m_shaders->getShader(ES_MIPVIZ);
overridemat.EnableFlags |= video::EMF_MATERIAL_TYPE;
overridemat.EnablePasses = scene::ESNRP_SOLID;
}
// Get a list of all glowing things. The driver's list contains the static ones,
// here we add items, as they may disappear each frame.
std::vector<GlowData> glows = m_glowing;
ItemManager * const items = ItemManager::get();
const u32 itemcount = items->getNumberOfItems();
u32 i;
for (i = 0; i < itemcount; i++)
{
Item * const item = items->getItem(i);
if (!item) continue;
const Item::ItemType type = item->getType();
if (type != Item::ITEM_NITRO_BIG && type != Item::ITEM_NITRO_SMALL &&
type != Item::ITEM_BONUS_BOX && type != Item::ITEM_BANANA && type != Item::ITEM_BUBBLEGUM)
continue;
LODNode * const lod = (LODNode *) item->getSceneNode();
if (!lod->isVisible()) continue;
const int level = lod->getLevel();
if (level < 0) continue;
scene::ISceneNode * const node = lod->getAllNodes()[level];
node->updateAbsolutePosition();
GlowData dat;
dat.node = node;
dat.r = 1.0f;
dat.g = 1.0f;
dat.b = 1.0f;
const video::SColorf &c = ItemManager::getGlowColor(type);
dat.r = c.getRed();
dat.g = c.getGreen();
dat.b = c.getBlue();
glows.push_back(dat);
}
// Start the RTT for post-processing.
// We do this before beginScene() because we want to capture the glClear()
// because of tracks that do not have skyboxes (generally add-on tracks)
m_post_processing->begin();
RaceGUIBase *rg = world->getRaceGUI();
if (rg) rg->update(dt);
for(unsigned int cam = 0; cam < Camera::getNumCameras(); cam++)
{
Camera * const camera = Camera::getCamera(cam);
scene::ICameraSceneNode * const camnode = camera->getCameraSceneNode();
std::ostringstream oss;
oss << "drawAll() for kart " << cam;
PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), (cam+1)*60,
0x00, 0x00);
camera->activate();
rg->preRenderCallback(camera); // adjusts start referee
m_scene_manager->setActiveCamera(camnode);
const core::recti &viewport = camera->getViewport();
unsigned plc = UpdateLightsInfo(camnode, dt);
computeCameraMatrix(camnode, viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y);
renderScene(camnode, plc, glows, dt, track->hasShadows(), false);
// Debug physic
// Note that drawAll must be called before rendering
// the bullet debug view, since otherwise the camera
// is not set up properly. This is only used for
// the bullet debug view.
if (UserConfigParams::m_artist_debug_mode)
World::getWorld()->getPhysics()->draw();
if (world != NULL && world->getPhysics() != NULL)
{
IrrDebugDrawer* debug_drawer = world->getPhysics()->getDebugDrawer();
if (debug_drawer != NULL && debug_drawer->debugEnabled())
{
const std::map<video::SColor, std::vector<float> >& lines = debug_drawer->getLines();
std::map<video::SColor, std::vector<float> >::const_iterator it;
glUseProgram(UtilShader::ColoredLine::Program);
glBindVertexArray(UtilShader::ColoredLine::vao);
glBindBuffer(GL_ARRAY_BUFFER, UtilShader::ColoredLine::vbo);
for (it = lines.begin(); it != lines.end(); it++)
{
UtilShader::ColoredLine::setUniforms(it->first);
const std::vector<float> &vertex = it->second;
const float *tmp = vertex.data();
for (unsigned int i = 0; i < vertex.size(); i += 1024 * 6)
{
unsigned count = MIN2(vertex.size() - i, 1024 * 6);
glBufferSubData(GL_ARRAY_BUFFER, 0, count * sizeof(float), &tmp[i]);
glDrawArrays(GL_LINES, 0, count / 3);
}
}
glUseProgram(0);
glBindVertexArray(0);
}
}
// Render the post-processed scene
if (UserConfigParams::m_dynamic_lights)
{
bool isRace = StateManager::get()->getGameState() == GUIEngine::GAME;
FrameBuffer *fbo = m_post_processing->render(camnode, isRace);
if (irr_driver->getNormals())
irr_driver->getFBO(FBO_NORMAL_AND_DEPTHS).BlitToDefault(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y);
else if (irr_driver->getSSAOViz())
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y);
m_post_processing->renderPassThrough(m_rtts->getFBO(FBO_HALF1_R).getRTT()[0]);
}
else if (irr_driver->getRSM())
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y);
m_post_processing->renderPassThrough(m_rtts->getRSM().getRTT()[0]);
}
else if (irr_driver->getShadowViz())
{
renderShadowsDebug();
}
else
fbo->BlitToDefault(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y);
}
PROFILER_POP_CPU_MARKER();
} // for i<world->getNumKarts()
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glUseProgram(0);
// Set the viewport back to the full screen for race gui
m_video_driver->setViewPort(core::recti(0, 0,
UserConfigParams::m_width,
UserConfigParams::m_height));
for(unsigned int i=0; i<Camera::getNumCameras(); i++)
{
Camera *camera = Camera::getCamera(i);
std::ostringstream oss;
oss << "renderPlayerView() for kart " << i;
PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), 0x00, 0x00, (i+1)*60);
rg->renderPlayerView(camera, dt);
PROFILER_POP_CPU_MARKER();
} // for i<getNumKarts
{
ScopedGPUTimer Timer(getGPUTimer(Q_GUI));
PROFILER_PUSH_CPU_MARKER("GUIEngine", 0x75, 0x75, 0x75);
// Either render the gui, or the global elements of the race gui.
GUIEngine::render(dt);
PROFILER_POP_CPU_MARKER();
}
// Render the profiler
if(UserConfigParams::m_profiler_enabled)
{
PROFILER_DRAW();
}
#ifdef DEBUG
drawDebugMeshes();
#endif
PROFILER_PUSH_CPU_MARKER("EndSccene", 0x45, 0x75, 0x45);
m_video_driver->endScene();
PROFILER_POP_CPU_MARKER();
getPostProcessing()->update(dt);
}
void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned pointlightcount, std::vector<GlowData>& glows, float dt, bool hasShadow, bool forceRTT)
{
glBindBufferBase(GL_UNIFORM_BUFFER, 0, SharedObject::ViewProjectionMatrixesUBO);
PROFILER_PUSH_CPU_MARKER("- Solid Pass 1", 0xFF, 0x00, 0x00);
renderSolidFirstPass();
PROFILER_POP_CPU_MARKER();
// Shadows
{
PROFILER_PUSH_CPU_MARKER("- Shadow", 0x30, 0x6F, 0x90);
ScopedGPUTimer Timer(getGPUTimer(Q_SHADOWS));
// To avoid wrong culling, use the largest view possible
m_scene_manager->setActiveCamera(m_suncam);
if (!m_mipviz && !m_wireframe && UserConfigParams::m_dynamic_lights &&
UserConfigParams::m_shadows && hasShadow)
renderShadows();
m_scene_manager->setActiveCamera(camnode);
PROFILER_POP_CPU_MARKER();
}
// Lights
{
PROFILER_PUSH_CPU_MARKER("- Light", 0x00, 0xFF, 0x00);
renderLights(pointlightcount);
PROFILER_POP_CPU_MARKER();
}
// Handle SSAO
{
PROFILER_PUSH_CPU_MARKER("- SSAO", 0xFF, 0xFF, 0x00);
ScopedGPUTimer Timer(getGPUTimer(Q_SSAO));
if (UserConfigParams::m_ssao)
renderSSAO();
PROFILER_POP_CPU_MARKER();
}
PROFILER_PUSH_CPU_MARKER("- Solid Pass 2", 0x00, 0x00, 0xFF);
if (!UserConfigParams::m_dynamic_lights && ! forceRTT)
{
glEnable(GL_FRAMEBUFFER_SRGB);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
else
m_rtts->getFBO(FBO_COLORS).Bind();
renderSolidSecondPass();
PROFILER_POP_CPU_MARKER();
if (UserConfigParams::m_dynamic_lights && World::getWorld() != NULL &&
World::getWorld()->isFogEnabled())
{
PROFILER_PUSH_CPU_MARKER("- Fog", 0xFF, 0x00, 0x00);
m_post_processing->renderFog();
PROFILER_POP_CPU_MARKER();
}
PROFILER_PUSH_CPU_MARKER("- Skybox", 0xFF, 0x00, 0xFF);
renderSkybox(camnode);
PROFILER_POP_CPU_MARKER();
if (getRH())
{
glEnable(GL_PROGRAM_POINT_SIZE);
m_rtts->getFBO(FBO_COLORS).Bind();
glUseProgram(FullScreenShader::RHDebug::Program);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, m_rtts->getRH().getRTT()[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_3D, m_rtts->getRH().getRTT()[1]);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_3D, m_rtts->getRH().getRTT()[2]);
FullScreenShader::RHDebug::setUniforms(rh_matrix, rh_extend, 0, 1, 2);
glDrawArrays(GL_POINTS, 0, 32 * 16 * 32);
glDisable(GL_PROGRAM_POINT_SIZE);
}
if (getGI())
{
m_rtts->getFBO(FBO_COLORS).Bind();
m_post_processing->renderGI(rh_matrix, rh_extend, m_rtts->getRH().getRTT()[0], m_rtts->getRH().getRTT()[1], m_rtts->getRH().getRTT()[2]);
}
PROFILER_PUSH_CPU_MARKER("- Glow", 0xFF, 0xFF, 0x00);
// Render anything glowing.
if (!m_mipviz && !m_wireframe && UserConfigParams::m_glow)
{
irr_driver->setPhase(GLOW_PASS);
renderGlow(glows);
} // end glow
PROFILER_POP_CPU_MARKER();
PROFILER_PUSH_CPU_MARKER("- Lensflare/godray", 0x00, 0xFF, 0xFF);
computeSunVisibility();
PROFILER_POP_CPU_MARKER();
// Render transparent
{
PROFILER_PUSH_CPU_MARKER("- Transparent Pass", 0xFF, 0x00, 0x00);
ScopedGPUTimer Timer(getGPUTimer(Q_TRANSPARENT));
renderTransparent();
PROFILER_POP_CPU_MARKER();
}
// Render particles
{
PROFILER_PUSH_CPU_MARKER("- Particles", 0xFF, 0xFF, 0x00);
ScopedGPUTimer Timer(getGPUTimer(Q_PARTICLES));
renderParticles();
PROFILER_POP_CPU_MARKER();
}
if (!UserConfigParams::m_dynamic_lights && !forceRTT)
{
glDisable(GL_FRAMEBUFFER_SRGB);
return;
}
// Render displacement
{
PROFILER_PUSH_CPU_MARKER("- Displacement", 0x00, 0x00, 0xFF);
ScopedGPUTimer Timer(getGPUTimer(Q_DISPLACEMENT));
renderDisplacement();
PROFILER_POP_CPU_MARKER();
}
// Ensure that no object will be drawn after that by using invalid pass
irr_driver->setPhase(PASS_COUNT);
}
// --------------------------------------------
void IrrDriver::renderFixed(float dt)
{
World *world = World::getWorld(); // Never NULL.
m_video_driver->beginScene(/*backBuffer clear*/ true, /*zBuffer*/ true,
world->getClearColor());
irr_driver->getVideoDriver()->enableMaterial2D();
RaceGUIBase *rg = world->getRaceGUI();
if (rg) rg->update(dt);
for(unsigned int i=0; i<Camera::getNumCameras(); i++)
{
Camera *camera = Camera::getCamera(i);
std::ostringstream oss;
oss << "drawAll() for kart " << i;
PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), (i+1)*60,
0x00, 0x00);
camera->activate();
rg->preRenderCallback(camera); // adjusts start referee
m_renderpass = ~0;
m_scene_manager->drawAll();
PROFILER_POP_CPU_MARKER();
// Note that drawAll must be called before rendering
// the bullet debug view, since otherwise the camera
// is not set up properly. This is only used for
// the bullet debug view.
if (UserConfigParams::m_artist_debug_mode)
World::getWorld()->getPhysics()->draw();
} // for i<world->getNumKarts()
// Set the viewport back to the full screen for race gui
m_video_driver->setViewPort(core::recti(0, 0,
UserConfigParams::m_width,
UserConfigParams::m_height));
for(unsigned int i=0; i<Camera::getNumCameras(); i++)
{
Camera *camera = Camera::getCamera(i);
std::ostringstream oss;
oss << "renderPlayerView() for kart " << i;
PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), 0x00, 0x00, (i+1)*60);
rg->renderPlayerView(camera, dt);
PROFILER_POP_CPU_MARKER();
} // for i<getNumKarts
// Either render the gui, or the global elements of the race gui.
GUIEngine::render(dt);
// Render the profiler
if(UserConfigParams::m_profiler_enabled)
{
PROFILER_DRAW();
}
#ifdef DEBUG
drawDebugMeshes();
#endif
m_video_driver->endScene();
}
// ----------------------------------------------------------------------------
void IrrDriver::computeSunVisibility()
{
// Is the lens flare enabled & visible? Check last frame's query.
bool hasflare = false;
bool hasgodrays = false;
if (World::getWorld() != NULL)
{
hasflare = World::getWorld()->getTrack()->hasLensFlare();
hasgodrays = World::getWorld()->getTrack()->hasGodRays();
}
irr::video::COpenGLDriver* gl_driver = (irr::video::COpenGLDriver*)m_device->getVideoDriver();
if (UserConfigParams::m_light_shaft && hasgodrays)//hasflare || hasgodrays)
{
GLuint res = 0;
if (m_query_issued)
gl_driver->extGlGetQueryObjectuiv(m_lensflare_query, GL_QUERY_RESULT, &res);
m_post_processing->setSunPixels(res);
// Prepare the query for the next frame.
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
gl_driver->extGlBeginQuery(GL_SAMPLES_PASSED_ARB, m_lensflare_query);
m_scene_manager->setCurrentRendertime(scene::ESNRP_SOLID);
m_scene_manager->drawAll(scene::ESNRP_CAMERA);
irr_driver->setPhase(GLOW_PASS);
m_sun_interposer->render();
gl_driver->extGlEndQuery(GL_SAMPLES_PASSED_ARB);
m_query_issued = true;
m_lensflare->setStrength(res / 4000.0f);
if (hasflare)
m_lensflare->OnRegisterSceneNode();
// Make sure the color mask is reset
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
}
template<unsigned N>
struct unroll_args_instance
{
template<typename T, typename ...TupleTypes, typename ...Args>
static void exec(const T *Shader, const std::tuple<TupleTypes...> &t, Args... args)
{
unroll_args_instance<N - 1>::template exec<T>(Shader, t, std::get<N - 1>(t), args...);
}
};
template<>
struct unroll_args_instance<0>
{
template<typename T, typename ...TupleTypes, typename ...Args>
static void exec(const T *Shader, const std::tuple<TupleTypes...> &t, Args... args)
{
draw<T>(Shader, args...);
}
};
template<typename T, typename... TupleType>
void apply_instance(const T *Shader, const std::tuple<TupleType...> &arg)
{
unroll_args_instance<std::tuple_size<std::tuple<TupleType...> >::value >::template exec<T>(Shader, arg);
}
template<typename T, enum E_VERTEX_TYPE VertexType, typename... TupleType>
void renderMeshes1stPass(const T *Shader, const std::vector<GLuint> &TexUnits, std::vector<std::tuple<TupleType...> > &meshes)
{
glUseProgram(Shader->Program);
glBindVertexArray(getVAO(VertexType));
for (unsigned i = 0; i < meshes.size(); i++)
{
GLMesh &mesh = *(std::get<0>(meshes[i]));
for (unsigned j = 0; j < TexUnits.size(); j++)
{
if (!mesh.textures[j])
mesh.textures[j] = getUnicolorTexture(video::SColor(255, 255, 255, 255));
compressTexture(mesh.textures[j], true);
setTexture(TexUnits[j], getTextureGLuint(mesh.textures[j]), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true);
}
if (mesh.VAOType != VertexType)
{
#ifdef DEBUG
Log::error("Materials", "Wrong vertex Type associed to pass 1 (hint texture : %s)", mesh.textures[0]->getName().getPath().c_str());
#endif
continue;
}
apply_instance(Shader, meshes[i]);
}
}
void IrrDriver::renderSolidFirstPass()
{
m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).Bind();
glClearColor(0., 0., 0., 0.);
glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
glEnable(GL_CULL_FACE);
irr_driver->setPhase(SOLID_NORMAL_AND_DEPTH_PASS);
ListDefaultStandardG::Arguments.clear();
ListDefault2TCoordG::Arguments.clear();
ListAlphaRefG::Arguments.clear();
ListNormalG::Arguments.clear();
ListGrassG::Arguments.clear();
m_scene_manager->drawAll(scene::ESNRP_SOLID);
if (!UserConfigParams::m_dynamic_lights)
return;
{
ScopedGPUTimer Timer(getGPUTimer(Q_SOLID_PASS1));
renderMeshes1stPass<MeshShader::ObjectPass1Shader, video::EVT_STANDARD>(MeshShader::ObjectPass1ShaderInstance, { MeshShader::ObjectPass1ShaderInstance->TU_tex }, ListDefaultStandardG::Arguments);
renderMeshes1stPass<MeshShader::ObjectPass1Shader, video::EVT_2TCOORDS>(MeshShader::ObjectPass1ShaderInstance, { MeshShader::ObjectPass1ShaderInstance->TU_tex }, ListDefault2TCoordG::Arguments);
renderMeshes1stPass<MeshShader::ObjectRefPass1Shader, video::EVT_STANDARD>(MeshShader::ObjectRefPass1ShaderInstance, { MeshShader::ObjectRefPass1ShaderInstance->TU_tex }, ListAlphaRefG::Arguments);
renderMeshes1stPass<MeshShader::NormalMapShader, video::EVT_TANGENTS>(MeshShader::NormalMapShaderInstance, { MeshShader::NormalMapShaderInstance->TU_glossy, MeshShader::NormalMapShaderInstance->TU_normalmap }, ListNormalG::Arguments);
renderMeshes1stPass<MeshShader::GrassPass1Shader, video::EVT_STANDARD>(MeshShader::GrassPass1ShaderInstance, { MeshShader::GrassPass1ShaderInstance->TU_tex }, ListGrassG::Arguments);
}
}
template<typename T, enum E_VERTEX_TYPE VertexType, typename... TupleType>
void renderMeshes2ndPass(const T *Shader, const std::vector<GLuint> &TexUnits, std::vector<std::tuple<TupleType...> > &meshes)
{
glUseProgram(Shader->Program);
glBindVertexArray(getVAO(VertexType));
for (unsigned i = 0; i < meshes.size(); i++)
{
GLMesh &mesh = *(std::get<0>(meshes[i]));
for (unsigned j = 0; j < TexUnits.size(); j++)
{
if (!mesh.textures[j])
mesh.textures[j] = getUnicolorTexture(video::SColor(255, 255, 255, 255));
compressTexture(mesh.textures[j], true);
setTexture(TexUnits[j], getTextureGLuint(mesh.textures[j]), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true);
if (irr_driver->getLightViz())
{
GLint swizzleMask[] = { GL_ONE, GL_ONE, GL_ONE, GL_ALPHA };
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
}
else
{
GLint swizzleMask[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA };
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
}
}
if (mesh.VAOType != VertexType)
{
#ifdef DEBUG
Log::error("Materials", "Wrong vertex Type associed to pass 2 (hint texture : %s)", mesh.textures[0]->getName().getPath().c_str());
#endif
continue;
}
apply_instance<T>(Shader, meshes[i]);
}
}
void IrrDriver::renderSolidSecondPass()
{
SColor clearColor(0, 150, 150, 150);
if (World::getWorld() != NULL)
clearColor = World::getWorld()->getClearColor();
glClearColor(clearColor.getRed() / 255.f, clearColor.getGreen() / 255.f,
clearColor.getBlue() / 255.f, clearColor.getAlpha() / 255.f);
glClear(GL_COLOR_BUFFER_BIT);
if (UserConfigParams::m_dynamic_lights)
glDepthMask(GL_FALSE);
else
{
glDepthMask(GL_TRUE);
glClear(GL_DEPTH_BUFFER_BIT);
}
irr_driver->setPhase(SOLID_LIT_PASS);
glEnable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
ListDefaultStandardSM::Arguments.clear();
ListDefaultTangentSM::Arguments.clear();
ListAlphaRefSM::Arguments.clear();
ListSphereMapSM::Arguments.clear();
ListUnlitSM::Arguments.clear();
ListDetailSM::Arguments.clear();
ListSplattingSM::Arguments.clear();
ListGrassSM::Arguments.clear();
setTexture(0, m_rtts->getRenderTarget(RTT_TMP1), GL_NEAREST, GL_NEAREST);
setTexture(1, m_rtts->getRenderTarget(RTT_TMP2), GL_NEAREST, GL_NEAREST);
setTexture(2, m_rtts->getRenderTarget(RTT_HALF1_R), GL_LINEAR, GL_LINEAR);
{
ScopedGPUTimer Timer(getGPUTimer(Q_SOLID_PASS2));
m_scene_manager->drawAll(scene::ESNRP_SOLID);
renderMeshes2ndPass<MeshShader::ObjectPass2Shader, video::EVT_STANDARD>(MeshShader::ObjectPass2ShaderInstance, { MeshShader::ObjectPass2ShaderInstance->TU_Albedo }, ListDefaultStandardSM::Arguments);
renderMeshes2ndPass<MeshShader::ObjectPass2Shader, video::EVT_TANGENTS>(MeshShader::ObjectPass2ShaderInstance, { MeshShader::ObjectPass2ShaderInstance->TU_Albedo }, ListDefaultTangentSM::Arguments);
renderMeshes2ndPass<MeshShader::ObjectRefPass2Shader, video::EVT_STANDARD>(MeshShader::ObjectRefPass2ShaderInstance, { MeshShader::ObjectRefPass2ShaderInstance->TU_Albedo }, ListAlphaRefSM::Arguments);
renderMeshes2ndPass<MeshShader::SphereMapShader, video::EVT_STANDARD>(MeshShader::SphereMapShaderInstance, { MeshShader::SphereMapShaderInstance->TU_tex }, ListSphereMapSM::Arguments);
renderMeshes2ndPass<MeshShader::ObjectUnlitShader, video::EVT_STANDARD>(MeshShader::ObjectUnlitShaderInstance, { MeshShader::ObjectUnlitShaderInstance->TU_tex }, ListUnlitSM::Arguments);
renderMeshes2ndPass<MeshShader::DetailledObjectPass2Shader, video::EVT_2TCOORDS>(MeshShader::DetailledObjectPass2ShaderInstance, { MeshShader::DetailledObjectPass2ShaderInstance->TU_Albedo, MeshShader::DetailledObjectPass2ShaderInstance->TU_detail }, ListDetailSM::Arguments);
renderMeshes2ndPass<MeshShader::SplattingShader, video::EVT_2TCOORDS>(MeshShader::SplattingShaderInstance, { 8, MeshShader::SplattingShaderInstance->TU_tex_layout, MeshShader::SplattingShaderInstance->TU_tex_detail0, MeshShader::SplattingShaderInstance->TU_tex_detail1, MeshShader::SplattingShaderInstance->TU_tex_detail2, MeshShader::SplattingShaderInstance->TU_tex_detail3 }, ListSplattingSM::Arguments);
renderMeshes2ndPass<MeshShader::GrassPass2Shader, video::EVT_STANDARD>(MeshShader::GrassPass2ShaderInstance, { MeshShader::GrassPass2ShaderInstance->TU_Albedo }, ListGrassSM::Arguments);
}
}
void IrrDriver::renderTransparent()
{
irr_driver->setPhase(TRANSPARENT_PASS);
glEnable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glDisable(GL_CULL_FACE);
ListBlendTransparent::Arguments.clear();
ListAdditiveTransparent::Arguments.clear();
ListBlendTransparentFog::Arguments.clear();
ListAdditiveTransparentFog::Arguments.clear();
m_scene_manager->drawAll(scene::ESNRP_TRANSPARENT);
glBindVertexArray(getVAO(EVT_STANDARD));
if (World::getWorld() && World::getWorld()->isFogEnabled())
{
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
renderMeshes2ndPass<MeshShader::TransparentFogShader, video::EVT_STANDARD>(MeshShader::TransparentFogShaderInstance, { MeshShader::TransparentFogShaderInstance->TU_tex }, ListBlendTransparentFog::Arguments);
glBlendFunc(GL_ONE, GL_ONE);
renderMeshes2ndPass<MeshShader::TransparentFogShader, video::EVT_STANDARD>(MeshShader::TransparentFogShaderInstance, { MeshShader::TransparentFogShaderInstance->TU_tex }, ListAdditiveTransparentFog::Arguments);
}
else
{
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
renderMeshes2ndPass<MeshShader::TransparentShader, video::EVT_STANDARD>(MeshShader::TransparentShaderInstance, { MeshShader::TransparentShaderInstance->TU_tex }, ListBlendTransparent::Arguments);
glBlendFunc(GL_ONE, GL_ONE);
renderMeshes2ndPass<MeshShader::TransparentShader, video::EVT_STANDARD>(MeshShader::TransparentShaderInstance, { MeshShader::TransparentShaderInstance->TU_tex }, ListAdditiveTransparent::Arguments);
}
}
void IrrDriver::renderParticles()
{
glDepthMask(GL_FALSE);
glDisable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
m_scene_manager->drawAll(scene::ESNRP_TRANSPARENT_EFFECT);
}
/** Given a matrix transform and a set of points returns an orthogonal projection matrix that maps coordinates of
transformed points between -1 and 1.
* \param transform a transform matrix.
* \param pointsInside a vector of point in 3d space.
*/
core::matrix4 getTighestFitOrthoProj(const core::matrix4 &transform, const std::vector<vector3df> &pointsInside)
{
float xmin = std::numeric_limits<float>::infinity();
float xmax = -std::numeric_limits<float>::infinity();
float ymin = std::numeric_limits<float>::infinity();
float ymax = -std::numeric_limits<float>::infinity();
float zmin = std::numeric_limits<float>::infinity();
float zmax = -std::numeric_limits<float>::infinity();
for (unsigned i = 0; i < pointsInside.size(); i++)
{
vector3df TransformedVector;
transform.transformVect(TransformedVector, pointsInside[i]);
xmin = MIN2(xmin, TransformedVector.X);
xmax = MAX2(xmax, TransformedVector.X);
ymin = MIN2(ymin, TransformedVector.Y);
ymax = MAX2(ymax, TransformedVector.Y);
zmin = MIN2(zmin, TransformedVector.Z);
zmax = MAX2(zmax, TransformedVector.Z);
}
float left = xmin;
float right = xmax;
float up = ymin;
float down = ymax;
core::matrix4 tmp_matrix;
// Prevent Matrix without extend
if (left == right || up == down)
return tmp_matrix;
tmp_matrix.buildProjectionMatrixOrthoLH(left, right,
down, up,
30, zmax);
return tmp_matrix;
}
void IrrDriver::computeCameraMatrix(scene::ICameraSceneNode * const camnode, size_t width, size_t height)
{
m_scene_manager->drawAll(scene::ESNRP_CAMERA);
irr_driver->setProjMatrix(irr_driver->getVideoDriver()->getTransform(video::ETS_PROJECTION));
irr_driver->setViewMatrix(irr_driver->getVideoDriver()->getTransform(video::ETS_VIEW));
irr_driver->genProjViewMatrix();
const float oldfar = camnode->getFarValue();
const float oldnear = camnode->getNearValue();
float FarValues[] =
{
6.,
21.,
55.,
150.,
};
float NearValues[] =
{
oldnear,
5.,
20.,
50.,
};
float *tmp = new float[18 * 8];
memcpy(tmp, irr_driver->getViewMatrix().pointer(), 16 * sizeof(float));
memcpy(&tmp[16], irr_driver->getProjMatrix().pointer(), 16 * sizeof(float));
memcpy(&tmp[32], irr_driver->getInvViewMatrix().pointer(), 16 * sizeof(float));
memcpy(&tmp[48], irr_driver->getInvProjMatrix().pointer(), 16 * sizeof(float));
const core::matrix4 &SunCamViewMatrix = m_suncam->getViewMatrix();
sun_ortho_matrix.clear();
if (World::getWorld() && World::getWorld()->getTrack())
{
const Vec3 *vmin, *vmax;
World::getWorld()->getTrack()->getAABB(&vmin, &vmax);
// Build the 3 ortho projection (for the 3 shadow resolution levels)
for (unsigned i = 0; i < 4; i++)
{
camnode->setFarValue(FarValues[i]);
camnode->setNearValue(NearValues[i]);
camnode->render();
const scene::SViewFrustum *frustrum = camnode->getViewFrustum();
float tmp[24] = {
frustrum->getFarLeftDown().X,
frustrum->getFarLeftDown().Y,
frustrum->getFarLeftDown().Z,
frustrum->getFarLeftUp().X,
frustrum->getFarLeftUp().Y,
frustrum->getFarLeftUp().Z,
frustrum->getFarRightDown().X,
frustrum->getFarRightDown().Y,
frustrum->getFarRightDown().Z,
frustrum->getFarRightUp().X,
frustrum->getFarRightUp().Y,
frustrum->getFarRightUp().Z,
frustrum->getNearLeftDown().X,
frustrum->getNearLeftDown().Y,
frustrum->getNearLeftDown().Z,
frustrum->getNearLeftUp().X,
frustrum->getNearLeftUp().Y,
frustrum->getNearLeftUp().Z,
frustrum->getNearRightDown().X,
frustrum->getNearRightDown().Y,
frustrum->getNearRightDown().Z,
frustrum->getNearRightUp().X,
frustrum->getNearRightUp().Y,
frustrum->getNearRightUp().Z,
};
memcpy(m_shadows_cam[i], tmp, 24 * sizeof(float));
const core::aabbox3df smallcambox = camnode->
getViewFrustum()->getBoundingBox();
core::aabbox3df trackbox(vmin->toIrrVector(), vmax->toIrrVector() -
core::vector3df(0, 30, 0));
// Set up a nice ortho projection that contains our camera frustum
core::aabbox3df box = smallcambox;
box = box.intersect(trackbox);
std::vector<vector3df> vectors;
vectors.push_back(frustrum->getFarLeftDown());
vectors.push_back(frustrum->getFarLeftUp());
vectors.push_back(frustrum->getFarRightDown());
vectors.push_back(frustrum->getFarRightUp());
vectors.push_back(frustrum->getNearLeftDown());
vectors.push_back(frustrum->getNearLeftUp());
vectors.push_back(frustrum->getNearRightDown());
vectors.push_back(frustrum->getNearRightUp());
/* SunCamViewMatrix.transformBoxEx(trackbox);
SunCamViewMatrix.transformBoxEx(box);
core::vector3df extent = box.getExtent();
const float w = fabsf(extent.X);
const float h = fabsf(extent.Y);
float z = box.MaxEdge.Z;
// Snap to texels
const float units_per_w = w / 1024;
const float units_per_h = h / 1024;*/
m_suncam->setProjectionMatrix(getTighestFitOrthoProj(SunCamViewMatrix, vectors) , true);
m_suncam->render();
sun_ortho_matrix.push_back(getVideoDriver()->getTransform(video::ETS_PROJECTION) * getVideoDriver()->getTransform(video::ETS_VIEW));
}
{
core::aabbox3df trackbox(vmin->toIrrVector(), vmax->toIrrVector() -
core::vector3df(0, 30, 0));
if (trackbox.MinEdge.X != trackbox.MaxEdge.X &&
trackbox.MinEdge.Y != trackbox.MaxEdge.Y &&
// Cover the case where SunCamViewMatrix is null
SunCamViewMatrix.getScale() != core::vector3df(0., 0., 0.))
{
SunCamViewMatrix.transformBoxEx(trackbox);
core::matrix4 tmp_matrix;
tmp_matrix.buildProjectionMatrixOrthoLH(trackbox.MinEdge.X, trackbox.MaxEdge.X,
trackbox.MaxEdge.Y, trackbox.MinEdge.Y,
30, trackbox.MaxEdge.Z);
m_suncam->setProjectionMatrix(tmp_matrix, true);
m_suncam->render();
}
rsm_matrix = getVideoDriver()->getTransform(video::ETS_PROJECTION) * getVideoDriver()->getTransform(video::ETS_VIEW);
}
rh_extend = core::vector3df(128, 64, 128);
core::vector3df campos = camnode->getAbsolutePosition();
core::vector3df translation(8 * floor(campos.X / 8), 8 * floor(campos.Y / 8), 8 * floor(campos.Z / 8));
rh_matrix.setTranslation(translation);
assert(sun_ortho_matrix.size() == 4);
camnode->setNearValue(oldnear);
camnode->setFarValue(oldfar);
size_t size = irr_driver->getShadowViewProj().size();
for (unsigned i = 0; i < size; i++)
memcpy(&tmp[16 * i + 64], irr_driver->getShadowViewProj()[i].pointer(), 16 * sizeof(float));
}
tmp[128] = float(width);
tmp[129] = float(height);
glBindBuffer(GL_UNIFORM_BUFFER, SharedObject::ViewProjectionMatrixesUBO);
glBufferSubData(GL_UNIFORM_BUFFER, 0, (16 * 8 + 2) * sizeof(float), tmp);
delete []tmp;
}
template<typename Shader, enum E_VERTEX_TYPE VertexType, typename... Args>
void drawShadow(const std::vector<GLuint> TextureUnits, const std::vector<std::tuple<GLMesh *, core::matrix4, Args...> >&t)
{
glUseProgram(Shader::Program);
glBindVertexArray(getVAO(VertexType));
for (unsigned i = 0; i < t.size(); i++)
{
const GLMesh *mesh = std::get<0>(t[i]);
irr_driver->IncreaseObjectCount();
GLenum ptype = mesh->PrimitiveType;
GLenum itype = mesh->IndexType;
size_t count = mesh->IndexCount;
for (unsigned j = 0; j < TextureUnits.size(); j++)
{
compressTexture(mesh->textures[j], true);
setTexture(TextureUnits[j], getTextureGLuint(mesh->textures[j]), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true);
}
Shader::setUniforms(std::get<1>(t[i]));
glDrawElementsInstancedBaseVertex(ptype, count, itype, (GLvoid *)mesh->vaoOffset, 4, mesh->vaoBaseVertex);
}
}
template<enum E_VERTEX_TYPE VertexType, typename... Args>
void drawRSM(const core::matrix4 & rsm_matrix, const std::vector<GLuint> TextureUnits, const std::vector<std::tuple<GLMesh *, core::matrix4, Args...> >&t)
{
glUseProgram(MeshShader::RSMShader::Program);
glBindVertexArray(getVAO(VertexType));
for (unsigned i = 0; i < t.size(); i++)
{
const GLMesh *mesh = std::get<0>(t[i]);
for (unsigned j = 0; j < TextureUnits.size(); j++)
{
compressTexture(mesh->textures[j], true);
setTexture(TextureUnits[j], getTextureGLuint(mesh->textures[j]), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true);
}
draw<MeshShader::RSMShader>(mesh, rsm_matrix, std::get<1>(t[i]));
}
}
void IrrDriver::renderShadows()
{
irr_driver->setPhase(SHADOW_PASS);
glDisable(GL_BLEND);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.5, 0.);
m_rtts->getShadowFBO().Bind();
glClear(GL_DEPTH_BUFFER_BIT);
glDrawBuffer(GL_NONE);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, SharedObject::ViewProjectionMatrixesUBO);
m_scene_manager->drawAll(scene::ESNRP_SOLID);
drawShadow<MeshShader::ShadowShader, EVT_STANDARD>({}, ListDefaultStandardG::Arguments);
drawShadow<MeshShader::ShadowShader, EVT_2TCOORDS>({}, ListDefault2TCoordG::Arguments);
drawShadow<MeshShader::ShadowShader, EVT_TANGENTS>({}, ListNormalG::Arguments);
drawShadow<MeshShader::RefShadowShader, EVT_STANDARD>({ MeshShader::RefShadowShader::TU_tex }, ListAlphaRefG::Arguments);
glDisable(GL_POLYGON_OFFSET_FILL);
if (!UserConfigParams::m_gi)
return;
m_rtts->getRSM().Bind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawRSM<EVT_STANDARD>(rsm_matrix, { MeshShader::RSMShader::TU_tex }, ListDefaultStandardG::Arguments);
drawRSM<EVT_2TCOORDS>(rsm_matrix, { MeshShader::RSMShader::TU_tex }, ListDefault2TCoordG::Arguments);
}
static void renderWireFrameFrustrum(float *tmp, unsigned i)
{
glUseProgram(MeshShader::ViewFrustrumShader::Program);
glBindVertexArray(MeshShader::ViewFrustrumShader::frustrumvao);
glBindBuffer(GL_ARRAY_BUFFER, SharedObject::frustrumvbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, 8 * 3 * sizeof(float), (void *)tmp);
MeshShader::ViewFrustrumShader::setUniforms(video::SColor(255, 0, 255, 0), i);
glDrawElements(GL_LINES, 24, GL_UNSIGNED_INT, 0);
}
void IrrDriver::renderShadowsDebug()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, UserConfigParams::m_height / 2, UserConfigParams::m_width / 2, UserConfigParams::m_height / 2);
m_post_processing->renderTextureLayer(m_rtts->getShadowDepthTex(), 0);
renderWireFrameFrustrum(m_shadows_cam[0], 0);
glViewport(UserConfigParams::m_width / 2, UserConfigParams::m_height / 2, UserConfigParams::m_width / 2, UserConfigParams::m_height / 2);
m_post_processing->renderTextureLayer(m_rtts->getShadowDepthTex(), 1);
renderWireFrameFrustrum(m_shadows_cam[1], 1);
glViewport(0, 0, UserConfigParams::m_width / 2, UserConfigParams::m_height / 2);
m_post_processing->renderTextureLayer(m_rtts->getShadowDepthTex(), 2);
renderWireFrameFrustrum(m_shadows_cam[2], 2);
glViewport(UserConfigParams::m_width / 2, 0, UserConfigParams::m_width / 2, UserConfigParams::m_height / 2);
m_post_processing->renderTextureLayer(m_rtts->getShadowDepthTex(), 3);
renderWireFrameFrustrum(m_shadows_cam[3], 3);
glViewport(0, 0, UserConfigParams::m_width, UserConfigParams::m_height);
}
// ----------------------------------------------------------------------------
void IrrDriver::renderGlow(std::vector<GlowData>& glows)
{
m_scene_manager->setCurrentRendertime(scene::ESNRP_SOLID);
m_rtts->getFBO(FBO_TMP1_WITH_DS).Bind();
glClearStencil(0);
glClearColor(0, 0, 0, 0);
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
const u32 glowcount = glows.size();
ColorizeProvider * const cb = (ColorizeProvider *) m_shaders->m_callbacks[ES_COLORIZE];
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilFunc(GL_ALWAYS, 1, ~0);
glEnable(GL_STENCIL_TEST);
glEnable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
glDepthMask(GL_FALSE);
glDisable(GL_BLEND);
glBindVertexArray(getVAO(EVT_STANDARD));
for (u32 i = 0; i < glowcount; i++)
{
const GlowData &dat = glows[i];
scene::ISceneNode * const cur = dat.node;
//TODO : implement culling on gpu
// Quick box-based culling
// const core::aabbox3df nodebox = cur->getTransformedBoundingBox();
// if (!nodebox.intersectsWithBox(cambox))
// continue;
cb->setColor(dat.r, dat.g, dat.b);
cur->render();
}
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glDisable(GL_STENCIL_TEST);
glDisable(GL_BLEND);
// To half
FrameBuffer::Blit(irr_driver->getFBO(FBO_TMP1_WITH_DS), irr_driver->getFBO(FBO_HALF1), GL_COLOR_BUFFER_BIT, GL_LINEAR);
// To quarter
FrameBuffer::Blit(irr_driver->getFBO(FBO_HALF1), irr_driver->getFBO(FBO_QUARTER1), GL_COLOR_BUFFER_BIT, GL_LINEAR);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glStencilFunc(GL_EQUAL, 0, ~0);
glEnable(GL_STENCIL_TEST);
m_rtts->getFBO(FBO_COLORS).Bind();
m_post_processing->renderGlow(m_rtts->getRenderTarget(RTT_QUARTER1));
glDisable(GL_STENCIL_TEST);
}
// ----------------------------------------------------------------------------
static LightShader::PointLightInfo PointLightsInfo[MAXLIGHT];
static void renderPointLights(unsigned count)
{
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glUseProgram(LightShader::PointLightShader::Program);
glBindVertexArray(LightShader::PointLightShader::vao);
glBindBuffer(GL_ARRAY_BUFFER, LightShader::PointLightShader::vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, count * sizeof(LightShader::PointLightInfo), PointLightsInfo);
setTexture(0, irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), GL_NEAREST, GL_NEAREST);
setTexture(1, irr_driver->getDepthStencilTexture(), GL_NEAREST, GL_NEAREST);
LightShader::PointLightShader
::setUniforms(core::vector2df(float(UserConfigParams::m_width),
float(UserConfigParams::m_height) ),
200, 0, 1);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, count);
}
unsigned IrrDriver::UpdateLightsInfo(scene::ICameraSceneNode * const camnode, float dt)
{
const u32 lightcount = m_lights.size();
const core::vector3df &campos = camnode->getAbsolutePosition();
std::vector<LightNode *> BucketedLN[15];
for (unsigned int i = 0; i < lightcount; i++)
{
if (!m_lights[i]->isPointLight())
{
m_lights[i]->render();
continue;
}
const core::vector3df &lightpos = (m_lights[i]->getAbsolutePosition() - campos);
unsigned idx = (unsigned)(lightpos.getLength() / 10);
if (idx > 14)
idx = 14;
BucketedLN[idx].push_back(m_lights[i]);
}
unsigned lightnum = 0;
for (unsigned i = 0; i < 15; i++)
{
for (unsigned j = 0; j < BucketedLN[i].size(); j++)
{
if (++lightnum >= MAXLIGHT)
{
LightNode* light_node = BucketedLN[i].at(j);
light_node->setEnergyMultiplier(0.0f);
}
else
{
LightNode* light_node = BucketedLN[i].at(j);
float em = light_node->getEnergyMultiplier();
if (em < 1.0f)
{
light_node->setEnergyMultiplier(std::min(1.0f, em + dt));
}
const core::vector3df &pos = light_node->getAbsolutePosition();
PointLightsInfo[lightnum].posX = pos.X;
PointLightsInfo[lightnum].posY = pos.Y;
PointLightsInfo[lightnum].posZ = pos.Z;
PointLightsInfo[lightnum].energy = light_node->getEffectiveEnergy();
const core::vector3df &col = light_node->getColor();
PointLightsInfo[lightnum].red = col.X;
PointLightsInfo[lightnum].green = col.Y;
PointLightsInfo[lightnum].blue = col.Z;
// Light radius
PointLightsInfo[lightnum].radius = light_node->getRadius();
}
}
if (lightnum > MAXLIGHT)
{
irr_driver->setLastLightBucketDistance(i * 10);
break;
}
}
lightnum++;
return lightnum;
}
void IrrDriver::renderLights(unsigned pointlightcount)
{
//RH
if (UserConfigParams::m_gi)
{
ScopedGPUTimer timer(irr_driver->getGPUTimer(Q_RH));
glDisable(GL_BLEND);
m_rtts->getRH().Bind();
glUseProgram(FullScreenShader::RadianceHintsConstructionShader::Program);
glBindVertexArray(FullScreenShader::RadianceHintsConstructionShader::vao);
setTexture(0, m_rtts->getRSM().getRTT()[0], GL_LINEAR, GL_LINEAR);
setTexture(1, m_rtts->getRSM().getRTT()[1], GL_LINEAR, GL_LINEAR);
setTexture(2, m_rtts->getRSM().getDepthTexture(), GL_LINEAR, GL_LINEAR);
FullScreenShader::RadianceHintsConstructionShader::setUniforms(rsm_matrix, rh_matrix, rh_extend, 0, 1, 2);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, 32);
}
for (unsigned i = 0; i < sun_ortho_matrix.size(); i++)
sun_ortho_matrix[i] *= getInvViewMatrix();
m_rtts->getFBO(FBO_COMBINED_TMP1_TMP2).Bind();
if (!UserConfigParams::m_dynamic_lights)
glClearColor(.5, .5, .5, .5);
glClear(GL_COLOR_BUFFER_BIT);
if (!UserConfigParams::m_dynamic_lights)
return;
m_rtts->getFBO(FBO_TMP1_WITH_DS).Bind();
if (UserConfigParams::m_gi)
{
ScopedGPUTimer timer(irr_driver->getGPUTimer(Q_GI));
m_post_processing->renderGI(rh_matrix, rh_extend, m_rtts->getRH().getRTT()[0], m_rtts->getRH().getRTT()[1], m_rtts->getRH().getRTT()[2]);
}
if (SkyboxCubeMap)
{
ScopedGPUTimer timer(irr_driver->getGPUTimer(Q_ENVMAP));
m_post_processing->renderDiffuseEnvMap(blueSHCoeff, greenSHCoeff, redSHCoeff);
}
m_rtts->getFBO(FBO_COMBINED_TMP1_TMP2).Bind();
if (World::getWorld() && World::getWorld()->getTrack()->hasShadows() && SkyboxCubeMap)
irr_driver->getSceneManager()->setAmbientLight(SColor(0, 0, 0, 0));
// Render sunlight if and only if track supports shadow
if (!World::getWorld() || World::getWorld()->getTrack()->hasShadows())
{
ScopedGPUTimer timer(irr_driver->getGPUTimer(Q_SUN));
if (World::getWorld() && UserConfigParams::m_shadows)
m_post_processing->renderShadowedSunlight(sun_ortho_matrix, m_rtts->getShadowDepthTex());
else
m_post_processing->renderSunlight();
}
{
ScopedGPUTimer timer(irr_driver->getGPUTimer(Q_POINTLIGHTS));
renderPointLights(MIN2(pointlightcount, MAXLIGHT));
}
}
void IrrDriver::renderSSAO()
{
m_rtts->getFBO(FBO_SSAO).Bind();
glClearColor(1., 1., 1., 1.);
glClear(GL_COLOR_BUFFER_BIT);
m_post_processing->renderSSAO();
// Blur it to reduce noise.
FrameBuffer::Blit(m_rtts->getFBO(FBO_SSAO), m_rtts->getFBO(FBO_HALF1_R), GL_COLOR_BUFFER_BIT, GL_LINEAR);
m_post_processing->renderGaussian17TapBlur(irr_driver->getFBO(FBO_HALF1_R), irr_driver->getFBO(FBO_HALF2_R));
}
static void getXYZ(GLenum face, float i, float j, float &x, float &y, float &z)
{
switch (face)
{
case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
x = 1.;
y = -i;
z = -j;
break;
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
x = -1.;
y = -i;
z = j;
break;
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
x = j;
y = 1.;
z = i;
break;
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
x = j;
y = -1;
z = -i;
break;
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
x = j;
y = -i;
z = 1;
break;
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
x = -j;
y = -i;
z = -1;
break;
}
float norm = sqrt(x * x + y * y + z * z);
x /= norm, y /= norm, z /= norm;
return;
}
static void getYml(GLenum face, size_t width, size_t height,
float *Y00,
float *Y1minus1, float *Y10, float *Y11,
float *Y2minus2, float *Y2minus1, float *Y20, float *Y21, float *Y22)
{
for (unsigned i = 0; i < width; i++)
{
for (unsigned j = 0; j < height; j++)
{
float x, y, z;
float fi = float(i), fj = float(j);
fi /= width, fj /= height;
fi = 2 * fi - 1, fj = 2 * fj - 1;
getXYZ(face, fi, fj, x, y, z);
// constant part of Ylm
float c00 = 0.282095f;
float c1minus1 = 0.488603f;
float c10 = 0.488603f;
float c11 = 0.488603f;
float c2minus2 = 1.092548f;
float c2minus1 = 1.092548f;
float c21 = 1.092548f;
float c20 = 0.315392f;
float c22 = 0.546274f;
size_t idx = i * height + j;
Y00[idx] = c00;
Y1minus1[idx] = c1minus1 * y;
Y10[idx] = c10 * z;
Y11[idx] = c11 * x;
Y2minus2[idx] = c2minus2 * x * y;
Y2minus1[idx] = c2minus1 * y * z;
Y21[idx] = c21 * x * z;
Y20[idx] = c20 * (3 * z * z - 1);
Y22[idx] = c22 * (x * x - y * y);
}
}
}
static float getTexelValue(unsigned i, unsigned j, size_t width, size_t height, float *Coeff, float *Y00, float *Y1minus1, float *Y10, float *Y11,
float *Y2minus2, float * Y2minus1, float * Y20, float *Y21, float *Y22)
{
float d = sqrt((float)(i * i + j * j + 1));
float solidangle = 1.;
size_t idx = i * height + j;
float reconstructedVal = Y00[idx] * Coeff[0];
reconstructedVal += Y1minus1[i * height + j] * Coeff[1] + Y10[i * height + j] * Coeff[2] + Y11[i * height + j] * Coeff[3];
reconstructedVal += Y2minus2[idx] * Coeff[4] + Y2minus1[idx] * Coeff[5] + Y20[idx] * Coeff[6] + Y21[idx] * Coeff[7] + Y22[idx] * Coeff[8];
reconstructedVal /= solidangle;
return MAX2(255.0f * reconstructedVal, 0.f);
}
static void unprojectSH(float *output[], size_t width, size_t height,
float *Y00[],
float *Y1minus1[], float *Y10[], float *Y11[],
float *Y2minus2[], float *Y2minus1[], float * Y20[],float *Y21[], float *Y22[],
float *blueSHCoeff, float *greenSHCoeff, float *redSHCoeff)
{
for (unsigned face = 0; face < 6; face++)
{
for (unsigned i = 0; i < width; i++)
{
for (unsigned j = 0; j < height; j++)
{
float fi = float(i), fj = float(j);
fi /= width, fj /= height;
fi = 2 * fi - 1, fj = 2 * fj - 1;
output[face][4 * height * i + 4 * j + 2] = getTexelValue(i, j, width, height,
redSHCoeff,
Y00[face], Y1minus1[face], Y10[face], Y11[face], Y2minus2[face], Y2minus1[face], Y20[face], Y21[face], Y22[face]);
output[face][4 * height * i + 4 * j + 1] = getTexelValue(i, j, width, height,
greenSHCoeff,
Y00[face], Y1minus1[face], Y10[face], Y11[face], Y2minus2[face], Y2minus1[face], Y20[face], Y21[face], Y22[face]);
output[face][4 * height * i + 4 * j] = getTexelValue(i, j, width, height,
blueSHCoeff,
Y00[face], Y1minus1[face], Y10[face], Y11[face], Y2minus2[face], Y2minus1[face], Y20[face], Y21[face], Y22[face]);
}
}
}
}
static void projectSH(float *color[], size_t width, size_t height,
float *Y00[],
float *Y1minus1[], float *Y10[], float *Y11[],
float *Y2minus2[], float *Y2minus1[], float * Y20[], float *Y21[], float *Y22[],
float *blueSHCoeff, float *greenSHCoeff, float *redSHCoeff
)
{
for (unsigned i = 0; i < 9; i++)
{
blueSHCoeff[i] = 0;
greenSHCoeff[i] = 0;
redSHCoeff[i] = 0;
}
float wh = float(width * height);
for (unsigned face = 0; face < 6; face++)
{
for (unsigned i = 0; i < width; i++)
{
for (unsigned j = 0; j < height; j++)
{
size_t idx = i * height + j;
float fi = float(i), fj = float(j);
fi /= width, fj /= height;
fi = 2 * fi - 1, fj = 2 * fj - 1;
float d = sqrt(fi * fi + fj * fj + 1);
// Constant obtained by projecting unprojected ref values
float solidangle = 2.75f / (wh * pow(d, 1.5f));
// pow(., 2.2) to convert from srgb
float b = pow(color[face][4 * height * i + 4 * j ] / 255.f, 2.2f);
float g = pow(color[face][4 * height * i + 4 * j + 1] / 255.f, 2.2f);
float r = pow(color[face][4 * height * i + 4 * j + 2] / 255.f, 2.2f);
assert(b >= 0.);
blueSHCoeff[0] += b * Y00[face][idx] * solidangle;
blueSHCoeff[1] += b * Y1minus1[face][idx] * solidangle;
blueSHCoeff[2] += b * Y10[face][idx] * solidangle;
blueSHCoeff[3] += b * Y11[face][idx] * solidangle;
blueSHCoeff[4] += b * Y2minus2[face][idx] * solidangle;
blueSHCoeff[5] += b * Y2minus1[face][idx] * solidangle;
blueSHCoeff[6] += b * Y20[face][idx] * solidangle;
blueSHCoeff[7] += b * Y21[face][idx] * solidangle;
blueSHCoeff[8] += b * Y22[face][idx] * solidangle;
greenSHCoeff[0] += g * Y00[face][idx] * solidangle;
greenSHCoeff[1] += g * Y1minus1[face][idx] * solidangle;
greenSHCoeff[2] += g * Y10[face][idx] * solidangle;
greenSHCoeff[3] += g * Y11[face][idx] * solidangle;
greenSHCoeff[4] += g * Y2minus2[face][idx] * solidangle;
greenSHCoeff[5] += g * Y2minus1[face][idx] * solidangle;
greenSHCoeff[6] += g * Y20[face][idx] * solidangle;
greenSHCoeff[7] += g * Y21[face][idx] * solidangle;
greenSHCoeff[8] += g * Y22[face][idx] * solidangle;
redSHCoeff[0] += r * Y00[face][idx] * solidangle;
redSHCoeff[1] += r * Y1minus1[face][idx] * solidangle;
redSHCoeff[2] += r * Y10[face][idx] * solidangle;
redSHCoeff[3] += r * Y11[face][idx] * solidangle;
redSHCoeff[4] += r * Y2minus2[face][idx] * solidangle;
redSHCoeff[5] += r * Y2minus1[face][idx] * solidangle;
redSHCoeff[6] += r * Y20[face][idx] * solidangle;
redSHCoeff[7] += r * Y21[face][idx] * solidangle;
redSHCoeff[8] += r * Y22[face][idx] * solidangle;
}
}
}
}
static void displayCoeff(float *SHCoeff)
{
printf("L00:%f\n", SHCoeff[0]);
printf("L1-1:%f, L10:%f, L11:%f\n", SHCoeff[1], SHCoeff[2], SHCoeff[3]);
printf("L2-2:%f, L2-1:%f, L20:%f, L21:%f, L22:%f\n", SHCoeff[4], SHCoeff[5], SHCoeff[6], SHCoeff[7], SHCoeff[8]);
}
// Only for 9 coefficients
static void testSH(unsigned char *color[6], size_t width, size_t height,
float *blueSHCoeff, float *greenSHCoeff, float *redSHCoeff)
{
float *Y00[6];
float *Y1minus1[6];
float *Y10[6];
float *Y11[6];
float *Y2minus2[6];
float *Y2minus1[6];
float *Y20[6];
float *Y21[6];
float *Y22[6];
float *testoutput[6];
for (unsigned i = 0; i < 6; i++)
{
testoutput[i] = new float[width * height * 4];
for (unsigned j = 0; j < width * height; j++)
{
testoutput[i][4 * j ] = float(0xFF & color[i][4 * j]);
testoutput[i][4 * j + 1] = float(0xFF & color[i][4 * j + 1]);
testoutput[i][4 * j + 2] = float(0xFF & color[i][4 * j + 2]);
}
}
for (unsigned face = 0; face < 6; face++)
{
Y00[face] = new float[width * height];
Y1minus1[face] = new float[width * height];
Y10[face] = new float[width * height];
Y11[face] = new float[width * height];
Y2minus2[face] = new float[width * height];
Y2minus1[face] = new float[width * height];
Y20[face] = new float[width * height];
Y21[face] = new float[width * height];
Y22[face] = new float[width * height];
getYml(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, width, height, Y00[face], Y1minus1[face], Y10[face], Y11[face], Y2minus2[face], Y2minus1[face], Y20[face], Y21[face], Y22[face]);
}
/* blueSHCoeff[0] = 0.54,
blueSHCoeff[1] = .6, blueSHCoeff[2] = -.27, blueSHCoeff[3] = .01,
blueSHCoeff[4] = -.12, blueSHCoeff[5] = -.47, blueSHCoeff[6] = -.15, blueSHCoeff[7] = .14, blueSHCoeff[8] = -.3;
greenSHCoeff[0] = .44,
greenSHCoeff[1] = .35, greenSHCoeff[2] = -.18, greenSHCoeff[3] = -.06,
greenSHCoeff[4] = -.05, greenSHCoeff[5] = -.22, greenSHCoeff[6] = -.09, greenSHCoeff[7] = .21, greenSHCoeff[8] = -.05;
redSHCoeff[0] = .79,
redSHCoeff[1] = .39, redSHCoeff[2] = -.34, redSHCoeff[3] = -.29,
redSHCoeff[4] = -.11, redSHCoeff[5] = -.26, redSHCoeff[6] = -.16, redSHCoeff[7] = .56, redSHCoeff[8] = .21;
printf("Blue:\n");
displayCoeff(blueSHCoeff);
printf("Green:\n");
displayCoeff(greenSHCoeff);
printf("Red:\n");
displayCoeff(redSHCoeff);*/
projectSH(testoutput, width, height,
Y00,
Y1minus1, Y10, Y11,
Y2minus2, Y2minus1, Y20, Y21, Y22,
blueSHCoeff, greenSHCoeff, redSHCoeff
);
//printf("Blue:\n");
//displayCoeff(blueSHCoeff);
//printf("Green:\n");
//displayCoeff(greenSHCoeff);
//printf("Red:\n");
//displayCoeff(redSHCoeff);
// Convolute in frequency space
/* float A0 = 3.141593;
float A1 = 2.094395;
float A2 = 0.785398;
blueSHCoeff[0] *= A0;
greenSHCoeff[0] *= A0;
redSHCoeff[0] *= A0;
for (unsigned i = 0; i < 3; i++)
{
blueSHCoeff[1 + i] *= A1;
greenSHCoeff[1 + i] *= A1;
redSHCoeff[1 + i] *= A1;
}
for (unsigned i = 0; i < 5; i++)
{
blueSHCoeff[4 + i] *= A2;
greenSHCoeff[4 + i] *= A2;
redSHCoeff[4 + i] *= A2;
}
unprojectSH(testoutput, width, height,
Y00,
Y1minus1, Y10, Y11,
Y2minus2, Y2minus1, Y20, Y21, Y22,
blueSHCoeff, greenSHCoeff, redSHCoeff
);*/
/* printf("Blue:\n");
displayCoeff(blueSHCoeff);
printf("Green:\n");
displayCoeff(greenSHCoeff);
printf("Red:\n");
displayCoeff(redSHCoeff);
printf("\nAfter projection\n\n");*/
for (unsigned i = 0; i < 6; i++)
{
for (unsigned j = 0; j < width * height; j++)
{
color[i][4 * j ] = char(MIN2(testoutput[i][4 * j], 255));
color[i][4 * j + 1] = char(MIN2(testoutput[i][4 * j + 1], 255));
color[i][4 * j + 2] = char(MIN2(testoutput[i][4 * j + 2], 255));
}
}
for (unsigned face = 0; face < 6; face++)
{
delete[] testoutput[face];
delete[] Y00[face];
delete[] Y1minus1[face];
delete[] Y10[face];
delete[] Y11[face];
}
}
/** Generate an opengl cubemap texture from 6 2d textures.
Out of legacy the sequence of textures maps to :
- 1st texture maps to GL_TEXTURE_CUBE_MAP_POSITIVE_Y
- 2nd texture maps to GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
- 3rd texture maps to GL_TEXTURE_CUBE_MAP_POSITIVE_X
- 4th texture maps to GL_TEXTURE_CUBE_MAP_NEGATIVE_X
- 5th texture maps to GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
- 6th texture maps to GL_TEXTURE_CUBE_MAP_POSITIVE_Z
* \param textures sequence of 6 textures.
*/
GLuint generateCubeMapFromTextures(const std::vector<video::ITexture *> &textures)
{
assert(textures.size() == 6);
GLuint result;
glGenTextures(1, &result);
unsigned w = 0, h = 0;
for (unsigned i = 0; i < 6; i++)
{
w = MAX2(w, textures[i]->getOriginalSize().Width);
h = MAX2(h, textures[i]->getOriginalSize().Height);
}
const unsigned texture_permutation[] = { 2, 3, 0, 1, 5, 4 };
char *rgba[6];
for (unsigned i = 0; i < 6; i++)
rgba[i] = new char[w * h * 4];
for (unsigned i = 0; i < 6; i++)
{
unsigned idx = texture_permutation[i];
video::IImage* image = irr_driver->getVideoDriver()->createImageFromData(
textures[idx]->getColorFormat(),
textures[idx]->getSize(),
textures[idx]->lock(),
false
);
textures[idx]->unlock();
image->copyToScaling(rgba[i], w, h);
image->drop();
glBindTexture(GL_TEXTURE_CUBE_MAP, result);
if (UserConfigParams::m_texture_compression)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_COMPRESSED_SRGB_ALPHA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)rgba[i]);
else
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_SRGB_ALPHA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)rgba[i]);
}
for (unsigned i = 0; i < 6; i++)
delete[] rgba[i];
return result;
}
void IrrDriver::generateSkyboxCubemap()
{
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
assert(SkyboxTextures.size() == 6);
SkyboxCubeMap = generateCubeMapFromTextures(SkyboxTextures);
const unsigned texture_permutation[] = { 2, 3, 0, 1, 5, 4 };
if (SphericalHarmonicsTextures.size() == 6)
{
unsigned sh_w = 0, sh_h = 0;
for (unsigned i = 0; i < 6; i++)
{
sh_w = MAX2(sh_w, SphericalHarmonicsTextures[i]->getOriginalSize().Width);
sh_h = MAX2(sh_h, SphericalHarmonicsTextures[i]->getOriginalSize().Height);
}
unsigned char *sh_rgba[6];
for (unsigned i = 0; i < 6; i++)
sh_rgba[i] = new unsigned char[sh_w * sh_h * 4];
for (unsigned i = 0; i < 6; i++)
{
unsigned idx = texture_permutation[i];
video::IImage* image = getVideoDriver()->createImageFromData(
SphericalHarmonicsTextures[idx]->getColorFormat(),
SphericalHarmonicsTextures[idx]->getSize(),
SphericalHarmonicsTextures[idx]->lock(),
false
);
SphericalHarmonicsTextures[idx]->unlock();
image->copyToScaling(sh_rgba[i], sh_w, sh_h);
image->drop();
}
testSH(sh_rgba, sh_w, sh_h, blueSHCoeff, greenSHCoeff, redSHCoeff);
for (unsigned i = 0; i < 6; i++)
delete[] sh_rgba[i];
}
else
{
int sh_w = 16;
int sh_h = 16;
const video::SColorf& ambientf = irr_driver->getSceneManager()->getAmbientLight();
video::SColor ambient = ambientf.toSColor();
unsigned char *sh_rgba[6];
for (unsigned i = 0; i < 6; i++)
{
sh_rgba[i] = new unsigned char[sh_w * sh_h * 4];
for (int j = 0; j < sh_w * sh_h * 4; j+=4)
{
sh_rgba[i][j] = ambient.getBlue();
sh_rgba[i][j + 1] = ambient.getGreen();
sh_rgba[i][j + 2] = ambient.getRed();
sh_rgba[i][j + 3] = 255;
}
}
testSH(sh_rgba, sh_w, sh_h, blueSHCoeff, greenSHCoeff, redSHCoeff);
for (unsigned i = 0; i < 6; i++)
delete[] sh_rgba[i];
}
/*for (unsigned i = 0; i < 6; i++)
{
glBindTexture(GL_TEXTURE_CUBE_MAP, ConvolutedSkyboxCubeMap);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_SRGB_ALPHA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)rgba[i]);
}
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);*/
}
void IrrDriver::renderSkybox(const scene::ICameraSceneNode *camera)
{
if (SkyboxTextures.empty())
return;
if (!SkyboxCubeMap)
generateSkyboxCubemap();
glBindVertexArray(MeshShader::SkyboxShader::cubevao);
glDisable(GL_CULL_FACE);
assert(SkyboxTextures.size() == 6);
core::matrix4 translate;
translate.setTranslation(camera->getAbsolutePosition());
// Draw the sky box between the near and far clip plane
const f32 viewDistance = (camera->getNearValue() + camera->getFarValue()) * 0.5f;
core::matrix4 scale;
scale.setScale(core::vector3df(viewDistance, viewDistance, viewDistance));
core::matrix4 transform = translate * scale;
core::matrix4 invtransform;
transform.getInverse(invtransform);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, SkyboxCubeMap);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glUseProgram(MeshShader::SkyboxShader::Program);
MeshShader::SkyboxShader::setUniforms(transform,
core::vector2df(float(UserConfigParams::m_width),
float(UserConfigParams::m_height)),
0);
glDrawElements(GL_TRIANGLES, 6 * 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
// ----------------------------------------------------------------------------
void IrrDriver::renderDisplacement()
{
irr_driver->getFBO(FBO_TMP1_WITH_DS).Bind();
glClear(GL_COLOR_BUFFER_BIT);
irr_driver->getFBO(FBO_DISPLACE).Bind();
glClear(GL_COLOR_BUFFER_BIT);
DisplaceProvider * const cb = (DisplaceProvider *)irr_driver->getCallback(ES_DISPLACE);
cb->update();
const int displacingcount = m_displacing.size();
irr_driver->setPhase(DISPLACEMENT_PASS);
glEnable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
glDepthMask(GL_FALSE);
glDisable(GL_BLEND);
glClear(GL_STENCIL_BUFFER_BIT);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glBindVertexArray(getVAO(EVT_2TCOORDS));
for (int i = 0; i < displacingcount; i++)
{
m_scene_manager->setCurrentRendertime(scene::ESNRP_TRANSPARENT);
m_displacing[i]->render();
}
irr_driver->getFBO(FBO_COLORS).Bind();
glStencilFunc(GL_EQUAL, 1, 0xFF);
m_post_processing->renderPassThrough(m_rtts->getRenderTarget(RTT_DISPLACE));
glDisable(GL_STENCIL_TEST);
}