17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-29 05:47:28 +01:00

Cast shadows of scenery lamps from the scenery, nearby vehicles and the cab in the GL 4.6 renderer

This commit is contained in:
2026-10-16 11:32:45 +02:00
parent 06d7b9e9f0
commit 2f0a06b98e
12 changed files with 665 additions and 75 deletions

View File

@@ -633,6 +633,7 @@ bool opengl46_renderer::Render()
{
// a scenery is being loaded: the cells, instances and models the lights were taken from may be gone
m_locallights.reset();
m_localshadows.reset_lamps();
}
// generate new frame
opengl_texture::reset_unit_cache();
@@ -644,6 +645,9 @@ bool opengl46_renderer::Render()
++m_instancerecordsversion;
m_nodestateversion = scene::node_state_version;
}
// scenery lamps which get shadows in this frame. their maps hold the scenery as of the version of the instance records
if (simulation::is_ready && (false == Global.gfx_skiprendering))
m_localshadows.update_lamps(m_locallights.lights(), Global.pCamera.Pos, m_instancerecordsversion, m_framestamp);
// instanced draws of the frame place their matrices in the next of the instance buffers, the one used the longest ago
m_instancebufferindex = ( m_instancebufferindex + 1 ) % m_instancebuffers.size();
m_instancebufferfill = 1;
@@ -967,7 +971,10 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
}
if (vp.main)
{
Render_local_shadows();
Render_lamp_shadows();
}
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
@@ -2966,14 +2973,18 @@ void opengl46_renderer::Render(cell_sequence::iterator First, cell_sequence::ite
for (auto *instance : cell->m_instancesopaque)
{
if( instance->m_instanceable ) { continue; } // already handled via bucket
// the model of a lamp would shadow everything in the map of the lamp
if( instance == m_cubeskipinstance ) { continue; }
Render(instance);
}
// opaque parts of vehicles
for (auto *path : cell->m_paths)
{
for (auto *dynamic : path->Dynamics)
// opaque parts of vehicles. the kept scenery part of the map of a lamp is drawn without them
if( false == m_cubescenery ) {
for (auto *path : cell->m_paths)
{
Render(dynamic);
for (auto *dynamic : path->Dynamics)
{
Render(dynamic);
}
}
}
break;
@@ -6278,6 +6289,7 @@ void opengl46_renderer::Update_Lights(light_array &Lights, bool const Headlights
m_lights_lit = static_cast<uint32_t>(light_i - headlightsstart);
// lamps of models and vehicles, the most important first, those which may be seen lighting something in this view
if( locallights ) {
auto const toworld{ glm::dmat4{ glm::transpose( glm::mat3{ mv } ) } };
uint32_t placed{ 0 };
uint32_t dropped{ 0 };
for( auto const &local : m_locallights.lights() ) {
@@ -6288,7 +6300,16 @@ void opengl46_renderer::Update_Lights(light_array &Lights, bool const Headlights
++dropped;
continue;
}
Write_Local_Light( light_data.lights[ light_i++ ], local, camera, mv );
auto &light{ light_data.lights[ light_i++ ] };
Write_Local_Light( light, local, camera, mv );
std::size_t slot;
if( auto const *lamp{ m_localshadows.lamp( local, slot ) } ) {
// view space to the space of the map: world axes with the lamp at the origin
light.headlight_projection = glm::mat4{ glm::translate( glm::dmat4{ 1.0 }, camera - lamp->position ) * toworld };
light.headlight_weights = m_localshadows.lamp_weights( slot );
// the map is in the array of the scenery lamps
light.linear = 1.0f;
}
++placed;
}
m_locallights.placed( placed, dropped );
@@ -6372,6 +6393,118 @@ void opengl46_renderer::Render_local_shadows()
glPolygonMode(GL_FRONT_AND_BACK, Global.bWireFrame ? GL_LINE : GL_FILL);
}
void opengl46_renderer::Render_lamp_shadows()
{
auto const &lamps{m_localshadows.lamps()};
if (lamps.empty() || (false == Global.gfx_lights_clustered))
return;
// vehicles near the camera cast the shadows of the lamps, and the occupied cab
m_lampshadowvehicles.clear();
auto const vehiclesrange{static_cast<double>(Global.gfx_lights_shadows_vehicles_range)};
if (vehiclesrange > 0.0)
{
for (auto *vehicle : simulation::Vehicles.sequence())
{
if (glm::length(vehicle->GetPosition() - Global.pCamera.Pos) <= vehiclesrange + vehicle->radius())
m_lampshadowvehicles.emplace_back(vehicle);
}
}
TDynamicObject *cab{nullptr};
if ((vehiclesrange > 0.0) && (false == FreeFlyModeFlag) && Global.render_cab && (simulation::Train != nullptr))
{
cab = simulation::Train->Dynamic();
if ((cab != nullptr) && ((false == cab->bDisplayCab) || (cab->mdKabina == nullptr) || (cab->mdKabina == cab->mdModel)))
cab = nullptr;
}
auto timing{false};
for (auto const idx : lamps)
{
auto const &slot{m_localshadows.slot(idx)};
if (m_localshadows.scenery_wanted(idx))
{
if (false == timing)
{
m_localshadows.lamps_timer_begin();
timing = true;
}
Render_lamp_shadow(idx, false, nullptr);
}
if (false == slot.scenery)
continue;
// the vehicles by the lamp, within its reach
m_lampshadowslot.clear();
for (auto *vehicle : m_lampshadowvehicles)
{
if (glm::length(vehicle->GetPosition() - slot.position) <= slot.range + vehicle->radius())
m_lampshadowslot.emplace_back(vehicle);
}
auto *slotcab{(cab != nullptr) && (glm::length(cab->GetPosition() - slot.position) <= slot.range + cab->radius()) ? cab : nullptr};
if (m_lampshadowslot.empty() && (slotcab == nullptr))
{
m_localshadows.no_vehicles(idx);
continue;
}
if (false == m_localshadows.vehicles_wanted(idx, m_framestamp))
continue;
if (false == timing)
{
m_localshadows.lamps_timer_begin();
timing = true;
}
Render_lamp_shadow(idx, true, slotcab);
}
if (timing)
m_localshadows.lamps_timer_end();
}
void opengl46_renderer::Render_lamp_shadow(std::size_t const Slot, bool const Vehicles, TDynamicObject *Cab)
{
auto const &slot{m_localshadows.slot(Slot)};
glDebug("render lamp shadows");
// the shadow pass from the lamp, in world axes: the camera of the pass is the lamp, its frustum the box of its reach. the
// distances which pick the details of the models are measured from the real camera, as in the shadows of the sun
m_renderpass = m_colorpass;
m_renderpass.draw_mode = rendermode::shadows;
m_renderpass.draw_range = slot.range;
m_renderpass.draw_stats = {};
auto &camera{m_renderpass.pass_camera};
camera.position() = slot.position;
camera.modelview() = glm::mat4{glm::translate(glm::dmat4{1.0}, -slot.position)};
auto const reach{slot.range};
camera.projection() = glm::ortho(-reach, reach, -reach, reach, -reach, reach);
camera.update_frustum();
OpenGLMatrices.mode(GL_MODELVIEW);
OpenGLMatrices.load_matrix(glm::mat4{1.f});
m_localshadows.begin_lamp(Slot, Vehicles, m_instancerecordsversion, m_framestamp);
m_cubeshadows = true;
if (Vehicles)
{
for (auto *vehicle : m_lampshadowslot)
{
Render(vehicle);
}
if (Cab != nullptr)
{
Render_cab(Cab, 0.0f, false);
}
}
else
{
m_cubescenery = true;
m_cubeskipinstance = static_cast<TAnimModel const *>(slot.owner);
Render(simulation::Region);
m_cubeskipinstance = nullptr;
m_cubescenery = false;
}
m_cubeshadows = false;
m_localshadows.end_lamp(static_cast<std::uint32_t>(m_renderpass.draw_stats.drawcalls));
m_renderpass = m_colorpass;
glPolygonMode(GL_FRONT_AND_BACK, Global.bWireFrame ? GL_LINE : GL_FILL);
}
void opengl46_renderer::Update_Clusters(viewport_config &vp, glm::ivec2 const &Targetsize)
{
glDebug("Update_Clusters");