mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-30 08:30:28 +01:00
Merge branch 'master' into fix/sonar-reliability-security
This commit is contained in:
41
rendering/editoroverlay.h
Normal file
41
rendering/editoroverlay.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
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 <vector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
// flat coloured geometry the editor shows in the 3d view, drawn with the depth test over the translucent scenery
|
||||
struct editor_overlay
|
||||
{
|
||||
struct batch
|
||||
{
|
||||
glm::vec4 color{1.0f};
|
||||
unsigned int type{0}; // GL primitive
|
||||
std::vector<glm::vec3> points; // relative to the origin
|
||||
};
|
||||
glm::dvec3 origin{0.0};
|
||||
std::vector<batch> batches;
|
||||
unsigned int revision{0}; // changed with every new content
|
||||
|
||||
void clear()
|
||||
{
|
||||
batches.clear();
|
||||
++revision;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
inline gfx::editor_overlay EditorOverlay;
|
||||
@@ -300,8 +300,8 @@ geometry_bank::replace( gfx::vertex_array &Vertices, userdata_array &Userdata, g
|
||||
// TBD: we could bail out with an error instead if such request occurs
|
||||
chunk.vertices.resize( Offset + Vertices.size(), gfx::basic_vertex() );
|
||||
chunk.userdata.resize( Offset + Userdata.size(), gfx::vertex_userdata() );
|
||||
chunk.vertices.insert( std::end( chunk.vertices ), std::begin( Vertices ), std::end( Vertices ) );
|
||||
chunk.userdata.insert( std::end( chunk.userdata ), std::begin( Userdata ), std::end( Userdata ) );
|
||||
std::copy( std::begin( Vertices ), std::end( Vertices ), std::begin( chunk.vertices ) + Offset );
|
||||
std::copy( std::begin( Userdata ), std::end( Userdata ), std::begin( chunk.userdata ) + Offset );
|
||||
}
|
||||
// template method implementation
|
||||
replace_( Geometry );
|
||||
@@ -332,6 +332,13 @@ geometry_bank::draw_instanced( gfx::geometry_handle const &Geometry, gfx::stream
|
||||
return draw_instanced_( Geometry, Units, InstanceCount, Streams );
|
||||
}
|
||||
|
||||
// draws geometry stored in supplied sequence of chunks of the bank
|
||||
std::size_t
|
||||
geometry_bank::draw_batch( gfx::geometry_handle const *First, gfx::geometry_handle const *Last, gfx::stream_units const &Units, unsigned int const Streams ) {
|
||||
// template method implementation
|
||||
return draw_batch_( First, Last, Units, Streams );
|
||||
}
|
||||
|
||||
// frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time
|
||||
void
|
||||
geometry_bank::release() {
|
||||
@@ -436,6 +443,32 @@ geometrybank_manager::draw_instanced( gfx::geometry_handle const &Geometry, std:
|
||||
m_primitivecount += bankrecord.first->draw_instanced( Geometry, m_units, InstanceCount, Streams );
|
||||
}
|
||||
|
||||
// draws geometry stored in supplied sequence of chunks
|
||||
void
|
||||
geometrybank_manager::draw_batch( gfx::geometry_handle const *First, gfx::geometry_handle const *Last, unsigned int const Streams ) {
|
||||
|
||||
while( First != Last ) {
|
||||
|
||||
if( *First == null_handle ) {
|
||||
++First;
|
||||
continue;
|
||||
}
|
||||
// neighbouring chunks stored in the same bank go to the bank together
|
||||
auto const *last { First + 1 };
|
||||
while( ( last != Last )
|
||||
&& ( last->bank == First->bank ) ) {
|
||||
++last;
|
||||
}
|
||||
|
||||
auto &bankrecord = bank( *First );
|
||||
|
||||
bankrecord.second = m_garbagecollector.timestamp();
|
||||
m_primitivecount += bankrecord.first->draw_batch( First, last, m_units, Streams );
|
||||
|
||||
First = last;
|
||||
}
|
||||
}
|
||||
|
||||
// provides direct access to index data of specfied chunk
|
||||
gfx::index_array const &
|
||||
geometrybank_manager::indices( gfx::geometry_handle const &Geometry ) const {
|
||||
|
||||
@@ -121,6 +121,8 @@ public:
|
||||
auto draw( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams = basic_streams ) -> std::size_t;
|
||||
// draws geometry stored in specified chunk N times via glDrawElementsInstanced*
|
||||
auto draw_instanced( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, std::size_t const InstanceCount, unsigned int const Streams = basic_streams ) -> std::size_t;
|
||||
// draws geometry stored in supplied sequence of chunks of the bank, in their order. the bank is free to combine them into fewer draw calls
|
||||
auto draw_batch( gfx::geometry_handle const *First, gfx::geometry_handle const *Last, gfx::stream_units const &Units, unsigned int const Streams = basic_streams ) -> std::size_t;
|
||||
// draws geometry stored in supplied list of chunks
|
||||
template <typename Iterator_>
|
||||
auto draw( Iterator_ First, Iterator_ Last, gfx::stream_units const &Units, unsigned int const Streams = basic_streams ) ->std::size_t {
|
||||
@@ -187,6 +189,11 @@ private:
|
||||
std::size_t count { 0 };
|
||||
for( std::size_t i = 0; i < InstanceCount; ++i ) { count += draw_( Geometry, Units, Streams ); }
|
||||
return count; }
|
||||
// draw_batch() subclass details. Default implementation falls back to regular draw of each chunk.
|
||||
virtual auto draw_batch_( gfx::geometry_handle const *First, gfx::geometry_handle const *Last, gfx::stream_units const &Units, unsigned int const Streams ) -> std::size_t {
|
||||
std::size_t count { 0 };
|
||||
for( ; First != Last; ++First ) { count += draw_( *First, Units, Streams ); }
|
||||
return count; }
|
||||
// resource release subclass details
|
||||
virtual void release_() = 0;
|
||||
};
|
||||
@@ -218,6 +225,8 @@ public:
|
||||
// draws geometry stored in specified chunk InstanceCount times via GPU instancing.
|
||||
// The shader reads per-instance modelview matrices from instance_ubo[gl_InstanceID].
|
||||
void draw_instanced( gfx::geometry_handle const &Geometry, std::size_t const InstanceCount, unsigned int const Streams = basic_streams );
|
||||
// draws geometry stored in supplied sequence of chunks, in their order. neighbouring chunks of the same bank are passed to it together
|
||||
void draw_batch( gfx::geometry_handle const *First, gfx::geometry_handle const *Last, unsigned int const Streams = basic_streams );
|
||||
template <typename Iterator_>
|
||||
void draw( Iterator_ First, Iterator_ Last, unsigned int const Streams = basic_streams ) {
|
||||
while( First != Last ) {
|
||||
|
||||
@@ -259,6 +259,66 @@ opengl33_vaogeometrybank::draw_instanced_( gfx::geometry_handle const &Geometry,
|
||||
return prims * InstanceCount;
|
||||
}
|
||||
|
||||
// draw_batch() subclass details — neighbouring non-indexed chunks of the same type are sent with a single GL call.
|
||||
// The chunks are drawn in the supplied order, same as with a draw_() call for each of them.
|
||||
std::size_t
|
||||
opengl33_vaogeometrybank::draw_batch_( gfx::geometry_handle const *First, gfx::geometry_handle const *Last, gfx::stream_units const &Units, unsigned int const Streams )
|
||||
{
|
||||
std::size_t count { 0 };
|
||||
|
||||
if( !glMultiDrawArrays ) {
|
||||
// GLES context doesn't provide the call
|
||||
for( ; First != Last; ++First ) { count += draw_( *First, Units, Streams ); }
|
||||
return count;
|
||||
}
|
||||
|
||||
setup_buffer();
|
||||
|
||||
std::array<GLint, 64> offsets;
|
||||
std::array<GLsizei, 64> sizes;
|
||||
GLsizei pending { 0 };
|
||||
unsigned int pendingtype { 0 };
|
||||
|
||||
auto const flush = [ & ]() {
|
||||
if( pending == 0 ) { return; }
|
||||
m_vao->bind();
|
||||
if( pending == 1 ) { ::glDrawArrays( pendingtype, offsets[ 0 ], sizes[ 0 ] ); }
|
||||
else { ::glMultiDrawArrays( pendingtype, offsets.data(), sizes.data(), pending ); }
|
||||
pending = 0;
|
||||
};
|
||||
|
||||
for( ; First != Last; ++First ) {
|
||||
|
||||
auto const &chunkrecord = m_chunkrecords.at( First->chunk - 1 );
|
||||
if( ( false == chunkrecord.is_good )
|
||||
|| ( chunkrecord.index_count > 0 ) ) {
|
||||
// chunks which wait for upload of their data and indexed chunks take the regular route
|
||||
flush();
|
||||
count += draw_( *First, Units, Streams );
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const type { gfx::geometry_bank::chunk( *First ).type };
|
||||
if( ( pending == static_cast<GLsizei>( offsets.size() ) )
|
||||
|| ( ( pending > 0 ) && ( type != pendingtype ) ) ) {
|
||||
flush();
|
||||
}
|
||||
pendingtype = type;
|
||||
offsets[ pending ] = static_cast<GLint>( chunkrecord.vertex_offset );
|
||||
sizes[ pending ] = static_cast<GLsizei>( chunkrecord.vertex_count );
|
||||
++pending;
|
||||
|
||||
switch( type ) {
|
||||
case GL_TRIANGLES: { count += chunkrecord.vertex_count / 3; break; }
|
||||
case GL_TRIANGLE_STRIP: { count += ( chunkrecord.vertex_count > 2 ? chunkrecord.vertex_count - 2 : 0 ); break; }
|
||||
default: { break; }
|
||||
}
|
||||
}
|
||||
flush();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// release () subclass details
|
||||
void
|
||||
opengl33_vaogeometrybank::release_() {
|
||||
|
||||
@@ -56,6 +56,9 @@ private:
|
||||
// draw_instanced() subclass details — issues glDrawElementsInstancedBaseVertex
|
||||
auto
|
||||
draw_instanced_( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, std::size_t const InstanceCount, unsigned int const Streams ) -> std::size_t override;
|
||||
// draw_batch() subclass details — issues glMultiDrawArrays for runs of non-indexed chunks of the same type
|
||||
auto
|
||||
draw_batch_( gfx::geometry_handle const *First, gfx::geometry_handle const *Last, gfx::stream_units const &Units, unsigned int const Streams ) -> std::size_t override;
|
||||
// release() subclass details
|
||||
void
|
||||
release_() override;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "gl/glsl_common.h"
|
||||
#include "gl/pbo.h"
|
||||
#include "gl/query.h"
|
||||
#include "rendering/opengl33terrain.h"
|
||||
|
||||
// bare-bones render controller, in lack of anything better yet
|
||||
class opengl33_renderer : public gfx_renderer {
|
||||
@@ -81,6 +82,10 @@ class opengl33_renderer : public gfx_renderer {
|
||||
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<gfx::terrain_layer> const &Layers, int const Samples, std::uint8_t const *Weights, glm::vec3 const &Placement ) override;
|
||||
void
|
||||
Terrain_Release( material_handle const Material ) override;
|
||||
// shader methods
|
||||
auto Fetch_Shader( std::string const &name ) -> std::shared_ptr<gl::program> override;
|
||||
// texture methods
|
||||
@@ -105,6 +110,8 @@ class opengl33_renderer : public gfx_renderer {
|
||||
Pick_Node() const override { return m_picksceneryitem; }
|
||||
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
|
||||
@@ -128,6 +135,7 @@ class opengl33_renderer : public gfx_renderer {
|
||||
std::string const &
|
||||
info_stats() const override;
|
||||
void MakeScreenshot() override;
|
||||
bool Render_Preview( TAnimModel *Instance, int const Size, int const Margin, bool const Shadows, std::vector<std::uint8_t> &Image ) override;
|
||||
|
||||
|
||||
|
||||
@@ -282,6 +290,11 @@ class opengl33_renderer : public gfx_renderer {
|
||||
// instanced_drawcall in draw_stats and contributes Instances.size() to the
|
||||
// instances counter. Instances are still individually frustum/distance culled.
|
||||
void Render_Instanced( TModel3d *Model, std::vector<TAnimModel *> const &Instances );
|
||||
// adds the bucketed instances of a scene cell to the frame-level buckets drawn by Render_Instanced(),
|
||||
// less the ones the copies of their bounds show to be out of sight in the current render pass
|
||||
void Queue_Instances( scene::basic_cell const *Cell );
|
||||
// makes the copies of the bounds of the instances held by a bucket of a scene cell
|
||||
void Update_Instance_Bounds( TModel3d const *Model, scene::basic_cell::instance_bucket const &Bucket );
|
||||
bool Render(TDynamicObject *Dynamic);
|
||||
bool Render(TModel3d *Model, material_data const *Material, float const Squaredistance, glm::dvec3 const &Position, glm::vec3 const &Angle);
|
||||
bool Render(TModel3d *Model, material_data const *Material, float const Squaredistance);
|
||||
@@ -297,6 +310,10 @@ class opengl33_renderer : public gfx_renderer {
|
||||
bool Render_interior( bool const Alpha = false );
|
||||
bool Render_lowpoly( TDynamicObject *Dynamic, float const Squaredistance, bool const Setup, bool const Alpha = false );
|
||||
bool Render_coupler_adapter( TDynamicObject *Dynamic, float const Squaredistance, int const End, bool const Alpha = false );
|
||||
// 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<texture_handle> const &Model_Textures( TModel3d *Model );
|
||||
void Render(TMemCell *Memcell);
|
||||
void Render_particles();
|
||||
void Render_precipitation();
|
||||
@@ -306,6 +323,7 @@ class opengl33_renderer : public gfx_renderer {
|
||||
void Render_Alpha(TAnimModel *Instance);
|
||||
void Render_Alpha(TTraction *Traction);
|
||||
void Render_Alpha(scene::lines_node const &Lines);
|
||||
void Render_editor_overlay();
|
||||
bool Render_Alpha(TDynamicObject *Dynamic);
|
||||
bool Render_Alpha(TModel3d *Model, material_data const *Material, float const Squaredistance, glm::dvec3 const &Position, glm::vec3 const &Angle);
|
||||
bool Render_Alpha(TModel3d *Model, material_data const *Material, float const Squaredistance);
|
||||
@@ -322,11 +340,22 @@ class opengl33_renderer : public gfx_renderer {
|
||||
|
||||
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;
|
||||
opengl33_terrain_materials m_terrainmaterials{m_materials, m_textures};
|
||||
opengl33_light m_sunlight;
|
||||
opengllight_array m_lights;
|
||||
/*
|
||||
@@ -344,6 +373,8 @@ class opengl33_renderer : public gfx_renderer {
|
||||
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
|
||||
opengl33_skydome m_skydomerenderer;
|
||||
@@ -379,6 +410,13 @@ class opengl33_renderer : public gfx_renderer {
|
||||
// of once per cell -- collapsing many tiny instanced draws into a few large
|
||||
// batches. Reused every pass; the vectors are emptied (not freed) at the top of Render(scene::basic_region*).
|
||||
scene::basic_cell::instance_bucket_map m_frame_instance_buckets;
|
||||
// textures used by vehicle models, for the release of far textures
|
||||
std::unordered_map<TModel3d const *, std::vector<texture_handle>> m_modeltextures;
|
||||
// geometry of neighbouring tracks of a cell which share the material, gathered to be drawn together. reused by each Render(path_sequence)
|
||||
std::vector<gfx::geometrybank_handle> m_pathbatch;
|
||||
// copies of instance bounds kept by the buckets of scene cells are good as long as they carry this number.
|
||||
// it changes with each frame drawn with the scenery editor active, as the editor moves and resizes instances in place
|
||||
unsigned int m_instanceboundsversion { 1 };
|
||||
renderpass_config m_colorpass; // parametrs of most recent color pass
|
||||
std::array<renderpass_config, 3> m_shadowpass; // parametrs of most recent shadowmap pass for each of csm stages
|
||||
std::vector<TSubModel const *> m_pickcontrolsitems;
|
||||
@@ -387,6 +425,7 @@ class opengl33_renderer : public gfx_renderer {
|
||||
std::vector<scene::basic_node *> m_picksceneryitems;
|
||||
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
|
||||
@@ -400,6 +439,7 @@ class opengl33_renderer : public gfx_renderer {
|
||||
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<std::function<void(TSubModel const *, glm::vec2)>> m_control_pick_requests;
|
||||
std::vector<std::function<void(scene::basic_node *)>> m_node_pick_requests;
|
||||
@@ -449,6 +489,9 @@ class opengl33_renderer : public gfx_renderer {
|
||||
std::unordered_map<std::string, std::shared_ptr<gl::program>> m_shaders;
|
||||
|
||||
std::unique_ptr<gl::program> m_line_shader;
|
||||
gfx::geometrybank_handle m_editor_overlay_bank;
|
||||
std::vector<gfx::geometry_handle> m_editor_overlay_geometry;
|
||||
unsigned int m_editor_overlay_revision{0};
|
||||
std::unique_ptr<gl::program> m_freespot_shader;
|
||||
std::unique_ptr<gl::program> m_billboard_shader;
|
||||
std::unique_ptr<gl::program> m_celestial_shader;
|
||||
@@ -521,6 +564,19 @@ class opengl33_renderer : public gfx_renderer {
|
||||
std::unique_ptr<vr_interface> vr;
|
||||
bool debug_ui_active = false;
|
||||
|
||||
// offscreen targets of model previews
|
||||
struct preview_targets
|
||||
{
|
||||
int size { 0 };
|
||||
std::unique_ptr<opengl_texture> hdr_tex;
|
||||
std::unique_ptr<gl::renderbuffer> hdr_rbds; // depth and stencil, the stencil marks pixels covered by the model
|
||||
std::unique_ptr<gl::framebuffer> hdr_fb;
|
||||
std::unique_ptr<opengl_texture> ldr_tex;
|
||||
std::unique_ptr<gl::framebuffer> ldr_fb;
|
||||
} m_preview;
|
||||
// set while a preview is drawn: submodels faded out entirely are skipped, instead of being drawn invisible over the stencil
|
||||
bool m_previewing { false };
|
||||
|
||||
static bool renderer_register;
|
||||
|
||||
class opengl33_imgui_renderer : public imgui_renderer
|
||||
@@ -529,6 +585,8 @@ class opengl33_renderer : public gfx_renderer {
|
||||
virtual void Shutdown() override;
|
||||
virtual void BeginFrame() override;
|
||||
virtual void Render() override;
|
||||
virtual std::uint64_t Create_Image(std::uint8_t const *Rgba, int const Width, int const Height) override;
|
||||
virtual void Release_Image(std::uint64_t const Image) override;
|
||||
} m_imgui_renderer;
|
||||
|
||||
virtual imgui_renderer* GetImguiRenderer() override {
|
||||
|
||||
440
rendering/opengl33terrain.cpp
Normal file
440
rendering/opengl33terrain.cpp
Normal file
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
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/opengl33terrain.h"
|
||||
|
||||
#include "rendering/opengl33geometrybank.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::size_t constexpr max_layers{8};
|
||||
int constexpr initial_capacity{8};
|
||||
int constexpr copy_attempts{600}; // frames to wait for the textures of a layer to load before it's left with what it has
|
||||
float constexpr default_repeat{8.f};
|
||||
|
||||
// array textures: diffuse, normal maps, specular/gloss maps
|
||||
std::array<int, 3> constexpr array_sizes{1024, 512, 256};
|
||||
|
||||
void bind_for_upload(opengl_texture const &Texture)
|
||||
{
|
||||
::glActiveTexture(GL_TEXTURE0);
|
||||
::glBindTexture(Texture.target, Texture.id);
|
||||
}
|
||||
|
||||
void set_anisotropy(GLenum const Target)
|
||||
{
|
||||
if (Global.AnisotropicFiltering >= 0 && (GLAD_GL_EXT_texture_filter_anisotropic || GLAD_GL_ARB_texture_filter_anisotropic))
|
||||
::glTexParameterf(Target, GL_TEXTURE_MAX_ANISOTROPY_EXT, Global.AnisotropicFiltering);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool opengl33_terrain_materials::init()
|
||||
{
|
||||
if (m_state != 0)
|
||||
return m_state > 0;
|
||||
m_state = -1;
|
||||
try
|
||||
{
|
||||
m_copy = std::make_unique<gl::postfx>(gl::shader("copy.frag"));
|
||||
}
|
||||
catch (gl::shader_exception const &e)
|
||||
{
|
||||
ErrorLog("heightmap terrain: can't use the copy shader: " + std::string(e.what()));
|
||||
return false;
|
||||
}
|
||||
m_framebuffer = std::make_unique<gl::framebuffer>();
|
||||
|
||||
m_white = make_texture("internal_src:terrain/white", GL_TEXTURE_2D);
|
||||
{
|
||||
auto const &white{m_textures.texture(m_white)};
|
||||
std::uint8_t const texel[4]{255, 255, 255, 255};
|
||||
bind_for_upload(white);
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texel);
|
||||
}
|
||||
char const *names[]{"internal_src:terrain/layers_diffuse", "internal_src:terrain/layers_normal", "internal_src:terrain/layers_specgloss"};
|
||||
for (std::size_t i = 0; i < m_arrays.size(); ++i)
|
||||
{
|
||||
auto &array{m_arrays[i]};
|
||||
array.handle = make_texture(names[i], GL_TEXTURE_2D_ARRAY);
|
||||
array.size = array_sizes[i];
|
||||
array.srgb = (i == 0);
|
||||
array.format = (array.srgb ? GL_SRGB8_ALPHA8 : GL_RGBA8);
|
||||
make_storage(array, initial_capacity);
|
||||
}
|
||||
m_capacity = initial_capacity;
|
||||
opengl_texture::reset_unit_cache();
|
||||
|
||||
// the shader is checked now, so a broken one makes the terrain fall back on plain materials instead of the failure indicator
|
||||
try
|
||||
{
|
||||
GfxRenderer->Fetch_Shader("terrain");
|
||||
}
|
||||
catch (gl::shader_exception const &e)
|
||||
{
|
||||
ErrorLog("heightmap terrain: can't use the terrain shader: " + std::string(e.what()));
|
||||
return false;
|
||||
}
|
||||
m_state = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
texture_handle opengl33_terrain_materials::make_texture(std::string const &Name, GLenum const Target)
|
||||
{
|
||||
auto const handle{m_textures.create(Name, false, GL_RGBA)};
|
||||
auto &texture{m_textures.texture(handle)};
|
||||
if (texture.id == opengl_texture::invalid_id)
|
||||
::glGenTextures(1, &texture.id);
|
||||
texture.target = Target;
|
||||
// the content is supplied here, the texture manager isn't to load, release or replace it
|
||||
texture.is_ready = true;
|
||||
texture.is_static = true;
|
||||
texture.is_rendertarget = true;
|
||||
texture.is_texstub = false;
|
||||
texture.data_state = resource_state::good;
|
||||
return handle;
|
||||
}
|
||||
|
||||
void opengl33_terrain_materials::make_storage(texture_array &Array, int const Layers)
|
||||
{
|
||||
auto &texture{m_textures.texture(Array.handle)};
|
||||
bind_for_upload(texture);
|
||||
::glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
::glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
::glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
::glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
set_anisotropy(GL_TEXTURE_2D_ARRAY);
|
||||
::glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, Array.format, Array.size, Array.size, Layers, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
texture.data_width = Array.size;
|
||||
texture.data_height = Array.size;
|
||||
texture.layers = Layers;
|
||||
texture.data_format = Array.format;
|
||||
texture.data_components = GL_RGBA;
|
||||
}
|
||||
|
||||
int opengl33_terrain_materials::layer_index(gfx::terrain_layer const &Layer)
|
||||
{
|
||||
for (auto const &existing : m_layers)
|
||||
if (existing.material == Layer.material && existing.size == Layer.size)
|
||||
return existing.index;
|
||||
|
||||
layer entry;
|
||||
entry.material = Layer.material;
|
||||
entry.size = Layer.size;
|
||||
entry.index = static_cast<int>(m_layers.size());
|
||||
auto const &material{m_materials.material(Layer.material)};
|
||||
entry.repeat = (Layer.size > 0.f ? Layer.size : (material.size.x > 0.f ? material.size.x : default_repeat));
|
||||
if (material.shader)
|
||||
{
|
||||
entry.normalmap = material.shader->texture_conf.count("normalmap") > 0;
|
||||
entry.specgloss = material.shader->texture_conf.count("specgloss") > 0;
|
||||
}
|
||||
entry.reflection = (std::isfinite(material.params[1].z) ? material.params[1].z : 0.f);
|
||||
m_layers.push_back(entry);
|
||||
m_pending = true;
|
||||
|
||||
if (static_cast<int>(m_layers.size()) > m_capacity)
|
||||
{
|
||||
// more room: the arrays are made anew, and every layer copied again
|
||||
auto const capacity{m_capacity * 2};
|
||||
for (auto &array : m_arrays)
|
||||
{
|
||||
auto &texture{m_textures.texture(array.handle)};
|
||||
::glDeleteTextures(1, &texture.id);
|
||||
::glGenTextures(1, &texture.id);
|
||||
make_storage(array, capacity);
|
||||
}
|
||||
m_capacity = capacity;
|
||||
for (auto &existing : m_layers)
|
||||
{
|
||||
existing.copied = false;
|
||||
existing.attempts = 0;
|
||||
}
|
||||
opengl_texture::reset_unit_cache();
|
||||
}
|
||||
return entry.index;
|
||||
}
|
||||
|
||||
std::size_t opengl33_terrain_materials::acquire_slot(material_handle const Reuse)
|
||||
{
|
||||
if (Reuse != null_handle)
|
||||
{
|
||||
auto const lookup{m_slotof.find(Reuse)};
|
||||
if (lookup != m_slotof.end())
|
||||
{
|
||||
m_slots[lookup->second].used = true;
|
||||
return lookup->second;
|
||||
}
|
||||
}
|
||||
if (false == m_free.empty())
|
||||
{
|
||||
auto const index{m_free.back()};
|
||||
m_free.pop_back();
|
||||
m_slots[index].used = true;
|
||||
return index;
|
||||
}
|
||||
auto const index{m_slots.size()};
|
||||
auto const suffix{std::to_string(index)};
|
||||
slot entry;
|
||||
entry.weights[0] = make_texture("internal_src:terrain/weights0_" + suffix, GL_TEXTURE_2D);
|
||||
entry.weights[1] = make_texture("internal_src:terrain/weights1_" + suffix, GL_TEXTURE_2D);
|
||||
entry.table = make_texture("internal_src:terrain/table_" + suffix, GL_TEXTURE_2D);
|
||||
// the textures get their content before the material is made of them, which checks they're there
|
||||
std::uint8_t const blank[4]{0, 0, 0, 0};
|
||||
for (auto const handle : entry.weights)
|
||||
{
|
||||
bind_for_upload(m_textures.texture(handle));
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, blank);
|
||||
::glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
{
|
||||
bind_for_upload(m_textures.texture(entry.table));
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
std::array<float, max_layers * 2 * 4> const zeros{};
|
||||
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, static_cast<GLsizei>(max_layers), 2, 0, GL_RGBA, GL_FLOAT, zeros.data());
|
||||
}
|
||||
opengl_texture::reset_unit_cache();
|
||||
auto const name = [&](texture_handle const Handle) { return "internal_src:" + m_textures.texture(Handle).name; };
|
||||
std::string const definition{
|
||||
"shader: terrain\n"
|
||||
"texture1: " + name(m_white) + "\n"
|
||||
"texture2: " + name(entry.weights[0]) + "\n"
|
||||
"texture3: " + name(entry.weights[1]) + "\n"
|
||||
"texture4: " + name(m_arrays[0].handle) + "\n"
|
||||
"texture5: " + name(m_arrays[1].handle) + "\n"
|
||||
"texture6: " + name(m_arrays[2].handle) + "\n"
|
||||
"texture7: " + name(entry.table) + "\n"};
|
||||
entry.material = m_materials.create_from_text("internal_src:terrain/chunk_" + suffix, definition);
|
||||
if (entry.material == null_handle || false == m_materials.material(entry.material).is_good)
|
||||
{
|
||||
ErrorLog("heightmap terrain: can't make the material of a chunk");
|
||||
m_state = -1;
|
||||
return static_cast<std::size_t>(-1);
|
||||
}
|
||||
entry.used = true;
|
||||
m_slots.push_back(entry);
|
||||
m_slotof.emplace(entry.material, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
material_handle opengl33_terrain_materials::make(material_handle const Reuse, std::vector<gfx::terrain_layer> const &Layers, int const Samples, std::uint8_t const *Weights, glm::vec3 const &Placement)
|
||||
{
|
||||
if (Layers.empty() || false == init())
|
||||
return null_handle;
|
||||
auto const count{std::min(Layers.size(), max_layers)};
|
||||
std::array<int, max_layers> indices{};
|
||||
for (std::size_t i = 0; i < count; ++i)
|
||||
{
|
||||
if (Layers[i].material == null_handle)
|
||||
return null_handle;
|
||||
indices[i] = layer_index(Layers[i]);
|
||||
}
|
||||
auto const slotindex{acquire_slot(Reuse)};
|
||||
if (slotindex == static_cast<std::size_t>(-1))
|
||||
return null_handle;
|
||||
auto &entry{m_slots[slotindex]};
|
||||
|
||||
// weights, four layers to a texture
|
||||
auto const painted{Weights != nullptr && Samples > 0 && count > 1};
|
||||
auto const side{painted ? Samples + 1 : 1};
|
||||
auto const samples{static_cast<std::size_t>(side) * side};
|
||||
std::vector<std::uint8_t> texels(samples * 4);
|
||||
for (std::size_t texture = 0; texture < 2; ++texture)
|
||||
{
|
||||
std::fill(texels.begin(), texels.end(), 0);
|
||||
for (std::size_t channel = 0; channel < 4; ++channel)
|
||||
{
|
||||
auto const layer{texture * 4 + channel};
|
||||
if (layer >= count)
|
||||
break;
|
||||
if (false == painted)
|
||||
{
|
||||
if (layer == 0)
|
||||
texels[channel] = 255;
|
||||
continue;
|
||||
}
|
||||
auto const *source{Weights + layer * samples};
|
||||
for (std::size_t i = 0; i < samples; ++i)
|
||||
texels[i * 4 + channel] = source[i];
|
||||
}
|
||||
bind_for_upload(m_textures.texture(entry.weights[texture]));
|
||||
::glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, side, side, 0, GL_RGBA, GL_UNSIGNED_BYTE, texels.data());
|
||||
::glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
// what the layers are: index in the arrays, repeat, which maps they have, reflection
|
||||
std::array<float, max_layers * 2 * 4> table{};
|
||||
for (std::size_t i = 0; i < count; ++i)
|
||||
{
|
||||
auto const &layer{m_layers[indices[i]]};
|
||||
table[i * 4 + 0] = static_cast<float>(layer.index);
|
||||
table[i * 4 + 1] = 1.f / std::max(0.01f, layer.repeat);
|
||||
table[i * 4 + 2] = layer.normalmap ? 1.f : 0.f;
|
||||
table[i * 4 + 3] = layer.specgloss ? 1.f : 0.f;
|
||||
table[(max_layers + i) * 4 + 0] = layer.reflection;
|
||||
// the textures turned by the layer of the chunk (the same material can be turned differently in another layer)
|
||||
auto const angle{glm::radians(static_cast<double>(Layers[i].rotation))};
|
||||
table[(max_layers + i) * 4 + 1] = static_cast<float>(std::cos(angle));
|
||||
table[(max_layers + i) * 4 + 2] = static_cast<float>(std::sin(angle));
|
||||
}
|
||||
bind_for_upload(m_textures.texture(entry.table));
|
||||
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, static_cast<GLsizei>(max_layers), 2, 0, GL_RGBA, GL_FLOAT, table.data());
|
||||
opengl_texture::reset_unit_cache();
|
||||
|
||||
auto &material{m_materials.material(entry.material)};
|
||||
material.params[2] = glm::vec4(Placement.x, Placement.y, 1.f / std::max(1.f, Placement.z), static_cast<float>(side - 1));
|
||||
return entry.material;
|
||||
}
|
||||
|
||||
void opengl33_terrain_materials::release(material_handle const Material)
|
||||
{
|
||||
auto const lookup{m_slotof.find(Material)};
|
||||
if (lookup == m_slotof.end() || false == m_slots[lookup->second].used)
|
||||
return;
|
||||
m_slots[lookup->second].used = false;
|
||||
m_free.push_back(lookup->second);
|
||||
}
|
||||
|
||||
bool opengl33_terrain_materials::copy_layer(layer &Layer)
|
||||
{
|
||||
auto const &material{m_materials.material(Layer.material)};
|
||||
auto const slot = [&](char const *Name, int const Fallback) {
|
||||
if (material.shader)
|
||||
{
|
||||
auto const lookup{material.shader->texture_conf.find(Name)};
|
||||
if (lookup != material.shader->texture_conf.end())
|
||||
return static_cast<int>(lookup->second.id);
|
||||
return -1;
|
||||
}
|
||||
return Fallback;
|
||||
};
|
||||
std::array<int, 3> const slots{slot("diffuse", 0), Layer.normalmap ? slot("normalmap", 1) : -1, Layer.specgloss ? slot("specgloss", 2) : -1};
|
||||
// all maps of the layer have to be ready, so they go together
|
||||
std::array<opengl_texture *, 3> sources{};
|
||||
for (std::size_t i = 0; i < slots.size(); ++i)
|
||||
{
|
||||
if (slots[i] < 0 || material.textures[slots[i]] == null_handle)
|
||||
continue;
|
||||
auto &texture{m_textures.mark_as_used(material.textures[slots[i]])};
|
||||
if (false == texture.is_ready)
|
||||
{
|
||||
// a texture which is on the card already may have dropped its data after the upload (create() would refuse it then),
|
||||
// one released by the sweep or never read has to be read first
|
||||
if (texture.data_state == resource_state::none)
|
||||
{
|
||||
texture.reload_on_use = false;
|
||||
texture.load();
|
||||
}
|
||||
if (texture.data_state == resource_state::failed)
|
||||
continue;
|
||||
if (false == texture.create())
|
||||
{
|
||||
if (++Layer.attempts < copy_attempts)
|
||||
return false;
|
||||
WriteLog("heightmap terrain: texture \"" + texture.name + "\" isn't ready, the terrain goes without it");
|
||||
continue; // waited long enough, the layer goes without it
|
||||
}
|
||||
}
|
||||
sources[i] = &texture;
|
||||
}
|
||||
// defaults for maps the layer doesn't have: grey, flat, without specular reflections
|
||||
std::array<std::array<float, 4>, 3> const defaults{{{0.5f, 0.5f, 0.5f, 1.f}, {0.5f, 0.5f, 1.f, 1.f}, {0.f, 1.f, 0.f, 1.f}}};
|
||||
for (std::size_t i = 0; i < m_arrays.size(); ++i)
|
||||
{
|
||||
auto const &array{m_arrays[i]};
|
||||
auto &target{m_textures.texture(array.handle)};
|
||||
m_framebuffer->attach(target, GL_COLOR_ATTACHMENT0, Layer.index);
|
||||
m_framebuffer->setup_drawing(1);
|
||||
::glViewport(0, 0, array.size, array.size);
|
||||
if (sources[i] == nullptr)
|
||||
{
|
||||
::glClearBufferfv(GL_COLOR, 0, defaults[i].data());
|
||||
continue;
|
||||
}
|
||||
if (false == Global.gfx_usegles)
|
||||
{
|
||||
if (array.srgb)
|
||||
::glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
else
|
||||
::glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
m_copy->apply(*sources[i], m_framebuffer.get());
|
||||
}
|
||||
Layer.copied = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void opengl33_terrain_materials::update()
|
||||
{
|
||||
if (false == m_pending || m_state <= 0)
|
||||
return;
|
||||
|
||||
auto const blend{::glIsEnabled(GL_BLEND)};
|
||||
auto const scissor{::glIsEnabled(GL_SCISSOR_TEST)};
|
||||
auto const cull{::glIsEnabled(GL_CULL_FACE)};
|
||||
auto const depth{::glIsEnabled(GL_DEPTH_TEST)};
|
||||
auto const srgb{(false == Global.gfx_usegles) && ::glIsEnabled(GL_FRAMEBUFFER_SRGB)};
|
||||
::glDisable(GL_BLEND);
|
||||
::glDisable(GL_SCISSOR_TEST);
|
||||
::glDisable(GL_CULL_FACE);
|
||||
|
||||
bool copied{false};
|
||||
m_pending = false;
|
||||
for (auto &layer : m_layers)
|
||||
{
|
||||
if (layer.copied)
|
||||
continue;
|
||||
if (copy_layer(layer))
|
||||
copied = true;
|
||||
else
|
||||
m_pending = true;
|
||||
}
|
||||
if (copied)
|
||||
{
|
||||
for (auto const &array : m_arrays)
|
||||
{
|
||||
bind_for_upload(m_textures.texture(array.handle));
|
||||
::glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
// leave the state as the rest of the renderer knows it
|
||||
gl::framebuffer::unbind();
|
||||
if (blend)
|
||||
::glEnable(GL_BLEND);
|
||||
if (scissor)
|
||||
::glEnable(GL_SCISSOR_TEST);
|
||||
if (cull)
|
||||
::glEnable(GL_CULL_FACE);
|
||||
if (depth)
|
||||
::glEnable(GL_DEPTH_TEST);
|
||||
else
|
||||
::glDisable(GL_DEPTH_TEST);
|
||||
if (false == Global.gfx_usegles)
|
||||
{
|
||||
if (srgb)
|
||||
::glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
else
|
||||
::glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
::glDepthMask(GL_TRUE);
|
||||
opengl_texture::reset_unit_cache();
|
||||
gfx::opengl33_vaogeometrybank::reset();
|
||||
}
|
||||
85
rendering/opengl33terrain.h
Normal file
85
rendering/opengl33terrain.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
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 <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "rendering/renderer.h"
|
||||
#include "model/material.h"
|
||||
#include "model/Texture.h"
|
||||
#include "gl/framebuffer.h"
|
||||
#include "gl/postfx.h"
|
||||
|
||||
// materials of the heightmap terrain for the opengl 3.3 renderer (shader mat_terrain).
|
||||
// the textures of the layer materials are copied into texture arrays shared by all chunks; each chunk gets a material
|
||||
// of its own, with textures holding the weights of its layers and a table of what its layers are
|
||||
class opengl33_terrain_materials
|
||||
{
|
||||
public:
|
||||
opengl33_terrain_materials(material_manager &Materials, texture_manager &Textures) : m_materials(Materials), m_textures(Textures) {}
|
||||
|
||||
// see gfx_renderer::Terrain_Material. returns null_handle if the terrain shader can't be used
|
||||
material_handle make(material_handle const Reuse, std::vector<gfx::terrain_layer> const &Layers, int const Samples, std::uint8_t const *Weights, glm::vec3 const &Placement);
|
||||
void release(material_handle const Material);
|
||||
// copies the textures of new layers into the arrays; to be called before the frame is drawn
|
||||
void update();
|
||||
|
||||
private:
|
||||
struct layer
|
||||
{
|
||||
material_handle material{null_handle};
|
||||
float size{0.f}; // as given by the chunk
|
||||
float repeat{8.f}; // metres the textures are repeated at
|
||||
int index{0}; // in the arrays
|
||||
bool normalmap{false};
|
||||
bool specgloss{false};
|
||||
float reflection{0.f};
|
||||
bool copied{false};
|
||||
int attempts{0};
|
||||
};
|
||||
struct texture_array
|
||||
{
|
||||
texture_handle handle{null_handle};
|
||||
int size{0};
|
||||
GLint format{0};
|
||||
bool srgb{false};
|
||||
};
|
||||
struct slot
|
||||
{
|
||||
material_handle material{null_handle};
|
||||
std::array<texture_handle, 2> weights{null_handle, null_handle};
|
||||
texture_handle table{null_handle};
|
||||
bool used{false};
|
||||
};
|
||||
|
||||
bool init();
|
||||
texture_handle make_texture(std::string const &Name, GLenum const Target);
|
||||
void make_storage(texture_array &Array, int const Layers);
|
||||
int layer_index(gfx::terrain_layer const &Layer);
|
||||
bool copy_layer(layer &Layer);
|
||||
std::size_t acquire_slot(material_handle const Reuse);
|
||||
|
||||
material_manager &m_materials;
|
||||
texture_manager &m_textures;
|
||||
int m_state{0}; // 0: not set up yet, 1: ready, -1: can't be used
|
||||
texture_handle m_white{null_handle};
|
||||
std::array<texture_array, 3> m_arrays; // diffuse, normal maps, specular/gloss
|
||||
int m_capacity{0}; // layers the arrays have room for
|
||||
std::vector<layer> m_layers;
|
||||
bool m_pending{false};
|
||||
std::vector<slot> m_slots;
|
||||
std::vector<std::size_t> m_free;
|
||||
std::unordered_map<material_handle, std::size_t> m_slotof;
|
||||
std::unique_ptr<gl::postfx> m_copy;
|
||||
std::unique_ptr<gl::framebuffer> m_framebuffer;
|
||||
};
|
||||
@@ -33,6 +33,28 @@ int constexpr EU07_REFLECTIONFIDELITYOFFSET { 250 }; // artificial increase of r
|
||||
|
||||
namespace {
|
||||
|
||||
float
|
||||
editor_track_view_range(float const Range, bool const Apply)
|
||||
{
|
||||
if( false == Apply || false == Global.editor_tracks )
|
||||
return Range;
|
||||
float extra = 50000.0f;
|
||||
if( Global.EditorOrtho )
|
||||
extra = Global.EditorOrthoExtent * 3.0f;
|
||||
return Range > extra ? Range : extra;
|
||||
}
|
||||
|
||||
bool
|
||||
editor_far_tracks_only(bool const Apply, scene::basic_cell const *Cell, glm::dvec3 const &Camera, double const SceneryLimit)
|
||||
{
|
||||
if( false == Apply )
|
||||
return false;
|
||||
double nearest = glm::length( Cell->m_area.center - Camera ) - static_cast<double>( Cell->m_area.radius );
|
||||
if( nearest < 0.0 )
|
||||
nearest = 0.0;
|
||||
return nearest > SceneryLimit;
|
||||
}
|
||||
|
||||
// returns material assigned to the submodel, resolving replacable skins. null_handle if no skin set is bound
|
||||
material_handle
|
||||
submodel_material( TSubModel const *Submodel ) {
|
||||
@@ -1875,7 +1897,9 @@ opengl_renderer::Render( scene::basic_region *Region ) {
|
||||
glm::vec3 const cameraposition { m_renderpass.camera.position() };
|
||||
auto const camerax = static_cast<int>( std::floor( cameraposition.x / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2 ) );
|
||||
auto const cameraz = static_cast<int>( std::floor( cameraposition.z / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2 ) );
|
||||
int const segmentcount = 2 * static_cast<int>( std::ceil( m_renderpass.draw_range * Global.fDistanceFactor / scene::EU07_SECTIONSIZE ) );
|
||||
float section_range = m_renderpass.draw_range * Global.fDistanceFactor;
|
||||
section_range = editor_track_view_range( section_range, EditorModeFlag && ( m_renderpass.draw_mode == rendermode::color || m_renderpass.draw_mode == rendermode::pickscenery ) );
|
||||
int const segmentcount = 2 * static_cast<int>( std::ceil( section_range / scene::EU07_SECTIONSIZE ) );
|
||||
int const originx = camerax - segmentcount / 2;
|
||||
int const originz = cameraz - segmentcount / 2;
|
||||
|
||||
@@ -2040,12 +2064,16 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator
|
||||
|
||||
// cache initial iterator for the second sweep
|
||||
auto first { First };
|
||||
auto const scenery_limit{ static_cast<double>( Global.BaseDrawRange ) * Global.fDistanceFactor + 250.0 };
|
||||
bool const far_paths{ EditorModeFlag && Global.editor_tracks && ( m_renderpass.draw_mode == rendermode::color || m_renderpass.draw_mode == rendermode::pickscenery ) };
|
||||
glm::dvec3 const camera{ m_renderpass.camera.position() };
|
||||
// first pass draws elements which we know are located in section banks, to reduce vbo switching
|
||||
while( First != Last ) {
|
||||
|
||||
auto *cell = First->second;
|
||||
// przeliczenia animacji torów w sektorze
|
||||
cell->RaAnimate( m_framestamp );
|
||||
bool const tracks_only{ editor_far_tracks_only( far_paths, cell, camera, scenery_limit ) };
|
||||
|
||||
switch( m_renderpass.draw_mode ) {
|
||||
case rendermode::color: {
|
||||
@@ -2056,7 +2084,9 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator
|
||||
|
||||
// render
|
||||
// opaque non-instanced shapes
|
||||
for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); }
|
||||
if( false == tracks_only ) {
|
||||
for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); }
|
||||
}
|
||||
// tracks
|
||||
// TODO: update after path node refactoring
|
||||
Render( std::begin( cell->m_paths ), std::end( cell->m_paths ) );
|
||||
@@ -2158,8 +2188,10 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator
|
||||
// render
|
||||
// opaque non-instanced shapes
|
||||
// non-interactive scenery elements get neutral colour
|
||||
::glColor3fv( glm::value_ptr( colors::none ) );
|
||||
for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); }
|
||||
if( false == tracks_only ) {
|
||||
::glColor3fv( glm::value_ptr( colors::none ) );
|
||||
for( auto const &shape : cell->m_shapesopaque ) { Render( shape, false ); }
|
||||
}
|
||||
// tracks
|
||||
for( auto *path : cell->m_paths ) {
|
||||
::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) );
|
||||
@@ -2188,6 +2220,8 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator
|
||||
case rendermode::color:
|
||||
case rendermode::shadows:
|
||||
case rendermode::cabshadows: {
|
||||
if( editor_far_tracks_only( far_paths, cell, camera, scenery_limit ) )
|
||||
break;
|
||||
// TBD, TODO: refactor in to a method to reuse in branch below?
|
||||
// opaque parts of instanced models
|
||||
for( auto *instance : cell->m_instancesopaque ) {
|
||||
@@ -2230,6 +2264,8 @@ opengl_renderer::Render( cell_sequence::iterator First, cell_sequence::iterator
|
||||
break;
|
||||
}
|
||||
case rendermode::pickscenery: {
|
||||
if( editor_far_tracks_only( far_paths, cell, camera, scenery_limit ) )
|
||||
break;
|
||||
// opaque parts of instanced models
|
||||
// same procedure like with regular render, but each node receives custom colour used for picking
|
||||
for( auto *instance : cell->m_instancesopaque ) {
|
||||
@@ -4565,3 +4601,29 @@ void opengl_renderer::opengl_imgui_renderer::Render()
|
||||
ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
|
||||
std::uint64_t opengl_renderer::opengl_imgui_renderer::Create_Image(std::uint8_t const *Rgba, int const Width, int const Height)
|
||||
{
|
||||
// the ui of this renderer works on srgb colours as they are. the binding the renderer keeps track of is put back as it was
|
||||
GLint texture{0}, alignment{4};
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture);
|
||||
glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
GLuint image{0};
|
||||
glGenTextures(1, &image);
|
||||
glBindTexture(GL_TEXTURE_2D, image);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, Rgba);
|
||||
glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(texture));
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
|
||||
return image;
|
||||
}
|
||||
|
||||
void opengl_renderer::opengl_imgui_renderer::Release_Image(std::uint64_t const Image)
|
||||
{
|
||||
auto const image{static_cast<GLuint>(Image)};
|
||||
glDeleteTextures(1, &image);
|
||||
}
|
||||
|
||||
|
||||
@@ -385,6 +385,8 @@ private:
|
||||
virtual void Shutdown() override;
|
||||
virtual void BeginFrame() override;
|
||||
virtual void Render() override;
|
||||
virtual std::uint64_t Create_Image(std::uint8_t const *Rgba, int const Width, int const Height) override;
|
||||
virtual void Release_Image(std::uint64_t const Image) override;
|
||||
} m_imgui_renderer;
|
||||
|
||||
virtual imgui_renderer *GetImguiRenderer() override
|
||||
|
||||
@@ -39,5 +39,29 @@ gfx_renderer_factory *gfx_renderer_factory::get_instance()
|
||||
|
||||
gfx_renderer_factory *gfx_renderer_factory::instance;
|
||||
|
||||
material_handle gfx_renderer::Terrain_Material( material_handle const Reuse, std::vector<gfx::terrain_layer> const &Layers, int const Samples, std::uint8_t const *Weights, glm::vec3 const &Placement )
|
||||
{
|
||||
// without blending the chunk gets the material which covers the most of it
|
||||
if( Layers.empty() ) {
|
||||
return null_handle;
|
||||
}
|
||||
std::size_t best { 0 };
|
||||
if( ( Weights != nullptr ) && ( Samples > 0 ) ) {
|
||||
auto const samples { static_cast<std::size_t>( Samples + 1 ) * ( Samples + 1 ) };
|
||||
std::uint64_t bestsum { 0 };
|
||||
for( std::size_t layer = 0; layer < Layers.size(); ++layer ) {
|
||||
std::uint64_t sum { 0 };
|
||||
for( std::size_t i = 0; i < samples; ++i ) {
|
||||
sum += Weights[ layer * samples + i ];
|
||||
}
|
||||
if( sum > bestsum ) {
|
||||
bestsum = sum;
|
||||
best = layer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Layers[ best ].material;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -21,6 +21,17 @@ namespace gl
|
||||
class program;
|
||||
}
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
// material painted over the heightmap terrain, and the size (metres) its textures are repeated at; 0: the size given by the material, or 8 m
|
||||
struct terrain_layer
|
||||
{
|
||||
material_handle material{null_handle};
|
||||
float size{0.f};
|
||||
float rotation{0.f}; // degrees the textures are turned by
|
||||
};
|
||||
} // namespace gfx
|
||||
|
||||
class gfx_renderer {
|
||||
|
||||
public:
|
||||
@@ -58,6 +69,14 @@ public:
|
||||
virtual auto Fetch_Material( std::string const &Filename, bool const Loadnow = true ) -> material_handle = 0;
|
||||
virtual void Bind_Material( material_handle const Material, TSubModel const *sm = nullptr, lighting_data const *lighting = nullptr ) = 0;
|
||||
virtual auto Material( material_handle const Material ) const -> IMaterial const * = 0;
|
||||
// heightmap terrain: material of a chunk blending the materials of up to 8 layers by their weights.
|
||||
// Weights: (Samples + 1)^2 values per layer, planar, rows of growing z, the weights of a sample adding up to 255; null with a single layer.
|
||||
// Placement: corner of the chunk (x, z) in the texture coordinates of its mesh, and the size of the chunk.
|
||||
// Reuse: a material made by this call before, to be filled anew, or null_handle.
|
||||
// returns: the material. backends which can't blend give back the material of the layer covering the most
|
||||
virtual auto Terrain_Material( material_handle const Reuse, std::vector<gfx::terrain_layer> const &Layers, int const Samples, std::uint8_t const *Weights, glm::vec3 const &Placement ) -> material_handle;
|
||||
// gives back a material made by Terrain_Material, once its chunk is gone
|
||||
virtual void Terrain_Release( material_handle const Material ) {}
|
||||
// shader methods
|
||||
virtual auto Fetch_Shader( std::string const &name ) -> std::shared_ptr<gl::program> = 0;
|
||||
// texture methods
|
||||
@@ -73,6 +92,7 @@ public:
|
||||
virtual auto Pick_Node() const -> scene::basic_node const * = 0;
|
||||
|
||||
virtual auto Mouse_Position() const -> glm::dvec3 = 0;
|
||||
virtual auto Mouse_Hit() const -> bool { return true; }
|
||||
// editor helpers: matrices/position of the most recent color pass camera.
|
||||
// the view matrix is camera-relative (rotation only, camera at origin), matching the
|
||||
// camera-relative rendering used by the engine; build object matrices relative to Camera_Position().
|
||||
@@ -92,6 +112,10 @@ public:
|
||||
// imgui renderer
|
||||
virtual class imgui_renderer *GetImguiRenderer() = 0;
|
||||
virtual void MakeScreenshot() = 0;
|
||||
// draws the model of a scenery instance on its own into a square image of Size pixels on white background, the model
|
||||
// seen in perspective from the front, side and above and filling the image less Margin pixels at each edge.
|
||||
// Image receives rgb rows, top row first. returns false if the backend can't make previews or there's nothing to draw
|
||||
virtual auto Render_Preview( TAnimModel *Instance, int const Size, int const Margin, bool const Shadows, std::vector<std::uint8_t> &Image ) -> bool { return false; }
|
||||
};
|
||||
|
||||
class gfx_renderer_factory
|
||||
@@ -116,6 +140,13 @@ class imgui_renderer
|
||||
virtual void Shutdown() = 0;
|
||||
virtual void BeginFrame() = 0;
|
||||
virtual void Render() = 0;
|
||||
// images of the user interface itself (e.g. the previews of the node bank), apart from the textures of the scene: rgba, rows
|
||||
// from the top, in srgb. returns: id for ImGui::Image, 0 if the renderer can't make them
|
||||
virtual std::uint64_t Create_Image(std::uint8_t const *Rgba, int const Width, int const Height)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual void Release_Image(std::uint64_t const Image) {}
|
||||
};
|
||||
|
||||
extern std::unique_ptr<gfx_renderer> GfxRenderer;
|
||||
|
||||
Reference in New Issue
Block a user