mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-28 14:37:29 +01:00
feat(terrain): holes cut in the heightmap terrain and shader grass painted over it
This commit is contained in:
@@ -309,6 +309,27 @@ geometry_bank::replace( gfx::vertex_array &Vertices, userdata_array &Userdata, g
|
||||
return true;
|
||||
}
|
||||
|
||||
// replaces the index and the vertex data of specified indexed chunk with the supplied data of the same counts
|
||||
bool
|
||||
geometry_bank::replace( gfx::index_array &Indices, gfx::vertex_array &Vertices, userdata_array &Userdata, gfx::geometry_handle const &Geometry ) {
|
||||
|
||||
if( ( Geometry.chunk == 0 ) || ( Geometry.chunk > m_chunks.size() ) ) { return false; }
|
||||
|
||||
auto &chunk = gfx::geometry_bank::chunk( Geometry );
|
||||
// the place of the chunk in the buffers of the bank is kept, so the sizes have to stay
|
||||
if( ( Indices.size() != chunk.indices.size() )
|
||||
|| ( Vertices.size() != chunk.vertices.size() )
|
||||
|| ( Userdata.size() != chunk.userdata.size() ) ) {
|
||||
return false;
|
||||
}
|
||||
chunk.indices.swap( Indices );
|
||||
chunk.vertices.swap( Vertices );
|
||||
chunk.userdata.swap( Userdata );
|
||||
// template method implementation; the chunk is uploaded whole the next time it's drawn
|
||||
replace_( Geometry );
|
||||
return true;
|
||||
}
|
||||
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
bool
|
||||
geometry_bank::append( gfx::vertex_array &Vertices, userdata_array &Userdata, gfx::geometry_handle const &Geometry ) {
|
||||
@@ -441,6 +462,13 @@ geometrybank_manager::replace( gfx::vertex_array &Vertices, userdata_array &User
|
||||
return bank( Geometry ).first->replace( Vertices, Userdata, Geometry, Offset );
|
||||
}
|
||||
|
||||
// replaces the index and the vertex data of specified indexed chunk with the supplied data of the same counts
|
||||
bool
|
||||
geometrybank_manager::replace( gfx::index_array &Indices, gfx::vertex_array &Vertices, userdata_array &Userdata, gfx::geometry_handle const &Geometry ) {
|
||||
|
||||
return bank( Geometry ).first->replace( Indices, Vertices, Userdata, Geometry );
|
||||
}
|
||||
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
bool
|
||||
geometrybank_manager::append( gfx::vertex_array &Vertices, userdata_array &Userdata, gfx::geometry_handle const &Geometry ) {
|
||||
|
||||
@@ -125,6 +125,8 @@ public:
|
||||
auto create( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, unsigned int const Type ) -> gfx::geometry_handle;
|
||||
// replaces vertex data of specified chunk with the supplied data, starting from specified offset
|
||||
auto replace( gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool;
|
||||
// replaces the index and the vertex data of specified indexed chunk with the supplied data of the same counts. returns: false if the counts differ
|
||||
auto replace( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry ) -> bool;
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
auto append( gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, gfx::geometry_handle const &Geometry ) -> bool;
|
||||
// draws geometry stored in specified chunk
|
||||
@@ -243,6 +245,8 @@ public:
|
||||
auto create_chunk( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, unsigned int const Type ) -> gfx::geometry_handle;
|
||||
// replaces data of specified chunk with the supplied vertex data, starting from specified offset
|
||||
auto replace( gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool;
|
||||
// replaces the index and the vertex data of specified indexed chunk with the supplied data of the same counts. returns: false if the counts differ
|
||||
auto replace( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry ) -> bool;
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
auto append( gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry ) -> bool;
|
||||
// draws geometry stored in specified chunk
|
||||
|
||||
303
rendering/opengl46/opengl46grass.cpp
Normal file
303
rendering/opengl46/opengl46grass.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "rendering/opengl46/opengl46grass.h"
|
||||
|
||||
#include "rendering/openglcamera.h"
|
||||
#include "model/Texture.h"
|
||||
#include "simulation/simulationenvironment.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/Logs.h"
|
||||
#include "utilities/Timer.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
int constexpr max_blades{65536}; // drawn in a tile at most
|
||||
float constexpr blade_reach{1.5f}; // metres the bounds of a tile reach above its ground, for the blades
|
||||
|
||||
// the share of the blades drawn at specified distance; the same as lod_share() of grass.vert
|
||||
float lod_share(float const Distance, float const Near)
|
||||
{
|
||||
return Distance <= Near ? 1.f : std::pow(Near / Distance, 1.5f);
|
||||
}
|
||||
|
||||
GLuint make_texture(GLenum const Format, int const Width, int const Height, GLenum const Filter)
|
||||
{
|
||||
GLuint texture{0};
|
||||
::glCreateTextures(GL_TEXTURE_2D, 1, &texture);
|
||||
::glTextureStorage2D(texture, 1, Format, Width, Height);
|
||||
::glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, Filter);
|
||||
::glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, Filter);
|
||||
::glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
::glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
return texture;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
opengl46_grass::~opengl46_grass()
|
||||
{
|
||||
// the textures go with the context; deleted here only if it's still there
|
||||
if (::glfwGetCurrentContext() != nullptr)
|
||||
clear();
|
||||
}
|
||||
|
||||
bool opengl46_grass::init()
|
||||
{
|
||||
if (m_state != 0)
|
||||
return m_state > 0;
|
||||
m_state = -1;
|
||||
try
|
||||
{
|
||||
gl::shader const vertex("grass.vert");
|
||||
gl::shader const fragment("grass.frag");
|
||||
m_program = std::make_unique<gl::program>(std::vector<std::reference_wrapper<const gl::shader>>{vertex, fragment});
|
||||
}
|
||||
catch (gl::shader_exception const &e)
|
||||
{
|
||||
ErrorLog("heightmap terrain: can't use the grass shader: " + std::string(e.what()));
|
||||
return false;
|
||||
}
|
||||
m_vao = std::make_unique<gl::vao>();
|
||||
auto const location = [&](char const *Name) { return ::glGetUniformLocation(*m_program, Name); };
|
||||
m_uniforms.tile = location("grass_tile");
|
||||
m_uniforms.part = location("grass_part");
|
||||
m_uniforms.chunk = location("grass_chunk");
|
||||
m_uniforms.camera = location("grass_camera");
|
||||
m_uniforms.lod = location("grass_lod");
|
||||
m_uniforms.wind = location("grass_wind");
|
||||
m_uniforms.base = location("grass_base");
|
||||
m_uniforms.tip = location("grass_tip");
|
||||
m_uniforms.shape = location("grass_shape");
|
||||
m_uniforms.share = location("grass_share");
|
||||
m_state = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
void opengl46_grass::release(chunk &Chunk)
|
||||
{
|
||||
for (auto *texture : {&Chunk.heights, &Chunk.density, &Chunk.holes})
|
||||
if (*texture != 0)
|
||||
{
|
||||
::glDeleteTextures(1, texture);
|
||||
*texture = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void opengl46_grass::clear()
|
||||
{
|
||||
for (auto &entry : m_chunks)
|
||||
release(entry.second);
|
||||
m_chunks.clear();
|
||||
}
|
||||
|
||||
void opengl46_grass::upload(chunk &Chunk, gfx::grass_chunk const &Source)
|
||||
{
|
||||
release(Chunk);
|
||||
Chunk.version = Source.version;
|
||||
Chunk.corner = Source.corner;
|
||||
Chunk.size = Source.size;
|
||||
Chunk.cells = Source.cells;
|
||||
Chunk.samples = Source.samples;
|
||||
Chunk.kinds = Source.kinds;
|
||||
|
||||
auto const side{Source.cells + 1};
|
||||
Chunk.heights = make_texture(GL_R32F, side, side, GL_NEAREST);
|
||||
::glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
::glTextureSubImage2D(Chunk.heights, 0, 0, 0, side, side, GL_RED, GL_FLOAT, Source.heights);
|
||||
|
||||
// the densities of the kinds side by side, as the channels of a texel
|
||||
auto const samples{Source.samples + 1};
|
||||
auto const plane{static_cast<std::size_t>(samples) * samples};
|
||||
std::size_t kinds{0};
|
||||
while (kinds < Source.kinds.size() && Source.kinds[kinds] >= 0)
|
||||
++kinds;
|
||||
std::vector<std::uint8_t> texels(plane * 4, 0);
|
||||
for (std::size_t kind = 0; kind < kinds; ++kind)
|
||||
for (std::size_t i = 0; i < plane; ++i)
|
||||
texels[i * 4 + kind] = Source.density[kind * plane + i];
|
||||
Chunk.density = make_texture(GL_RGBA8, samples, samples, GL_LINEAR);
|
||||
::glTextureSubImage2D(Chunk.density, 0, 0, 0, samples, samples, GL_RGBA, GL_UNSIGNED_BYTE, texels.data());
|
||||
|
||||
std::uint8_t const none{0};
|
||||
auto const holeside{(Source.holes > 0 && Source.hole_mask != nullptr) ? Source.holes + 1 : 1};
|
||||
Chunk.holes = make_texture(GL_R8, holeside, holeside, GL_LINEAR);
|
||||
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
::glTextureSubImage2D(Chunk.holes, 0, 0, 0, holeside, holeside, GL_RED, GL_UNSIGNED_BYTE, holeside > 1 ? Source.hole_mask : &none);
|
||||
::glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
|
||||
// the ground and the densest place of every kind of each tile, to skip the empty tiles and to draw as many blades as needed
|
||||
auto const spacing{static_cast<double>(Source.size) / Source.cells};
|
||||
auto const step{static_cast<double>(Source.size) / Source.samples};
|
||||
auto const tilesize{static_cast<double>(Source.size) / tiles};
|
||||
for (int tz = 0; tz < tiles; ++tz)
|
||||
for (int tx = 0; tx < tiles; ++tx)
|
||||
{
|
||||
auto &part{Chunk.parts[static_cast<std::size_t>(tz) * tiles + tx]};
|
||||
auto const range = [](double const From, double const To, double const Step, int const Last) {
|
||||
return std::make_pair(std::clamp(static_cast<int>(std::floor(From / Step)), 0, Last), std::clamp(static_cast<int>(std::ceil(To / Step)), 0, Last));
|
||||
};
|
||||
auto const [gx0, gx1]{range(tx * tilesize, (tx + 1) * tilesize, spacing, Source.cells)};
|
||||
auto const [gz0, gz1]{range(tz * tilesize, (tz + 1) * tilesize, spacing, Source.cells)};
|
||||
part.low = std::numeric_limits<float>::max();
|
||||
part.high = -std::numeric_limits<float>::max();
|
||||
for (int z = gz0; z <= gz1; ++z)
|
||||
for (int x = gx0; x <= gx1; ++x)
|
||||
{
|
||||
auto const height{Source.heights[static_cast<std::size_t>(z) * side + x]};
|
||||
part.low = std::min(part.low, height);
|
||||
part.high = std::max(part.high, height);
|
||||
}
|
||||
part.most.fill(0.f);
|
||||
auto const [sx0, sx1]{range(tx * tilesize, (tx + 1) * tilesize, step, Source.samples)};
|
||||
auto const [sz0, sz1]{range(tz * tilesize, (tz + 1) * tilesize, step, Source.samples)};
|
||||
for (std::size_t kind = 0; kind < kinds; ++kind)
|
||||
{
|
||||
std::uint8_t most{0};
|
||||
for (int z = sz0; z <= sz1; ++z)
|
||||
for (int x = sx0; x <= sx1; ++x)
|
||||
most = std::max(most, Source.density[kind * plane + static_cast<std::size_t>(z) * samples + x]);
|
||||
part.most[kind] = most / 255.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void opengl46_grass::update(std::vector<gfx::grass_kind> const &Kinds, std::vector<gfx::grass_chunk> const &Chunks)
|
||||
{
|
||||
m_kinds = Kinds;
|
||||
for (auto &entry : m_chunks)
|
||||
entry.second.listed = false;
|
||||
if (false == Global.gfx_grass)
|
||||
{
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
for (auto const &source : Chunks)
|
||||
{
|
||||
if (source.heights == nullptr || source.density == nullptr || source.cells <= 0 || source.samples <= 0)
|
||||
continue;
|
||||
auto &entry{m_chunks[source.id]};
|
||||
entry.listed = true;
|
||||
if (entry.heights == 0 || entry.version != source.version)
|
||||
upload(entry, source);
|
||||
}
|
||||
// what's no longer near goes
|
||||
for (auto it = m_chunks.begin(); it != m_chunks.end();)
|
||||
{
|
||||
if (it->second.listed)
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
release(it->second);
|
||||
it = m_chunks.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t opengl46_grass::render(opengl_camera const &Camera, glm::mat4 const &View, std::function<void(glm::mat4 const &)> const &Modelview)
|
||||
{
|
||||
if (false == Global.gfx_grass || m_chunks.empty() || m_kinds.empty() || false == init())
|
||||
return 0;
|
||||
|
||||
auto const reach{std::max(5.f, Global.gfx_grass_distance)};
|
||||
auto const near{std::max(3.f, reach * 0.2f)};
|
||||
auto const multiplier{std::max(0.f, Global.gfx_grass_density)};
|
||||
auto const camera{Camera.position()};
|
||||
|
||||
m_program->bind();
|
||||
m_vao->bind();
|
||||
::glDisable(GL_CULL_FACE);
|
||||
::glUniform4f(m_uniforms.lod, near, reach, 0.f, 0.f);
|
||||
// the wind sways the blades along it, harder in the gusts
|
||||
auto const wind{glm::vec3{simulation::Environment.wind()}};
|
||||
auto const windspeed{glm::length(glm::vec2{wind.x, wind.z})};
|
||||
auto const winddirection{windspeed > 0.01f ? glm::vec2{wind.x, wind.z} / windspeed : glm::vec2{1.f, 0.f}};
|
||||
::glUniform4f(m_uniforms.wind, winddirection.x, winddirection.y, std::clamp(windspeed / 15.f, 0.f, 1.f), static_cast<float>(std::fmod(Timer::GetTime(), 3600.0)));
|
||||
|
||||
std::size_t triangles{0};
|
||||
for (auto &entry : m_chunks)
|
||||
{
|
||||
auto &chunk{entry.second};
|
||||
// the kinds of the chunk, and the share of the blades of each against its densest
|
||||
std::array<glm::vec4, 4> base{}, tip{}, shape{};
|
||||
glm::vec4 share{0.f};
|
||||
float densest{0.f};
|
||||
for (std::size_t i = 0; i < chunk.kinds.size(); ++i)
|
||||
if (chunk.kinds[i] >= 0 && chunk.kinds[i] < static_cast<int>(m_kinds.size()))
|
||||
densest = std::max(densest, m_kinds[chunk.kinds[i]].density);
|
||||
if (densest <= 0.f)
|
||||
continue;
|
||||
for (std::size_t i = 0; i < chunk.kinds.size(); ++i)
|
||||
{
|
||||
if (chunk.kinds[i] < 0 || chunk.kinds[i] >= static_cast<int>(m_kinds.size()))
|
||||
continue;
|
||||
auto const &kind{m_kinds[chunk.kinds[i]]};
|
||||
base[i] = glm::vec4{kind.base, 0.f};
|
||||
tip[i] = glm::vec4{kind.tip, 0.f};
|
||||
shape[i] = glm::vec4{kind.height, kind.variation, kind.width, kind.bend};
|
||||
share[i] = kind.density / densest;
|
||||
}
|
||||
auto const tallest{std::max({shape[0].x, shape[1].x, shape[2].x, shape[3].x}) * 1.5f + blade_reach};
|
||||
|
||||
auto const tilesize{chunk.size / tiles};
|
||||
bool set{false};
|
||||
for (int tz = 0; tz < tiles; ++tz)
|
||||
for (int tx = 0; tx < tiles; ++tx)
|
||||
{
|
||||
auto const &part{chunk.parts[static_cast<std::size_t>(tz) * tiles + tx]};
|
||||
auto const fill{std::min(1.f, glm::dot(glm::vec4{part.most[0], part.most[1], part.most[2], part.most[3]}, share))};
|
||||
if (fill <= 0.f)
|
||||
continue;
|
||||
// distance to the box of the tile
|
||||
glm::dvec3 const low{chunk.corner.x + tx * tilesize, part.low, chunk.corner.y + tz * tilesize};
|
||||
glm::dvec3 const high{low.x + tilesize, part.high + tallest, low.z + tilesize};
|
||||
auto const nearest{glm::clamp(camera, low, high)};
|
||||
auto const distance{static_cast<float>(glm::length(nearest - camera))};
|
||||
if (distance > reach)
|
||||
continue;
|
||||
if (false == Camera.visible(scene::bounding_area{(low + high) * 0.5, static_cast<float>(glm::length(high - low) * 0.5)}))
|
||||
continue;
|
||||
auto const full{tilesize * tilesize * densest * multiplier * fill};
|
||||
auto const count{std::min(max_blades, static_cast<int>(std::ceil(full * lod_share(distance, near))))};
|
||||
if (count <= 0)
|
||||
continue;
|
||||
if (false == set)
|
||||
{
|
||||
// the chunk is drawn relative to its corner, the heights as they are
|
||||
set = true;
|
||||
Modelview(glm::translate(View, glm::vec3{glm::dvec3{chunk.corner.x - camera.x, -camera.y, chunk.corner.y - camera.z}}));
|
||||
::glUniform4f(m_uniforms.chunk, chunk.size, static_cast<float>(chunk.cells), static_cast<float>(chunk.samples), 0.f);
|
||||
::glUniform4f(m_uniforms.camera, static_cast<float>(camera.x - chunk.corner.x), static_cast<float>(camera.y), static_cast<float>(camera.z - chunk.corner.y), 0.f);
|
||||
::glUniform4fv(m_uniforms.base, 4, glm::value_ptr(base[0]));
|
||||
::glUniform4fv(m_uniforms.tip, 4, glm::value_ptr(tip[0]));
|
||||
::glUniform4fv(m_uniforms.shape, 4, glm::value_ptr(shape[0]));
|
||||
::glUniform4fv(m_uniforms.share, 1, glm::value_ptr(share));
|
||||
GLuint const textures[]{chunk.heights, chunk.density, chunk.holes};
|
||||
::glBindTextures(0, 3, textures);
|
||||
}
|
||||
// fewer segments further away
|
||||
auto const segments{distance < near * 1.5f ? 3 : distance < reach * 0.5f ? 2 : 1};
|
||||
// the number of the tile in the world, the seed of its blades
|
||||
auto const seedx{static_cast<float>(std::floor(chunk.corner.x / tilesize + 0.5) + tx)};
|
||||
auto const seedz{static_cast<float>(std::floor(chunk.corner.y / tilesize + 0.5) + tz)};
|
||||
::glUniform4f(m_uniforms.tile, tx * tilesize, tz * tilesize, tilesize, std::max(1.f, full));
|
||||
::glUniform4f(m_uniforms.part, fill, static_cast<float>(segments), seedx, seedz);
|
||||
::glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, segments * 2 + 1, count);
|
||||
triangles += static_cast<std::size_t>(count) * (segments * 2 - 1);
|
||||
}
|
||||
}
|
||||
|
||||
::glEnable(GL_CULL_FACE);
|
||||
gl::vao::unbind();
|
||||
// the units of the textures were taken behind the back of the texture cache
|
||||
opengl_texture::reset_unit_cache();
|
||||
return triangles;
|
||||
}
|
||||
85
rendering/opengl46/opengl46grass.h
Normal file
85
rendering/opengl46/opengl46grass.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 <functional>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "rendering/renderer.h"
|
||||
#include "gl/shader.h"
|
||||
#include "gl/vao.h"
|
||||
|
||||
class opengl_camera;
|
||||
|
||||
// grass of the heightmap terrain for the opengl 4.6 renderer: blades made up by the vertex shader (grass.vert) where the
|
||||
// grass is painted, in the chunks near the camera.
|
||||
//
|
||||
// a chunk is cut into tiles; each tile visible within the reach of the grass is drawn as instances of a blade, as many as
|
||||
// its densest kind and its distance ask for. a blade finds its place, its kind and its shape from its number, so the same
|
||||
// blades stand at the same places in every frame. further away the blades thin out and grow wider, and fade out at the end
|
||||
// of the reach. the heights, the densities and the holes of a chunk are kept in textures, made when the chunk comes near
|
||||
class opengl46_grass
|
||||
{
|
||||
public:
|
||||
opengl46_grass() = default;
|
||||
~opengl46_grass();
|
||||
|
||||
// see gfx_renderer::Terrain_Grass()
|
||||
void update(std::vector<gfx::grass_kind> const &Kinds, std::vector<gfx::grass_chunk> const &Chunks);
|
||||
// draws the grass seen by the camera. View: the modelview of the pass with the camera at its origin; Modelview: hands a
|
||||
// modelview to the model block of the draws which follow. returns: triangles drawn
|
||||
std::size_t render(opengl_camera const &Camera, glm::mat4 const &View, std::function<void(glm::mat4 const &)> const &Modelview);
|
||||
// drops everything made on the gpu, e.g. when the context goes
|
||||
void clear();
|
||||
bool empty() const { return m_chunks.empty(); }
|
||||
|
||||
static int constexpr tiles{16}; // along a side of a chunk
|
||||
|
||||
private:
|
||||
struct tile
|
||||
{
|
||||
float low{0.f}, high{0.f}; // of the ground
|
||||
std::array<float, 4> most{}; // the highest density of each kind of the chunk within the tile, 0..1
|
||||
};
|
||||
struct chunk
|
||||
{
|
||||
std::uint64_t version{0};
|
||||
glm::dvec2 corner{0.0};
|
||||
float size{0.f};
|
||||
int cells{0};
|
||||
int samples{0};
|
||||
std::array<int, 4> kinds{-1, -1, -1, -1};
|
||||
GLuint heights{0}; // R32F, the grid points
|
||||
GLuint density{0}; // RGBA8, the densities of the kinds
|
||||
GLuint holes{0}; // R8, a single texel of 0 without holes
|
||||
std::array<tile, tiles * tiles> parts;
|
||||
bool listed{false}; // in the last list given
|
||||
};
|
||||
|
||||
bool init();
|
||||
void upload(chunk &Chunk, gfx::grass_chunk const &Source);
|
||||
void release(chunk &Chunk);
|
||||
|
||||
int m_state{0}; // 0: not set up yet, 1: ready, -1: can't be used
|
||||
std::unique_ptr<gl::program> m_program;
|
||||
std::unique_ptr<gl::vao> m_vao; // without attributes, the blades are made of the numbers of their vertices
|
||||
std::vector<gfx::grass_kind> m_kinds;
|
||||
std::unordered_map<std::uint64_t, chunk> m_chunks;
|
||||
// locations of the uniforms of the program
|
||||
struct locations
|
||||
{
|
||||
GLint tile{-1}, part{-1}, chunk{-1}, camera{-1}, lod{-1}, wind{-1};
|
||||
GLint base{-1}, tip{-1}, shape{-1}, share{-1};
|
||||
} m_uniforms;
|
||||
};
|
||||
@@ -1108,6 +1108,12 @@ void opengl46_renderer::draw_debug_ui()
|
||||
if (Global.gfx_contactshadows > 0)
|
||||
ImGui::TextUnformatted("(drawn anyway for the contact shadows, gfx.contactshadows)");
|
||||
ImGui::Checkbox("walk the scene once for its depth and colour, drawing both from a record (gfx.depthprepass.replay)", &Global.gfx_depthprepass_replay);
|
||||
ImGui::Checkbox("grass of the heightmap terrain (gfx.grass)", &Global.gfx_grass);
|
||||
if (Global.gfx_grass)
|
||||
{
|
||||
ImGui::SliderFloat("reach of the grass, m (gfx.grass.distance)", &Global.gfx_grass_distance, 10.f, 250.f, "%.0f");
|
||||
ImGui::SliderFloat("density of the grass (gfx.grass.density)", &Global.gfx_grass_density, 0.1f, 2.f, "%.2f");
|
||||
}
|
||||
auto info{m_instancecull.info()};
|
||||
if (Global.gfx_gpucull_shadows)
|
||||
{
|
||||
@@ -1428,6 +1434,13 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
|
||||
m_scenedepthdrawn = false;
|
||||
m_indirectcolourpending = false;
|
||||
|
||||
// the grass of the terrain, opaque, over the ground drawn
|
||||
if (vp.main && (false == Global.bWireFrame))
|
||||
{
|
||||
gpupart("grass");
|
||||
Render_grass(future);
|
||||
}
|
||||
|
||||
Render_vr_models();
|
||||
|
||||
// ...translucent parts
|
||||
@@ -2742,6 +2755,14 @@ bool opengl46_renderer::Replace(gfx::vertex_array &Vertices, gfx::userdata_array
|
||||
return m_geometry.replace(Vertices, Userdata, Geometry, Offset);
|
||||
}
|
||||
|
||||
// replaces the index and the vertex data of specified indexed chunk with the supplied data of the same counts
|
||||
bool opengl46_renderer::Replace_Indexed(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry)
|
||||
{
|
||||
// NOTE: we expect indexed geometry to come with calculated tangents
|
||||
m_chunkspheres.erase(static_cast<std::uint64_t>(Geometry));
|
||||
return m_geometry.replace(Indices, Vertices, Userdata, Geometry);
|
||||
}
|
||||
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
bool opengl46_renderer::Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type)
|
||||
{
|
||||
@@ -3041,13 +3062,14 @@ IMaterial const *opengl46_renderer::Material(material_handle const Material) con
|
||||
return &m_materials.material(Material);
|
||||
}
|
||||
|
||||
material_handle opengl46_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)
|
||||
material_handle opengl46_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,
|
||||
int const HoleSamples, std::uint8_t const *Holes)
|
||||
{
|
||||
auto const material{m_terrainmaterials.make(Reuse, Layers, Samples, Weights, Placement)};
|
||||
auto const material{m_terrainmaterials.make(Reuse, Layers, Samples, Weights, Placement, HoleSamples, Holes)};
|
||||
if (material != null_handle)
|
||||
return material;
|
||||
// the terrain shader can't be used, the chunk makes do with a plain material
|
||||
return gfx_renderer::Terrain_Material(Reuse, Layers, Samples, Weights, Placement);
|
||||
return gfx_renderer::Terrain_Material(Reuse, Layers, Samples, Weights, Placement, HoleSamples, Holes);
|
||||
}
|
||||
|
||||
void opengl46_renderer::Terrain_Release(material_handle const Material)
|
||||
@@ -3055,6 +3077,11 @@ void opengl46_renderer::Terrain_Release(material_handle const Material)
|
||||
m_terrainmaterials.release(Material);
|
||||
}
|
||||
|
||||
void opengl46_renderer::Terrain_Grass(std::vector<gfx::grass_kind> const &Kinds, std::vector<gfx::grass_chunk> const &Chunks)
|
||||
{
|
||||
m_grass.update(Kinds, Chunks);
|
||||
}
|
||||
|
||||
opengl_material &opengl46_renderer::Material(material_handle const Material)
|
||||
{
|
||||
return m_materials.material(Material);
|
||||
@@ -6801,6 +6828,27 @@ void opengl46_renderer::Render(TMemCell *Memcell)
|
||||
::glPopMatrix();
|
||||
}
|
||||
|
||||
void opengl46_renderer::Render_grass(glm::mat4 const &Future)
|
||||
{
|
||||
if (false == Global.gfx_grass || m_grass.empty())
|
||||
return;
|
||||
glDebug("render grass");
|
||||
// the blades bring their colour, the lights do the rest
|
||||
model_ubs.future = Future;
|
||||
model_ubs.param[0] = glm::vec4(1.f);
|
||||
model_ubs.param[1] = glm::vec4(1.f, 1.f, 0.f, 1.f); // diffuse, specular, reflection, glossiness
|
||||
model_ubs.param[2] = glm::vec4(0.f);
|
||||
model_ubs.emission = 0.f;
|
||||
model_ubs.alpha_mult = 1.f;
|
||||
model_ubs.opacity = 0.5f;
|
||||
glm::mat4 const view{OpenGLMatrices.data(GL_MODELVIEW)};
|
||||
auto const triangles{m_grass.render(m_renderpass.pass_camera, view, [this](glm::mat4 const &Modelview) {
|
||||
model_ubs.set_modelview(Modelview);
|
||||
model_ubo->update(model_ubs);
|
||||
})};
|
||||
m_renderpass.draw_stats.triangles += static_cast<int>(triangles);
|
||||
}
|
||||
|
||||
void opengl46_renderer::Render_particles()
|
||||
{
|
||||
model_ubs.set_modelview(OpenGLMatrices.data(GL_MODELVIEW));
|
||||
|
||||
@@ -34,6 +34,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "gl/pbo.h"
|
||||
#include "gl/query.h"
|
||||
#include "rendering/opengl46/opengl46terrain.h"
|
||||
#include "rendering/opengl46/opengl46grass.h"
|
||||
#include "rendering/opengl46/opengl46sky.h"
|
||||
#include "rendering/opengl46/opengl46weather.h"
|
||||
#include "rendering/opengl46/opengl46exposure.h"
|
||||
@@ -77,6 +78,8 @@ class opengl46_renderer : public gfx_renderer {
|
||||
gfx::geometry_handle Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) override;
|
||||
// replaces data of specified chunk with the supplied vertex data, starting from specified offset
|
||||
bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) override;
|
||||
// replaces the index and the vertex data of specified indexed chunk with the supplied data of the same counts
|
||||
bool Replace_Indexed(gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry) override;
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override;
|
||||
// provides direct access to index data of specfied chunk
|
||||
@@ -102,9 +105,12 @@ class opengl46_renderer : public gfx_renderer {
|
||||
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;
|
||||
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,
|
||||
int const HoleSamples = 0, std::uint8_t const *Holes = nullptr ) override;
|
||||
void
|
||||
Terrain_Release( material_handle const Material ) override;
|
||||
void
|
||||
Terrain_Grass( std::vector<gfx::grass_kind> const &Kinds, std::vector<gfx::grass_chunk> const &Chunks ) override;
|
||||
// shader methods
|
||||
auto Fetch_Shader( std::string const &name ) -> std::shared_ptr<gl::program> override;
|
||||
// texture methods
|
||||
@@ -489,6 +495,8 @@ class opengl46_renderer : public gfx_renderer {
|
||||
void Render(TMemCell *Memcell);
|
||||
void Render_particles();
|
||||
void Render_precipitation();
|
||||
// the grass of the heightmap terrain around the camera of the main view; Future: the matrix of the motion vectors
|
||||
void Render_grass(glm::mat4 const &Future);
|
||||
void Render_vr_models();
|
||||
void Render_Alpha(scene::basic_region *Region);
|
||||
void Render_Alpha(cell_sequence::reverse_iterator First, cell_sequence::reverse_iterator Last);
|
||||
@@ -544,6 +552,7 @@ class opengl46_renderer : public gfx_renderer {
|
||||
material_manager m_materials;
|
||||
texture_manager m_textures;
|
||||
opengl46_terrain_materials m_terrainmaterials{m_materials, m_textures};
|
||||
opengl46_grass m_grass; // of the heightmap terrain
|
||||
opengl46_light m_sunlight;
|
||||
// fog colour of the frame: the one of the simulation, or of the atmosphere (see opengl46_sky::update())
|
||||
glm::vec3 m_fogcolor{0.f};
|
||||
|
||||
@@ -79,7 +79,7 @@ bool opengl46_terrain_materials::init()
|
||||
// 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");
|
||||
m_program = GfxRenderer->Fetch_Shader("terrain");
|
||||
}
|
||||
catch (gl::shader_exception const &e)
|
||||
{
|
||||
@@ -192,6 +192,7 @@ std::size_t opengl46_terrain_materials::acquire_slot(material_handle const Reuse
|
||||
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);
|
||||
entry.holes = make_texture("internal_src:terrain/holes_" + 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)
|
||||
@@ -211,6 +212,18 @@ std::size_t opengl46_terrain_materials::acquire_slot(material_handle const Reuse
|
||||
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());
|
||||
}
|
||||
{
|
||||
// led between the samples, so the edges of the holes run smoothly across the cells
|
||||
bind_for_upload(m_textures.texture(entry.holes));
|
||||
::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_MIN_FILTER, GL_LINEAR);
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
std::uint8_t const none{0};
|
||||
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
::glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, &none);
|
||||
::glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
}
|
||||
opengl_texture::reset_unit_cache();
|
||||
auto const name = [&](texture_handle const Handle) { return "internal_src:" + m_textures.texture(Handle).name; };
|
||||
std::string const definition{
|
||||
@@ -221,7 +234,8 @@ std::size_t opengl46_terrain_materials::acquire_slot(material_handle const Reuse
|
||||
"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"};
|
||||
"texture7: " + name(entry.table) + "\n"
|
||||
"texture8: " + name(entry.holes) + "\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)
|
||||
{
|
||||
@@ -235,7 +249,8 @@ std::size_t opengl46_terrain_materials::acquire_slot(material_handle const Reuse
|
||||
return index;
|
||||
}
|
||||
|
||||
material_handle opengl46_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)
|
||||
material_handle opengl46_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,
|
||||
int const HoleSamples, std::uint8_t const *Holes)
|
||||
{
|
||||
if (Layers.empty() || false == init())
|
||||
return null_handle;
|
||||
@@ -286,8 +301,32 @@ material_handle opengl46_terrain_materials::make(material_handle const Reuse, st
|
||||
entry.rotations[i] = Layers[i].rotation;
|
||||
write_table(entry);
|
||||
|
||||
// the holes: the cells cut out entirely are left out of the mesh, the program of the holes cuts the rest of the openings
|
||||
auto const cut{Holes != nullptr && HoleSamples > 0};
|
||||
{
|
||||
auto const holeside{cut ? HoleSamples + 1 : 1};
|
||||
std::uint8_t const none{0};
|
||||
bind_for_upload(m_textures.texture(entry.holes));
|
||||
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
::glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, holeside, holeside, 0, GL_RED, GL_UNSIGNED_BYTE, cut ? Holes : &none);
|
||||
::glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
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));
|
||||
if (m_holesprogram == nullptr && cut)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_holesprogram = GfxRenderer->Fetch_Shader("terrain_holes");
|
||||
}
|
||||
catch (gl::shader_exception const &e)
|
||||
{
|
||||
ErrorLog("heightmap terrain: can't use the shader of the holes: " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
// the program of the holes discards fragments, which the depth prepass of the plain one never does
|
||||
material.shader = (cut && m_holesprogram != nullptr) ? m_holesprogram : m_program;
|
||||
return entry.material;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "gl/framebuffer.h"
|
||||
#include "gl/postfx.h"
|
||||
|
||||
// materials of the heightmap terrain for the opengl 3.3 renderer (shader mat_terrain).
|
||||
// materials of the heightmap terrain for the opengl 4.6 renderer (shader mat_terrain, mat_terrain_holes for chunks with holes).
|
||||
// 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 opengl46_terrain_materials
|
||||
@@ -29,7 +29,8 @@ class opengl46_terrain_materials
|
||||
opengl46_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);
|
||||
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,
|
||||
int const HoleSamples = 0, std::uint8_t const *Holes = nullptr);
|
||||
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();
|
||||
@@ -63,6 +64,7 @@ class opengl46_terrain_materials
|
||||
material_handle material{null_handle};
|
||||
std::array<texture_handle, 2> weights{null_handle, null_handle};
|
||||
texture_handle table{null_handle};
|
||||
texture_handle holes{null_handle}; // where the ground is cut, R8; a single texel of 0 without holes
|
||||
bool used{false};
|
||||
// what the table was made of, kept to make it again when a layer changes
|
||||
std::size_t count{0};
|
||||
@@ -90,6 +92,8 @@ class opengl46_terrain_materials
|
||||
std::vector<slot> m_slots;
|
||||
std::vector<std::size_t> m_free;
|
||||
std::unordered_map<material_handle, std::size_t> m_slotof;
|
||||
std::shared_ptr<gl::program> m_program; // of the chunks, mat_terrain
|
||||
std::shared_ptr<gl::program> m_holesprogram; // of the chunks with holes, mat_terrain_holes
|
||||
std::unique_ptr<gl::postfx> m_copy;
|
||||
std::unique_ptr<gl::framebuffer> m_framebuffer;
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@ 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 )
|
||||
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, int const HoleSamples, std::uint8_t const *Holes )
|
||||
{
|
||||
// without blending the chunk gets the material which covers the most of it
|
||||
if( Layers.empty() ) {
|
||||
|
||||
@@ -9,6 +9,9 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
#include "rendering/geometrybank.h"
|
||||
#include "interfaces/IMaterial.h"
|
||||
#include "interfaces/ITexture.h"
|
||||
@@ -30,6 +33,35 @@ struct terrain_layer
|
||||
float size{0.f};
|
||||
float rotation{0.f}; // degrees the textures are turned by
|
||||
};
|
||||
|
||||
// kind of grass grown over the heightmap terrain (heightmap::grass_def), colours in linear rgb
|
||||
struct grass_kind
|
||||
{
|
||||
glm::vec3 base{0.f}; // at the root of the blades
|
||||
glm::vec3 tip{0.f};
|
||||
float height{0.3f}; // metres
|
||||
float variation{0.4f}; // share of the height the blades differ by, either way
|
||||
float width{0.012f}; // metres, at the root
|
||||
float density{60.f}; // blades per square metre where painted fully
|
||||
float bend{0.3f}; // share of the height the tips lean over by
|
||||
};
|
||||
|
||||
// a chunk of the heightmap terrain the grass grows on, see gfx_renderer::Terrain_Grass()
|
||||
struct grass_chunk
|
||||
{
|
||||
std::uint64_t id{0}; // the same for the same chunk, as long as it's in memory
|
||||
std::uint64_t version{0}; // changes with every change of what the grass is made of
|
||||
glm::dvec2 corner{0.0}; // x, z
|
||||
float size{0.f}; // metres along a side
|
||||
float low{0.f}, high{0.f}; // the lowest and the highest point
|
||||
int cells{0}; // quads of the grid along a side
|
||||
float const *heights{nullptr}; // (cells + 1)^2, rows of growing z
|
||||
int samples{0}; // grass samples along a side less one
|
||||
std::uint8_t const *density{nullptr}; // (samples + 1)^2 per kind, planar
|
||||
std::array<int, 4> kinds{-1, -1, -1, -1}; // indices of the kinds of the samples, -1 unused
|
||||
int holes{0}; // hole samples along a side less one, 0: none
|
||||
std::uint8_t const *hole_mask{nullptr}; // (holes + 1)^2, 255: cut
|
||||
};
|
||||
} // namespace gfx
|
||||
|
||||
class gfx_renderer {
|
||||
@@ -57,6 +89,9 @@ public:
|
||||
virtual auto Insert(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometrybank_handle const &Geometry, int const Type) -> gfx::geometry_handle = 0;
|
||||
// replaces data of specified chunk with the supplied vertex data, starting from specified offset
|
||||
virtual auto Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) -> bool = 0;
|
||||
// replaces the index and the vertex data of specified indexed chunk with the supplied data of the same counts.
|
||||
// returns: false if it wasn't replaced (the counts differ, or the backend can't)
|
||||
virtual auto Replace_Indexed( gfx::index_array &Indices, gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry ) -> bool { return false; }
|
||||
// adds supplied vertex data at the end of specified chunk
|
||||
virtual auto Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) -> bool = 0;
|
||||
// provides direct access to index data of specfied chunk
|
||||
@@ -78,10 +113,17 @@ public:
|
||||
// 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.
|
||||
// Holes: (HoleSamples + 1)^2 values (255: the ground is cut there), rows like the weights; null without holes. the cells
|
||||
// cut out entirely are left out of the geometry, the material cuts the rest of the openings.
|
||||
// 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;
|
||||
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,
|
||||
int const HoleSamples = 0, std::uint8_t const *Holes = nullptr ) -> material_handle;
|
||||
// gives back a material made by Terrain_Material, once its chunk is gone
|
||||
virtual void Terrain_Release( material_handle const Material ) {}
|
||||
// heightmap terrain: the grass to draw, the kinds of the grass palette and the chunks near the camera which have grass.
|
||||
// the data of the chunks is read during the call only; a chunk the renderer has with the same id and version isn't read
|
||||
// again. chunks left out are dropped. backends which can't draw grass ignore it
|
||||
virtual void Terrain_Grass( std::vector<gfx::grass_kind> const &Kinds, std::vector<gfx::grass_chunk> const &Chunks ) {}
|
||||
// shader methods
|
||||
virtual auto Fetch_Shader( std::string const &name ) -> std::shared_ptr<gl::program> = 0;
|
||||
// texture methods
|
||||
|
||||
Reference in New Issue
Block a user