16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-18 03:09:18 +02:00

semaphores controllable on minimap

This commit is contained in:
milek7
2019-03-02 14:32:39 +01:00
parent 2d66192c9a
commit 1f0d8c4dc8
26 changed files with 678 additions and 315 deletions

View File

@@ -591,6 +591,24 @@ void TAnimModel::LightSet(int const n, float const v)
lsLights[ n ] = v;
};
std::optional<std::tuple<float, float, std::optional<glm::vec3>> > TAnimModel::LightGet(const int n)
{
if (n >= iMaxNumLights)
return std::nullopt;
if (!LightsOn[n] && !LightsOff[n])
return std::nullopt;
std::optional<glm::vec3> 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;

View File

@@ -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<std::tuple<float, float, std::optional<glm::vec3> > > LightGet( int const n );
int TerrainCount();
TSubModel * TerrainSquare(int n);
int Flags();

View File

@@ -126,6 +126,7 @@ set(SOURCES
"widgets/vehiclelist.cpp"
"widgets/map.cpp"
"widgets/map_objects.cpp"
"widgets/popup.cpp"
"ref/glad/src/glad.c"

View File

@@ -91,6 +91,30 @@ TSubModel::SetDiffuseOverride( glm::vec3 const &Color, bool const Includechildre
}
}
std::optional<glm::vec3>
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 ) {

View File

@@ -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<glm::vec3> 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

View File

@@ -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 {

View File

@@ -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

View File

@@ -230,6 +230,7 @@ enum class user_command {
pausetoggle,
entervehicle,
queueevent,
setlight,
none = -1
};

View File

@@ -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<gl::renderbuffer>();
m_msaa_rbc->alloc(Global.gfx_format_color, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, samples);
m_pfx_motionblur = std::make_unique<gl::postfx>("motionblur");
m_pfx_tonemapping = std::make_unique<gl::postfx>("tonemapping");
m_msaa_rbd = std::make_unique<gl::renderbuffer>();
m_msaa_rbd->alloc(Global.gfx_format_depth, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, samples);
m_empty_cubemap = std::make_unique<gl::cubemap>();
m_empty_cubemap->alloc(Global.gfx_format_color, 16, 16, GL_RGB, GL_FLOAT);
m_msaa_fb = std::make_unique<gl::framebuffer>();
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<gl::renderbuffer>();
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<opengl_texture>();
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<gl::framebuffer>();
m_main_fb->attach(*m_main_tex, GL_COLOR_ATTACHMENT0);
m_main_texv = std::make_unique<opengl_texture>();
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<gl::postfx>("motionblur");
WriteLog("motion blur enabled");
}
if (!m_msaa_fb->is_complete())
return false;
m_main2_tex = std::make_unique<opengl_texture>();
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<gl::framebuffer>();
m_main2_fb->attach(*m_main2_tex, GL_COLOR_ATTACHMENT0);
if (!m_main2_fb->is_complete())
return false;
m_pfx_tonemapping = std::make_unique<gl::postfx>("tonemapping");
}
if (Global.gfx_shadowmap_enabled)
{
m_shadow_fb = std::make_unique<gl::framebuffer>();
m_shadow_tex = std::make_unique<opengl_texture>();
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<gl::framebuffer>();
m_cabshadows_tex = std::make_unique<opengl_texture>();
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<opengl_texture>();
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<gl::renderbuffer>();
m_env_rb->alloc(Global.gfx_format_depth, gl::ENVMAP_SIZE, gl::ENVMAP_SIZE);
m_env_tex = std::make_unique<gl::cubemap>();
m_env_tex->alloc(Global.gfx_format_color, gl::ENVMAP_SIZE, gl::ENVMAP_SIZE, GL_RGB, GL_FLOAT);
m_empty_cubemap = std::make_unique<gl::cubemap>();
m_empty_cubemap->alloc(Global.gfx_format_color, 16, 16, GL_RGB, GL_FLOAT);
m_env_fb = std::make_unique<gl::framebuffer>();
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<gl::pbo>();
m_picking_node_pbo = std::make_unique<gl::pbo>();
@@ -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<gl::renderbuffer>();
vp.msaa_rbc->alloc(Global.gfx_format_color, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, samples);
vp.msaa_rbd = std::make_unique<gl::renderbuffer>();
vp.msaa_rbd->alloc(Global.gfx_format_depth, Global.gfx_framebuffer_width, Global.gfx_framebuffer_height, samples);
vp.msaa_fb = std::make_unique<gl::framebuffer>();
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<gl::renderbuffer>();
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<opengl_texture>();
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<gl::framebuffer>();
vp.main_fb->attach(*vp.main_tex, GL_COLOR_ATTACHMENT0);
vp.main_texv = std::make_unique<opengl_texture>();
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<opengl_texture>();
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<gl::framebuffer>();
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<gl::framebuffer>();
vp.shadow_tex = std::make_unique<opengl_texture>();
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<gl::framebuffer>();
vp.cabshadows_tex = std::make_unique<opengl_texture>();
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<gl::renderbuffer>();
vp.env_rb->alloc(Global.gfx_format_depth, gl::ENVMAP_SIZE, gl::ENVMAP_SIZE);
vp.env_tex = std::make_unique<gl::cubemap>();
vp.env_tex->alloc(Global.gfx_format_color, gl::ENVMAP_SIZE, gl::ENVMAP_SIZE, GL_RGB, GL_FLOAT);
vp.env_fb = std::make_unique<gl::framebuffer>();
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<gl::program> 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<int>(std::floor(cameraposition.x / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2));
auto const cameraz = static_cast<int>(std::floor(cameraposition.z / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2));
int const segmentcount = 2 * static_cast<int>(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<std::uintptr_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)
@@ -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)

View File

@@ -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<gl::framebuffer> msaa_fb;
std::unique_ptr<gl::renderbuffer> msaa_rbc;
std::unique_ptr<gl::renderbuffer> msaa_rbv;
std::unique_ptr<gl::renderbuffer> msaa_rbd;
std::unique_ptr<gl::framebuffer> main_fb;
std::unique_ptr<opengl_texture> main_texv;
std::unique_ptr<opengl_texture> main_tex;
std::unique_ptr<gl::framebuffer> main2_fb;
std::unique_ptr<opengl_texture> main2_tex;
std::unique_ptr<gl::framebuffer> shadow_fb;
std::unique_ptr<opengl_texture> shadow_tex;
std::unique_ptr<gl::framebuffer> cabshadows_fb;
std::unique_ptr<opengl_texture> cabshadows_tex;
std::unique_ptr<gl::framebuffer> env_fb;
std::unique_ptr<gl::renderbuffer> env_rb;
std::unique_ptr<gl::cubemap> env_tex;
};
typedef std::vector<opengl_light> 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<gfx::geometrybank_handle>::iterator begin, std::vector<gfx::geometrybank_handle>::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<gl::vao> m_empty_vao;
std::unique_ptr<gl::framebuffer> m_msaa_fb;
std::unique_ptr<gl::renderbuffer> m_msaa_rbc;
std::unique_ptr<gl::renderbuffer> m_msaa_rbv;
std::unique_ptr<gl::renderbuffer> m_msaa_rbd;
viewport_data default_viewport;
std::unique_ptr<gl::framebuffer> m_main_fb;
std::unique_ptr<opengl_texture> m_main_texv;
std::unique_ptr<opengl_texture> m_main_tex;
std::unique_ptr<gl::postfx> m_pfx_motionblur;
std::unique_ptr<gl::postfx> m_pfx_tonemapping;
std::unique_ptr<gl::framebuffer> m_main2_fb;
std::unique_ptr<opengl_texture> m_main2_tex;
std::unique_ptr<gl::postfx> m_pfx_motionblur;
std::unique_ptr<gl::postfx> m_pfx_tonemapping;
std::unique_ptr<gl::framebuffer> m_shadow_fb;
std::unique_ptr<opengl_texture> m_shadow_tex;
std::unique_ptr<gl::program> m_shadow_shader;
std::unique_ptr<gl::program> m_alpha_shadow_shader;
std::unique_ptr<gl::program> m_alpha_shadow_shader;
std::unique_ptr<gl::framebuffer> m_pick_fb;
std::unique_ptr<opengl_texture> m_pick_tex;
std::unique_ptr<gl::renderbuffer> m_pick_rb;
std::unique_ptr<gl::program> m_pick_shader;
std::unique_ptr<gl::framebuffer> m_cabshadows_fb;
std::unique_ptr<opengl_texture> m_cabshadows_tex;
std::unique_ptr<gl::framebuffer> m_env_fb;
std::unique_ptr<gl::renderbuffer> m_env_rb;
std::unique_ptr<gl::cubemap> m_env_tex;
std::unique_ptr<gl::cubemap> m_empty_cubemap;
std::unique_ptr<gl::cubemap> m_empty_cubemap;
std::unique_ptr<gl::pbo> m_picking_pbo;
std::unique_ptr<gl::pbo> m_picking_node_pbo;

View File

@@ -624,6 +624,24 @@ void basic_cell::get_map_active_switches(std::vector<gfx::geometrybank_handle> &
path->get_map_active_switches(handles);
}
TTrack* basic_cell::find_nearest_track_point(const glm::dvec3 &pos)
{
float min = std::numeric_limits<float>::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<gfx::basic_vertex> vertices;
for (const auto sem : map::Semaphores) {
vertices.push_back(gfx::basic_vertex(sem->location, glm::vec3(), glm::vec3()));
std::vector<gfx::basic_vertex> 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

19
scene.h
View File

@@ -157,6 +157,7 @@ public:
create_map_geometry(std::vector<gfx::basic_vertex> &Bank, const gfx::geometrybank_handle Extra);
void
get_map_active_switches(std::vector<gfx::geometrybank_handle> &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<shape_node> &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;

View File

@@ -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<map::semaphore>());
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::semaphore>();
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<TAnimModel*>(node))
if (string_starts_with(model->name(), sem_name))
sem_info->models.push_back(model);
}
if (TEventLauncher *launcher = dynamic_cast<TEventLauncher*>(node)) {
if (!launcher || !launcher->Event1 || !launcher->Event2)
continue;
auto track_switch = std::make_shared<map::track_switch>();
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 {

View File

@@ -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;

View File

@@ -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);

View File

@@ -116,6 +116,7 @@ state_serializer::deserialize_continue(std::shared_ptr<deserializer_state> state
deserialize_firstinit( Input, Scratchpad );
}
scene::Groups.update_map();
Region->create_map_geometry();
if( ( false == state->scratchpad.binary.terrain )

View File

@@ -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",

View File

@@ -68,6 +68,9 @@ enum string {
ui_map,
ui_mode_windows,
map_straight,
map_divert,
cab_mainctrl,
cab_scndctrl,
cab_shuntmodepower,

View File

@@ -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<ui::popup> &popup)
{
return popup->render();
});
}
ImGui::End();
}
@@ -63,6 +70,11 @@ void ui_panel::render_contents()
}
}
void ui_panel::register_popup(std::unique_ptr<ui::popup> &&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();

View File

@@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/.
#include <string>
#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<ui::popup> &&popup);
protected:
// members
std::string name;
std::list<std::unique_ptr<ui::popup>> popups;
};
class ui_expandable_panel : public ui_panel {

View File

@@ -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<TEventLauncher *> 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<ui::disambiguation_popup>(*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<std::shared_ptr<map::semaphore>> 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<map::map_object> &obj)
{
if (auto sem = std::dynamic_pointer_cast<map::semaphore>(obj)) {
parent.register_popup(std::make_unique<semaphore_window>(parent, std::move(sem)));
}
else if (auto track = std::dynamic_pointer_cast<map::track_switch>(obj)) {
parent.register_popup(std::make_unique<switch_window>(parent, std::move(track)));
}
}
void ui::handle_map_object_hover(std::shared_ptr<map::map_object> &obj)
{
ImGui::BeginTooltip();
if (auto sem = std::dynamic_pointer_cast<map::semaphore>(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<map::track_switch>(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<map::semaphore> &&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<map::track_switch> &&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();
}
}

View File

@@ -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<map::semaphore> m_list;
class disambiguation_popup : public popup {
map::sorted_object_list m_list;
public:
disambiguation_popup(std::string &&id, std::vector<map::semaphore> &&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<map::semaphore> m_sem;
command_relay m_relay;
public:
semaphore_window(ui_panel &panel, std::shared_ptr<map::semaphore> &&sem);
virtual void render_content() override;
};
class switch_window : public popup {
std::shared_ptr<map::track_switch> m_switch;
command_relay m_relay;
public:
switch_window(ui_panel &panel, std::shared_ptr<map::track_switch> &&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<map::map_object> &obj);
void handle_map_object_hover(std::shared_ptr<map::map_object> &obj);
}

View File

@@ -1,4 +1,27 @@
#include "widgets/map_objects.h"
std::vector<std::shared_ptr<map::semaphore>> map::Semaphores;
std::vector<std::shared_ptr<map::track_switch>> 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;
}

View File

@@ -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<std::shared_ptr<map::map_object>>;
using sorted_object_list = std::map<float, std::shared_ptr<map::map_object>>;
// semaphore description (only for minimap purposes)
struct semaphore : public map_object {
std::vector<TAnimModel *> models;
std::vector<basic_event *> 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<std::shared_ptr<semaphore>> Semaphores;
extern std::vector<std::shared_ptr<track_switch>> Switches;
struct objects {
std::vector<std::shared_ptr<map_object>> 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;
}

29
widgets/popup.cpp Normal file
View File

@@ -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;

22
widgets/popup.h Normal file
View File

@@ -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;
};
}