diff --git a/CMakeLists.txt b/CMakeLists.txt index 62b41aa5..d87f1d47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,7 +68,6 @@ if (APPLE) endif() option(WITH_OPENGL_MODERN "Compile with OpenGL modern renderer" ON) -option(WITH_OPENGL_LEGACY "Compile with OpenGL legacy renderer" ON) option(WITH_PYTHON "Python 3.14 embedded scripting" ON) option(WITH_UART "Compile with libserialport" ON) option(WITH_LUA "Compile with lua scripting support" ON) @@ -225,7 +224,6 @@ set(SOURCES "utilities/headtrack.cpp" "scripting/ladderlogic.cpp" "rendering/geometrybank.cpp" -"rendering/openglcolor.cpp" "simulation/simulationsounds.cpp" "rendering/openglcamera.cpp" @@ -284,9 +282,7 @@ set(SOURCES set(DEFINITIONS) set(SOURCES ${SOURCES} "imgui/imgui_impl_opengl3.cpp") -set(SOURCES ${SOURCES} "imgui/imgui_impl_opengl2.cpp") set_source_files_properties("imgui/imgui_impl_opengl3.cpp" PROPERTIES SKIP_PRECOMPILE_HEADERS TRUE) -set_source_files_properties("imgui/imgui_impl_opengl2.cpp" PROPERTIES SKIP_PRECOMPILE_HEADERS TRUE) if (WITH_CRASHPAD) set(DEFINITIONS ${DEFINITIONS} "WITH_CRASHPAD") @@ -341,17 +337,6 @@ if (WITH_OPENGL_MODERN) set(DEFINITIONS ${DEFINITIONS} "WITH_OPENGL_MODERN") endif() -if (WITH_OPENGL_LEGACY) - set(SOURCES ${SOURCES} - "rendering/openglgeometrybank.cpp" - "rendering/opengllight.cpp" - "rendering/openglskydome.cpp" - "rendering/openglprecipitation.cpp" - "rendering/openglparticles.cpp" - "rendering/openglrenderer.cpp") - set(DEFINITIONS ${DEFINITIONS} "WITH_OPENGL_LEGACY") -endif() - add_subdirectory("${DEPS_DIR}/glfw/" EXCLUDE_FROM_ALL) set(SOURCES ${SOURCES} "extras/piped_proc.cpp" "widgets/cameraview_extcam.cpp") diff --git a/application/application.cpp b/application/application.cpp index 8afd50db..eefd20d8 100644 --- a/application/application.cpp +++ b/application/application.cpp @@ -1252,7 +1252,7 @@ int eu07_application::init_glfw() monitor = glfwGetPrimaryMonitor(); glfwWindowHint(GLFW_AUTO_ICONIFY, GLFW_FALSE); - if ((Global.gfx_skippipeline || Global.LegacyRenderer) && Global.iMultisampling > 0) + if (Global.gfx_skippipeline && Global.iMultisampling > 0) { glfwWindowHint(GLFW_SAMPLES, 1 << Global.iMultisampling); } @@ -1265,39 +1265,23 @@ int eu07_application::init_glfw() } else { - if (!Global.LegacyRenderer) + // activate core profile for opengl 3.3 renderer + if (!Global.gfx_usegles) { - Global.bUseVBO = true; - // activate core profile for opengl 3.3 renderer - if (!Global.gfx_usegles) - { - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - } - else - { -#ifdef GLFW_CONTEXT_CREATION_API - if (m_glfwversion >= 30200) - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); -#endif - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - } + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); } else { - if (Global.gfx_usegles) - { - ErrorLog("legacy renderer not supported in gles mode"); - return -1; - } - Global.gfx_shadergamma = false; - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); +#ifdef GLFW_CONTEXT_CREATION_API + if (m_glfwversion >= 30200) + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); +#endif + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); } if (Global.gfx_gldebug) @@ -1396,20 +1380,15 @@ int eu07_application::init_ui() int eu07_application::init_gfx() { - if (Global.GfxRenderer == "default") - { - // default render path - GfxRenderer = gfx_renderer_factory::get_instance()->create("modern"); - } - else if (Global.GfxRenderer == "experimental") + // NOTE: global_settings maps unsupported gfxrenderer values (e.g. the removed "legacy") to "default" + if (Global.GfxRenderer == "experimental") { GfxRenderer = gfx_renderer_factory::get_instance()->create(Global.GfxRenderer); } else { - // legacy render path - GfxRenderer = gfx_renderer_factory::get_instance()->create("legacy"); - Global.DisabledLogTypes |= static_cast(logtype::material); + // default render path + GfxRenderer = gfx_renderer_factory::get_instance()->create("modern"); } if (!GfxRenderer) diff --git a/application/driveruipanels.cpp b/application/driveruipanels.cpp index e318d356..c1da3f5e 100644 --- a/application/driveruipanels.cpp +++ b/application/driveruipanels.cpp @@ -1446,26 +1446,8 @@ debug_panel::update_section_renderer( std::vector &Output ) const { { textline += "NVRHI on "; } - else - { - if (Global.BasicRenderer) - { - textline += "Legacy Simple"; - } - else - { - textline += "Legacy"; - } - } - if (Global.bUseVBO) - { - textline += ", VBO"; - } - else - { - textline += ", Display Lists"; - } + textline += ", VBO"; textline += " "; diff --git a/audio/audiorenderer.h b/audio/audiorenderer.h index 3f462eb4..e48d582d 100644 --- a/audio/audiorenderer.h +++ b/audio/audiorenderer.h @@ -30,7 +30,6 @@ http://mozilla.org/MPL/2.0/. #define EU07_SOUND_PROOFINGUSESRANGE -class opengl_renderer; class sound_source; using uint32_sequence = std::vector; @@ -101,8 +100,6 @@ private: class openal_renderer { - friend opengl_renderer; - public: openal_renderer() = default; ~openal_renderer(); diff --git a/environment/sky.h b/environment/sky.h index 34b1ae75..94f3245f 100644 --- a/environment/sky.h +++ b/environment/sky.h @@ -14,7 +14,6 @@ http://mozilla.org/MPL/2.0/. class TSky { - friend opengl_renderer; friend opengl33_renderer; public: diff --git a/environment/stars.h b/environment/stars.h index f3643d26..393b3f6c 100644 --- a/environment/stars.h +++ b/environment/stars.h @@ -8,7 +8,6 @@ class cStars { - friend opengl_renderer; friend opengl33_renderer; public: diff --git a/imgui/imgui_impl_opengl2.cpp b/imgui/imgui_impl_opengl2.cpp deleted file mode 100644 index d970e52a..00000000 --- a/imgui/imgui_impl_opengl2.cpp +++ /dev/null @@ -1,349 +0,0 @@ -// dear imgui: Renderer Backend for OpenGL2 (legacy OpenGL, fixed pipeline) -// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) - -// Implemented features: -// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -// Missing features or Issues: -// [ ] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// Learn about Dear ImGui: -// - FAQ https://dearimgui.com/faq -// - Getting Started https://dearimgui.com/getting-started -// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). -// - Introduction, links and more at the top of imgui.cpp - -// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** -// **Prefer using the code in imgui_impl_opengl3.cpp** -// This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. -// If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more -// complicated, will require your code to reset every single OpenGL attributes to their initial state, and might -// confuse your GPU driver. -// The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. -// 2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap. -// 2024-06-28: OpenGL: ImGui_ImplOpenGL2_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL2_DestroyFontsTexture(). (#7748) -// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. -// 2021-12-08: OpenGL: Fixed mishandling of the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86. -// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). -// 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) -// 2021-01-03: OpenGL: Backup, setup and restore GL_SHADE_MODEL state, disable GL_STENCIL_TEST and disable GL_NORMAL_ARRAY client state to increase compatibility with legacy OpenGL applications. -// 2020-01-23: OpenGL: Backup, setup and restore GL_TEXTURE_ENV to increase compatibility with legacy OpenGL applications. -// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. -// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display. -// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. -// 2018-08-03: OpenGL: Disabling/restoring GL_LIGHTING and GL_COLOR_MATERIAL to increase compatibility with legacy OpenGL applications. -// 2018-06-08: Misc: Extracted imgui_impl_opengl2.cpp/.h away from the old combined GLFW/SDL+OpenGL2 examples. -// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplOpenGL2_RenderDrawData() in the .h file so you can call it yourself. -// 2017-09-01: OpenGL: Save and restore current polygon mode. -// 2016-09-10: OpenGL: Uploading font texture as RGBA32 to increase compatibility with users shaders (not ideal). -// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. - -#include "imgui.h" -#ifndef IMGUI_DISABLE -#include "imgui_impl_opengl2.h" -#include // intptr_t - -// Clang/GCC warnings with -Weverything -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-macros" // warning: macro is not used -#pragma clang diagnostic ignored "-Wnonportable-system-include-path" -#endif - -#include // MaSzyna: GL functions come from glad, initialized by the simulator - -// [Debugging] -//#define IMGUI_IMPL_OPENGL_DEBUG -#ifdef IMGUI_IMPL_OPENGL_DEBUG -#include -#define GL_CALL(_CALL) do { _CALL; GLenum gl_err = glGetError(); if (gl_err != 0) fprintf(stderr, "GL error 0x%x returned from '%s'.\n", gl_err, #_CALL); } while (0) // Call with error check -#else -#define GL_CALL(_CALL) _CALL // Call without error check -#endif - -// OpenGL data -struct ImGui_ImplOpenGL2_Data -{ - GLuint FontTexture; - - ImGui_ImplOpenGL2_Data() { memset((void*)this, 0, sizeof(*this)); } -}; - -// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts -// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. -static ImGui_ImplOpenGL2_Data* ImGui_ImplOpenGL2_GetBackendData() -{ - return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL2_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; -} - -// Forward Declarations -static void ImGui_ImplOpenGL2_InitMultiViewportSupport(); -static void ImGui_ImplOpenGL2_ShutdownMultiViewportSupport(); - -// Functions -bool ImGui_ImplOpenGL2_Init() -{ - ImGuiIO& io = ImGui::GetIO(); - IMGUI_CHECKVERSION(); - IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); - - // Setup backend capabilities flags - ImGui_ImplOpenGL2_Data* bd = IM_NEW(ImGui_ImplOpenGL2_Data)(); - io.BackendRendererUserData = (void*)bd; - io.BackendRendererName = "imgui_impl_opengl2"; - io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) - - ImGui_ImplOpenGL2_InitMultiViewportSupport(); - - return true; -} - -void ImGui_ImplOpenGL2_Shutdown() -{ - ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); - IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); - ImGuiIO& io = ImGui::GetIO(); - - ImGui_ImplOpenGL2_ShutdownMultiViewportSupport(); - ImGui_ImplOpenGL2_DestroyDeviceObjects(); - io.BackendRendererName = nullptr; - io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasViewports; - IM_DELETE(bd); -} - -void ImGui_ImplOpenGL2_NewFrame() -{ - ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); - IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplOpenGL2_Init()?"); - - if (!bd->FontTexture) - ImGui_ImplOpenGL2_CreateDeviceObjects(); - if (!bd->FontTexture) - ImGui_ImplOpenGL2_CreateFontsTexture(); -} - -static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height) -{ - // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - //glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // In order to composite our output buffer we need to preserve alpha - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_STENCIL_TEST); - glDisable(GL_LIGHTING); - glDisable(GL_COLOR_MATERIAL); - glEnable(GL_SCISSOR_TEST); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - glEnable(GL_TEXTURE_2D); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glShadeModel(GL_SMOOTH); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - // If you are using this code with non-legacy OpenGL header/contexts (which you should not, prefer using imgui_impl_opengl3.cpp!!), - // you may need to backup/reset/restore other state, e.g. for current shader using the commented lines below. - // (DO NOT MODIFY THIS FILE! Add the code in your calling function) - // GLint last_program; - // glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); - // glUseProgram(0); - // ImGui_ImplOpenGL2_RenderDrawData(...); - // glUseProgram(last_program) - // There are potentially many more states you could need to clear/setup that we can't access from default headers. - // e.g. glBindBuffer(GL_ARRAY_BUFFER, 0), glDisable(GL_TEXTURE_CUBE_MAP). - - // Setup viewport, orthographic projection matrix - // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. - GL_CALL(glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height)); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(draw_data->DisplayPos.x, draw_data->DisplayPos.x + draw_data->DisplaySize.x, draw_data->DisplayPos.y + draw_data->DisplaySize.y, draw_data->DisplayPos.y, -1.0f, +1.0f); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); -} - -// OpenGL2 Render function. -// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly. -// This is in order to be able to run within an OpenGL engine that doesn't do so. -void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) -{ - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); - int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); - if (fb_width == 0 || fb_height == 0) - return; - - // Backup GL state - GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); - GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); - GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); - GLint last_shade_model; glGetIntegerv(GL_SHADE_MODEL, &last_shade_model); - GLint last_tex_env_mode; glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &last_tex_env_mode); - glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); - - // Setup desired GL state - ImGui_ImplOpenGL2_SetupRenderState(draw_data, fb_width, fb_height); - - // Will project scissor/clipping rectangles into framebuffer space - ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports - ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) - - // Render command lists - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* draw_list = draw_data->CmdLists[n]; - const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data; - const ImDrawIdx* idx_buffer = draw_list->IdxBuffer.Data; - glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + offsetof(ImDrawVert, pos))); - glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + offsetof(ImDrawVert, uv))); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + offsetof(ImDrawVert, col))); - - for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) - { - // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) - ImGui_ImplOpenGL2_SetupRenderState(draw_data, fb_width, fb_height); - else - pcmd->UserCallback(draw_list, pcmd); - } - else - { - // Project scissor/clipping rectangles into framebuffer space - ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); - ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); - if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) - continue; - - // Apply scissor/clipping rectangle (Y is inverted in OpenGL) - glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y)); - - // Bind texture, Draw - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset); - } - } - } - - // Restore modified GL state - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glPopAttrib(); - glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]); - glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); - glShadeModel(last_shade_model); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, last_tex_env_mode); -} - -bool ImGui_ImplOpenGL2_CreateFontsTexture() -{ - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. - - // Upload texture to graphics system - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &bd->FontTexture); - glBindTexture(GL_TEXTURE_2D, bd->FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Store our identifier - io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); - - // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); - - return true; -} - -void ImGui_ImplOpenGL2_DestroyFontsTexture() -{ - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); - if (bd->FontTexture) - { - glDeleteTextures(1, &bd->FontTexture); - io.Fonts->SetTexID(0); - bd->FontTexture = 0; - } -} - -bool ImGui_ImplOpenGL2_CreateDeviceObjects() -{ - return ImGui_ImplOpenGL2_CreateFontsTexture(); -} - -void ImGui_ImplOpenGL2_DestroyDeviceObjects() -{ - ImGui_ImplOpenGL2_DestroyFontsTexture(); -} - - -//-------------------------------------------------------------------------------------------------------- -// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT -// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. -// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. -//-------------------------------------------------------------------------------------------------------- - -static void ImGui_ImplOpenGL2_RenderWindow(ImGuiViewport* viewport, void*) -{ - if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) - { - ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); - glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); - glClear(GL_COLOR_BUFFER_BIT); - } - ImGui_ImplOpenGL2_RenderDrawData(viewport->DrawData); -} - -static void ImGui_ImplOpenGL2_InitMultiViewportSupport() -{ - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL2_RenderWindow; -} - -static void ImGui_ImplOpenGL2_ShutdownMultiViewportSupport() -{ - ImGui::DestroyPlatformWindows(); -} - -//----------------------------------------------------------------------------- - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -#endif // #ifndef IMGUI_DISABLE diff --git a/imgui/imgui_impl_opengl2.h b/imgui/imgui_impl_opengl2.h deleted file mode 100644 index c7b40e07..00000000 --- a/imgui/imgui_impl_opengl2.h +++ /dev/null @@ -1,42 +0,0 @@ -// dear imgui: Renderer Backend for OpenGL2 (legacy OpenGL, fixed pipeline) -// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) - -// Implemented features: -// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -// Missing features or Issues: -// [ ] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// Learn about Dear ImGui: -// - FAQ https://dearimgui.com/faq -// - Getting Started https://dearimgui.com/getting-started -// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). -// - Introduction, links and more at the top of imgui.cpp - -// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** -// **Prefer using the code in imgui_impl_opengl3.cpp** -// This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. -// If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more -// complicated, will require your code to reset every single OpenGL attributes to their initial state, and might -// confuse your GPU driver. -// The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. - -#pragma once -#include "imgui.h" // IMGUI_IMPL_API -#ifndef IMGUI_DISABLE - -// Follow "Getting Started" link and check examples/ folder to learn about using backends! -IMGUI_IMPL_API bool ImGui_ImplOpenGL2_Init(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_NewFrame(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data); - -// Called by Init/NewFrame/Shutdown -IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateFontsTexture(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyFontsTexture(); -IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateDeviceObjects(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyDeviceObjects(); - -#endif // #ifndef IMGUI_DISABLE diff --git a/model/AnimModel.h b/model/AnimModel.h index 0087418d..fdec3cff 100644 --- a/model/AnimModel.h +++ b/model/AnimModel.h @@ -107,7 +107,6 @@ class TAnimContainer : public std::enable_shared_from_this // opakowanie modelu, określające stan egzemplarza class TAnimModel : public scene::basic_node { - friend opengl_renderer; friend opengl33_renderer; friend itemproperties_panel; diff --git a/model/Model3d.h b/model/Model3d.h index 27565c20..6ee06a32 100644 --- a/model/Model3d.h +++ b/model/Model3d.h @@ -56,7 +56,6 @@ class TSubModel { // klasa submodelu - pojedyncza siatka, punkt świetlny albo grupa punktów //m7todo: zrobić normalną serializację - friend opengl_renderer; friend opengl33_renderer; friend TModel3d; // temporary workaround. TODO: clean up class content/hierarchy friend TDynamicObject; // temporary etc @@ -268,7 +267,6 @@ public: class TModel3d { - friend opengl_renderer; friend opengl33_renderer; public: diff --git a/model/Texture.cpp b/model/Texture.cpp index f5b57ebb..89caea19 100644 --- a/model/Texture.cpp +++ b/model/Texture.cpp @@ -1374,28 +1374,6 @@ opengl_texture::set_filtering() const // anisotropic filtering ::glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, Global.AnisotropicFiltering ); } - - if( Global.LegacyRenderer ) { - - bool sharpen{ false }; - for( auto const &trait : traits ) { - - switch( trait ) { - - case '#': { sharpen = true; break; } - default: { break; } - } - } - - if( true == sharpen ) { - // #: sharpen more - ::glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -2.0f ); - } - else { - // regular texture sharpening - ::glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0f ); - } - } } void diff --git a/nodebank_previews_standalone.cpp b/nodebank_previews_standalone.cpp index 39884fd3..d339f847 100644 --- a/nodebank_previews_standalone.cpp +++ b/nodebank_previews_standalone.cpp @@ -306,9 +306,7 @@ int generate_nodebank_previews(int Argc, char *Argv[]) // the full renderer, drawing offscreen Global.GfxRenderer = "default"; - Global.LegacyRenderer = false; Global.NvRenderer = false; - Global.BasicRenderer = false; Global.gfx_usegles = false; Global.gfx_skippipeline = false; Global.gfx_postfx_motionblur_enabled = false; diff --git a/rendering/openglcolor.cpp b/rendering/openglcolor.cpp deleted file mode 100644 index 3ca8ad66..00000000 --- a/rendering/openglcolor.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#include "stdafx.h" -#include "rendering/openglcolor.h" - -opengl_color OpenGLColor; - diff --git a/rendering/openglcolor.h b/rendering/openglcolor.h deleted file mode 100644 index f6777bed..00000000 --- a/rendering/openglcolor.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#pragma once - -#include "utilities/Globals.h" - -// encapsulation of the fixed pipeline opengl color -class opengl_color { - -public: -// constructors: - opengl_color() = default; - -// methods: - inline - void - color3( glm::vec3 const &Color ) { - return color4( glm::vec4{ Color, 1.f } ); } - inline - void - color3( float const Red, float const Green, float const Blue ) { - return color3( glm::vec3 { Red, Green, Blue } ); } - inline - void - color3( float const *Value ) { - return color3( glm::make_vec3( Value ) ); } - inline - void - color4( glm::vec4 const &Color ) { - if( ( Color != m_color ) || ( false == Global.bUseVBO ) ) { - m_color = Color; - ::glColor4fv( glm::value_ptr( m_color ) ); } } - inline - void - color4( float const Red, float const Green, float const Blue, float const Alpha ) { - return color4( glm::vec4{ Red, Green, Blue, Alpha } ); } - inline - void - color4( float const *Value ) { - return color4( glm::make_vec4( Value ) ); - } - inline - glm::vec4 const & - data() const { - return m_color; } - inline - float const * - data_array() const { - return glm::value_ptr( m_color ); } - -private: -// members: - glm::vec4 m_color { -1 }; -}; - -extern opengl_color OpenGLColor; - -// NOTE: standard opengl calls re-definitions -#undef glColor3f -#undef glColor3fv -#undef glColor4f -#undef glColor4fv - -#define glColor3f OpenGLColor.color3 -#define glColor3fv OpenGLColor.color3 -#define glColor4f OpenGLColor.color4 -#define glColor4fv OpenGLColor.color4 - -//--------------------------------------------------------------------------- - diff --git a/rendering/openglgeometrybank.cpp b/rendering/openglgeometrybank.cpp deleted file mode 100644 index d020bb56..00000000 --- a/rendering/openglgeometrybank.cpp +++ /dev/null @@ -1,375 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#include "stdafx.h" -#include "rendering/openglgeometrybank.h" -#include "rendering/openglcolor.h" - -#include "utilities/Logs.h" - -namespace gfx { - -// opengl vbo-based variant of the geometry bank - -GLuint opengl_vbogeometrybank::m_activevertexbuffer { 0 }; // buffer bound currently on the opengl end, if any -unsigned int opengl_vbogeometrybank::m_activestreams { gfx::stream::none }; // currently enabled data type pointers -std::vector opengl_vbogeometrybank::m_activetexturearrays; // currently enabled texture coord arrays - -// create() subclass details -void -opengl_vbogeometrybank::create_( gfx::geometry_handle const &Geometry ) { - // adding a chunk means we'll be (re)building the buffer, which will fill the chunk records, amongst other things. - // thus we don't need to initialize the values here - m_chunkrecords.emplace_back( chunk_record() ); - // kiss the existing buffer goodbye, new overall data size means we'll be making a new one - delete_buffer(); -} - -// replace() subclass details -void -opengl_vbogeometrybank::replace_( gfx::geometry_handle const &Geometry ) { - - auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ]; - chunkrecord.is_good = false; - // if the overall length of the chunk didn't change we can get away with reusing the old buffer... - if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.vertex_count ) { - // ...but otherwise we'll need to allocate a new one - // TBD: we could keep and reuse the old buffer also if the new chunk is smaller than the old one, - // but it'd require some extra tracking and work to keep all chunks up to date; also wasting vram; may be not worth it? - delete_buffer(); - } -} - -// draw() subclass details -std::size_t -opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams ) { - - setup_buffer(); - - auto &chunkrecord { m_chunkrecords[ Geometry.chunk - 1 ] }; - // sanity check; shouldn't be needed but, eh - if( chunkrecord.vertex_count == 0 ) { return 0; } - // setup... - if( m_activevertexbuffer != m_vertexbuffer ) { - bind_buffer(); - } - auto const &chunk = gfx::geometry_bank::chunk( Geometry ); - if( false == chunkrecord.is_good ) { - // we may potentially need to upload new buffer data before we can draw it - if( chunkrecord.index_count > 0 ) { - ::glBufferSubData( - GL_ELEMENT_ARRAY_BUFFER, - chunkrecord.index_offset * sizeof( gfx::basic_index ), - chunkrecord.index_count * sizeof( gfx::basic_index ), - chunk.indices.data() ); - } - ::glBufferSubData( - GL_ARRAY_BUFFER, - chunkrecord.vertex_offset * sizeof( gfx::basic_vertex ), - chunkrecord.vertex_count * sizeof( gfx::basic_vertex ), - chunk.vertices.data() ); - chunkrecord.is_good = true; - } - // ...render... - if( chunkrecord.index_count > 0 ) { -/* - ::glDrawElementsBaseVertex( - chunk.type, - chunkrecord.index_count, GL_UNSIGNED_INT, reinterpret_cast( chunkrecord.index_offset * sizeof( gfx::basic_index ) ), - chunkrecord.vertex_offset ); -*/ - if (glDrawRangeElementsBaseVertex) { - if( m_activestreams != Streams ) { - bind_streams( Units, Streams ); - } - ::glDrawRangeElementsBaseVertex( - chunk.type, - 0, static_cast(chunkrecord.vertex_count), - static_cast(chunkrecord.index_count), GL_UNSIGNED_INT, reinterpret_cast( chunkrecord.index_offset * sizeof( gfx::basic_index ) ), - static_cast(chunkrecord.vertex_offset) ); - } - else { - bind_streams( Units, Streams, chunkrecord.vertex_offset ); - ::glDrawRangeElements( - chunk.type, - 0, static_cast(chunkrecord.vertex_count), - static_cast(chunkrecord.index_count), GL_UNSIGNED_INT, reinterpret_cast( chunkrecord.index_offset * sizeof( gfx::basic_index ) ) ); - } - } - else { - if( m_activestreams != Streams ) { - bind_streams( Units, Streams ); - } - ::glDrawArrays( chunk.type, static_cast(chunkrecord.vertex_offset), static_cast(chunkrecord.vertex_count) ); - } - // ...post-render cleanup -/* - ::glDisableClientState( GL_VERTEX_ARRAY ); - ::glDisableClientState( GL_NORMAL_ARRAY ); - ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); - ::glBindBuffer( GL_ARRAY_BUFFER, 0 ); m_activebuffer = 0; -*/ - auto const vertexcount { ( chunkrecord.index_count > 0 ? chunkrecord.index_count : chunkrecord.vertex_count ) }; - switch( chunk.type ) { - case GL_TRIANGLES: { return vertexcount / 3; } - case GL_TRIANGLE_STRIP: { return vertexcount - 2; } - default: { return 0; } - } -} - -// release () subclass details -void -opengl_vbogeometrybank::release_() { - - delete_buffer(); -} - -void -opengl_vbogeometrybank::setup_buffer() { - - if( m_vertexbuffer != 0 ) { return; } - // if there's no buffer, we'll have to make one - // NOTE: this isn't exactly optimal in terms of ensuring the gfx card doesn't stall waiting for the data - // may be better to initiate upload earlier (during update phase) and trust this effort won't go to waste - if( true == m_chunks.empty() ) { return; } - - std::size_t vertexcount{ 0 }; - std::size_t indexcount{ 0 }; - auto chunkiterator = m_chunks.cbegin(); - for( auto &chunkrecord : m_chunkrecords ) { - // fill records for all chunks, based on the chunk data - chunkrecord.is_good = false; // if we're re-creating buffer, chunks might've been uploaded in the old one - chunkrecord.vertex_offset = vertexcount; - chunkrecord.vertex_count = chunkiterator->vertices.size(); - vertexcount += chunkrecord.vertex_count; - chunkrecord.index_offset = indexcount; - chunkrecord.index_count = chunkiterator->indices.size(); - indexcount += chunkrecord.index_count; - ++chunkiterator; - } - // the odds for all created chunks to get replaced with empty ones are quite low, but the possibility does exist - if( vertexcount == 0 ) { return; } - // try to set up the buffers we need - if( ( indexcount > 0 ) && ( m_indexbuffer == 0 ) ) { - ::glGenBuffers( 1, &m_indexbuffer ); - if( m_indexbuffer == 0 ) { return; } // if we didn't get a buffer we'll try again during the next draw call - } - if( m_vertexbuffer == 0 ) { - ::glGenBuffers( 1, &m_vertexbuffer ); - if( m_vertexbuffer == 0 ) { return; } // if we didn't get a buffer we'll try again during the next draw call - } - bind_buffer(); - // NOTE: we're using static_draw since it's generally true for all we have implemented at the moment - // TODO: allow to specify usage hint at the object creation, and pass it here - if( indexcount > 0 ) { - ::glBufferData( - GL_ELEMENT_ARRAY_BUFFER, - indexcount * sizeof( gfx::basic_index ), - nullptr, - GL_STATIC_DRAW ); - if( ::glGetError() == GL_OUT_OF_MEMORY ) { - // TBD: throw a bad_alloc? - ErrorLog( "openGL error: out of memory; failed to create a geometry index buffer" ); - delete_buffer(); - return; - } - } - ::glBufferData( - GL_ARRAY_BUFFER, - vertexcount * sizeof( gfx::basic_vertex ), - nullptr, - GL_STATIC_DRAW ); - if( ::glGetError() == GL_OUT_OF_MEMORY ) { - // TBD: throw a bad_alloc? - ErrorLog( "openGL error: out of memory; failed to create a geometry buffer" ); - delete_buffer(); - return; - } -} - -void -opengl_vbogeometrybank::bind_buffer() const { - - ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer ); - ::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer ); - m_activevertexbuffer = m_vertexbuffer; - m_activestreams = gfx::stream::none; -} - -void -opengl_vbogeometrybank::delete_buffer() { - - if( m_indexbuffer != 0 ) { - ::glDeleteBuffers( 1, &m_indexbuffer ); - m_indexbuffer = 0; - } - if( m_vertexbuffer != 0 ) { - - ::glDeleteBuffers( 1, &m_vertexbuffer ); - if( m_activevertexbuffer == m_vertexbuffer ) { - m_activevertexbuffer = 0; - release_streams(); - } - m_vertexbuffer = 0; - // NOTE: since we've deleted the buffer all chunks it held were rendered invalid as well - // instead of clearing their state here we're delaying it until new buffer is created to avoid looping through chunk records twice - } -} - -void -opengl_vbogeometrybank::bind_streams( gfx::stream_units const &Units, unsigned int const Streams, size_t offset ) { - - if( Streams & gfx::stream::position ) { - ::glVertexPointer( 3, GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast( offsetof(gfx::basic_vertex, position) + sizeof( gfx::basic_vertex ) * offset ) ); - ::glEnableClientState( GL_VERTEX_ARRAY ); - } - else { - ::glDisableClientState( GL_VERTEX_ARRAY ); - } - // NOTE: normal and color streams share the data, making them effectively mutually exclusive - if( Streams & gfx::stream::normal ) { - ::glNormalPointer( GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast( offsetof(gfx::basic_vertex, normal) + sizeof( gfx::basic_vertex ) * offset ) ); - ::glEnableClientState( GL_NORMAL_ARRAY ); - } - else { - ::glDisableClientState( GL_NORMAL_ARRAY ); - } - if( Streams & gfx::stream::color ) { - ::glColorPointer( 3, GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast( offsetof(gfx::basic_vertex, normal) + sizeof( gfx::basic_vertex ) * offset ) ); - ::glEnableClientState( GL_COLOR_ARRAY ); - } - else { - ::glDisableClientState( GL_COLOR_ARRAY ); - } - if( Streams & gfx::stream::texture ) { - for( auto unit : Units.texture ) { - ::glClientActiveTexture( GL_TEXTURE0 + unit ); - ::glTexCoordPointer( 2, GL_FLOAT, sizeof( gfx::basic_vertex ), reinterpret_cast( offsetof(gfx::basic_vertex, texture) + sizeof( gfx::basic_vertex ) * offset ) ); - ::glEnableClientState( GL_TEXTURE_COORD_ARRAY ); - } - m_activetexturearrays = Units.texture; - } - else { - for( auto unit : Units.texture ) { - ::glClientActiveTexture( GL_TEXTURE0 + unit ); - ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); - } - m_activetexturearrays.clear(); // NOTE: we're simplifying here, since we always toggle the same texture coord sets - } - - m_activestreams = Streams; -} - -void -opengl_vbogeometrybank::release_streams() { - - ::glDisableClientState( GL_VERTEX_ARRAY ); - ::glDisableClientState( GL_NORMAL_ARRAY ); - ::glDisableClientState( GL_COLOR_ARRAY ); - for( auto unit : m_activetexturearrays ) { - ::glClientActiveTexture( GL_TEXTURE0 + unit ); - ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); - } - - m_activestreams = gfx::stream::none; - m_activetexturearrays.clear(); -} - -// opengl display list based variant of the geometry bank - -// create() subclass details -void -opengl_dlgeometrybank::create_( gfx::geometry_handle const &Geometry ) { - - m_chunkrecords.emplace_back( chunk_record() ); -} - -// replace() subclass details -void -opengl_dlgeometrybank::replace_( gfx::geometry_handle const &Geometry ) { - - delete_list( Geometry ); -} - -// draw() subclass details -std::size_t -opengl_dlgeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams ) { - - auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ]; - if( chunkrecord.streams != Streams ) { - delete_list( Geometry ); - chunkrecord.primitive_count = 0; - } - if( chunkrecord.list == 0 ) { - // we don't have a list ready, so compile one - chunkrecord.streams = Streams; - chunkrecord.list = ::glGenLists( 1 ); - auto const &chunk = gfx::geometry_bank::chunk( Geometry ); - ::glNewList( chunkrecord.list, GL_COMPILE ); - - ::glBegin( chunk.type ); - if( chunk.indices.size() > 0 ) { - // indexed geometry - for( auto const &index : chunk.indices ) { - auto const &vertex { chunk.vertices[ index ] }; - if( Streams & gfx::stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); } - else if( Streams & gfx::stream::color ) { ::glColor3fv( glm::value_ptr( vertex.normal ) ); } - if( Streams & gfx::stream::texture ) { for( auto unit : Units.texture ) { ::glMultiTexCoord2fv( unit, glm::value_ptr( vertex.texture ) ); } } - if( Streams & gfx::stream::position ) { ::glVertex3fv( glm::value_ptr( vertex.position ) ); } - } - } - else { - // raw geometry - for( auto const &vertex : chunk.vertices ) { - if( Streams & gfx::stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); } - else if( Streams & gfx::stream::color ) { ::glColor3fv( glm::value_ptr( vertex.normal ) ); } - if( Streams & gfx::stream::texture ) { for( auto unit : Units.texture ) { ::glMultiTexCoord2fv( unit, glm::value_ptr( vertex.texture ) ); } } - if( Streams & gfx::stream::position ) { ::glVertex3fv( glm::value_ptr( vertex.position ) ); } - } - } - ::glEnd(); - ::glEndList(); - - auto const vertexcount { ( chunk.indices.empty() ? chunk.vertices.size() : chunk.indices.size() ) }; - switch( chunk.type ) { - case GL_TRIANGLES: { chunkrecord.primitive_count += vertexcount / 3; break; } - case GL_TRIANGLE_STRIP: { chunkrecord.primitive_count += vertexcount - 2; break; } - default: { break; } - } - } - // with the list done we can just play it - ::glCallList( chunkrecord.list ); - return chunkrecord.primitive_count; -} - -// release () subclass details -void -opengl_dlgeometrybank::release_() { - - for( auto &chunkrecord : m_chunkrecords ) { - if( chunkrecord.list != 0 ) { - ::glDeleteLists( chunkrecord.list, 1 ); - chunkrecord.list = 0; - } - chunkrecord.streams = gfx::stream::none; - } -} - -void -opengl_dlgeometrybank::delete_list( gfx::geometry_handle const &Geometry ) { - // NOTE: given it's our own internal method we trust it to be called with valid parameters - auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ]; - if( chunkrecord.list != 0 ) { - ::glDeleteLists( chunkrecord.list, 1 ); - chunkrecord.list = 0; - } - chunkrecord.streams = gfx::stream::none; -} - -} // namespace gfx diff --git a/rendering/openglgeometrybank.h b/rendering/openglgeometrybank.h deleted file mode 100644 index 7d260a82..00000000 --- a/rendering/openglgeometrybank.h +++ /dev/null @@ -1,124 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#pragma once - -#include "rendering/geometrybank.h" - -namespace gfx { - -// opengl vbo-based variant of the geometry bank - -class opengl_vbogeometrybank : public geometry_bank { - -public: -// constructors: - opengl_vbogeometrybank() = default; -// destructor - ~opengl_vbogeometrybank() { - delete_buffer(); } -// methods: - static - void - reset() { - m_activevertexbuffer = 0; - m_activestreams = gfx::stream::none; } - -private: -// types: - struct chunk_record { - std::size_t vertex_offset{ 0 }; // beginning of the chunk vertex data as offset from the beginning of the last established buffer - std::size_t vertex_count{ 0 }; // size of the chunk in the last established buffer - std::size_t index_offset{ 0 }; - std::size_t index_count{ 0 }; - bool is_good{ false }; // true if local content of the chunk matches the data on the opengl end - }; - - using chunkrecord_sequence = std::vector; - -// methods: - // create() subclass details - void - create_( gfx::geometry_handle const &Geometry ) override; - // replace() subclass details - void - replace_( gfx::geometry_handle const &Geometry ) override; - // draw() subclass details - auto - draw_( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams ) -> std::size_t override; - // release() subclass details - void - release_() override; - void - setup_buffer(); - void - bind_buffer() const; - void - delete_buffer(); - static - void - bind_streams(gfx::stream_units const &Units, unsigned int const Streams , size_t offset = 0); - static - void - release_streams(); - -// members: - static GLuint m_activevertexbuffer; // buffer bound currently on the opengl end, if any - static unsigned int m_activestreams; - static std::vector m_activetexturearrays; - GLuint m_vertexbuffer { 0 }; // id of the buffer holding vertex data on the opengl end - GLuint m_indexbuffer { 0 }; // id of the buffer holding index data on the opengl end - chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order - -}; - -// opengl display list based variant of the geometry bank - -class opengl_dlgeometrybank : public geometry_bank { - -public: -// constructors: - opengl_dlgeometrybank() = default; -// destructor: - ~opengl_dlgeometrybank() { - for( auto const &chunkrecord : m_chunkrecords ) { - ::glDeleteLists( chunkrecord.list, 1 ); } } - -private: -// types: - struct chunk_record { - GLuint list { 0 }; // display list associated with the chunk - unsigned int streams { 0 }; // stream combination used to generate the display list - std::size_t primitive_count { 0 }; - }; - - using chunkrecord_sequence = std::vector; - -// methods: - // create() subclass details - void - create_( gfx::geometry_handle const &Geometry ) override; - // replace() subclass details - void - replace_( gfx::geometry_handle const &Geometry ) override; - // draw() subclass details - auto - draw_( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams ) -> std::size_t override; - // release () subclass details - void - release_() override; - void - delete_list( gfx::geometry_handle const &Geometry ); - -// members: - chunkrecord_sequence m_chunkrecords; // helper data for all stored geometry chunks, in matching order - -}; - -} // namespace gfx diff --git a/rendering/opengllight.cpp b/rendering/opengllight.cpp deleted file mode 100644 index 79ce967c..00000000 --- a/rendering/opengllight.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#include "stdafx.h" - -#include "rendering/opengllight.h" - -void -opengl_light::apply_intensity( float const Factor ) { - - if( Factor == 1.f ) { - - ::glLightfv( id, GL_AMBIENT, glm::value_ptr( ambient ) ); - ::glLightfv( id, GL_DIFFUSE, glm::value_ptr( diffuse ) ); - ::glLightfv( id, GL_SPECULAR, glm::value_ptr( specular ) ); - } - else { - auto const factor{ std::clamp( Factor, 0.05f, 1.f ) }; - // temporary light scaling mechanics (ultimately this work will be left to the shaders - glm::vec4 scaledambient( ambient.r * factor, ambient.g * factor, ambient.b * factor, ambient.a ); - glm::vec4 scaleddiffuse( diffuse.r * factor, diffuse.g * factor, diffuse.b * factor, diffuse.a ); - glm::vec4 scaledspecular( specular.r * factor, specular.g * factor, specular.b * factor, specular.a ); - glLightfv( id, GL_AMBIENT, glm::value_ptr( scaledambient ) ); - glLightfv( id, GL_DIFFUSE, glm::value_ptr( scaleddiffuse ) ); - glLightfv( id, GL_SPECULAR, glm::value_ptr( scaledspecular ) ); - } -} - -void -opengl_light::apply_angle() { - - ::glLightfv( id, GL_POSITION, glm::value_ptr( glm::vec4{ position, ( is_directional ? 0.f : 1.f ) } ) ); - if( false == is_directional ) { - ::glLightfv( id, GL_SPOT_DIRECTION, glm::value_ptr( direction ) ); - } -} - -//--------------------------------------------------------------------------- diff --git a/rendering/opengllight.h b/rendering/opengllight.h deleted file mode 100644 index 1fe71b9d..00000000 --- a/rendering/opengllight.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#pragma once - -#include "rendering/light.h" - -struct opengl_light : public basic_light { - - GLuint id { (GLuint)-1 }; - - void - apply_intensity( float const Factor = 1.0f ); - void - apply_angle(); - - opengl_light & - operator=( basic_light const &Right ) { - basic_light::operator=( Right ); - return *this; } -}; - -//--------------------------------------------------------------------------- diff --git a/rendering/openglparticles.cpp b/rendering/openglparticles.cpp deleted file mode 100644 index 3b8de946..00000000 --- a/rendering/openglparticles.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#include "stdafx.h" -#include "rendering/openglparticles.h" - -#include "rendering/particles.h" -#include "rendering/openglcamera.h" -#include "simulation/simulation.h" -#include "utilities/Logs.h" - -std::vector> const billboard_vertices { - - { { -0.5f, -0.5f, 0.f }, { 0.f, 0.f } }, - { { 0.5f, -0.5f, 0.f }, { 1.f, 0.f } }, - { { 0.5f, 0.5f, 0.f }, { 1.f, 1.f } }, - { { -0.5f, 0.5f, 0.f }, { 0.f, 1.f } } -}; - -void -opengl_particles::update( opengl_camera const &Camera ) { - - m_particlevertices.clear(); - - if( false == Global.Smoke ) { return; } - - // build a list of visible smoke sources - // NOTE: arranged by distance to camera, if we ever need sorting and/or total amount cap-based culling - std::multimap sources; - - for( auto const &source : simulation::Particles.sequence() ) { - if( false == Camera.visible( source.area() ) ) { continue; } - // NOTE: the distance is negative when the camera is inside the source's bounding area - sources.emplace( - static_cast( glm::length( Camera.position() - source.area().center ) - source.area().radius ), - source ); - } - - if( true == sources.empty() ) { return; } - - // build billboard data for particles from visible sources - auto const camerarotation { glm::mat3( Camera.modelview() ) }; - particle_vertex vertex; - for( auto const &[sourcekey, sourcedata] : sources ) { - - auto const particlecolor { - glm::clamp( - sourcedata.color() - * ( glm::vec3 { Global.DayLight.ambient } + 0.35f * glm::vec3{ Global.DayLight.diffuse } ) - * 255.f, - glm::vec3{ 0.f }, glm::vec3{ 255.f } ) }; - auto const &particles { sourcedata.sequence() }; - // TODO: put sanity cap on the overall amount of particles that can be drawn - auto const sizestep { 256.0 * billboard_vertices.size() }; - m_particlevertices.reserve( - static_cast( sizestep * std::ceil( static_cast(m_particlevertices.size()) + ( static_cast(particles.size() * billboard_vertices.size()) ) / sizestep ) ) ); - for( auto const &particle : particles ) { - // TODO: particle color support - vertex.color[ 0 ] = static_cast( particlecolor.r ); - vertex.color[ 1 ] = static_cast( particlecolor.g ); - vertex.color[ 2 ] = static_cast( particlecolor.b ); - vertex.color[ 3 ] = static_cast( std::clamp( particle.opacity * 255, 0.f, 255.f ) ); - - auto const offset { glm::vec3{ particle.position - Camera.position() } }; - auto const rotation { glm::angleAxis( particle.rotation, glm::vec3{ 0.f, 0.f, 1.f } ) }; - - for( auto const &[billboardoffset, billboardtexture] : billboard_vertices ) { - vertex.position = offset + ( rotation * billboardoffset * particle.size ) * camerarotation; - vertex.texture = billboardtexture; - - m_particlevertices.emplace_back( vertex ); - } - } - } - - // ship the billboard data to the gpu: - // setup... - ::glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT ); - // ...make sure we have enough room... - if( m_buffercapacity < m_particlevertices.size() ) { - // allocate gpu side buffer big enough to hold the data - m_buffercapacity = 0; - if( m_buffer != (GLuint)-1 ) { - // get rid of the old buffer - ::glDeleteBuffers( 1, &m_buffer ); - m_buffer = (GLuint)-1; - } - ::glGenBuffers( 1, &m_buffer ); - if( ( m_buffer > 0 ) && ( m_buffer != (GLuint)-1 ) ) { - // if we didn't get a buffer we'll try again during the next draw call - // NOTE: we match capacity instead of current size to reduce number of re-allocations - auto const particlecount { m_particlevertices.capacity() }; - ::glBindBuffer( GL_ARRAY_BUFFER, m_buffer ); - ::glBufferData( - GL_ARRAY_BUFFER, - particlecount * sizeof( particle_vertex ), - nullptr, - GL_DYNAMIC_DRAW ); - if( ::glGetError() == GL_OUT_OF_MEMORY ) { - // TBD: throw a bad_alloc? - ErrorLog( "openGL error: out of memory; failed to create a geometry buffer" ); - ::glDeleteBuffers( 1, &m_buffer ); - m_buffer = (GLuint)-1; - } - else { - m_buffercapacity = particlecount; - } - } - } - // ...send the data... - if( ( m_buffer > 0 ) && ( m_buffer != (GLuint)-1 ) ) { - // if the buffer exists at this point it's guaranteed to be big enough to hold our data - ::glBindBuffer( GL_ARRAY_BUFFER, m_buffer ); - ::glBufferSubData( - GL_ARRAY_BUFFER, - 0, - m_particlevertices.size() * sizeof( particle_vertex ), - m_particlevertices.data() ); - } - // ...and cleanup - ::glPopClientAttrib(); - ::glBindBuffer( GL_ARRAY_BUFFER, 0 ); -} - -std::size_t -opengl_particles::render( GLint const Textureunit ) { - - if( false == Global.Smoke ) { return 0; } - if( m_buffercapacity == 0 ) { return 0; } - if( m_particlevertices.empty() ) { return 0; } - if( ( m_buffer == 0 ) || ( m_buffer == (GLuint)-1 ) ) { return 0; } - - // setup... - ::glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT ); - ::glBindBuffer( GL_ARRAY_BUFFER, m_buffer ); - ::glVertexPointer( 3, GL_FLOAT, sizeof( particle_vertex ), static_cast( nullptr ) ); - ::glEnableClientState( GL_VERTEX_ARRAY ); - ::glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( particle_vertex ), reinterpret_cast( sizeof( float ) * 3 ) ); - ::glEnableClientState( GL_COLOR_ARRAY ); - ::glClientActiveTexture( GL_TEXTURE0 + Textureunit ); - ::glTexCoordPointer( 2, GL_FLOAT, sizeof( particle_vertex ), reinterpret_cast( sizeof( float ) * 3 + sizeof( std::uint8_t ) * 4 ) ); - ::glEnableClientState( GL_TEXTURE_COORD_ARRAY ); - // ...draw... - ::glDrawArrays( GL_QUADS, 0, static_cast(m_particlevertices.size()) ); - // ...and cleanup - ::glPopClientAttrib(); - ::glBindBuffer( GL_ARRAY_BUFFER, 0 ); - - return m_particlevertices.size() / 4; -} - -//--------------------------------------------------------------------------- diff --git a/rendering/openglparticles.h b/rendering/openglparticles.h deleted file mode 100644 index 267fffc4..00000000 --- a/rendering/openglparticles.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#pragma once - -class opengl_camera; - -// particle data visualizer -class opengl_particles { -public: -// constructors - opengl_particles() = default; -// destructor - ~opengl_particles() { - if( m_buffer != -1 ) { - ::glDeleteBuffers( 1, &m_buffer ); } } -// methods - void - update( opengl_camera const &Camera ); - std::size_t - render( GLint const Textureunit ); -private: -// types - struct particle_vertex { - glm::vec3 position; // 3d space - std::uint8_t color[ 4 ]; // rgba, unsigned byte format - glm::vec2 texture; // uv space - float padding[ 2 ]; // experimental, some gfx hardware allegedly works better with 32-bit aligned data blocks - }; -/* - using sourcedistance_pair = std::pair; - using source_sequence = std::vector; -*/ - using particlevertex_sequence = std::vector; -// methods -// members -/* - source_sequence m_sources; // list of particle sources visible in current render pass, with their respective distances to the camera -*/ - particlevertex_sequence m_particlevertices; // geometry data of visible particles, generated on the cpu end - GLuint m_buffer{ (GLuint)-1 }; // id of the buffer holding geometry data on the opengl end - std::size_t m_buffercapacity{ 0 }; // total capacity of the last established buffer -}; - -//--------------------------------------------------------------------------- diff --git a/rendering/openglprecipitation.cpp b/rendering/openglprecipitation.cpp deleted file mode 100644 index acf24687..00000000 --- a/rendering/openglprecipitation.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#include "stdafx.h" -#include "rendering/openglprecipitation.h" - -#include "utilities/Globals.h" -#include "rendering/renderer.h" -#include "simulation/simulationenvironment.h" - -opengl_precipitation::~opengl_precipitation() { - - if( m_vertexbuffer != -1 ) { ::glDeleteBuffers( 1, &m_vertexbuffer ); } - if( m_indexbuffer != -1 ) { ::glDeleteBuffers( 1, &m_indexbuffer ); } - if( m_uvbuffer != -1 ) { ::glDeleteBuffers( 1, &m_uvbuffer ); } -} - -void -opengl_precipitation::create( int const Tesselation ) { - - m_vertices.clear(); - m_uvs.clear(); - m_indices.clear(); - - auto const heightfactor { 10.f }; // height-to-radius factor - auto const verticaltexturestretchfactor { 1.5f }; // crude motion blur - - // create geometry chunk - auto const latitudes { 3 }; // just a cylinder with end cones - auto const longitudes { Tesselation }; - auto const longitudehalfstep { 0.5f * static_cast( 2.0 * M_PI * 1.f / longitudes ) }; // for crude uv correction - - std::uint16_t index = 0; - -// auto const radius { 25.f }; // cylinder radius - std::vector radii { 25.f, 10.f, 5.f, 1.f }; - for( auto radius : radii ) { - - for( int i = 0; i <= latitudes; ++i ) { - - auto const latitude{ static_cast( M_PI * ( -0.5f + (float)( i ) / latitudes ) ) }; - auto const z{ std::sin( latitude ) }; - auto const zr{ std::cos( latitude ) }; - - for( int j = 0; j <= longitudes; ++j ) { - // NOTE: for the first and last row half of the points we create end up unused but, eh - auto const longitude{ static_cast( 2.0 * M_PI * (float)( j ) / longitudes ) }; - auto const x{ std::cos( longitude ) }; - auto const y{ std::sin( longitude ) }; - // NOTE: cartesian to opengl swap would be: -x, -z, -y - m_vertices.emplace_back( glm::vec3( -x * zr, -z * heightfactor, -y * zr ) * radius ); - // uvs - // NOTE: first and last row receives modified u values to deal with limitation of mapping onto triangles - auto u = ( - i == 0 ? longitude + longitudehalfstep : - i == latitudes ? longitude - longitudehalfstep : - longitude ); - m_uvs.emplace_back( - u / ( 2.0 * M_PI ) * radius, - 1.f - (float)( i ) / latitudes * radius * heightfactor * 0.5f / verticaltexturestretchfactor ); - - if( ( i == 0 ) || ( j == 0 ) ) { - // initial edge of the dome, don't start indices yet - ++index; - } - else { - // the end cones are built from one triangle of each quad, the middle rows use both - if( i < latitudes ) { - m_indices.emplace_back( index - 1 - ( longitudes + 1 ) ); - m_indices.emplace_back( index - 1 ); - m_indices.emplace_back( index ); - } - if( i > 1 ) { - m_indices.emplace_back( index ); - m_indices.emplace_back( index - ( longitudes + 1 ) ); - m_indices.emplace_back( index - 1 - ( longitudes + 1 ) ); - } - ++index; - } - } // longitude - } // latitude - } // radius -} - -void -opengl_precipitation::update() { - - // NOTE: we should really be checking state of each buffer as theoretically allocation could go wrong mid-way, but, eh - if( m_vertexbuffer == (GLuint)-1 ) { - - if( m_vertices.empty() ) { - // create visualization mesh - create( 18 ); - } - // cache entry state - ::glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT ); - - // build the buffers - ::glGenBuffers( 1, &m_vertexbuffer ); - ::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer ); - ::glBufferData( GL_ARRAY_BUFFER, m_vertices.size() * sizeof( glm::vec3 ), m_vertices.data(), GL_STATIC_DRAW ); - - ::glGenBuffers( 1, &m_uvbuffer ); - ::glBindBuffer( GL_ARRAY_BUFFER, m_uvbuffer ); - ::glBufferData( GL_ARRAY_BUFFER, m_uvs.size() * sizeof( glm::vec2 ), m_uvs.data(), GL_STATIC_DRAW ); - - ::glGenBuffers( 1, &m_indexbuffer ); - ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer ); - ::glBufferData( GL_ELEMENT_ARRAY_BUFFER, m_indices.size() * sizeof( unsigned short ), m_indices.data(), GL_STATIC_DRAW ); - // NOTE: vertex and index source data is superfluous past this point, but, eh - - // cleanup - ::glPopClientAttrib(); - ::glBindBuffer( GL_ARRAY_BUFFER, 0 ); - ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); - } - - // TODO: include weather type check in the entry conditions - if( m_overcast == Global.Overcast ) { return; } - - m_overcast = Global.Overcast; - - std::string const densitysuffix { ( - m_overcast < 1.35 ? - "_light" : - "_medium" ) }; - if( Global.Weather == "rain:" ) { - m_texture = GfxRenderer->Fetch_Texture( "fx/rain" + densitysuffix ); - } - else if( Global.Weather == "snow:" ) { - m_texture = GfxRenderer->Fetch_Texture( "fx/snow" + densitysuffix ); - } -} - -void -opengl_precipitation::render( GLint const Textureunit ) { - - if( m_texture == null_handle ) { return; } - - GfxRenderer->Bind_Texture( m_texture ); - - // cache entry state - ::glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT ); - - // positions - ::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer ); - ::glVertexPointer( 3, GL_FLOAT, sizeof( glm::vec3 ), reinterpret_cast( 0 ) ); - ::glEnableClientState( GL_VERTEX_ARRAY ); - // uvs - ::glBindBuffer( GL_ARRAY_BUFFER, m_uvbuffer ); - ::glClientActiveTexture( GL_TEXTURE0 + Textureunit ); - ::glTexCoordPointer( 2, GL_FLOAT, sizeof( glm::vec2 ), reinterpret_cast( 0 ) ); - ::glEnableClientState( GL_TEXTURE_COORD_ARRAY ); - // uv transformation matrix - ::glMatrixMode( GL_TEXTURE ); - ::glLoadIdentity(); - ::glTranslatef( 0.f, simulation::Environment.precipitation().get_textureoffset(), 0.f ); - // indices - ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer ); - ::glDrawElements( GL_TRIANGLES, static_cast( m_indices.size() ), GL_UNSIGNED_SHORT, reinterpret_cast( 0 ) ); - // cleanup - ::glLoadIdentity(); - ::glMatrixMode( GL_MODELVIEW ); - ::glPopClientAttrib(); - ::glBindBuffer( GL_ARRAY_BUFFER, 0 ); - ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); -} diff --git a/rendering/openglprecipitation.h b/rendering/openglprecipitation.h deleted file mode 100644 index a0bb2dcd..00000000 --- a/rendering/openglprecipitation.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#pragma once - -#include "model/Texture.h" - -class opengl_precipitation { - -public: -// constructors - opengl_precipitation() = default; -// destructor - ~opengl_precipitation(); -// methods - void - update(); - void - render( GLint const Textureunit ); - -private: -// methods - void create( int const Tesselation ); -// members - std::vector m_vertices; - std::vector m_uvs; - std::vector m_indices; - GLuint m_vertexbuffer { (GLuint)-1 }; - GLuint m_uvbuffer { (GLuint)-1 }; - GLuint m_indexbuffer { (GLuint)-1 }; - texture_handle m_texture { null_handle }; - float m_overcast { -1.f }; // cached overcast level, difference from current state triggers texture update -}; diff --git a/rendering/openglrenderer.cpp b/rendering/openglrenderer.cpp deleted file mode 100644 index 59152c64..00000000 --- a/rendering/openglrenderer.cpp +++ /dev/null @@ -1,4629 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#include "stdafx.h" -#include "rendering/openglrenderer.h" - -#include "utilities/color.h" -#include "utilities/Globals.h" -#include "vehicle/Camera.h" -#include "utilities/Timer.h" -#include "simulation/simulation.h" -#include "simulation/simulationtime.h" -#include "vehicle/Train.h" -#include "vehicle/DynObj.h" -#include "model/AnimModel.h" -#include "world/Traction.h" -#include "application/application.h" -#include "utilities/Logs.h" -#include "rendering/openglgeometrybank.h" -#include "rendering/openglcolor.h" -#include "rendering/screenshot.h" -#include - -int constexpr EU07_PICKBUFFERSIZE { 1024 }; // size of (square) textures bound with the pick framebuffer -int constexpr EU07_ENVIRONMENTBUFFERSIZE { 256 }; // size of (square) environmental cube map texture -int constexpr EU07_REFLECTIONFIDELITYOFFSET { 250 }; // artificial increase of range for reflection pass detail reduction - -namespace { - -float -editor_track_view_range(float const Range, bool const Apply) -{ - if( false == Apply || false == Global.editor_tracks ) - return Range; - float extra = 50000.0f; - if( Global.EditorOrtho ) - extra = Global.EditorOrthoExtent * 3.0f; - return Range > extra ? Range : extra; -} - -bool -editor_far_tracks_only(bool const Apply, scene::basic_cell const *Cell, glm::dvec3 const &Camera, double const SceneryLimit) -{ - if( false == Apply ) - return false; - double nearest = glm::length( Cell->m_area.center - Camera ) - static_cast( Cell->m_area.radius ); - if( nearest < 0.0 ) - nearest = 0.0; - return nearest > SceneryLimit; -} - -// returns material assigned to the submodel, resolving replacable skins. null_handle if no skin set is bound -material_handle -submodel_material( TSubModel const *Submodel ) { - - if( Submodel->m_material >= 0 ) { - return Submodel->m_material; - } - return ( - TSubModel::ReplacableSkinId != nullptr ? - TSubModel::ReplacableSkinId[ -Submodel->m_material ] : - null_handle ); -} - -} // namespace - -float const EU07_OPACITYDEFAULT { 0.5f }; - -bool -opengl_renderer::Init( GLFWwindow *Window ) { - - if( false == Init_caps() ) { return false; } - - m_window = Window; - - ::glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); - ::glPixelStorei( GL_PACK_ALIGNMENT, 1 ); - - glClearDepth( 1.0f ); - glClearColor( 51.0f / 255.0f, 102.0f / 255.0f, 85.0f / 255.0f, 1.0f ); // initial background Color - - glPolygonMode( GL_FRONT, GL_FILL ); - glFrontFace( GL_CCW ); // Counter clock-wise polygons face out - glEnable( GL_CULL_FACE ); // Cull back-facing triangles - glShadeModel( GL_SMOOTH ); // Enable Smooth Shading - - m_geometry.units().texture = ( - Global.BasicRenderer ? - std::vector{ m_diffusetextureunit } : - std::vector{ m_normaltextureunit, m_diffusetextureunit } ); - ui_layer::set_unit( m_diffusetextureunit ); - select_unit( m_diffusetextureunit ); - - ::glDepthFunc( GL_LEQUAL ); - glEnable( GL_DEPTH_TEST ); - glAlphaFunc( GL_GREATER, 0.f ); - glEnable( GL_ALPHA_TEST ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - glEnable( GL_BLEND ); - glEnable( GL_TEXTURE_2D ); // Enable Texture Mapping - - glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); // Really Nice Perspective Calculations - glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST ); - glHint( GL_LINE_SMOOTH_HINT, GL_NICEST ); - glLineWidth( 1.0f ); - glPointSize( 3.0f ); - glEnable( GL_POINT_SMOOTH ); - - ::glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR ); - ::glMaterialf( GL_FRONT, GL_SHININESS, 15.0f ); - if( true == Global.ScaleSpecularValues ) { - m_specularopaquescalefactor = 0.25f; - m_speculartranslucentscalefactor = 1.5f; - } - ::glEnable( GL_COLOR_MATERIAL ); - ::glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE ); - - // setup lighting - ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr(m_baseambient) ); - ::glEnable( GL_LIGHTING ); - ::glEnable( opengl_renderer::sunlight ); - - // rgb value for 5780 kelvin - Global.DayLight.diffuse[0] = 1.0f; // 255 / 255 - Global.DayLight.diffuse[1] = 0.9490196f; // 242 / 255 - Global.DayLight.diffuse[2] = 0.9058824f; // 231 / 255 - Global.DayLight.is_directional = true; - m_sunlight.id = opengl_renderer::sunlight; - // ::glLightf( opengl_renderer::sunlight, GL_SPOT_CUTOFF, 90.0f ); - - // create dynamic light pool - for( int idx = 0; idx < Global.DynamicLightCount; ++idx ) { - - opengl_light light; - light.id = GL_LIGHT1 + idx; - - light.is_directional = false; - ::glEnable( light.id ); // experimental intel chipset fix - ::glLightf( light.id, GL_SPOT_CUTOFF, 7.5f ); - ::glLightf( light.id, GL_SPOT_EXPONENT, 7.5f ); - ::glLightf( light.id, GL_CONSTANT_ATTENUATION, 0.0f ); - ::glLightf( light.id, GL_LINEAR_ATTENUATION, 0.035f ); - ::glDisable( light.id ); // experimental intel chipset fix - - m_lights.emplace_back( light ); - } - // preload some common textures - m_glaretexture = Fetch_Texture( "fx/lightglare" ); - m_suntexture = Fetch_Texture( "fx/sun" ); - m_moontexture = Fetch_Texture( "fx/moon" ); - if( m_helpertextureunit >= 0 ) { - m_reflectiontexture = Fetch_Texture( "fx/reflections" ); - } - m_smoketexture = Fetch_Texture( "fx/smoke" ); - m_invalid_material = Fetch_Material( "fx/invalid" ); - -#ifdef EU07_USE_PICKING_FRAMEBUFFER - // pick buffer resources - if( true == m_framebuffersupport ) { - // try to create the pick buffer. RGBA8 2D texture, 24 bit depth texture, 1024x1024 (height of 512 would suffice but, eh) - // texture - ::glGenTextures( 1, &m_picktexture ); - ::glBindTexture( GL_TEXTURE_2D, m_picktexture ); - ::glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); - // depth buffer - ::glGenRenderbuffersEXT( 1, &m_pickdepthbuffer ); - ::glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, m_pickdepthbuffer ); - ::glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE ); - // create and assemble the framebuffer - ::glGenFramebuffersEXT( 1, &m_pickframebuffer ); - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_pickframebuffer ); - ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_picktexture, 0 ); - ::glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_pickdepthbuffer ); - // check if we got it working - GLenum status = ::glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT ); - if( status == GL_FRAMEBUFFER_COMPLETE_EXT ) { - WriteLog( "Picking framebuffer setup complete" ); - } - else{ - ErrorLog( "Picking framebuffer setup failed" ); - m_framebuffersupport = false; - } - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target for now - } -#endif - // shadowmap resources - if( ( true == Global.RenderShadows ) - && ( true == m_framebuffersupport ) ) { - // primary shadow map - { - // texture: - ::glGenTextures( 1, &m_shadowtexture ); - ::glBindTexture( GL_TEXTURE_2D, m_shadowtexture ); - // allocate memory - ::glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, m_shadowbuffersize, m_shadowbuffersize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL ); - // setup parameters - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - // enable shadow comparison: true (ie not in shadow) if r<=texture... - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL ); - ::glTexParameteri( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE ); - ::glBindTexture( GL_TEXTURE_2D, 0 ); -#ifdef EU07_USE_DEBUG_SHADOWMAP - ::glGenTextures( 1, &m_shadowdebugtexture ); - ::glBindTexture( GL_TEXTURE_2D, m_shadowdebugtexture ); - // allocate memory - ::glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, m_shadowbuffersize, m_shadowbuffersize, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL ); - // setup parameters - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - ::glBindTexture( GL_TEXTURE_2D, 0 ); -#endif - // create and assemble the framebuffer - ::glGenFramebuffersEXT( 1, &m_shadowframebuffer ); - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_shadowframebuffer ); -#ifdef EU07_USE_DEBUG_SHADOWMAP - ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_shadowdebugtexture, 0 ); -#else - ::glDrawBuffer( GL_NONE ); // we won't be rendering colour data, so can skip the colour attachment - ::glReadBuffer( GL_NONE ); -#endif - ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_shadowtexture, 0 ); - // check if we got it working - GLenum const status{ ::glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT ) }; - if( status == GL_FRAMEBUFFER_COMPLETE_EXT ) { - WriteLog( "Shadows framebuffer setup complete" ); - } - else { - ErrorLog( "Shadows framebuffer setup failed" ); - Global.RenderShadows = false; - } - // switch back to primary render target for now - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); - } - // cab shadow map - { - // NOTE: using separate framebuffer as more efficient arrangement than swapping attachments on a single one - // TODO: for shader-based implementation use instead a quarter of the CSM - // texture: - ::glGenTextures( 1, &m_cabshadowtexture ); - ::glBindTexture( GL_TEXTURE_2D, m_cabshadowtexture ); - // allocate memory - ::glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, m_shadowbuffersize / 2, m_shadowbuffersize / 2, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL ); - // setup parameters - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - // enable shadow comparison: true (ie not in shadow) if r<=texture... - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL ); - ::glTexParameteri( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE ); - ::glBindTexture( GL_TEXTURE_2D, 0 ); -#ifdef EU07_USE_DEBUG_CABSHADOWMAP - ::glGenTextures( 1, &m_cabshadowdebugtexture ); - ::glBindTexture( GL_TEXTURE_2D, m_cabshadowdebugtexture ); - // allocate memory - ::glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, m_shadowbuffersize / 2, m_shadowbuffersize / 2, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL ); - // setup parameters - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - ::glBindTexture( GL_TEXTURE_2D, 0 ); -#endif - // create and assemble the framebuffer - ::glGenFramebuffersEXT( 1, &m_cabshadowframebuffer ); - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_cabshadowframebuffer ); -#ifdef EU07_USE_DEBUG_CABSHADOWMAP - ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_cabshadowdebugtexture, 0 ); -#else - ::glDrawBuffer( GL_NONE ); // we won't be rendering colour data, so can skip the colour attachment - ::glReadBuffer( GL_NONE ); -#endif - ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_cabshadowtexture, 0 ); - // check if we got it working - GLenum const status{ ::glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT ) }; - if( status == GL_FRAMEBUFFER_COMPLETE_EXT ) { - WriteLog( "Cab shadows framebuffer setup complete" ); - } - else { - ErrorLog( "Cab shadows framebuffer setup failed" ); - Global.RenderShadows = false; - } - } - // switch back to primary render target for now - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); - } - // environment cube map resources - if( ( false == Global.BasicRenderer ) - && ( true == m_framebuffersupport ) ) { - // texture: - ::glGenTextures( 1, &m_environmentcubetexture ); - ::glBindTexture( GL_TEXTURE_CUBE_MAP, m_environmentcubetexture ); - // allocate space - for( GLuint faceindex = GL_TEXTURE_CUBE_MAP_POSITIVE_X; faceindex < GL_TEXTURE_CUBE_MAP_POSITIVE_X + 6; ++faceindex ) { - ::glTexImage2D( faceindex, 0, GL_RGBA8, EU07_ENVIRONMENTBUFFERSIZE, EU07_ENVIRONMENTBUFFERSIZE, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL ); - } - // setup parameters - ::glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - ::glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - ::glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - ::glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - ::glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE ); - // depth buffer - ::glGenRenderbuffersEXT( 1, &m_environmentdepthbuffer ); - ::glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, m_environmentdepthbuffer ); - ::glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, EU07_ENVIRONMENTBUFFERSIZE, EU07_ENVIRONMENTBUFFERSIZE ); - // create and assemble the framebuffer - ::glGenFramebuffersEXT( 1, &m_environmentframebuffer ); - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_environmentframebuffer ); - ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X, m_environmentcubetexture, 0 ); - ::glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_environmentdepthbuffer ); - // check if we got it working - GLenum status = ::glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT ); - if( status == GL_FRAMEBUFFER_COMPLETE_EXT ) { - WriteLog( "Reflections framebuffer setup complete" ); - m_environmentcubetexturesupport = true; - } - else { - ErrorLog( "Reflections framebuffer setup failed" ); - m_environmentcubetexturesupport = false; - } - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target for now - } - // prepare basic geometry chunks - auto const geometrybank = Create_Bank(); - float const size = 2.5f; - gfx::vertex_array billboard_array{ - { { -size, size, 0.f }, glm::vec3(), { 1.f, 1.f } }, - { { size, size, 0.f }, glm::vec3(), { 0.f, 1.f } }, - { { -size, -size, 0.f }, glm::vec3(), { 1.f, 0.f } }, - { { size, -size, 0.f }, glm::vec3(), { 0.f, 0.f } } }; - gfx::userdata_array userdata{}; - m_billboardgeometry = m_geometry.create_chunk(billboard_array, userdata, geometrybank, GL_TRIANGLE_STRIP); - // prepare debug mode objects - //m_quadric = ::gluNewQuadric(); - //::gluQuadricNormals( m_quadric, GLU_FLAT ); - - WriteLog( "Gfx Renderer: setup complete" ); - - return true; -} - -bool -opengl_renderer::Render() { - - Timer::subsystem.gfx_total.stop(); - Timer::subsystem.gfx_total.start(); // note: gfx_total is actually frame total, clean this up - Timer::subsystem.gfx_color.start(); - // fetch simulation data - if( simulation::is_ready ) { - m_sunlight = Global.DayLight; - // quantize sun angle to reduce shadow crawl - auto const quantizationstep { 0.004f }; - m_sunlight.direction = glm::normalize( quantizationstep * glm::roundEven( m_sunlight.direction * ( 1.f / quantizationstep ) ) ); - } - // generate new frame - opengl_texture::reset_unit_cache(); - m_renderpass.draw_mode = rendermode::none; // force setup anew - m_renderpass.draw_stats = debug_stats(); - m_geometry.primitives_count() = 0; - Render_pass( rendermode::color ); - Timer::subsystem.gfx_color.stop(); - // add user interface - setup_units( true, false, false ); - ::glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT ); - ::glClientActiveTexture( GL_TEXTURE0 + m_diffusetextureunit ); - - // clear state for ui - ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); - ::glBindBuffer( GL_ARRAY_BUFFER, 0 ); - Application.render_ui(); - ::glPopClientAttrib(); - if( Global.bUseVBO ) { - // swapbuffers() will unbind current buffers so we prepare for it on our end - gfx::opengl_vbogeometrybank::reset(); - } - ++m_framestamp; - - return true; // for now always succeed -} - -void -opengl_renderer::SwapBuffers() { - - Timer::subsystem.gfx_swap.start(); - glfwSwapBuffers( m_window ); - Timer::subsystem.gfx_swap.stop(); - - m_debugtimestext.clear(); - m_debugtimestext = - "cpu frame total: " + to_string( Timer::subsystem.gfx_color.average() + Timer::subsystem.gfx_shadows.average() + Timer::subsystem.gfx_swap.average(), 2 ) + " ms\n" - + " color: " + to_string( Timer::subsystem.gfx_color.average(), 2 ) + " ms (" + std::to_string( m_cellqueue.size() ) + " sectors)\n"; - if( Global.RenderShadows ) { - m_debugtimestext += - " shadows: " + to_string( Timer::subsystem.gfx_shadows.average(), 2 ) + " ms\n"; - } - m_debugtimestext += " swap: " + to_string( Timer::subsystem.gfx_swap.average(), 2 ) + " ms\n"; - m_debugtimestext += "uilayer: " + to_string( Timer::subsystem.gfx_gui.average(), 2 ) + " ms\n"; - - m_debugstatstext = - "triangles: " + to_string( static_cast(m_geometry.primitives_count()), 7 ) + "\n" - + "vehicles: " + to_string( m_colorpass.draw_stats.dynamics, 7 ) + " +" + to_string( m_shadowpass.draw_stats.dynamics, 7 ) - + " =" + to_string( m_colorpass.draw_stats.dynamics + m_shadowpass.draw_stats.dynamics, 7 ) + "\n" - + "models: " + to_string( m_colorpass.draw_stats.models, 7 ) + " +" + to_string( m_shadowpass.draw_stats.models, 7 ) - + " =" + to_string( m_colorpass.draw_stats.models + m_shadowpass.draw_stats.models, 7 ) + "\n" - + "drawcalls: " + to_string( m_colorpass.draw_stats.drawcalls, 7 ) + " +" + to_string( m_shadowpass.draw_stats.drawcalls, 7 ) - + " =" + to_string( m_colorpass.draw_stats.drawcalls + m_shadowpass.draw_stats.drawcalls, 7 ) + "\n" - + " submodels:" + to_string( m_colorpass.draw_stats.submodels, 7 ) + " +" + to_string( m_shadowpass.draw_stats.submodels, 7 ) - + " =" + to_string( m_colorpass.draw_stats.submodels + m_shadowpass.draw_stats.submodels, 7 ) + "\n" - + " paths: " + to_string( m_colorpass.draw_stats.paths, 7 ) + " +" + to_string( m_shadowpass.draw_stats.paths, 7 ) - + " =" + to_string( m_colorpass.draw_stats.paths + m_shadowpass.draw_stats.paths, 7 ) + "\n" - + " shapes: " + to_string( m_colorpass.draw_stats.shapes, 7 ) + " +" + to_string( m_shadowpass.draw_stats.shapes, 7 ) - + " =" + to_string( m_colorpass.draw_stats.shapes + m_shadowpass.draw_stats.shapes, 7 ) + "\n" - + " traction: " + to_string( m_colorpass.draw_stats.traction, 7 ) + "\n" - + " lines: " + to_string( m_colorpass.draw_stats.lines, 7 ) + "\n" - + "particles: " + to_string( m_colorpass.draw_stats.particles, 7 ); -} - -// runs jobs needed to generate graphics for specified render pass -void -opengl_renderer::Render_pass( rendermode const Mode ) { - -#ifdef EU07_USE_DEBUG_CAMERA - // setup world camera for potential visualization - setup_pass( - m_worldcamera, - rendermode::color, - 0.f, - 1.0, - true ); -#endif - setup_pass( m_renderpass, Mode ); - switch( m_renderpass.draw_mode ) { - - case rendermode::color: { - - m_colorpass = m_renderpass; - - if( ( !simulation::is_ready ) || ( Global.gfx_skiprendering ) ) { - ::glClearColor( 51.0f / 255.f, 102.0f / 255.f, 85.0f / 255.f, 1.f ); // initial background Color - ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - break; - } - - if (Global.bWireFrame) - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - else - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - - if( ( true == Global.RenderShadows ) - && ( false == Global.bWireFrame ) - && ( m_shadowcolor != colors::white ) ) { - - // run shadowmaps pass before color - Timer::subsystem.gfx_shadows.start(); - if( false == FreeFlyModeFlag ) { - Render_pass( rendermode::cabshadows ); - } - Render_pass( rendermode::shadows ); - Timer::subsystem.gfx_shadows.stop(); -#ifdef EU07_USE_DEBUG_SHADOWMAP - UILayer.set_texture( m_shadowdebugtexture ); -#endif -#ifdef EU07_USE_DEBUG_CABSHADOWMAP - UILayer.set_texture( m_cabshadowdebugtexture ); -#endif - setup_pass( m_renderpass, Mode ); // restore draw mode. TBD, TODO: render mode stack - // setup shadowmap matrices - m_shadowtexturematrix = - glm::mat4 { 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f } // bias from [-1, 1] to [0, 1] - * m_shadowpass.camera.projection() - // during colour pass coordinates are moved from camera-centric to light-centric, essentially the difference between these two origins - * glm::translate( - glm::mat4{ glm::mat3{ m_shadowpass.camera.modelview() } }, - glm::vec3{ m_renderpass.camera.position() - m_shadowpass.camera.position() } ); - - if( false == FreeFlyModeFlag ) { - m_cabshadowtexturematrix = - glm::mat4{ 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f } // bias from [-1, 1] to [0, 1] - * m_cabshadowpass.camera.projection() - // during colour pass coordinates are moved from camera-centric to light-centric, essentially the difference between these two origins - * glm::translate( - glm::mat4{ glm::mat3{ m_cabshadowpass.camera.modelview() } }, - glm::vec3{ m_renderpass.camera.position() - m_cabshadowpass.camera.position() } ); - } - } - - if( true == m_environmentcubetexturesupport ) { - // potentially update environmental cube map - m_renderpass.draw_stats = {}; - if( true == Render_reflections() ) { - setup_pass( m_renderpass, Mode ); // restore draw mode. TBD, TODO: render mode stack - } - } - - ::glViewport( 0, 0, Global.fb_size.x, Global.fb_size.y ); - - auto const skydomecolour = simulation::Environment.m_skydome.GetAverageColor(); - ::glClearColor( skydomecolour.x, skydomecolour.y, skydomecolour.z, 0.f ); // kolor nieba - ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - - // setup - setup_matrices(); - // render - setup_drawing( true ); - setup_units( true, false, false ); - m_renderpass.draw_stats = {}; - Render( &simulation::Environment ); - // opaque parts... - setup_drawing( false ); - setup_units( true, true, true ); -#ifdef EU07_USE_DEBUG_CAMERA - if( DebugModeFlag ) { - // draw light frustum - ::glLineWidth( 2.f ); - ::glColor4f( 1.f, 0.9f, 0.8f, 1.f ); - ::glDisable( GL_LIGHTING ); - ::glDisable( GL_TEXTURE_2D ); - if( ( true == Global.RenderShadows ) && ( false == Global.bWireFrame ) ) { - m_shadowpass.camera.draw( m_renderpass.camera.position() - m_shadowpass.camera.position() ); - } - if( DebugCameraFlag ) { - ::glColor4f( 0.8f, 1.f, 0.9f, 1.f ); - m_worldcamera.camera.draw( m_renderpass.camera.position() - m_worldcamera.camera.position() ); - } - ::glLineWidth( 1.f ); - ::glEnable( GL_LIGHTING ); - ::glEnable( GL_TEXTURE_2D ); - } -#endif - // without rain/snow we can render the cab early to limit the overdraw - if( ( false == FreeFlyModeFlag ) - && ( Global.Overcast <= 1.f ) ) { // precipitation happens when overcast is in 1-2 range -#ifdef EU07_DISABLECABREFLECTIONS - switch_units( true, true, false ); -#endif - setup_shadow_map( m_cabshadowtexture, m_cabshadowtexturematrix ); - // cache shadow colour in case we need to account for cab light - auto const shadowcolor { m_shadowcolor }; - auto const *vehicle{ simulation::Train->Dynamic() }; - if( vehicle->InteriorLightLevel > 0.f ) { - setup_shadow_color( glm::min( colors::white, shadowcolor + glm::vec4( vehicle->InteriorLight * vehicle->InteriorLightLevel, 1.f ) ) ); - } - Render_cab( vehicle, vehicle->InteriorLightLevel, false ); - if( vehicle->InteriorLightLevel > 0.f ) { - setup_shadow_color( shadowcolor ); - } - } - switch_units( true, true, true ); - setup_shadow_map( m_shadowtexture, m_shadowtexturematrix ); - Render( simulation::Region ); - // ...translucent parts - setup_drawing( true ); - Render_Alpha( simulation::Region ); - // particles - Render_particles(); - // precipitation; done at the end, only before cab render - Render_precipitation(); - // cab render - if( false == FreeFlyModeFlag ) { -#ifdef EU07_DISABLECABREFLECTIONS - switch_units( true, true, false ); -#endif - setup_shadow_map( m_cabshadowtexture, m_cabshadowtexturematrix ); - // cache shadow colour in case we need to account for cab light - auto const shadowcolor{ m_shadowcolor }; - auto *vehicle { simulation::Train->Dynamic() }; - if( vehicle->InteriorLightLevel > 0.f ) { - setup_shadow_color( glm::min( colors::white, shadowcolor + glm::vec4( vehicle->InteriorLight * vehicle->InteriorLightLevel, 1.f ) ) ); - } - if( Global.Overcast > 1.f ) { - // with active precipitation draw the opaque cab parts here to mask rain/snow placed 'inside' the cab - setup_drawing( false ); - Render_cab( vehicle, vehicle->InteriorLightLevel, false ); - Render_interior( false); - setup_drawing( true ); - Render_interior( true ); - } - Render_cab( vehicle, vehicle->InteriorLightLevel, true ); - if( vehicle->InteriorLightLevel > 0.f ) { - setup_shadow_color( shadowcolor ); - } - // with the cab in place we can (finally) safely draw translucent part of the occupied vehicle - Render_Alpha( vehicle ); - } - - if( m_environmentcubetexturesupport ) { - // restore default texture matrix for reflections cube map - select_unit( m_helpertextureunit ); - ::glMatrixMode( GL_TEXTURE ); -// ::glPopMatrix(); - ::glLoadIdentity(); - select_unit( m_diffusetextureunit ); - ::glMatrixMode( GL_MODELVIEW ); - } - // store draw stats - m_colorpass.draw_stats = m_renderpass.draw_stats; - - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - - break; - } - - case rendermode::shadows: { - - if( simulation::is_ready ) { - // setup - ::glEnable( GL_POLYGON_OFFSET_FILL ); // alleviate depth-fighting - ::glPolygonOffset( 1.f, 1.f ); - - // main shadowmap - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_shadowframebuffer ); - ::glViewport( 0, 0, m_shadowbuffersize, m_shadowbuffersize ); - -#ifdef EU07_USE_DEBUG_SHADOWMAP - ::glClearColor( 0.f / 255.f, 0.f / 255.f, 0.f / 255.f, 1.f ); // initial background Color - ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); -#else - ::glClear( GL_DEPTH_BUFFER_BIT ); -#endif - ::glScissor( 1, 1, m_shadowbuffersize - 2, m_shadowbuffersize - 2 ); - ::glEnable( GL_SCISSOR_TEST ); - - setup_matrices(); - // render - // opaque parts... - setup_drawing( false ); -#ifdef EU07_USE_DEBUG_SHADOWMAP - setup_units( true, false, false ); -#else - setup_units( false, false, false ); -#endif - Render( simulation::Region ); - m_shadowpass = m_renderpass; - - // post-render restore - ::glDisable( GL_SCISSOR_TEST ); - ::glDisable( GL_POLYGON_OFFSET_FILL ); - - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target - } - break; - } - - case rendermode::cabshadows: { - - if( ( simulation::is_ready ) - && ( false == FreeFlyModeFlag ) ) { - // setup - ::glEnable( GL_POLYGON_OFFSET_FILL ); // alleviate depth-fighting - ::glPolygonOffset( 1.f, 1.f ); - - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_cabshadowframebuffer ); - ::glViewport( 0, 0, m_shadowbuffersize / 2, m_shadowbuffersize / 2 ); - -#ifdef EU07_USE_DEBUG_CABSHADOWMAP - ::glClearColor( 0.f / 255.f, 0.f / 255.f, 0.f / 255.f, 1.f ); // initial background Color - ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); -#else - ::glClear( GL_DEPTH_BUFFER_BIT ); -#endif - ::glScissor( 1, 1, m_shadowbuffersize / 2 - 2, m_shadowbuffersize / 2 - 2 ); - ::glEnable( GL_SCISSOR_TEST ); - - setup_matrices(); - // render - // opaque parts... - setup_drawing( false ); -#ifdef EU07_USE_DEBUG_CABSHADOWMAP - setup_units( true, false, false ); -#else - setup_units( false, false, false ); -#endif - ::glDisable( GL_CULL_FACE ); - - if( Global.RenderCabShadowsRange > 0 ) { - Render( simulation::Region ); - } - Render_cab( simulation::Train->Dynamic(), 0.f, false ); - Render_cab( simulation::Train->Dynamic(), 0.f, true ); - m_cabshadowpass = m_renderpass; - - // post-render restore - ::glDisable( GL_SCISSOR_TEST ); - ::glEnable( GL_CULL_FACE ); - ::glDisable( GL_POLYGON_OFFSET_FILL ); - - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target - } - break; - } - - case rendermode::reflections: { - // NOTE: buffer and viewport setup in this mode is handled by the wrapper method - ::glClearColor( 0.f, 0.f, 0.f, 1.f ); - ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - - if( simulation::is_ready ) { - - ::glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE ); - // setup - setup_matrices(); - // render - setup_drawing( true ); - setup_units( true, false, false ); - Render( &simulation::Environment ); - // opaque parts... - setup_drawing( false ); - setup_units( true, true, true ); - setup_shadow_map( m_shadowtexture, m_shadowtexturematrix ); - Render( simulation::Region ); -/* - // reflections are limited to sky and ground only, the update rate is too low for anything else - // ...translucent parts - setup_drawing( true ); - Render_Alpha( &World.Ground ); - // cab render is performed without shadows, due to low resolution and number of models without windows :| - switch_units( true, false, false ); - // cab render is done in translucent phase to deal with badly configured vehicles - if( World.Train != nullptr ) { Render_cab( World.Train->Dynamic() ); } -*/ - ::glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); - } - break; - } - - case rendermode::pickcontrols: { - if( simulation::is_ready ) { - // setup -#ifdef EU07_USE_PICKING_FRAMEBUFFER - ::glViewport( 0, 0, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE ); -#endif - ::glClearColor( 0.f, 0.f, 0.f, 1.f ); - ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - - m_pickcontrolsitems.clear(); - setup_matrices(); - // render - // opaque parts... - setup_drawing( false ); - setup_units( false, false, false ); - // cab render skips translucent parts, so we can do it here - if( simulation::Train != nullptr ) { - Render_cab( simulation::Train->Dynamic(), 0.f ); - Render( simulation::Train->Dynamic() ); - } - // post-render cleanup - } - break; - } - - case rendermode::pickscenery: { - if( simulation::is_ready ) { - // setup - m_picksceneryitems.clear(); -#ifdef EU07_USE_PICKING_FRAMEBUFFER - ::glViewport( 0, 0, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE ); -#endif - ::glClearColor( 0.f, 0.f, 0.f, 1.f ); - ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - - setup_matrices(); - // render - // opaque parts... - setup_drawing( false ); - setup_units( false, false, false ); - Render( simulation::Region ); - // post-render cleanup - } - break; - } - - default: { - break; - } - } -} - -bool opengl_renderer::Render_interior( bool const Alpha ) { - // NOTE: legacy renderer doesn't need to repaint translucent parts - // TODO: investigate reasons (depth writing?) and duplicate in opengl33 branch - if( Alpha ) { return false; } - // TODO: early frustum based cull, camera might be pointing elsewhere - std::vector< std::pair > dynamics; - auto *dynamic { simulation::Train->Dynamic() }; - // draw interiors of the occupied vehicle, and all vehicles behind it with permanent coupling, in case they're open-ended - while( dynamic != nullptr ) { - - glm::dvec3 const originoffset { dynamic->vPosition - m_renderpass.camera.position() }; - float const squaredistance{ glm::length2( glm::vec3{ originoffset } / Global.ZoomFactor ) }; - dynamics.emplace_back( squaredistance, dynamic ); - dynamic = dynamic->Next( coupling::permanent ); - } - // draw also interiors of permanently coupled vehicles in front, if there's any - dynamic = simulation::Train->Dynamic()->Prev( coupling::permanent ); - while( dynamic != nullptr ) { - - glm::dvec3 const originoffset { dynamic->vPosition - m_renderpass.camera.position() }; - float const squaredistance{ glm::length2( glm::vec3{ originoffset } / Global.ZoomFactor ) }; - dynamics.emplace_back( squaredistance, dynamic ); - dynamic = dynamic->Prev( coupling::permanent ); - } - if( Alpha ) { - std::sort( - std::begin( dynamics ), std::end( dynamics ), - []( std::pair const &Left, std::pair const &Right ) { - return ( Left.first ) > ( Right.first ); } ); - } - for( auto &dynamic : dynamics ) { - Render_lowpoly( dynamic.second, dynamic.first, true, Alpha ); - } - - return true; -} - -bool opengl_renderer::Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha ) { - - if( Dynamic->mdLowPolyInt == nullptr ) { return false; } - - // low poly interior - glm::dvec3 const originoffset { Dynamic->vPosition - m_renderpass.camera.position() }; - - if( Setup ) { - - TSubModel::iInstance = reinterpret_cast( Dynamic ); //żeby nie robić cudzych animacji - TSubModel::iVariantSeed = Dynamic->variant_seed(); - Dynamic->ABuLittleUpdate( Squaredistance ); // ustawianie zmiennych submodeli dla wspólnego modelu - ::glPushMatrix(); - - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - ::glMultMatrixd( glm::value_ptr(Dynamic->mMatrix) ); - - m_renderspecular = true; // vehicles are rendered with specular component. static models without, at least for the time being - } - // HACK: reduce light level for vehicle interior if there's strong global lighting source - if( false == Alpha ) { - auto const luminance{ static_cast( - 0.5 - * ( std::max( - 0.3, - ( Global.fLuminance - Global.Overcast ) - * ( Dynamic->fShade > 0.0 ? - Dynamic->fShade : - 1.0 ) ) ) ) }; - m_sunlight.apply_intensity( - std::clamp( ( - Dynamic->fShade > 0.f ? - Dynamic->fShade : - 1.f ) - - luminance, - 0.f, 1.f ) ); - Render( Dynamic->mdLowPolyInt, Dynamic->Material(), Squaredistance ); - // HACK: if the model has low poly interior, we presume the load is placed inside and also affected by reduced light level - if( Dynamic->mdLoad ) { - // renderowanie nieprzezroczystego ładunku - Render( Dynamic->mdLoad, Dynamic->Material(), Squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); - } - m_sunlight.apply_intensity( Dynamic->fShade > 0.f ? Dynamic->fShade : 1.f ); - } - else { - // Render_Alpha( Dynamic->mdLowPolyInt, Dynamic->Material(), Squaredistance ); - // HACK: some models have windows included as part of the main model instead of lowpoly - if( Dynamic->mdModel ) { - // main model - Render_Alpha( Dynamic->mdModel, Dynamic->Material(), Squaredistance ); - } - } - - if( Setup ) { - - m_renderspecular = false; - if( Dynamic->fShade > 0.0f ) { - // restore regular light level - m_sunlight.apply_intensity(); - } - - ::glPopMatrix(); - - // TODO: check if this reset is needed. In theory each object should render all parts based on its own instance data anyway? - if( Dynamic->btnOn ) { - Dynamic->TurnOff(); // przywrócenie domyślnych pozycji submodeli - } - } - - return true; -} - -bool opengl_renderer::Render_coupler_adapter( TDynamicObject *Dynamic, float const Squaredistance, int const End, bool const Alpha ) { - - if( Dynamic->m_coupleradapters[ End ] == nullptr ) { return false; } - - auto const position { glm::dvec3 { - 0.f, - Dynamic->MoverParameters->Couplers[ End ].adapter_height, - ( Dynamic->MoverParameters->Couplers[ End ].adapter_length + Dynamic->MoverParameters->Dim.L * 0.5 ) * ( End == end::front ? 1 : -1 ) } }; - - auto const angle{ glm::vec3{ 0, ( End == end::front ? 0 : 180 ), 0 } }; - - if( Alpha ) { - Render_Alpha( Dynamic->m_coupleradapters[ End ], Dynamic->Material(), Squaredistance, position, angle ); - } - else { - Render( Dynamic->m_coupleradapters[ End ], Dynamic->Material(), Squaredistance, position, angle ); - } - - return true; -} - -// creates dynamic environment cubemap -bool -opengl_renderer::Render_reflections() { - - if( Global.reflectiontune.update_interval == 0 ) { return false; } - - auto const timestamp { Timer::GetRenderTime() }; - if( ( timestamp - m_environmentupdatetime < Global.reflectiontune.update_interval ) - && ( glm::length2( m_renderpass.camera.position() - m_environmentupdatelocation ) < sq(1000.0)) ) { - // run update every 5+ mins of simulation time, or at least 1km from the last location - return false; - } - m_environmentupdatetime = timestamp; - m_environmentupdatelocation = m_renderpass.camera.position(); - - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_environmentframebuffer ); - ::glViewport( 0, 0, EU07_ENVIRONMENTBUFFERSIZE, EU07_ENVIRONMENTBUFFERSIZE ); - for( m_environmentcubetextureface = GL_TEXTURE_CUBE_MAP_POSITIVE_X; - m_environmentcubetextureface < GL_TEXTURE_CUBE_MAP_POSITIVE_X + 6; - ++m_environmentcubetextureface ) { - - ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, m_environmentcubetextureface, m_environmentcubetexture, 0 ); - Render_pass( rendermode::reflections ); - } - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); - - return true; -} - -void -opengl_renderer::setup_pass( renderpass_config &Config, rendermode const Mode, float const Znear, float const Zfar, bool const Ignoredebug ) { - - Config.draw_mode = Mode; - - if( false == simulation::is_ready ) { return; } - // setup draw range - switch( Mode ) { - case rendermode::color: { Config.draw_range = Global.BaseDrawRange * Global.fDistanceFactor; break; } - case rendermode::shadows: { Config.draw_range = Global.shadowtune.range; break; } - case rendermode::cabshadows: { Config.draw_range = ( Global.RenderCabShadowsRange > 0 ? std::clamp( Global.RenderCabShadowsRange, 5, 100 ) : simulation::Train->Occupied()->Dim.L ); break; } - case rendermode::reflections: { Config.draw_range = Global.BaseDrawRange; break; } - case rendermode::pickcontrols: { Config.draw_range = 50.f; break; } - case rendermode::pickscenery: { Config.draw_range = Global.BaseDrawRange * 0.5f; break; } - default: { Config.draw_range = 0.f; break; } - } - // setup camera - auto &camera = Config.camera; - - camera.projection() = glm::mat4( 1.f ); - glm::dmat4 viewmatrix( 1.0 ); - - switch( Mode ) { - case rendermode::color: { - // modelview - if( ( false == DebugCameraFlag ) || ( true == Ignoredebug ) ) { - camera.position() = Global.pCamera.Pos; - Global.pCamera.SetMatrix( viewmatrix ); - } - else { - camera.position() = Global.pDebugCamera.Pos; - Global.pDebugCamera.SetMatrix( viewmatrix ); - } - // optimization: don't bother drawing things completely blended with the background - // but ensure at least 2 km range to account for signals and lights - Config.draw_range = std::min( Config.draw_range, m_fogrange * 2 ); - Config.draw_range = std::max( 2000.f, Config.draw_range ); - // projection - auto const zfar = ( Zfar > 1.f ? Zfar : Config.draw_range * Zfar ); - auto const znear = ( Znear > 1.f ? Znear : Znear > 0.f ? Znear * zfar : 0.1f * Global.ZoomFactor); - camera.projection() *= - glm::perspective( - glm::radians( Global.FieldOfView / Global.ZoomFactor ), - std::max( 1.f, (float)Global.window_size.x ) / std::max( 1.f, (float)Global.window_size.y ), - znear, - zfar ); -/* - m_sunandviewangle = - glm::dot( - m_sunlight.direction, - glm::vec3( 0.f, 0.f, -1.f ) * glm::mat3( viewmatrix ) ); -*/ - break; - } - case rendermode::shadows: { - // calculate lightview boundaries based on relevant area of the world camera frustum: - // ...setup chunk of frustum we're interested in... -// auto const zfar = std::min( 1.f, Global.shadowtune.range / ( Global.BaseDrawRange * Global.fDistanceFactor ) * std::max( 1.f, Global.ZoomFactor * 0.5f ) ); -// auto const zfar = Global.shadowtune.range * std::max( Zfar, Global.ZoomFactor * 0.5f ); - auto const zfar = ( Zfar > 1.f ? Zfar : Zfar * Global.shadowtune.range * std::max( Zfar, Global.ZoomFactor * 0.5f ) ); - renderpass_config worldview; - setup_pass( worldview, rendermode::color, Znear, zfar, true ); - auto &frustumchunkshapepoints = worldview.camera.frustum_points(); - // ...modelview matrix: determine the centre of frustum chunk in world space... - glm::vec3 frustumchunkmin, frustumchunkmax; - bounding_box( frustumchunkmin, frustumchunkmax, std::begin( frustumchunkshapepoints ), std::end( frustumchunkshapepoints ) ); - auto const frustumchunkcentre = ( frustumchunkmin + frustumchunkmax ) * 0.5f; - // ...cap the vertical angle to keep shadows from getting too long... - auto const lightvector = - glm::normalize( glm::vec3{ - m_sunlight.direction.x, - std::min( m_sunlight.direction.y, Global.gfx_shadow_angle_min ), - m_sunlight.direction.z } ); - // ...place the light source at the calculated centre and setup world space light view matrix... - camera.position() = worldview.camera.position() + glm::dvec3{ frustumchunkcentre }; - viewmatrix *= glm::lookAt( - camera.position(), - camera.position() + glm::dvec3{ lightvector }, - glm::dvec3{ 0.f, 1.f, 0.f } ); - // ...projection matrix: calculate boundaries of the frustum chunk in light space... - auto const lightviewmatrix = - glm::translate( - glm::mat4{ glm::mat3{ viewmatrix } }, - -frustumchunkcentre ); - for( auto &point : frustumchunkshapepoints ) { - point = lightviewmatrix * point; - } - bounding_box( frustumchunkmin, frustumchunkmax, std::begin( frustumchunkshapepoints ), std::end( frustumchunkshapepoints ) ); - // quantize the frustum points and add some padding, to reduce shadow shimmer on scale changes - auto const frustumchunkdepth { zfar - ( Znear > 1.f ? Znear : 0.f ) }; - auto const quantizationstep{std::min(frustumchunkdepth, 50.f)}; - frustumchunkmin = quantizationstep * glm::floor( frustumchunkmin * ( 1.f / quantizationstep ) ); - frustumchunkmax = quantizationstep * glm::ceil( frustumchunkmax * ( 1.f / quantizationstep ) ); - // ...use the dimensions to set up light projection boundaries... - // NOTE: since we only have one cascade map stage, we extend the chunk forward/back to catch areas normally covered by other stages - camera.projection() *= - glm::ortho( - frustumchunkmin.x, frustumchunkmax.x, - frustumchunkmin.y, frustumchunkmax.y, - frustumchunkmin.z - 500.f, frustumchunkmax.z + 500.f ); - // ... and adjust the projection to sample complete shadow map texels: - // get coordinates for a sample texel... - auto shadowmaptexel = glm::vec2 { camera.projection() * glm::mat4{ viewmatrix } * glm::vec4{ 0.f, 0.f, 0.f, 1.f } }; - // ...convert result from clip space to texture coordinates, and calculate adjustment... - shadowmaptexel *= m_shadowbuffersize * 0.5f; - auto shadowmapadjustment = glm::round( shadowmaptexel ) - shadowmaptexel; - // ...transform coordinate change back to homogenous light space... - shadowmapadjustment /= m_shadowbuffersize * 0.5f; - // ... and bake the adjustment into the projection matrix - camera.projection() = - glm::translate( - glm::mat4{ 1.f }, - glm::vec3{ shadowmapadjustment, 0.f } ) - * camera.projection(); - - break; - } - case rendermode::cabshadows: { - // fixed size cube large enough to enclose a vehicle compartment - // modelview - auto const lightvector = - glm::normalize( glm::vec3{ - m_sunlight.direction.x, - std::min( m_sunlight.direction.y, Global.gfx_shadow_angle_min ), - m_sunlight.direction.z } ); - camera.position() = Global.pCamera.Pos - glm::dvec3 { lightvector }; - viewmatrix *= glm::lookAt( - camera.position(), - Global.pCamera.Pos, - glm::dvec3 { 0, 1, 0 } ); - // projection - auto const maphalfsize { std::min( 10.f, Config.draw_range * 0.5f ) }; - camera.projection() *= - glm::ortho( - -maphalfsize, maphalfsize, - -maphalfsize, maphalfsize, - -Config.draw_range, Config.draw_range ); -/* - // adjust the projection to sample complete shadow map texels - auto shadowmaptexel = glm::vec2 { camera.projection() * glm::mat4{ viewmatrix } * glm::vec4{ 0.f, 0.f, 0.f, 1.f } }; - shadowmaptexel *= ( m_shadowbuffersize / 2 ) * 0.5f; - auto shadowmapadjustment = glm::round( shadowmaptexel ) - shadowmaptexel; - shadowmapadjustment /= ( m_shadowbuffersize / 2 ) * 0.5f; - camera.projection() = glm::translate( glm::mat4{ 1.f }, glm::vec3{ shadowmapadjustment, 0.f } ) * camera.projection(); -*/ - break; - } - case rendermode::reflections: { - // modelview - camera.position() = ( - ( ( true == DebugCameraFlag ) && ( false == Ignoredebug ) ) ? - Global.pDebugCamera.Pos : - Global.pCamera.Pos ); - glm::dvec3 const cubefacetargetvectors[ 6 ] = { { 1.0, 0.0, 0.0 }, { -1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 0.0, -1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { 0.0, 0.0, -1.0 } }; - glm::dvec3 const cubefaceupvectors[ 6 ] = { { 0.0, -1.0, 0.0 }, { 0.0, -1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { 0.0, 0.0, -1.0 }, { 0.0, -1.0, 0.0 }, { 0.0, -1.0, 0.0 } }; - auto const cubefaceindex = m_environmentcubetextureface - GL_TEXTURE_CUBE_MAP_POSITIVE_X; - viewmatrix *= - glm::lookAt( - camera.position(), - camera.position() + cubefacetargetvectors[ cubefaceindex ], - cubefaceupvectors[ cubefaceindex ] ); - // projection - camera.projection() *= - glm::perspective( - glm::radians( 90.f ), - 1.f, - 0.1f * Global.ZoomFactor, - Config.draw_range * Global.fDistanceFactor ); - break; - } - - case rendermode::pickcontrols: - case rendermode::pickscenery: { - // TODO: scissor test for pick modes - // modelview - camera.position() = Global.pCamera.Pos; - Global.pCamera.SetMatrix( viewmatrix ); - // projection - camera.projection() *= - glm::perspective( - glm::radians( Global.FieldOfView / Global.ZoomFactor ), - std::max( 1.f, (float)Global.window_size.x ) / std::max( 1.f, (float)Global.window_size.y ), - 0.1f * Global.ZoomFactor, - Config.draw_range * Global.fDistanceFactor ); - break; - } - default: { - break; - } - } - camera.modelview() = viewmatrix; - camera.update_frustum(); -} - -void -opengl_renderer::setup_matrices() { - - ::glMatrixMode( GL_PROJECTION ); - OpenGLMatrices.load_matrix( m_renderpass.camera.projection() ); - - if( ( m_renderpass.draw_mode == rendermode::color ) - && ( m_environmentcubetexturesupport ) ) { - // special case, for colour render pass setup texture matrix for reflections cube map - select_unit( m_helpertextureunit ); - ::glMatrixMode( GL_TEXTURE ); -// ::glPushMatrix(); - ::glLoadIdentity(); - ::glMultMatrixf( glm::value_ptr( glm::inverse( glm::mat4{ glm::mat3{ m_renderpass.camera.modelview() } } ) ) ); - select_unit( m_diffusetextureunit ); - } - - // trim modelview matrix just to rotation, since rendering is done in camera-centric world space - ::glMatrixMode( GL_MODELVIEW ); - OpenGLMatrices.load_matrix( glm::mat4( glm::mat3( m_renderpass.camera.modelview() ) ) ); -} - -void -opengl_renderer::setup_drawing( bool const Alpha ) { - - if( true == Alpha ) { - - ::glEnable( GL_BLEND ); - ::glAlphaFunc( GL_GREATER, 0.f ); - } - else { - ::glDisable( GL_BLEND ); - ::glAlphaFunc( GL_GREATER, EU07_OPACITYDEFAULT ); - } - - switch( m_renderpass.draw_mode ) { - case rendermode::color: - case rendermode::reflections: { - ::glEnable( GL_LIGHTING ); - ::glShadeModel( GL_SMOOTH ); - if( Global.iMultisampling ) { - ::glEnable( GL_MULTISAMPLE ); - } - // setup fog - if( Global.fFogEnd > 0 ) { - // fog setup - m_fogrange = Global.fFogEnd / std::max( 1.f, Global.Overcast * 2.f ); - ::glFogfv( GL_FOG_COLOR, glm::value_ptr( Global.FogColor ) ); - ::glFogf( GL_FOG_DENSITY, static_cast( 1.0 / m_fogrange ) ); - ::glEnable( GL_FOG ); - } - else { ::glDisable( GL_FOG ); } - - break; - } - case rendermode::pickcontrols: - case rendermode::pickscenery: - ::glAlphaFunc( GL_ALWAYS, 0.0f ); - [[ fallthrough ]]; - case rendermode::shadows: - case rendermode::cabshadows: { - ::glColor4fv( glm::value_ptr( colors::white ) ); - ::glDisable( GL_LIGHTING ); - ::glShadeModel( GL_FLAT ); - if( Global.iMultisampling ) { - ::glDisable( GL_MULTISAMPLE ); - } - ::glDisable( GL_FOG ); - - break; - } - default: { - break; - } - } -} - -// configures, enables and disables specified texture units -void -opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const Reflections ) { - // helper texture unit. - // darkens previous stage, preparing data for the shadow texture unit to select from - if( m_helpertextureunit >= 0 ) { - - select_unit( m_helpertextureunit ); - - if( ( true == Reflections ) - || ( ( true == Global.RenderShadows ) && ( true == Shadows ) && ( false == Global.bWireFrame ) ) ) { - // we need to have texture on the helper for either the reflection and shadow generation (or both) - if( true == m_environmentcubetexturesupport ) { - // bind dynamic environment cube if it's enabled... - // NOTE: environment cube map isn't part of regular texture system, so we use direct bind call here - ::glBindTexture( GL_TEXTURE_CUBE_MAP, m_environmentcubetexture ); - ::glEnable( GL_TEXTURE_CUBE_MAP ); - } - else { - // ...otherwise fallback on static spherical image - m_textures.bind( textureunit::helper, m_reflectiontexture ); - ::glEnable( GL_TEXTURE_2D ); - } - } - else { - if( true == m_environmentcubetexturesupport ) { - ::glDisable( GL_TEXTURE_CUBE_MAP ); - } - else { - ::glDisable( GL_TEXTURE_2D ); - } - } - - if( ( true == Global.RenderShadows ) && ( true == Shadows ) && ( false == Global.bWireFrame ) ) { - - ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); - ::glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, glm::value_ptr( m_shadowcolor ) ); - - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE ); // darken the previous stage - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); - - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); // pass the previous stage alpha down the chain - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); - } - else { - - ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE ); // pass the previous stage colour down the chain - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); - - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); // pass the previous stage alpha down the chain - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); - } - - if( true == Reflections ) { - if( true == m_environmentcubetexturesupport ) { - ::glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP ); - ::glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP ); - ::glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP ); - ::glEnable( GL_TEXTURE_GEN_S ); - ::glEnable( GL_TEXTURE_GEN_T ); - ::glEnable( GL_TEXTURE_GEN_R ); - } - else { - // fixed texture fall back - ::glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); - ::glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); - ::glEnable( GL_TEXTURE_GEN_S ); - ::glEnable( GL_TEXTURE_GEN_T ); - } - } - else { - if( true == m_environmentcubetexturesupport ) { - ::glDisable( GL_TEXTURE_GEN_S ); - ::glDisable( GL_TEXTURE_GEN_T ); - ::glDisable( GL_TEXTURE_GEN_R ); - } - else { - ::glDisable( GL_TEXTURE_GEN_S ); - ::glDisable( GL_TEXTURE_GEN_T ); - } - } - } - // shadow texture unit. - // interpolates between primary colour and the previous unit, which should hold darkened variant of the primary colour - if( m_shadowtextureunit >= 0 ) { - if( ( true == Global.RenderShadows ) - && ( true == Shadows ) - && ( false == Global.bWireFrame ) - && ( m_shadowcolor != colors::white ) ) { - - select_unit( m_shadowtextureunit ); - // NOTE: texture and transformation matrix setup are done through separate call - ::glEnable( GL_TEXTURE_2D ); - // s - ::glEnable( GL_TEXTURE_GEN_S ); - // t - ::glEnable( GL_TEXTURE_GEN_T ); - // r - ::glEnable( GL_TEXTURE_GEN_R ); - // q - ::glEnable( GL_TEXTURE_GEN_Q ); - - ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE ); // choose between lit and lit * shadow colour, based on depth test - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR ); - - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); // pass the previous stage alpha down the chain - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); - } - else { - // turn off shadow map tests - select_unit( m_shadowtextureunit ); - - ::glDisable( GL_TEXTURE_2D ); - ::glDisable( GL_TEXTURE_GEN_S ); - ::glDisable( GL_TEXTURE_GEN_T ); - ::glDisable( GL_TEXTURE_GEN_R ); - ::glDisable( GL_TEXTURE_GEN_Q ); - } - } - // reflections/normals texture unit - // NOTE: comes after diffuse stage in the operation chain - if( m_normaltextureunit >= 0 ) { - - select_unit( m_normaltextureunit ); - - if( true == Reflections ) { - ::glEnable( GL_TEXTURE_2D ); - - ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE ); // blend between object colour and env.reflection - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0 ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE ); // alpha channel of the normal map controls reflection strength - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA ); - - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); // pass the previous stage alpha down the chain - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); - } - else { - ::glDisable( GL_TEXTURE_2D ); - } - } - // diffuse texture unit. - // NOTE: diffuse texture mapping is never fully disabled, alpha channel information is always included - select_unit( m_diffusetextureunit ); - ::glEnable( GL_TEXTURE_2D ); - if( true == Diffuse ) { - // default behaviour, modulate with previous stage - ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); -/* - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); -*/ - } - else { - // solid colour with texture alpha - ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); -/* - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); -*/ - } - // update unit state - m_unitstate.diffuse = Diffuse; - m_unitstate.shadows = Shadows; - m_unitstate.reflections = Reflections; -} - -// configures shadow texture unit for specified shadow map and conersion matrix -void -opengl_renderer::setup_shadow_map( GLuint const Texture, glm::mat4 const &Transformation ) { - - if( ( m_shadowtextureunit == -1 ) - || ( false == Global.RenderShadows ) - || ( true == Global.bWireFrame ) - || ( m_shadowcolor == colors::white ) ) { - // shadows are off - return; - } - - select_unit( m_shadowtextureunit ); - // NOTE: shadowmap isn't part of regular texture system, so we use direct bind call here - ::glBindTexture( GL_TEXTURE_2D, Texture ); - // s - ::glTexGenfv( GL_S, GL_EYE_PLANE, glm::value_ptr( glm::row( Transformation, 0 ) ) ); - // t - ::glTexGenfv( GL_T, GL_EYE_PLANE, glm::value_ptr( glm::row( Transformation, 1 ) ) ); - // r - ::glTexGenfv( GL_R, GL_EYE_PLANE, glm::value_ptr( glm::row( Transformation, 2 ) ) ); - // q - ::glTexGenfv( GL_Q, GL_EYE_PLANE, glm::value_ptr( glm::row( Transformation, 3 ) ) ); - - select_unit( m_diffusetextureunit ); -} - -// enables and disables specified texture units -void -opengl_renderer::switch_units( bool const Diffuse, bool const Shadows, bool const Reflections ) { - // helper texture unit. - if( m_helpertextureunit >= 0 ) { - - select_unit( m_helpertextureunit ); - if( ( true == Reflections ) - || ( ( true == Global.RenderShadows ) - && ( true == Shadows ) - && ( false == Global.bWireFrame ) - && ( m_shadowcolor != colors::white ) ) ) { - if( true == m_environmentcubetexturesupport ) { - ::glEnable( GL_TEXTURE_CUBE_MAP ); - } - else { - ::glEnable( GL_TEXTURE_2D ); - } - } - else { - if( true == m_environmentcubetexturesupport ) { - ::glDisable( GL_TEXTURE_CUBE_MAP ); - } - else { - ::glDisable( GL_TEXTURE_2D ); - } - } - } - // shadow texture unit. - if( m_shadowtextureunit >= 0 ) { - if( ( true == Global.RenderShadows ) && ( true == Shadows ) && ( false == Global.bWireFrame ) ) { - - select_unit( m_shadowtextureunit ); - ::glEnable( GL_TEXTURE_2D ); - } - else { - - select_unit( m_shadowtextureunit ); - ::glDisable( GL_TEXTURE_2D ); - } - } - // normal/reflection texture unit - if( m_normaltextureunit >= 0 ) { - if( true == Reflections ) { - - select_unit( m_normaltextureunit ); - ::glEnable( GL_TEXTURE_2D ); - } - else { - select_unit( m_normaltextureunit ); - ::glDisable( GL_TEXTURE_2D ); - } - } - // diffuse texture unit. - // NOTE: toggle actually disables diffuse texture mapping, unlike setup counterpart - if( true == Diffuse ) { - - select_unit( m_diffusetextureunit ); - ::glEnable( GL_TEXTURE_2D ); - } - else { - - select_unit( m_diffusetextureunit ); - ::glDisable( GL_TEXTURE_2D ); - } - // update unit state - m_unitstate.diffuse = Diffuse; - m_unitstate.shadows = Shadows; - m_unitstate.reflections = Reflections; -} - -void -opengl_renderer::setup_shadow_color( glm::vec4 const &Shadowcolor ) { - - select_unit( m_helpertextureunit ); - ::glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, glm::value_ptr( Shadowcolor ) ); // in-shadow colour multiplier - select_unit( m_diffusetextureunit ); -} - -void -opengl_renderer::setup_environment_light( TEnvironmentType const Environment ) { - - switch( Environment ) { - case e_flat: { - m_sunlight.apply_intensity(); -// m_environment = Environment; - break; - } - case e_canyon: { - m_sunlight.apply_intensity( 0.4f ); -// m_environment = Environment; - break; - } - case e_tunnel: { - m_sunlight.apply_intensity( 0.2f ); -// m_environment = Environment; - break; - } - default: { - break; - } - } -} - -bool -opengl_renderer::Render( world_environment *Environment ) { - - // calculate shadow tone, based on positions of celestial bodies - m_shadowcolor = glm::mix( - glm::vec4{ colors::shadow }, - glm::vec4{ colors::white }, - std::clamp( -Environment->m_sun.getAngle(), 0.f, 6.f ) / 6.f ); - if( ( Environment->m_sun.getAngle() < -18.f ) - && ( Environment->m_moon.getAngle() > 0.f ) ) { - // turn on moon shadows after nautical twilight, if the moon is actually up - m_shadowcolor = colors::shadow; - } - // soften shadows depending on sky overcast factor - m_shadowcolor = glm::min( - colors::white, - m_shadowcolor + ( ( colors::white - colors::shadow ) * Global.Overcast ) ); - - if( Global.bWireFrame ) { - // bez nieba w trybie rysowania linii - return false; - } - - Bind_Material( null_handle ); - ::glDisable( GL_LIGHTING ); - ::glDisable( GL_DEPTH_TEST ); - ::glDepthMask( GL_FALSE ); - // skydome - // drawn with 500m radius to blend in if the fog range is low - ::glPushMatrix(); - ::glScalef( 500.f, 500.f, 500.f ); - m_skydomerenderer.render(); - if( Global.bUseVBO ) { - gfx::opengl_vbogeometrybank::reset(); - } - ::glPopMatrix(); - // stars - if( Environment->m_stars.m_stars != nullptr ) { - // setup - ::glPushMatrix(); - ::glRotatef( Environment->m_stars.m_latitude, 1.f, 0.f, 0.f ); // ustawienie osi OY na północ - ::glRotatef( -std::fmod( (float)Global.fTimeAngleDeg, 360.f ), 0.f, 1.f, 0.f ); // obrót dobowy osi OX - ::glPointSize( 2.f ); - // render - Render( Environment->m_stars.m_stars, nullptr, 1.0 ); - // post-render cleanup - ::glPointSize( 3.f ); - ::glPopMatrix(); - } - // celestial bodies - if( DebugModeFlag == true ) { - // mark sun position for easier debugging - Environment->m_sun.render(); - Environment->m_moon.render(); - } - // render actual sun and moon - ::glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT ); - - ::glDisable( GL_LIGHTING ); - ::glDisable( GL_ALPHA_TEST ); - ::glEnable( GL_BLEND ); - ::glBlendFunc( GL_SRC_ALPHA, GL_ONE ); - - auto const &modelview = OpenGLMatrices.data( GL_MODELVIEW ); - - auto const fogfactor { std::clamp( Global.fFogEnd / 2000.f, 0.f, 1.f ) }; // closer/denser fog reduces opacity of the celestial bodies - float const duskfactor = 1.0f - std::clamp( std::abs( Environment->m_sun.getAngle() ), 0.0f, 12.0f ) / 12.0f; - glm::vec3 suncolor = glm::mix( - glm::vec3( 1.0f, 242.0f / 255.0f, 231.0f / 255.0f ), - glm::vec3( 235.0f / 255.0f, 140.0f / 255.0f, 36.0f / 255.0f ), - duskfactor ); - // sun - if (!m_isATI) - { - Bind_Texture( m_suntexture ); - ::glColor4f( suncolor.x, suncolor.y, suncolor.z, std::clamp( 1.5f - Global.Overcast, 0.f, 1.f ) * fogfactor ); - auto const sunvector = Environment->m_sun.getDirection(); - auto const sunposition = modelview * glm::vec4( sunvector.x, sunvector.y, sunvector.z, 1.0f ); - - ::glPushMatrix(); - ::glLoadIdentity(); // macierz jedynkowa - ::glTranslatef( sunposition.x, sunposition.y, sunposition.z ); // początek układu zostaje bez zmian - - float const size = std::lerp( // TODO: expose distance/scale factor from the moon object - 0.0325f, - 0.0275f, - std::clamp( Environment->m_sun.getAngle(), 0.f, 90.f ) / 90.f ); - ::glBegin( GL_TRIANGLE_STRIP ); - ::glMultiTexCoord2f( m_diffusetextureunit, 1.f, 1.f ); ::glVertex3f( -size, size, 0.f ); - ::glMultiTexCoord2f( m_diffusetextureunit, 1.f, 0.f ); ::glVertex3f( -size, -size, 0.f ); - ::glMultiTexCoord2f( m_diffusetextureunit, 0.f, 1.f ); ::glVertex3f( size, size, 0.f ); - ::glMultiTexCoord2f( m_diffusetextureunit, 0.f, 0.f ); ::glVertex3f( size, -size, 0.f ); - ::glEnd(); - - ::glPopMatrix(); - } - // moon - if (!m_isATI) - { - Bind_Texture( m_moontexture ); - glm::vec3 mooncolor(1.0f, 0.9490196f, 0.9058824f); - ::glColor4f( - mooncolor.r, mooncolor.g, mooncolor.b, - // fade the moon if it's near the sun in the sky, especially during the day - std::max( - 0.f, - 1.0 - - 0.5 * Global.fLuminance - - 0.65 * std::max( 0.f, glm::dot( Environment->m_sun.getDirection(), Environment->m_moon.getDirection() ) ) ) - * fogfactor ); - - auto const moonposition = modelview * glm::vec4( Environment->m_moon.getDirection(), 1.0f ); - ::glPushMatrix(); - ::glLoadIdentity(); // macierz jedynkowa - ::glTranslatef( moonposition.x, moonposition.y, moonposition.z ); - - float const size = std::lerp( // TODO: expose distance/scale factor from the moon object - 0.0160f, - 0.0135f, - std::clamp( Environment->m_moon.getAngle(), 0.f, 90.f ) / 90.f ); - // choose the moon appearance variant, based on current moon phase - // NOTE: implementation specific, 8 variants are laid out in 3x3 arrangement - // from new moon onwards, top left to right bottom (last spot is left for future use, if any) - auto const moonphase = Environment->m_moon.getPhase(); - float moonu, moonv; - if( moonphase < 1.84566f ) { moonv = 1.0f - 0.0f; moonu = 0.0f; } - else if( moonphase < 5.53699f ) { moonv = 1.0f - 0.0f; moonu = 0.333f; } - else if( moonphase < 9.22831f ) { moonv = 1.0f - 0.0f; moonu = 0.667f; } - else if( moonphase < 12.91963f ) { moonv = 1.0f - 0.333f; moonu = 0.0f; } - else if( moonphase < 16.61096f ) { moonv = 1.0f - 0.333f; moonu = 0.333f; } - else if( moonphase < 20.30228f ) { moonv = 1.0f - 0.333f; moonu = 0.667f; } - else if( moonphase < 23.99361f ) { moonv = 1.0f - 0.667f; moonu = 0.0f; } - else if( moonphase < 27.68493f ) { moonv = 1.0f - 0.667f; moonu = 0.333f; } - else if( moonphase == 50 ) { moonv = 1.0f - 0.667f; moonu = 0.66f; } //9th slot used for easter egg - else { moonv = 1.0f - 0.0f; moonu = 0.0f; } - - ::glBegin( GL_TRIANGLE_STRIP ); - ::glMultiTexCoord2f( m_diffusetextureunit, moonu, moonv ); ::glVertex3f( -size, size, 0.0f ); - ::glMultiTexCoord2f( m_diffusetextureunit, moonu, moonv - 0.333f ); ::glVertex3f( -size, -size, 0.0f ); - ::glMultiTexCoord2f( m_diffusetextureunit, moonu + 0.333f, moonv ); ::glVertex3f( size, size, 0.0f ); - ::glMultiTexCoord2f( m_diffusetextureunit, moonu + 0.333f, moonv - 0.333f ); ::glVertex3f( size, -size, 0.0f ); - ::glEnd(); - - ::glPopMatrix(); - } - ::glPopAttrib(); - - m_sunlight.apply_angle(); - m_sunlight.apply_intensity(); - - // clouds - if( Environment->m_clouds.mdCloud ) { - // setup - Disable_Lights(); - ::glEnable( GL_LIGHTING ); - ::glEnable( GL_LIGHT0 ); // other lights will be enabled during lights update - ::glLightModelfv( - GL_LIGHT_MODEL_AMBIENT, - glm::value_ptr( - glm::mix( Environment->m_skydome.GetAverageColor(), suncolor, duskfactor * 0.25f ) - * std::lerp( 1.f, 0.35f, Global.Overcast / 2.f ) // overcast darkens the clouds - * 0.5f // arbitrary adjustment factor - ) ); - // render - Render( Environment->m_clouds.mdCloud, nullptr, 100.0 ); - Render_Alpha( Environment->m_clouds.mdCloud, nullptr, 100.0 ); - // post-render cleanup - ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr( colors::none ) ); - ::glDisable( GL_LIGHTING ); - } - - ::glDepthMask( GL_TRUE ); - ::glEnable( GL_DEPTH_TEST ); - ::glEnable( GL_LIGHTING ); - - return true; -} - -// geometry methods -// creates a new geometry bank. returns: handle to the bank or NULL -gfx::geometrybank_handle -opengl_renderer::Create_Bank() { - if (Global.bUseVBO) - return m_geometry.register_bank(std::make_unique()); - else - return m_geometry.register_bank(std::make_unique()); -} - -// creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL -gfx::geometry_handle opengl_renderer::Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) -{ - - return m_geometry.create_chunk( Indices, Vertices, Userdata, Geometry, Type ); -} - -// creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL -gfx::geometry_handle opengl_renderer::Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) -{ - - return m_geometry.create_chunk( Vertices, Userdata, Geometry, Type ); -} - -// replaces data of specified chunk with the supplied vertex data, starting from specified offset -bool opengl_renderer::Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset) -{ - - return m_geometry.replace( Vertices, Userdata, Geometry, Offset ); -} - -// adds supplied vertex data at the end of specified chunk -bool opengl_renderer::Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) -{ - - return m_geometry.append( Vertices, Userdata, Geometry ); -} - -// provides direct access to vertex data of specfied chunk -gfx::index_array const & -opengl_renderer::Indices( gfx::geometry_handle const &Geometry ) const { - - return m_geometry.indices( Geometry ); -} - -// provides direct access to vertex data of specfied chunk -gfx::vertex_array const & -opengl_renderer::Vertices( gfx::geometry_handle const &Geometry ) const { - - return m_geometry.vertices( Geometry ); -} - -gfx::userdata_array const &opengl_renderer::UserData(const gfx::geometry_handle &Geometry) const -{ - return m_geometry.userdata( Geometry ); -} - -// material methods -material_handle -opengl_renderer::Fetch_Material( std::string const &Filename, bool const Loadnow ) { - - return m_materials.create( Filename, Loadnow ); -} - -void -opengl_renderer::Bind_Material( material_handle const Material, TSubModel const *sm, lighting_data const *lighting ) { - - if( Material != null_handle ) { - - auto const &material { m_materials.material( Material ) }; - - if( false == material.is_good ) { - // use failure indicator instead - if( Material != m_invalid_material ) { - Bind_Material( m_invalid_material ); - } - return; - } - - // materials which declare texture variants provide a different texture set per model instance, - // picked from the seed of the rendered instance and the variant group of the submodel - auto const &materialtextures { - material.texture_variants.empty() ? - material.textures : - material.GetTextures( TSubModel::VariantId( sm ) ) }; - - if( false == Global.BasicRenderer ) { - m_textures.bind( m_normaltextureunit, materialtextures[1] ); - } - m_textures.bind( m_diffusetextureunit, materialtextures[0] ); - } - else { - // null material, unbind all textures - if( false == Global.BasicRenderer ) { - m_textures.bind( m_normaltextureunit, null_handle ); - } - m_textures.bind( m_diffusetextureunit, null_handle ); - } -} - -IMaterial const * -opengl_renderer::Material( material_handle const Material ) const { - - return &m_materials.material( Material ); -} - -opengl_material const & -opengl_renderer::Material( TSubModel const * Submodel ) const { - - return m_materials.material( submodel_material( Submodel ) ); -} - -// shader methods -std::shared_ptr -opengl_renderer::Fetch_Shader( std::string const &name ) { - - return std::shared_ptr(); -} - -// texture methods -void -opengl_renderer::select_unit( GLint const Textureunit ) { - - return m_textures.unit( Textureunit ); -} - -texture_handle -opengl_renderer::Fetch_Texture( std::string const &Filename, bool const Loadnow, GLint format_hint ) { - - return m_textures.create( Filename, Loadnow, GL_RGBA ); -} - -void -opengl_renderer::Bind_Texture( texture_handle const Texture ) { - - m_textures.bind( m_diffusetextureunit, Texture ); -} - -void -opengl_renderer::Bind_Texture( std::size_t const Unit, texture_handle const Texture ) { - - m_textures.bind( Unit, Texture ); -} - -ITexture & -opengl_renderer::Texture( texture_handle const Texture ) { - - return m_textures.texture( Texture ); -} - -ITexture const & -opengl_renderer::Texture( texture_handle const Texture ) const { - - return m_textures.texture( Texture ); -} - -void -opengl_renderer::Pick_Control_Callback( std::function Callback ) { - - m_control_pick_requests.emplace_back( Callback ); -} - -void -opengl_renderer::Pick_Node_Callback( std::function Callback ) { - - m_node_pick_requests.emplace_back( Callback ); -} - -void -opengl_renderer::Render( scene::basic_region *Region ) { - - m_sectionqueue.clear(); - m_cellqueue.clear(); - // build a list of region sections to render - glm::vec3 const cameraposition { m_renderpass.camera.position() }; - auto const camerax = static_cast( std::floor( cameraposition.x / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2 ) ); - auto const cameraz = static_cast( std::floor( cameraposition.z / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2 ) ); - float section_range = m_renderpass.draw_range * Global.fDistanceFactor; - section_range = editor_track_view_range( section_range, EditorModeFlag && ( m_renderpass.draw_mode == rendermode::color || m_renderpass.draw_mode == rendermode::pickscenery ) ); - int const segmentcount = 2 * static_cast( std::ceil( section_range / scene::EU07_SECTIONSIZE ) ); - int const originx = camerax - segmentcount / 2; - int const originz = cameraz - segmentcount / 2; - - for( int row = originz; row <= originz + segmentcount; ++row ) { - if( row < 0 ) { continue; } - if( row >= scene::EU07_REGIONSIDESECTIONCOUNT ) { break; } - for( int column = originx; column <= originx + segmentcount; ++column ) { - if( column < 0 ) { continue; } - if( column >= scene::EU07_REGIONSIDESECTIONCOUNT ) { break; } - auto *section { Region->m_sections[ row * scene::EU07_REGIONSIDESECTIONCOUNT + column ] }; - if( ( section != nullptr ) - && ( m_renderpass.camera.visible( section->m_area ) ) ) { - m_sectionqueue.emplace_back( section ); - } - } - } - - switch( m_renderpass.draw_mode ) { - case rendermode::color: { - - Update_Lights( simulation::Lights ); - - Render( std::begin( m_sectionqueue ), std::end( m_sectionqueue ) ); - if( EditorModeFlag ) { - // when editor mode is active calculate world position of the cursor - // at this stage the z-buffer is filled with only ground geometry - Update_Mouse_Position(); - } - // draw queue is filled while rendering sections - Render( std::begin( m_cellqueue ), std::end( m_cellqueue ) ); - break; - } - case rendermode::shadows: - case rendermode::cabshadows: - case rendermode::pickscenery: { - // these render modes don't bother with lights - Render( std::begin( m_sectionqueue ), std::end( m_sectionqueue ) ); - // they can also skip queue sorting, as they only deal with opaque geometry - // NOTE: there's benefit from rendering front-to-back, but is it significant enough? TODO: investigate - Render( std::begin( m_cellqueue ), std::end( m_cellqueue ) ); - break; - } - case rendermode::reflections: { - Render( std::begin( m_sectionqueue ), std::end( m_sectionqueue ) ); - if( Global.reflectiontune.fidelity >= 1 ) { - Render(std::begin(m_cellqueue), std::end(m_cellqueue)); - } - break; - } - case rendermode::pickcontrols: - default: { - // no need to render anything ourside of the cab in control picking mode - break; - } - } -} - -void -opengl_renderer::Render( section_sequence::iterator First, section_sequence::iterator Last ) { - - switch( m_renderpass.draw_mode ) { - case rendermode::color: - case rendermode::reflections: { - - break; - } - case rendermode::shadows: - case rendermode::cabshadows: { - // experimental, for shadows render both back and front faces, to supply back faces of the 'forest strips' - ::glDisable( GL_CULL_FACE ); - break; } - case rendermode::pickscenery: { - // non-interactive scenery elements get neutral colour - ::glColor3fv( glm::value_ptr( colors::none ) ); - break; - } - default: { - break; } - } - - while( First != Last ) { - - auto *section = *First; - section->create_geometry(); - - // render shapes held by the section - switch( m_renderpass.draw_mode ) { - case rendermode::color: - case rendermode::reflections: - case rendermode::shadows: - case rendermode::cabshadows: - case rendermode::pickscenery: { - if( false == section->m_shapes.empty() ) { - // since all shapes of the section share center point we can optimize out a few calls here - ::glPushMatrix(); - auto const originoffset { section->m_area.center - m_renderpass.camera.position() }; - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - // render - for( auto const &shape : section->m_shapes ) { Render( shape, true ); } - // post-render cleanup - ::glPopMatrix(); - } - break; - } - case rendermode::pickcontrols: - default: { - break; - } - } - - // add the section's cells to the cell queue - switch( m_renderpass.draw_mode ) { - case rendermode::color: - case rendermode::shadows: - case rendermode::cabshadows: - case rendermode::pickscenery: { - // TBD, TODO: refactor into a method to reuse below? - for( auto &cell : section->m_cells ) { - if( ( true == cell.m_active ) - && ( m_renderpass.camera.visible( cell.m_area ) ) ) { - // store visible cells with content as well as their current distance, for sorting later - m_cellqueue.emplace_back( - glm::length2( m_renderpass.camera.position() - cell.m_area.center ), - &cell ); - } - } - break; - } - case rendermode::reflections: { - // we can skip filling the cell queue if reflections pass isn't going to use it - if( Global.reflectiontune.fidelity == 0 ) { break; } - for( auto &cell : section->m_cells ) { - if( ( true == cell.m_active ) && ( m_renderpass.camera.visible( cell.m_area ) ) ) { - // store visible cells with content as well as their current distance, for sorting later - m_cellqueue.emplace_back( glm::length2( m_renderpass.camera.position() - cell.m_area.center ), &cell ); - } - } - break; - } - case rendermode::pickcontrols: - default: { - break; - } - } - // proceed to next section - ++First; - } - - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: - case rendermode::cabshadows: { - // restore standard face cull mode - ::glEnable( GL_CULL_FACE ); - break; } - default: { - break; } - } -} - -void -opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator Last ) { - - // cache initial iterator for the second sweep - auto first { First }; - auto const scenery_limit{ static_cast( Global.BaseDrawRange ) * Global.fDistanceFactor + 250.0 }; - bool const far_paths{ EditorModeFlag && Global.editor_tracks && ( m_renderpass.draw_mode == rendermode::color || m_renderpass.draw_mode == rendermode::pickscenery ) }; - glm::dvec3 const camera{ m_renderpass.camera.position() }; - // first pass draws elements which we know are located in section banks, to reduce vbo switching - while( First != Last ) { - - auto *cell = First->second; - // przeliczenia animacji torów w sektorze - cell->RaAnimate( m_framestamp ); - bool const tracks_only{ editor_far_tracks_only( far_paths, cell, camera, scenery_limit ) }; - - switch( m_renderpass.draw_mode ) { - case rendermode::color: { - // since all shapes of the section share center point we can optimize out a few calls here - ::glPushMatrix(); - auto const originoffset { cell->m_area.center - m_renderpass.camera.position() }; - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - - // render - // opaque non-instanced shapes - if( false == tracks_only ) { - for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } - } - // tracks - // TODO: update after path node refactoring - Render( std::begin( cell->m_paths ), std::end( cell->m_paths ) ); -#ifdef EU07_USE_DEBUG_CULLING - // debug - ::glLineWidth( 2.f ); - float const width = cell->m_area.radius; - float const height = cell->m_area.radius * 0.2f; - glDisable( GL_LIGHTING ); - glDisable( GL_TEXTURE_2D ); - glColor3ub( 255, 128, 128 ); - glBegin( GL_LINE_LOOP ); - glVertex3f( -width, height, width ); - glVertex3f( -width, height, -width ); - glVertex3f( width, height, -width ); - glVertex3f( width, height, width ); - glEnd(); - glBegin( GL_LINE_LOOP ); - glVertex3f( -width, 0, width ); - glVertex3f( -width, 0, -width ); - glVertex3f( width, 0, -width ); - glVertex3f( width, 0, width ); - glEnd(); - glBegin( GL_LINES ); - glVertex3f( -width, height, width ); glVertex3f( -width, 0, width ); - glVertex3f( -width, height, -width ); glVertex3f( -width, 0, -width ); - glVertex3f( width, height, -width ); glVertex3f( width, 0, -width ); - glVertex3f( width, height, width ); glVertex3f( width, 0, width ); - glEnd(); - glColor3ub( 255, 255, 255 ); - glEnable( GL_TEXTURE_2D ); - glEnable( GL_LIGHTING ); - glLineWidth( 1.f ); -#endif - // post-render cleanup - ::glPopMatrix(); - - break; - } - case rendermode::reflections: - { - // since all shapes of the section share center point we can optimize out a few calls here - ::glPushMatrix(); - auto const originoffset{ cell->m_area.center - m_renderpass.camera.position() }; - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - - // render - // opaque non-instanced shapes - for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } - - // post-render cleanup - ::glPopMatrix(); - - break; - } - case rendermode::shadows: { - // since all shapes of the section share center point we can optimize out a few calls here - ::glPushMatrix(); - auto const originoffset { cell->m_area.center - m_renderpass.camera.position() }; - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - - // render - // opaque non-instanced shapes - for( auto const &shape : cell->m_shapesopaque ) { - Render( shape, false ); - } - // tracks - Render( std::begin( cell->m_paths ), std::end( cell->m_paths ) ); - - // post-render cleanup - ::glPopMatrix(); - - break; - } - case rendermode::cabshadows: { - // since all shapes of the section share center point we can optimize out a few calls here - ::glPushMatrix(); - auto const originoffset { cell->m_area.center - m_renderpass.camera.position() }; - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - - // render - // opaque non-instanced shapes - for( auto const &shape : cell->m_shapesopaque ) { - Render( shape, false ); - } - // NOTE: tracks aren't likely to cast shadows into the cab, so we skip them in this pass - - // post-render cleanup - ::glPopMatrix(); - - break; - } - case rendermode::pickscenery: { - // same procedure like with regular render, but editor-enabled nodes receive custom colour used for picking - // since all shapes of the section share center point we can optimize out a few calls here - ::glPushMatrix(); - auto const originoffset { cell->m_area.center - m_renderpass.camera.position() }; - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - // render - // opaque non-instanced shapes - // non-interactive scenery elements get neutral colour - if( false == tracks_only ) { - ::glColor3fv( glm::value_ptr( colors::none ) ); - for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); } - } - // tracks - for( auto *path : cell->m_paths ) { - ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); - Render( path ); - } - - // post-render cleanup - ::glPopMatrix(); - - break; - } - case rendermode::pickcontrols: - default: { - break; - } - } - - ++First; - } - // second pass draws elements with their own vbos - while( first != Last ) { - - auto const *cell = first->second; - - switch( m_renderpass.draw_mode ) { - case rendermode::color: - case rendermode::shadows: - case rendermode::cabshadows: { - if( editor_far_tracks_only( far_paths, cell, camera, scenery_limit ) ) - break; - // TBD, TODO: refactor in to a method to reuse in branch below? - // opaque parts of instanced models - for( auto *instance : cell->m_instancesopaque ) { - Render( instance ); - } - // opaque parts of vehicles - for( auto *path : cell->m_paths ) { - for( auto *dynamic : path->Dynamics ) { - Render( dynamic ); - } - } - // memcells - if( ( EditorModeFlag ) - && ( DebugModeFlag ) ) { - ::glPushAttrib( GL_ENABLE_BIT ); - ::glDisable( GL_TEXTURE_2D ); - ::glColor3f( 0.36f, 0.75f, 0.35f ); - for( auto *memorycell : cell->m_memorycells ) { - Render( memorycell ); - } - ::glPopAttrib(); - } - break; - } - case rendermode::reflections: { - if( Global.reflectiontune.fidelity >= 1 ) { - // opaque parts of instanced models - for( auto *instance : cell->m_instancesopaque ) { - Render( instance ); - } - } - if( Global.reflectiontune.fidelity >= 2 ) { - // opaque parts of vehicles - for( auto *path : cell->m_paths ) { - for( auto *dynamic : path->Dynamics ) { - Render( dynamic ); - } - } - } - break; - } - case rendermode::pickscenery: { - if( editor_far_tracks_only( far_paths, cell, camera, scenery_limit ) ) - break; - // opaque parts of instanced models - // same procedure like with regular render, but each node receives custom colour used for picking - for( auto *instance : cell->m_instancesopaque ) { - ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); - Render( instance ); - } - // memcells - if( ( EditorModeFlag ) - && ( DebugModeFlag ) ) { - for( auto *memorycell : cell->m_memorycells ) { - ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); - Render( memorycell ); - } - } - // vehicles aren't included in scenery picking for the time being - break; - } - case rendermode::pickcontrols: - default: { - break; - } - } - - ++first; - } -#ifdef EU07_USE_DEBUG_SOUNDEMITTERS - // sound emitters - if( DebugModeFlag ) { - switch( m_renderpass.draw_mode ) { - case rendermode::color: { - - ::glPushAttrib( GL_ENABLE_BIT ); - ::glDisable( GL_TEXTURE_2D ); - ::glColor3f( 0.36f, 0.75f, 0.35f ); - - for( auto const &audiosource : audio::renderer.m_sources ) { - - ::glPushMatrix(); - auto const position = audiosource.properties.location - m_renderpass.camera.position(); - ::glTranslated( position.x, position.y, position.z ); - - ::gluSphere( m_quadric, 0.1, 4, 2 ); - - ::glPopMatrix(); - } - - ::glPopAttrib(); - - break; - } - default: { - break; - } - } - } -#endif - -} - -void -opengl_renderer::Render( scene::shape_node const &Shape, bool const Ignorerange ) { - - auto const &data { Shape.data() }; - - if( false == Ignorerange ) { - double distancesquared; - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: - case rendermode::cabshadows: { - // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = glm::length2( ( data.area.center - Global.pCamera.Pos ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor; - break; - } - case rendermode::reflections: { - // reflection mode draws simplified version of the shapes, by artificially increasing view range - distancesquared = - // TBD, TODO: bind offset value with setting variable? - sq(EU07_REFLECTIONFIDELITYOFFSET) - // TBD: take into account distance multipliers? - + glm::length2( ( data.area.center - m_renderpass.camera.position() ) ) /* / Global.fDistanceFactor */; - break; - } - default: { - distancesquared = glm::length2( ( data.area.center - m_renderpass.camera.position() ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor; - break; - } - } - if( ( distancesquared < data.rangesquared_min ) - || ( distancesquared >= data.rangesquared_max ) ) { - return; - } - } - - // setup - Bind_Material( data.material ); - switch( m_renderpass.draw_mode ) { - case rendermode::color: - case rendermode::reflections: { - ::glColor3fv( glm::value_ptr( data.lighting.diffuse ) ); -/* - // NOTE: ambient component is set by diffuse component - // NOTE: for the time being non-instanced shapes are rendered without specular component due to wrong/arbitrary values set in legacy scenarios - // TBD, TODO: find a way to resolve this with the least amount of tears? - ::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( data.lighting.specular * m_sunlight.specular.a * m_specularopaquescalefactor ) ); -*/ - break; - } - // pick modes are painted with custom colours, and shadow pass doesn't use any - case rendermode::shadows: { - // skip if the shadow caster rank is too low for currently set threshold - if( Material( data.material )->GetShadowRank() > Global.gfx_shadow_rank_cutoff ) { - return; - } - } - [[ fallthrough ]]; - case rendermode::cabshadows: - case rendermode::pickscenery: - case rendermode::pickcontrols: - default: { - break; - } - } - // render - m_geometry.draw( data.geometry ); - // debug data - ++m_renderpass.draw_stats.shapes; - ++m_renderpass.draw_stats.drawcalls; -} - -void -opengl_renderer::Render( TAnimModel *Instance ) { - - if( false == Instance->m_visible ) { return; } - if( false == m_renderpass.camera.visible( Instance->m_area ) ) { return; } - - double distancesquared; - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: - case rendermode::cabshadows: { - // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = glm::length2((Instance->location() - m_renderpass.camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; - break; - } - case rendermode::reflections: { - // reflection mode draws simplified version of the shapes, by artificially increasing view range - // it also ignores zoom settings and distance multipliers - // TBD: take into account distance multipliers? - distancesquared = glm::length2( ( Instance->location() - m_renderpass.camera.position() ) ) /* / Global.fDistanceFactor */; - // NOTE: arbitrary draw range limit - if( distancesquared > Global.reflectiontune.range_instances * Global.reflectiontune.range_instances ) { - return; - } - // TBD, TODO: bind offset value with setting variable? - distancesquared += sq(EU07_REFLECTIONFIDELITYOFFSET); - break; - } - default: { - distancesquared = glm::length2((Instance->location() - m_renderpass.camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; - break; - } - } - if( ( distancesquared < Instance->m_rangesquaredmin ) - || ( distancesquared >= Instance->m_rangesquaredmax ) ) { - return; - } - // crude way to reject early items too far to affect the output (mostly relevant for shadow passes) - auto const drawdistancethreshold{ m_renderpass.draw_range + 250 }; - if( distancesquared > sq(drawdistancethreshold) ) { - return; - } - // second stage visibility cull, reject modelstoo far away to be noticeable - auto const radiussquared { Instance->radius() * Instance->radius() }; - if( radiussquared * Global.ZoomFactor / distancesquared < 0.003 * 0.003 ) { - return; - } - - switch( m_renderpass.draw_mode ) { - case rendermode::pickscenery: { - // add the node to the pick list - m_picksceneryitems.emplace_back( Instance ); - break; - } - default: { - break; - } - } - - Instance->RaAnimate( m_framestamp ); // jednorazowe przeliczenie animacji - Instance->RaPrepare(); - if( Instance->pModel ) { - // renderowanie rekurencyjne submodeli - Render( - Instance->pModel, - Instance->Material(), - distancesquared, - Instance->location() - m_renderpass.camera.position(), - Instance->vAngle ); - // debug data - ++m_renderpass.draw_stats.models; - } -} - -bool -opengl_renderer::Render( TDynamicObject *Dynamic ) { - - Dynamic->renderme = ( - ( Global.pCamera.m_owner == Dynamic && !FreeFlyModeFlag ) - || ( m_renderpass.camera.visible( Dynamic ) ) ); - - if( false == Dynamic->renderme ) { return false; } - - // lod visibility ranges are defined for base (x 1.0) viewing distance. for render we adjust them for actual range multiplier and zoom - float squaredistance; - glm::dvec3 const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: { - squaredistance = glm::length2( glm::vec3{ glm::dvec3{ Dynamic->vPosition - Global.pCamera.Pos } } / Global.ZoomFactor ); - if( false == FreeFlyModeFlag ) { - // filter out small details if we're in vehicle cab - squaredistance = std::max( sq(100.f), squaredistance ); - } - break; - } - case rendermode::cabshadows: { - squaredistance = glm::length2( glm::vec3{ glm::dvec3{ Dynamic->vPosition - Global.pCamera.Pos } } / Global.ZoomFactor ); - // filter out small details - squaredistance = std::max( sq(100.f), squaredistance ); - break; - } - case rendermode::reflections: { - // reflection mode draws simplified version of the shapes, by artificially increasing view range - // it also ignores zoom settings and distance multipliers - squaredistance = - std::max( - sq(100.f), - // TBD: take into account distance multipliers? - glm::length2( glm::vec3{ originoffset } ) /* / Global.fDistanceFactor */ ); - // NOTE: arbitrary draw range limit - if( squaredistance > Global.reflectiontune.range_vehicles * Global.reflectiontune.range_vehicles ) { - return false; - } - // TBD, TODO: bind offset value with setting variable? - // NOTE: combined 'squared' distance doesn't equal actual squared (distance + offset) but, eh - squaredistance += sq(EU07_REFLECTIONFIDELITYOFFSET); - break; - } - default: { - squaredistance = glm::length2( glm::vec3{ originoffset } / Global.ZoomFactor ); - // TODO: filter out small details based on fidelity setting - break; - } - } - // crude way to reject early items too far to affect the output (mostly relevant for shadow and reflection passes) - auto const drawdistancethreshold{ m_renderpass.draw_range + 250 }; - if( squaredistance > sq(drawdistancethreshold) ) { - return false; - } - // second stage visibility cull, reject vehicles too far away to be noticeable - Dynamic->renderme = ( Dynamic->radius() * Global.ZoomFactor / std::sqrt( squaredistance ) > 0.003 ); - if( false == Dynamic->renderme ) { - return false; - } - - // debug data - ++m_renderpass.draw_stats.dynamics; - - - // setup - TSubModel::iInstance = reinterpret_cast( Dynamic ); //żeby nie robić cudzych animacji - TSubModel::iVariantSeed = Dynamic->variant_seed(); - Dynamic->ABuLittleUpdate( squaredistance ); // ustawianie zmiennych submodeli dla wspólnego modelu - ::glPushMatrix(); - - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - ::glMultMatrixd( glm::value_ptr(Dynamic->mMatrix) ); - - switch( m_renderpass.draw_mode ) { - - case rendermode::color: { - if( Dynamic->fShade > 0.0f ) { - // change light level based on light level of the occupied track - m_sunlight.apply_intensity( Dynamic->fShade ); - } - m_renderspecular = true; // vehicles are rendered with specular component. static models without, at least for the time being - // render - if( Dynamic->mdLowPolyInt ) { - Render_lowpoly( Dynamic, squaredistance, false ); - } - else { - // HACK: if the model lacks low poly interior, we presume the load is placed outside - if( Dynamic->mdLoad ) { - // renderowanie nieprzezroczystego ładunku - Render( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); - } - } - if( Dynamic->mdModel ) { - // main model - Render( Dynamic->mdModel, Dynamic->Material(), squaredistance ); - } - // optional attached models - for( auto *attachment : Dynamic->mdAttachments ) { - Render( attachment, Dynamic->Material(), squaredistance ); - } - // optional coupling adapters - Render_coupler_adapter( Dynamic, squaredistance, end::front ); - Render_coupler_adapter( Dynamic, squaredistance, end::rear ); - // post-render cleanup - m_renderspecular = false; - if( Dynamic->fShade > 0.0f ) { - // restore regular light level - m_sunlight.apply_intensity(); - } - break; - } - case rendermode::shadows: - case rendermode::cabshadows: { - if( Dynamic->mdLowPolyInt ) { - // low poly interior -// if( FreeFlyModeFlag ? true : !Dynamic->mdKabina || !Dynamic->bDisplayCab ) { - Render( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance ); -// } - } - if( Dynamic->mdModel ) { - // main model - Render( Dynamic->mdModel, Dynamic->Material(), squaredistance ); - } - // optional attached models - for( auto *attachment : Dynamic->mdAttachments ) { - Render( attachment, Dynamic->Material(), squaredistance ); - } - // optional coupling adapters - Render_coupler_adapter( Dynamic, squaredistance, end::front ); - Render_coupler_adapter( Dynamic, squaredistance, end::rear ); - if( Dynamic->mdLoad ) { - // renderowanie nieprzezroczystego ładunku - Render( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); - } - // post-render cleanup - break; - } - case rendermode::pickcontrols: { - if( Dynamic->mdLowPolyInt ) { - // low poly interior - Render( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance ); - } - break; - } - case rendermode::pickscenery: - default: { - break; - } - } - - ::glPopMatrix(); - - // TODO: check if this reset is needed. In theory each object should render all parts based on its own instance data anyway? - if( Dynamic->btnOn ) - Dynamic->TurnOff(); // przywrócenie domyślnych pozycji submodeli - - return true; -} - -// rendering kabiny gdy jest oddzielnym modelem i ma byc wyswietlana -bool -opengl_renderer::Render_cab( TDynamicObject const *Dynamic, float const Lightlevel, bool const Alpha ) { - - if( Dynamic == nullptr ) { - - TSubModel::iInstance = 0; - TSubModel::iVariantSeed = 0; - return false; - } - - TSubModel::iInstance = reinterpret_cast( Dynamic ); - TSubModel::iVariantSeed = Dynamic->variant_seed(); - - if( ( true == FreeFlyModeFlag ) - || ( false == Dynamic->bDisplayCab ) - || ( Dynamic->mdKabina == Dynamic->mdModel ) ) { - // ABu: Rendering kabiny jako ostatniej, zeby bylo widac przez szyby, tylko w widoku ze srodka - return false; - } - - if( Dynamic->mdKabina ) { // bo mogła zniknąć przy przechodzeniu do innego pojazdu - // setup shared by all render paths - ::glPushMatrix(); - - auto const originoffset = Dynamic->GetPosition() - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - ::glMultMatrixd( glm::value_ptr(Dynamic->mMatrix) ); - - switch( m_renderpass.draw_mode ) { - case rendermode::color: { - // render path specific setup: - if( Dynamic->fShade > 0.0f ) { - // change light level based on light level of the occupied track - m_sunlight.apply_intensity( Dynamic->fShade ); - } - if( Lightlevel > 0.f ) { - // crude way to light the cabin, until we have something more complete in place - auto const luminance { Global.fLuminance * ( Dynamic->fShade > 0.0f ? Dynamic->fShade : 1.0f ) }; - ::glLightModelfv( - GL_LIGHT_MODEL_AMBIENT, - glm::value_ptr( - glm::vec3( m_baseambient ) - + ( Dynamic->InteriorLight * Lightlevel ) * static_cast( std::clamp( 1.25 - luminance, 0.0, 1.0 ) ) ) ); - } - // render - if( true == Alpha ) { - // translucent parts - Render_Alpha( Dynamic->mdKabina, Dynamic->Material(), 0.0 ); - } - else { - // opaque parts - Render( Dynamic->mdKabina, Dynamic->Material(), 0.0 ); - } - // post-render restore - if( Dynamic->fShade > 0.0f ) { - // change light level based on light level of the occupied track - m_sunlight.apply_intensity(); - } - if( Lightlevel > 0.f ) { - // reset the overall ambient - ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr(m_baseambient) ); - } - break; - } - case rendermode::cabshadows: { - // cab shadowmap mode skips lighting setup and translucent parts - // render - if( true == Alpha ) { - // translucent parts - Render_Alpha( Dynamic->mdKabina, Dynamic->Material(), 0.0 ); - } - else { - // opaque parts - Render( Dynamic->mdKabina, Dynamic->Material(), 0.0 ); - } - // since the setup is simpler, there's nothing to reset afterwards - break; - } - case rendermode::pickcontrols: { - // control picking mode skips lighting setup and translucent parts - // render - Render( Dynamic->mdKabina, Dynamic->Material(), 0.0 ); - // since the setup is simpler, there's nothing to reset afterwards - break; - } - default: { - break; - } - } - // post-render restore - ::glPopMatrix(); - } - - return true; -} - -bool -opengl_renderer::Render( TModel3d *Model, material_data const *Material, float const Squaredistance ) { - - auto alpha = - ( Material != nullptr ? - Material->textures_alpha : - 0x30300030 ); - alpha ^= 0x0F0F000F; // odwrócenie flag tekstur, aby wyłapać nieprzezroczyste - if( 0 == ( alpha & Model->iFlags & 0x1F1F001F ) ) { - // czy w ogóle jest co robić w tym cyklu? - return false; - } - - Model->Root->fSquareDist = Squaredistance; // zmienna globalna! - - // setup - Model->Root->ReplacableSet( - ( Material != nullptr ? - Material->replacable_skins : - nullptr ), - alpha ); - - Model->Root->pRoot = Model; - - // render - Render( Model->Root ); - - // post-render cleanup - - return true; -} - -bool -opengl_renderer::Render( TModel3d *Model, material_data const *Material, float const Squaredistance, glm::dvec3 const &Position, glm::vec3 const &Angle ) { - - ::glPushMatrix(); - ::glTranslated( Position.x, Position.y, Position.z ); - if( Angle.y != 0.0 ) - ::glRotatef( Angle.y, 0.f, 1.f, 0.f ); - if( Angle.x != 0.0 ) - ::glRotatef( Angle.x, 1.f, 0.f, 0.f ); - if( Angle.z != 0.0 ) - ::glRotatef( Angle.z, 0.f, 0.f, 1.f ); - - auto const result = Render( Model, Material, Squaredistance ); - - ::glPopMatrix(); - - return result; -} - -void -opengl_renderer::Render( TSubModel *Submodel ) { - - if( ( Submodel->iVisible ) - && ( TSubModel::fSquareDist >= Submodel->fSquareMinDist ) - && ( TSubModel::fSquareDist < Submodel->fSquareMaxDist ) ) { - - if( Submodel->iFlags & 0xC000 ) { - ::glPushMatrix(); - if( Submodel->fMatrix ) - ::glMultMatrixf( Submodel->fMatrix->readArray() ); - if( Submodel->b_Anim != TAnimType::at_None ) - Submodel->RaAnimation( Submodel->b_Anim ); - } - - if( Submodel->eType < TP_ROTATOR ) { - // renderowanie obiektów OpenGL - if( Submodel->iAlpha & Submodel->iFlags & 0x1F ) { - // rysuj gdy element nieprzezroczysty - - // debug data - ++m_renderpass.draw_stats.submodels; - ++m_renderpass.draw_stats.drawcalls; - - switch( m_renderpass.draw_mode ) { - case rendermode::color: - case rendermode::reflections: { -// NOTE: code disabled as normalization marking doesn't take into account scaling propagation down hierarchy chains -// for the time being we'll do with enforced worst-case scaling method, when speculars are enabled -#ifdef EU07_USE_OPTIMIZED_NORMALIZATION - switch( Submodel->m_normalizenormals ) { - case TSubModel::normalize: { - ::glEnable( GL_NORMALIZE ); break; } - case TSubModel::rescale: { - ::glEnable( GL_RESCALE_NORMAL ); break; } - default: { - break; } - } -#else - if( true == m_renderspecular ) { - ::glEnable( GL_NORMALIZE ); - } -#endif - // material configuration: - auto const material{ submodel_material( Submodel ) }; // zmienialne skóry, również 0 - // textures... - Bind_Material( material ); - // ...colors and opacity... - auto const opacity { std::clamp( Material( material )->get_or_guess_opacity(), 0.f, 1.f ) }; - if( Submodel->fVisible < 1.f ) { - // setup - ::glAlphaFunc( GL_GREATER, 0.f ); - ::glEnable( GL_BLEND ); - ::glColor4f( - Submodel->f4Diffuse.r, - Submodel->f4Diffuse.g, - Submodel->f4Diffuse.b, - Submodel->fVisible ); - } - else { - ::glColor3fv( glm::value_ptr( Submodel->f4Diffuse ) ); // McZapkie-240702: zamiast ub - if( ( opacity != 0.f ) - && ( opacity != EU07_OPACITYDEFAULT ) ) { - ::glAlphaFunc( GL_GREATER, opacity ); - } - } - // ...specular... - if( ( true == m_renderspecular ) && ( m_sunlight.specular.a > 0.01f ) ) { - // specular strength in legacy models is set uniformly to 150, 150, 150 so we scale it down for opaque elements - ::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( Submodel->f4Specular * m_sunlight.specular.a * m_specularopaquescalefactor ) ); - ::glEnable( GL_RESCALE_NORMAL ); - } - // ...luminance - auto const unitstate = m_unitstate; - auto const isemissive { ( Submodel->f4Emision.a > 0.f ) && ( Global.fLuminance < Submodel->fLight ) }; - if( isemissive ) { - // zeby swiecilo na kolorowo - ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( /* Submodel->f4Emision */ Submodel->f4Diffuse * Submodel->f4Emision.a ) ); - // disable shadows so they don't obstruct self-lit items -/* - setup_shadow_color( colors::white ); -*/ - switch_units( unitstate.diffuse, false, false ); - } - - // main draw call - m_geometry.draw( Submodel->m_geometry.handle ); -/* - if( DebugModeFlag ) { - auto const & vertices { m_geometry.vertices( Submodel->m_geometry ) }; - ::glBegin( GL_LINES ); - for( auto const &vertex : vertices ) { - ::glVertex3fv( glm::value_ptr( vertex.position ) ); - ::glVertex3fv( glm::value_ptr( vertex.position + vertex.normal * 0.2f ) ); - } - ::glEnd(); - } -*/ - // post-draw reset - if( Submodel->fVisible < 1.f ) { - ::glAlphaFunc( GL_GREATER, EU07_OPACITYDEFAULT ); - ::glDisable( GL_BLEND ); - } - else if( ( opacity != 0.f ) - && ( opacity != EU07_OPACITYDEFAULT ) ) { - ::glAlphaFunc( GL_GREATER, EU07_OPACITYDEFAULT ); - } - if( ( true == m_renderspecular ) && ( m_sunlight.specular.a > 0.01f ) ) { - ::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( colors::none ) ); - } - if( isemissive ) { - // restore default (lack of) brightness - ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( colors::none ) ); -/* - setup_shadow_color( m_shadowcolor ); -*/ - switch_units( unitstate.diffuse, unitstate.shadows, unitstate.reflections ); - } -#ifdef EU07_USE_OPTIMIZED_NORMALIZATION - switch( Submodel->m_normalizenormals ) { - case TSubModel::normalize: { - ::glDisable( GL_NORMALIZE ); break; } - case TSubModel::rescale: { - ::glDisable( GL_RESCALE_NORMAL ); break; } - default: { - break; } - } -#else - if( true == m_renderspecular ) { - ::glDisable( GL_NORMALIZE ); - } -#endif - break; - } - case rendermode::shadows: { - // skip if the shadow caster rank is too low for currently set threshold - // or if the submodel is hidden (colour pass only fades it out, e.g. inactive seasonal variant) - if( ( Material( Submodel ).shadow_rank > Global.gfx_shadow_rank_cutoff ) - || ( Submodel->fVisible <= 0.f ) ) - { - --m_renderpass.draw_stats.submodels; - --m_renderpass.draw_stats.drawcalls; - break; - } - } - [[fallthrough]]; - case rendermode::cabshadows: - case rendermode::pickscenery: { - // scenery picking and shadow both use enforced colour and no frills - // material configuration: - // textures... - if( Submodel->m_material < 0 ) { // zmienialne skóry - Bind_Material( submodel_material( Submodel ), Submodel ); - } - else { - // również 0 - Bind_Material( Submodel->m_material, Submodel ); - } - // main draw call - m_geometry.draw( Submodel->m_geometry.handle ); - // post-draw reset - break; - } - case rendermode::pickcontrols: { - // material configuration: - // control picking applies individual colour for each submodel - m_pickcontrolsitems.emplace_back( Submodel ); - ::glColor3fv( glm::value_ptr( pick_color( m_pickcontrolsitems.size() ) ) ); - // textures... - if( Submodel->m_material < 0 ) { // zmienialne skóry - Bind_Material( submodel_material( Submodel ), Submodel ); - } - else { - // również 0 - Bind_Material( Submodel->m_material, Submodel ); - } - // main draw call - m_geometry.draw( Submodel->m_geometry.handle ); - // post-draw reset - break; - } - default: { - break; - } - } - } - } - else if( Submodel->eType == TP_FREESPOTLIGHT ) { - - switch( m_renderpass.draw_mode ) { - // spotlights are only rendered in colour mode(s) - case rendermode::color: - case rendermode::reflections: { - auto const &modelview = OpenGLMatrices.data( GL_MODELVIEW ); - auto const lightcenter = - modelview - * glm::mix( - glm::vec4( 0.f, 0.f, -0.05f, 1.f ), - glm::vec4( 0.f, 0.f, -0.25f, 1.f ), - static_cast( TSubModel::fSquareDist / Submodel->fSquareMaxDist ) ); // pozycja punktu świecącego względem kamery - Submodel->fCosViewAngle = glm::dot( glm::normalize( modelview * glm::vec4( 0.f, 0.f, -1.f, 1.f ) - lightcenter ), glm::normalize( -lightcenter ) ); - - if( Submodel->fCosViewAngle > Submodel->fCosFalloffAngle ) { - // kąt większy niż maksymalny stożek swiatła - float lightlevel = 1.f; // TODO, TBD: parameter to control light strength - // view angle attenuation - float const anglefactor = std::clamp( - ( Submodel->fCosViewAngle - Submodel->fCosFalloffAngle ) / ( Submodel->fCosHotspotAngle - Submodel->fCosFalloffAngle ), - 0.f, 1.f ); - lightlevel *= anglefactor; - // distance attenuation. NOTE: since it's fixed pipeline with built-in gamma correction we're using linear attenuation - // we're capping how much effect the distance attenuation can have, otherwise the lights get too tiny at regular distances - float const distancefactor { std::max( 0.5f, ( Submodel->fSquareMaxDist - TSubModel::fSquareDist ) / Submodel->fSquareMaxDist ) }; - auto const pointsize { std::max( 3.f, 5.f * distancefactor * anglefactor ) }; - - // additionally reduce light strength for farther sources in rain or snow - if( Global.Overcast > 0.75f ) { - float const precipitationfactor{ - std::lerp( - std::lerp( 1.f, 0.25f, std::clamp( Global.Overcast * 0.75f - 0.5f, 0.f, 1.f ) ), - 1.f, - distancefactor ) }; - lightlevel *= precipitationfactor; - } - - if( lightlevel > 0.f ) { - // material configuration: - Bind_Material( null_handle ); - // limit impact of dense fog on the lights - auto const lightrange { std::max( 500, m_fogrange * 2 ) }; // arbitrary, visibility at least 500m - ::glFogf( GL_FOG_DENSITY, static_cast( 1.0 / lightrange ) ); - - ::glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT ); - ::glDisable( GL_LIGHTING ); - ::glEnable( GL_BLEND ); - ::glAlphaFunc( GL_GREATER, 0.f ); - - ::glPushMatrix(); - ::glLoadIdentity(); - ::glTranslatef( lightcenter.x, lightcenter.y, lightcenter.z ); // początek układu zostaje bez zmian - - auto const unitstate = m_unitstate; - switch_units( m_unitstate.diffuse, false, false ); - - glm::vec3 const lightcolor { - Submodel->DiffuseOverride.r < 0.f ? // -1 indicates no override - glm::vec3( Submodel->f4Diffuse ) : - Submodel->DiffuseOverride }; - - // main draw call - if( Global.Overcast > 1.f ) { - // fake fog halo - float const fogfactor { - std::lerp( - 2.f, 1.f, - std::clamp( Global.fFogEnd / 2000, 0.f, 1.f ) ) - * std::max( 1.f, Global.Overcast ) }; - - ::glPointSize( pointsize * fogfactor ); - ::glColor4f( - lightcolor.r, - lightcolor.g, - lightcolor.b, - Submodel->fVisible * std::min( 1.f, lightlevel ) * 0.5f ); - ::glDepthMask( GL_FALSE ); - m_geometry.draw( Submodel->m_geometry.handle ); - ::glDepthMask( GL_TRUE ); - } - ::glPointSize( pointsize ); - ::glColor4f( - lightcolor.r, - lightcolor.g, - lightcolor.b, - Submodel->fVisible * std::min( 1.f, lightlevel ) ); - m_geometry.draw( Submodel->m_geometry.handle ); - - // post-draw reset - switch_units( unitstate.diffuse, unitstate.shadows, unitstate.reflections ); - - ::glPopMatrix(); - ::glPopAttrib(); - ::glFogf( GL_FOG_DENSITY, static_cast( 1.0 / m_fogrange ) ); - } - } - break; - } - default: { - break; - } - } - } - else if( Submodel->eType == TP_STARS ) { - - switch( m_renderpass.draw_mode ) { - // colour points are only rendered in colour mode(s) - case rendermode::color: - case rendermode::reflections: { - if( Global.fLuminance < Submodel->fLight ) { - - // material configuration: - ::glPushAttrib( GL_ENABLE_BIT ); - - Bind_Material( null_handle ); - ::glDisable( GL_LIGHTING ); - - // main draw call - m_geometry.draw( Submodel->m_geometry.handle, gfx::color_streams ); - - // post-draw reset - ::glPopAttrib(); - } - break; - } - default: { - break; - } - } - } - if( Submodel->Child != nullptr ) - if( Submodel->iAlpha & Submodel->iFlags & 0x001F0000 ) - Render( Submodel->Child ); - - if( Submodel->iFlags & 0xC000 ) - ::glPopMatrix(); - } -/* - if( Submodel->b_Anim < at_SecondsJump ) - Submodel->b_Anim = at_None; // wyłączenie animacji dla kolejnego użycia subm -*/ - if( Submodel->Next ) - if( Submodel->iAlpha & Submodel->iFlags & 0x1F000000 ) - Render( Submodel->Next ); // dalsze rekurencyjnie -} - -void -opengl_renderer::Render( TTrack *Track ) { - - if( ( Track->m_material1 == 0 ) - && ( Track->m_material2 == 0 ) - && ( ( Track->eType != tt_Switch ) - || ( Track->SwitchExtension->m_material3 == 0 ) ) ) { - return; - } - if( false == Track->m_visible ) { - return; - } - - ++m_renderpass.draw_stats.paths; - ++m_renderpass.draw_stats.drawcalls; - - switch( m_renderpass.draw_mode ) { - // single path pieces are rendererd in pick scenery mode only - case rendermode::pickscenery: { - // add the node to the pick list - m_picksceneryitems.emplace_back( Track ); - - if( Track->m_material1 != 0 ) { - Bind_Material( Track->m_material1 ); - m_geometry.draw( std::begin( Track->Geometry1 ), std::end( Track->Geometry1 ) ); - } - if( Track->m_material2 != 0 ) { - Bind_Material( Track->m_material2 ); - m_geometry.draw( std::begin( Track->Geometry2 ), std::end( Track->Geometry2 ) ); - } - if( ( Track->eType == tt_Switch ) - && ( Track->SwitchExtension->m_material3 != 0 ) ) { - Bind_Material( Track->SwitchExtension->m_material3 ); - m_geometry.draw( Track->SwitchExtension->Geometry3 ); - } - break; - } - default: { - break; - } - } -} - -// experimental, does track rendering in two passes, to take advantage of reduced texture switching -void -opengl_renderer::Render( scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last ) { - - ::glColor3fv( glm::value_ptr( colors::white ) ); - // setup - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: - case rendermode::cabshadows: { - // NOTE: roads-based platforms tend to miss parts of shadows if rendered with either back or front culling - ::glDisable( GL_CULL_FACE ); - break; - } - default: { - break; - } - } - - // TODO: render auto generated trackbeds together with regular trackbeds in pass 1, and all rails in pass 2 - // first pass, material 1 - for( auto first { First }; first != Last; ++first ) { - - auto const track { *first }; - - if( track->m_material1 == 0 ) { - continue; - } - if( false == track->m_visible ) { - continue; - } - - ++m_renderpass.draw_stats.paths; - ++m_renderpass.draw_stats.drawcalls; - - switch( m_renderpass.draw_mode ) { - case rendermode::color: - case rendermode::reflections: { - if( track->eEnvironment != e_flat ) { - setup_environment_light( track->eEnvironment ); - } - Bind_Material( track->m_material1 ); - m_geometry.draw( std::begin( track->Geometry1 ), std::end( track->Geometry1 ) ); - if( track->eEnvironment != e_flat ) { - // restore default lighting - setup_environment_light(); - } - break; - } - case rendermode::shadows: - case rendermode::cabshadows: { - if( ( std::abs( track->fTexHeight1 ) < 0.35f ) - || ( track->iCategoryFlag != 2 ) ) { - // shadows are only calculated for high enough roads, typically meaning track platforms - continue; - } - if( Material( track->m_material1 )->GetShadowRank() > Global.gfx_shadow_rank_cutoff ) - { - // skip if the shadow caster rank is too low for currently set threshold - continue; - } - Bind_Material( track->m_material1 ); - m_geometry.draw( std::begin( track->Geometry1 ), std::end( track->Geometry1 ) ); - break; - } - case rendermode::pickscenery: // pick scenery mode uses piece-by-piece approach - case rendermode::pickcontrols: - default: { - break; - } - } - } - // second pass, material 2 - for( auto first { First }; first != Last; ++first ) { - - auto const track { *first }; - - if( track->m_material2 == 0 ) { - continue; - } - if( false == track->m_visible ) { - continue; - } - - switch( m_renderpass.draw_mode ) { - case rendermode::color: - case rendermode::reflections: { - if( track->eEnvironment != e_flat ) { - setup_environment_light( track->eEnvironment ); - } - Bind_Material( track->m_material2 ); - m_geometry.draw( std::begin( track->Geometry2 ), std::end( track->Geometry2 ) ); - if( track->eEnvironment != e_flat ) { - // restore default lighting - setup_environment_light(); - } - break; - } - case rendermode::shadows: - case rendermode::cabshadows: { - if( ( std::abs( track->fTexHeight1 ) < 0.35f ) - || ( ( track->iCategoryFlag == 1 ) - && ( track->eType != tt_Normal ) ) ) { - // shadows are only calculated for high enough trackbeds - continue; - } - if( Material( track->m_material2 )->GetShadowRank() > Global.gfx_shadow_rank_cutoff ) - { - // skip if the shadow caster rank is too low for currently set threshold - continue; - } - Bind_Material( track->m_material2 ); - m_geometry.draw( std::begin( track->Geometry2 ), std::end( track->Geometry2 ) ); - break; - } - case rendermode::pickscenery: // pick scenery mode uses piece-by-piece approach - case rendermode::pickcontrols: - default: { - break; - } - } - } - // third pass, material 3 - for( auto first { First }; first != Last; ++first ) { - - auto const track { *first }; - - if( track->eType != tt_Switch ) { - continue; - } - if( track->SwitchExtension->m_material3 == 0 ) { - continue; - } - if( false == track->m_visible ) { - continue; - } - - switch( m_renderpass.draw_mode ) { - case rendermode::color: - case rendermode::reflections: { - if( track->eEnvironment != e_flat ) { - setup_environment_light( track->eEnvironment ); - } - Bind_Material( track->SwitchExtension->m_material3 ); - m_geometry.draw( track->SwitchExtension->Geometry3 ); - if( track->eEnvironment != e_flat ) { - // restore default lighting - setup_environment_light(); - } - break; - } - case rendermode::shadows: - case rendermode::cabshadows: { - if( ( std::abs( track->fTexHeight1 ) < 0.35f ) - || ( ( track->iCategoryFlag == 1 ) - && ( track->eType != tt_Normal ) ) ) { - // shadows are only calculated for high enough trackbeds - continue; - } - if( Material( track->SwitchExtension->m_material3 )->GetShadowRank() > Global.gfx_shadow_rank_cutoff ) - { - // skip if the shadow caster rank is too low for currently set threshold - continue; - } - Bind_Material( track->SwitchExtension->m_material3 ); - m_geometry.draw( track->SwitchExtension->Geometry3 ); - break; - } - case rendermode::pickscenery: // pick scenery mode uses piece-by-piece approach - case rendermode::pickcontrols: - default: { - break; - } - } - } - // post-render reset - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: - case rendermode::cabshadows: { - ::glEnable( GL_CULL_FACE ); - break; - } - default: { - break; - } - } -} - -void -opengl_renderer::Render( TMemCell *Memcell ) { - - ::glPushMatrix(); - auto const position = Memcell->location() - m_renderpass.camera.position(); - ::glTranslated( position.x, position.y + 0.5, position.z ); - - switch( m_renderpass.draw_mode ) { - case rendermode::color: - case rendermode::shadows: - case rendermode::cabshadows: { - //::gluSphere( m_quadric, 0.35, 4, 2 ); - break; - } - case rendermode::pickscenery: { - // add the node to the pick list - m_picksceneryitems.emplace_back( Memcell ); - - //::gluSphere( m_quadric, 0.35, 4, 2 ); - break; - } - case rendermode::reflections: - case rendermode::pickcontrols: { - break; - } - default: { - break; - } - } - - ::glPopMatrix(); -} - -void -opengl_renderer::Render_particles() { - - switch_units( true, false, false ); - - Bind_Material( null_handle ); // TODO: bind smoke texture - - // TBD: leave lighting on to allow vehicle lights to affect it? - ::glDisable( GL_LIGHTING ); - // momentarily disable depth write, to allow vehicle cab drawn afterwards to mask it instead of leaving it 'inside' - ::glDepthMask( GL_FALSE ); - - Bind_Texture( m_smoketexture ); - m_renderpass.draw_stats.particles = m_particlerenderer.render( m_diffusetextureunit ); - if( Global.bUseVBO ) { - gfx::opengl_vbogeometrybank::reset(); - } - ::glDepthMask( GL_TRUE ); - ::glEnable( GL_LIGHTING ); -} - -void -opengl_renderer::Render_precipitation() { - - if( Global.Overcast <= 1.f ) { return; } - - switch_units( true, false, false ); - -// ::glColor4fv( glm::value_ptr( glm::vec4( glm::min( glm::vec3( Global.fLuminance ), glm::vec3( 1 ) ), 1 ) ) ); - ::glColor4fv( - glm::value_ptr( - glm::mix( - 0.5f * ( Global.DayLight.diffuse + Global.DayLight.ambient ), - colors::white, - 0.5f * std::clamp( (float)Global.fLuminance, 0.f, 1.f ) ) ) ); - ::glPushMatrix(); - // tilt the precipitation cone against the camera movement vector for crude motion blur - // include current wind vector while at it - auto const velocity { - simulation::Environment.m_precipitation.m_cameramove * -1.0 - + glm::dvec3{ simulation::Environment.wind() } * 0.5 }; - if( glm::length2( velocity ) > 0.0 ) { - auto const forward{ glm::normalize( velocity ) }; - auto left { glm::cross( forward, {0.0,1.0,0.0} ) }; - auto const rotationangle { - std::min( - 45.0, - ( FreeFlyModeFlag ? - 5 * glm::length( velocity ) : - simulation::Train->Dynamic()->GetVelocity() * 0.2 ) ) }; - ::glRotated( rotationangle, left.x, 0.0, left.z ); - } - if( false == FreeFlyModeFlag ) { - // counter potential vehicle roll - auto const roll { 0.5 * glm::degrees( simulation::Train->Dynamic()->Roll() ) }; - if( roll != 0.0 ) { - auto const forward { simulation::Train->Dynamic()->VectorFront() }; - auto const vehicledirection = simulation::Train->Dynamic()->DirectionGet(); - ::glRotated( roll, forward.x, 0.0, forward.z ); - } - } - if( Global.Weather == "rain:" ) { - // oddly enough random streaks produce more natural looking rain than ones the eye can follow - ::glRotated( LocalRandom() * 360, 0.0, 1.0, 0.0 ); - } - - // TBD: leave lighting on to allow vehicle lights to affect it? - ::glDisable( GL_LIGHTING ); - // momentarily disable depth write, to allow vehicle cab drawn afterwards to mask it instead of leaving it 'inside' - ::glDepthMask( GL_FALSE ); - - m_precipitationrenderer.render( m_diffusetextureunit ); - if( Global.bUseVBO ) { - gfx::opengl_vbogeometrybank::reset(); - } - - ::glDepthMask( GL_TRUE ); - ::glEnable( GL_LIGHTING ); - ::glPopMatrix(); -} - -void -opengl_renderer::Render_Alpha( scene::basic_region *Region ) { - - // sort the nodes based on their distance to viewer - std::sort( - std::begin( m_cellqueue ), - std::end( m_cellqueue ), - []( distancecell_pair const &Left, distancecell_pair const &Right ) { - return ( Left.first ) < ( Right.first ); } ); - - Render_Alpha( std::rbegin( m_cellqueue ), std::rend( m_cellqueue ) ); -} - -void -opengl_renderer::Render_Alpha( cell_sequence::reverse_iterator First, cell_sequence::reverse_iterator Last ) { - - // NOTE: this method is launched only during color pass therefore we don't bother with mode test here - // first pass draws elements which we know are located in section banks, to reduce vbo switching - { - auto first { First }; - while( first != Last ) { - - auto const *cell = first->second; - - if( false == cell->m_shapestranslucent.empty() ) { - // since all shapes of the cell share center point we can optimize out a few calls here - ::glPushMatrix(); - auto const originoffset{ cell->m_area.center - m_renderpass.camera.position() }; - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - // render - // NOTE: we can reuse the method used to draw opaque geometry - for( auto const &shape : cell->m_shapestranslucent ) { Render( shape, false ); } - // post-render cleanup - ::glPopMatrix(); - } - - ++first; - } - } - // second pass draws elements with their own vbos - { - auto first { First }; - while( first != Last ) { - - auto const *cell = first->second; - - // translucent parts of instanced models - for( auto *instance : cell->m_instancetranslucent ) { Render_Alpha( instance ); } - // translucent parts of vehicles - for( auto *path : cell->m_paths ) { - for( auto *dynamic : path->Dynamics ) { - if( ( false == FreeFlyModeFlag ) - && ( simulation::Train->Dynamic() == dynamic ) ) { - // delay drawing translucent parts of occupied vehicle until after the cab was drawn - continue; - } - Render_Alpha( dynamic ); - } - } - - ++first; - } - } - // third pass draws the wires; - // wires use section vbos, but for the time being we want to draw them at the very end - { - ::glDisable( GL_LIGHTING ); // linie nie powinny świecić - - auto first{ First }; - while( first != Last ) { - - auto const *cell = first->second; - - if( ( false == cell->m_traction.empty() - || ( false == cell->m_lines.empty() ) ) ) { - // since all shapes of the cell share center point we can optimize out a few calls here - ::glPushMatrix(); - auto const originoffset { cell->m_area.center - m_renderpass.camera.position() }; - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - if( !Global.bSmoothTraction ) { - // na liniach kiepsko wygląda - robi gradient - ::glDisable( GL_LINE_SMOOTH ); - } - Bind_Material( null_handle ); - // render - for( auto *traction : cell->m_traction ) { Render_Alpha( traction ); } - for( auto &lines : cell->m_lines ) { Render_Alpha( lines ); } - // post-render cleanup - ::glLineWidth( 1.0 ); - if( !Global.bSmoothTraction ) { - ::glEnable( GL_LINE_SMOOTH ); - } - ::glPopMatrix(); - } - - ++first; - } - - ::glEnable( GL_LIGHTING ); - } -} - -void -opengl_renderer::Render_Alpha( TAnimModel *Instance ) { - - if (false == Instance->m_visible) { return; } - if( false == m_renderpass.camera.visible( Instance->m_area ) ) { return; } - - double distancesquared; - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: { - // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = glm::length2( ( Instance->location() - Global.pCamera.Pos ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor; - break; - } - default: { - distancesquared = glm::length2( ( Instance->location() - m_renderpass.camera.position() ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor; - break; - } - } - if( ( distancesquared < Instance->m_rangesquaredmin ) - || ( distancesquared >= Instance->m_rangesquaredmax ) ) { - return; - } - // crude way to reject early items too far to affect the output (mostly relevant for shadow passes) - auto const drawdistancethreshold{ m_renderpass.draw_range + 250 }; - if( distancesquared > sq(drawdistancethreshold) ) { - return; - } - // second stage visibility cull, reject modelstoo far away to be noticeable - auto const radiussquared { Instance->radius() * Instance->radius() }; - if( radiussquared * Global.ZoomFactor / distancesquared < 0.003 * 0.003 ) { - return; - } - - Instance->RaAnimate( m_framestamp ); // jednorazowe przeliczenie animacji - Instance->RaPrepare(); - if( Instance->pModel ) { - // renderowanie rekurencyjne submodeli - Render_Alpha( - Instance->pModel, - Instance->Material(), - distancesquared, - Instance->location() - m_renderpass.camera.position(), - Instance->vAngle ); - } -} - -void -opengl_renderer::Render_Alpha( TTraction *Traction ) { -/* -// NOTE: test disabled as this call is only executed as part of the colour pass - double distancesquared; - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: { - // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = glm::length2( ( Traction->location() - Global.pCamera.Pos ) / Global.ZoomFactor ) / Global.fDistanceFactor; - break; - } - default: { - distancesquared = glm::length2( ( Traction->location() - m_renderpass.camera.position() ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor; - break; - } - } -*/ - auto const distancesquared { glm::length2( ( Traction->location() - m_renderpass.camera.position() ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor }; - if( ( distancesquared < Traction->m_rangesquaredmin ) - || ( distancesquared >= Traction->m_rangesquaredmax ) ) { - return; - } - - if( false == Traction->m_visible ) { - return; - } - // rysuj jesli sa druty i nie zerwana - if( ( Traction->Wires == 0 ) - || ( true == TestFlag( Traction->DamageFlag, 128 ) ) ) { - return; - } - // setup -/* - float const linealpha = static_cast( - std::min( - 1.25, - 5000 * Traction->WireThickness / ( distancesquared + 1.0 ) ) ); // zbyt grube nie są dobre - ::glLineWidth( linealpha ); -*/ - auto const distance { static_cast( std::sqrt( distancesquared ) ) }; - auto const linealpha { - 20.f * Traction->WireThickness - / std::max( - 0.5f * Traction->radius() + 1.f, - distance - ( 0.5f * Traction->radius() ) ) }; - ::glLineWidth( - std::clamp( - 0.5f * linealpha + Traction->WireThickness * Traction->radius() / 1000.f, - 1.f, 1.75f ) ); - // McZapkie-261102: kolor zalezy od materialu i zasniedzenia - ::glColor4fv( - glm::value_ptr( - glm::vec4{ - Traction->wire_color() /* * ( DebugModeFlag ? 1.f : std::clamp( m_sunandviewangle, 0.25f, 1.f ) ) */, - linealpha } ) ); - // render - m_geometry.draw( Traction->m_geometry ); - // debug data - ++m_renderpass.draw_stats.traction; - ++m_renderpass.draw_stats.drawcalls; -} - -void -opengl_renderer::Render_Alpha( scene::lines_node const &Lines ) { - - auto const &data { Lines.data() }; -/* - // NOTE: test disabled as this call is only executed as part of the colour pass - double distancesquared; - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: { - // 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees - distancesquared = glm::length2( ( data.area.center - Global.pCamera.Pos ) / Global.ZoomFactor ) / Global.fDistanceFactor; - break; - } - default: { - distancesquared = glm::length2( ( data.area.center - m_renderpass.camera.position() ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor; - break; - } - } -*/ - auto const distancesquared { glm::length2( ( data.area.center - m_renderpass.camera.position() ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor }; - if( ( distancesquared < data.rangesquared_min ) - || ( distancesquared >= data.rangesquared_max ) ) { - return; - } - // setup - auto const distance { static_cast( std::sqrt( distancesquared ) ) }; - auto const linealpha = ( - data.line_width > 0.f ? - 10.f * data.line_width - / std::max( - 0.5f * data.area.radius + 1.f, - distance - ( 0.5f * data.area.radius ) ) : - 1.f ); // negative width means the lines are always opague - ::glLineWidth( - std::clamp( - 0.5f * linealpha + data.line_width * data.area.radius / 1000.f, - 1.f, 8.f ) ); - ::glColor4fv( - glm::value_ptr( - glm::vec4{ - glm::vec3{ data.lighting.diffuse * m_sunlight.ambient }, // w zaleznosci od koloru swiatla - std::min( 1.f, linealpha ) } ) ); - // render - m_geometry.draw( data.geometry ); - ++m_renderpass.draw_stats.lines; - ++m_renderpass.draw_stats.drawcalls; -} - -bool -opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) { - - if( false == Dynamic->renderme ) { return false; } - - // setup - TSubModel::iInstance = ( size_t )Dynamic; //żeby nie robić cudzych animacji - TSubModel::iVariantSeed = Dynamic->variant_seed(); - glm::dvec3 const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); - // lod visibility ranges are defined for base (x 1.0) viewing distance. for render we adjust them for actual range multiplier and zoom - float squaredistance; - switch( m_renderpass.draw_mode ) { - case rendermode::shadows: { - squaredistance = glm::length2( glm::vec3{ glm::dvec3{ Dynamic->vPosition - Global.pCamera.Pos } } / Global.ZoomFactor ); - break; - } - default: { - squaredistance = glm::length2( glm::vec3{ originoffset } / Global.ZoomFactor ); - break; - } - } - Dynamic->ABuLittleUpdate( squaredistance ); // ustawianie zmiennych submodeli dla wspólnego modelu - ::glPushMatrix(); - - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); - ::glMultMatrixd( glm::value_ptr(Dynamic->mMatrix) ); - - if( Dynamic->fShade > 0.0f ) { - // change light level based on light level of the occupied track - m_sunlight.apply_intensity( Dynamic->fShade ); - } - m_renderspecular = true; - - // render - if( Dynamic->mdLowPolyInt ) { - // low poly interior -// if( FreeFlyModeFlag ? true : !Dynamic->mdKabina || !Dynamic->bDisplayCab ) { -/* - // enable cab light if needed - if( Dynamic->InteriorLightLevel > 0.0f ) { - - // crude way to light the cabin, until we have something more complete in place - ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr( Dynamic->InteriorLight * Dynamic->InteriorLightLevel ) ); - } -*/ - Render_Alpha( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance ); -/* - if( Dynamic->InteriorLightLevel > 0.0f ) { - // reset the overall ambient - ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr( m_baseambient ) ); - } -*/ -// } - } - - if( Dynamic->mdModel ) { - // main model - Render_Alpha( Dynamic->mdModel, Dynamic->Material(), squaredistance ); - } - // optional attached models - for( auto *attachment : Dynamic->mdAttachments ) { - Render_Alpha( attachment, Dynamic->Material(), squaredistance ); - } - // optional coupling adapters - Render_coupler_adapter( Dynamic, squaredistance, end::front, true ); - Render_coupler_adapter( Dynamic, squaredistance, end::rear, true ); - if( Dynamic->mdLoad ) { - // renderowanie nieprzezroczystego ładunku - Render_Alpha( Dynamic->mdLoad, Dynamic->Material(), squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); - } - - // post-render cleanup - m_renderspecular = false; - if( Dynamic->fShade > 0.0f ) { - // restore regular light level - m_sunlight.apply_intensity(); - } - - ::glPopMatrix(); - - if( Dynamic->btnOn ) - Dynamic->TurnOff(); // przywrócenie domyślnych pozycji submodeli - - return true; -} - -bool -opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, float const Squaredistance ) { - - auto alpha = - ( Material != nullptr ? - Material->textures_alpha : - 0x30300030 ); - - if( 0 == ( alpha & Model->iFlags & 0x2F2F002F ) ) { - // nothing to render - return false; - } - - Model->Root->fSquareDist = Squaredistance; // zmienna globalna! - - // setup - Model->Root->ReplacableSet( - ( Material != nullptr ? - Material->replacable_skins : - nullptr ), - alpha ); - - Model->Root->pRoot = Model; - - // render - Render_Alpha( Model->Root ); - - // post-render cleanup - - return true; -} - -bool -opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, float const Squaredistance, glm::dvec3 const &Position, glm::vec3 const &Angle ) { - - ::glPushMatrix(); - ::glTranslated( Position.x, Position.y, Position.z ); - if( Angle.y != 0.0 ) - ::glRotatef( Angle.y, 0.f, 1.f, 0.f ); - if( Angle.x != 0.0 ) - ::glRotatef( Angle.x, 1.f, 0.f, 0.f ); - if( Angle.z != 0.0 ) - ::glRotatef( Angle.z, 0.f, 0.f, 1.f ); - - auto const result = Render_Alpha( Model, Material, Squaredistance ); // position is effectively camera offset - - ::glPopMatrix(); - - return result; -} - -// characters of a text display, laid out in a row. the text can be encoded in utf-8 or in windows-1250 -void -opengl_renderer::Render_Alpha_text( TSubModel *Submodel ) { - - if( TSubModel::pasText == nullptr ) { return; } - - for( auto *p : Submodel->text_letters( *TSubModel::pasText ) ) { // translucent only, for the time being - Render_Alpha( p ); - if( p->fMatrix ) - ::glMultMatrixf( p->fMatrix->readArray() ); // move on to the place of the next character - } -} - -void -opengl_renderer::Render_Alpha( TSubModel *Submodel ) { - // renderowanie przezroczystych przez DL - if( ( Submodel->iVisible ) - && ( TSubModel::fSquareDist >= Submodel->fSquareMinDist ) - && ( TSubModel::fSquareDist < Submodel->fSquareMaxDist ) ) { - - if( Submodel->iFlags & 0xC000 ) { - ::glPushMatrix(); - if( Submodel->fMatrix ) - ::glMultMatrixf( Submodel->fMatrix->readArray() ); - if( Submodel->b_aAnim != TAnimType::at_None ) - Submodel->RaAnimation( Submodel->b_aAnim ); - } - - if( Submodel->eType < TP_ROTATOR ) { - // renderowanie obiektów OpenGL - if( Submodel->iAlpha & Submodel->iFlags & 0x2F ) { - // rysuj gdy element przezroczysty - - // debug data - ++m_renderpass.draw_stats.submodels; - ++m_renderpass.draw_stats.drawcalls; - - switch( m_renderpass.draw_mode ) { - case rendermode::color: { - -// NOTE: code disabled as normalization marking doesn't take into account scaling propagation down hierarchy chains -// for the time being we'll do with enforced worst-case scaling method, when speculars are enabled -#ifdef EU07_USE_OPTIMIZED_NORMALIZATION - switch( Submodel->m_normalizenormals ) { - case TSubModel::normalize: { - ::glEnable( GL_NORMALIZE ); break; } - case TSubModel::rescale: { - ::glEnable( GL_RESCALE_NORMAL ); break; } - default: { - break; } - } -#else - if( true == m_renderspecular ) { - ::glEnable( GL_NORMALIZE ); - } -#endif - // material configuration: - // textures... - auto const material { submodel_material( Submodel ) }; // zmienialne skóry, również 0 - // textures... - Bind_Material( material ); - // ...colors and opacity... - auto const opacity { std::clamp( Material( material )->get_or_guess_opacity(), 0.f, 1.f ) }; - if( Submodel->fVisible < 1.f ) { - ::glColor4f( - Submodel->f4Diffuse.r, - Submodel->f4Diffuse.g, - Submodel->f4Diffuse.b, - Submodel->fVisible ); - } - else { - ::glColor3fv( glm::value_ptr( Submodel->f4Diffuse ) ); // McZapkie-240702: zamiast ub - } - if( opacity != 0.f ) { - // discard fragments with opacity exceeding the threshold - ::glAlphaFunc( GL_LEQUAL, opacity ); - } - if( ( true == m_renderspecular ) && ( m_sunlight.specular.a > 0.01f ) ) { - ::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( Submodel->f4Specular * m_sunlight.specular.a * m_speculartranslucentscalefactor ) ); - } - // ...luminance - auto const unitstate = m_unitstate; - auto const isemissive { ( Submodel->f4Emision.a > 0.f ) && ( Global.fLuminance < Submodel->fLight ) }; - if( isemissive ) { - // zeby swiecilo na kolorowo - ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( /* Submodel->f4Emision */ Submodel->f4Diffuse * Submodel->f4Emision.a ) ); - // disable shadows so they don't obstruct self-lit items -/* - setup_shadow_color( colors::white ); -*/ - switch_units( unitstate.diffuse, false, false ); - } - - // main draw call - m_geometry.draw( Submodel->m_geometry.handle ); - - // post-draw reset - if( opacity != 0.f ) { - // discard fragments with opacity exceeding the threshold - ::glAlphaFunc( GL_GREATER, 0.0f ); - } - if( ( true == m_renderspecular ) && ( m_sunlight.specular.a > 0.01f ) ) { - ::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( colors::none ) ); - } - if( isemissive ) { - // restore default (lack of) brightness - ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( colors::none ) ); -/* - setup_shadow_color( m_shadowcolor ); -*/ - switch_units( unitstate.diffuse, unitstate.shadows, unitstate.reflections ); - } -#ifdef EU07_USE_OPTIMIZED_NORMALIZATION - switch( Submodel->m_normalizenormals ) { - case TSubModel::normalize: { - ::glDisable( GL_NORMALIZE ); break; } - case TSubModel::rescale: { - ::glDisable( GL_RESCALE_NORMAL ); break; } - default: { - break; } - } -#else - if( true == m_renderspecular ) { - ::glDisable( GL_NORMALIZE ); - } -#endif - break; - } - case rendermode::cabshadows: { - // scenery picking and shadow both use enforced colour and no frills - // material configuration: - // textures... - if( Submodel->m_material < 0 ) { // zmienialne skóry - Bind_Material( submodel_material( Submodel ), Submodel ); - } - else { - // również 0 - Bind_Material( Submodel->m_material, Submodel ); - } - // main draw call - m_geometry.draw( Submodel->m_geometry.handle ); - // post-draw reset - break; - } - default: { - break; - } - } - } - } - else if( Submodel->eType == TP_FREESPOTLIGHT ) { - - if( ( Global.fLuminance < Submodel->fLight ) || ( Global.Overcast > 1.f ) ) { - // NOTE: we're forced here to redo view angle calculations etc, because this data isn't instanced but stored along with the single mesh - // TODO: separate instance data from reusable geometry - auto const &modelview = OpenGLMatrices.data( GL_MODELVIEW ); - auto const lightcenter = - modelview - * glm::mix( - glm::vec4( 0.f, 0.f, -0.05f, 1.f ), - glm::vec4( 0.f, 0.f, -0.10f, 1.f ), - static_cast( TSubModel::fSquareDist / Submodel->fSquareMaxDist ) ); // pozycja punktu świecącego względem kamery - Submodel->fCosViewAngle = glm::dot( glm::normalize( modelview * glm::vec4( 0.f, 0.f, -1.f, 1.f ) - lightcenter ), glm::normalize( -lightcenter ) ); - - if( Submodel->fCosViewAngle > Submodel->fCosFalloffAngle ) { - // only bother if the viewer is inside the visibility cone - // luminosity at night is at level of ~0.1, so the overall resulting transparency in clear conditions is ~0.5 at full 'brightness' - auto glarelevel { std::clamp( - std::max( - 0.6f - Global.fLuminance, // reduce the glare in bright daylight - Global.Overcast - 1.f ), // ensure some glare in rainy/foggy conditions - 0.f, 1.f ) }; - // view angle attenuation - float const anglefactor { std::clamp( - ( Submodel->fCosViewAngle - Submodel->fCosFalloffAngle ) / ( Submodel->fCosHotspotAngle - Submodel->fCosFalloffAngle ), - 0.f, 1.f ) }; - glarelevel *= anglefactor; - - if( glarelevel > 0.0f ) { - // setup - ::glPushAttrib( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT ); - - Bind_Texture( m_glaretexture ); - ::glDisable( GL_LIGHTING ); - ::glDisable( GL_FOG ); - ::glDepthMask( GL_FALSE ); - ::glBlendFunc( GL_SRC_ALPHA, GL_ONE ); - - ::glPushMatrix(); - ::glLoadIdentity(); // macierz jedynkowa - ::glTranslatef( lightcenter.x, lightcenter.y, lightcenter.z ); // początek układu zostaje bez zmian - ::glRotated( std::atan2( lightcenter.x, lightcenter.z ) * 180.0 / M_PI, 0.0, 1.0, 0.0 ); // jedynie obracamy w pionie o kąt - // disable shadows so they don't obstruct self-lit items - auto const unitstate = m_unitstate; - switch_units( unitstate.diffuse, false, false ); - - glm::vec3 const lightcolor { - Submodel->DiffuseOverride.r < 0.f ? // -1 indicates no override - glm::vec3( Submodel->f4Diffuse ) : - Submodel->DiffuseOverride }; - ::glColor4f( - lightcolor.r, - lightcolor.g, - lightcolor.b, - Submodel->fVisible * glarelevel ); - - // main draw call - m_geometry.draw( m_billboardgeometry ); -/* - // NOTE: we could do simply... - vec3 vertexPosition_worldspace = - particleCenter_wordspace - + CameraRight_worldspace * squareVertices.x * BillboardSize.x - + CameraUp_worldspace * squareVertices.y * BillboardSize.y; - // ...etc instead IF we had easy access to camera's forward and right vectors. TODO: check if Camera matrix is accessible -*/ - // post-render cleanup - switch_units( unitstate.diffuse, unitstate.shadows, unitstate.reflections ); - - ::glPopMatrix(); - ::glPopAttrib(); - } - } - } - } - - if( Submodel->Child != nullptr ) { - if( Submodel->eType == TP_TEXT ) { // tekst renderujemy w specjalny sposób, zamiast submodeli z łańcucha Child - Render_Alpha_text( Submodel ); - } - else if( Submodel->iAlpha & Submodel->iFlags & 0x002F0000 ) - Render_Alpha( Submodel->Child ); - } - - if( Submodel->iFlags & 0xC000 ) - ::glPopMatrix(); - } -/* - if( Submodel->b_aAnim < at_SecondsJump ) - Submodel->b_aAnim = at_None; // wyłączenie animacji dla kolejnego użycia submodelu -*/ - if( Submodel->Next != nullptr ) - if( Submodel->iAlpha & Submodel->iFlags & 0x2F000000 ) - Render_Alpha( Submodel->Next ); -}; - - - -// utility methods -void -opengl_renderer::Update_Pick_Control() { - -#ifdef EU07_USE_PICKING_FRAMEBUFFER - if( true == m_framebuffersupport ) { - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_pickframebuffer ); - } -#endif - Render_pass( rendermode::pickcontrols ); - // determine point to examine - glm::vec2 mousepos = Global.cursor_pos; - mousepos.y = Global.window_size.y - mousepos.y; // cursor coordinates are flipped compared to opengl - -#ifdef EU07_USE_PICKING_FRAMEBUFFER - glm::ivec2 pickbufferpos; - if( true == m_framebuffersupport ) { -// ::glReadBuffer( GL_COLOR_ATTACHMENT0_EXT ); - pickbufferpos = mousepos * glm::vec2(EU07_PICKBUFFERSIZE) / glm::vec2(Global.window_size); - } - else { -// ::glReadBuffer( GL_BACK ); - pickbufferpos = mousepos * glm::vec2(Global.fb_size) / glm::vec2(Global.window_size); - } -#else -// ::glReadBuffer( GL_BACK ); - glm::ivec2 pickbufferpos{ mousepos }; -#endif - unsigned char pickreadout[4]; - ::glReadPixels( pickbufferpos.x, pickbufferpos.y, 1, 1, GL_BGRA, GL_UNSIGNED_BYTE, pickreadout ); - auto const controlindex = pick_index( glm::ivec3{ pickreadout[ 2 ], pickreadout[ 1 ], pickreadout[ 0 ] } ); - TSubModel *control { nullptr }; - if( ( controlindex > 0 ) - && ( controlindex <= m_pickcontrolsitems.size() ) ) { - control = m_pickcontrolsitems[ controlindex - 1 ]; - } -#ifdef EU07_USE_PICKING_FRAMEBUFFER - if( true == m_framebuffersupport ) { - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); - } -#endif - m_pickcontrolitem = control; -// return control; - for( auto& callback : m_control_pick_requests ) { - callback( m_pickcontrolitem, glm::vec2() ); - } - m_control_pick_requests.clear(); -} - -void -opengl_renderer::Update_Pick_Node() { - -#ifdef EU07_USE_PICKING_FRAMEBUFFER - if( true == m_framebuffersupport ) { - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_pickframebuffer ); - } -#endif - Render_pass( rendermode::pickscenery ); - // determine point to examine - glm::vec2 mousepos = Global.cursor_pos; - mousepos.y = Global.window_size.y - mousepos.y; // cursor coordinates are flipped compared to opengl - -#ifdef EU07_USE_PICKING_FRAMEBUFFER - glm::ivec2 pickbufferpos; - if( true == m_framebuffersupport ) { -// ::glReadBuffer( GL_COLOR_ATTACHMENT0_EXT ); - pickbufferpos = mousepos * glm::vec2(EU07_PICKBUFFERSIZE) / glm::vec2(Global.window_size); - } - else { -// ::glReadBuffer( GL_BACK ); - pickbufferpos = mousepos * glm::vec2(Global.fb_size) / glm::vec2(Global.window_size); - } -#else -// ::glReadBuffer( GL_BACK ); - glm::ivec2 pickbufferpos{ mousepos }; -#endif - - unsigned char pickreadout[4]; - ::glReadPixels( pickbufferpos.x, pickbufferpos.y, 1, 1, GL_BGRA, GL_UNSIGNED_BYTE, pickreadout ); - auto const nodeindex = pick_index( glm::ivec3{ pickreadout[ 2 ], pickreadout[ 1 ], pickreadout[ 0 ] } ); - scene::basic_node *node { nullptr }; - if( ( nodeindex > 0 ) - && ( nodeindex <= m_picksceneryitems.size() ) ) { - node = m_picksceneryitems[ nodeindex - 1 ]; - } -#ifdef EU07_USE_PICKING_FRAMEBUFFER - if( true == m_framebuffersupport ) { - ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); - } -#endif - m_picksceneryitem = node; -// return node; - for( auto& callback : m_node_pick_requests ) { - callback( m_picksceneryitem ); - } - m_node_pick_requests.clear(); -} - -// converts provided screen coordinates to world coordinates of most recent color pass -glm::dvec3 -opengl_renderer::Update_Mouse_Position() { - - glm::ivec2 mousepos = Global.cursor_pos * Global.fb_size / Global.window_size; - mousepos.x = std::clamp( mousepos.x, 0, Global.fb_size.x - 1 ); - mousepos.y = std::clamp(Global.fb_size.y - mousepos.y, 0, Global.fb_size.y - 1); - GLfloat pointdepth; - ::glReadPixels( mousepos.x, mousepos.y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &pointdepth ); - - if( pointdepth < 1.0 ) { - m_worldmousecoordinates = - glm::unProject( - glm::vec3{ mousepos, pointdepth }, - glm::mat4{ glm::mat3{ m_colorpass.camera.modelview() } }, - m_colorpass.camera.projection(), - glm::vec4{ 0, 0, Global.fb_size.x, Global.fb_size.y } ); - } - - return m_colorpass.camera.position() + glm::dvec3{ m_worldmousecoordinates }; -} - -void -opengl_renderer::Update( double const Deltatime ) { - - // per frame updates - if( simulation::is_ready ) { - // update particle subsystem - renderpass_config renderpass; - setup_pass( renderpass, rendermode::color ); - m_skydomerenderer.update(); - m_precipitationrenderer.update(); - m_particlerenderer.update( renderpass.camera ); - } - - // fixed step updates -/* - m_pickupdateaccumulator += Deltatime; - - if( m_updateaccumulator > 0.5 ) { - - m_pickupdateaccumulator = 0.0; - - if( ( true == Global.ControlPicking ) - && ( false == FreeFlyModeFlag ) ) { - Update_Pick_Control(); - } - else { - m_pickcontrolitem = nullptr; - } - // temporary conditions for testing. eventually will be coupled with editor mode - if( ( true == Global.ControlPicking ) - && ( true == DebugModeFlag ) - && ( true == FreeFlyModeFlag ) ) { - Update_Pick_Node(); - } - else { - m_picksceneryitem = nullptr; - } - } -*/ - m_updateaccumulator += Deltatime; - - if( ( true == Global.ControlPicking ) - && ( false == FreeFlyModeFlag ) ) { - if( ( false == m_control_pick_requests.empty() ) - || ( m_updateaccumulator >= 1.0 ) ) { - Update_Pick_Control(); - } - } - else { - m_pickcontrolitem = nullptr; - } - // temporary conditions for testing. eventually will be coupled with editor mode - if( ( true == Global.ControlPicking ) - && ( true == FreeFlyModeFlag ) - && ( ( true == DebugModeFlag ) - || ( true == EditorModeFlag ) ) ) { - if( ( false == m_node_pick_requests.empty() ) - || ( m_updateaccumulator >= 1.0 ) ) { - Update_Pick_Node(); - } - } - else { - m_picksceneryitem = nullptr; - } - - if( m_updateaccumulator < 1.0 ) { - // too early for any work - return; - } - - m_updateaccumulator = 0.0; - m_framerate = 1000.f / ( Timer::subsystem.gfx_total.average() ); - - // adjust draw ranges etc, based on recent performance - // TODO: it doesn't make much sense with vsync - if( Global.targetfps != 0.f ) { - auto const fps_diff = m_framerate - Global.targetfps; - if( fps_diff < -0.5f ) { - Global.fDistanceFactor = std::min( std::max( 1.0f, Global.fDistanceFactor - 0.05f ), Global.gfx_distance_factor_max ); - } - else if( fps_diff > 0.5f ) { - Global.fDistanceFactor = std::min( std::min( 3.0f, Global.fDistanceFactor + 0.05f ), Global.gfx_distance_factor_max ); - } - } - // legacy framerate parameters - else if( Global.fFpsAverage != 0.f ) { - auto const fps_diff = m_framerate - Global.fFpsAverage; - if( fps_diff < -Global.fFpsDeviation ) { - Global.fDistanceFactor = std::min( std::max( 1.0f, Global.fDistanceFactor - 0.05f ), Global.gfx_distance_factor_max ); - } - else if( fps_diff > Global.fFpsDeviation ) { - Global.fDistanceFactor = std::min( std::min( 3.0f, Global.fDistanceFactor + 0.05f ), Global.gfx_distance_factor_max ); - } - } - - if( Global.UpdateMaterials ) { - // update resources if there was environmental change - simulation_state simulationstate{ - Global.Weather, - Global.Season - }; - std::swap( m_simulationstate, simulationstate ); - if( ( m_simulationstate.season != simulationstate.season ) && ( false == simulationstate.season.empty() ) ) { - m_materials.on_season_change(); - } - if( ( m_simulationstate.weather != simulationstate.weather ) && ( false == simulationstate.weather.empty() ) ) { - m_materials.on_weather_change(); - } - } - - if( ( true == Global.ResourceSweep ) - && ( true == simulation::is_ready ) ) { - // garbage collection - m_geometry.update(); - m_textures.update(); - } - - if( true == DebugModeFlag ) { - m_debugtimestext += m_textures.info(); - } - - // dump last opengl error, if any - auto const glerror = ::glGetError(); - if( glerror != GL_NO_ERROR ) { - std::string glerrorstring; - win1250_to_ascii( glerrorstring ); - Global.LastGLError = std::to_string( glerror ) + " (" + glerrorstring + ")"; - } -} - -// debug performance string -std::string const & -opengl_renderer::info_times() const { - - return m_debugtimestext; -} - -std::string const & -opengl_renderer::info_stats() const { - - return m_debugstatstext; -} - -void opengl_renderer::MakeScreenshot() -{ - screenshot_manager::make_screenshot(); -} - -void -opengl_renderer::Update_Lights( light_array &Lights ) { - // arrange the light array from closest to farthest from current position of the camera - auto const &camera = m_renderpass.camera.position(); - std::sort( - std::begin( Lights.data ), - std::end( Lights.data ), - [&camera]( light_array::light_record const &Left, light_array::light_record const &Right ) { - // move lights which are off at the end... - if( Left.intensity == 0.f ) { return false; } - if( Right.intensity == 0.f ) { return true; } - // ...otherwise prefer closer and/or brigher light sources - return ( glm::length2( camera - Left.position ) / Left.intensity ) < ( glm::length2( camera - Right.position ) / Right.intensity ); } ); - - size_t const count = std::min( m_lights.size(), Lights.data.size() ); - if( count == 0 ) { return; } - - auto renderlight = m_lights.begin(); - - for( auto const &scenelight : Lights.data ) { - - if( renderlight == m_lights.end() ) { - // we ran out of lights to assign - break; - } - if( scenelight.intensity == 0.f ) { - // all lights past this one are bound to be off - break; - } - auto const lightoffset = glm::vec3{ scenelight.position - camera }; - // length2 is better than length for comparing because it does not require sqrt function - if( glm::length2( lightoffset ) > sq(1000.f)) { - // we don't care about lights past arbitrary limit of 1 km. - // but there could still be weaker lights which are closer, so keep looking - continue; - } - // if the light passed tests so far, it's good enough - renderlight->position = lightoffset; - renderlight->direction = scenelight.direction; - - auto luminance = static_cast( Global.fLuminance ); - // adjust luminance level based on vehicle's location, e.g. tunnels - auto const environment = scenelight.owner->fShade; - if( environment > 0.f ) { - luminance *= environment; - } - renderlight->diffuse = - glm::vec4{ - glm::max( glm::vec3{ colors::none }, scenelight.color - glm::vec3{ luminance } ), - renderlight->diffuse[ 3 ] }; - renderlight->ambient = - glm::vec4{ - glm::max( glm::vec3{ colors::none }, scenelight.color * glm::vec3{ scenelight.intensity } - glm::vec3{ luminance } ), - renderlight->ambient[ 3 ] }; - - ::glLightf( renderlight->id, GL_LINEAR_ATTENUATION, static_cast( (0.25 * scenelight.count) / std::pow( scenelight.count, 2 ) * (scenelight.owner->DimHeadlights ? 1.25 : 1.0) ) ); - ::glEnable( renderlight->id ); - - renderlight->apply_intensity(); - renderlight->apply_angle(); - - ++renderlight; - } - - while( renderlight != m_lights.end() ) { - // if we went through all scene lights and there's still opengl lights remaining, kill these - ::glDisable( renderlight->id ); - ++renderlight; - } -} - -void -opengl_renderer::Disable_Lights() { - - for( size_t idx = 0; idx < m_lights.size() + 1; ++idx ) { - - ::glDisable( GL_LIGHT0 + (int)idx ); - } -} - -bool -opengl_renderer::Init_caps() { - - std::string gl_renderer((char *)glGetString(GL_RENDERER)); - std::string gl_vendor((char *)glGetString(GL_VENDOR)); - std::string gl_version((char *)glGetString(GL_VERSION)); - - crashreport_add_info("gl_renderer", gl_renderer); - crashreport_add_info("gl_vendor", gl_vendor); - crashreport_add_info("gl_version", gl_version); - crashreport_add_info("gl_mode", Global.bUseVBO ? "vbo" : "dl"); - - WriteLog( - "Gfx Renderer: " + gl_renderer - + " Vendor: " + gl_vendor - + " OpenGL Version: " + gl_version ); - - if( !GLAD_GL_VERSION_2_1 ) { - ErrorLog( "Requires openGL >= 2.1" ); - return false; - } - - m_isATI = (gl_vendor.find("ATI") != -1); - - char* extensions = (char*)glGetString( GL_EXTENSIONS ); - if (extensions) - WriteLog( "Supported extensions: \n" + std::string(extensions)); - - WriteLog( std::string("render path: ") + ( Global.bUseVBO ? "VBO" : "Display lists" ) ); - if( GL_EXT_framebuffer_object ) { - m_framebuffersupport = true; - WriteLog( "framebuffer objects enabled" ); - } - else { - WriteLog( "framebuffer objects not supported, resorting to back buffer rendering where possible" ); - } - // ograniczenie maksymalnego rozmiaru tekstur - parametr dla skalowania tekstur - { - GLint texturesize; - ::glGetIntegerv( GL_MAX_TEXTURE_SIZE, &texturesize ); - Global.iMaxTextureSize = std::min(Global.iMaxTextureSize, texturesize); - Global.iMaxCabTextureSize = std::min( Global.iMaxCabTextureSize, texturesize ); - WriteLog( "texture sizes capped at " + std::to_string(Global.iMaxTextureSize) + "p (" + std::to_string( Global.iMaxCabTextureSize ) + "p for cab textures)" ); - Global.CurrentMaxTextureSize = Global.iMaxTextureSize; - m_shadowbuffersize = Global.shadowtune.map_size; - m_shadowbuffersize = std::min( m_shadowbuffersize, texturesize ); - WriteLog( "shadows map size capped at " + std::to_string( m_shadowbuffersize ) + "p" ); - } - // cap the number of supported lights based on hardware - { - GLint maxlights; - ::glGetIntegerv( GL_MAX_LIGHTS, &maxlights ); - Global.DynamicLightCount = std::min( Global.DynamicLightCount, maxlights - 1 ); - WriteLog( "dynamic light amount capped at " + std::to_string( Global.DynamicLightCount ) + " (" + std::to_string(maxlights) + " lights total supported by the gfx card)" ); - } - // select renderer mode - if( true == Global.BasicRenderer ) { - WriteLog( "basic renderer selected, shadow and reflection mapping will be disabled" ); - Global.RenderShadows = false; - m_diffusetextureunit = 0; - m_helpertextureunit = -1; - m_shadowtextureunit = -1; - m_normaltextureunit = -1; - } - else { - GLint maxtextureunits; - ::glGetIntegerv( GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtextureunits ); - if( maxtextureunits < 4 ) { - WriteLog( "less than 4 texture units, shadow and reflection mapping will be disabled" ); - Global.BasicRenderer = true; - Global.RenderShadows = false; - m_diffusetextureunit = 0; - m_helpertextureunit = -1; - m_shadowtextureunit = -1; - m_normaltextureunit = -1; - } - } - - if( Global.iMultisampling ) { - WriteLog( "using multisampling x" + std::to_string( 1 << Global.iMultisampling ) ); - } - - WriteLog( "main window size: " + std::to_string( Global.fb_size.x ) + "x" + std::to_string( Global.fb_size.y ) ); - - return true; -} - -glm::vec3 -opengl_renderer::pick_color( std::size_t const Index ) { -/* - // pick colours are set with step of 4 for some slightly easier visual debugging. not strictly needed but, eh - int const colourstep = 4; - int const componentcapacity = 256 / colourstep; - auto const redgreen = std::div( Index, componentcapacity * componentcapacity ); - auto const greenblue = std::div( redgreen.rem, componentcapacity ); - auto const blue = Index % componentcapacity; - return - glm::vec3 { - redgreen.quot * colourstep / 255.0f, - greenblue.quot * colourstep / 255.0f, - greenblue.rem * colourstep / 255.0f }; -*/ - // alternatively - return - glm::vec3{ - ( ( Index & 0xff0000 ) >> 16 ) / 255.0f, - ( ( Index & 0x00ff00 ) >> 8 ) / 255.0f, - ( Index & 0x0000ff ) / 255.0f }; - -} - -std::size_t -opengl_renderer::pick_index( glm::ivec3 const &Color ) { -/* - return ( - std::floor( Color.b / 4 ) - + std::floor( Color.g / 4 ) * 64 - + std::floor( Color.r / 4 ) * 64 * 64 ); -*/ - // alternatively - return - Color.b - + ( Color.g * 256 ) - + ( Color.r * 256 * 256 ); - -} - -std::unique_ptr opengl_renderer::create_func() -{ - return std::unique_ptr(new opengl_renderer()); -} - -bool opengl_renderer::renderer_register = gfx_renderer_factory::get_instance()->register_backend("legacy", opengl_renderer::create_func); - -bool opengl_renderer::opengl_imgui_renderer::Init() -{ - crashreport_add_info("imgui_ver", "gl2"); - return ImGui_ImplOpenGL2_Init(); -} - -void opengl_renderer::opengl_imgui_renderer::Shutdown() -{ - ImGui_ImplOpenGL2_Shutdown(); -} - -void opengl_renderer::opengl_imgui_renderer::BeginFrame() -{ - ImGui_ImplOpenGL2_NewFrame(); -} - -void opengl_renderer::opengl_imgui_renderer::Render() -{ - gl::buffer::unbind(gl::buffer::ARRAY_BUFFER); - ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); -} - -std::uint64_t opengl_renderer::opengl_imgui_renderer::Create_Image(std::uint8_t const *Rgba, int const Width, int const Height) -{ - // the ui of this renderer works on srgb colours as they are. the binding the renderer keeps track of is put back as it was - GLint texture{0}, alignment{4}; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture); - glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - GLuint image{0}; - glGenTextures(1, &image); - glBindTexture(GL_TEXTURE_2D, image); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, Rgba); - glBindTexture(GL_TEXTURE_2D, static_cast(texture)); - glPixelStorei(GL_UNPACK_ALIGNMENT, alignment); - return image; -} - -void opengl_renderer::opengl_imgui_renderer::Release_Image(std::uint64_t const Image) -{ - auto const image{static_cast(Image)}; - glDeleteTextures(1, &image); -} - diff --git a/rendering/openglrenderer.h b/rendering/openglrenderer.h deleted file mode 100644 index 78fffa40..00000000 --- a/rendering/openglrenderer.h +++ /dev/null @@ -1,398 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#pragma once - -#include "rendering/renderer.h" -#include "rendering/opengllight.h" -#include "rendering/openglcamera.h" -#include "rendering/openglparticles.h" -#include "rendering/openglskydome.h" -#include "rendering/openglprecipitation.h" -#include "rendering/lightarray.h" -#include "scene/scene.h" -#include "simulation/simulationenvironment.h" -#include "world/MemCell.h" - -#define EU07_USE_PICKING_FRAMEBUFFER -//#define EU07_USE_DEBUG_SHADOWMAP -//#define EU07_USE_DEBUG_CABSHADOWMAP -//#define EU07_USE_DEBUG_CAMERA -//#define EU07_USE_DEBUG_SOUNDEMITTERS -//#define EU07_DISABLECABREFLECTIONS - -// bare-bones render controller, in lack of anything better yet -class opengl_renderer : public gfx_renderer { - -public: -// types -// constructors - opengl_renderer() = default; -// destructor - ~opengl_renderer() { } -// methods - bool - Init( GLFWwindow *Window ) override; - // main draw call. returns false on error - bool - Render() override; - void - SwapBuffers() override; - inline - float - Framerate() override { return m_framerate; } - - bool AddViewport(const global_settings::extraviewport_config &conf) override { return false; } - bool Debug_Ui_State(std::optional) override { return false; } - void Shutdown() override {} - - // geometry methods - // NOTE: hands-on geometry management is exposed as a temporary measure; ultimately all visualization data should be generated/handled automatically by the renderer itself - // creates a new geometry bank. returns: handle to the bank or NULL - gfx::geometrybank_handle - Create_Bank() override; - // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL - gfx::geometry_handle Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override; - // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL - gfx::geometry_handle Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override; - // replaces data of specified chunk with the supplied vertex data, starting from specified offset - bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) override; - // adds supplied vertex data at the end of specified chunk - bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override; - // provides direct access to index data of specfied chunk - gfx::index_array const & - Indices( gfx::geometry_handle const &Geometry ) const override; - // provides direct access to vertex data of specfied chunk - gfx::vertex_array const & - Vertices( gfx::geometry_handle const &Geometry ) const override; - // provides direct access to vertex data of specfied chunk - gfx::userdata_array const & - UserData( gfx::geometry_handle const &Geometry ) const override; - // material methods - material_handle - Fetch_Material( std::string const &Filename, bool const Loadnow = true ) override; - void - Bind_Material( material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr ) override; - IMaterial const * - Material( material_handle const Material ) const override; - // shader methods - auto Fetch_Shader( std::string const &name ) -> std::shared_ptr override; - // texture methods - texture_handle - Fetch_Texture( std::string const &Filename, bool const Loadnow = true, GLint format_hint = GL_SRGB_ALPHA ) override; - void - Bind_Texture( texture_handle const Texture ) override; - void - Bind_Texture( std::size_t const Unit, texture_handle const Texture ) override; - ITexture & - Texture( texture_handle const Texture ) override; - ITexture const & - Texture( texture_handle const Texture ) const override; - // utility methods - void - Pick_Control_Callback( std::function Callback ) override; - void - Pick_Node_Callback( std::function Callback ) override; - TSubModel const * - Pick_Control() const override { return m_pickcontrolitem; } - scene::basic_node const * - Pick_Node() const override { return m_picksceneryitem; } - glm::dvec3 - Mouse_Position() const override { return m_worldmousecoordinates; } - glm::mat4 - Camera_View_Matrix() const override { return glm::mat4( glm::mat3( m_colorpass.camera.modelview() ) ); } - glm::mat4 - Camera_Projection_Matrix() const override { return m_colorpass.camera.projection(); } - glm::dvec3 - Camera_Position() const override { return m_colorpass.camera.position(); } - // maintenance methods - void - Update( double const Deltatime ) override; - void - Update_Pick_Control() override; - void - Update_Pick_Node() override; - glm::dvec3 - Update_Mouse_Position() override; - // debug methods - std::string const & - info_times() const override; - std::string const & - info_stats() const override; - void MakeScreenshot() override; - - opengl_material const & Material( TSubModel const * Submodel ) const; - -// members - GLenum static const sunlight { GL_LIGHT0 }; - - static std::unique_ptr create_func(); - -private: -// types - enum class rendermode { - none, - color, - shadows, - cabshadows, - reflections, - pickcontrols, - pickscenery - }; - - enum textureunit { - helper = 0, - shadows, - normals, - diffuse - }; - - struct debug_stats { - int dynamics { 0 }; - int models { 0 }; - int submodels { 0 }; - int paths { 0 }; - int traction { 0 }; - int shapes { 0 }; - int lines { 0 }; - int particles { 0 }; - int drawcalls { 0 }; - }; - - using section_sequence = std::vector; - using distancecell_pair = std::pair; - using cell_sequence = std::vector; - - struct renderpass_config { - - opengl_camera camera; - rendermode draw_mode { rendermode::none }; - float draw_range { 0.0f }; - debug_stats draw_stats; - }; - - struct units_state { - - bool diffuse { false }; - bool shadows { false }; - bool reflections { false }; - }; - - typedef std::vector opengllight_array; - -// methods - void - Disable_Lights(); - void - setup_pass( renderpass_config &Config, rendermode const Mode, float const Znear = 0.f, float const Zfar = 1.f, bool const Ignoredebug = false ); - void - setup_matrices(); - void - setup_drawing( bool const Alpha = false ); - void - setup_units( bool const Diffuse, bool const Shadows, bool const Reflections ); - void - setup_shadow_map( GLuint const Texture, glm::mat4 const &Transformation ); - void - setup_shadow_color( glm::vec4 const &Shadowcolor ); - void - setup_environment_light( TEnvironmentType const Environment = e_flat ); - void - switch_units( bool const Diffuse, bool const Shadows, bool const Reflections ); - // helper, texture manager method; activates specified texture unit - void - select_unit( GLint const Textureunit ); - // runs jobs needed to generate graphics for specified render pass - void - Render_pass( rendermode const Mode ); - // creates dynamic environment cubemap - bool - Render_reflections(); - bool - Render( world_environment *Environment ); - void - Render( scene::basic_region *Region ); - void - Render( section_sequence::iterator First, section_sequence::iterator Last ); - void - Render( cell_sequence::iterator First, cell_sequence::iterator Last ); - void - Render( scene::shape_node const &Shape, bool const Ignorerange ); - void - Render( TAnimModel *Instance ); - bool - Render( TDynamicObject *Dynamic ); - bool - Render( TModel3d *Model, material_data const *Material, float const Squaredistance, glm::dvec3 const &Position, glm::vec3 const &Angle ); - bool - Render( TModel3d *Model, material_data const *Material, float const Squaredistance ); - void - Render( TSubModel *Submodel ); - void - Render( TTrack *Track ); - void - Render( scene::basic_cell::path_sequence::const_iterator First, scene::basic_cell::path_sequence::const_iterator Last ); - bool - Render_cab( TDynamicObject const *Dynamic, float const Lightlevel, bool const Alpha = false ); - bool - Render_interior( bool const Alpha = false ); - bool - Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha = false ); - bool - Render_coupler_adapter( TDynamicObject *Dynamic, float const Squaredistance, int const End, bool const Alpha = false ); - void - Render( TMemCell *Memcell ); - void - Render_particles(); - void - Render_precipitation(); - void - Render_Alpha( scene::basic_region *Region ); - void - Render_Alpha( cell_sequence::reverse_iterator First, cell_sequence::reverse_iterator Last ); - void - Render_Alpha( TAnimModel *Instance ); - void - Render_Alpha( TTraction *Traction ); - void - Render_Alpha( scene::lines_node const &Lines ); - bool - Render_Alpha( TDynamicObject *Dynamic ); - bool - Render_Alpha( TModel3d *Model, material_data const *Material, float const Squaredistance, glm::dvec3 const &Position, glm::vec3 const &Angle ); - bool - Render_Alpha( TModel3d *Model, material_data const *Material, float const Squaredistance ); - void - Render_Alpha( TSubModel *Submodel ); - // characters of a text display - void - Render_Alpha_text( TSubModel *Submodel ); - void - Update_Lights( light_array &Lights ); - bool - Init_caps(); - glm::vec3 - pick_color( std::size_t const Index ); - std::size_t - pick_index( glm::ivec3 const &Color ); - -// members - GLFWwindow *m_window { nullptr }; - gfx::geometrybank_manager m_geometry; - material_manager m_materials; - texture_manager m_textures; - opengl_light m_sunlight; - opengllight_array m_lights; - opengl_skydome m_skydomerenderer; - opengl_precipitation m_precipitationrenderer; - opengl_particles m_particlerenderer; // particle visualization subsystem -/* - float m_sunandviewangle; // cached dot product of sunlight and camera vectors -*/ - gfx::geometry_handle m_billboardgeometry { 0, 0 }; - texture_handle m_glaretexture { -1 }; - texture_handle m_suntexture { -1 }; - texture_handle m_moontexture { -1 }; - texture_handle m_reflectiontexture { -1 }; - texture_handle m_smoketexture { -1 }; - material_handle m_invalid_material; - // TODO: refactor framebuffer stuff into an object - bool m_framebuffersupport { false }; -#ifdef EU07_USE_PICKING_FRAMEBUFFER - GLuint m_pickframebuffer { 0 }; - GLuint m_picktexture { 0 }; - GLuint m_pickdepthbuffer { 0 }; -#endif - // main shadowmap resources - int m_shadowbuffersize { 2048 }; - GLuint m_shadowframebuffer { 0 }; - GLuint m_shadowtexture { 0 }; -#ifdef EU07_USE_DEBUG_SHADOWMAP - GLuint m_shadowdebugtexture{ 0 }; -#endif -#ifdef EU07_USE_DEBUG_CABSHADOWMAP - GLuint m_cabshadowdebugtexture{ 0 }; -#endif - glm::mat4 m_shadowtexturematrix; // conversion from camera-centric world space to light-centric clip space - // cab shadowmap resources - GLuint m_cabshadowframebuffer { 0 }; - GLuint m_cabshadowtexture { 0 }; - glm::mat4 m_cabshadowtexturematrix; // conversion from cab-centric world space to light-centric clip space - // environment map resources - GLuint m_environmentframebuffer { 0 }; - GLuint m_environmentcubetexture { 0 }; - GLuint m_environmentdepthbuffer { 0 }; - bool m_environmentcubetexturesupport { false }; // indicates whether we can use the dynamic environment cube map - int m_environmentcubetextureface { 0 }; // helper, currently processed cube map face - double m_environmentupdatetime { 0.0 }; // time of the most recent environment map update - glm::dvec3 m_environmentupdatelocation; // coordinates of most recent environment map update - - int m_helpertextureunit { 0 }; - int m_shadowtextureunit { 1 }; - int m_normaltextureunit { 2 }; - int m_diffusetextureunit{ 3 }; - units_state m_unitstate; - - unsigned int m_framestamp; // id of currently rendered gfx frame - float m_framerate; - double m_updateaccumulator { 0.0 }; -// double m_pickupdateaccumulator { 0.0 }; - std::string m_debugtimestext; - std::string m_pickdebuginfo; - std::string m_debugstatstext; - struct simulation_state { - std::string weather; - std::string season; - } m_simulationstate; - - glm::vec4 m_baseambient { 0.0f, 0.0f, 0.0f, 1.0f }; - glm::vec4 m_shadowcolor { colors::shadow }; - float m_fogrange { 2000.f }; -// TEnvironmentType m_environment { e_flat }; - float m_specularopaquescalefactor { 1.f }; - float m_speculartranslucentscalefactor { 1.f }; - bool m_renderspecular{ false }; // controls whether to include specular component in the calculations - - renderpass_config m_renderpass; // parameters for current render pass - section_sequence m_sectionqueue; // list of sections in current render pass - cell_sequence m_cellqueue; - renderpass_config m_colorpass; // parametrs of most recent color pass - renderpass_config m_shadowpass; // parametrs of most recent shadowmap pass - renderpass_config m_cabshadowpass; // parameters of most recent cab shadowmap pass - std::vector m_pickcontrolsitems; - TSubModel *m_pickcontrolitem { nullptr }; - std::vector m_picksceneryitems; - scene::basic_node *m_picksceneryitem { nullptr }; - glm::vec3 m_worldmousecoordinates { 0.f }; - std::vector> m_control_pick_requests; - std::vector> m_node_pick_requests; -#ifdef EU07_USE_DEBUG_CAMERA - renderpass_config m_worldcamera; // debug item -#endif - bool m_isATI; - - static bool renderer_register; - - class opengl_imgui_renderer : public imgui_renderer - { - virtual bool Init() override; - virtual void Shutdown() override; - virtual void BeginFrame() override; - virtual void Render() override; - virtual std::uint64_t Create_Image(std::uint8_t const *Rgba, int const Width, int const Height) override; - virtual void Release_Image(std::uint64_t const Image) override; - } m_imgui_renderer; - - virtual imgui_renderer *GetImguiRenderer() override - { - return &m_imgui_renderer; - } -}; - -//--------------------------------------------------------------------------- diff --git a/rendering/openglskydome.cpp b/rendering/openglskydome.cpp deleted file mode 100644 index 6506482b..00000000 --- a/rendering/openglskydome.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#include "stdafx.h" -#include "rendering/openglskydome.h" - -#include "simulation/simulationenvironment.h" - -opengl_skydome::~opengl_skydome() { - - if( m_vertexbuffer != -1 ) { ::glDeleteBuffers( 1, &m_vertexbuffer ); } - if( m_indexbuffer != -1 ) { ::glDeleteBuffers( 1, &m_indexbuffer ); } - if( m_coloursbuffer != -1 ) { ::glDeleteBuffers( 1, &m_coloursbuffer ); } -} - -void opengl_skydome::update() { - - auto &skydome { simulation::Environment.skydome() }; - // cache entry state - ::glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT ); - // setup gpu data buffers: - // ...static data... - if( m_indexbuffer == (GLuint)-1 ) { - ::glGenBuffers( 1, &m_indexbuffer ); - if( ( m_indexbuffer > 0 ) && ( m_indexbuffer != (GLuint)-1 ) ) { - ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer ); - auto const &indices { skydome.indices() }; - ::glBufferData( GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof( unsigned short ), indices.data(), GL_STATIC_DRAW ); - m_indexcount = indices.size(); - } - } - if( m_vertexbuffer == (GLuint)-1 ) { - ::glGenBuffers( 1, &m_vertexbuffer ); - if( ( m_vertexbuffer > 0 ) && ( m_vertexbuffer != (GLuint)-1 ) ) { - ::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer ); - auto const &vertices { skydome.vertices() }; - ::glBufferData( GL_ARRAY_BUFFER, vertices.size() * sizeof( glm::vec3 ), vertices.data(), GL_STATIC_DRAW ); - } - } - // ...and dynamic data - if( m_coloursbuffer == (GLuint)-1 ) { - ::glGenBuffers( 1, &m_coloursbuffer ); - if( ( m_coloursbuffer > 0 ) && ( m_coloursbuffer != (GLuint)-1 ) ) { - ::glBindBuffer( GL_ARRAY_BUFFER, m_coloursbuffer ); - auto const &colors { skydome.colors() }; - ::glBufferData( GL_ARRAY_BUFFER, colors.size() * sizeof( glm::vec3 ), colors.data(), GL_DYNAMIC_DRAW ); - } - } - // ship the current dynamic data to the gpu - // TODO: ship the data if it changed since the last update - if (true == skydome.is_dirty() && ( m_coloursbuffer > 0 ) && ( m_coloursbuffer != (GLuint)-1 )) { - ::glBindBuffer( GL_ARRAY_BUFFER, m_coloursbuffer ); - auto &colors{ skydome.colors() }; - /* - float twilightfactor = std::clamp( -simulation::Environment.sun().getAngle(), 0.0f, 18.0f ) / 18.0f; - auto gamma = std::lerp( glm::vec3( 0.45f ), glm::vec3( 1.0f ), twilightfactor ); - for( auto & color : colors ) { - color = glm::pow( color, gamma ); - } - */ - ::glBufferSubData( GL_ARRAY_BUFFER, 0, colors.size() * sizeof( glm::vec3 ), colors.data() ); - skydome.is_dirty() = false; - } - // cleanup - ::glPopClientAttrib(); - ::glBindBuffer( GL_ARRAY_BUFFER, 0 ); - ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); -} - -// render skydome to screen -void opengl_skydome::render() { - - if( ( m_indexbuffer == (GLuint)-1 ) - || ( m_vertexbuffer == (GLuint)-1 ) - || ( m_coloursbuffer == (GLuint)-1 ) ) { - return; - } - // setup: - ::glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT ); - // ...positions... - ::glBindBuffer( GL_ARRAY_BUFFER, m_vertexbuffer ); - ::glVertexPointer( 3, GL_FLOAT, sizeof( glm::vec3 ), reinterpret_cast( 0 ) ); - ::glEnableClientState( GL_VERTEX_ARRAY ); - // ...colours... - ::glBindBuffer( GL_ARRAY_BUFFER, m_coloursbuffer ); - ::glColorPointer( 3, GL_FLOAT, sizeof( glm::vec3 ), reinterpret_cast( 0 ) ); - ::glEnableClientState( GL_COLOR_ARRAY ); - // ...indices - ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexbuffer ); - // draw - ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); - ::glDrawElements( GL_TRIANGLES, static_cast( m_indexcount ), GL_UNSIGNED_SHORT, reinterpret_cast( 0 ) ); - // cleanup - ::glPopClientAttrib(); - ::glBindBuffer( GL_ARRAY_BUFFER, 0 ); - ::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); -} diff --git a/rendering/openglskydome.h b/rendering/openglskydome.h deleted file mode 100644 index 90d88f89..00000000 --- a/rendering/openglskydome.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -This Source Code Form is subject to the -terms of the Mozilla Public License, v. -2.0. If a copy of the MPL was not -distributed with this file, You can -obtain one at -http://mozilla.org/MPL/2.0/. -*/ - -#pragma once - -class opengl_skydome { - -public: -// constructors - opengl_skydome() = default; -// destructor - ~opengl_skydome(); -// methods - // updates data stores on the opengl end. NOTE: unbinds buffers - void update(); - // draws the skydome - void render(); - -private: -// members - GLuint m_indexbuffer{ (GLuint)-1 }; // id of the buffer holding index data on the opengl end - std::size_t m_indexcount { 0 }; - GLuint m_vertexbuffer{ (GLuint)-1 }; // id of the buffer holding vertex data on the opengl end - GLuint m_coloursbuffer{ (GLuint)-1 }; // id of the buffer holding colour data on the opengl end -}; diff --git a/rendering/particles.h b/rendering/particles.h index 1d225e54..df592014 100644 --- a/rendering/particles.h +++ b/rendering/particles.h @@ -164,8 +164,6 @@ private: // holds all particle emitters defined in the scene and updates their state class particle_manager { - friend opengl_renderer; - public: // types using source_sequence = std::vector; diff --git a/scene/scene.h b/scene/scene.h index 086e2e83..c2369584 100644 --- a/scene/scene.h +++ b/scene/scene.h @@ -26,7 +26,6 @@ http://mozilla.org/MPL/2.0/. #include "input/command.h" #include "utilities/utilities.h" -class opengl_renderer; class opengl33_renderer; namespace scene { @@ -96,7 +95,6 @@ struct terrain_block { // TBD, TODO: replace with quadtree scheme? class basic_cell { - friend opengl_renderer; friend opengl33_renderer; public: @@ -294,7 +292,6 @@ public: // basic scene partitioning structure, holds terrain geometry and collection of cells class basic_section { - friend opengl_renderer; friend opengl33_renderer; public: @@ -418,7 +415,6 @@ public: // top-level of scene spatial structure, holds collection of sections class basic_region { - friend opengl_renderer; friend opengl33_renderer; public: diff --git a/simulation/simulationenvironment.h b/simulation/simulationenvironment.h index 218a2d18..84082470 100644 --- a/simulation/simulationenvironment.h +++ b/simulation/simulationenvironment.h @@ -17,13 +17,11 @@ http://mozilla.org/MPL/2.0/. #include "rendering/precipitation.h" #include "audio/sound.h" -class opengl_renderer; class opengl33_renderer; // wrapper for environment elements -- sky, sun, stars, clouds etc class world_environment { - friend opengl_renderer; friend opengl33_renderer; public: diff --git a/utilities/Classes.h b/utilities/Classes.h index 6a5ca167..f4bb3c7a 100644 --- a/utilities/Classes.h +++ b/utilities/Classes.h @@ -12,7 +12,6 @@ http://mozilla.org/MPL/2.0/. //--------------------------------------------------------------------------- // Ra: zestaw klas do robienia wskaźników, aby uporządkować nagłówki //--------------------------------------------------------------------------- -class opengl_renderer; class opengl33_renderer; class TTrack; // odcinek trajektorii class basic_event; diff --git a/utilities/Globals.cpp b/utilities/Globals.cpp index b4f2e14b..8ea9aa34 100644 --- a/utilities/Globals.cpp +++ b/utilities/Globals.cpp @@ -283,7 +283,10 @@ bool global_settings::ConfigParseGraphics(cParser& Parser, const std::string& to if (token == "usevbo") { - ParseOne(Parser, bUseVBO); + // only the removed legacy renderer had a non-VBO path; read and ignore so old eu07.ini files still load + bool usevbo{true}; + ParseOne(Parser, usevbo); + WriteLog("eu07.ini: \"usevbo\" is no longer supported and was ignored"); return true; } @@ -323,8 +326,12 @@ bool global_settings::ConfigParseGraphics(cParser& Parser, const std::string& to GfxRenderer = "experimental"; } - BasicRenderer = GfxRenderer == "simple"; - LegacyRenderer = !NvRenderer && GfxRenderer != "default"; + if (GfxRenderer != "default" && GfxRenderer != "experimental") + { + // the legacy OpenGL 2.1 renderer ("legacy", "simple" and other values) was removed + WriteLog("eu07.ini: gfxrenderer \"" + GfxRenderer + "\" is no longer supported, using \"default\""); + GfxRenderer = "default"; + } return true; } @@ -1574,7 +1581,6 @@ global_settings::export_as_text( std::ostream &Output ) const { export_as_text( Output, "defaultext", szDefaultExt ); export_as_text( Output, "newaircouplers", bnewAirCouplers ); export_as_text( Output, "anisotropicfiltering", AnisotropicFiltering ); - export_as_text( Output, "usevbo", bUseVBO ); export_as_text( Output, "feedbackmode", iFeedbackMode ); export_as_text( Output, "feedbackport", iFeedbackPort ); export_as_text( Output, "multiplayer", iMultiplayer ); diff --git a/utilities/Globals.h b/utilities/Globals.h index ad216cd9..0b9917c2 100644 --- a/utilities/Globals.h +++ b/utilities/Globals.h @@ -153,9 +153,7 @@ struct global_settings { int DynamicLightCount{ 7 }; bool ScaleSpecularValues{ true }; std::string GfxRenderer{ "default" }; - bool LegacyRenderer{ false }; bool NvRenderer{ false }; - bool BasicRenderer{ false }; bool RenderShadows{ true }; bool applicationQuitOrder{false}; int RenderCabShadowsRange{ 0 }; @@ -169,7 +167,6 @@ struct global_settings { float range_instances{ 750.f }; float range_vehicles{ 250.f }; } reflectiontune; - bool bUseVBO{ true }; // czy jest VBO w karcie graficznej (czy użyć) float AnisotropicFiltering{ 8.f }; // requested level of anisotropic filtering. TODO: move it to renderer object float FieldOfView{ 45.f }; // vertical field of view for the camera. TODO: move it to the renderer bool EditorOrtho{ false }; diff --git a/vehicle/DynObj.h b/vehicle/DynObj.h index 36335a32..687dafe4 100644 --- a/vehicle/DynObj.h +++ b/vehicle/DynObj.h @@ -188,7 +188,6 @@ struct material_data { class TDynamicObject { // klasa pojazdu - friend opengl_renderer; friend opengl33_renderer; public: diff --git a/world/Track.h b/world/Track.h index 72a6b7ff..51bb1d6b 100644 --- a/world/Track.h +++ b/world/Track.h @@ -151,7 +151,6 @@ private: // trajektoria ruchu - opakowanie class TTrack : public scene::basic_node { - friend opengl_renderer; friend opengl33_renderer; // NOTE: temporary arrangement friend itemproperties_panel; diff --git a/world/Traction.h b/world/Traction.h index 6506dfb3..6a041f28 100644 --- a/world/Traction.h +++ b/world/Traction.h @@ -20,7 +20,6 @@ class TTractionPowerSource; class TTraction : public scene::basic_node { - friend opengl_renderer; friend opengl33_renderer; public: // na razie