mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
renderer changes, time and environment window
This commit is contained in:
@@ -127,6 +127,7 @@ set(SOURCES
|
||||
"widgets/map.cpp"
|
||||
"widgets/map_objects.cpp"
|
||||
"widgets/popup.cpp"
|
||||
"widgets/time.cpp"
|
||||
|
||||
"ref/glad/src/glad.c"
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ struct global_settings {
|
||||
std::mt19937 random_engine;
|
||||
std::mt19937 local_random_engine;
|
||||
bool ready_to_load { false };
|
||||
std::time_t starting_timestamp = 0; // starting time, in local timezone
|
||||
uint32_t random_seed = 0;
|
||||
TCamera pCamera; // parametry kamery
|
||||
TCamera pDebugCamera;
|
||||
@@ -73,8 +74,7 @@ struct global_settings {
|
||||
float FrictionWeatherFactor { 1.f };
|
||||
bool bLiveTraction{ true };
|
||||
float Overcast{ 0.1f }; // NOTE: all this weather stuff should be moved elsewhere
|
||||
glm::vec3 FogColor = { 0.6f, 0.7f, 0.8f };
|
||||
double fFogStart{ 1700 };
|
||||
glm::vec3 FogColor = { 0.6f, 0.7f, 0.8f };
|
||||
double fFogEnd{ 2000 };
|
||||
std::string Season{}; // season of the year, based on simulation date
|
||||
std::string Weather{ "cloudy:" }; // current weather
|
||||
|
||||
@@ -63,7 +63,7 @@ void UpdateTimers(bool pause)
|
||||
QueryPerformanceCounter((LARGE_INTEGER *)&count);
|
||||
#elif __linux__
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
count = (uint64_t)ts.tv_sec * 1000000000 + (uint64_t)ts.tv_nsec;
|
||||
fr = 1000000000;
|
||||
#endif
|
||||
|
||||
@@ -767,6 +767,12 @@ bool eu07_application::init_network() {
|
||||
if (!Global.random_seed)
|
||||
Global.random_seed = std::random_device{}();
|
||||
Global.random_engine.seed(Global.random_seed);
|
||||
|
||||
// TODO: sort out this timezone mess
|
||||
std::time_t utc_now = std::time(nullptr);
|
||||
std::time_t local_now = utc_now + (std::mktime(std::localtime(&utc_now)) - std::mktime(std::gmtime(&utc_now)));
|
||||
|
||||
Global.starting_timestamp = local_now;
|
||||
Global.ready_to_load = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -227,6 +227,9 @@ commanddescription_sequence Commands_descriptions = {
|
||||
{ "timejump", command_target::simulation, command_mode::oneoff },
|
||||
{ "timejumplarge", command_target::simulation, command_mode::oneoff },
|
||||
{ "timejumpsmall", command_target::simulation, command_mode::oneoff },
|
||||
{ "setdatetime", command_target::simulation, command_mode::oneoff },
|
||||
{ "setweather", command_target::simulation, command_mode::oneoff },
|
||||
{ "settemperature", command_target::simulation, command_mode::oneoff },
|
||||
{ "vehiclemove", command_target::vehicle, command_mode::oneoff },
|
||||
{ "vehiclemoveforwards", command_target::vehicle, command_mode::oneoff },
|
||||
{ "vehiclemovebackwards", command_target::vehicle, command_mode::oneoff },
|
||||
|
||||
@@ -221,6 +221,9 @@ enum class user_command {
|
||||
timejump,
|
||||
timejumplarge,
|
||||
timejumpsmall,
|
||||
setdatetime,
|
||||
setweather,
|
||||
settemperature,
|
||||
vehiclemove,
|
||||
vehiclemoveforwards,
|
||||
vehiclemovebackwards,
|
||||
|
||||
@@ -28,6 +28,7 @@ driver_ui::driver_ui() {
|
||||
push_back( &m_transcriptspanel );
|
||||
|
||||
//push_back( &m_vehiclelist );
|
||||
push_back( &m_timepanel );
|
||||
push_back( &m_mappanel );
|
||||
push_back( &m_logpanel );
|
||||
m_logpanel.is_open = false;
|
||||
@@ -52,6 +53,10 @@ void driver_ui::render_menu_contents() {
|
||||
ImGui::MenuItem(m_timetablepanel.title.c_str(), "F2", &m_timetablepanel.is_open);
|
||||
ImGui::MenuItem(m_debugpanel.get_name().c_str(), "F12", &m_debugpanel.is_open);
|
||||
ImGui::MenuItem(m_mappanel.get_name().c_str(), "Tab", &m_mappanel.is_open);
|
||||
|
||||
if (ImGui::MenuItem(m_timepanel.get_name().c_str()))
|
||||
m_timepanel.open();
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include "widgets/vehiclelist.h"
|
||||
#include "widgets/map.h"
|
||||
#include "widgets/time.h"
|
||||
|
||||
class driver_ui : public ui_layer {
|
||||
|
||||
@@ -58,4 +59,5 @@ private:
|
||||
command_relay m_relay;
|
||||
ui::vehiclelist_panel m_vehiclelist;
|
||||
ui::map_panel m_mappanel;
|
||||
ui::time_panel m_timepanel;
|
||||
};
|
||||
|
||||
@@ -16,11 +16,13 @@ void network::client_hello::deserialize(std::istream &stream)
|
||||
void network::server_hello::serialize(std::ostream &stream) const
|
||||
{
|
||||
sn_utils::ls_uint32(stream, seed);
|
||||
sn_utils::ls_int64(stream, timestamp);
|
||||
}
|
||||
|
||||
void network::server_hello::deserialize(std::istream &stream)
|
||||
{
|
||||
seed = sn_utils::ld_uint32(stream);
|
||||
timestamp = sn_utils::ld_int64(stream);
|
||||
}
|
||||
|
||||
void ::network::request_command::serialize(std::ostream &stream) const
|
||||
|
||||
@@ -39,6 +39,7 @@ struct server_hello : public message
|
||||
server_hello() : message(SERVER_HELLO) {}
|
||||
|
||||
uint32_t seed;
|
||||
int64_t timestamp;
|
||||
|
||||
virtual void serialize(std::ostream &stream) const override;
|
||||
virtual void deserialize(std::istream &stream) override;
|
||||
|
||||
@@ -122,6 +122,7 @@ void network::server::handle_message(std::shared_ptr<connection> conn, const mes
|
||||
|
||||
server_hello reply;
|
||||
reply.seed = Global.random_seed;
|
||||
reply.timestamp = Global.starting_timestamp;
|
||||
conn->state = connection::CATCHING_UP;
|
||||
conn->backbuffer = backbuffer;
|
||||
conn->backbuffer_pos = 0;
|
||||
@@ -229,6 +230,7 @@ void network::client::handle_message(std::shared_ptr<connection> conn, const mes
|
||||
if (!Global.ready_to_load) {
|
||||
Global.random_seed = cmd.seed;
|
||||
Global.random_engine.seed(Global.random_seed);
|
||||
Global.starting_timestamp = cmd.timestamp;
|
||||
Global.ready_to_load = true;
|
||||
} else if (Global.random_seed != cmd.seed) {
|
||||
ErrorLog("net: seed mismatch", logtype::net);
|
||||
|
||||
@@ -90,24 +90,28 @@ basic_precipitation::init() {
|
||||
|
||||
create( 18 );
|
||||
|
||||
// TODO: select texture based on current overcast level
|
||||
// TODO: when the overcast level dynamic change is in check the current level during render and pick the appropriate texture on the fly
|
||||
std::string const densitysuffix { (
|
||||
Global.Overcast < 1.35 ?
|
||||
"_light" :
|
||||
"_medium" ) };
|
||||
if( Global.Weather == "rain:" ) {
|
||||
m_moverateweathertypefactor = 2.f;
|
||||
m_texture = GfxRenderer.Fetch_Texture( "fx/rain" + densitysuffix );
|
||||
}
|
||||
else if( Global.Weather == "snow:" ) {
|
||||
m_moverateweathertypefactor = 1.25f;
|
||||
m_texture = GfxRenderer.Fetch_Texture( "fx/snow" + densitysuffix );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
basic_precipitation::update_weather() {
|
||||
|
||||
// TODO: select texture based on current overcast level
|
||||
// TODO: when the overcast level dynamic change is in check the current level during render and pick the appropriate texture on the fly
|
||||
std::string const densitysuffix { (
|
||||
Global.Overcast < 1.35 ?
|
||||
"_light" :
|
||||
"_medium" ) };
|
||||
if( Global.Weather == "rain:" ) {
|
||||
m_moverateweathertypefactor = 2.f;
|
||||
m_texture = GfxRenderer.Fetch_Texture( "fx/rain" + densitysuffix );
|
||||
}
|
||||
else if( Global.Weather == "snow:" ) {
|
||||
m_moverateweathertypefactor = 1.25f;
|
||||
m_texture = GfxRenderer.Fetch_Texture( "fx/snow" + densitysuffix );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
basic_precipitation::update() {
|
||||
|
||||
|
||||
@@ -26,11 +26,14 @@ public:
|
||||
// methods
|
||||
bool
|
||||
init();
|
||||
void
|
||||
update_weather();
|
||||
void
|
||||
update();
|
||||
void
|
||||
render();
|
||||
float get_textureoffset();
|
||||
float
|
||||
get_textureoffset();
|
||||
|
||||
glm::dvec3 m_cameramove{ 0.0 };
|
||||
|
||||
|
||||
75
renderer.cpp
75
renderer.cpp
@@ -195,9 +195,26 @@ bool opengl_renderer::Init(GLFWwindow *Window)
|
||||
m_empty_cubemap = std::make_unique<gl::cubemap>();
|
||||
m_empty_cubemap->alloc(Global.gfx_format_color, 16, 16, GL_RGB, GL_FLOAT);
|
||||
|
||||
m_current_viewport = &default_viewport;
|
||||
if (!init_viewport(default_viewport))
|
||||
return false;
|
||||
m_viewports.push_back(std::make_unique<viewport_config>());
|
||||
viewport_config &default_viewport = *m_viewports.front().get();
|
||||
default_viewport.width = Global.gfx_framebuffer_width;
|
||||
default_viewport.height = Global.gfx_framebuffer_height;
|
||||
|
||||
/*
|
||||
default_viewport.camera_transform = glm::rotate(glm::mat4(), 0.5f, glm::vec3(1.0f, 0.0f, 0.f));
|
||||
|
||||
m_viewports.push_back(std::make_unique<viewport_config>());
|
||||
viewport_config &vp2 = *m_viewports.back().get();
|
||||
vp2.width = Global.gfx_framebuffer_width;
|
||||
vp2.height = Global.gfx_framebuffer_height;
|
||||
|
||||
vp2.camera_transform = glm::rotate(glm::mat4(), -0.5f, glm::vec3(1.0f, 0.0f, 0.f));
|
||||
*/
|
||||
|
||||
for (auto &viewport : m_viewports) {
|
||||
if (!init_viewport(*viewport.get()))
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pick_tex = std::make_unique<opengl_texture>();
|
||||
m_pick_tex->alloc_rendertarget(GL_RGB8, GL_RGB, EU07_PICKBUFFERSIZE, EU07_PICKBUFFERSIZE);
|
||||
@@ -271,17 +288,17 @@ bool opengl_renderer::Init(GLFWwindow *Window)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool opengl_renderer::init_viewport(viewport_data &vp)
|
||||
bool opengl_renderer::init_viewport(viewport_config &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_rbc->alloc(Global.gfx_format_color, vp.width, vp.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_rbd->alloc(Global.gfx_format_depth, vp.width, vp.height, samples);
|
||||
|
||||
vp.msaa_fb = std::make_unique<gl::framebuffer>();
|
||||
vp.msaa_fb->attach(*vp.msaa_rbc, GL_COLOR_ATTACHMENT0);
|
||||
@@ -290,17 +307,17 @@ bool opengl_renderer::init_viewport(viewport_data &vp)
|
||||
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_rbv->alloc(Global.gfx_postfx_motionblur_format, vp.width, vp.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_tex->alloc_rendertarget(Global.gfx_format_color, GL_RGB, vp.width, vp.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_texv->alloc_rendertarget(Global.gfx_postfx_motionblur_format, GL_RG, vp.width, vp.height);
|
||||
vp.main_fb->attach(*vp.main_texv, GL_COLOR_ATTACHMENT1);
|
||||
vp.main_fb->setup_drawing(2);
|
||||
|
||||
@@ -314,7 +331,7 @@ bool opengl_renderer::init_viewport(viewport_data &vp)
|
||||
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_tex->alloc_rendertarget(Global.gfx_format_color, GL_RGB, vp.width, vp.height);
|
||||
|
||||
vp.main2_fb = std::make_unique<gl::framebuffer>();
|
||||
vp.main2_fb->attach(*vp.main2_tex, GL_COLOR_ATTACHMENT0);
|
||||
@@ -412,7 +429,10 @@ bool opengl_renderer::Render()
|
||||
m_renderpass.draw_mode = rendermode::none; // force setup anew
|
||||
m_debugstats = debug_stats();
|
||||
|
||||
Render_pass(default_viewport, rendermode::color);
|
||||
for (auto &viewport : m_viewports) {
|
||||
Render_pass(*viewport.get(), rendermode::color);
|
||||
}
|
||||
//Render_pass(*m_viewports.front().get(), rendermode::color);
|
||||
|
||||
m_drawcount = m_cellqueue.size();
|
||||
m_debugtimestext.clear();
|
||||
@@ -475,9 +495,9 @@ void opengl_renderer::draw_debug_ui()
|
||||
}
|
||||
|
||||
// runs jobs needed to generate graphics for specified render pass
|
||||
void opengl_renderer::Render_pass(viewport_data &vp, rendermode const Mode)
|
||||
void opengl_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
|
||||
{
|
||||
setup_pass(m_renderpass, Mode);
|
||||
setup_pass(vp, m_renderpass, Mode);
|
||||
switch (m_renderpass.draw_mode)
|
||||
{
|
||||
|
||||
@@ -520,7 +540,7 @@ void opengl_renderer::Render_pass(viewport_data &vp, rendermode const Mode)
|
||||
Render_pass(vp, rendermode::shadows);
|
||||
if (!FreeFlyModeFlag)
|
||||
Render_pass(vp, rendermode::cabshadows);
|
||||
setup_pass(m_renderpass, Mode); // restore draw mode. TBD, TODO: render mode stack
|
||||
setup_pass(vp, m_renderpass, Mode); // restore draw mode. TBD, TODO: render mode stack
|
||||
|
||||
Timer::subsystem.gfx_shadows.stop();
|
||||
glDebug("render shadowmap end");
|
||||
@@ -530,7 +550,7 @@ void opengl_renderer::Render_pass(viewport_data &vp, rendermode const Mode)
|
||||
{
|
||||
// potentially update environmental cube map
|
||||
if (Render_reflections(vp))
|
||||
setup_pass(m_renderpass, Mode); // restore color pass settings
|
||||
setup_pass(vp, m_renderpass, Mode); // restore color pass settings
|
||||
setup_env_map(vp.env_tex.get());
|
||||
}
|
||||
|
||||
@@ -851,7 +871,7 @@ void opengl_renderer::Render_pass(viewport_data &vp, rendermode const Mode)
|
||||
}
|
||||
|
||||
// creates dynamic environment cubemap
|
||||
bool opengl_renderer::Render_reflections(viewport_data &vp)
|
||||
bool opengl_renderer::Render_reflections(viewport_config &vp)
|
||||
{
|
||||
if (Global.ReflectionUpdatesPerSecond == 0)
|
||||
return false;
|
||||
@@ -943,7 +963,8 @@ glm::mat4 opengl_renderer::ortho_frustumtest_projection(float l, float r, float
|
||||
return glm::ortho(l, r, b, t, znear, zfar);
|
||||
}
|
||||
|
||||
void opengl_renderer::setup_pass(renderpass_config &Config, rendermode const Mode, float const Znear, float const Zfar, bool const Ignoredebug)
|
||||
void opengl_renderer::setup_pass(viewport_config &Viewport, renderpass_config &Config, rendermode const Mode,
|
||||
float const Znear, float const Zfar, bool const Ignoredebug)
|
||||
{
|
||||
|
||||
Config.draw_mode = Mode;
|
||||
@@ -1001,10 +1022,14 @@ void opengl_renderer::setup_pass(renderpass_config &Config, rendermode const Mod
|
||||
float const fovy = glm::radians(Global.FieldOfView / Global.ZoomFactor);
|
||||
float const aspect = std::max(1.f, (float)Global.iWindowWidth) / std::max(1.f, (float)Global.iWindowHeight);
|
||||
|
||||
Config.viewport_camera.position() = Global.pCamera.Pos;
|
||||
|
||||
switch (Mode)
|
||||
{
|
||||
case rendermode::color:
|
||||
{
|
||||
viewmatrix = glm::dmat4(Viewport.camera_transform);
|
||||
|
||||
// modelview
|
||||
if ((false == DebugCameraFlag) || (true == Ignoredebug))
|
||||
{
|
||||
@@ -1016,6 +1041,7 @@ void opengl_renderer::setup_pass(renderpass_config &Config, rendermode const Mod
|
||||
camera.position() = Global.pDebugCamera.Pos;
|
||||
Global.pDebugCamera.SetMatrix(viewmatrix);
|
||||
}
|
||||
|
||||
// projection
|
||||
auto const zfar = Config.draw_range * Global.fDistanceFactor * Zfar;
|
||||
auto const znear = (Znear > 0.f ? Znear * zfar : 0.1f * Global.ZoomFactor);
|
||||
@@ -1030,7 +1056,7 @@ void opengl_renderer::setup_pass(renderpass_config &Config, rendermode const Mod
|
||||
// ...setup chunk of frustum we're interested in...
|
||||
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);
|
||||
setup_pass(Viewport, worldview, rendermode::color, 0.f, zfar, true);
|
||||
auto &frustumchunkshapepoints = worldview.pass_camera.frustum_points();
|
||||
// ...modelview matrix: determine the centre of frustum chunk in world space...
|
||||
glm::vec3 frustumchunkmin, frustumchunkmax;
|
||||
@@ -1110,9 +1136,12 @@ void opengl_renderer::setup_pass(renderpass_config &Config, rendermode const Mod
|
||||
case rendermode::pickcontrols:
|
||||
case rendermode::pickscenery:
|
||||
{
|
||||
viewmatrix = glm::dmat4(Viewport.camera_transform);
|
||||
|
||||
// modelview
|
||||
camera.position() = Global.pCamera.Pos;
|
||||
Global.pCamera.SetMatrix(viewmatrix);
|
||||
|
||||
// projection
|
||||
float znear = 0.1f * Global.ZoomFactor;
|
||||
float zfar = Config.draw_range * Global.fDistanceFactor;
|
||||
@@ -1998,7 +2027,7 @@ void opengl_renderer::Render(scene::shape_node const &Shape, bool const Ignorera
|
||||
case rendermode::shadows:
|
||||
{
|
||||
// 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees
|
||||
distancesquared = Math3D::SquareMagnitude((data.area.center - Global.pCamera.Pos) / Global.ZoomFactor) / Global.fDistanceFactor;
|
||||
distancesquared = Math3D::SquareMagnitude((data.area.center - m_renderpass.viewport_camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -2052,7 +2081,7 @@ void opengl_renderer::Render(TAnimModel *Instance)
|
||||
case rendermode::shadows:
|
||||
{
|
||||
// 'camera' for the light pass is the light source, but we need to draw what the 'real' camera sees
|
||||
distancesquared = Math3D::SquareMagnitude((Instance->location() - Global.pCamera.Pos) / Global.ZoomFactor) / Global.fDistanceFactor;
|
||||
distancesquared = Math3D::SquareMagnitude((Instance->location() - m_renderpass.viewport_camera.position()) / (double)Global.ZoomFactor) / Global.fDistanceFactor;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -2110,7 +2139,7 @@ bool opengl_renderer::Render(TDynamicObject *Dynamic)
|
||||
{
|
||||
case rendermode::shadows:
|
||||
{
|
||||
squaredistance = glm::length2(glm::vec3{glm::dvec3{Dynamic->vPosition - Global.pCamera.Pos}} / Global.ZoomFactor) / Global.fDistanceFactor;
|
||||
squaredistance = glm::length2(glm::vec3{glm::dvec3{Dynamic->vPosition - m_renderpass.viewport_camera.position()}} / Global.ZoomFactor) / Global.fDistanceFactor;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -3430,7 +3459,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(default_viewport, rendermode::pickcontrols);
|
||||
Render_pass(*m_viewports.front().get(), rendermode::pickcontrols);
|
||||
m_pick_fb->bind();
|
||||
m_picking_pbo->request_read(pickbufferpos.x, pickbufferpos.y, 1, 1);
|
||||
m_pick_fb->unbind();
|
||||
@@ -3469,7 +3498,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(default_viewport, rendermode::pickscenery);
|
||||
Render_pass(*m_viewports.front().get(), rendermode::pickscenery);
|
||||
m_pick_fb->bind();
|
||||
m_picking_node_pbo->request_read(pickbufferpos.x, pickbufferpos.y, 1, 1);
|
||||
m_pick_fb->unbind();
|
||||
|
||||
19
renderer.h
19
renderer.h
@@ -235,7 +235,12 @@ class opengl_renderer
|
||||
float draw_range{0.0f};
|
||||
};
|
||||
|
||||
struct viewport_data {
|
||||
struct viewport_config {
|
||||
int width;
|
||||
int height;
|
||||
|
||||
glm::mat4 camera_transform;
|
||||
|
||||
std::unique_ptr<gl::framebuffer> msaa_fb;
|
||||
std::unique_ptr<gl::renderbuffer> msaa_rbc;
|
||||
std::unique_ptr<gl::renderbuffer> msaa_rbv;
|
||||
@@ -264,16 +269,16 @@ class opengl_renderer
|
||||
// methods
|
||||
std::unique_ptr<gl::program> make_shader(std::string v, std::string f);
|
||||
bool Init_caps();
|
||||
void setup_pass(renderpass_config &Config, rendermode const Mode, float const Znear = 0.f, float const Zfar = 1.f, bool const Ignoredebug = false);
|
||||
void setup_pass(viewport_config &Viewport, renderpass_config &Config, rendermode const Mode, float const Znear = 0.f, float const Zfar = 1.f, bool const Ignoredebug = false);
|
||||
void setup_matrices();
|
||||
void setup_drawing(bool const Alpha = false);
|
||||
void setup_shadow_map(opengl_texture *tex, renderpass_config conf);
|
||||
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(viewport_data &vp, rendermode const Mode);
|
||||
void Render_pass(viewport_config &vp, rendermode const Mode);
|
||||
// creates dynamic environment cubemap
|
||||
bool Render_reflections(viewport_data &vp);
|
||||
bool Render_reflections(viewport_config &vp);
|
||||
bool Render(world_environment *Environment);
|
||||
void Render(scene::basic_region *Region);
|
||||
void Render(section_sequence::iterator First, section_sequence::iterator Last);
|
||||
@@ -302,7 +307,7 @@ 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);
|
||||
bool init_viewport(viewport_config &vp);
|
||||
|
||||
void draw(const gfx::geometry_handle &handle);
|
||||
void draw(std::vector<gfx::geometrybank_handle>::iterator begin, std::vector<gfx::geometrybank_handle>::iterator end);
|
||||
@@ -350,7 +355,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
|
||||
viewport_config *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
|
||||
@@ -395,7 +400,7 @@ class opengl_renderer
|
||||
|
||||
std::unique_ptr<gl::vao> m_empty_vao;
|
||||
|
||||
viewport_data default_viewport;
|
||||
std::vector<std::unique_ptr<viewport_config>> m_viewports;
|
||||
|
||||
std::unique_ptr<gl::postfx> m_pfx_motionblur;
|
||||
std::unique_ptr<gl::postfx> m_pfx_tonemapping;
|
||||
|
||||
@@ -91,6 +91,6 @@ scenarioloader_mode::enter() {
|
||||
void
|
||||
scenarioloader_mode::exit() {
|
||||
|
||||
simulation::Time.init();
|
||||
simulation::Time.init(Global.starting_timestamp);
|
||||
simulation::Environment.init();
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ void state_manager::process_commands() {
|
||||
|
||||
if (commanddata.command == user_command::queueevent) {
|
||||
uint32_t id = std::round(commanddata.param1);
|
||||
basic_event *ev = Events.FindEventById(id);
|
||||
basic_event *ev = Events.FindEventById(id); // TODO: depends on vector position
|
||||
Events.AddToQuery(ev, nullptr);
|
||||
}
|
||||
|
||||
@@ -139,7 +139,26 @@ void state_manager::process_commands() {
|
||||
int light = std::round(commanddata.param1);
|
||||
float state = commanddata.param2;
|
||||
if (id < simulation::Instances.sequence().size())
|
||||
simulation::Instances.sequence()[id]->LightSet(light, state);
|
||||
simulation::Instances.sequence()[id]->LightSet(light, state); // TODO: depends on vector position
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::setdatetime) {
|
||||
int yearday = std::round(commanddata.param1);
|
||||
int minute = std::round(commanddata.param2 * 60.0);
|
||||
simulation::Time.set_time(yearday, minute);
|
||||
|
||||
simulation::Environment.compute_season(yearday);
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::setweather) {
|
||||
Global.fFogEnd = commanddata.param1;
|
||||
Global.Overcast = commanddata.param2;
|
||||
|
||||
simulation::Environment.compute_weather();
|
||||
}
|
||||
|
||||
if (commanddata.command == user_command::settemperature) {
|
||||
Global.AirTemperature = commanddata.param1;
|
||||
}
|
||||
|
||||
if (DebugModeFlag) {
|
||||
|
||||
@@ -35,7 +35,7 @@ world_environment::toggle_daylight() {
|
||||
|
||||
// calculates current season of the year based on set simulation date
|
||||
void
|
||||
world_environment::compute_season( int const Yearday ) const {
|
||||
world_environment::compute_season( int const Yearday ) {
|
||||
|
||||
using dayseasonpair = std::pair<int, std::string>;
|
||||
|
||||
@@ -59,7 +59,7 @@ world_environment::compute_season( int const Yearday ) const {
|
||||
|
||||
// calculates current weather
|
||||
void
|
||||
world_environment::compute_weather() const {
|
||||
world_environment::compute_weather() {
|
||||
|
||||
Global.Weather = (
|
||||
Global.Overcast <= 0.25 ? "clear:" :
|
||||
@@ -67,6 +67,8 @@ world_environment::compute_weather() const {
|
||||
( Global.Season != "winter:" ?
|
||||
"rain:" :
|
||||
"snow:" ) );
|
||||
|
||||
m_precipitation.update_weather();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -161,7 +163,11 @@ world_environment::update() {
|
||||
else if( Global.Weather == "snow:" ) {
|
||||
// reduce friction due to snow
|
||||
Global.FrictionWeatherFactor = 0.75f;
|
||||
m_precipitationsound.stop();
|
||||
}
|
||||
else {
|
||||
m_precipitationsound.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -33,9 +33,9 @@ public:
|
||||
// switches between static and dynamic daylight calculation
|
||||
void toggle_daylight();
|
||||
// calculates current season of the year based on set simulation date
|
||||
void compute_season( int const Yearday ) const;
|
||||
void compute_season( int const Yearday );
|
||||
// calculates current weather
|
||||
void compute_weather() const;
|
||||
void compute_weather();
|
||||
|
||||
private:
|
||||
// members
|
||||
|
||||
@@ -150,10 +150,9 @@ state_serializer::deserialize_atmo( cParser &Input, scene::scratch_data &Scratch
|
||||
// atmosphere color; legacy parameter, no longer used
|
||||
Input.getTokens( 3 );
|
||||
// fog range
|
||||
Input.getTokens( 2 );
|
||||
Input
|
||||
>> Global.fFogStart
|
||||
>> Global.fFogEnd;
|
||||
Input.getTokens( 1 ); // fog start ignored
|
||||
Input.getTokens( 1 );
|
||||
Input >> Global.fFogEnd;
|
||||
|
||||
if( Global.fFogEnd > 0.0 ) {
|
||||
// fog colour; optional legacy parameter, no longer used
|
||||
@@ -297,9 +296,24 @@ state_serializer::deserialize_firstinit( cParser &Input, scene::scratch_data &Sc
|
||||
simulation::Events.InitLaunchers();
|
||||
simulation::Memory.InitCells();
|
||||
|
||||
init_time();
|
||||
|
||||
Scratchpad.initialized = true;
|
||||
}
|
||||
|
||||
void state_serializer::init_time() {
|
||||
auto &time = simulation::Time.data();
|
||||
if( true == Global.ScenarioTimeCurrent ) {
|
||||
// calculate time shift required to match scenario time with local clock
|
||||
auto const *localtime = std::gmtime( &Global.starting_timestamp );
|
||||
Global.ScenarioTimeOffset = ( ( localtime->tm_hour * 60 + localtime->tm_min ) - ( time.wHour * 60 + time.wMinute ) ) / 60.f;
|
||||
}
|
||||
else if( false == std::isnan( Global.ScenarioTimeOverride ) ) {
|
||||
// scenario time override takes precedence over scenario time offset
|
||||
Global.ScenarioTimeOffset = ( ( Global.ScenarioTimeOverride * 60 ) - ( time.wHour * 60 + time.wMinute ) ) / 60.f;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
state_serializer::deserialize_group( cParser &Input, scene::scratch_data &Scratchpad ) {
|
||||
|
||||
@@ -583,17 +597,6 @@ state_serializer::deserialize_time( cParser &Input, scene::scratch_data &Scratch
|
||||
>> time.wHour
|
||||
>> time.wMinute;
|
||||
|
||||
if( true == Global.ScenarioTimeCurrent ) {
|
||||
// calculate time shift required to match scenario time with local clock
|
||||
auto timenow = std::time( 0 );
|
||||
auto const *localtime = std::localtime( &timenow );
|
||||
Global.ScenarioTimeOffset = ( ( localtime->tm_hour * 60 + localtime->tm_min ) - ( time.wHour * 60 + time.wMinute ) ) / 60.f;
|
||||
}
|
||||
else if( false == std::isnan( Global.ScenarioTimeOverride ) ) {
|
||||
// scenario time override takes precedence over scenario time offset
|
||||
Global.ScenarioTimeOffset = ( ( Global.ScenarioTimeOverride * 60 ) - ( time.wHour * 60 + time.wMinute ) ) / 60.f;
|
||||
}
|
||||
|
||||
// remaining sunrise and sunset parameters are no longer used, as they're now calculated dynamically
|
||||
// anything else left in the section has no defined meaning
|
||||
skip_until( Input, "endtime" );
|
||||
|
||||
@@ -73,6 +73,7 @@ private:
|
||||
TAnimModel * deserialize_model( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
|
||||
TDynamicObject * deserialize_dynamic( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
|
||||
sound_source * deserialize_sound( cParser &Input, scene::scratch_data &Scratchpad, scene::node_data const &Nodedata );
|
||||
void init_time();
|
||||
// skips content of stream until specified token
|
||||
void skip_until( cParser &Input, std::string const &Token );
|
||||
// transforms provided location by specifed rotation and offset
|
||||
|
||||
@@ -20,7 +20,7 @@ scenario_time Time;
|
||||
} // simulation
|
||||
|
||||
void
|
||||
scenario_time::init() {
|
||||
scenario_time::init(std::time_t timestamp) {
|
||||
char monthdaycounts[ 2 ][ 13 ] = {
|
||||
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
|
||||
{ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } };
|
||||
@@ -32,10 +32,7 @@ scenario_time::init() {
|
||||
auto const requestedminute { requestedtime % 60 };
|
||||
// cache requested elements, if any
|
||||
|
||||
#ifdef __linux__
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
tm *tms = localtime(&ts.tv_sec);
|
||||
std::tm *tms = std::gmtime(×tamp);
|
||||
m_time.wYear = tms->tm_year;
|
||||
m_time.wMonth = tms->tm_mon;
|
||||
m_time.wDayOfWeek = tms->tm_wday;
|
||||
@@ -43,16 +40,7 @@ scenario_time::init() {
|
||||
m_time.wHour = tms->tm_hour;
|
||||
m_time.wMinute = tms->tm_min;
|
||||
m_time.wSecond = tms->tm_sec;
|
||||
m_time.wMilliseconds = ts.tv_nsec / 1000000;
|
||||
|
||||
/*
|
||||
time_t local = mktime(localtime(&ts.tv_sec));
|
||||
time_t utc = mktime(gmtime(&ts.tv_sec));
|
||||
m_timezonebias = (double)(local - utc) / 3600.0f;
|
||||
*/
|
||||
#elif _WIN32
|
||||
::GetLocalTime( &m_time );
|
||||
#endif
|
||||
m_time.wMilliseconds = 0;
|
||||
|
||||
if( Global.fMoveLight > 0.0 ) {
|
||||
// day and month of the year can be overriden by scenario setup
|
||||
@@ -94,7 +82,7 @@ scenario_time::init() {
|
||||
zonebias += timezoneinfo.StandardBias;
|
||||
}
|
||||
|
||||
m_timezonebias = ( zonebias / 60.0 );
|
||||
m_timezonebias = ( zonebias / 60.0 );
|
||||
}
|
||||
|
||||
void
|
||||
@@ -197,6 +185,12 @@ scenario_time::julian_day() const {
|
||||
return JD;
|
||||
}
|
||||
|
||||
void scenario_time::set_time(int yearday, int minute) {
|
||||
daymonth(m_time.wDay, m_time.wMonth, m_time.wYear, yearday);
|
||||
m_time.wHour = minute / 60;
|
||||
m_time.wMinute = minute % 60;
|
||||
}
|
||||
|
||||
// calculates day of week for provided date
|
||||
int
|
||||
scenario_time::day_of_week( int const Day, int const Month, int const Year ) const {
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
scenario_time() {
|
||||
m_time.wHour = 10; m_time.wMinute = 30; }
|
||||
void
|
||||
init();
|
||||
init(time_t timestamp);
|
||||
void
|
||||
update( double const Deltatime );
|
||||
inline
|
||||
@@ -45,6 +45,8 @@ public:
|
||||
double
|
||||
zone_bias() const {
|
||||
return m_timezonebias; }
|
||||
void
|
||||
set_time(int yearday, int minute);
|
||||
|
||||
/** Returns std::string in format: `"mm:ss"`. */
|
||||
operator std::string();
|
||||
|
||||
26
sn_utils.cpp
26
sn_utils.cpp
@@ -38,6 +38,18 @@ int32_t sn_utils::ld_int32(std::istream &s)
|
||||
return reinterpret_cast<int32_t&>(v);
|
||||
}
|
||||
|
||||
// deserialize little endian int64
|
||||
int64_t sn_utils::ld_int64(std::istream &s)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
s.read((char*)buf, 8);
|
||||
uint64_t v = ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) |
|
||||
((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) |
|
||||
((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) |
|
||||
((uint64_t)buf[1] << 8) | (uint64_t)buf[0];
|
||||
return reinterpret_cast<int64_t&>(v);
|
||||
}
|
||||
|
||||
// deserialize little endian ieee754 float32
|
||||
float sn_utils::ld_float32(std::istream &s)
|
||||
{
|
||||
@@ -133,6 +145,20 @@ void sn_utils::ls_int32(std::ostream &s, int32_t v)
|
||||
s.write((char*)buf, 4);
|
||||
}
|
||||
|
||||
void sn_utils::ls_int64(std::ostream &s, int64_t v)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
buf[0] = v;
|
||||
buf[1] = v >> 8;
|
||||
buf[2] = v >> 16;
|
||||
buf[3] = v >> 24;
|
||||
buf[4] = v >> 32;
|
||||
buf[5] = v >> 40;
|
||||
buf[6] = v >> 48;
|
||||
buf[7] = v >> 56;
|
||||
s.write((char*)buf, 8);
|
||||
}
|
||||
|
||||
void sn_utils::ls_float32(std::ostream &s, float t)
|
||||
{
|
||||
uint32_t v = reinterpret_cast<uint32_t&>(t);
|
||||
|
||||
@@ -10,6 +10,7 @@ public:
|
||||
static uint16_t ld_uint16(std::istream&);
|
||||
static uint32_t ld_uint32(std::istream&);
|
||||
static int32_t ld_int32(std::istream&);
|
||||
static int64_t ld_int64(std::istream&);
|
||||
static float ld_float32(std::istream&);
|
||||
static double ld_float64(std::istream&);
|
||||
static std::string d_str(std::istream&);
|
||||
@@ -21,6 +22,7 @@ public:
|
||||
static void ls_uint16(std::ostream&, uint16_t);
|
||||
static void ls_uint32(std::ostream&, uint32_t);
|
||||
static void ls_int32(std::ostream&, int32_t);
|
||||
static void ls_int64(std::ostream&, int64_t);
|
||||
static void ls_float32(std::ostream&, float);
|
||||
static void ls_float64(std::ostream&, double);
|
||||
static void s_str(std::ostream&, std::string);
|
||||
|
||||
@@ -79,6 +79,14 @@ init() {
|
||||
"Map",
|
||||
"Mode windows",
|
||||
|
||||
"Time and environment",
|
||||
"Time",
|
||||
"Day in year",
|
||||
"Visibility",
|
||||
"Overcast and precipitation",
|
||||
"Temperature",
|
||||
"Apply",
|
||||
|
||||
"Straight |",
|
||||
"Divert /",
|
||||
|
||||
@@ -242,6 +250,14 @@ init() {
|
||||
u8"Mapa",
|
||||
u8"Okna trybu",
|
||||
|
||||
u8"Czas i środowisko",
|
||||
u8"Czas",
|
||||
u8"Dzień w roku",
|
||||
u8"Widoczność",
|
||||
u8"Zachmurzenie i opady",
|
||||
u8"Temperatura",
|
||||
u8"Zastosuj",
|
||||
|
||||
u8"Prosto |",
|
||||
u8"W bok /",
|
||||
|
||||
|
||||
@@ -68,6 +68,14 @@ enum string {
|
||||
ui_map,
|
||||
ui_mode_windows,
|
||||
|
||||
time_window,
|
||||
time_time,
|
||||
time_yearday,
|
||||
time_visibility,
|
||||
time_weather,
|
||||
time_temperature,
|
||||
time_apply,
|
||||
|
||||
map_straight,
|
||||
map_divert,
|
||||
|
||||
|
||||
@@ -133,8 +133,10 @@ void ui::map_panel::render_labels(glm::mat4 transform, ImVec2 origin, glm::vec2
|
||||
for (TDynamicObject *vehicle : simulation::Vehicles.sequence()) {
|
||||
if (vehicle->Prev() || !vehicle->Mechanik)
|
||||
continue;
|
||||
if (vehicle->Mechanik->TrainName().empty())
|
||||
continue;
|
||||
|
||||
std::string label = vehicle->Mechanik->TrainName();
|
||||
if (label.empty() || label == "none")
|
||||
label = ToUpper(vehicle->name());
|
||||
|
||||
glm::vec4 ndc_pos = transform * glm::vec4(glm::vec3(vehicle->GetPosition()), 1.0f);
|
||||
if (glm::abs(ndc_pos.x) > 1.0f || glm::abs(ndc_pos.z) > 1.0f)
|
||||
@@ -144,11 +146,10 @@ void ui::map_panel::render_labels(glm::mat4 transform, ImVec2 origin, glm::vec2
|
||||
|
||||
TDynamicObject *veh = vehicle;
|
||||
|
||||
const char *desc = vehicle->Mechanik->TrainName().c_str();
|
||||
ImVec2 textsize = ImGui::CalcTextSize(desc);
|
||||
ImVec2 textsize = ImGui::CalcTextSize(label.c_str());
|
||||
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::TextUnformatted(label.c_str());
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
|
||||
41
widgets/time.cpp
Normal file
41
widgets/time.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "stdafx.h"
|
||||
#include "widgets/time.h"
|
||||
#include "simulationtime.h"
|
||||
#include "Globals.h"
|
||||
|
||||
ui::time_panel::time_panel()
|
||||
: ui_panel(LOC_STR(time_window), false)
|
||||
{
|
||||
size.x = 450;
|
||||
}
|
||||
|
||||
void ui::time_panel::render_contents()
|
||||
{
|
||||
ImGui::SliderFloat(LOC_STR(time_time), &time, 0.0f, 24.0f, "%.1f");
|
||||
ImGui::SliderInt(LOC_STR(time_yearday), &yearday, 1, 365);
|
||||
ImGui::SliderFloat(LOC_STR(time_visibility), &fog, 50.0f, 3000.0f, "%.0f");
|
||||
ImGui::SliderFloat(LOC_STR(time_weather), &overcast, 0.0f, 2.0f, "%.1f");
|
||||
ImGui::SliderFloat(LOC_STR(time_temperature), &temperature, -20.0f, 40.0f, "%.0f");
|
||||
|
||||
if (ImGui::Button(LOC_STR(time_apply))) {
|
||||
m_relay.post(user_command::setdatetime, (double)yearday, time, 1, 0);
|
||||
m_relay.post(user_command::setweather, fog, overcast, 1, 0);
|
||||
m_relay.post(user_command::settemperature, temperature, 0.0, 1, 0);
|
||||
|
||||
is_open = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ui::time_panel::open()
|
||||
{
|
||||
auto &data = simulation::Time.data();
|
||||
time = (float)data.wHour + (float)data.wMinute / 60.0f;
|
||||
|
||||
yearday = simulation::Time.year_day();
|
||||
fog = Global.fFogEnd;
|
||||
|
||||
overcast = Global.Overcast;
|
||||
temperature = Global.AirTemperature;
|
||||
|
||||
is_open = true;
|
||||
}
|
||||
21
widgets/time.h
Normal file
21
widgets/time.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "uilayer.h"
|
||||
#include "translation.h"
|
||||
#include "command.h"
|
||||
|
||||
namespace ui {
|
||||
class time_panel : public ui_panel {
|
||||
command_relay m_relay;
|
||||
|
||||
float time;
|
||||
int yearday;
|
||||
float fog;
|
||||
float overcast;
|
||||
float temperature;
|
||||
|
||||
public:
|
||||
time_panel();
|
||||
|
||||
void render_contents() override;
|
||||
void open();
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user