From 65e104f81351b2c402766100901292963ffd2b99 Mon Sep 17 00:00:00 2001 From: Hirek193 Date: Wed, 28 Oct 2026 21:06:02 +0100 Subject: [PATCH] Light the occupied cab with the light points of its model --- docs/opengl46-renderer.md | 17 ++++---- rendering/common/locallights.cpp | 74 ++++++-------------------------- rendering/common/locallights.h | 14 ++---- 3 files changed, 25 insertions(+), 80 deletions(-) diff --git a/docs/opengl46-renderer.md b/docs/opengl46-renderer.md index 575abfa6..d3a49ede 100644 --- a/docs/opengl46-renderer.md +++ b/docs/opengl46-renderer.md @@ -173,14 +173,13 @@ scales can also be changed in the **Light clusters** debug window. light of the whole cab and weakened the shadows of the sun in it; now a share of that ambient light is kept for the light which bounces around the cab (`gfx.lights.cab.bounce`), and the sun shadows stay. With the probe of the cab (below) the light which bounces comes from the probe instead and nothing of the lamp is kept in the ambient light. -- **Lights of the cab model.** Light points (`FreeSpotLight`) of the cab model whose names start with `cablight` (any case) - are the lamps of the cab: they shine with its lamp switch (off, dimmed, half bright without 110 V), in place of the lamp - under the top of the cab, which a cab model gets only when it has no `cablight` of its own. Those whose names start with - `dashboardlight` are the lights of the desk: they shine with the dashboard light (`i-dashboardlight:` / - `dashboardlight_sw:`). Their colour, power, cone and the optional `Radius:`/`SoftFalloff:`/`CastShadows:` are those of the - light point (brightness `gfx.lights.freespot.intensity`, the self-illumination threshold doesn't matter), and they fade - in the daylight as the cab lamp does. Visibility counts: a light point under a submodel that is hidden doesn't shine. - Other light points of the cab only glare, as before. +- **Lights of the cab model.** Every light point (`FreeSpotLight`) of the cab model lights the cab, in place of the lamp + under the top of the cab, which a cab model gets only when it has no light points at all. They are switched by the + visibility of the submodels they are children of: under the submodel of `i-cablight:` a light point shines with the + lamp of the cab, under `i-dashboardlight:` with the dashboard light, and so on (one not under any switched submodel + shines always). Their colour, power, cone and the optional `Radius:`/`SoftFalloff:`/`CastShadows:` are those of the light + point (brightness `gfx.lights.freespot.intensity`, the self-illumination threshold doesn't matter), and they fade in the + daylight as the cab lamp does. The dimming of the cab lamp applies only to the lamp under the top. The lamps of models and compartments fade in from the light level at which scenery lamps switch on (`ls_Dark`) to full darkness; in daylight they are not gathered at all. The cab lamp and the lamps of scenery models nearest to the camera cast @@ -248,7 +247,7 @@ shadows of the sun, see `headlight shadows:` in Shift+F12); with the vehicle on | `gfx.lights.cab.bounce` | `0.3` | 0..1: share of the former ambient light of the cab lamp kept with the lamp on | | `gfx.lights.range` | `300` | distance from the camera the lamps of models and vehicles are gathered from, metres | | `gfx.lights.shadows` | `medium` | `no`/`low`/`medium`/`high` (or 0..3): shadows of the cab lamp and the scenery lamps, see above | -| `gfx.lights.shadows.cab` | `4` | 0..8: how many lights of the cab (the brightest; the lamp, `cablight`/`dashboardlight` light points) cast the shadows of the cab, a cube map each; 0: none | +| `gfx.lights.shadows.cab` | `4` | 0..8: how many lights of the cab (the brightest; the lamp or the light points of the cab model) cast the shadows of the cab, a cube map each; 0: none | | `gfx.lights.shadows.lamps` | `8` | 0..32: how many scenery lamps nearest to the camera cast shadows; 0: none | | `gfx.lights.shadows.lamps.size` | `512` | 128..2048: size of a face of the shadow map of a scenery lamp | | `gfx.lights.shadows.lamps.range` | `100` | distance from the camera within which scenery lamps cast shadows, metres | diff --git a/rendering/common/locallights.cpp b/rendering/common/locallights.cpp index 4e401265..9116dd8f 100644 --- a/rendering/common/locallights.cpp +++ b/rendering/common/locallights.cpp @@ -13,10 +13,8 @@ http://mozilla.org/MPL/2.0/. #include #include -#include #include #include -#include #include "utilities/Globals.h" #include "rendering/common/skyocclusion.h" @@ -42,18 +40,6 @@ float const LIGHT_DUSK_LUMINANCE{0.325f}; float const LIGHT_DAY_LUMINANCE{1.0f}; // lights of the occupied cab, at most std::size_t const CAB_LIGHTS_MAX{16}; -// kinds of the light points of a cab model, by the start of their names (any case): the lamps of the cab and the lights of the desk -std::uint8_t const CAB_LIGHT_LAMP{0x1}; -std::uint8_t const CAB_LIGHT_DASHBOARD{0x2}; - -std::uint8_t cab_light_kind(std::string const &Name) -{ - auto const starts = [&Name](std::string_view const Prefix) { - return (Name.size() >= Prefix.size()) && - std::equal(std::begin(Prefix), std::end(Prefix), std::begin(Name), [](char const Left, char const Right) { return Left == static_cast(std::tolower(static_cast(Right))); }); - }; - return starts("cablight") ? CAB_LIGHT_LAMP : starts("dashboardlight") ? CAB_LIGHT_DASHBOARD : 0; -} glm::vec3 to_linear(glm::vec3 const &Color) { @@ -209,7 +195,6 @@ void gfx::local_lights::reset() m_cablights.clear(); m_cablampon = false; m_modellights.clear(); - m_cabkinds.clear(); m_cells.clear(); m_sections.clear(); m_models.clear(); @@ -341,25 +326,21 @@ void gfx::local_lights::gather_cab_lights(gfx::geometrybank_manager const &Geome { return; } - // as bright as the ambient light the lamp used to add to the cab: lost in the daylight. the lamps of the cab at the level of - // their switch (off, dimmed, with or without the 110 V), the lights of the desk with the dashboard light (i-dashboardlight:) + // as bright as the ambient light the lamp used to add to the cab: lost in the daylight auto const luminance{Global.fLuminance * gfx::vehicle_light_level(vehicle)}; auto const daylight{std::clamp(1.25f - static_cast(luminance), 0.f, 1.f)}; - auto const cablevel{std::max(vehicle->InteriorLightLevel, 0.f) * daylight}; - auto const dashboardlevel{(train->btDashboardLight.GetValue() ? 1.f : 0.f) * daylight}; - auto const kinds{cab_light_kinds(vehicle->mdKabina)}; + auto const lightpoints{has_light_points(vehicle->mdKabina)}; - if ((kinds != 0) && ((cablevel > 0.f) || (dashboardlevel > 0.f))) + if (lightpoints && (daylight > 0.f)) { - // the light points of the cab model, in its space, the way Render_cab() sets it up + // the light points of the cab model, in its space, the way Render_cab() sets it up. they are switched by the visibility of + // the submodels they're in (the lamp of the cab under i-cablight:, the lights of the desk under i-dashboardlight:, ...) TSubModel::iInstance = reinterpret_cast(vehicle); TSubModel::iVariantSeed = vehicle->variant_seed(); TSubModel::fSquareDist = 0.f; auto const count{m_lights.size()}; m_walkcab = true; - m_walkcablevel = cablevel; - m_walkdashboardlevel = dashboardlevel; - walk(vehicle->mdKabina->Root, glm::mat4{1.f}, glm::dmat4{1.0}, Global.gfx_lights_freespot_intensity); + walk(vehicle->mdKabina->Root, glm::mat4{1.f}, glm::dmat4{1.0}, Global.gfx_lights_freespot_intensity * daylight); m_walkcab = false; // within the cab: without shadows (gfx.lights.shadows) the light goes through its walls auto const reach{std::max(2.f * glm::length(bounds.extent), 2.5f)}; @@ -378,8 +359,10 @@ void gfx::local_lights::gather_cab_lights(gfx::geometrybank_manager const &Geome m_lights.resize(count); } - // the lamp of a cab model without lamps of its own: under the ceiling, in the middle of the cab - if (((kinds & CAB_LIGHT_LAMP) == 0) && (cablevel > 0.f)) + // the lamp of a cab model without light points of its own: under the ceiling, in the middle of the cab, at the level of its + // switch (off, dimmed, with or without the 110 V) + auto const cablevel{std::max(vehicle->InteriorLightLevel, 0.f) * daylight}; + if ((false == lightpoints) && (cablevel > 0.f)) { auto const lamp{glm::vec3{bounds.center.x, std::max(bounds.center.y, bounds.center.y + bounds.extent.y - 0.25f), bounds.center.z}}; cab_light cablight; @@ -441,10 +424,8 @@ void gfx::local_lights::add_light_point(TSubModel const &Submodel, glm::mat4 con { if (m_walkcab) { - // the lights of the cab: with their switches, whatever the self-illumination threshold. other light points of the cab only glare - auto const kind{cab_light_kind(Submodel.pName)}; - Brightness *= (kind == CAB_LIGHT_LAMP ? m_walkcablevel : kind == CAB_LIGHT_DASHBOARD ? m_walkdashboardlevel : 0.f); - if ((Brightness <= 0.f) || (Submodel.f4Emision.a <= 0.f) || (Submodel.fVisible <= 0.f)) + // the lights of the cab: switched with the submodels they're in, whatever the self-illumination threshold + if ((Submodel.f4Emision.a <= 0.f) || (Submodel.fVisible <= 0.f)) { return; } @@ -520,35 +501,6 @@ bool gfx::local_lights::has_light_points(TModel3d *Model) return found; } -std::uint8_t gfx::local_lights::cab_light_kinds(TModel3d *Model) -{ - auto lookup{m_cabkinds.find(Model)}; - if (lookup != m_cabkinds.end()) - { - return lookup->second; - } - std::uint8_t kinds{0}; - std::vector pending{Model->Root}; - while (false == pending.empty()) - { - auto const *submodel{pending.back()}; - pending.pop_back(); - for (; submodel != nullptr; submodel = submodel->Next) - { - if (submodel->eType == TP_FREESPOTLIGHT) - { - kinds |= cab_light_kind(submodel->pName); - } - if (submodel->Child != nullptr) - { - pending.emplace_back(submodel->Child); - } - } - } - m_cabkinds.emplace(Model, kinds); - return kinds; -} - gfx::local_lights::cell_data const &gfx::local_lights::cell_instances(scene::basic_cell &Cell, bool const Rebuild) { // light points are drawn in the translucent phase, so the instances which have them are on the translucent list of the cell @@ -636,7 +588,7 @@ void gfx::local_lights::draw_debug_ui() ImGui::TextUnformatted("local lights (need clustered lights)"); ImGui::Checkbox("light points of scenery models", &Global.gfx_lights_scenery); ImGui::Checkbox("light points and compartments of vehicles", &Global.gfx_lights_vehicles); - ImGui::Checkbox("cab lamp as a light (cablight... / dashboardlight... light points of the cab)", &Global.gfx_lights_cab); + ImGui::Checkbox("cab lamp as a light (or the light points of the cab model)", &Global.gfx_lights_cab); ImGui::SliderFloat("light point brightness", &Global.gfx_lights_freespot_intensity, 0.f, 50.f); ImGui::SliderFloat("compartment brightness", &Global.gfx_lights_compartment_intensity, 0.f, 10.f); ImGui::SliderFloat("cab lamp brightness", &Global.gfx_lights_cab_intensity, 0.f, 10.f); diff --git a/rendering/common/locallights.h b/rendering/common/locallights.h index 590eff91..3dfcb52d 100644 --- a/rendering/common/locallights.h +++ b/rendering/common/locallights.h @@ -34,9 +34,9 @@ class TDynamicObject; // light along the -z axis of its submodel, with the cone of its glare. lamps of scenery models switched with Light_On / Light_Off // and the self-illumination of vehicle compartments (TDynamicObject::Sections) work through the same state // - the lit compartments of vehicles (low poly interior sections) without light points of their own: a point light in the middle -// - the lights of the occupied cab, in place of the ambient light its lamp used to add: the light points of the cab model (mdKabina) -// named cablight... (the lamps of the cab, switched and dimmed with them) and dashboardlight... (the lights of the desk, switched -// with the dashboard light, i-dashboardlight:). a cab model without any cablight... gets a point light under its top instead +// - the lights of the occupied cab, in place of the ambient light its lamp used to add: the light points of the cab model (mdKabina), +// any of them, switched by the visibility of the submodels they're in (children of i-cablight:, i-dashboardlight: ...). a cab +// model without light points gets a point light under its top instead, switched and dimmed with the lamp of the cab // gathered once per frame around the camera, written to the light buffers by the renderers (Update_Lights()) namespace gfx { @@ -139,8 +139,6 @@ class local_lights void gather_vehicle(TDynamicObject *Vehicle, glm::dvec3 const &Camera, gfx::geometrybank_manager const &Geometry, float const Freespotbrightness, float const Compartmentbrightness); void gather_cab_lights(gfx::geometrybank_manager const &Geometry); bool has_light_points(TModel3d *Model); - // the kinds of the light points of a cab model by their names, CAB_LIGHT_LAMP | CAB_LIGHT_DASHBOARD (locallights.cpp) - std::uint8_t cab_light_kinds(TModel3d *Model); cell_data const &cell_instances(scene::basic_cell &Cell, bool const Rebuild); section_data const §ion(TModel3d *Model, TSubModel *Section, gfx::geometrybank_manager const &Geometry); section_data const &model_bounds(TModel3d *Model, gfx::geometrybank_manager const &Geometry); @@ -149,13 +147,9 @@ class local_lights std::vector m_cablights; bool m_cablampon{false}; TDynamicObject const *m_cablampvehicle{nullptr}; - // walk() goes through the cab model: only its light points of the cab and of the desk, at these levels (0-1) of their switches, - // whatever the self-illumination threshold + // walk() goes through the cab model: its light points shine while they're visible, whatever the self-illumination threshold bool m_walkcab{false}; - float m_walkcablevel{0.f}; - float m_walkdashboardlevel{0.f}; std::unordered_map m_modellights; // whether the model has light points - std::unordered_map m_cabkinds; // the kinds of light points of cab models std::unordered_map m_cells; std::unordered_map m_sections; // compartments, by their submodel std::unordered_map m_models; // bounds of cab models