/* 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/renderer.h" #include "rendering/openglcamera.h" #include "rendering/opengl46/opengl46light.h" #include "rendering/opengl46/opengl46particles.h" #include "rendering/opengl46/opengl46gpuparticles.h" #include "rendering/opengl46/opengl46water.h" #include "rendering/opengl46/opengl46skydome.h" #include "rendering/opengl46/opengl46precipitation.h" #include "simulation/simulationenvironment.h" #include "scene/scene.h" #include "world/MemCell.h" #include "world/TrackSleepers.h" #include "rendering/lightarray.h" #include "vr/vr_interface.h" #include "gl/ubo.h" #include "gl/ssbo.h" #include "gl/streambuffer.h" #include "gl/cluster_grid.h" #include "gl/framebuffer.h" #include "gl/renderbuffer.h" #include "gl/postfx.h" #include "gl/shader.h" #include "gl/cubemap.h" #include "gl/glsl_common.h" #include "gl/pbo.h" #include "gl/query.h" #include "rendering/opengl46/opengl46terrain.h" #include "rendering/opengl46/opengl46grass.h" #include "rendering/opengl46/opengl46sky.h" #include "rendering/opengl46/opengl46weather.h" #include "rendering/opengl46/opengl46exposure.h" #include "rendering/opengl46/opengl46ao.h" #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/opengl46timbers.h" #include "rendering/opengl46/opengl46shadowfit.h" #include "rendering/common/scenewalk.h" #include "rendering/common/temporalaa.h" #include "rendering/common/screenreflections.h" // bare-bones render controller, in lack of anything better yet class opengl46_renderer : public gfx_renderer, public gfx::scene_walk { public: // constructors opengl46_renderer() = default; // destructor ~opengl46_renderer() {} // methods bool Init( GLFWwindow *Window ) override; void Shutdown() override; bool AddViewport(const global_settings::extraviewport_config &conf) override; // main draw call. returns false on error bool Render() override; void SwapBuffers() override; inline float Framerate() override { return m_framerate; } // geometry methods // NOTE: hands-on geometry management is exposed as a temporary measure; ultimately all visualization data should be generated/handled automatically by the renderer itself // creates a new geometry bank. returns: handle to the bank or NULL gfx::geometrybank_handle Create_Bank() override; // creates a new indexed geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL gfx::geometry_handle Insert(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override; // creates a new geometry chunk of specified type from supplied data, in specified bank. returns: handle to the chunk or NULL gfx::geometry_handle Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override; // replaces data of specified chunk with the supplied vertex data, starting from specified offset bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) override; // replaces the index and the vertex data of specified indexed chunk with the supplied data of the same counts bool Replace_Indexed(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry) override; // adds supplied vertex data at the end of specified chunk bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override; void Erase( gfx::geometry_handle const &Geometry ) override; // provides direct access to index data of specfied chunk gfx::index_array const & Indices( gfx::geometry_handle const &Geometry ) const override; // provides direct access to vertex data of specfied chunk gfx::vertex_array const & Vertices( gfx::geometry_handle const &Geometry ) const override; // provides direct access to vertex data of specfied chunk gfx::userdata_array const & UserData( gfx::geometry_handle const &Geometry ) const override; int Primitive( gfx::geometry_handle const &Geometry ) const override; // material methods material_handle Fetch_Material( std::string const &Filename, bool const Loadnow = true ) override; material_handle Matte_Material( material_handle const Material ) override; material_handle Tinted_Material( material_handle const Material, glm::vec3 const &Colour ) override; material_handle Blend_Material( material_handle const Material, std::array const &Others ) override; bool Blendable_Material( material_handle const Material ) override; void Bind_Material( material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr ) override; IMaterial const * Material( material_handle const Material ) const override; material_handle Terrain_Material( material_handle const Reuse, std::vector const &Layers, int const Samples, std::uint8_t const *Weights, glm::vec3 const &Placement, int const HoleSamples = 0, std::uint8_t const *Holes = nullptr ) override; void Terrain_Release( material_handle const Material ) override; void Terrain_Grass( std::vector const &Kinds, std::vector const &Chunks ) override; // shader methods auto Fetch_Shader( std::string const &name ) -> std::shared_ptr override; auto Shader_Name( std::shared_ptr const &Program ) const -> std::string override; // texture methods texture_handle Fetch_Texture( std::string const &Filename, bool const Loadnow = true, GLint format_hint = GL_SRGB_ALPHA ) override; void Bind_Texture( texture_handle const Texture ) override; void Bind_Texture( std::size_t const Unit, texture_handle const Texture ) override; ITexture & Texture( texture_handle const Texture ) override; ITexture const & Texture( texture_handle const Texture ) const override; // utility methods void Pick_Control_Callback( std::function Callback ) override; void Pick_Node_Callback( std::function Callback ) override; TSubModel const * Pick_Control() const override { return m_pickcontrolitem; } scene::basic_node const * Pick_Node() const override { return m_picksceneryitem; } void Forget_Node( scene::basic_node const *Node ) override; glm::dvec3 Mouse_Position() const override { return m_worldmousecoordinates; } bool Mouse_Hit() const override { return m_mousehit; } glm::mat4 Camera_View_Matrix() const override { return glm::mat4( glm::mat3( m_colorpass.pass_camera.modelview() ) ); } glm::mat4 Camera_Projection_Matrix() const override { return m_colorpass.pass_camera.projection(); } glm::dvec3 Camera_Position() const override { return m_colorpass.pass_camera.position(); } // maintenance methods void Update( double const Deltatime ) override; bool Debug_Ui_State(std::optional) override; void Update_Pick_Control() override; void Update_Pick_Node() override; glm::dvec3 Update_Mouse_Position() override; // debug methods std::string const & info_times() const override; std::string const & info_stats() const override; std::vector const & info_sections() const override; void MakeScreenshot() override; bool Render_Preview( TAnimModel *Instance, int const Size, int const Margin, bool const Shadows, std::vector &Image ) override; opengl_material & Material( material_handle const Material ); opengl_material const & Material( TSubModel const * Submodel ) const; // draws supplied geometry handles void Draw_Geometry(std::vector::iterator begin, std::vector::iterator end); void Draw_Geometry(const gfx::geometrybank_handle &handle); // material methods void Bind_Material_Shadow(material_handle const Material); void Update_AnimModel(TAnimModel *model); // members GLenum static const sunlight{0}; static std::unique_ptr create_func(); // how the windows of the renderer are made (opengl46backend.cpp) static gfx_backend_desc backend_desc(); private: using gfx::scene_walk::Render; using gfx::scene_walk::Render_Alpha; // the draws of the shared walk of the scene void walk_material(material_handle const Material, TSubModel const *Submodel = nullptr, lighting_data const *Lighting = nullptr) override; void walk_shadow_material(material_handle const Material) override; opengl_material const &walk_material_data(material_handle const Material) const override; opengl_material const &walk_material_data(TSubModel const *Submodel) const override; void walk_program(walk_shader const Program) override; void walk_texture(std::size_t const Unit, texture_handle const Texture) override; void walk_blending(bool const Alpha) override; void walk_sunlight(float const Factor = 1.f) override; void walk_environment(TEnvironmentType const Environment = e_flat) override; void walk_ambient_save() override; void walk_ambient_add(glm::vec3 const &Addition) override; void walk_ambient_restore() override; glm::vec4 walk_sun_ambient() const override; void walk_draw(gfx::geometry_handle const &Geometry) override; void walk_draw(std::vector::iterator First, std::vector::iterator Last) override; std::uint32_t walk_instances(glm::mat4 const *Modelviews, std::size_t const Count) override; // the instances of the sleepers and timbers of the tracks on the gpu (m_timbers) bool walk_timbers_gpu() const override; bool walk_timbers_place(std::vector const &Tracks, bool const Restart) override; std::uint32_t walk_timbers_first(TTrack const &Track) const override; std::uint32_t walk_instances_reserve(std::size_t const Count) override; void walk_timbers_expand(std::vector const &Origins, std::vector const &Ranges) override; void walk_cull_face(bool const Enabled) override; void walk_glare(bool const Enabled) override; void walk_glare_draw(TSubModel *Submodel, gfx::geometry_handle const &Geometry) override; void walk_light_query(TSubModel *Submodel, bool const Begin) override; // the gpu culling of the instances, models and sleepers, the depth pass of the whole scene void walk_region_begin() override; void walk_editor_cursor() override; bool walk_instances_begin() override; void walk_instances_end() override; gfx::geometrybank_manager &walk_geometry() override; bool walk_blending_enabled() const override; // types struct viewport_config { int width; int height; float draw_range; bool main = false; GLFWwindow *window = nullptr; // ogl window context bool real_window = true; // whether we need to blit onto GLFWwindow surface bool custom_backbuffer = false; // whether we want to render to our offscreen LDR backbuffer (pipeline required) bool shadow = false; // include this viewport area in shadowmap enum vp_type { normal, custom, vr_left, vr_right } proj_type; viewport_proj_config projection; // main msaa render target for pipeline mode std::unique_ptr msaa_fb; std::unique_ptr msaa_rbc; std::unique_ptr msaa_rbv; std::unique_ptr msaa_rbd; // msaa resolve buffer (when using motion blur) std::unique_ptr main_fb; std::unique_ptr main_texv; std::unique_ptr main_texd; std::unique_ptr main_tex; // final HDR buffer (also serving as msaa resolve buffer when not using motion blur) std::unique_ptr main2_fb; std::unique_ptr main2_tex; // ambient occlusion, half size: raw and smoothed (gtao), or the ssao and its blur (older ssao) std::unique_ptr ssao_tex; std::unique_ptr ssao_blur_tex; std::unique_ptr ssao_fb; std::unique_ptr ssao_blur_fb; // main_tex alone, for the ambient occlusion to be applied to it before the motion blur (main_fb holds the depth it reads) std::unique_ptr ao_apply_fb; // the temporal anti-aliasing (main viewport only): the two images of the history, written in turn std::array, 2> taa_tex; std::array, 2> taa_fb; // the screen space reflections (main viewport only): the finished image of the last frame with its mipmaps, which they take // their colour from (Ssr_History_Target()), and the number of its levels. made when it is first needed std::unique_ptr ssr_tex; std::unique_ptr ssr_fb; int ssr_levels { 0 }; // LDR backbuffer for offscreen rendering std::unique_ptr backbuffer_fb; std::unique_ptr backbuffer_tex; // levels of the bloom of the post processing; null without the pipeline std::unique_ptr bloom; // light cluster grid of the viewport std::unique_ptr clusters; // depth of the scene resolved from the msaa buffer (Resolved_Depth_Target()): of the finished scene for the cluster heatmap, // and of the depth pass of the scene for the contact shadows, read while the colour is drawn. made when it is first needed std::unique_ptr clusters_depth_fb; std::unique_ptr clusters_depth_tex; bool initialized = false; }; viewport_config *m_current_viewport = nullptr; // methods std::unique_ptr make_shader(std::string v, std::string f); bool Init_caps(); void setup_pass(viewport_config &Viewport, renderpass_config &Config, rendermode const Mode, float const Znear = 0.f, float const Zfar = 1.f, bool const Ignoredebug = false); void setup_matrices(); void setup_drawing(bool const Alpha = false); void setup_shadow_empty_map(); void setup_shadow_bind_map(); void setup_shadow_color( glm::vec4 const &Shadowcolor ); void setup_env_map(gl::cubemap *tex); void setup_environment_light(TEnvironmentType const Environment = e_flat); void setup_sunlight_intensity( float const Factor = 1.f); // sends the header of the light buffer and the sun to the gpu void upload_light_header(); // runs jobs needed to generate graphics for specified render pass void Render_pass(viewport_config &vp, rendermode const Mode); // creates dynamic environment cubemap bool Render_reflections(viewport_config &vp); // draws the cube shadow maps of the lamps which need it in this frame (the cab lamp: the cab) void Render_local_shadows(); // the occupied vehicle whose cab is drawn in the colour pass, nullptr when there's none (the conditions of Render_cab()) TDynamicObject *Cab_Probe_Vehicle() const; // draws the probe of the occupied cab when it needs it in this frame void Render_cab_probe(); // draws the parts of the cube shadow maps of the scenery lamps which need it in this frame void Render_lamp_shadows(); // one part of the map of a scenery lamp: the scenery, or the vehicles by it (and Cab, the occupied cab) over a copy of it void Render_lamp_shadow(std::size_t const Slot, bool const Vehicles, TDynamicObject *Cab); // picks the headlights which get shadow maps in this frame and draws the maps which need it void Render_headlight_shadows(); // the map of the headlights in the slot: the scene within reach ahead of them (and Cab, the occupied cab), without their vehicle void Render_headlight_shadow(std::size_t const Slot, light_array::light_record const &Light, TDynamicObject *Cab); // the culling of one of the maps of lamps and headlights (Global.gfx_gpucull_shadows_headlights, _lamps), the one at Index of // Culls, made when it's first needed; nullptr when the instances of the map go through the cpu opengl46_instance_cull *Light_Shadow_Cull(std::vector> &Culls, std::size_t const Index, bool const Enabled); // the rows of these cullings for the debug panel: their timings (Times) or their counts std::string Light_Shadow_Cull_Info(bool const Times); // the projector of the beam of a set of headlights: a narrow frustum along the vehicle, from behind it, its near plane at the // end of the vehicle. the beam texture is projected with it (Update_Lights()) and the shadow map of the headlights drawn with it. // Position in world space, Rotation from world axes to its axes; Near is the distance of the near plane from the projector void Headlight_Projector(light_array::light_record const &Light, glm::dvec3 &Position, glm::mat3 &Rotation, glm::mat4 &Projection, float &Near, float &Fieldofview); bool Render(world_environment *Environment); // places supplied modelview matrices in the instance buffer of the frame. returns: base instance of the first of them std::uint32_t Upload_Instance_Modelviews( glm::mat4 const *Modelviews, std::size_t const Count ); // the room for Count modelviews in the instance buffer of the frame, for the gpu to write. returns: base instance of the first std::uint32_t Reserve_Instance_Modelviews( std::size_t const Count ); // binds the instance buffer of the frame for the vertex shaders (INSTANCE_SSBO_BINDING) void Bind_Instance_Stream() const; // draws the instances queued in the pass from the result of the gpu culling (Global.gfx_gpucull 1): makes the depth // pyramid of the occlusion test, runs the culling, walks the submodel trees of the groups once per lod band to record // the draws (m_indirectdraws, m_indirectcommands), has their instances filled in, then draws them with indirect draws, // their depth first (Global.gfx_depthprepass). inside the depth pass of the whole scene only their depth is drawn, their // colour waits for the colour pass (Render_Instances_Indirect_Colour()). in two phases // (Global.gfx_gpucull_occlusion_instances) the instances seen in the previous frame are drawn first (their depth, or // their colour without the prepass), the pyramid is made again and the other instances are culled against it void Render_Instances_Indirect(); // the same for the passes culled on the gpu other than the main view (Global.gfx_gpucull_shadows, _reflections, // _shadows_headlights, _shadows_lamps): the cascades of the sun shadows and the maps of headlights and lamps, drawn with the // shadow programs (the cube ones for the lamps), and the faces of the environment map, drawn with the programs of the // materials like the main view. no occlusion test, no depth prepass, one phase void Render_Instances_Indirect_Pass(); // the walk of the submodel trees of the groups culled by Cull, once per lod band, which records their draws // (m_indirectdraws, m_indirectcommands; Walk_Indirect_Draws()), puts them in order for multi draws and has Cull fill in their // commands void Record_Indirect_Draws( opengl46_instance_cull &Cull ); // the culling the pass under way feeds (Begin_Pass_Cull()), always one of this renderer opengl46_instance_cull &Pass_Cull() { return static_cast( *m_passcull ); } // draws of the instances made, and the gl calls which made them struct indirect_counts { std::uint32_t draws { 0 }; std::uint32_t calls { 0 }; indirect_counts &operator+=( indirect_counts const &Other ) { draws += Other.draws; calls += Other.calls; return *this; } }; // the recorded draws of the instances, with the commands of the given set: their depth with the programs of the depth // prepass, with the state of the prepass already set, the same with the state set for it and the time measured outside // the depth pass of the whole scene, and their colour. the command buffer and the instances are bound by the caller. // with multi draws (m_indirectmultidraw) the draws which follow one another with the same state go in one call indirect_counts Render_Instances_Indirect_Depth( opengl46_instance_cull::command_set const Set ); indirect_counts Render_Instances_Indirect_Prepass( opengl46_instance_cull::command_set const Set, gl::gpu_timer &Timer ); indirect_counts Render_Instances_Indirect_Colour( opengl46_instance_cull::command_set const Set ); // the recorded draws of a shadow pass with the shadow programs, the way Bind_Material_Shadow() picks them indirect_counts Render_Instances_Indirect_Shadow( opengl46_instance_cull::command_set const Set ); // multi draws of the recorded draws (Global.gfx_gpucull_multidraw), put in order by Order_Indirect_Draws(): places their // model blocks in the buffer of the frame (m_drawblockstream), in the same order void Upload_Indirect_Blocks(); // binds the model blocks of Count recorded draws from First for the programs of the multi draws (MODEL_DRAW_SSBO_BINDING) void Bind_Indirect_Blocks( std::size_t const First, std::size_t const Count ); // the program Bind_Material() binds for the material, nullptr when it binds none gl::program const *Indirect_Program( material_handle Material ) const; // the variant of a material program for multi draws (built with MODEL_DRAWS), made the first time it's asked for. // nullptr if there's none (a program not fetched by Fetch_Shader(), or one which didn't compile) gl::program *Multidraw_Program( gl::program const *Program ); // how the depth prepass treats the material: 0 left out, 1 drawn without alpha test, 2 with the alpha test of the diffuse texture int Prepass_Class( material_handle const Material ); // depth of the whole opaque scene of the colour pass, before its colour (Global.gfx_depthprepass_scene): the colour pass // walks the scene a second time, Bind_Material() binds the programs of the depth prepass and draw() leaves out what they // can't draw. with Global.gfx_depthprepass_replay the walk of the colour pass is made here, once: its opaque draws are // recorded (Record_Scene_Draw()), their depth is drawn from the record (Render_Scene_Prepass()) and the colour pass draws // them from it (Render_Scene_Colour()). Future: the motion matrix the colour pass draws the region with. Contactonly: the // depth is drawn only for the contact shadows, which need it within their reach void Render_Scene_Depth( glm::mat4 const &Future, bool const Contactonly ); // notes a draw of the walk recorded by Render_Scene_Depth(): the chunks drawn, with the state the walk left for them void Record_Scene_Draw( gfx::geometry_handle const *First, std::size_t const Count, bool const Batch ); // draws the depth of the recorded draws not drawn yet, in runs of the same program, bank and texture of the alpha test, // with multi draws when there are any. the depth pyramid of the occlusion test and the cursor of the editor take the depth // drawn so far, and the end of the walk the rest void Render_Scene_Prepass(); // the colour of the recorded draws, in the order of the walk, with the programs of their materials void Render_Scene_Colour(); // the colour of the instances culled and recorded by the depth pass of the whole scene (m_indirectcolourpending) void Render_Instances_Indirect_Pending(); // the bounding sphere of a geometry chunk in the space of its vertices, w the radius (negative if there's none) glm::vec4 const &Chunk_Sphere( gfx::geometry_handle const &Geometry ); // whether the colour pass of the viewport gets contact shadows, which need the depth pass of the scene bool Contact_Shadows_Wanted( viewport_config const &Viewport ) const; // resolves the depth drawn by Render_Scene_Depth() for the contact shadows and sets them up for the colour pass void Setup_Contact_Shadows( viewport_config &Viewport ); // makes the target of the resolved depth of the viewport if it isn't there yet; false if it can't be made bool Resolved_Depth_Target( viewport_config &Viewport ); // whether the colour pass of the viewport takes the ambient occlusion into the light of its surfaces (gfx.postfx.ao.lighting), // which needs the depth pass of the scene bool Ao_Lighting_Wanted( viewport_config const &Viewport ) const; // works out the ambient occlusion of the depth drawn by Render_Scene_Depth() and sets it up for the colour pass void Setup_Ao_Lighting( viewport_config &Viewport ); // resolves the depth drawn so far in the colour pass of the viewport for the soft particles (gfx.particles.soft) and binds it // to gl::SCENE_DEPTH_TEX; false when the viewport has no such depth bool Setup_Particles_Depth( viewport_config &Viewport ); // the direction the sun shadows are cast in (the sun, its height capped by gfx.shadow.angle.min), and the rotation from // the world to the space of the light of the cascades: x and y across the maps, z along the light glm::vec3 Shadow_Light_Vector() const; glm::mat3 Shadow_Light_Rotation() const; // the distances the fixed cascades of the sun shadows go from and to (shadowtune) std::array Shadow_Fixed_Splits() const; // why the cascades of the sun shadows can't be fitted to the depth of the viewport (gfx.shadow.sdsm), null if they can char const *Shadow_Fit_Unavailable( viewport_config const &Viewport ) const; // the cascades of the frame fitted to the latest measurement of the depth of the view (gfx.shadow.sdsm): the distances // they go from and to, and for each cascade the corners of the area its map is to cover, camera-relative world space // (none: the whole slice of the view). false: the fixed cascades. fills m_shadowfitstate bool Shadow_Fit( viewport_config const &Viewport, std::array &Splits, std::array>, gl::MAX_CASCADES> &Areas ); // whether the depth pass of the whole scene draws the material bound last (Bind_Material()) and the geometry bool Scene_Depth_Draws( gfx::geometry_handle const &Geometry ) const; // releases gl textures used only by vehicles farther than gfx.textures.releasedistance times the draw range void Update_Texture_Release(); // textures used by materials of the sub-models of specified model, replaceable skins excluded std::vector const &Model_Textures( TModel3d *Model ); // has the data of the textures read ahead of the camera, with gfx.textures.streaming void Update_Texture_Prefetch(); void Render_particles(); void Render_precipitation(); // the view of the colour pass being drawn, for the particles of the gpu opengl46_gpu_particles::view Particles_View() const; // the grass of the heightmap terrain around the camera of the main view; Future: the matrix of the motion vectors void Render_grass(glm::mat4 const &Future); void Render_vr_models(); void Render_editor_overlay(); // writes the lighting of the pass about to be drawn to the light buffer: the sun, the ambient light and the fog, and the headlights of Lights // unless Headlights is false, as in the pass which draws the environment map, where lights of vehicles would only get baked into the map. // Clustered tells the shading to take the headlights from the lists of the clusters (see Cull_Lights()) and not to go through the first few. // with Headlights and Clustered the cab lamp (first) and the lamps of models and vehicles (after the headlights) are written as well void Update_Lights(light_array &Lights, bool const Headlights = true, bool const Clustered = false); // writes a light of a model or a vehicle, or the cab lamp, to the slot of the light buffer, in the view space of the pass void Write_Local_Light(gl::light_element &Light, gfx::local_lights::light const &Source, glm::dvec3 const &Camera, glm::mat4 const &View); // brings the light cluster grid of the viewport up to date with the projection of the color pass being drawn. // Targetsize is the size in pixels of what the pass draws to, which gl_FragCoord of the shading counts in void Update_Clusters(viewport_config &vp, glm::ivec2 const &Targetsize); // assigns the lights set up by Update_Lights() to the clusters of the viewport; run after it, before the scene is drawn void Cull_Lights(viewport_config &vp); // debug view: tints the finished image of the viewport with the cluster of every pixel. Target is the framebuffer // holding the image (null for the default one), Targetsize its size void Render_cluster_heatmap(viewport_config &vp, gl::framebuffer *Target, glm::ivec2 const &Targetsize); // ambient occlusion from the resolved depth (vp.main_texd), multiplied into the image of Target. Mode: Global.gfx_postfx_ao void Render_ambient_occlusion(viewport_config &vp, gl::framebuffer &Target, int const Mode); // the temporal anti-aliasing of the main view (gfx.antialiasing taa), and whether the viewport has it void Render_taa(viewport_config &vp); bool Taa_View(viewport_config const &vp) const; // the screen space reflections (gfx.ssr): whether the colour pass of the viewport gets them (they need the depth pass of the // scene), the depth resolved for them with the history bound and the parameters of the pass set, the finished image of the // frame copied to the history (from the colour attachment Attachment of Source), and the target of the history made when it // isn't there yet (false if it can't be made) bool Ssr_View(viewport_config const &vp) const; void Setup_Screen_Reflections(viewport_config &vp, bool const Temporal); void Store_Ssr_History(viewport_config &vp, gl::framebuffer &Source, GLenum const Attachment); bool Ssr_History_Target(viewport_config &vp); bool init_viewport(viewport_config &vp); void draw(const gfx::geometry_handle &handle); void draw(std::vector::iterator begin, std::vector::iterator end); void draw_debug_ui(); // model previews // extends provided bounds with the geometry of the submodel and its siblings and children visible in a preview void Preview_Bounds( TSubModel const *Submodel, glm::dmat4 const &Transform, glm::dvec3 &Min, glm::dvec3 &Max ) const; // prepares the offscreen targets of model previews for square images of specified size bool Preview_Targets( int const Size ); // draws the instance on its own with the current render pass settings void Preview_Model( TAnimModel *Instance, float const Squaredistance, bool const Alpha ); // draws the lit instance into the hdr preview target, marking in its stencil the pixels covered by the model void Preview_Color( TAnimModel *Instance, float const Squaredistance, int const Size, glm::dvec3 const &Eye, glm::mat4 const &View, glm::mat4 const &Projection, bool const Shadows ); // members GLFWwindow *m_window{nullptr}; // main window gfx::geometrybank_manager m_geometry; material_manager m_materials; texture_manager m_textures; opengl46_terrain_materials m_terrainmaterials{m_materials, m_textures}; opengl46_grass m_grass; // of the heightmap terrain opengl46_light m_sunlight; // fog colour of the frame: the one of the simulation, or of the atmosphere (see opengl46_sky::update()) glm::vec3 m_fogcolor{0.f}; // light of the sky: directional ambient light, atmosphere, prefiltered environment map opengl46_sky m_sky; // fog, clouds and their shadows, stars opengl46_weather m_weather; // automatic exposure and bloom of the post processing opengl46_exposure m_exposure; // screen space ambient occlusion of the post processing opengl46_ao m_ao; // true while the environment map bound for the shading is the real one (not the empty stand-in of the reflection pass) bool m_envmap_bound{false}; /* float m_sunandviewangle; // cached dot product of sunlight and camera vectors */ texture_handle m_suntexture{-1}; texture_handle m_moontexture{-1}; texture_handle m_smoketexture{-1}; texture_handle m_headlightstexture{-1}; // main shadowmap resources int m_shadowbuffersize{2048}; glm::mat4 m_shadowtexturematrix; // conversion from camera-centric world space to light-centric clip space int m_environmentcubetextureface{0}; // helper, currently processed cube map face int m_environmentupdateface{6}; // cube map face the environment map update in progress draws next; 6: no update in progress bool m_environmentready{false}; // the environment map was drawn whole at least once double m_environmentupdatetime{0}; // time of the most recent environment map update glm::dvec3 m_environmentupdatelocation; // coordinates of most recent environment map update opengl46_skydome m_skydomerenderer; opengl46_precipitation m_precipitationrenderer; opengl46_particles m_particlerenderer; // particle visualization subsystem opengl46_gpu_particles m_gpuparticles; // the smoke, rain and snow of the gpu (gfx.smoke.gpu, gfx.precipitation.gpu) opengl46_windscreen_water m_windscreenwater; // the water on the windscreens of the cab of the train bool m_particlesoft{false}; // the depth for the soft particles is bound in the colour pass being drawn float m_framerate; double m_updateaccumulator{0.0}; std::string m_debugtimestext; std::string m_pickdebuginfo; //debug_stats m_debugstats; std::string m_debugstatstext; std::vector m_debugsections; // the same data in sections, for the debug panel // what the process and its parts keep in memory, for the debug panel, gathered every 2 s; whether the card tells its memory // (GL_NVX_gpu_memory_info), -1 not asked yet void Update_Memory_Info(); std::string m_memorytext; std::chrono::steady_clock::time_point m_memorytime {}; int m_nvxmemory { -1 }; struct simulation_state { std::string weather; std::string season; } m_simulationstate; glm::vec4 m_baseambient{0.0f, 0.0f, 0.0f, 1.0f}; glm::vec4 m_shadowcolor{colors::shadow}; // TEnvironmentType m_environment { e_flat }; float m_specularopaquescalefactor{1.f}; float m_speculartranslucentscalefactor{1.f}; // textures used by vehicle models, for the release of far textures std::unordered_map> m_modeltextures; // the read ahead of textures (Update_Texture_Prefetch()): the cell it goes on with std::size_t m_prefetchcursor { 0 }; std::chrono::steady_clock::time_point m_prefetchvehicletime; std::chrono::steady_clock::time_point m_prefetchdroptime; unsigned int m_nodestateversion { 0 }; // scene::node_state_version which m_instancerecordsversion was last checked against // culling of instances in compute passes, run along the first color pass of the main viewport of a frame // when Global.gfx_gpucull is set: the instances of the pass are drawn from its result (1), or the result is compared // with the one of the cpu (2) opengl46_instance_cull m_instancecull; unsigned int m_instancecullframe { ~0u }; // frame it last ran in // the cullings of the cascades of the sun shadows (Global.gfx_gpucull_shadows), with records and programs shared with // m_instancecull std::array m_shadowcull; // the cullings of the maps of headlights, by the slot of the map, and of the scenery parts of the maps of lamps, by their order // among those drawn in a frame: the passes of a frame don't share the buffers of a culling. made when first needed std::vector> m_headlightcull; std::vector> m_lampcull; opengl46_instance_cull *m_lightshadowcull { nullptr }; // the one of the map of headlights or of a lamp being drawn std::size_t m_lampscenerydrawn { 0 }; // scenery parts of the maps of lamps drawn in this frame so far opengl46_instance_cull *m_indirectcull { &m_instancecull }; // the one the recorded draws (m_indirectdraws) were culled by int m_shadowcascade { -1 }; // the region is drawn for this cascade of the sun shadows // depth pyramid for the occlusion test of the gpu culling opengl46_hiz m_hiz; opengl46_timbers m_timbers; // the instances of the sleepers and timbers of the tracks on the gpu // the cascades of the sun shadows fitted to the depth of the main view (Global.gfx_shadow_sdsm) opengl46_shadow_fit m_shadowfit; // with a cascade fitted: the corners of the area its map is to cover, for setup_pass() std::array const *m_shadowfitarea { nullptr }; // what the last shadow pass did, for the debug panel gfx::shadow_fit::fit_state m_shadowfitstate; // the sizes setup_pass() found for the last shadow pass: of the whole slice of the view and as drawn float m_shadowslicesize { 0.f }; float m_shadowdrawnsize { 0.f }; // cpu time of the parts of the culling and the draws of the instances of the main view (Render_Instances_Indirect() and // _Pending()), for the panel: the depth of the recorded walk of the scene drawn before the culling, the depth pyramids, the // culling passes, the walks with putting the draws in order, the depth and the colour of the instances struct cull_parts { double scenedepth { 0.0 }; double pyramid { 0.0 }; double culling { 0.0 }; double record { 0.0 }; double depth { 0.0 }; double colour { 0.0 }; }; cull_parts m_cullparts; // multi draws of the recorded draws: whether they were recorded for them (ordered, their model blocks uploaded), and where // the blocks are bool m_indirectmultidraw { false }; GLuint m_indirectblockbuffer { 0 }; GLintptr m_indirectblockoffset { 0 }; std::uint64_t m_indirectblockgeneration { 0 }; // working space of Upload_Indirect_Blocks(), kept between frames std::vector m_indirectblocks; // the model blocks of the multi draws, one buffer for each of the last frames like model_ubo (nullptr: no multi draws) std::unique_ptr m_drawblockstream; // programs of the multi draws: the vertex shader of the materials, variants of the material programs, of the depth prepass std::unique_ptr m_vertex_multidraw_shader; std::unordered_map> m_multidrawprograms; std::unique_ptr m_prepass_multidraw_shader; std::unique_ptr m_prepass_alpha_multidraw_shader; std::unique_ptr m_shadow_multidraw_shader; // and of the sun shadows std::unique_ptr m_alpha_shadow_multidraw_shader; bool m_bindmultidraw { false }; // Bind_Material() binds the variant of the material program for multi draws // depth prepass: programs and the class of the material programs (see Prepass_Class()) std::unique_ptr m_prepass_shader; std::unique_ptr m_prepass_alpha_shader; std::unordered_map m_prepassclasses; gl::gpu_timer m_prepasstimer; gl::gpu_timer m_prepassfirsttimer; // of the instances drawn first, in two phases // depth pass of the whole scene (Render_Scene_Depth()) bool m_scenedepthpass { false }; // inside it: Bind_Material() and draw() work for the depth bool m_scenedepthskip { true }; // the material bound last isn't drawn in it bool m_scenedepthdrawn { false }; // it was drawn for the colour pass under way, which skips what was done in it already // with m_indirectcolourpending: the instances were culled in it, the colour pass draws them from the recorded draws opengl46_instance_cull::command_set m_indirectcolourset { opengl46_instance_cull::command_set::first }; // with these commands debug_stats m_scenedepthstats; // of its last run double m_scenedepthcputime { 0.0 }; // ms gl::gpu_timer m_scenedepthtimer; // the recorded walk of the colour pass (Global.gfx_depthprepass_replay): a draw with the state it was made with struct scene_draw { gl::model_ubs ubs; material_handle material { null_handle }; // bound last before it (Bind_Material()) TSubModel const *submodel { nullptr }; std::uint32_t seed { 0 }; // TSubModel::iVariantSeed then std::uint32_t variant { 0 }; // TSubModel::VariantId() then: which textures of the material were bound std::uint32_t chunkfirst { 0 }; // in m_scenechunks std::uint32_t chunkcount { 0 }; std::uint32_t instancecount { 0 }; // instanced draw (Render_Instanced(), Render_Sleepers()), 0 if not std::uint32_t instancebase { 0 }; GLuint instancebuffer { 0 }; // the instance buffer of the frame the matrices of the instances went to std::uint32_t header { 0 }; // the light header (m_sceneheaders) it is lit with texture_handle prepasstexture { null_handle }; // of the alpha test of the depth pass int prepassclass { 0 }; // Prepass_Class(), 0 as well for what the depth pass leaves out (blending, opacity < 0) bool blending { false }; bool batch { false }; // drawn with draw_batch() (neighbouring tracks) bool instances { false }; // no draw: the colour of the instances culled in the walk goes here }; struct scene_chunk { gfx::geometry_handle geometry; std::uint32_t draw; bool depth; // drawn in the depth pass }; using light_header = std::array; bool m_scenerecording { false }; // inside the walk of Render_Scene_Depth(): draw(), Bind_Material(), upload_light_header() record bool m_scenerecorded { false }; // the colour pass under way draws the record bool m_scenereplayed { false }; // the last depth pass of the scene was drawn from a record bool m_scenedepthreach { false }; // the depth pass leaves out the draws lying whole farther than m_scenedepthlimit float m_scenedepthlimit { 0.f }; material_handle m_scenematerial { null_handle }; // what Bind_Material() bound last in the walk TSubModel const *m_scenesubmodel { nullptr }; std::uint32_t m_sceneseed { 0 }; std::uint32_t m_scenevariant { 0 }; std::vector m_scenedraws; std::vector m_scenechunks; std::vector m_scenegeometry; // the chunks of m_scenechunks, for draw_batch() std::vector m_sceneheaders; // the first one is in the light buffer when the walk begins std::size_t m_sceneflushed { 0 }; // chunks Render_Scene_Prepass() went through // working space of Render_Scene_Prepass(): a chunk drawn and what its run has in common struct scene_prepass_item { int prepassclass; texture_handle texture; std::uint32_t bank; unsigned int type; std::uint32_t indexed; GLuint instancebuffer; // 0 for a draw which isn't instanced, which reads the identity every buffer starts with std::uint32_t chunk; gl::instance_draw_command command; auto tie() const { return std::tie( prepassclass, texture, bank, type, indexed, instancebuffer, chunk ); } }; std::vector m_sceneprepassitems; std::vector m_sceneprepassblocks; std::vector m_sceneprepasscommands; // the commands of the multi draws of the depth pass, one buffer for each of the last frames (nullptr: no multi draws) std::unique_ptr m_scenecommandstream; // of the last recorded walk: parts drawn (chunks of geometry; neighbouring tracks are drawn together), those of them drawn in // the depth pass and its gl calls, and those left out of it as farther than the reach of the contact shadows std::uint32_t m_scenerecordedparts { 0 }; std::uint32_t m_sceneprepassdraws { 0 }; std::uint32_t m_sceneprepasscalls { 0 }; std::uint32_t m_scenebeyondreach { 0 }; // Chunk_Sphere() of the chunks asked about, dropped when their vertices change (Replace(), Append()) std::unordered_map m_chunkspheres; // contact shadows: set up for the colour pass under way, and whether the last main view had them bool m_contactshadows { false }; bool m_contactshadowslast { false }; // the ambient occlusion of the colour pass of the main view under way is in the light of its surfaces (gfx.postfx.ao.lighting), // not applied to its image bool m_aolighting { false }; // a vehicle drawn in the colour pass of the last frame moved, and one drawn in this frame did (motion blur, Render_pass()) bool m_vehiclesmoved { true }; gl::gpu_timer m_contactshadowstimer; // resolve of the depth // the screen space reflections of the main view: the matrices and the parameters, set up for the colour pass under way, begun // for the frame (the history written at its end), and the copy of the image to the history with its mipmaps gfx::screen_reflections m_ssr; bool m_ssractive { false }; bool m_ssrframe { false }; gl::gpu_timer m_ssrhistorytimer; renderpass_config m_colorpass; // parametrs of most recent color pass std::array m_shadowpass; // parametrs of most recent shadowmap pass for each of csm stages TSubModel const *m_pickcontrolitem{nullptr}; scene::basic_node *m_picksceneryitem{nullptr}; glm::vec3 m_worldmousecoordinates { 0.f }; bool m_mousehit { false }; #ifdef EU07_USE_DEBUG_CAMERA renderpass_config m_worldcamera; // debug item #endif std::optional m_timequery; GLuint64 m_gllasttime = 0; // gpu time of the parts of the frame, marks put one after another through the main view (the context of m_window) gl::gpu_timeline m_gputimeline; // cpu time of the same parts, marked at the same places: a part lasts from its mark to the next one, the parts of the same // name are added up in a frame and averaged over the frames the way Timer::stopwatch averages struct cpu_timeline { struct part { char const *name; float time { 0.f }; // average, ms float frame { 0.f }; // in the frame under way, ms }; std::vector parts; // in the order they came first std::size_t current { 0 }; std::chrono::steady_clock::time_point mark; bool active { false }; // starts the frame with its first part. the names are kept as pointers: string literals void begin( char const *Name ); // ends the part under way and starts the next one void next( char const *Name ); void end(); // average of the part of the name, or none std::optional time( char const *Name ) const; }; cpu_timeline m_cputimeline; // gpu time of the light cluster compute passes; the debug panel shows it once a pass measures it gl::gpu_timer m_clusterstimer; // gpu time of assigning the lights to the clusters, done every frame gl::gpu_timer m_cullstimer; // headlights placed in the light buffer by the last Update_Lights(), and those which are on but did not fit in it uint32_t m_lights_lit = 0; uint32_t m_lights_dropped = 0; // lights of models and vehicles and the cab lamp, gathered once per frame for the light clusters gfx::local_lights m_locallights; // the ambient light of the light buffer while the cab is lit (walk_ambient_save()) packed_vec3 m_cabambient { 0.f }; // cube shadow maps of the lamps opengl46_local_shadows m_localshadows; // the shadow pass draws to a cube shadow map, with the programs of m_localshadows bool m_cubeshadows = false; // 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; glm::mat4 perspective_projection_raw(float fovy, float aspect, float znear, float zfar); glm::mat4 perspective_projection(const viewport_proj_config &c, float n, float f, glm::mat4 &frustum); glm::mat4 ortho_projection(float left, float right, float bottom, float top, float z_near, float z_far); glm::mat4 ortho_frustumtest_projection(float left, float right, float bottom, float top, float z_near, float z_far); void editor_ortho_projection(viewport_config const &Viewport, float const Zfar, glm::mat4 &Projection, glm::mat4 &Frustum); std::vector> m_control_pick_requests; std::vector> m_node_pick_requests; std::unique_ptr m_vertex_shader; std::unique_ptr scene_ubo; // the model block of every draw, streamed (Global.gfx_bufferstreaming): each update to its own place in the buffer of // the frame instead of glBufferSubData of the one block std::unique_ptr model_ubo; std::unique_ptr light_ssbo; // per-instance modelview matrices of GPU-instanced draws, one buffer for each of the last frames, used in turn so // a frame doesn't write to the buffer the gpu may still read for an earlier one. element 0 of each is identity // and never overwritten: draws which aren't instanced (gl_BaseInstance == gl_InstanceID == 0) compute // identity*modelview in the vertex shader. Render_Instanced and Render_Sleepers append the camera-space root // matrices of their instances past the ones placed earlier in the frame, and draw them from that base instance. // persistently mapped with Global.gfx_bufferstreaming std::unique_ptr m_instancestream; gl::scene_ubs scene_ubs; gl::light_data light_data; std::unordered_map> m_shaders; std::unique_ptr m_line_shader; gfx::geometrybank_handle m_editor_overlay_bank; std::vector m_editor_overlay_geometry; unsigned int m_editor_overlay_revision{0}; std::unique_ptr m_freespot_shader; std::unique_ptr m_billboard_shader; std::unique_ptr m_celestial_shader; std::unique_ptr m_hiddenarea_shader; std::unique_ptr m_copy_shader; std::unique_ptr m_cluster_build_shader; std::unique_ptr m_cluster_cull_shader; std::unique_ptr m_cluster_heatmap_shader; std::unique_ptr m_empty_vao; std::vector> m_viewports; std::unique_ptr m_pfx_motionblur; std::unique_ptr m_pfx_taa; gfx::temporal_aa m_taa; std::unique_ptr m_pfx_tonemapping; std::unique_ptr m_pfx_chromaticaberration; // postfx ssao std::unique_ptr m_pfx_ssao; std::unique_ptr m_pfx_ssao_blur; std::unique_ptr m_ssao_noise_tex; glm::vec3 m_ssao_kernel[32]; std::unique_ptr m_shadow_shader; std::unique_ptr m_alpha_shadow_shader; std::unique_ptr m_pick_fb; std::unique_ptr m_pick_tex; std::unique_ptr m_pick_rb; std::unique_ptr m_pick_surface_shader; std::unique_ptr m_pick_shader; std::unique_ptr m_empty_cubemap; std::unique_ptr m_env_fb; std::unique_ptr m_env_rb; std::unique_ptr m_env_tex; std::unique_ptr m_shadow_fb; std::unique_ptr m_shadow_tex; std::unique_ptr m_shadow_empty_tex; // what the shadow sampler reads when there is no shadow map in place std::unique_ptr m_picking_pbo; std::unique_ptr m_picking_node_pbo; std::unique_ptr m_depth_pointer_pbo; std::unique_ptr m_depth_pointer_fb; std::unique_ptr m_depth_pointer_rb; material_handle m_invalid_material; bool m_blendingenabled; struct headlight_config_s { float in_cutoff = 1.005f; float out_cutoff = 0.993f; float falloff_linear = 0.15f; float falloff_quadratic = 0.15f; float intensity = 1.0f; float ambient = 0.0f; }; headlight_config_s headlight_config; std::unique_ptr vr; bool debug_ui_active = false; // offscreen targets of model previews struct preview_targets { int size { 0 }; std::unique_ptr hdr_tex; std::unique_ptr hdr_rbds; // depth and stencil, the stencil marks pixels covered by the model std::unique_ptr hdr_fb; std::unique_ptr ldr_tex; std::unique_ptr ldr_fb; } m_preview; static bool renderer_register; class opengl46_imgui_renderer : public imgui_renderer { virtual bool Init() override; virtual void Shutdown() override; virtual void BeginFrame() override; virtual void Render() override; using imgui_renderer::Create_Image; virtual std::uint64_t Create_Image(std::uint8_t const *Rgba, int const Width, int const Height, image_options const &Options) override; virtual std::uint64_t Update_Image(std::uint64_t const Image, std::uint8_t const *Rgba, int const Width, int const Height, image_options const &Options) override; virtual void Release_Image(std::uint64_t const Image) override; } m_imgui_renderer; virtual imgui_renderer* GetImguiRenderer() override { return &m_imgui_renderer; } }; //---------------------------------------------------------------------------