// SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2015 SuperTuxKart-Team // // 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. #ifndef SERVER_ONLY #include "graphics/shader_based_renderer.hpp" #include "config/user_config.hpp" #include "graphics/camera.hpp" #include "graphics/central_settings.hpp" #include "graphics/draw_policies.hpp" #include "graphics/geometry_passes.hpp" #include "graphics/glwrap.hpp" #include "graphics/graphics_restrictions.hpp" #include "graphics/irr_driver.hpp" #include "graphics/lod_node.hpp" #include "graphics/post_processing.hpp" #include "graphics/render_target.hpp" #include "graphics/rtts.hpp" #include "graphics/shaders.hpp" #include "graphics/skybox.hpp" #include "graphics/stk_mesh_scene_node.hpp" #include "graphics/spherical_harmonics.hpp" #include "items/item_manager.hpp" #include "items/powerup_manager.hpp" #include "modes/world.hpp" #include "physics/physics.hpp" #include "states_screens/race_gui_base.hpp" #include "tracks/track.hpp" #include "utils/profiler.hpp" #include // ---------------------------------------------------------------------------- void ShaderBasedRenderer::setRTT(RTT* rtts) { if (m_rtts != rtts && rtts != NULL) { // Update prefilled textures if new RTT is used std::vector prefilled_textures = createVector(rtts->getRenderTarget(RTT_DIFFUSE), rtts->getRenderTarget(RTT_SPECULAR), rtts->getRenderTarget(RTT_HALF1_R), rtts->getDepthStencilTexture()); m_geometry_passes->setFirstPassRenderTargets(prefilled_textures, rtts->getPrefilledHandles()); m_rtts = rtts; } else if (rtts == NULL) { m_rtts = NULL; } } //setRTT // ---------------------------------------------------------------------------- void ShaderBasedRenderer::setOverrideMaterial() { video::SOverrideMaterial &overridemat = irr_driver->getVideoDriver()->getOverrideMaterial(); overridemat.EnablePasses = scene::ESNRP_SOLID | scene::ESNRP_TRANSPARENT; overridemat.EnableFlags = 0; if (irr_driver->getWireframe()) { overridemat.Material.Wireframe = 1; overridemat.EnableFlags |= video::EMF_WIREFRAME; } if (irr_driver->getMipViz()) { overridemat.Material.MaterialType = Shaders::getShader(ES_MIPVIZ); overridemat.EnableFlags |= video::EMF_MATERIAL_TYPE; overridemat.EnablePasses = scene::ESNRP_SOLID; } } //setOverrideMaterial // ---------------------------------------------------------------------------- /** Add glowing items, they may appear or disappear each frame. */ void ShaderBasedRenderer::addItemsInGlowingList() { 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(); STKMeshSceneNode *stk_node = static_cast(node); stk_node->setGlowColors(irr::video::SColor(0, (unsigned) (dat.r * 255.f), (unsigned)(dat.g * 255.f), (unsigned)(dat.b * 255.f))); m_glowing.push_back(dat); } } //addItemsInGlowingList // ---------------------------------------------------------------------------- /** Remove all non static glowing things */ void ShaderBasedRenderer::removeItemsInGlowingList() { while(m_glowing.size() > m_nb_static_glowing) m_glowing.pop_back(); } // ---------------------------------------------------------------------------- void ShaderBasedRenderer::prepareForwardRenderer() { irr::video::SColor clearColor(0, 150, 150, 150); if (World::getWorld() != NULL) clearColor = irr_driver->getClearColor(); glClear(GL_COLOR_BUFFER_BIT); glDepthMask(GL_TRUE); glBindFramebuffer(GL_FRAMEBUFFER, 0); glClearColor(clearColor.getRed() / 255.f, clearColor.getGreen() / 255.f, clearColor.getBlue() / 255.f, clearColor.getAlpha() / 255.f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } // ---------------------------------------------------------------------------- void ShaderBasedRenderer::updateLightsInfo(scene::ICameraSceneNode * const camnode, float dt) { m_lighting_passes.updateLightsInfo(camnode, dt); } // ---------------------------------------------------------------------------- /** Upload lighting info to the dedicated uniform buffer */ void ShaderBasedRenderer::uploadLightingData() const { assert(CVS->isARBUniformBufferObjectUsable()); float Lighting[36]; core::vector3df sun_direction = irr_driver->getSunDirection(); video::SColorf sun_color = irr_driver->getSunColor(); Lighting[0] = sun_direction.X; Lighting[1] = sun_direction.Y; Lighting[2] = sun_direction.Z; Lighting[4] = sun_color.getRed(); Lighting[5] = sun_color.getGreen(); Lighting[6] = sun_color.getBlue(); Lighting[7] = 0.54f; const SHCoefficients* sh_coeff = m_spherical_harmonics->getCoefficients(); if(sh_coeff) { memcpy(&Lighting[8], sh_coeff->blue_SH_coeff, 9 * sizeof(float)); memcpy(&Lighting[17], sh_coeff->green_SH_coeff, 9 * sizeof(float)); memcpy(&Lighting[26], sh_coeff->red_SH_coeff, 9 * sizeof(float)); } glBindBuffer(GL_UNIFORM_BUFFER, SharedGPUObjects::getLightingDataUBO()); glBufferSubData(GL_UNIFORM_BUFFER, 0, 36 * sizeof(float), Lighting); } // uploadLightingData // ---------------------------------------------------------------------------- void ShaderBasedRenderer::computeMatrixesAndCameras(scene::ICameraSceneNode *const camnode, size_t width, size_t height) { float w = width * UserConfigParams::m_scale_rtts_factor; float h = height * UserConfigParams::m_scale_rtts_factor; m_current_screen_size = core::vector2df(w, h); m_shadow_matrices.computeMatrixesAndCameras(camnode, int(w), int(h), m_rtts->getDepthStencilTexture()); } // computeMatrixesAndCameras // ---------------------------------------------------------------------------- void ShaderBasedRenderer::renderSkybox(const scene::ICameraSceneNode *camera) const { if(m_skybox) { m_skybox->render(camera); } } // renderSkybox // ---------------------------------------------------------------------------- void ShaderBasedRenderer::renderSSAO() const { #if defined(USE_GLES2) if (!CVS->isEXTColorBufferFloatUsable()) return; #endif m_rtts->getFBO(FBO_SSAO).bind(); glClearColor(1., 1., 1., 1.); glClear(GL_COLOR_BUFFER_BIT); m_post_processing->renderSSAO(m_rtts->getFBO(FBO_LINEAR_DEPTH), m_rtts->getFBO(FBO_SSAO), m_rtts->getDepthStencilTexture()); // 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(m_rtts->getFBO(FBO_HALF1_R), m_rtts->getFBO(FBO_HALF2_R), m_rtts->getFBO(FBO_LINEAR_DEPTH)); } // renderSSAO // ---------------------------------------------------------------------------- void ShaderBasedRenderer::renderScene(scene::ICameraSceneNode * const camnode, float dt, bool hasShadow, bool forceRTT) { if (CVS->isARBUniformBufferObjectUsable()) { glBindBufferBase(GL_UNIFORM_BUFFER, 0, SharedGPUObjects::getViewProjectionMatricesUBO()); glBindBufferBase(GL_UNIFORM_BUFFER, 1, SharedGPUObjects::getLightingDataUBO()); if (CVS->supportsHardwareSkinning()) glBindBufferBase(GL_UNIFORM_BUFFER, 2, SharedGPUObjects::getSkinningUBO()); } irr_driver->getSceneManager()->setActiveCamera(camnode); PROFILER_PUSH_CPU_MARKER("- Draw Call Generation", 0xFF, 0xFF, 0xFF); unsigned solid_poly_count = 0; unsigned shadow_poly_count = 0; m_draw_calls.prepareDrawCalls(m_shadow_matrices, camnode, solid_poly_count, shadow_poly_count); m_poly_count[SOLID_NORMAL_AND_DEPTH_PASS] += solid_poly_count; m_poly_count[SHADOW_PASS] += shadow_poly_count; PROFILER_POP_CPU_MARKER(); #if !defined(USE_GLES2) // Shadows { // To avoid wrong culling, use the largest view possible irr_driver->getSceneManager()->setActiveCamera(m_shadow_matrices.getSunCam()); if (CVS->isDefferedEnabled() && CVS->isShadowEnabled() && hasShadow) { PROFILER_PUSH_CPU_MARKER("- Shadow", 0x30, 0x6F, 0x90); m_geometry_passes->renderShadows(m_draw_calls, m_shadow_matrices, m_rtts->getShadowFrameBuffer(), m_rtts->getFBO(FBO_SCALAR_1024), m_post_processing); PROFILER_POP_CPU_MARKER(); if (CVS->isGlobalIlluminationEnabled()) { if (!m_shadow_matrices.isRSMMapAvail()) { PROFILER_PUSH_CPU_MARKER("- RSM", 0xFF, 0x0, 0xFF); m_geometry_passes->renderReflectiveShadowMap(m_draw_calls, m_shadow_matrices, m_rtts->getReflectiveShadowMapFrameBuffer()); //TODO: move somewhere else as RSM are computed only once per track m_shadow_matrices.setRSMMapAvail(true); PROFILER_POP_CPU_MARKER(); } } } irr_driver->getSceneManager()->setActiveCamera(camnode); } #endif // !defined(USE_GLES2) PROFILER_PUSH_CPU_MARKER("- Solid Pass 1", 0xFF, 0x00, 0x00); glDepthMask(GL_TRUE); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); glEnable(GL_CULL_FACE); if (CVS->isDefferedEnabled() || forceRTT) { m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).bind(); glClearColor(0., 0., 0., 0.); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); m_geometry_passes->renderSolidFirstPass(m_draw_calls); } else { // We need a cleared depth buffer for some effect (eg particles depth blending) if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_FRAMEBUFFER_SRGB_WORKING)) glDisable(GL_FRAMEBUFFER_SRGB); m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).bind(); // Bind() modifies the viewport. In order not to affect anything else, // the viewport is just reset here and not removed in Bind(). const core::recti &vp = Camera::getActiveCamera()->getViewport(); glViewport(vp.UpperLeftCorner.X, irr_driver->getActualScreenSize().Height - vp.LowerRightCorner.Y, vp.LowerRightCorner.X - vp.UpperLeftCorner.X, vp.LowerRightCorner.Y - vp.UpperLeftCorner.Y); glClear(GL_DEPTH_BUFFER_BIT); if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_FRAMEBUFFER_SRGB_WORKING)) glEnable(GL_FRAMEBUFFER_SRGB); glBindFramebuffer(GL_FRAMEBUFFER, 0); } PROFILER_POP_CPU_MARKER(); // Lights { PROFILER_PUSH_CPU_MARKER("- Light", 0x00, 0xFF, 0x00); if (CVS->isDefferedEnabled()) { if (CVS->isGlobalIlluminationEnabled() && hasShadow) { ScopedGPUTimer timer(irr_driver->getGPUTimer(Q_RH)); m_lighting_passes.renderRadianceHints( m_shadow_matrices, m_rtts->getRadianceHintFrameBuffer(), m_rtts->getReflectiveShadowMapFrameBuffer()); } m_rtts->getFBO(FBO_COMBINED_DIFFUSE_SPECULAR).bind(); glClear(GL_COLOR_BUFFER_BIT); m_rtts->getFBO(FBO_DIFFUSE).bind(); if (CVS->isGlobalIlluminationEnabled() && hasShadow) { ScopedGPUTimer timer(irr_driver->getGPUTimer(Q_GI)); m_lighting_passes.renderGlobalIllumination( m_shadow_matrices, m_rtts->getRadianceHintFrameBuffer(), m_rtts->getRenderTarget(RTT_NORMAL_AND_DEPTH), m_rtts->getDepthStencilTexture()); } m_rtts->getFBO(FBO_COMBINED_DIFFUSE_SPECULAR).bind(); GLuint specular_probe = 0; if (m_skybox) { specular_probe = m_skybox->getSpecularProbe(); } m_lighting_passes.renderLights( hasShadow, m_rtts->getRenderTarget(RTT_NORMAL_AND_DEPTH), m_rtts->getDepthStencilTexture(), m_rtts->getShadowFrameBuffer(), specular_probe); } PROFILER_POP_CPU_MARKER(); } // Handle SSAO { PROFILER_PUSH_CPU_MARKER("- SSAO", 0xFF, 0xFF, 0x00); ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_SSAO)); if (UserConfigParams::m_ssao) renderSSAO(); PROFILER_POP_CPU_MARKER(); } PROFILER_PUSH_CPU_MARKER("- Solid Pass 2", 0x00, 0x00, 0xFF); if (CVS->isDefferedEnabled() || forceRTT) { m_rtts->getFBO(FBO_COLORS).bind(); video::SColor clearColor(0, 150, 150, 150); if (World::getWorld() != NULL) clearColor = irr_driver->getClearColor(); glClearColor(clearColor.getRed() / 255.f, clearColor.getGreen() / 255.f, clearColor.getBlue() / 255.f, clearColor.getAlpha() / 255.f); glClear(GL_COLOR_BUFFER_BIT); glDepthMask(GL_FALSE); } glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); m_geometry_passes->renderSolidSecondPass(m_draw_calls); PROFILER_POP_CPU_MARKER(); if (irr_driver->getNormals()) { m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).bind(); m_geometry_passes->renderNormalsVisualisation(m_draw_calls); m_rtts->getFBO(FBO_COLORS).bind(); } // Render ambient scattering const Track * const track = Track::getCurrentTrack(); if (CVS->isDefferedEnabled() && track && track->isFogEnabled()) { PROFILER_PUSH_CPU_MARKER("- Ambient scatter", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_FOG)); m_lighting_passes.renderAmbientScatter(m_rtts->getDepthStencilTexture()); PROFILER_POP_CPU_MARKER(); } { PROFILER_PUSH_CPU_MARKER("- Skybox", 0xFF, 0x00, 0xFF); ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_SKYBOX)); renderSkybox(camnode); PROFILER_POP_CPU_MARKER(); } // Render discrete lights scattering if (CVS->isDefferedEnabled() && track && track->isFogEnabled()) { PROFILER_PUSH_CPU_MARKER("- PointLight Scatter", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_FOG)); m_lighting_passes.renderLightsScatter(m_rtts->getDepthStencilTexture(), m_rtts->getFBO(FBO_HALF1), m_rtts->getFBO(FBO_HALF2), m_rtts->getFBO(FBO_COLORS), m_post_processing); PROFILER_POP_CPU_MARKER(); } if (irr_driver->getRH()) { glDisable(GL_BLEND); m_rtts->getFBO(FBO_COLORS).bind(); m_post_processing->renderRHDebug(m_rtts->getRadianceHintFrameBuffer().getRTT()[0], m_rtts->getRadianceHintFrameBuffer().getRTT()[1], m_rtts->getRadianceHintFrameBuffer().getRTT()[2], m_shadow_matrices.getRHMatrix(), m_shadow_matrices.getRHExtend()); } if (irr_driver->getGI()) { glDisable(GL_BLEND); m_rtts->getFBO(FBO_COLORS).bind(); m_lighting_passes.renderGlobalIllumination(m_shadow_matrices, m_rtts->getRadianceHintFrameBuffer(), m_rtts->getRenderTarget(RTT_NORMAL_AND_DEPTH), m_rtts->getDepthStencilTexture()); } PROFILER_PUSH_CPU_MARKER("- Glow", 0xFF, 0xFF, 0x00); // Render anything glowing. if (!irr_driver->getWireframe() && !irr_driver->getMipViz() && UserConfigParams::m_glow) { ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_GLOW)); irr_driver->setPhase(GLOW_PASS); m_geometry_passes->renderGlowingObjects(m_draw_calls, m_glowing, m_rtts->getFBO(FBO_TMP1_WITH_DS)); m_post_processing->renderGlow(m_rtts->getFBO(FBO_TMP1_WITH_DS), m_rtts->getFBO(FBO_HALF1), m_rtts->getFBO(FBO_QUARTER1), m_rtts->getFBO(FBO_COLORS)); } // end glow PROFILER_POP_CPU_MARKER(); // Render transparent { PROFILER_PUSH_CPU_MARKER("- Transparent Pass", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_TRANSPARENT)); m_geometry_passes->renderTransparent(m_draw_calls, m_rtts->getFBO(FBO_TMP1_WITH_DS), m_rtts->getFBO(FBO_DISPLACE), m_rtts->getFBO(FBO_COLORS), m_post_processing); PROFILER_POP_CPU_MARKER(); } m_draw_calls.setFenceSync(); // Render particles { PROFILER_PUSH_CPU_MARKER("- Particles", 0xFF, 0xFF, 0x00); ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_PARTICLES)); renderParticles(); PROFILER_POP_CPU_MARKER(); } if (!CVS->isDefferedEnabled() && !forceRTT) { glDisable(GL_FRAMEBUFFER_SRGB); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); return; } // Ensure that no object will be drawn after that by using invalid pass irr_driver->setPhase(PASS_COUNT); } //renderScene // ---------------------------------------------------------------------------- void ShaderBasedRenderer::renderParticles() { glDepthMask(GL_FALSE); glDisable(GL_CULL_FACE); glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); m_draw_calls.renderParticlesList(); // m_scene_manager->drawAll(scene::ESNRP_TRANSPARENT_EFFECT); } //renderParticles // ---------------------------------------------------------------------------- void ShaderBasedRenderer::debugPhysics() { // 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(Physics::getInstance()) { if (UserConfigParams::m_artist_debug_mode) Physics::getInstance()->draw(); IrrDebugDrawer* debug_drawer = Physics::getInstance()->getDebugDrawer(); if (debug_drawer != NULL && debug_drawer->debugEnabled()) { const std::map >& lines = debug_drawer->getLines(); std::map >::const_iterator it; Shaders::ColoredLine *line = Shaders::ColoredLine::getInstance(); line->use(); line->bindVertexArray(); line->bindBuffer(); for (it = lines.begin(); it != lines.end(); it++) { line->setUniforms(it->first); const std::vector &vertex = it->second; const float *tmp = vertex.data(); for (unsigned int i = 0; i < vertex.size(); i += 1024 * 6) { unsigned count = std::min((unsigned)vertex.size() - i, (unsigned)1024 * 6); glBufferSubData(GL_ARRAY_BUFFER, 0, count * sizeof(float), &tmp[i]); glDrawArrays(GL_LINES, 0, count / 3); } } glUseProgram(0); glBindVertexArray(0); } } } //debugPhysics // ---------------------------------------------------------------------------- void ShaderBasedRenderer::renderPostProcessing(Camera * const camera) { scene::ICameraSceneNode * const camnode = camera->getCameraSceneNode(); const core::recti &viewport = camera->getViewport(); bool isRace = StateManager::get()->getGameState() == GUIEngine::GAME; FrameBuffer *fbo = m_post_processing->render(camnode, isRace, m_rtts); // The viewport has been changed using glViewport function directly // during scene rendering, but irrlicht thinks that nothing changed // when single camera is used. In this case we set the viewport // to whole screen manually. glViewport(0, 0, irr_driver->getActualScreenSize().Width, irr_driver->getActualScreenSize().Height); if (irr_driver->getNormals()) { m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).BlitToDefault( viewport.UpperLeftCorner.X, irr_driver->getActualScreenSize().Height - viewport.LowerRightCorner.Y, viewport.LowerRightCorner.X, irr_driver->getActualScreenSize().Height - viewport.UpperLeftCorner.Y); } else if (irr_driver->getSSAOViz()) { glBindFramebuffer(GL_FRAMEBUFFER, 0); camera->activate(); m_post_processing->renderPassThrough(m_rtts->getFBO(FBO_HALF1_R).getRTT()[0], viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y); } else if (irr_driver->getRSM()) { glBindFramebuffer(GL_FRAMEBUFFER, 0); camera->activate(); m_post_processing->renderPassThrough(m_rtts->getReflectiveShadowMapFrameBuffer().getRTT()[0], viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y); } else if (irr_driver->getShadowViz()) { m_shadow_matrices.renderShadowsDebug(m_rtts->getShadowFrameBuffer(), m_post_processing); } else { glEnable(GL_FRAMEBUFFER_SRGB); glBindFramebuffer(GL_FRAMEBUFFER, 0); camera->activate(); m_post_processing->renderPassThrough(fbo->getRTT()[0], viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y); glDisable(GL_FRAMEBUFFER_SRGB); } } //renderPostProcessing // ---------------------------------------------------------------------------- ShaderBasedRenderer::ShaderBasedRenderer() { m_rtts = NULL; m_skybox = NULL; m_spherical_harmonics = new SphericalHarmonics(irr_driver->getAmbientLight().toSColor()); m_nb_static_glowing = 0; Log::info("ShaderBasedRenderer", "Preloading shaders..."); preloadShaderFiles(); if (CVS->isAZDOEnabled()) { m_geometry_passes = new GeometryPasses(); Log::info("ShaderBasedRenderer", "Geometry will be rendered with multidraw policy."); } else if (CVS->supportsIndirectInstancingRendering()) { m_geometry_passes = new GeometryPasses(); Log::info("ShaderBasedRenderer", "Geometry will be rendered with indirect draw policy."); } else { m_geometry_passes = new GeometryPasses(); Log::info("ShaderBasedRenderer", "Geometry will be rendered with GL3 policy."); } m_post_processing = new PostProcessing(irr_driver->getVideoDriver()); } // ---------------------------------------------------------------------------- ShaderBasedRenderer::~ShaderBasedRenderer() { // Note that we can not simply delete m_post_processing here: // m_post_processing uses a material that has a reference to // m_post_processing (for a callback). So when the material is // removed it will try to drop the ref count of its callback object, // which is m_post_processing, and which was already deleted. So // instead we just decrease the ref count here. When the material // is deleted, it will trigger the actual deletion of // PostProcessing when decreasing the refcount of its callback object. if(m_post_processing) { // check if we createad the OpenGL device by calling initDevice() m_post_processing->drop(); } delete m_geometry_passes; delete m_spherical_harmonics; delete m_skybox; delete m_rtts; ShaderFilesManager::kill(); } // ---------------------------------------------------------------------------- void ShaderBasedRenderer::onLoadWorld() { const core::recti &viewport = Camera::getCamera(0)->getViewport(); size_t width = viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X; size_t height = viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y; RTT* rtts = new RTT(width, height); setRTT(rtts); } // ---------------------------------------------------------------------------- void ShaderBasedRenderer::onUnloadWorld() { delete m_rtts; m_rtts = NULL; removeSkyBox(); } // ---------------------------------------------------------------------------- void ShaderBasedRenderer::resetPostProcessing() { m_post_processing->reset(); } // ---------------------------------------------------------------------------- void ShaderBasedRenderer::giveBoost(unsigned int cam_index) { m_post_processing->giveBoost(cam_index); } // ---------------------------------------------------------------------------- void ShaderBasedRenderer::addSkyBox(const std::vector &texture, const std::vector &spherical_harmonics_textures) { m_skybox = new Skybox(texture); if(spherical_harmonics_textures.size() == 6) { m_spherical_harmonics->setTextures(spherical_harmonics_textures); } } // ---------------------------------------------------------------------------- void ShaderBasedRenderer::removeSkyBox() { delete m_skybox; m_skybox = NULL; } // ---------------------------------------------------------------------------- const SHCoefficients* ShaderBasedRenderer::getSHCoefficients() const { return m_spherical_harmonics->getCoefficients(); } // ---------------------------------------------------------------------------- GLuint ShaderBasedRenderer::getRenderTargetTexture(TypeRTT which) const { return m_rtts->getRenderTarget(which); } // ---------------------------------------------------------------------------- GLuint ShaderBasedRenderer::getDepthStencilTexture() const { return m_rtts->getDepthStencilTexture(); } // ---------------------------------------------------------------------------- void ShaderBasedRenderer::setAmbientLight(const video::SColorf &light, bool force_SH_computation) { if (force_SH_computation) m_spherical_harmonics->setAmbientLight(light.toSColor()); } // ---------------------------------------------------------------------------- void ShaderBasedRenderer::addSunLight(const core::vector3df &pos) { m_shadow_matrices.addLight(pos); } // ---------------------------------------------------------------------------- void ShaderBasedRenderer::addGlowingNode(scene::ISceneNode *n, float r, float g, float b) { GlowData dat; dat.node = n; dat.r = r; dat.g = g; dat.b = b; STKMeshSceneNode *node = static_cast(n); node->setGlowColors(irr::video::SColor(0, (unsigned) (dat.r * 255.f), (unsigned)(dat.g * 255.f), (unsigned)(dat.b * 255.f))); m_glowing.push_back(dat); m_nb_static_glowing++; } //addGlowingNode // ---------------------------------------------------------------------------- void ShaderBasedRenderer::clearGlowingNodes() { m_glowing.clear(); m_nb_static_glowing = 0; } // ---------------------------------------------------------------------------- void ShaderBasedRenderer::render(float dt) { resetObjectCount(); resetPolyCount(); setOverrideMaterial(); addItemsInGlowingList(); // 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(); World *world = World::getWorld(); // Never NULL. Track *track = Track::getCurrentTrack(); RaceGUIBase *rg = world->getRaceGUI(); if (rg) rg->update(dt); bool force_rtt = UserConfigParams::m_scale_rtts_factor != 1.0f; if (!CVS->isDefferedEnabled() && !force_rtt) { prepareForwardRenderer(); } 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(!CVS->isDefferedEnabled() && !force_rtt); rg->preRenderCallback(camera); // adjusts start referee irr_driver->getSceneManager()->setActiveCamera(camnode); const core::recti &viewport = camera->getViewport(); if (!CVS->isDefferedEnabled()) glEnable(GL_FRAMEBUFFER_SRGB); PROFILER_PUSH_CPU_MARKER("Update Light Info", 0xFF, 0x0, 0x0); m_lighting_passes.updateLightsInfo(camnode, dt); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("UBO upload", 0x0, 0xFF, 0x0); computeMatrixesAndCameras(camnode, viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y); m_shadow_matrices.updateSunOrthoMatrices(); if(CVS->isARBUniformBufferObjectUsable()) uploadLightingData(); PROFILER_POP_CPU_MARKER(); renderScene(camnode, dt, track->hasShadows(), force_rtt); if (irr_driver->getBoundingBoxesViz()) { m_draw_calls.renderBoundingBoxes(); } debugPhysics(); if (CVS->isDefferedEnabled() || force_rtt) { renderPostProcessing(camera); } // Save projection-view matrix for the next frame camera->setPreviousPVMatrix(irr_driver->getProjViewMatrix()); PROFILER_POP_CPU_MARKER(); } // for igetNumKarts() if(CVS->isARBUniformBufferObjectUsable()) { // Use full screen size float tmp[2]; tmp[0] = float(irr_driver->getActualScreenSize().Width); tmp[1] = float(irr_driver->getActualScreenSize().Height); glBindBuffer(GL_UNIFORM_BUFFER, SharedGPUObjects::getViewProjectionMatricesUBO()); glBufferSubData(GL_UNIFORM_BUFFER, (16 * 9) * sizeof(float), 2 * sizeof(float), tmp); } 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 irr_driver->getVideoDriver()->setViewPort(core::recti(0, 0, irr_driver->getActualScreenSize().Width, irr_driver->getActualScreenSize().Height)); m_current_screen_size = core::vector2df( irr_driver->getActualScreenSize().Width, irr_driver->getActualScreenSize().Height); for(unsigned int i=0; irenderPlayerView(camera, dt); PROFILER_POP_CPU_MARKER(); } // for igetGPUTimer(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); irr_driver->getVideoDriver()->endScene(); PROFILER_POP_CPU_MARKER(); m_post_processing->update(dt); removeItemsInGlowingList(); } //render // ---------------------------------------------------------------------------- std::unique_ptr ShaderBasedRenderer::createRenderTarget(const irr::core::dimension2du &dimension, const std::string &name) { return std::unique_ptr(new GL3RenderTarget(dimension, name, this)); //return std::make_unique(dimension, name, this); //require C++14 } // ---------------------------------------------------------------------------- void ShaderBasedRenderer::renderToTexture(GL3RenderTarget *render_target, irr::scene::ICameraSceneNode* camera, float dt) { resetObjectCount(); resetPolyCount(); assert(m_rtts != NULL); irr_driver->getSceneManager()->setActiveCamera(camera); computeMatrixesAndCameras(camera, m_rtts->getWidth(), m_rtts->getHeight()); updateLightsInfo(camera, dt); if (CVS->isARBUniformBufferObjectUsable()) uploadLightingData(); renderScene(camera, dt, false, true); render_target->setFrameBuffer(m_post_processing ->render(camera, false, m_rtts)); // reset glViewport(0, 0, irr_driver->getActualScreenSize().Width, irr_driver->getActualScreenSize().Height); glBindFramebuffer(GL_FRAMEBUFFER, 0); irr_driver->getSceneManager()->setActiveCamera(NULL); } //renderToTexture // ---------------------------------------------------------------------------- void ShaderBasedRenderer::preloadShaderFiles() { SharedGPUObjects::init(); ShaderFilesManager* sfm = ShaderFilesManager::getInstance(); sfm->addShaderFile("object_pass.vert", GL_VERTEX_SHADER); sfm->addShaderFile("object_pass1.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("splatting.frag", GL_FRAGMENT_SHADER); if (CVS->supportsHardwareSkinning()) sfm->addShaderFile("skinning.vert", GL_VERTEX_SHADER); sfm->addShaderFile("transparent.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("coloredquad.vert", GL_VERTEX_SHADER); sfm->addShaderFile("coloredquad.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("screenquad.vert", GL_VERTEX_SHADER); sfm->addShaderFile("tonemap.frag", GL_FRAGMENT_SHADER); if (!GraphicsRestrictions::isDisabled (GraphicsRestrictions::GR_FRAMEBUFFER_SRGB_WORKING)) sfm->addShaderFile("passthrough.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("billboard.vert", GL_VERTEX_SHADER); sfm->addShaderFile("billboard.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("pointemitter.vert", GL_VERTEX_SHADER); sfm->addShaderFile("particle.vert", GL_VERTEX_SHADER); sfm->addShaderFile("particle.frag", GL_FRAGMENT_SHADER); if (CVS->supportsIndirectInstancingRendering()) { sfm->addShaderFile("instanced_object_pass.vert", GL_VERTEX_SHADER); if (CVS->supportsHardwareSkinning()) sfm->addShaderFile("instanced_skinning.vert", GL_VERTEX_SHADER); sfm->addShaderFile("instanced_object_pass1.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("instanced_object_pass2.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("instanced_objectref_pass1.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("instanced_objectref_pass2.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("instanced_object_unlit.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("instanced_normalmap.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("instanced_grass.vert", GL_VERTEX_SHADER); sfm->addShaderFile("instanced_grass_pass2.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("instanced_objectpass_spheremap.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("instanced_detailed_object_pass2.frag", GL_FRAGMENT_SHADER); } else { sfm->addShaderFile("object_pass2.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("objectref_pass1.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("objectref_pass2.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("object_unlit.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("normalmap.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("grass_pass.vert", GL_VERTEX_SHADER); sfm->addShaderFile("grass_pass2.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("objectpass_spheremap.frag", GL_FRAGMENT_SHADER); sfm->addShaderFile("detailed_object_pass2.frag", GL_FRAGMENT_SHADER); } } //preloadShaderFiles #endif // !SERVER_ONLY