From 1f0d8c4dc8a9099be315e4d942ac6f07f2271b9f Mon Sep 17 00:00:00 2001 From: milek7 Date: Sat, 2 Mar 2019 14:32:39 +0100 Subject: [PATCH] semaphores controllable on minimap --- AnimModel.cpp | 18 ++ AnimModel.h | 1 + CMakeLists.txt | 1 + Model3d.cpp | 24 +++ Model3d.h | 2 + Names.h | 7 + command.cpp | 1 + command.h | 1 + renderer.cpp | 358 ++++++++++++++++++---------------- renderer.h | 65 +++--- scene.cpp | 27 ++- scene.h | 19 +- scenenodegroups.cpp | 72 ++++--- scenenodegroups.h | 3 + simulation.cpp | 8 + simulationstateserializer.cpp | 1 + translation.cpp | 6 + translation.h | 3 + uilayer.cpp | 16 +- uilayer.h | 3 + widgets/map.cpp | 218 +++++++++++++++------ widgets/map.h | 33 +++- widgets/map_objects.cpp | 27 ++- widgets/map_objects.h | 28 ++- widgets/popup.cpp | 29 +++ widgets/popup.h | 22 +++ 26 files changed, 678 insertions(+), 315 deletions(-) create mode 100644 widgets/popup.cpp create mode 100644 widgets/popup.h diff --git a/AnimModel.cpp b/AnimModel.cpp index a4f10bc4..5c23c0a0 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -591,6 +591,24 @@ void TAnimModel::LightSet(int const n, float const v) lsLights[ n ] = v; }; +std::optional> > TAnimModel::LightGet(const int n) +{ + if (n >= iMaxNumLights) + return std::nullopt; + if (!LightsOn[n] && !LightsOff[n]) + return std::nullopt; + + std::optional color; + + if (m_lightcolors[n].r >= 0.0f) + color.emplace(m_lightcolors[n]); + + if (!color && LightsOn[n]) + color = LightsOn[n]->GetDiffuse(); + + return std::make_tuple(lsLights[n], m_lightopacities[n], color); +} + void TAnimModel::AnimUpdate(double dt) { // wykonanie zakolejkowanych animacji, nawet gdy modele nie są aktualnie wyświetlane TAnimContainer *p = TAnimModel::acAnimList; diff --git a/AnimModel.h b/AnimModel.h index 4c0a3cd1..21436609 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -121,6 +121,7 @@ public: TAnimContainer * AddContainer(std::string const &Name); TAnimContainer * GetContainer(std::string const &Name = ""); void LightSet( int const n, float const v ); + std::optional > > LightGet( int const n ); int TerrainCount(); TSubModel * TerrainSquare(int n); int Flags(); diff --git a/CMakeLists.txt b/CMakeLists.txt index 745b5d11..cb1ae54f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,7 @@ set(SOURCES "widgets/vehiclelist.cpp" "widgets/map.cpp" "widgets/map_objects.cpp" +"widgets/popup.cpp" "ref/glad/src/glad.c" diff --git a/Model3d.cpp b/Model3d.cpp index 80c52e35..e7009f21 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -91,6 +91,30 @@ TSubModel::SetDiffuseOverride( glm::vec3 const &Color, bool const Includechildre } } +std::optional +TSubModel::GetDiffuse(float Includesiblings) { + if (eType == TP_FREESPOTLIGHT) { + if (DiffuseOverride.x >= 0.0f) + return DiffuseOverride; + else + return glm::vec3(f4Diffuse); + } + + if (Includesiblings) { + auto sibling = this; + while ((sibling = sibling->Next)) { + auto result = sibling->GetDiffuse(true); + if (result) + return result; + } + } + + if (Child) + return Child->GetDiffuse(true); + + return std::nullopt; +} + // sets visibility level (alpha component) to specified value void TSubModel::SetVisibilityLevel( float const Level, bool const Includechildren, bool const Includesiblings ) { diff --git a/Model3d.h b/Model3d.h index 69a8cdd5..85438b1c 100644 --- a/Model3d.h +++ b/Model3d.h @@ -196,6 +196,8 @@ public: void ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular ); // sets rgb components of diffuse color override to specified value void SetDiffuseOverride( glm::vec3 const &Color, bool const Includechildren = false, bool const Includesiblings = false ); + // gets rgb components of any freespot diffuse color (searches also in children) + std::optional GetDiffuse(float Includesiblings = false); // sets visibility level (alpha component) to specified value void SetVisibilityLevel( float const Level, bool const Includechildren = false, bool const Includesiblings = false ); // sets light level (alpha component of illumination color) to specified value diff --git a/Names.h b/Names.h index 67b64068..8232ad2b 100644 --- a/Names.h +++ b/Names.h @@ -62,6 +62,13 @@ public: m_itemmap.erase(lookup); } + uint32_t find_id( std::string const &Name) const { + auto lookup = m_itemmap.find( Name ); + return ( + lookup != m_itemmap.end() ? + lookup->second : + -1 ); + } // locates item with specified name. returns pointer to the item, or nullptr Type_ * find( std::string const &Name ) const { diff --git a/command.cpp b/command.cpp index b7ee82cb..0b28391b 100644 --- a/command.cpp +++ b/command.cpp @@ -236,6 +236,7 @@ commanddescription_sequence Commands_descriptions = { { "pausetoggle", command_target::simulation, command_mode::oneoff }, { "entervehicle", command_target::simulation, command_mode::oneoff }, { "queueevent", command_target::simulation, command_mode::oneoff }, + { "setlight", command_target::simulation, command_mode::oneoff }, }; } // simulation diff --git a/command.h b/command.h index 21330d17..8b34478c 100644 --- a/command.h +++ b/command.h @@ -230,6 +230,7 @@ enum class user_command { pausetoggle, entervehicle, queueevent, + setlight, none = -1 }; diff --git a/renderer.cpp b/renderer.cpp index 89f735e8..8feefd70 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -189,77 +189,15 @@ bool opengl_renderer::Init(GLFWwindow *Window) if (!Global.gfx_usegles && samples > 1) glEnable(GL_MULTISAMPLE); - if (!Global.gfx_skippipeline) - { - m_msaa_rbc = std::make_unique(); - m_msaa_rbc->alloc(Global.gfx_format_color, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, samples); + m_pfx_motionblur = std::make_unique("motionblur"); + m_pfx_tonemapping = std::make_unique("tonemapping"); - m_msaa_rbd = std::make_unique(); - m_msaa_rbd->alloc(Global.gfx_format_depth, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, samples); + m_empty_cubemap = std::make_unique(); + m_empty_cubemap->alloc(Global.gfx_format_color, 16, 16, GL_RGB, GL_FLOAT); - m_msaa_fb = std::make_unique(); - m_msaa_fb->attach(*m_msaa_rbc, GL_COLOR_ATTACHMENT0); - m_msaa_fb->attach(*m_msaa_rbd, GL_DEPTH_ATTACHMENT); - - if (Global.gfx_postfx_motionblur_enabled) - { - m_msaa_rbv = std::make_unique(); - m_msaa_rbv->alloc(Global.gfx_postfx_motionblur_format, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, samples); - m_msaa_fb->attach(*m_msaa_rbv, GL_COLOR_ATTACHMENT1); - - m_main_tex = std::make_unique(); - m_main_tex->alloc_rendertarget(Global.gfx_format_color, GL_RGB, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, 1, GL_CLAMP_TO_EDGE); - - m_main_fb = std::make_unique(); - m_main_fb->attach(*m_main_tex, GL_COLOR_ATTACHMENT0); - - m_main_texv = std::make_unique(); - m_main_texv->alloc_rendertarget(Global.gfx_postfx_motionblur_format, GL_RG, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height); - m_main_fb->attach(*m_main_texv, GL_COLOR_ATTACHMENT1); - m_main_fb->setup_drawing(2); - - if (!m_main_fb->is_complete()) - return false; - - m_pfx_motionblur = std::make_unique("motionblur"); - - WriteLog("motion blur enabled"); - } - - if (!m_msaa_fb->is_complete()) - return false; - - m_main2_tex = std::make_unique(); - m_main2_tex->alloc_rendertarget(Global.gfx_format_color, GL_RGB, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height); - - m_main2_fb = std::make_unique(); - m_main2_fb->attach(*m_main2_tex, GL_COLOR_ATTACHMENT0); - if (!m_main2_fb->is_complete()) - return false; - - m_pfx_tonemapping = std::make_unique("tonemapping"); - } - - if (Global.gfx_shadowmap_enabled) - { - m_shadow_fb = std::make_unique(); - m_shadow_tex = std::make_unique(); - m_shadow_tex->alloc_rendertarget(Global.gfx_format_depth, GL_DEPTH_COMPONENT, m_shadowbuffersize, m_shadowbuffersize); - m_shadow_fb->attach(*m_shadow_tex, GL_DEPTH_ATTACHMENT); - - if (!m_shadow_fb->is_complete()) - return false; - - m_cabshadows_fb = std::make_unique(); - m_cabshadows_tex = std::make_unique(); - m_cabshadows_tex->alloc_rendertarget(Global.gfx_format_depth, GL_DEPTH_COMPONENT, m_shadowbuffersize, m_shadowbuffersize); - m_cabshadows_fb->attach(*m_cabshadows_tex, GL_DEPTH_ATTACHMENT); - - if (!m_cabshadows_fb->is_complete()) - return false; - - WriteLog("shadows enabled"); - } + m_current_viewport = &default_viewport; + if (!init_viewport(default_viewport)) + return false; m_pick_tex = std::make_unique(); m_pick_tex->alloc_rendertarget(GL_RGB8, GL_RGB, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE); @@ -272,30 +210,6 @@ bool opengl_renderer::Init(GLFWwindow *Window) if (!m_pick_fb->is_complete()) return false; - if (Global.gfx_envmap_enabled) - { - m_env_rb = std::make_unique(); - m_env_rb->alloc(Global.gfx_format_depth, gl::ENVMAP_SIZE, gl::ENVMAP_SIZE); - m_env_tex = std::make_unique(); - m_env_tex->alloc(Global.gfx_format_color, gl::ENVMAP_SIZE, gl::ENVMAP_SIZE, GL_RGB, GL_FLOAT); - m_empty_cubemap = std::make_unique(); - m_empty_cubemap->alloc(Global.gfx_format_color, 16, 16, GL_RGB, GL_FLOAT); - - m_env_fb = std::make_unique(); - - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - for (int i = 0; i < 6; i++) - { - m_env_fb->attach(*m_empty_cubemap, i, GL_COLOR_ATTACHMENT0); - m_env_fb->clear(GL_COLOR_BUFFER_BIT); - } - - m_env_fb->detach(GL_COLOR_ATTACHMENT0); - m_env_fb->attach(*m_env_rb, GL_DEPTH_ATTACHMENT); - - WriteLog("envmap enabled"); - } - m_picking_pbo = std::make_unique(); m_picking_node_pbo = std::make_unique(); @@ -357,6 +271,103 @@ bool opengl_renderer::Init(GLFWwindow *Window) return true; } +bool opengl_renderer::init_viewport(viewport_data &vp) +{ + int samples = 1 << Global.iMultisampling; + + if (!Global.gfx_skippipeline) + { + vp.msaa_rbc = std::make_unique(); + vp.msaa_rbc->alloc(Global.gfx_format_color, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, samples); + + vp.msaa_rbd = std::make_unique(); + vp.msaa_rbd->alloc(Global.gfx_format_depth, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, samples); + + vp.msaa_fb = std::make_unique(); + vp.msaa_fb->attach(*vp.msaa_rbc, GL_COLOR_ATTACHMENT0); + vp.msaa_fb->attach(*vp.msaa_rbd, GL_DEPTH_ATTACHMENT); + + if (Global.gfx_postfx_motionblur_enabled) + { + vp.msaa_rbv = std::make_unique(); + vp.msaa_rbv->alloc(Global.gfx_postfx_motionblur_format, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, samples); + vp.msaa_fb->attach(*vp.msaa_rbv, GL_COLOR_ATTACHMENT1); + + vp.main_tex = std::make_unique(); + vp.main_tex->alloc_rendertarget(Global.gfx_format_color, GL_RGB, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, 1, GL_CLAMP_TO_EDGE); + + vp.main_fb = std::make_unique(); + vp.main_fb->attach(*vp.main_tex, GL_COLOR_ATTACHMENT0); + + vp.main_texv = std::make_unique(); + vp.main_texv->alloc_rendertarget(Global.gfx_postfx_motionblur_format, GL_RG, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height); + vp.main_fb->attach(*vp.main_texv, GL_COLOR_ATTACHMENT1); + vp.main_fb->setup_drawing(2); + + if (!vp.main_fb->is_complete()) + return false; + + WriteLog("motion blur enabled"); + } + + if (!vp.msaa_fb->is_complete()) + return false; + + vp.main2_tex = std::make_unique(); + vp.main2_tex->alloc_rendertarget(Global.gfx_format_color, GL_RGB, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height); + + vp.main2_fb = std::make_unique(); + vp.main2_fb->attach(*vp.main2_tex, GL_COLOR_ATTACHMENT0); + if (!vp.main2_fb->is_complete()) + return false; + } + + if (Global.gfx_shadowmap_enabled) + { + vp.shadow_fb = std::make_unique(); + vp.shadow_tex = std::make_unique(); + vp.shadow_tex->alloc_rendertarget(Global.gfx_format_depth, GL_DEPTH_COMPONENT, m_shadowbuffersize, m_shadowbuffersize); + vp.shadow_fb->attach(*vp.shadow_tex, GL_DEPTH_ATTACHMENT); + + if (!vp.shadow_fb->is_complete()) + return false; + + vp.cabshadows_fb = std::make_unique(); + vp.cabshadows_tex = std::make_unique(); + vp.cabshadows_tex->alloc_rendertarget(Global.gfx_format_depth, GL_DEPTH_COMPONENT, m_shadowbuffersize, m_shadowbuffersize); + vp.cabshadows_fb->attach(*vp.cabshadows_tex, GL_DEPTH_ATTACHMENT); + + if (!vp.cabshadows_fb->is_complete()) + return false; + + WriteLog("shadows enabled"); + } + + if (Global.gfx_envmap_enabled) + { + vp.env_rb = std::make_unique(); + vp.env_rb->alloc(Global.gfx_format_depth, gl::ENVMAP_SIZE, gl::ENVMAP_SIZE); + vp.env_tex = std::make_unique(); + vp.env_tex->alloc(Global.gfx_format_color, gl::ENVMAP_SIZE, gl::ENVMAP_SIZE, GL_RGB, GL_FLOAT); + + vp.env_fb = std::make_unique(); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + for (int i = 0; i < 6; i++) + { + vp.env_fb->attach(*m_empty_cubemap, i, GL_COLOR_ATTACHMENT0); + vp.env_fb->clear(GL_COLOR_BUFFER_BIT); + } + + vp.env_fb->detach(GL_COLOR_ATTACHMENT0); + vp.env_fb->attach(*vp.env_rb, GL_DEPTH_ATTACHMENT); + + WriteLog("envmap enabled"); + } + + return true; +} + std::unique_ptr opengl_renderer::make_shader(std::string v, std::string f) { gl::shader vert(v); @@ -401,7 +412,7 @@ bool opengl_renderer::Render() m_renderpass.draw_mode = rendermode::none; // force setup anew m_debugstats = debug_stats(); - Render_pass(rendermode::color); + Render_pass(default_viewport, rendermode::color); m_drawcount = m_cellqueue.size(); m_debugtimestext.clear(); @@ -464,7 +475,7 @@ void opengl_renderer::draw_debug_ui() } // runs jobs needed to generate graphics for specified render pass -void opengl_renderer::Render_pass(rendermode const Mode) +void opengl_renderer::Render_pass(viewport_data &vp, rendermode const Mode) { setup_pass(m_renderpass, Mode); switch (m_renderpass.draw_mode) @@ -506,9 +517,9 @@ void opengl_renderer::Render_pass(rendermode const Mode) glDebug("render shadowmap start"); Timer::subsystem.gfx_shadows.start(); - Render_pass(rendermode::shadows); + Render_pass(vp, rendermode::shadows); if (!FreeFlyModeFlag) - Render_pass(rendermode::cabshadows); + Render_pass(vp, rendermode::cabshadows); setup_pass(m_renderpass, Mode); // restore draw mode. TBD, TODO: render mode stack Timer::subsystem.gfx_shadows.stop(); @@ -518,9 +529,9 @@ void opengl_renderer::Render_pass(rendermode const Mode) if (Global.gfx_envmap_enabled) { // potentially update environmental cube map - if (Render_reflections()) + if (Render_reflections(vp)) setup_pass(m_renderpass, Mode); // restore color pass settings - setup_env_map(m_env_tex.get()); + setup_env_map(vp.env_tex.get()); } glClearColor(0.0f, 0.0f, 0.0f, 0.0f); @@ -528,16 +539,16 @@ void opengl_renderer::Render_pass(rendermode const Mode) setup_drawing(false); if (!Global.gfx_skippipeline) { - m_msaa_fb->bind(); + vp.msaa_fb->bind(); if (Global.gfx_postfx_motionblur_enabled) - m_msaa_fb->setup_drawing(2); + vp.msaa_fb->setup_drawing(2); else - m_msaa_fb->setup_drawing(1); + vp.msaa_fb->setup_drawing(1); glViewport(0, 0, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height); - m_msaa_fb->clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + vp.msaa_fb->clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { @@ -579,7 +590,7 @@ void opengl_renderer::Render_pass(rendermode const Mode) { glDebug("render cab opaque"); if (Global.gfx_shadowmap_enabled) - setup_shadow_map(m_cabshadows_tex.get(), m_cabshadowpass); + setup_shadow_map(vp.cabshadows_tex.get(), m_cabshadowpass); auto const *vehicle = simulation::Train->Dynamic(); Render_cab(vehicle, vehicle->InteriorLightLevel, false); @@ -590,7 +601,7 @@ void opengl_renderer::Render_pass(rendermode const Mode) model_ubs.future = future; if (Global.gfx_shadowmap_enabled) - setup_shadow_map(m_shadow_tex.get(), m_shadowpass); + setup_shadow_map(vp.shadow_tex.get(), m_shadowpass); Render(simulation::Region); @@ -608,7 +619,7 @@ void opengl_renderer::Render_pass(rendermode const Mode) glDebug("render translucent cab"); model_ubs.future = glm::mat4(); if (Global.gfx_shadowmap_enabled) - setup_shadow_map(m_cabshadows_tex.get(), m_cabshadowpass); + setup_shadow_map(vp.cabshadows_tex.get(), m_cabshadowpass); // cache shadow colour in case we need to account for cab light auto const *vehicle{simulation::Train->Dynamic()}; if (Global.Overcast > 1.0f) @@ -633,24 +644,24 @@ void opengl_renderer::Render_pass(rendermode const Mode) { if (Global.gfx_postfx_motionblur_enabled) { - m_main_fb->clear(GL_COLOR_BUFFER_BIT); - m_msaa_fb->blit_to(m_main_fb.get(), Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, GL_COLOR_BUFFER_BIT, GL_COLOR_ATTACHMENT0); - m_msaa_fb->blit_to(m_main_fb.get(), Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, GL_COLOR_BUFFER_BIT, GL_COLOR_ATTACHMENT1); + vp.main_fb->clear(GL_COLOR_BUFFER_BIT); + vp.msaa_fb->blit_to(vp.main_fb.get(), Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, GL_COLOR_BUFFER_BIT, GL_COLOR_ATTACHMENT0); + vp.msaa_fb->blit_to(vp.main_fb.get(), Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, GL_COLOR_BUFFER_BIT, GL_COLOR_ATTACHMENT1); model_ubs.param[0].x = m_framerate / (1.0 / Global.gfx_postfx_motionblur_shutter); model_ubo->update(model_ubs); - m_pfx_motionblur->apply({m_main_tex.get(), m_main_texv.get()}, m_main2_fb.get()); + m_pfx_motionblur->apply({vp.main_tex.get(), vp.main_texv.get()}, vp.main2_fb.get()); } else { - m_main2_fb->clear(GL_COLOR_BUFFER_BIT); - m_msaa_fb->blit_to(m_main2_fb.get(), Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, GL_COLOR_BUFFER_BIT, GL_COLOR_ATTACHMENT0); + vp.main2_fb->clear(GL_COLOR_BUFFER_BIT); + vp.msaa_fb->blit_to(vp.main2_fb.get(), Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, GL_COLOR_BUFFER_BIT, GL_COLOR_ATTACHMENT0); } if (!Global.gfx_usegles && !Global.gfx_shadergamma) glEnable(GL_FRAMEBUFFER_SRGB); glViewport(0, 0, Global.iWindowWidth, Global.iWindowHeight); - m_pfx_tonemapping->apply(*m_main2_tex, nullptr); + m_pfx_tonemapping->apply(*vp.main2_tex, nullptr); opengl_texture::reset_unit_cache(); } @@ -685,8 +696,8 @@ void opengl_renderer::Render_pass(rendermode const Mode) glEnable(GL_DEPTH_TEST); glViewport(0, 0, m_shadowbuffersize, m_shadowbuffersize); - m_shadow_fb->bind(); - m_shadow_fb->clear(GL_DEPTH_BUFFER_BIT); + vp.shadow_fb->bind(); + vp.shadow_fb->clear(GL_DEPTH_BUFFER_BIT); setup_matrices(); setup_drawing(false); @@ -703,7 +714,7 @@ void opengl_renderer::Render_pass(rendermode const Mode) glDisable(GL_POLYGON_OFFSET_FILL); - m_shadow_fb->unbind(); + vp.shadow_fb->unbind(); glDebug("rendermodeshadows ::end"); @@ -720,8 +731,8 @@ void opengl_renderer::Render_pass(rendermode const Mode) glEnable(GL_DEPTH_TEST); glViewport(0, 0, m_shadowbuffersize, m_shadowbuffersize); - m_cabshadows_fb->bind(); - m_cabshadows_fb->clear(GL_DEPTH_BUFFER_BIT); + vp.cabshadows_fb->bind(); + vp.cabshadows_fb->clear(GL_DEPTH_BUFFER_BIT); setup_matrices(); setup_drawing(false); @@ -735,7 +746,7 @@ void opengl_renderer::Render_pass(rendermode const Mode) glDisable(GL_POLYGON_OFFSET_FILL); - m_cabshadows_fb->unbind(); + vp.cabshadows_fb->unbind(); glDebug("rendermode::cabshadows end"); @@ -752,8 +763,8 @@ void opengl_renderer::Render_pass(rendermode const Mode) // NOTE: buffer attachment and viewport setup in this mode is handled by the wrapper method glEnable(GL_DEPTH_TEST); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - m_env_fb->clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - m_env_fb->bind(); + vp.env_fb->clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + vp.env_fb->bind(); setup_env_map(m_empty_cubemap.get()); @@ -769,13 +780,13 @@ void opengl_renderer::Render_pass(rendermode const Mode) // opaque parts... setup_drawing(false); - setup_shadow_map(m_shadow_tex.get(), m_shadowpass); + setup_shadow_map(vp.shadow_tex.get(), m_shadowpass); scene_ubs.projection = OpenGLMatrices.data(GL_PROJECTION); scene_ubo->update(scene_ubs); Render(simulation::Region); - m_env_fb->unbind(); + vp.env_fb->unbind(); glDebug("rendermode::reflections end"); @@ -840,7 +851,7 @@ void opengl_renderer::Render_pass(rendermode const Mode) } // creates dynamic environment cubemap -bool opengl_renderer::Render_reflections() +bool opengl_renderer::Render_reflections(viewport_data &vp) { if (Global.ReflectionUpdatesPerSecond == 0) return false; @@ -849,23 +860,23 @@ bool opengl_renderer::Render_reflections() auto const timestamp = time.wMilliseconds + time.wSecond * 1000 + time.wMinute * 1000 * 60 + time.wHour * 1000 * 60 * 60; if ((timestamp - m_environmentupdatetime < Global.ReflectionUpdatesPerSecond) - && (glm::length(m_renderpass.camera.position() - m_environmentupdatelocation) < 1000.0)) + && (glm::length(m_renderpass.pass_camera.position() - m_environmentupdatelocation) < 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(); + m_environmentupdatelocation = m_renderpass.pass_camera.position(); glViewport(0, 0, gl::ENVMAP_SIZE, gl::ENVMAP_SIZE); for (m_environmentcubetextureface = 0; m_environmentcubetextureface < 6; ++m_environmentcubetextureface) { - m_env_fb->attach(*m_env_tex, m_environmentcubetextureface, GL_COLOR_ATTACHMENT0); - if (m_env_fb->is_complete()) - Render_pass(rendermode::reflections); + vp.env_fb->attach(*vp.env_tex, m_environmentcubetextureface, GL_COLOR_ATTACHMENT0); + if (vp.env_fb->is_complete()) + Render_pass(vp, rendermode::reflections); } - m_env_tex->generate_mipmaps(); - m_env_fb->detach(GL_COLOR_ATTACHMENT0); + vp.env_tex->generate_mipmaps(); + vp.env_fb->detach(GL_COLOR_ATTACHMENT0); return true; } @@ -981,7 +992,7 @@ void opengl_renderer::setup_pass(renderpass_config &Config, rendermode const Mod } } // setup camera - auto &camera = Config.camera; + auto &camera = Config.pass_camera; glm::dmat4 viewmatrix(1.0); @@ -1020,7 +1031,7 @@ void opengl_renderer::setup_pass(renderpass_config &Config, rendermode const Mod auto const zfar = std::min(1.f, Global.shadowtune.depth / (Global.BaseDrawRange * Global.fDistanceFactor) * std::max(1.f, Global.ZoomFactor * 0.5f)); renderpass_config worldview; setup_pass(worldview, rendermode::color, 0.f, zfar, true); - auto &frustumchunkshapepoints = worldview.camera.frustum_points(); + auto &frustumchunkshapepoints = worldview.pass_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)); @@ -1028,7 +1039,7 @@ void opengl_renderer::setup_pass(renderpass_config &Config, rendermode const Mod // ...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, -0.2f), 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}; + camera.position() = worldview.pass_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); @@ -1136,11 +1147,11 @@ void opengl_renderer::setup_pass(renderpass_config &Config, rendermode const Mod void opengl_renderer::setup_matrices() { ::glMatrixMode(GL_PROJECTION); - OpenGLMatrices.load_matrix(m_renderpass.camera.projection()); + OpenGLMatrices.load_matrix(m_renderpass.pass_camera.projection()); // 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()))); + OpenGLMatrices.load_matrix(glm::mat4(glm::mat3(m_renderpass.pass_camera.modelview()))); } void opengl_renderer::setup_drawing(bool const Alpha) @@ -1213,9 +1224,9 @@ void opengl_renderer::setup_shadow_map(opengl_texture *tex, renderpass_config co 0.0, 0.0, 0.5, 0.0, // 0.5, 0.5, 0.5, 1.0 // ); - glm::mat4 depthproj = conf.camera.projection(); - glm::mat4 depthcam = conf.camera.modelview(); - glm::mat4 worldcam = m_renderpass.camera.modelview(); + glm::mat4 depthproj = conf.pass_camera.projection(); + glm::mat4 depthcam = conf.pass_camera.modelview(); + glm::mat4 worldcam = m_renderpass.pass_camera.modelview(); scene_ubs.lightview = coordmove * depthproj * depthcam * glm::inverse(worldcam); scene_ubo->update(scene_ubs); @@ -1626,13 +1637,18 @@ opengl_texture &opengl_renderer::Texture(texture_handle const Texture) const return m_textures.texture(Texture); } +void opengl_renderer::Update_AnimModel(TAnimModel *model) +{ + model->RaAnimate(m_framestamp); +} + 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()}; + glm::vec3 const cameraposition{m_renderpass.pass_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)); int const segmentcount = 2 * static_cast(std::ceil(m_renderpass.draw_range * Global.fDistanceFactor / scene::EU07_SECTIONSIZE)); @@ -1660,7 +1676,7 @@ void opengl_renderer::Render(scene::basic_region *Region) break; } auto *section{Region->m_sections[row * scene::EU07_REGIONSIDESECTIONCOUNT + column]}; - if ((section != nullptr) && (m_renderpass.camera.visible(section->m_area))) + if ((section != nullptr) && (m_renderpass.pass_camera.visible(section->m_area))) { m_sectionqueue.emplace_back(section); } @@ -1746,7 +1762,7 @@ void opengl_renderer::Render(section_sequence::iterator First, section_sequence: { // 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()}; + auto const originoffset{section->m_area.center - m_renderpass.pass_camera.position()}; ::glTranslated(originoffset.x, originoffset.y, originoffset.z); // render for (auto const &shape : section->m_shapes) @@ -1774,10 +1790,10 @@ void opengl_renderer::Render(section_sequence::iterator First, section_sequence: { for (auto &cell : section->m_cells) { - if ((true == cell.m_active) && (m_renderpass.camera.visible(cell.m_area))) + if ((true == cell.m_active) && (m_renderpass.pass_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); + m_cellqueue.emplace_back(glm::length2(m_renderpass.pass_camera.position() - cell.m_area.center), &cell); } } break; @@ -1825,7 +1841,7 @@ void opengl_renderer::Render(cell_sequence::iterator First, cell_sequence::itera { // 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()}; + auto const originoffset{cell->m_area.center - m_renderpass.pass_camera.position()}; ::glTranslated(originoffset.x, originoffset.y, originoffset.z); // render @@ -1846,7 +1862,7 @@ void opengl_renderer::Render(cell_sequence::iterator First, cell_sequence::itera { // 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()}; + auto const originoffset{cell->m_area.center - m_renderpass.pass_camera.position()}; ::glTranslated(originoffset.x, originoffset.y, originoffset.z); // render @@ -1866,7 +1882,7 @@ void opengl_renderer::Render(cell_sequence::iterator First, cell_sequence::itera // 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()}; + auto const originoffset{cell->m_area.center - m_renderpass.pass_camera.position()}; ::glTranslated(originoffset.x, originoffset.y, originoffset.z); // render // opaque non-instanced shapes @@ -1987,7 +2003,7 @@ void opengl_renderer::Render(scene::shape_node const &Shape, bool const Ignorera } default: { - distancesquared = glm::length2((data.area.center - m_renderpass.camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; + distancesquared = glm::length2((data.area.center - m_renderpass.pass_camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; break; } } @@ -2041,7 +2057,7 @@ void opengl_renderer::Render(TAnimModel *Instance) } default: { - distancesquared = Math3D::SquareMagnitude((Instance->location() - m_renderpass.camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; + distancesquared = Math3D::SquareMagnitude((Instance->location() - m_renderpass.pass_camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; break; } } @@ -2069,7 +2085,7 @@ void opengl_renderer::Render(TAnimModel *Instance) if (Instance->pModel) { // renderowanie rekurencyjne submodeli - Render(Instance->pModel, Instance->Material(), distancesquared, Instance->location() - m_renderpass.camera.position(), Instance->vAngle); + Render(Instance->pModel, Instance->Material(), distancesquared, Instance->location() - m_renderpass.pass_camera.position(), Instance->vAngle); } } @@ -2077,7 +2093,7 @@ bool opengl_renderer::Render(TDynamicObject *Dynamic) { glDebug("Render TDynamicObject"); - Dynamic->renderme = m_renderpass.camera.visible(Dynamic); + Dynamic->renderme = m_renderpass.pass_camera.visible(Dynamic); if (false == Dynamic->renderme) { return false; @@ -2087,7 +2103,7 @@ bool opengl_renderer::Render(TDynamicObject *Dynamic) // setup TSubModel::iInstance = reinterpret_cast(Dynamic); //żeby nie robić cudzych animacji - glm::dvec3 const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); + glm::dvec3 const originoffset = Dynamic->vPosition - m_renderpass.pass_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) @@ -2208,7 +2224,7 @@ bool opengl_renderer::Render_cab(TDynamicObject const *Dynamic, float const Ligh // setup shared by all render paths ::glPushMatrix(); - auto const originoffset = Dynamic->GetPosition() - m_renderpass.camera.position(); + auto const originoffset = Dynamic->GetPosition() - m_renderpass.pass_camera.position(); ::glTranslated(originoffset.x, originoffset.y, originoffset.z); ::glMultMatrixd(Dynamic->mMatrix.readArray()); @@ -2805,7 +2821,7 @@ void opengl_renderer::Render(TMemCell *Memcell) { ::glPushMatrix(); - auto const position = Memcell->location() - m_renderpass.camera.position(); + auto const position = Memcell->location() - m_renderpass.pass_camera.position(); ::glTranslated(position.x, position.y + 0.5, position.z); switch (m_renderpass.draw_mode) @@ -2912,7 +2928,7 @@ void opengl_renderer::Render_Alpha(cell_sequence::reverse_iterator First, cell_s { // 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()}; + auto const originoffset{cell->m_area.center - m_renderpass.pass_camera.position()}; ::glTranslated(originoffset.x, originoffset.y, originoffset.z); // render // NOTE: we can reuse the method used to draw opaque geometry @@ -2965,7 +2981,7 @@ void opengl_renderer::Render_Alpha(cell_sequence::reverse_iterator First, cell_s { // 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()}; + auto const originoffset{cell->m_area.center - m_renderpass.pass_camera.position()}; ::glTranslated(originoffset.x, originoffset.y, originoffset.z); Bind_Material(null_handle); // render @@ -3000,7 +3016,7 @@ void opengl_renderer::Render_Alpha(TAnimModel *Instance) case rendermode::shadows: default: { - distancesquared = glm::length2((Instance->location() - m_renderpass.camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; + distancesquared = glm::length2((Instance->location() - m_renderpass.pass_camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor; break; } } @@ -3013,7 +3029,7 @@ void opengl_renderer::Render_Alpha(TAnimModel *Instance) if (Instance->pModel) { // renderowanie rekurencyjne submodeli - Render_Alpha(Instance->pModel, Instance->Material(), distancesquared, Instance->location() - m_renderpass.camera.position(), Instance->vAngle); + Render_Alpha(Instance->pModel, Instance->Material(), distancesquared, Instance->location() - m_renderpass.pass_camera.position(), Instance->vAngle); } } @@ -3021,7 +3037,7 @@ void opengl_renderer::Render_Alpha(TTraction *Traction) { glDebug("Render_Alpha TTraction"); - auto const distancesquared { glm::length2( ( Traction->location() - m_renderpass.camera.position() ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor }; + auto const distancesquared { glm::length2( ( Traction->location() - m_renderpass.pass_camera.position() ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor }; if ((distancesquared < Traction->m_rangesquaredmin) || (distancesquared >= Traction->m_rangesquaredmax)) { return; @@ -3065,7 +3081,7 @@ void opengl_renderer::Render_Alpha(scene::lines_node const &Lines) auto const &data{Lines.data()}; - auto const distancesquared { glm::length2( ( data.area.center - m_renderpass.camera.position() ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor }; + auto const distancesquared { glm::length2( ( data.area.center - m_renderpass.pass_camera.position() ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor }; if ((distancesquared < data.rangesquared_min) || (distancesquared >= data.rangesquared_max)) { return; @@ -3099,7 +3115,7 @@ bool opengl_renderer::Render_Alpha(TDynamicObject *Dynamic) // setup TSubModel::iInstance = (size_t)Dynamic; //żeby nie robić cudzych animacji - glm::dvec3 const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); + glm::dvec3 const originoffset = Dynamic->vPosition - m_renderpass.pass_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) @@ -3414,7 +3430,7 @@ void opengl_renderer::Update_Pick_Control() pickbufferpos = glm::ivec2{mousepos.x * EU07_PICKBUFFERSIZE / std::max(1, Global.iWindowWidth), mousepos.y * EU07_PICKBUFFERSIZE / std::max(1, Global.iWindowHeight)}; pickbufferpos = glm::clamp(pickbufferpos, glm::ivec2(0, 0), glm::ivec2(EU07_PICKBUFFERSIZE - 1, EU07_PICKBUFFERSIZE - 1)); - Render_pass(rendermode::pickcontrols); + Render_pass(default_viewport, rendermode::pickcontrols); m_pick_fb->bind(); m_picking_pbo->request_read(pickbufferpos.x, pickbufferpos.y, 1, 1); m_pick_fb->unbind(); @@ -3453,7 +3469,7 @@ void opengl_renderer::Update_Pick_Node() pickbufferpos = glm::ivec2{mousepos.x * EU07_PICKBUFFERSIZE / std::max(1, Global.iWindowWidth), mousepos.y * EU07_PICKBUFFERSIZE / std::max(1, Global.iWindowHeight)}; pickbufferpos = glm::clamp(pickbufferpos, glm::ivec2(0, 0), glm::ivec2(EU07_PICKBUFFERSIZE - 1, EU07_PICKBUFFERSIZE - 1)); - Render_pass(rendermode::pickscenery); + Render_pass(default_viewport, rendermode::pickscenery); m_pick_fb->bind(); m_picking_node_pbo->request_read(pickbufferpos.x, pickbufferpos.y, 1, 1); m_pick_fb->unbind(); @@ -3503,11 +3519,11 @@ glm::dvec3 opengl_renderer::get_mouse_depth() } else { - gl::framebuffer::blit(m_msaa_fb.get(), m_depth_pointer_fb.get(), bufferpos.x, bufferpos.y, 1, 1, GL_DEPTH_BUFFER_BIT, 0); + gl::framebuffer::blit(m_current_viewport->msaa_fb.get(), m_depth_pointer_fb.get(), bufferpos.x, bufferpos.y, 1, 1, GL_DEPTH_BUFFER_BIT, 0); m_depth_pointer_fb->bind(); m_depth_pointer_pbo->request_read(0, 0, 1, 1, 4, GL_DEPTH_COMPONENT, GL_FLOAT); - m_msaa_fb->bind(); + m_current_viewport->msaa_fb->bind(); } } else @@ -3532,7 +3548,7 @@ glm::dvec3 opengl_renderer::get_mouse_depth() } else { - gl::framebuffer::blit(m_msaa_fb.get(), m_depth_pointer_fb.get(), 0, 0, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, GL_DEPTH_BUFFER_BIT, 0); + gl::framebuffer::blit(m_current_viewport->msaa_fb.get(), m_depth_pointer_fb.get(), 0, 0, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, GL_DEPTH_BUFFER_BIT, 0); m_empty_vao->bind(); m_depth_pointer_tex->bind(0); @@ -3542,7 +3558,7 @@ glm::dvec3 opengl_renderer::get_mouse_depth() m_depth_pointer_pbo->request_read(bufferpos.x, bufferpos.y, 1, 1, 16, GL_RGBA_INTEGER, GL_UNSIGNED_INT); m_depth_pointer_shader->unbind(); m_empty_vao->unbind(); - m_msaa_fb->bind(); + m_current_viewport->msaa_fb->bind(); } } @@ -3550,15 +3566,15 @@ glm::dvec3 opengl_renderer::get_mouse_depth() { if (GLAD_GL_ARB_clip_control || GLAD_GL_EXT_clip_control) { if (pointdepth > 0.0f) - m_worldmousecoordinates = glm::unProjectZO(glm::vec3(bufferpos, pointdepth), glm::mat4(glm::mat3(m_colorpass.camera.modelview())), m_colorpass.camera.projection(), + m_worldmousecoordinates = glm::unProjectZO(glm::vec3(bufferpos, pointdepth), glm::mat4(glm::mat3(m_colorpass.pass_camera.modelview())), m_colorpass.pass_camera.projection(), glm::vec4(0, 0, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height)); } else if (pointdepth < 1.0f) - m_worldmousecoordinates = glm::unProjectNO(glm::vec3(bufferpos, pointdepth), glm::mat4(glm::mat3(m_colorpass.camera.modelview())), m_colorpass.camera.projection(), + m_worldmousecoordinates = glm::unProjectNO(glm::vec3(bufferpos, pointdepth), glm::mat4(glm::mat3(m_colorpass.pass_camera.modelview())), m_colorpass.pass_camera.projection(), glm::vec4(0, 0, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height)); } } - return m_colorpass.camera.position() + glm::dvec3{m_worldmousecoordinates}; + return m_colorpass.pass_camera.position() + glm::dvec3{m_worldmousecoordinates}; } void opengl_renderer::Update(double const Deltatime) @@ -3659,7 +3675,7 @@ void opengl_renderer::Update_Lights(light_array &Lights) glDebug("Update_Lights"); // arrange the light array from closest to farthest from current position of the camera - auto const camera = m_renderpass.camera.position(); + auto const camera = m_renderpass.pass_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) diff --git a/renderer.h b/renderer.h index 442b2213..efe194bb 100644 --- a/renderer.h +++ b/renderer.h @@ -179,6 +179,7 @@ class opengl_renderer { return m_worldmousecoordinates; } + void Update_AnimModel(TAnimModel *model); // maintenance methods void Update(double const Deltatime); void Update_Pick_Control(); @@ -228,12 +229,36 @@ class opengl_renderer struct renderpass_config { - - opengl_camera camera; + opengl_camera pass_camera; + opengl_camera viewport_camera; rendermode draw_mode{rendermode::none}; float draw_range{0.0f}; }; + struct viewport_data { + std::unique_ptr msaa_fb; + std::unique_ptr msaa_rbc; + std::unique_ptr msaa_rbv; + std::unique_ptr msaa_rbd; + + std::unique_ptr main_fb; + std::unique_ptr main_texv; + std::unique_ptr main_tex; + + std::unique_ptr main2_fb; + std::unique_ptr main2_tex; + + std::unique_ptr shadow_fb; + std::unique_ptr shadow_tex; + + std::unique_ptr cabshadows_fb; + std::unique_ptr cabshadows_tex; + + std::unique_ptr env_fb; + std::unique_ptr env_rb; + std::unique_ptr env_tex; + }; + typedef std::vector opengllight_array; // methods @@ -246,9 +271,9 @@ class opengl_renderer void setup_env_map(gl::cubemap *tex); void setup_environment_light(TEnvironmentType const Environment = e_flat); // runs jobs needed to generate graphics for specified render pass - void Render_pass(rendermode const Mode); + void Render_pass(viewport_data &vp, rendermode const Mode); // creates dynamic environment cubemap - bool Render_reflections(); + bool Render_reflections(viewport_data &vp); bool Render(world_environment *Environment); void Render(scene::basic_region *Region); void Render(section_sequence::iterator First, section_sequence::iterator Last); @@ -277,6 +302,8 @@ class opengl_renderer glm::vec3 pick_color(std::size_t const Index); std::size_t pick_index(glm::ivec3 const &Color); + bool init_viewport(viewport_data &vp); + void draw(const gfx::geometry_handle &handle); void draw(std::vector::iterator begin, std::vector::iterator end); @@ -323,6 +350,7 @@ class opengl_renderer float m_fogrange = 2000.0f; renderpass_config m_renderpass; // parameters for current render pass + viewport_data *m_current_viewport; // active viewport 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 @@ -367,39 +395,20 @@ class opengl_renderer std::unique_ptr m_empty_vao; - std::unique_ptr m_msaa_fb; - std::unique_ptr m_msaa_rbc; - std::unique_ptr m_msaa_rbv; - std::unique_ptr m_msaa_rbd; + viewport_data default_viewport; - std::unique_ptr m_main_fb; - std::unique_ptr m_main_texv; - std::unique_ptr m_main_tex; + std::unique_ptr m_pfx_motionblur; + std::unique_ptr m_pfx_tonemapping; - std::unique_ptr m_main2_fb; - std::unique_ptr m_main2_tex; - - std::unique_ptr m_pfx_motionblur; - std::unique_ptr m_pfx_tonemapping; - - std::unique_ptr m_shadow_fb; - std::unique_ptr m_shadow_tex; std::unique_ptr m_shadow_shader; - std::unique_ptr m_alpha_shadow_shader; + std::unique_ptr m_alpha_shadow_shader; std::unique_ptr m_pick_fb; std::unique_ptr m_pick_tex; std::unique_ptr m_pick_rb; std::unique_ptr m_pick_shader; - std::unique_ptr m_cabshadows_fb; - std::unique_ptr m_cabshadows_tex; - - std::unique_ptr m_env_fb; - std::unique_ptr m_env_rb; - std::unique_ptr m_env_tex; - - std::unique_ptr m_empty_cubemap; + std::unique_ptr m_empty_cubemap; std::unique_ptr m_picking_pbo; std::unique_ptr m_picking_node_pbo; diff --git a/scene.cpp b/scene.cpp index c205438f..5b7d6fca 100644 --- a/scene.cpp +++ b/scene.cpp @@ -624,6 +624,24 @@ void basic_cell::get_map_active_switches(std::vector & path->get_map_active_switches(handles); } +TTrack* basic_cell::find_nearest_track_point(const glm::dvec3 &pos) +{ + float min = std::numeric_limits::max(); + TTrack *nearest = nullptr; + + for (auto *path : m_paths) { + for (auto &ep : path->endpoints()) { + float dist2 = glm::distance2(ep, pos); + if (dist2 < min) { + min = dist2; + nearest = path; + } + } + } + + return nearest; +} + // executes event assigned to specified launcher void basic_cell::launch_event( TEventLauncher *Launcher, bool local_only ) { @@ -1671,12 +1689,13 @@ void basic_region::create_map_geometry() s->create_map_geometry(m_map_geometrybank); } - std::vector vertices; - for (const auto sem : map::Semaphores) { - vertices.push_back(gfx::basic_vertex(sem->location, glm::vec3(), glm::vec3())); + std::vector poi_vertices; + for (const auto sem : map::Objects.entries) { + poi_vertices.push_back(gfx::basic_vertex(sem->location, glm::vec3(), glm::vec3())); } + gfx::geometrybank_handle poibank = GfxRenderer.Create_Bank(); - m_map_poipoints = GfxRenderer.Insert(vertices, poibank, GL_POINTS); + m_map_poipoints = GfxRenderer.Insert(poi_vertices, poibank, GL_POINTS); } } // scene diff --git a/scene.h b/scene.h index 196e4f02..e8b39d26 100644 --- a/scene.h +++ b/scene.h @@ -157,6 +157,7 @@ public: create_map_geometry(std::vector &Bank, const gfx::geometrybank_handle Extra); void get_map_active_switches(std::vector &handles); + TTrack *find_nearest_track_point(const glm::dvec3 &pos); // provides access to bounding area data bounding_area const & area() const { @@ -289,9 +290,9 @@ public: area() const { return m_area; } const gfx::geometrybank_handle get_map_geometry() - { - return m_map_geometryhandle; - } + { return m_map_geometryhandle;} + TTrack* find_nearest_track_point(const glm::dvec3 &pos) + { return cell(pos).find_nearest_track_point(pos); } private: // types @@ -406,11 +407,11 @@ public: void create_map_geometry(); basic_section* get_section(size_t section) - { - return m_sections[section]; - } + { return m_sections[section]; } gfx::geometrybank_handle get_map_poi_geometry() { return m_map_poipoints; } + TTrack* find_nearest_track_point(const glm::dvec3 &pos) + { return section(pos).find_nearest_track_point(pos); } private: // types @@ -432,9 +433,9 @@ private: static bool RaTriangleDivider( shape_node &Shape, std::deque &Shapes ); - // provides access to section enclosing specified point - basic_section & - section( glm::dvec3 const &Location ); + // provides access to section enclosing specified point + basic_section & + section( glm::dvec3 const &Location ); // members section_array m_sections; diff --git a/scenenodegroups.cpp b/scenenodegroups.cpp index 940030cc..cd8ecce1 100644 --- a/scenenodegroups.cpp +++ b/scenenodegroups.cpp @@ -13,6 +13,7 @@ http://mozilla.org/MPL/2.0/. #include "Event.h" #include "MemCell.h" +#include "AnimModel.h" #include "widgets/map_objects.h" namespace scene { @@ -32,29 +33,6 @@ node_groups::create() { scene::group_handle node_groups::close() { - if (!m_activegroup.empty()) { - for (basic_node *node : m_groupmap[m_activegroup.top()].nodes) { - std::string postfix { "sem_mem" }; - - if (typeid(TMemCell) == typeid(*node) && string_ends_with(node->name(), postfix)) { - std::string sem_name = node->name().substr(0, node->name().length() - postfix.length()); - - map::Semaphores.push_back(std::make_shared()); - auto sem_info = map::Semaphores.back(); - - sem_info->location = node->location(); - sem_info->name = sem_name; - - for (basic_event *event : m_groupmap[m_activegroup.top()].events) { - if (string_starts_with(event->name(), sem_name) - && event->name().substr(sem_name.length()).find("sem") == std::string::npos) { - sem_info->events.push_back(event); - } - } - } - } - } - if( false == m_activegroup.empty() ) { auto const closinggroup { m_activegroup.top() }; @@ -75,6 +53,54 @@ node_groups::close() return handle(); } +void +node_groups::update_map() +{ + map::Objects.entries.clear(); + + for (auto const &pair : m_groupmap) { + auto const &group = pair.second; + + for (basic_node *node : group.nodes) { + std::string postfix { "_sem_mem" }; + + if (typeid(*node) == typeid(TMemCell) && string_ends_with(node->name(), postfix)) { + std::string sem_name = node->name().substr(0, node->name().length() - postfix.length()); + + auto sem_info = std::make_shared(); + map::Objects.entries.push_back(sem_info); + + sem_info->location = node->location(); + sem_info->name = sem_name; + + for (basic_event *event : group.events) { + if (string_starts_with(event->name(), sem_name) + && event->name().substr(sem_name.length()).find("sem") == std::string::npos) { + sem_info->events.push_back(event); + } + } + + for (basic_node *node : group.nodes) + if (auto *model = dynamic_cast(node)) + if (string_starts_with(model->name(), sem_name)) + sem_info->models.push_back(model); + } + if (TEventLauncher *launcher = dynamic_cast(node)) { + if (!launcher || !launcher->Event1 || !launcher->Event2) + continue; + + auto track_switch = std::make_shared(); + map::Objects.entries.push_back(track_switch); + + track_switch->location = node->location(); + track_switch->name = node->name(); + track_switch->straight_event = launcher->Event1; + track_switch->divert_event = launcher->Event2; + } + } + } +} + // returns current active group, or null_handle if group stack is empty group_handle node_groups::handle() const { diff --git a/scenenodegroups.h b/scenenodegroups.h index d0815bae..a8cfd7cc 100644 --- a/scenenodegroups.h +++ b/scenenodegroups.h @@ -33,6 +33,9 @@ public: // indicates creation of current group ended. returns: handle to the parent group or null_handle if group stack is empty group_handle close(); + // update minimap objects + void + update_map(); // returns current active group, or null_handle if group stack is empty group_handle handle() const; diff --git a/simulation.cpp b/simulation.cpp index 1993963e..6629f99c 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -134,6 +134,14 @@ void state_manager::process_commands() { Events.AddToQuery(ev, nullptr); } + if (commanddata.command == user_command::setlight) { + uint32_t id = commanddata.action; + int light = std::round(commanddata.param1); + float state = commanddata.param2; + if (id < simulation::Instances.sequence().size()) + simulation::Instances.sequence()[id]->LightSet(light, state); + } + if (DebugModeFlag) { if (commanddata.command == user_command::timejump) { Time.update(commanddata.param1); diff --git a/simulationstateserializer.cpp b/simulationstateserializer.cpp index a601893c..6d2679ae 100644 --- a/simulationstateserializer.cpp +++ b/simulationstateserializer.cpp @@ -116,6 +116,7 @@ state_serializer::deserialize_continue(std::shared_ptr state deserialize_firstinit( Input, Scratchpad ); } + scene::Groups.update_map(); Region->create_map_geometry(); if( ( false == state->scratchpad.binary.terrain ) diff --git a/translation.cpp b/translation.cpp index b10c2f8f..1a68bc75 100644 --- a/translation.cpp +++ b/translation.cpp @@ -79,6 +79,9 @@ init() { "Map", "Mode windows", + "Straight |", + "Divert /", + "master controller", "second controller", "shunt mode power", @@ -239,6 +242,9 @@ init() { u8"Mapa", u8"Okna trybu", + u8"Prosto |", + u8"W bok /", + u8"nastawnik jazdy", u8"nastawnik dodatkowy", u8"sterowanie analogowe", diff --git a/translation.h b/translation.h index e863d421..882179aa 100644 --- a/translation.h +++ b/translation.h @@ -68,6 +68,9 @@ enum string { ui_map, ui_mode_windows, + map_straight, + map_divert, + cab_mainctrl, cab_scndctrl, cab_shuntmodepower, diff --git a/uilayer.cpp b/uilayer.cpp index 1063d776..c6702699 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -50,8 +50,15 @@ void ui_panel::render() ImGui::SetNextWindowSizeConstraints(ImVec2(size_min.x, size_min.y), ImVec2(size_max.x, size_max.y)); auto const panelname{(title.empty() ? name : title) + "###" + name}; - if (true == ImGui::Begin(panelname.c_str(), &is_open, flags)) + if (ImGui::Begin(panelname.c_str(), &is_open, flags)) { render_contents(); + + popups.remove_if([](std::unique_ptr &popup) + { + return popup->render(); + }); + } + ImGui::End(); } @@ -63,6 +70,11 @@ void ui_panel::render_contents() } } +void ui_panel::register_popup(std::unique_ptr &&popup) +{ + popups.push_back(std::move(popup)); +} + void ui_expandable_panel::render_contents() { ImGui::Checkbox(LOC_STR(ui_expand), &is_expanded); @@ -317,7 +329,7 @@ void ui_layer::render_panels() void ui_layer::render_tooltip() { - if (!m_cursorvisible || m_tooltip.empty()) + if (!m_cursorvisible || m_imguiio->WantCaptureMouse || m_tooltip.empty()) return; ImGui::BeginTooltip(); diff --git a/uilayer.h b/uilayer.h index 29b39b82..6bdfb62e 100644 --- a/uilayer.h +++ b/uilayer.h @@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/. #include #include "Texture.h" #include "translation.h" +#include "widgets/popup.h" // GuiLayer -- basic user interface class. draws requested information on top of openGL screen @@ -48,10 +49,12 @@ public: int window_flags = -1; const std::string& get_name() { return name; } + void register_popup(std::unique_ptr &&popup); protected: // members std::string name; + std::list> popups; }; class ui_expandable_panel : public ui_panel { diff --git a/widgets/map.cpp b/widgets/map.cpp index fc460828..63e0e4c3 100644 --- a/widgets/map.cpp +++ b/widgets/map.cpp @@ -7,6 +7,7 @@ #include "Camera.h" #include "simulation.h" #include "Driver.h" +#include "AnimModel.h" ui::map_panel::map_panel() : ui_panel(LOC_STR(ui_map), false) { @@ -115,8 +116,8 @@ void ui::map_panel::render_map_texture(glm::mat4 transform, glm::vec2 surface_si scene_ubo->update(scene_ubs); GfxRenderer.Draw_Geometry(m_switch_handles.begin(), m_switch_handles.end()); - glPointSize(3.0f); - scene_ubs.time = 0.0f; + glPointSize(5.0f); + scene_ubs.time = 1.0f; scene_ubo->update(scene_ubs); GfxRenderer.Draw_Geometry(simulation::Region->get_map_poi_geometry()); @@ -153,21 +154,7 @@ void ui::map_panel::render_labels(glm::mat4 transform, ImVec2 origin, glm::vec2 ImGui::PopStyleColor(); if (zoom > 0.005f) { - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 1.0f, 1.0f, 0.7f)); - for (auto sem : map::Semaphores) { - glm::vec4 ndc_pos = transform * glm::vec4(sem->location, 1.0f); - if (glm::abs(ndc_pos.x) > 1.0f || glm::abs(ndc_pos.z) > 1.0f) - continue; - glm::vec2 gui_pos = (glm::vec2(ndc_pos.x, -ndc_pos.z) / 2.0f + 0.5f) * glm::vec2(surface_size.x, surface_size.y); - - const char *desc = sem->name.c_str(); - ImVec2 textsize = ImGui::CalcTextSize(desc); - ImGui::SetCursorPos(ImVec2(origin.x + gui_pos.x - textsize.x / 2.0f, - origin.y + gui_pos.y - textsize.y / 2.0f)); - ImGui::TextUnformatted(desc); - } - ImGui::PopStyleColor(); } } @@ -176,19 +163,6 @@ void ui::map_panel::render_contents() if (!init_done) return; - if (active) { - if (ImGui::BeginPopup("Sem")) { - for (auto ev : active->events) - if (ImGui::Button(ev->name().c_str())) { - active.reset(); - command_relay relay; - relay.post(user_command::queueevent, (double)simulation::Events.GetEventId(ev), 0.0, GLFW_PRESS, 0); - break; - } - ImGui::EndPopup(); - } - } - float prev_zoom = zoom; if (ImGui::Button("-")) @@ -271,53 +245,169 @@ void ui::map_panel::render_contents() translate -= delta * 2.0f; } - if (ImGui::IsMouseClicked(1)) { + else { ImVec2 screen_pos = ImGui::GetMousePos(); glm::vec2 surface_pos(screen_pos.x - screen_origin.x, screen_pos.y - screen_origin.y); glm::vec2 ndc_pos = surface_pos / surface_size * 2.0f - 1.0f; glm::vec3 world_pos = glm::inverse(transform) * glm::vec4(ndc_pos.x, 0.0f, -ndc_pos.y, 1.0f); - std::vector launchers = simulation::Events.find_eventlaunchers(glm::vec2(world_pos.x, world_pos.z), 10.0f); + map::sorted_object_list objects = + map::Objects.find_in_range(glm::vec3(world_pos.x, NAN, world_pos.z), 0.03f / zoom); - float distx = 1000000.0f; + if (ImGui::IsMouseClicked(1)) { + if (objects.size() > 1) + register_popup(std::make_unique(*this, std::move(objects))); + else if (objects.size() == 1) + handle_map_object_click(*this, objects.begin()->second); - TEventLauncher *lau = nullptr; - for (auto launcher : launchers) { - float distance = glm::distance2(glm::vec2(launcher->location().x, launcher->location().z), - glm::vec2(world_pos.x, world_pos.z)); - if (distance < distx) { - lau = launcher; - distx = distance; + TTrack *nearest = simulation::Region->find_nearest_track_point(world_pos); + if (nearest) { + WriteLog(nearest->name()); } + //scene::basic_section &clicked_section = simulation::Region->section(world_pos); + //clicked_section. } - - if (lau) { - command_relay relay; - if (!Global.shiftState && lau->Event1) - relay.post(user_command::queueevent, (double)simulation::Events.GetEventId(lau->Event1), 0.0, GLFW_PRESS, 0); - else if (lau->Event2) - relay.post(user_command::queueevent, (double)simulation::Events.GetEventId(lau->Event2), 0.0, GLFW_PRESS, 0); - } - - std::vector> list; - - for (auto entry : map::Semaphores) { - float distance = glm::distance2(glm::vec2(entry->location.x, entry->location.z), - glm::vec2(world_pos.x, world_pos.z)); - if (distance < 100.0f) { - if (distance < distx) { - list.clear(); - list.push_back(entry); - distx = distance; - } - } - } - if (!list.empty()) { - ImGui::OpenPopup("Sem"); - active.emplace(*list[0].get()); + else if (!objects.empty()) { + handle_map_object_hover(objects.begin()->second); } } } render_labels(transform, window_origin, surface_size); } + +void ui::handle_map_object_click(ui_panel &parent, std::shared_ptr &obj) +{ + if (auto sem = std::dynamic_pointer_cast(obj)) { + parent.register_popup(std::make_unique(parent, std::move(sem))); + } + else if (auto track = std::dynamic_pointer_cast(obj)) { + parent.register_popup(std::make_unique(parent, std::move(track))); + } +} + +void ui::handle_map_object_hover(std::shared_ptr &obj) +{ + ImGui::BeginTooltip(); + + if (auto sem = std::dynamic_pointer_cast(obj)) { + for (auto &model : sem->models) { + ImGui::PushID(model); + ImGui::TextUnformatted(model->name().c_str()); + + for (int i = 0; i < iMaxNumLights; i++) { + GfxRenderer.Update_AnimModel(model); // update lamp opacities + auto state = model->LightGet(i); + if (!state) + continue; + + glm::vec3 current_color = std::get<2>(*state).value_or(glm::vec3(0.5f)) * std::get<1>(*state); + if (std::get<0>(*state) < 0.0f) + continue; + + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(current_color.x, current_color.y, current_color.z, 1.0f)); + + std::string res = " ##" + std::to_string(i); + ImGui::Button(res.c_str()); + + ImGui::PopStyleColor(); + } + + ImGui::PopID(); + } + } + else if (auto sw = std::dynamic_pointer_cast(obj)) { + ImGui::TextUnformatted(sw->name.c_str()); + } + + ImGui::EndTooltip(); +} + +ui::disambiguation_popup::disambiguation_popup(ui_panel &panel, map::sorted_object_list &&list) + : popup(panel), m_list(list) { } + +void ui::disambiguation_popup::render_content() +{ + for (auto &item : m_list) { + if (ImGui::Button(item.second->name.c_str())) { + ImGui::CloseCurrentPopup(); + + handle_map_object_click(m_parent, item.second); + } + } +} + +ui::semaphore_window::semaphore_window(ui_panel &panel, std::shared_ptr &&sem) + : popup(panel), m_sem(sem) { } + +void ui::semaphore_window::render_content() +{ + for (auto &model : m_sem->models) { + ImGui::PushID(model); + ImGui::TextUnformatted(model->name().c_str()); + + for (int i = 0; i < iMaxNumLights; i++) { + GfxRenderer.Update_AnimModel(model); // update lamp opacities + auto state = model->LightGet(i); + if (!state) + continue; + + glm::vec3 lamp_color = std::get<2>(*state).value_or(glm::vec3(0.5f)); + glm::vec3 current_color = lamp_color * std::get<1>(*state); + float level = std::get<0>(*state); + + if (level < 0.0f) + continue; + + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(lamp_color.x, lamp_color.y, lamp_color.z, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(current_color.x, current_color.y, current_color.z, 1.0f)); + + std::string res = " ##" + std::to_string(i); + if (ImGui::Button(res.c_str())) { + level += 1.0f; + if (level >= 3.0f) + level = 0.0f; + + int id = simulation::Instances.find_id(model->name()); + m_relay.post(user_command::setlight, (double)i, level, id, 0); + } + + ImGui::PopStyleColor(2); + } + + ImGui::PopID(); + } + + ImGui::Separator(); + + for (auto &item : m_sem->events) { + std::string displayname = item->name().substr(m_sem->name.size()); + + if (displayname.size() < 2) + continue; + + displayname[1] = std::toupper(displayname[1]); + + if (ImGui::Button(displayname.c_str())) { + m_relay.post(user_command::queueevent, (double)simulation::Events.GetEventId(item), 0.0, GLFW_PRESS, 0); + } + } +} + +ui::switch_window::switch_window(ui_panel &panel, std::shared_ptr &&sw) + : popup(panel), m_switch(sw) { } + +void ui::switch_window::render_content() +{ + ImGui::TextUnformatted(m_switch->name.c_str()); + + if (ImGui::Button(LOC_STR(map_straight))) { + m_relay.post(user_command::queueevent, (double)simulation::Events.GetEventId(m_switch->straight_event), 0.0, GLFW_PRESS, 0); + ImGui::CloseCurrentPopup(); + } + + if (ImGui::Button(LOC_STR(map_divert))) { + m_relay.post(user_command::queueevent, (double)simulation::Events.GetEventId(m_switch->divert_event), 0.0, GLFW_PRESS, 0); + ImGui::CloseCurrentPopup(); + } +} diff --git a/widgets/map.h b/widgets/map.h index eb51b143..13dd8f60 100644 --- a/widgets/map.h +++ b/widgets/map.h @@ -5,18 +5,37 @@ #include "Texture.h" #include "uilayer.h" #include "widgets/map_objects.h" +#include "widgets/popup.h" namespace ui { -class disambiguation_popup { - std::string m_id; - std::vector m_list; +class disambiguation_popup : public popup { + map::sorted_object_list m_list; public: - disambiguation_popup(std::string &&id, std::vector &&list) - : m_id(id), m_list(list) {} + disambiguation_popup(ui_panel &panel, map::sorted_object_list &&list); - void render(); + virtual void render_content() override; +}; + +class semaphore_window : public popup { + std::shared_ptr m_sem; + command_relay m_relay; + +public: + semaphore_window(ui_panel &panel, std::shared_ptr &&sem); + + virtual void render_content() override; +}; + +class switch_window : public popup { + std::shared_ptr m_switch; + command_relay m_relay; + +public: + switch_window(ui_panel &panel, std::shared_ptr &&sw); + + virtual void render_content() override; }; class map_panel : public ui_panel { @@ -50,4 +69,6 @@ public: void render_contents() override; }; +void handle_map_object_click(ui_panel &parent, std::shared_ptr &obj); +void handle_map_object_hover(std::shared_ptr &obj); } diff --git a/widgets/map_objects.cpp b/widgets/map_objects.cpp index dd0252b8..6ed22ac6 100644 --- a/widgets/map_objects.cpp +++ b/widgets/map_objects.cpp @@ -1,4 +1,27 @@ #include "widgets/map_objects.h" -std::vector> map::Semaphores; -std::vector> map::Switches; +map::objects map::Objects; + +map::sorted_object_list map::objects::find_in_range(glm::vec3 from, float distance) +{ + sorted_object_list items; + + float max_distance2 = distance * distance; + + for (auto const entry : entries) { + glm::vec3 entry_location = entry->location; + glm::vec3 search_point = from; + + if (glm::isnan(from.y)) { + entry_location.y = 0.0f; + search_point.y = 0.0f; + } + + float dist = glm::distance2(entry_location, search_point); + if (dist < max_distance2) { + items.emplace(dist, std::move(entry)); + } + } + + return items; +} diff --git a/widgets/map_objects.h b/widgets/map_objects.h index a5b70d84..43c24e2e 100644 --- a/widgets/map_objects.h +++ b/widgets/map_objects.h @@ -6,19 +6,35 @@ namespace map { - // semaphore description (only for minimap purposes) - struct semaphore { + struct map_object { std::string name; glm::vec3 location; + virtual ~map_object() = default; + }; + + using object_list = std::vector>; + using sorted_object_list = std::map>; + + // semaphore description (only for minimap purposes) + struct semaphore : public map_object { + std::vector models; + std::vector events; }; // switch description (only for minimap purposes) - struct track_switch { - glm::vec3 location; + struct track_switch : public map_object { + basic_event *straight_event = nullptr; + basic_event *divert_event = nullptr; }; - extern std::vector> Semaphores; - extern std::vector> Switches; + struct objects { + std::vector> entries; + + // returns objects in range from vec3, NaN in Y ignores it + sorted_object_list find_in_range(glm::vec3 from, float distance); + }; + + extern objects Objects; } diff --git a/widgets/popup.cpp b/widgets/popup.cpp new file mode 100644 index 00000000..bca293a9 --- /dev/null +++ b/widgets/popup.cpp @@ -0,0 +1,29 @@ +#include "widgets/popup.h" + +ui::popup::popup(ui_panel &panel) + : m_parent(panel) +{ +} + +ui::popup::~popup() +{ +} + +bool ui::popup::render() +{ + if (!m_id.size()) { + m_id = "popup:" + std::to_string(id++); + ImGui::OpenPopup(m_id.c_str()); + } + + if (!ImGui::BeginPopup(m_id.c_str())) + return true; + + render_content(); + + ImGui::EndPopup(); + + return false; +} + +int ui::popup::id = 0; diff --git a/widgets/popup.h b/widgets/popup.h new file mode 100644 index 00000000..26ad5e19 --- /dev/null +++ b/widgets/popup.h @@ -0,0 +1,22 @@ +#pragma once + +class ui_panel; + +namespace ui { + +class popup { + std::string m_id; + static int id; + +public: + popup(ui_panel &panel); + ~popup(); + + bool render(); + +protected: + ui_panel &m_parent; + virtual void render_content() = 0; +}; + +}