17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-06 02:18:35 +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:
2026-10-27 16:02:36 +01:00
parent 9dde5b0e5e
commit 2a876befb0
35 changed files with 2351 additions and 55 deletions

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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();

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -218,6 +218,7 @@ bool opengl46_renderer::Init(GLFWwindow *Window)
m_ao.init();
m_localshadows.init();
m_cabprobe.init();
m_skyocclusion.init();
}
catch (gl::shader_exception const &e)
{
@@ -684,11 +685,15 @@ bool opengl46_renderer::Render()
// lamps of models and vehicles around the camera and the cab lamp, for the views of the frame to take into their light buffers
if ((false == Global.gfx_skiprendering) && (simulation::Region != nullptr))
m_locallights.gather(Global.pCamera.Pos, *simulation::Region, m_geometry, m_framestamp);
// the occlusion of the sky by the scenery around the camera: the tiles which came in, what the background worked out
if (false == Global.gfx_skiprendering)
m_skyocclusion.update(simulation::Region, Global.pCamera.Pos, -m_sunlight.direction);
}
else
{
// a scenery is being loaded: the cells, instances and models the lights were taken from may be gone
m_locallights.reset();
m_skyocclusion.clear();
m_localshadows.reset_lamps();
m_localshadows.reset_headlights();
// and so may the tracks the sleeper records were made for, and the models drawn on their own
@@ -1055,6 +1060,7 @@ void opengl46_renderer::SwapBuffers()
", off in this view\n" ) :
std::string{} )
+ ( Global.gfx_cabprobe > 0 ? " " + m_cabprobe.info() + "\n" : std::string{} )
+ ( Global.gfx_skyvisibility > 0 ? " " + m_skyocclusion.info() + "\n" : std::string{} )
+ shadowcascadestext
+ " instance buffer: " + std::to_string( m_instancestream->used() / static_cast<GLsizeiptr>( sizeof( glm::mat4 ) ) ) + " matrices, "
+ std::to_string( m_instancestream->size() / 1024 ) + " KiB\n"
@@ -1471,6 +1477,8 @@ void opengl46_renderer::draw_debug_ui()
m_ao.draw_debug_ui(!m_viewports.empty() && m_viewports.front()->ssao_tex != nullptr);
m_cabprobe.draw_debug_ui();
ImGui::Separator();
m_skyocclusion.draw_debug_ui();
ImGui::Separator();
m_ssr.draw_debug_ui(nullptr);
}
ImGui::End();
@@ -1655,6 +1663,8 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
// pass of the main view only
scene_ubs.env_params = glm::vec4{Global.gfx_ambient_groundalbedo, 0.f, 0.f, 0.f};
gfx::screen_reflections::clear(scene_ubs);
// the occlusion of the sky by the scenery, seen from the camera of the view
m_skyocclusion.setup_scene(scene_ubs, m_colorpass.pass_camera.position());
scene_ubs.projection = OpenGLMatrices.data(GL_PROJECTION);
scene_ubs.inv_view = glm::inverse( glm::mat4{ glm::mat3{ m_colorpass.pass_camera.modelview() } } );
scene_ubo->update(scene_ubs);
@@ -1675,6 +1685,8 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
m_localshadows.bind();
// the probe of the cab, or the stand-ins
m_cabprobe.bind();
// the maps of the occlusion of the sky, or the stand-ins
m_skyocclusion.bind();
if (Global.gfx_shadowmap_enabled && vp.main)
{
@@ -5730,6 +5742,8 @@ void opengl46_renderer::Preview_Color(TAnimModel *Instance, float const Squaredi
scene_ubs.cascade_end[idx] = std::numeric_limits<float>::max();
}
scene_ubs.shadow_fit = glm::vec4{0.f};
// the model stands on its own, nothing around it
gfx::sky_occlusion::clear_scene(scene_ubs);
scene_ubo->update(scene_ubs);
scene_ubo->bind_uniform();
// the sun is the only light, and there's no fog
@@ -6113,13 +6127,8 @@ void opengl46_renderer::Update_Lights(light_array &Lights, bool const Headlights
renderlight.position = lightoffset;
renderlight.direction = scenelight.direction;
auto luminance = static_cast<float>(Global.fLuminance);
// adjust luminance level based on vehicle's location, e.g. tunnels
auto const environment = scenelight.owner->fShade;
if (environment > 0.f)
{
luminance *= environment;
}
auto const luminance = static_cast<float>(Global.fLuminance) * gfx::vehicle_light_level(scenelight.owner);
renderlight.diffuse = glm::vec4{glm::max(glm::vec3{colors::none}, scenelight.color - glm::vec3{luminance}), renderlight.diffuse[3]};
renderlight.ambient = glm::vec4{glm::max(glm::vec3{colors::none}, scenelight.color * glm::vec3{scenelight.intensity} - glm::vec3{luminance}), renderlight.ambient[3]};

View File

@@ -44,6 +44,7 @@ http://mozilla.org/MPL/2.0/.
#include "rendering/common/locallights.h"
#include "rendering/opengl46/opengl46localshadows.h"
#include "rendering/opengl46/opengl46cabprobe.h"
#include "rendering/opengl46/opengl46skyocclusion.h"
#include "rendering/opengl46/opengl46instancecull.h"
#include "rendering/opengl46/opengl46hiz.h"
#include "rendering/opengl46/opengl46shadowfit.h"
@@ -783,6 +784,8 @@ class opengl46_renderer : public gfx_renderer, public gfx::scene_walk {
// the probe of the occupied cab, and the colour mode draws the cab into it with its programs (Render_cab_probe())
opengl46_cab_probe m_cabprobe;
bool m_cabprobepass = false;
// the occlusion of the sky and of the sun by the scenery around the camera (tunnels, bridges, roofs: the track T)
opengl46_sky_occlusion m_skyocclusion;
double m_precipitationrotation;

View File

@@ -0,0 +1,97 @@
/*
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/.
*/
#include "stdafx.h"
#include "rendering/opengl46/opengl46skyocclusion.h"
#include "gl/ubo.h"
namespace
{
// a toroidal map read texel by texel (textureGather): nearest, repeated
GLuint make_map(GLenum const Format, int const Side)
{
GLuint texture{0};
glCreateTextures(GL_TEXTURE_2D, 1, &texture);
glTextureStorage2D(texture, 1, Format, Side, Side);
glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_REPEAT);
return texture;
}
} // namespace
opengl46_sky_occlusion::~opengl46_sky_occlusion()
{
gpu_release();
if (m_emptyheights != 0)
glDeleteTextures(1, &m_emptyheights);
if (m_emptyocclusion != 0)
glDeleteTextures(1, &m_emptyocclusion);
}
void opengl46_sky_occlusion::init()
{
// a sampler with no texture to read from is undefined, also where the shading doesn't read it
m_emptyheights = make_map(GL_R32F, 1);
float const none{-1.0e4f};
glClearTexImage(m_emptyheights, 0, GL_RED, GL_FLOAT, &none);
m_emptyocclusion = make_map(GL_RGBA16F, 1);
float const open[4]{1.f, 1.f, -1000.f, 1000.f};
glClearTexImage(m_emptyocclusion, 0, GL_RGBA, GL_FLOAT, open);
bind();
}
void opengl46_sky_occlusion::bind() const
{
glBindTextureUnit(gl::SKY_OCCLUSION_HEIGHT_TEX, (m_heights != 0) ? m_heights : m_emptyheights);
glBindTextureUnit(gl::SKY_OCCLUSION_TEX, (m_occlusion != 0) ? m_occlusion : m_emptyocclusion);
}
bool opengl46_sky_occlusion::gpu_allocate(int const Side)
{
gpu_release();
m_heights = make_map(GL_R32F, Side);
m_occlusion = make_map(GL_RGBA16F, Side);
if ((m_heights == 0) || (m_occlusion == 0))
return false;
bind();
return true;
}
void opengl46_sky_occlusion::gpu_release()
{
if (m_heights != 0)
glDeleteTextures(1, &m_heights);
if (m_occlusion != 0)
glDeleteTextures(1, &m_occlusion);
m_heights = 0;
m_occlusion = 0;
if (m_emptyheights != 0)
bind();
}
void opengl46_sky_occlusion::gpu_upload(int const X, int const Y, int const Size, float const *Heights, std::uint16_t const *Occlusion)
{
if ((m_heights == 0) || (m_occlusion == 0))
return;
// from the memory of the process: the buffer the texture streaming may have bound and the alignment are put back after
GLint unpack{0}, alignment{4};
glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &unpack);
glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTextureSubImage2D(m_heights, 0, X, Y, Size, Size, GL_RED, GL_FLOAT, Heights);
glTextureSubImage2D(m_occlusion, 0, X, Y, Size, Size, GL_RGBA, GL_HALF_FLOAT, Occlusion);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, static_cast<GLuint>(unpack));
glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
}

View File

@@ -0,0 +1,37 @@
/*
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 "rendering/common/skyocclusion.h"
// the maps of the occlusion of the sky by the scenery (gfx::sky_occlusion) for the opengl 4.6 renderer: two textures bound to their
// units (gl::SKY_OCCLUSION_HEIGHT_TEX, gl::SKY_OCCLUSION_TEX) for every pass, stand-ins of one texel while there are none
class opengl46_sky_occlusion : public gfx::sky_occlusion
{
public:
opengl46_sky_occlusion() = default;
~opengl46_sky_occlusion() override;
// the stand-ins
void init();
// the textures (or the stand-ins) bound to their units
void bind() const;
protected:
bool gpu_allocate(int const Side) override;
void gpu_release() override;
void gpu_upload(int const X, int const Y, int const Size, float const *Heights, std::uint16_t const *Occlusion) override;
private:
GLuint m_heights{0};
GLuint m_occlusion{0};
GLuint m_emptyheights{0};
GLuint m_emptyocclusion{0};
};

View File

@@ -80,7 +80,11 @@ constexpr std::uint32_t SCENE_AO_TEX = 27;
constexpr std::uint32_t CAB_PROBE_TEX = 28;
constexpr std::uint32_t CAB_PROBE_ALBEDO_TEX = 29;
constexpr std::uint32_t CAB_PROBE_SSBO = 30;
constexpr std::uint32_t PASS_BINDING_COUNT = 31;
// the occlusion of the sky and of the sun by the scenery around the camera (gfx::sky_occlusion, the track T): the height of the top of
// the scenery and the shares of the sky and the limits of the key light, read texel by texel
constexpr std::uint32_t SKY_OCCLUSION_HEIGHT_TEX = 31;
constexpr std::uint32_t SKY_OCCLUSION_TEX = 32;
constexpr std::uint32_t PASS_BINDING_COUNT = 33;
// the offsets of the bindings above with dynamic offsets, in the order of the bindings
constexpr std::uint32_t PASS_DYNAMIC_OFFSETS = 6;
// the textures of a material take the bindings of their units (#pragma texture(name, unit, ...)), 0 .. MAX_TEXTURES - 1

View File

@@ -29,6 +29,7 @@ http://mozilla.org/MPL/2.0/.
#include "rendering/vulkan14/vulkan14cull.h"
#include "rendering/vulkan14/vulkan14shadows.h"
#include "rendering/vulkan14/vulkan14cabprobe.h"
#include "rendering/vulkan14/vulkan14skyocclusion.h"
#include "rendering/vulkan14/vulkan14terrain.h"
#include "rendering/vulkan14/vulkan14grass.h"
#include "rendering/vulkan14/vulkan14particles.h"
@@ -399,6 +400,9 @@ struct scene_state
compute_kernel cabprobe_sh;
buffer cabprobe_bounce;
std::shared_ptr<image> cabprobe_empty;
// the occlusion of the sky by the scenery around the camera (the track T): its maps, and their stand-ins while there are none
sky_occlusion_maps skyocclusion;
std::shared_ptr<image> skyocclusion_heights_empty, skyocclusion_empty;
// the materials of the heightmap terrain (the stage P6), made with the bank of the textures
std::unique_ptr<terrain_materials> terrain;
// the grass of the heightmap terrain, and its program, made when it's first drawn

View File

@@ -168,6 +168,10 @@ std::string vulkan14_renderer::scene_info()
{
text += " " + scene.cabprobe.info() + "\n";
}
if (Global.gfx_skyvisibility > 0)
{
text += " " + scene.skyocclusion.info() + "\n";
}
text += cascades + line(" submodels:", colour.submodels, shadowstats.submodels) + line(" paths: ", colour.paths, shadowstats.paths) +
line(" shapes: ", colour.shapes, shadowstats.shapes) + " traction: " + to_string(colour.traction, 7) + "\n" + " lines: " + to_string(colour.lines, 7) + "\n" +
"particles: " + to_string(colour.particles, 7) + "\nlights: " + to_string(static_cast<int>(m_lights_lit), 7) + " (" + std::to_string(m_lights_dropped) + " over capacity), " +
@@ -333,6 +337,8 @@ void vulkan14_renderer::draw_debug_ui()
}
}
ImGui::Separator();
scene.skyocclusion.draw_debug_ui();
ImGui::Separator();
std::string raytracing;
if (m_context->scene.raytracing)
{

View File

@@ -610,6 +610,7 @@ bool vulkan14_renderer::Render()
}
scene.lamps.begin_frame(ctx.commands, value);
scene.cabprobe.begin_frame(value);
scene.skyocclusion.begin_frame(value);
if (scene.terrain)
{
// textures of new heightmap terrain layers go to the arrays the terrain shader reads them from
@@ -680,8 +681,9 @@ bool vulkan14_renderer::Render()
if (false == scene.images_ready)
{
scene.images_ready = true;
std::vector<vk14::image *> const standins{scene.shadow_empty.get(), scene.env_empty.get(), scene.volume_empty.get(), scene.cube_shadow_empty.get(),
scene.headlight_shadow_empty.get(), scene.depth_empty.get(), scene.cabprobe_empty.get()};
std::vector<vk14::image *> const standins{scene.shadow_empty.get(), scene.env_empty.get(), scene.volume_empty.get(), scene.cube_shadow_empty.get(),
scene.headlight_shadow_empty.get(), scene.depth_empty.get(), scene.cabprobe_empty.get(), scene.skyocclusion_heights_empty.get(),
scene.skyocclusion_empty.get()};
std::vector<vk14::image_use> clears;
for (auto *image : standins)
{
@@ -769,11 +771,17 @@ bool vulkan14_renderer::Render()
{
m_locallights.gather(Global.pCamera.Pos, *simulation::Region, m_geometry, m_framestamp);
}
// the occlusion of the sky by the scenery around the camera: the tiles which came in, what the background worked out
if (false == Global.gfx_skiprendering)
{
scene.skyocclusion.update(simulation::Region, Global.pCamera.Pos, -m_sunlight.direction);
}
}
else
{
// a scenery is being loaded: the cells, instances and models the lights were taken from may be gone
m_locallights.reset();
scene.skyocclusion.clear();
scene.lamps.reset_lamps();
scene.lamps.reset_headlights();
// and so may the tracks the sleeper records were made for, and the models drawn on their own
@@ -791,6 +799,8 @@ bool vulkan14_renderer::Render()
++m_instancerecordsversion;
m_nodestateversion = scene::node_state_version;
}
// what the maps of the occlusion of the sky were given, copied to them
scene.skyocclusion.flush(ctx.commands);
// the sleeper records of tracks out of sight for a while, and the records of the models drawn on their own
Expire_Indirect_Records();
// scenery lamps which get shadows in this frame. their maps hold the scenery as of the version of the instance records
@@ -1547,6 +1557,8 @@ void vulkan14_renderer::render_viewport(std::size_t const Index)
// the clouds of the view and the fog volume, which take the lights of the clusters and the shadow map of the pass
{
auto const fogtarget{glm::ivec2{vp.width, vp.height}};
// the occlusion of the sky by the scenery, seen from the camera of the view
scene.skyocclusion.setup_scene(scene_ubs, m_colorpass.pass_camera.position());
scene_ubs.projection = OpenGLMatrices.data(GL_PROJECTION);
scene_ubs.inv_view = glm::inverse(glm::mat4{glm::mat3{m_colorpass.pass_camera.modelview()}});
setup_shadow_matrices();
@@ -3737,12 +3749,7 @@ void vulkan14_renderer::update_lights(light_array &Lights, bool const Headlights
}
renderlight.position = lightoffset;
renderlight.direction = scenelight.direction;
auto luminance = static_cast<float>(Global.fLuminance);
auto const environment = scenelight.owner->fShade;
if (environment > 0.f)
{
luminance *= environment;
}
auto const luminance = static_cast<float>(Global.fLuminance) * gfx::vehicle_light_level(scenelight.owner);
renderlight.diffuse = glm::vec4{glm::max(glm::vec3{colors::none}, scenelight.color - glm::vec3{luminance}), renderlight.diffuse[3]};
renderlight.ambient = glm::vec4{glm::max(glm::vec3{colors::none}, scenelight.color * glm::vec3{scenelight.intensity} - glm::vec3{luminance}), renderlight.ambient[3]};
renderlight.factor = (scenelight.count * 0.5) * (scenelight.owner->DimHeadlights ? 0.5 : 1.0);

View File

@@ -202,6 +202,11 @@ std::array<std::array<VkImageView, vk14::bindings::PASS_BINDING_COUNT>, 3> pass_
auto const probeempty{scene.cabprobe_empty ? scene.cabprobe_empty->view() : scene.env_empty->view()};
views[CAB_PROBE_TEX] = (colour && (probelight != nullptr) ? probelight->view() : probeempty);
views[CAB_PROBE_ALBEDO_TEX] = (colour && (probealbedo != nullptr) ? probealbedo->view() : probeempty);
// the occlusion of the sky by the scenery (the track T), for every pass which shades
auto const *occlusionheights{scene.skyocclusion.heights()};
auto const *occlusion{scene.skyocclusion.occlusion()};
views[SKY_OCCLUSION_HEIGHT_TEX] = (occlusionheights != nullptr ? occlusionheights->view() : scene.skyocclusion_heights_empty->view());
views[SKY_OCCLUSION_TEX] = (occlusion != nullptr ? occlusion->view() : scene.skyocclusion_empty->view());
}
return result;
}
@@ -663,6 +668,29 @@ bool vulkan14_renderer::init_scene(std::string &Reason)
{
return false;
}
// the occlusion of the sky by the scenery: the stand-ins of its maps of one texel, cleared with the other stand-ins in the first
// frame (nothing reads them: the block of the scene tells there are no maps)
{
auto standin = [&](std::shared_ptr<vk14::image> &Image, VkFormat const Format, char const *Name) {
vk14::image_desc desc;
desc.format = Format;
desc.extent = {1, 1, 1};
desc.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
Image = std::make_shared<vk14::image>();
if (false == Image->create(ctx.memory, desc, Name))
{
Image.reset();
return false;
}
return true;
};
if ((false == standin(scene.skyocclusion_heights_empty, VK_FORMAT_R32_SFLOAT, "empty sky occlusion heights")) || (false == standin(scene.skyocclusion_empty, VK_FORMAT_R16G16B16A16_SFLOAT, "empty sky occlusion")))
{
Reason = "no stand-ins of the maps of the occlusion of the sky";
return false;
}
scene.skyocclusion.init(ctx.memory, ctx.deletions);
}
scene.lamps.init(device, ctx.memory, ctx.deletions);
// the layer maps of the heightmap terrain drawn into the layers of its arrays by the copy shader
@@ -832,6 +860,9 @@ void vulkan14_renderer::destroy_scene()
destroy_weather();
destroy_ao();
destroy_cab_probe();
scene.skyocclusion.destroy();
scene.skyocclusion_heights_empty.reset();
scene.skyocclusion_empty.reset();
for (auto *pool : scene.occlusion_pools)
{
vkDestroyQueryPool(device, pool, nullptr);
@@ -1032,6 +1063,8 @@ void vulkan14_renderer::write_pass_sets(vk14::instance_cull *Cull)
write(handle, CAB_PROBE_TEX, nullptr, image(ctx.sampling.clamp_linear, views[CAB_PROBE_TEX]));
write(handle, CAB_PROBE_ALBEDO_TEX, nullptr, image(ctx.sampling.clamp_linear, views[CAB_PROBE_ALBEDO_TEX]));
write(handle, CAB_PROBE_SSBO, &cabprobe, nullptr);
write(handle, SKY_OCCLUSION_HEIGHT_TEX, nullptr, image(ctx.sampling.repeat_nearest, views[SKY_OCCLUSION_HEIGHT_TEX]));
write(handle, SKY_OCCLUSION_TEX, nullptr, image(ctx.sampling.repeat_nearest, views[SKY_OCCLUSION_TEX]));
}
vkUpdateDescriptorSets(device, static_cast<std::uint32_t>(writes.size()), writes.data(), 0, nullptr);
}

View File

@@ -807,6 +807,8 @@ std::string binding_preamble(bool const Raytracing)
define("CAB_PROBE_TEX", CAB_PROBE_TEX);
define("CAB_PROBE_ALBEDO_TEX", CAB_PROBE_ALBEDO_TEX);
define("CAB_PROBE_SSBO", CAB_PROBE_SSBO);
define("SKY_OCCLUSION_HEIGHT_TEX", SKY_OCCLUSION_HEIGHT_TEX);
define("SKY_OCCLUSION_TEX", SKY_OCCLUSION_TEX);
define("PASS_BINDING_COUNT", PASS_BINDING_COUNT);
define("MATERIAL_TEXTURE_FIRST", MATERIAL_TEXTURE_FIRST);
define("CL_CLUSTER_UBO", CL_CLUSTER_UBO);

View File

@@ -0,0 +1,160 @@
/*
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/.
*/
#include "stdafx.h"
#include "rendering/vulkan14/vulkan14skyocclusion.h"
#include "rendering/vulkan14/vulkan14commands.h"
#include "utilities/Logs.h"
namespace vk14
{
namespace
{
// the shading and the compute passes (the fog, the particles) read them
VkPipelineStageFlags2 const READ_STAGES{VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT};
} // namespace
sky_occlusion_maps::~sky_occlusion_maps()
{
let_go();
}
void sky_occlusion_maps::init(allocator &Memory, deletion_queue &Deletions)
{
m_memory = &Memory;
m_deletions = &Deletions;
}
void sky_occlusion_maps::destroy()
{
// the device is idle: the maps go at once
m_deletions = nullptr;
let_go();
m_memory = nullptr;
}
bool sky_occlusion_maps::gpu_allocate(int const Side)
{
let_go();
if ((m_memory == nullptr) || (Side <= 0))
{
return false;
}
auto make = [&](std::shared_ptr<image> &Map, VkFormat const Format, char const *Name) {
image_desc desc;
desc.format = Format;
desc.extent = {static_cast<std::uint32_t>(Side), static_cast<std::uint32_t>(Side), 1};
desc.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
Map = std::make_shared<image>();
if (false == Map->create(*m_memory, desc, Name))
{
Map.reset();
return false;
}
return true;
};
if ((false == make(m_heights, VK_FORMAT_R32_SFLOAT, "sky occlusion heights")) || (false == make(m_occlusion, VK_FORMAT_R16G16B16A16_SFLOAT, "sky occlusion")))
{
let_go();
return false;
}
m_ready = false;
return true;
}
void sky_occlusion_maps::gpu_release()
{
let_go();
}
void sky_occlusion_maps::let_go()
{
m_ready = false;
m_squares.clear();
m_data.clear();
if ((m_deletions != nullptr) && (m_heights || m_occlusion))
{
// the frames in flight may still read them
m_deletions->push(m_value, [heights = std::move(m_heights), occlusion = std::move(m_occlusion)]() {});
}
m_heights.reset();
m_occlusion.reset();
}
void sky_occlusion_maps::gpu_upload(int const X, int const Y, int const Size, float const *Heights, std::uint16_t const *Occlusion)
{
if (false == bool(m_heights))
{
return;
}
auto const texels{static_cast<std::size_t>(Size) * Size};
auto const offset{m_data.size()};
m_data.resize(offset + texels * (sizeof(float) + 4 * sizeof(std::uint16_t)));
std::memcpy(m_data.data() + offset, Heights, texels * sizeof(float));
std::memcpy(m_data.data() + offset + texels * sizeof(float), Occlusion, texels * 4 * sizeof(std::uint16_t));
m_squares.push_back(square{X, Y, Size, offset});
}
void sky_occlusion_maps::flush(command_stream *Commands)
{
if ((Commands == nullptr) || (false == bool(m_heights)) || m_squares.empty())
{
return;
}
auto staging{std::make_shared<buffer>()};
if ((false == staging->create(*m_memory, static_cast<VkDeviceSize>(m_data.size()), VK_BUFFER_USAGE_TRANSFER_SRC_BIT, memory_kind::upload, "sky occlusion upload")) || (staging->mapped() == nullptr))
{
ErrorLog("sky occlusion: no staging buffer for the maps");
return;
}
std::memcpy(staging->mapped(), m_data.data(), m_data.size());
staging->flush(0, static_cast<VkDeviceSize>(m_data.size()));
transition(Commands, *m_heights, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_2_COPY_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);
transition(Commands, *m_occlusion, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_2_COPY_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT);
std::vector<VkBufferImageCopy2> heights, occlusion;
for (auto const &entry : m_squares)
{
VkBufferImageCopy2 region{};
region.sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2;
region.bufferOffset = static_cast<VkDeviceSize>(entry.offset);
region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
region.imageOffset = {entry.x, entry.y, 0};
region.imageExtent = {static_cast<std::uint32_t>(entry.size), static_cast<std::uint32_t>(entry.size), 1};
heights.push_back(region);
region.bufferOffset += static_cast<VkDeviceSize>(entry.size) * entry.size * sizeof(float);
occlusion.push_back(region);
}
auto copy = [&](image &Target, std::vector<VkBufferImageCopy2> const &Regions) {
VkCopyBufferToImageInfo2 info{};
info.sType = VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2;
info.srcBuffer = staging->handle();
info.dstImage = Target.handle();
info.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
info.regionCount = static_cast<std::uint32_t>(Regions.size());
info.pRegions = Regions.data();
Commands->copy_buffer_to_image2(&info);
};
copy(*m_heights, heights);
copy(*m_occlusion, occlusion);
transition(Commands, *m_heights, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, READ_STAGES, VK_ACCESS_2_SHADER_SAMPLED_READ_BIT);
transition(Commands, *m_occlusion, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, READ_STAGES, VK_ACCESS_2_SHADER_SAMPLED_READ_BIT);
if (m_deletions != nullptr)
{
m_deletions->push(m_value, [staging]() {});
}
m_squares.clear();
m_data.clear();
m_ready = true;
}
} // namespace vk14

View File

@@ -0,0 +1,79 @@
/*
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/.
*/
// the maps of the occlusion of the sky by the scenery in the vulkan renderer (the track T, opengl46_sky_occlusion): the images of
// gfx::sky_occlusion, which works out what goes into them. the squares of the tiles it gives are gathered in a frame and copied at
// once from one staging buffer, with the commands of the frame (flush())
#pragma once
#include "rendering/common/skyocclusion.h"
#include "rendering/vulkan14/vulkan14memory.h"
#include <memory>
namespace vk14
{
class command_stream;
class sky_occlusion_maps : public gfx::sky_occlusion
{
public:
sky_occlusion_maps() = default;
~sky_occlusion_maps() override;
// Memory, Deletions: what the maps are made and let go with; called once
void init(allocator &Memory, deletion_queue &Deletions);
// the maps go (the device is about to)
void destroy();
// the value of the timeline the gpu reaches when it's done with the frame being recorded
void begin_frame(std::uint64_t const Value)
{
m_value = Value;
}
// the squares given in the frame copied to the maps, which are left in the layout the shading reads them in
void flush(command_stream *Commands);
// the maps once they're filled, for the pass sets; null: the stand-ins are read
image *heights() const
{
return m_ready ? m_heights.get() : nullptr;
}
image *occlusion() const
{
return m_ready ? m_occlusion.get() : nullptr;
}
protected:
bool gpu_allocate(int const Side) override;
void gpu_release() override;
void gpu_upload(int const X, int const Y, int const Size, float const *Heights, std::uint16_t const *Occlusion) override;
private:
void let_go();
allocator *m_memory{nullptr};
deletion_queue *m_deletions{nullptr};
std::uint64_t m_value{0};
std::shared_ptr<image> m_heights;
std::shared_ptr<image> m_occlusion;
bool m_ready{false};
// the squares waiting for the copy: where, and their data one after another (the heights, then the occlusion of each)
struct square
{
int x;
int y;
int size;
std::size_t offset; // of the heights in m_data
};
std::vector<square> m_squares;
std::vector<std::uint8_t> m_data;
};
} // namespace vk14