17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-29 04:37:30 +01:00

Light the occupied cab with the light points of its model

This commit is contained in:
2026-10-28 21:16:22 +01:00
parent 65e104f813
commit 8c6a47f42c
5 changed files with 70 additions and 19 deletions

View File

@@ -187,7 +187,9 @@ shadows (see below); the others (lamps of vehicles, compartments, farther scener
within their reach, which is kept short for compartments.
**Shadows of the cab lamp** (`gfx.lights.shadows`): the cab casts shadows in the light of its lamp (and of the brightest
`gfx.lights.shadows.cab` lights of the cab model, each with a map of its own; the maps are made as they're needed), the desk, the seats,
`gfx.lights.shadows.cab` lights of the cab model, each with a map of its own, made as they're needed; the other lights of the
cab take the map of the closest light with one; for the light points the near plane is 3 cm, or their `Radius:`, so the desk
right under its lights holds their light back; a few maps are redrawn per frame), the desk, the seats,
the levers and the walls (light leaves the cab only through the windows, glass doesn't stop it). They come from a cube
shadow map around the lamp, drawn with the opaque parts of the cab model only. The map is kept in the space of the cab, so
it stays right as the vehicle moves; it is drawn anew every few frames, for the moving parts of the cab, and at once when
@@ -247,7 +249,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 or the light points of the cab model) cast the shadows of the cab, a cube map each; 0: none |
| `gfx.lights.shadows.cab` | `8` | 0..16: how many lights of the cab (the brightest; the lamp or the light points of the cab model) have a cube shadow map of their own; the others take the map of the closest light with one (within 1 m); 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 |

View File

@@ -35,6 +35,18 @@ float lamp_near(gfx::local_lights::light const &Light)
{
return std::clamp(Light.radius, local_shadows::shadow_near, std::max(local_shadows::shadow_near, Light.range * 0.5f));
}
// the same for a light of the cab, which is often right by what it lights and what holds its light back (the lights of the desk
// under its edge): only what is within a few centimetres, or within its sphere, casts no shadow
float const CAB_SHADOW_NEAR{0.03f};
float cab_near(gfx::local_lights::light const &Light)
{
return std::clamp(Light.radius, CAB_SHADOW_NEAR, std::max(CAB_SHADOW_NEAR, Light.range * 0.5f));
}
// lights of the cab with a shorter reach aren't worth a map of their own, metres
float const CAB_RANGE_MIN{0.25f};
// a light of the cab without a map of its own takes the shadows of the map of the closest light within this, metres: the desk, the
// walls are where they are for both
float const CAB_SHARE_DISTANCE{1.0f};
} // namespace
@@ -172,7 +184,7 @@ void local_shadows::update_cab(TDynamicObject const *Vehicle, TModel3d const *Ca
{
break;
}
if (light.source.cast_shadows && (light.source.range >= LAMP_RANGE_MIN))
if (light.source.cast_shadows && (light.source.range >= CAB_RANGE_MIN))
{
picked.emplace_back(&light);
}
@@ -191,7 +203,8 @@ void local_shadows::update_cab(TDynamicObject const *Vehicle, TModel3d const *Ca
}
}
auto const place = [&](cab_slot &Slot, std::size_t const Index, gfx::local_lights::cab_light const &Light) {
auto const nearplane{lamp_near(Light.source)};
// the lamp put under the top of a cab model without lights of its own keeps the near plane it had
auto const nearplane{Light.source.part == static_cast<void const *>(Vehicle) ? lamp_near(Light.source) : cab_near(Light.source)};
if ((Slot.lamp != Light.local) || (Slot.nearplane != nearplane))
{
Slot.valid = false;
@@ -200,12 +213,6 @@ void local_shadows::update_cab(TDynamicObject const *Vehicle, TModel3d const *Ca
Slot.nearplane = nearplane;
Slot.wantedframe = Framestamp;
m_cabwanted.emplace_back(Index);
// the moving parts of the cab are followed every few frames
if ((false == Slot.valid) || (Framestamp - Slot.drawnframe >= setup(level).interval))
{
Slot.drawnframe = Framestamp;
m_cabdraws.emplace_back(Index);
}
};
// lights which already have a slot keep it, the others take the slots of lights no longer picked
std::vector<gfx::local_lights::cab_light const *> unplaced;
@@ -242,6 +249,33 @@ void local_shadows::update_cab(TDynamicObject const *Vehicle, TModel3d const *Ca
break;
}
}
// the maps to be drawn: those which don't hold their light yet, at once, and those of the moving parts of the cab, every few
// frames, a few of them per frame (the ones drawn longest ago first): a cab may have many lights
std::vector<std::size_t> refresh;
for (auto const idx : m_cabwanted)
{
auto const &slot{m_cabslots[idx]};
if (false == slot.valid)
{
m_cabdraws.emplace_back(idx);
}
else if (Framestamp - slot.drawnframe >= setup(level).interval)
{
refresh.emplace_back(idx);
}
}
auto const refreshes{static_cast<std::size_t>(level >= 3 ? 4 : 2)};
if (refresh.size() > refreshes)
{
std::partial_sort(std::begin(refresh), std::begin(refresh) + refreshes, std::end(refresh),
[this, Framestamp](std::size_t const Left, std::size_t const Right) { return Framestamp - m_cabslots[Left].drawnframe > Framestamp - m_cabslots[Right].drawnframe; });
refresh.resize(refreshes);
}
m_cabdraws.insert(std::end(m_cabdraws), std::begin(refresh), std::end(refresh));
for (auto const idx : m_cabdraws)
{
m_cabslots[idx].drawnframe = Framestamp;
}
}
void local_shadows::begin(std::size_t const Slot)
@@ -277,15 +311,28 @@ int local_shadows::cab_slot_of(TDynamicObject const *Vehicle, gfx::local_lights:
{
return -1;
}
auto closest{-1};
auto closestdistance{CAB_SHARE_DISTANCE};
for (auto const idx : m_cabwanted)
{
auto const &slot{m_cabslots[idx]};
if ((slot.part == Light.source.part) && slot.valid && (slot.lamp == Light.local))
if (false == slot.valid)
{
continue;
}
if ((slot.part == Light.source.part) && (slot.lamp == Light.local))
{
return static_cast<int>(idx);
}
// a light without a map of its own takes the one closest to it
auto const distance{glm::length(slot.lamp - Light.local)};
if (Light.source.cast_shadows && (distance <= closestdistance))
{
closest = static_cast<int>(idx);
closestdistance = distance;
}
}
return -1;
return closest;
}
glm::vec4 local_shadows::weights(std::size_t const Slot) const

View File

@@ -31,8 +31,9 @@ namespace gfx
// - the lights of the occupied cab (gfx::local_lights::cab_lights(): its lamps, the lights of its desk), the brightest
// Global.gfx_lights_shadows_cab of them, with the shadows cast by the cab (the model drawn by Render_cab()). the map of a light is
// kept in the space of the cab model, centred on the light: it stays right while the vehicle moves and turns, and is drawn anew only
// every few frames (for the moving parts of the cab: levers, doors, wipers) or when the light, the cab or the level change. a light
// keeps its map (slot) while it's among those picked;
// every few frames (for the moving parts of the cab: levers, doors, wipers; a few maps per frame) or when the light, the cab or the
// level change. a light keeps its map (slot) while it's among those picked; the other lights of the cab take the map of the
// closest light with one. the near plane of these maps is a few centimetres: the desk right under its lights holds their light back;
// - the lamps of scenery models closest to the camera (Global.gfx_lights_shadows_lamps of them, within
// Global.gfx_lights_shadows_lamps_range). the map of such a lamp is kept in world axes, centred on the lamp, and has two parts:
// the scenery (drawn once, when the lamp gets its slot or the scene changes) and a working copy of it, with the vehicles near
@@ -54,7 +55,7 @@ class local_shadows
// near plane of the maps, metres. surfaces closer to the lamp (its own fitting) cast no shadow
static constexpr float shadow_near{0.15f};
// lights of the cab with shadow maps, at most
static constexpr int cab_slots_max{8};
static constexpr int cab_slots_max{16};
local_shadows() = default;
virtual ~local_shadows() = default;
@@ -92,7 +93,8 @@ class local_shadows
void begin(std::size_t const Slot);
// ends the drawing to the map
void end(std::uint32_t const Drawcalls);
// the slot of the light of the cab of Vehicle with its shadows ready, -1 if it has none
// the slot of the light of the cab of Vehicle with its shadows ready, -1 if it has none. a light without a map of its own (past
// the number of maps, or too weak for one) takes the map of the closest light within a metre, whose shadows are close to its own
int cab_slot_of(TDynamicObject const *Vehicle, gfx::local_lights::cab_light const &Light) const;
// the headlight_weights of the light which reads the map of the slot: layer, near plane, size of a texel one metre from the lamp,
// radius of the filter in texels

View File

@@ -1975,7 +1975,7 @@ global_settings::ConfigParse_gfx_sky( cParser &Parser, std::string_view const To
else if( Token == "gfx.lights.shadows.cab" ) {
Parser.getTokens( 1 );
Parser >> gfx_lights_shadows_cab;
gfx_lights_shadows_cab = std::clamp( gfx_lights_shadows_cab, 0, 8 ); }
gfx_lights_shadows_cab = std::clamp( gfx_lights_shadows_cab, 0, 16 ); }
else if( Token == "gfx.lights.shadows.lamps" ) {
Parser.getTokens( 1 );
Parser >> gfx_lights_shadows_lamps;

View File

@@ -539,8 +539,8 @@ struct global_settings {
// occupied cab, its shadows cast by the cab
int gfx_lights_shadows { 2 };
// with gfx_lights_shadows above 0: how many lights of the occupied cab (its lamps, the lights of its desk) at most have shadows
// cast by the cab, the brightest ones (0-8)
int gfx_lights_shadows_cab { 4 };
// cast by the cab, the brightest ones (0-16); the others take the shadows of the map of the closest light with one
int gfx_lights_shadows_cab { 8 };
// shadows of the lamps of scenery models (cube maps of the scenery around them, kept; vehicles added as they pass), with
// gfx_lights_shadows above 0: how many lamps at once, the size of a face of their maps, the distance from the camera within
// which lamps get shadows and within which vehicles (and the cab) cast them