17
0
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:
jerrrrycho
2026-10-11 12:33:07 +02:00
committed by GitHub
179 changed files with 172242 additions and 16421 deletions

41
rendering/editoroverlay.h Normal file
View 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;

View File

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

View File

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

View File

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

View File

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

View File

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

View 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();
}

View 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;
};

View File

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

View File

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

View File

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

View File

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