mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-08 23:07:38 +01:00
Shade the sky and the sun by the scenery around the camera instead of the track environment flags
This commit is contained in:
@@ -17,6 +17,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include <limits>
|
||||
|
||||
#include "utilities/Globals.h"
|
||||
#include "rendering/common/skyocclusion.h"
|
||||
#include "utilities/utilities.h"
|
||||
#include "scene/scene.h"
|
||||
#include "model/AnimModel.h"
|
||||
@@ -320,7 +321,7 @@ void gfx::local_lights::gather_cab_lamp(gfx::geometrybank_manager const &Geometr
|
||||
return;
|
||||
}
|
||||
// as bright as the ambient light it used to add to the cab: lost in the daylight
|
||||
auto const luminance{Global.fLuminance * (vehicle->fShade > 0.0f ? vehicle->fShade : 1.0f)};
|
||||
auto const luminance{Global.fLuminance * gfx::vehicle_light_level(vehicle)};
|
||||
auto const level{vehicle->InteriorLightLevel * std::clamp(1.25f - static_cast<float>(luminance), 0.f, 1.f)};
|
||||
if (level <= 0.f)
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "utilities/utilities.h"
|
||||
#include "vehicle/DynObj.h"
|
||||
#include "vehicle/Train.h"
|
||||
#include "rendering/common/skyocclusion.h"
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
@@ -409,9 +410,7 @@ std::vector<light_array::light_record const *> const &local_shadows::pick_headli
|
||||
if (light.intensity == 0.f)
|
||||
continue;
|
||||
// as in the light buffers: by day the light around takes the beam down to nothing
|
||||
auto luminance{static_cast<float>(Global.fLuminance)};
|
||||
if (light.owner->fShade > 0.f)
|
||||
luminance *= light.owner->fShade;
|
||||
auto const luminance{static_cast<float>(Global.fLuminance) * gfx::vehicle_light_level(light.owner)};
|
||||
auto const beam{light.color - glm::vec3{luminance}};
|
||||
if (std::max(beam.r, std::max(beam.g, beam.b)) <= 0.f)
|
||||
continue;
|
||||
|
||||
@@ -26,10 +26,23 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "world/TrackSleepers.h"
|
||||
#include "model/AnimModel.h"
|
||||
#include "model/Model3d.h"
|
||||
#include "rendering/common/skyocclusion.h"
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// the environment of a track the drawing takes into its light: the flag of the track (a tunnel, a canyon) only with
|
||||
// Global.gfx_tunnelflags. otherwise the light is the one of the geometry around it (gfx::sky_occlusion)
|
||||
TEnvironmentType track_environment(TTrack const *Track)
|
||||
{
|
||||
return (Global.gfx_tunnelflags ? Track->eEnvironment : e_flat);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// the top view of the editor shows everything this close to the camera, the corners of the screen included
|
||||
float scene_walk::editor_ortho_reach()
|
||||
{
|
||||
@@ -150,13 +163,11 @@ bool scene_walk::Render_lowpoly( TDynamicObject *Dynamic, float const Squaredist
|
||||
* ( std::max(
|
||||
0.3,
|
||||
( Global.fLuminance - Global.Overcast )
|
||||
* ( Dynamic->fShade > 0.0 ?
|
||||
Dynamic->fShade :
|
||||
1.0 ) ) ) ) };
|
||||
* gfx::vehicle_light_level( Dynamic ) ) ) ) };
|
||||
walk_sunlight(
|
||||
std::clamp( (
|
||||
Dynamic->fShade > 0.f ?
|
||||
Dynamic->fShade :
|
||||
gfx::vehicle_shade( Dynamic ) > 0.f ?
|
||||
gfx::vehicle_shade( Dynamic ) :
|
||||
1.f )
|
||||
- luminance,
|
||||
0.f, 1.f ) );
|
||||
@@ -166,7 +177,7 @@ bool scene_walk::Render_lowpoly( TDynamicObject *Dynamic, float const Squaredist
|
||||
// renderowanie nieprzezroczystego ładunku
|
||||
Render( Dynamic->mdLoad, Dynamic->Material(), Squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} );
|
||||
}
|
||||
walk_sunlight( Dynamic->fShade > 0.f ? Dynamic->fShade : 1.f );
|
||||
walk_sunlight( gfx::vehicle_shade( Dynamic ) > 0.f ? gfx::vehicle_shade( Dynamic ) : 1.f );
|
||||
}
|
||||
else {
|
||||
// Render_Alpha( Dynamic->mdLowPolyInt, Dynamic->Material(), Squaredistance );
|
||||
@@ -179,7 +190,7 @@ bool scene_walk::Render_lowpoly( TDynamicObject *Dynamic, float const Squaredist
|
||||
|
||||
if( Setup ) {
|
||||
|
||||
if( Dynamic->fShade > 0.0f ) {
|
||||
if( gfx::vehicle_shade( Dynamic ) > 0.0f ) {
|
||||
// restore regular light level
|
||||
walk_sunlight();
|
||||
}
|
||||
@@ -1659,10 +1670,10 @@ bool scene_walk::Render(TDynamicObject *Dynamic)
|
||||
case rendermode::color:
|
||||
case rendermode::reflections:
|
||||
{
|
||||
if (Dynamic->fShade > 0.0f)
|
||||
if (gfx::vehicle_shade(Dynamic) > 0.0f)
|
||||
{
|
||||
// change light level based on light level of the occupied track
|
||||
walk_sunlight(Dynamic->fShade);
|
||||
walk_sunlight(gfx::vehicle_shade(Dynamic));
|
||||
}
|
||||
|
||||
// render
|
||||
@@ -1690,7 +1701,7 @@ bool scene_walk::Render(TDynamicObject *Dynamic)
|
||||
Render_coupler_adapter( Dynamic, squaredistance, end::rear );
|
||||
|
||||
// post-render cleanup
|
||||
if (Dynamic->fShade > 0.0f)
|
||||
if (gfx::vehicle_shade(Dynamic) > 0.0f)
|
||||
{
|
||||
// restore regular light level
|
||||
walk_sunlight();
|
||||
@@ -1782,14 +1793,14 @@ bool scene_walk::Render_cab(TDynamicObject const *Dynamic, float const Lightleve
|
||||
case rendermode::color:
|
||||
{
|
||||
// render path specific setup:
|
||||
if (Dynamic->fShade > 0.0f)
|
||||
if (gfx::vehicle_shade(Dynamic) > 0.0f)
|
||||
{
|
||||
// change light level based on light level of the occupied track
|
||||
walk_sunlight(Dynamic->fShade);
|
||||
walk_sunlight(gfx::vehicle_shade(Dynamic));
|
||||
}
|
||||
|
||||
walk_ambient_save();
|
||||
auto const luminance { Global.fLuminance * ( Dynamic->fShade > 0.0f ? Dynamic->fShade : 1.0f ) };
|
||||
auto const luminance { Global.fLuminance * gfx::vehicle_light_level( Dynamic ) };
|
||||
if( Lightlevel > 0.f ) {
|
||||
// crude way to light the cabin, until we have something more complete in place. with the lamp of the cab among the
|
||||
// lights a share of it is kept for the light which bounces around the cab, unless the probe of the cab gives that light
|
||||
@@ -2174,11 +2185,11 @@ void scene_walk::Render(scene::basic_cell::path_sequence::const_iterator First,
|
||||
case rendermode::color:
|
||||
case rendermode::reflections:
|
||||
{
|
||||
if (track->eEnvironment != e_flat)
|
||||
if (track_environment(track) != e_flat)
|
||||
{
|
||||
// track with light level of its own is drawn on its own
|
||||
flush_batch();
|
||||
walk_environment(track->eEnvironment);
|
||||
walk_environment(track_environment(track));
|
||||
walk_material(track->m_blendmaterial1 != null_handle ? track->m_blendmaterial1 : track->m_material1);
|
||||
walk_draw(std::begin(track->Geometry1), std::end(track->Geometry1));
|
||||
// restore default lighting
|
||||
@@ -2244,10 +2255,10 @@ void scene_walk::Render(scene::basic_cell::path_sequence::const_iterator First,
|
||||
case rendermode::color:
|
||||
case rendermode::reflections:
|
||||
{
|
||||
if (track->eEnvironment != e_flat)
|
||||
if (track_environment(track) != e_flat)
|
||||
{
|
||||
flush_batch();
|
||||
walk_environment(track->eEnvironment);
|
||||
walk_environment(track_environment(track));
|
||||
walk_material(track->m_blendmaterial2 != null_handle ? track->m_blendmaterial2 : track->m_material2);
|
||||
walk_draw(std::begin(track->Geometry2), std::end(track->Geometry2));
|
||||
// restore default lighting
|
||||
@@ -2311,10 +2322,10 @@ void scene_walk::Render(scene::basic_cell::path_sequence::const_iterator First,
|
||||
case rendermode::color:
|
||||
case rendermode::reflections:
|
||||
{
|
||||
if (track->eEnvironment != e_flat)
|
||||
if (track_environment(track) != e_flat)
|
||||
{
|
||||
flush_batch();
|
||||
walk_environment(track->eEnvironment);
|
||||
walk_environment(track_environment(track));
|
||||
walk_material(track->m_blendmaterial3 != null_handle ? track->m_blendmaterial3 : track->SwitchExtension->m_material3);
|
||||
walk_draw(track->SwitchExtension->Geometry3);
|
||||
// restore default lighting
|
||||
@@ -2373,10 +2384,10 @@ void scene_walk::Render(scene::basic_cell::path_sequence::const_iterator First,
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (track->eEnvironment != e_flat)
|
||||
if (track_environment(track) != e_flat)
|
||||
{
|
||||
flush_batch();
|
||||
walk_environment(track->eEnvironment);
|
||||
walk_environment(track_environment(track));
|
||||
walk_material(cuts.first);
|
||||
walk_draw(std::begin(*cuts.second), std::end(*cuts.second));
|
||||
walk_environment();
|
||||
@@ -2762,10 +2773,10 @@ bool scene_walk::Render_Alpha(TDynamicObject *Dynamic)
|
||||
::glTranslated(originoffset.x, originoffset.y, originoffset.z);
|
||||
::glMultMatrixd(glm::value_ptr(Dynamic->mMatrix));
|
||||
|
||||
if (Dynamic->fShade > 0.0f)
|
||||
if (gfx::vehicle_shade(Dynamic) > 0.0f)
|
||||
{
|
||||
// change light level based on light level of the occupied track
|
||||
walk_sunlight(Dynamic->fShade);
|
||||
walk_sunlight(gfx::vehicle_shade(Dynamic));
|
||||
}
|
||||
|
||||
// render
|
||||
@@ -2790,7 +2801,7 @@ bool scene_walk::Render_Alpha(TDynamicObject *Dynamic)
|
||||
}
|
||||
|
||||
// post-render cleanup
|
||||
if (Dynamic->fShade > 0.0f)
|
||||
if (gfx::vehicle_shade(Dynamic) > 0.0f)
|
||||
{
|
||||
// restore regular light level
|
||||
walk_sunlight();
|
||||
|
||||
1314
rendering/common/skyocclusion.cpp
Normal file
1314
rendering/common/skyocclusion.cpp
Normal file
File diff suppressed because it is too large
Load Diff
244
rendering/common/skyocclusion.h
Normal file
244
rendering/common/skyocclusion.h
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "gl/ubo.h"
|
||||
|
||||
class TDynamicObject;
|
||||
class TModel3d;
|
||||
class TSubModel;
|
||||
|
||||
namespace scene
|
||||
{
|
||||
class basic_region;
|
||||
}
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
// the occlusion of the sky and of the sun by the scenery around the camera (Global.gfx_skyvisibility, the track T of
|
||||
// claude/renderers-plan.md), the part shared by the renderers; the backend (opengl46_sky_occlusion, vk14::sky_occlusion_maps) keeps
|
||||
// two textures the shading reads and copies to them what this part works out, through the gpu_* methods.
|
||||
// tunnels, the ground under bridges and roofs, cuttings and the foot of slopes are dark because the geometry around holds back the
|
||||
// sky and the sun, not because of the environment flags of the tracks (tunnel, canyon), which the renderers leave alone now
|
||||
// (Global.gfx_tunnelflags brings them back). the maps cover a square around the camera, in tiles of the world; a tile is worked out
|
||||
// once when it comes into the square and kept while it stays there (the scenery doesn't change, the editor and the streamed terrain
|
||||
// are noticed by a signature of what the tile holds):
|
||||
// - the main thread gathers the triangles of the static opaque scenery over a tile: the shapes (the terrain, the heightmap
|
||||
// terrain, the roads and the platforms) and the opaque submodels of the models at their nearest level of detail; not the
|
||||
// vehicles, the tracks, the traction, the lines, nor what is see-through (the trees);
|
||||
// - a thread of its own lays them out seen from above: the height of the highest surface over each texel (the top) and of the
|
||||
// lowest one facing down (the ceiling: the vault of a tunnel, the deck of a bridge seen from below, a roof); things narrower than
|
||||
// a few texels (poles, masts, fences) are taken out of both by an opening (an erosion and a dilation);
|
||||
// - and works out for each texel, from the heights around it: the share of the sky a surface facing up sees (the irradiance
|
||||
// weighed by the cosine) above the top, and under the ceiling, where there's one, a little below it (light comes in under the
|
||||
// vault along the tunnel from the portal); and the heights between which the key light (the sun, the moon at night) is let
|
||||
// through: above the top it lights what is higher than a height worked out by marching towards it, under the ceiling what is
|
||||
// lower than one;
|
||||
// - the shading (light_common.glsl, sky_occlusion()) reads the height of the top (GL: SKY_OCCLUSION_HEIGHT_TEX, R32F) and the four
|
||||
// values above (SKY_OCCLUSION_TEX, RGBA16F) where the fragment is, decides from its height whether it's under the top, and holds
|
||||
// back the ambient light, the reflection of the sky and the key light by them. the lamps, the headlights and the light of the cab
|
||||
// are direct light and are left as they are;
|
||||
// - the light around a vehicle (the headlights fade by day, the light of its interior, the cab lamp) is taken from the same maps
|
||||
// on the cpu (vehicle_light_level()), with no delay.
|
||||
// the maps are toroidal: the texel of a world position is its world texel index modulo the size of the map, so a tile which comes
|
||||
// into the square takes the place of the one which left it on the other side and nothing is moved
|
||||
class sky_occlusion
|
||||
{
|
||||
public:
|
||||
sky_occlusion() = default;
|
||||
virtual ~sky_occlusion();
|
||||
sky_occlusion(const sky_occlusion &) = delete;
|
||||
sky_occlusion &operator=(const sky_occlusion &) = delete;
|
||||
|
||||
// the layout of a level (Global.gfx_skyvisibility 1..3)
|
||||
struct layout
|
||||
{
|
||||
float tile{64.f}; // side of a tile, metres
|
||||
int tile_texels{64}; // texels across a tile
|
||||
int tiles{8}; // tiles across the map
|
||||
int azimuths{12}; // directions the sky is searched in from a texel
|
||||
int steps{10}; // samples along each direction
|
||||
float reach{48.f}; // how far, metres
|
||||
int sun_steps{16}; // samples towards the key light
|
||||
float sun_reach{128.f}; // how far, metres
|
||||
float texel() const
|
||||
{
|
||||
return tile / static_cast<float>(tile_texels);
|
||||
}
|
||||
int side() const
|
||||
{
|
||||
return tile_texels * tiles;
|
||||
}
|
||||
};
|
||||
static layout layout_of(int const Level);
|
||||
|
||||
// the frame: the square follows the camera (world space), tiles come in and go out, what the thread worked out goes to the
|
||||
// textures. Keylight: the direction towards the key light (the sun, the moon at night), world space; it is followed when it
|
||||
// moved enough. called by the renderer once a frame, before the passes which read the maps, with the region of the scenery
|
||||
void update(scene::basic_region *Region, glm::dvec3 const &Camera, glm::vec3 const &Keylight);
|
||||
// the fields of the block of the scene the shading reads the maps with, for a pass seen from Camera (world space); zero when
|
||||
// there are no maps
|
||||
void setup_scene(gl::scene_ubs &Scene, glm::dvec3 const &Camera) const;
|
||||
static void clear_scene(gl::scene_ubs &Scene);
|
||||
// the share of the sky (x) and of the key light (y) a point of the world gets, 0..1, as the shading works them out; 1 where
|
||||
// there are no maps
|
||||
glm::vec2 sample(glm::dvec3 const &Position) const;
|
||||
// whether the maps are there
|
||||
bool active() const
|
||||
{
|
||||
return m_side > 0;
|
||||
}
|
||||
// the maps of the renderer in use, nullptr when there are none
|
||||
static sky_occlusion const *current();
|
||||
// a scenery is being loaded or let go: the tiles and the signatures go
|
||||
void clear();
|
||||
// line for the statistics of the debug panel
|
||||
std::string info() const;
|
||||
// controls, counters and a view of the maps for the debug window
|
||||
void draw_debug_ui();
|
||||
|
||||
protected:
|
||||
// the textures of Side x Side texels: the height of the top (one 32 bit float per texel) and the occlusion (four 16 bit floats);
|
||||
// false when they can't be made
|
||||
virtual bool gpu_allocate(int const Side) = 0;
|
||||
virtual void gpu_release() = 0;
|
||||
// copies a square of Size x Size texels at X, Y of the textures: Heights one float per texel, Occlusion four halves per texel,
|
||||
// rows along x, one after another along z
|
||||
virtual void gpu_upload(int const X, int const Y, int const Size, float const *Heights, std::uint16_t const *Occlusion) = 0;
|
||||
|
||||
int m_side{0};
|
||||
|
||||
private:
|
||||
// the triangle of the gathered scenery, x and z relative to the corner of its tile, y in the world; Down: it faces down
|
||||
struct triangle
|
||||
{
|
||||
glm::vec3 a, b, c;
|
||||
bool down;
|
||||
};
|
||||
// what the thread is asked to do
|
||||
struct raster_job
|
||||
{
|
||||
int slot{0};
|
||||
std::uint32_t generation{0};
|
||||
std::vector<triangle> triangles;
|
||||
};
|
||||
// what it gives back: the top and the occlusion of the texels of a tile
|
||||
struct tile_result
|
||||
{
|
||||
int slot{0};
|
||||
std::uint32_t generation{0};
|
||||
std::vector<float> heights;
|
||||
std::vector<std::uint16_t> occlusion;
|
||||
double milliseconds{0.0};
|
||||
};
|
||||
enum class slot_state
|
||||
{
|
||||
empty, // nothing asked yet: neutral values
|
||||
queued, // the triangles went to the thread
|
||||
ready // the thread gave its result
|
||||
};
|
||||
struct slot
|
||||
{
|
||||
glm::ivec2 tile{std::numeric_limits<int>::min()}; // world tile index
|
||||
std::uint32_t generation{0};
|
||||
slot_state state{slot_state::empty};
|
||||
std::uint64_t signature{0};
|
||||
bool regather{false}; // the scenery of the tile changed: gathered again, the old result kept until the new one comes
|
||||
};
|
||||
|
||||
void start(int const Level);
|
||||
void stop();
|
||||
// the slot of a world tile
|
||||
int slot_of(glm::ivec2 const &Tile) const;
|
||||
// the triangles of the scenery over a tile
|
||||
void gather(scene::basic_region *Region, glm::ivec2 const &Tile, std::vector<triangle> &Triangles);
|
||||
void gather_model(TModel3d *Model, std::uint32_t const Alpha, glm::dmat4 const &Transform, glm::dvec2 const &Corner, glm::dvec2 const &Far, std::vector<triangle> &Triangles);
|
||||
void gather_submodel(TSubModel *Submodel, std::uint32_t const Alpha, glm::dmat4 const &Transform, glm::dvec2 const &Corner, glm::dvec2 const &Far, std::vector<triangle> &Triangles);
|
||||
// what the scenery over a tile is made of, cheap to work out: a change of it means the tile is gathered again
|
||||
std::uint64_t signature(scene::basic_region *Region, glm::ivec2 const &Tile);
|
||||
// the values of a tile nothing is known of yet, in the textures and in the copy of the cpu
|
||||
void neutral(int const Slot);
|
||||
void apply(tile_result &Result);
|
||||
|
||||
// the thread
|
||||
void work();
|
||||
void rasterize(raster_job &Job);
|
||||
void filter(int const Slot);
|
||||
void occlude(int const Slot, tile_result &Result);
|
||||
bool worker_slot_valid(glm::ivec2 const &Tile) const;
|
||||
|
||||
layout m_layout;
|
||||
int m_level{0};
|
||||
bool m_sun{true};
|
||||
std::vector<slot> m_slots;
|
||||
glm::ivec2 m_cameratile{0};
|
||||
glm::dvec3 m_camera{0.0};
|
||||
glm::vec3 m_keylight{0.f, 1.f, 0.f}; // the direction the thread was given last
|
||||
// the copy of the maps the cpu samples (sample()): the top, and the occlusion as floats
|
||||
std::vector<float> m_heights;
|
||||
std::vector<glm::vec4> m_occlusion;
|
||||
// neutral tiles, uploaded when a tile comes into the square
|
||||
std::vector<float> m_neutralheights;
|
||||
std::vector<std::uint16_t> m_neutralocclusion;
|
||||
std::size_t m_checkslot{0}; // the slot whose signature is checked next
|
||||
scene::basic_region const *m_region{nullptr};
|
||||
|
||||
// shared with the thread, under m_mutex
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_wake;
|
||||
bool m_quit{false};
|
||||
std::deque<raster_job> m_jobs;
|
||||
std::deque<tile_result> m_results;
|
||||
std::vector<glm::ivec2> m_workertiles; // the world tile of each slot, as the thread knows it
|
||||
std::vector<std::uint32_t> m_workergenerations;
|
||||
std::vector<bool> m_workerraster; // the slot holds the rasterized scenery of its tile
|
||||
std::vector<bool> m_workerdirty; // the occlusion of the slot is to be worked out again
|
||||
glm::ivec2 m_workercamera{0};
|
||||
glm::vec3 m_workerkeylight{0.f, 1.f, 0.f};
|
||||
bool m_workersun{true};
|
||||
std::thread m_thread;
|
||||
// the maps of the thread: the top and the ceiling as rasterized, and opened
|
||||
std::vector<float> m_rawtop, m_rawceiling, m_top, m_ceiling;
|
||||
|
||||
// statistics
|
||||
std::uint32_t m_gathered{0};
|
||||
std::uint32_t m_triangles{0};
|
||||
std::uint32_t m_regathered{0};
|
||||
std::uint32_t m_uploaded{0};
|
||||
double m_gathertime{0.0};
|
||||
double m_workertime{0.0}; // under m_mutex
|
||||
std::uint32_t m_workertiles_done{0}; // under m_mutex
|
||||
int m_pending{0};
|
||||
std::uint64_t m_previewimage{0};
|
||||
int m_previewmode{0};
|
||||
std::vector<std::uint8_t> m_previewrgba;
|
||||
};
|
||||
|
||||
// the strength the shading of a vehicle gives the sun and the ambient light (walk_sunlight()), from the environment flags of the
|
||||
// tracks under it: only with Global.gfx_tunnelflags; 0 otherwise (the light isn't changed)
|
||||
float vehicle_shade(TDynamicObject const *Vehicle);
|
||||
// the light around a vehicle, 0 (none: the depth of a tunnel) .. 1 (the open sky), which fades its headlights by day and the
|
||||
// lights of its interior: with Global.gfx_tunnelflags from the environment flags of the tracks under it (TDynamicObject::fShade),
|
||||
// otherwise from the maps of the occlusion around the camera (1 without them, or away from the camera)
|
||||
float vehicle_light_level(TDynamicObject const *Vehicle);
|
||||
|
||||
} // namespace gfx
|
||||
Reference in New Issue
Block a user