summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2022-12-08 08:13:37 +0100
committerStanislaw Halik <sthalik@misaki.pl>2022-12-08 08:29:12 +0100
commitab31f0ed81301c23d6847f3ca1513d4b90275de7 (patch)
tree735d63bd525eccabc43ebc75e804469e898950af
parentb26ddfcee60de0951f0feaf4ae4e12551853ee21 (diff)
draw, main, src/chunk: batch scenery writes
-rw-r--r--draw/anim.cpp88
-rw-r--r--draw/anim.hpp16
-rw-r--r--main/draw.cpp34
-rw-r--r--main/main-impl.hpp1
-rw-r--r--shaders/tile.hpp1
-rw-r--r--src/chunk-render.cpp53
-rw-r--r--src/chunk.cpp1
-rw-r--r--src/chunk.hpp20
8 files changed, 176 insertions, 38 deletions
diff --git a/draw/anim.cpp b/draw/anim.cpp
index 951c7f5a..df3011da 100644
--- a/draw/anim.cpp
+++ b/draw/anim.cpp
@@ -2,8 +2,12 @@
#include "anim-atlas.hpp"
#include "chunk.hpp"
#include "shaders/tile.hpp"
+#include "main/clickable.hpp"
+#include <Magnum/GL/MeshView.h>
#include <Magnum/GL/Texture.h>
+//#define FM_DEBUG_DRAW_COUNT
+
namespace floormat {
anim_mesh::anim_mesh()
@@ -22,6 +26,85 @@ std::array<UnsignedShort, 6> anim_mesh::make_index_array()
}};
}
+void anim_mesh::add_clickable(tile_shader& shader, const Vector2i& win_size,
+ chunk_coords c, std::uint8_t i, const std::shared_ptr<anim_atlas>& atlas, scenery& s,
+ std::vector<clickable_scenery>& clickable)
+{
+ const local_coords xy{i};
+ const auto& g = atlas->group(s.r);
+ const auto& f = atlas->frame(s.r, s.frame);
+ const auto world_pos = TILE_SIZE20 * Vector3(xy.x, xy.y, 0) + Vector3(g.offset);
+ const Vector2ui offset((Vector2(shader.camera_offset()) + Vector2(win_size)*.5f)
+ + shader.project(world_pos) - Vector2(f.ground));
+ clickable_scenery item = {
+ *atlas, s,
+ { f.offset, f.offset + f.size }, { offset, offset + f.size },
+ atlas->bitmask(), tile_shader::depth_value(xy, tile_shader::scenery_depth_offset), c, xy,
+ !g.mirror_from.isEmpty(),
+ };
+ clickable.push_back(item);
+}
+
+void anim_mesh::draw(tile_shader& shader, chunk& c)
+{
+ constexpr auto quad_index_count = 6;
+ auto [mesh_, ids, size] = c.ensure_scenery_mesh();
+ struct { anim_atlas* atlas = nullptr; std::size_t pos = 0; } last;
+ GL::MeshView mesh{mesh_};
+ anim_atlas* bound = nullptr;
+
+ [[maybe_unused]] std::size_t draw_count = 0;
+
+ const auto do_draw = [&](std::size_t i, anim_atlas* atlas, std::uint32_t max_index, bool force) {
+ if (!force && atlas == last.atlas)
+ return;
+ if (auto len = i - last.pos; last.atlas && len > 0)
+ {
+ if (last.atlas != bound)
+ last.atlas->texture().bind(0);
+ bound = last.atlas;
+ mesh.setCount((int)(quad_index_count * len));
+ mesh.setIndexRange((int)(last.pos*quad_index_count), 0, max_index);
+ shader.draw(mesh);
+ draw_count++;
+ }
+ last = { atlas, i };
+ };
+
+ constexpr auto next_is_dynamic = [](chunk& c, std::size_t i) {
+ for (; i < TILE_COUNT; i++)
+ if (auto [atlas, s] = c[i].scenery(); atlas && atlas->info().fps == 0)
+ return false;
+ return true;
+ };
+
+ const auto draw_dynamic = [&](std::size_t last_id, std::size_t id) {
+ for (std::size_t i = last_id+1; i < id; i++)
+ if (auto [atlas, s] = c[i].scenery(); atlas && atlas->info().fps > 0)
+ {
+ draw(shader, *atlas, s.r, s.frame, local_coords{i});
+ bound = nullptr;
+ }
+ };
+
+ const auto max_index = std::uint32_t(size*quad_index_count - 1);
+ std::size_t k, last_id = 0;
+ for (k = 0; k < size; k++)
+ {
+ const auto id = ids[k];
+ draw_dynamic(last_id, id);
+ do_draw(k, c.scenery_atlas_at(id), max_index, next_is_dynamic(c, id+1));
+ last_id = id;
+ }
+ draw_dynamic(size == 0 ? 0 : ids.back(), TILE_COUNT);
+ do_draw(size, nullptr, max_index, true);
+
+#ifdef FM_DEBUG_DRAW_COUNT
+ if (draw_count)
+ fm_debug("anim draws: %zu", draw_count);
+#endif
+}
+
void anim_mesh::draw(tile_shader& shader, anim_atlas& atlas, rotation r, std::size_t frame, const Vector3& center, float depth)
{
const auto pos = atlas.frame_quad(center, r, frame);
@@ -37,7 +120,10 @@ void anim_mesh::draw(tile_shader& shader, anim_atlas& atlas, rotation r, std::si
void anim_mesh::draw(tile_shader& shader, anim_atlas& atlas, rotation r, std::size_t frame, local_coords xy)
{
- draw(shader, atlas, r, frame, Vector3(xy.x, xy.y, 0.f) * TILE_SIZE, tile_shader::depth_value(xy, .25f));
+ const auto pos = Vector3(xy.x, xy.y, 0.f) * TILE_SIZE;
+ const float depth = tile_shader::depth_value(xy, tile_shader::scenery_depth_offset);
+ draw(shader, atlas, r, frame, pos, depth);
}
+
} // namespace floormat
diff --git a/draw/anim.hpp b/draw/anim.hpp
index e11d45ae..263d851d 100644
--- a/draw/anim.hpp
+++ b/draw/anim.hpp
@@ -1,7 +1,5 @@
#pragma once
-
#include "local-coords.hpp"
-#include "scenery.hpp"
#include <array>
#include <Corrade/Containers/ArrayViewStl.h>
#include <Magnum/Magnum.h>
@@ -9,6 +7,8 @@
#include <Magnum/Math/Vector3.h>
#include <Magnum/GL/Mesh.h>
#include <Magnum/GL/Buffer.h>
+#include "src/scenery.hpp"
+#include "main/clickable.hpp"
//namespace floormat::Serialize { struct anim_frame; }
@@ -17,13 +17,21 @@ namespace floormat {
struct tile_shader;
struct anim_atlas;
struct chunk;
-//using anim_frame = Serialize::anim_frame;
+template<typename Atlas, typename T> struct clickable;
+struct scenery;
struct anim_mesh final
{
+ using clickable_scenery = clickable<anim_atlas, scenery>;
+
anim_mesh();
- void draw(tile_shader& shader, anim_atlas& atlas, rotation r, std::size_t frame, local_coords xy);
+
+ void draw(tile_shader& shader, chunk& c);
void draw(tile_shader& shader, anim_atlas& atlas, rotation r, std::size_t frame, const Vector3& pos, float depth);
+ void draw(tile_shader& shader, anim_atlas& atlas, rotation r, std::size_t frame, local_coords xy);
+ static void add_clickable(tile_shader& shader, const Vector2i& win_size,
+ chunk_coords c, std::uint8_t i, const std::shared_ptr<anim_atlas>& atlas, scenery& s,
+ std::vector<clickable_scenery>& clickable);
private:
struct vertex_data final {
diff --git a/main/draw.cpp b/main/draw.cpp
index cfb5ec80..88cd2745 100644
--- a/main/draw.cpp
+++ b/main/draw.cpp
@@ -85,6 +85,8 @@ void main_impl::draw_world() noexcept
_floor_mesh.draw(_shader, c);
}
+ _clickable_scenery.clear();
+ const auto window_size = framebufferSize();
GL::Renderer::enable(GL::Renderer::Feature::DepthTest);
GL::defaultFramebuffer.clearDepthStencil(0, 0);
for (std::int16_t y = miny; y <= maxy; y++)
@@ -98,14 +100,8 @@ void main_impl::draw_world() noexcept
if (check_chunk_visible(_shader.camera_offset(), sz))
_wall_mesh.draw(_shader, c);
}
-}
-
-void main_impl::draw_anim() noexcept
-{
- const auto sz = framebufferSize();
- const auto [minx, maxx, miny, maxy] = get_draw_bounds();
- _clickable_scenery.clear();
+ GL::Renderer::setDepthMask(false);
for (std::int16_t y = miny; y <= maxy; y++)
for (std::int16_t x = minx; x <= maxx; x++)
{
@@ -115,27 +111,20 @@ void main_impl::draw_anim() noexcept
continue;
const with_shifted_camera_offset o{_shader, pos, {minx, miny}, {maxx, maxy}};
if (check_chunk_visible(_shader.camera_offset(), sz))
+ {
+ _anim_mesh.draw(_shader, c);
for (std::size_t i = 0; i < TILE_COUNT; i++)
{
const local_coords xy{i};
if (auto [atlas, s] = c[xy].scenery(); atlas)
- {
- _anim_mesh.draw(_shader, *atlas, s.r, s.frame, xy);
- const auto& g = atlas->group(s.r);
- const auto& f = atlas->frame(s.r, s.frame);
- const auto world_pos = TILE_SIZE20 * Vector3(xy.x, xy.y, 0) + Vector3(g.offset);
- const Vector2ui offset((Vector2(_shader.camera_offset()) + Vector2(sz)*.5f)
- + _shader.project(world_pos) - Vector2(f.ground));
- clickable<anim_atlas, scenery> item = {
- *atlas, s,
- { f.offset, f.offset + f.size }, { offset, offset + f.size },
- atlas->bitmask(), tile_shader::depth_value(xy, 0.25f), pos, xy,
- !g.mirror_from.isEmpty(),
- };
- _clickable_scenery.push_back(item);
- }
+ if (s.can_activate(*atlas))
+ _anim_mesh.add_clickable(_shader, window_size, pos, std::uint8_t(i), atlas, s, _clickable_scenery);
}
+ }
}
+ GL::Renderer::setDepthMask(true);
+
+ GL::Renderer::disable(GL::Renderer::Feature::DepthTest);
}
bool main_impl::check_chunk_visible(const Vector2d& offset, const Vector2i& size) noexcept
@@ -194,7 +183,6 @@ void main_impl::drawEvent()
const auto clear_color = 0x222222ff_rgbaf;
GL::defaultFramebuffer.clearColor(clear_color);
draw_world();
- draw_anim();
GL::Renderer::disable(GL::Renderer::Feature::DepthTest);
}
diff --git a/main/main-impl.hpp b/main/main-impl.hpp
index a96d6aee..2ea8f940 100644
--- a/main/main-impl.hpp
+++ b/main/main-impl.hpp
@@ -88,7 +88,6 @@ private:
void recalc_viewport(Vector2i fb_size, Vector2i win_size) noexcept;
void draw_world() noexcept;
- void draw_anim() noexcept;
draw_bounds get_draw_bounds() const noexcept override;
[[nodiscard]] static bool check_chunk_visible(const Vector2d& offset, const Vector2i& size) noexcept;
diff --git a/shaders/tile.hpp b/shaders/tile.hpp
index d6eb7cca..73dc019d 100644
--- a/shaders/tile.hpp
+++ b/shaders/tile.hpp
@@ -38,6 +38,7 @@ struct tile_shader : GL::AbstractShaderProgram
decltype(auto) draw(T&& mesh, Xs&&... xs);
static constexpr float depth_tile_size = 1.f/(256 * TILE_COUNT);
+ static constexpr float scenery_depth_offset = .25f;
private:
void _draw();
diff --git a/src/chunk-render.cpp b/src/chunk-render.cpp
index cfd10307..f7b0678a 100644
--- a/src/chunk-render.cpp
+++ b/src/chunk-render.cpp
@@ -9,11 +9,11 @@
namespace floormat {
template<std::size_t N = 1>
-static auto make_index_array(std::size_t offset, std::size_t max)
+static auto make_index_array(std::size_t max)
{
std::array<std::array<UnsignedShort, 6>, N*TILE_COUNT> array; // NOLINT(cppcoreguidelines-pro-type-member-init)
for (std::size_t i = 0; i < max; i++)
- array[i] = tile_atlas::indices(i + offset);
+ array[i] = tile_atlas::indices(i);
return array;
}
@@ -53,7 +53,7 @@ auto chunk::ensure_ground_mesh() noexcept -> ground_mesh_tuple
v[j] = { quad[j], texcoords[j], depth };
}
- const auto indexes = make_index_array(0, count);
+ const auto indexes = make_index_array(count);
const auto vertex_view = ArrayView{vertexes.data(), count};
const auto vert_index_view = ArrayView{indexes.data(), count};
@@ -97,7 +97,7 @@ auto chunk::ensure_wall_mesh() noexcept -> wall_mesh_tuple
v[j] = { quad[j], texcoords[j], depth, };
}
- auto indexes = make_index_array<2>(0, count);
+ auto indexes = make_index_array<2>(count);
const auto vertex_view = ArrayView{vertexes.data(), count};
const auto vert_index_view = ArrayView{indexes.data(), count};
@@ -109,4 +109,49 @@ auto chunk::ensure_wall_mesh() noexcept -> wall_mesh_tuple
return { wall_mesh, wall_indexes, count };
}
+auto chunk::ensure_scenery_mesh() noexcept -> scenery_mesh_tuple
+{
+ if (!_scenery_modified)
+ return { scenery_mesh, scenery_indexes, std::size_t(scenery_mesh.count()/6) };
+ _scenery_modified = false;
+
+ std::size_t count = 0;
+ for (std::size_t i = 0; i < TILE_COUNT; i++)
+ if (const auto& atlas = _scenery_atlases[i]; atlas && atlas->info().fps == 0)
+ scenery_indexes[count++] = std::uint8_t(i);
+
+#if 0
+ std::sort(scenery_indexes.begin(), scenery_indexes.begin() + count,
+ [this](std::uint8_t a, std::uint8_t b) {
+ return _scenery_atlases[a] < _scenery_atlases[b];
+ });
+#endif
+ std::array<std::array<vertex, 4>, TILE_COUNT> vertexes;
+ for (std::size_t k = 0; k < count; k++)
+ {
+ const std::uint8_t i = scenery_indexes[k];
+ const local_coords pos{i};
+ const auto& atlas = _scenery_atlases[i];
+ const auto& fr = _scenery_variants[i];
+ const auto quad = atlas->frame_quad(Vector3(pos.x, pos.y, 0) * TILE_SIZE, fr.r, fr.frame);
+ const auto& group = atlas->group(fr.r);
+ const auto texcoords = atlas->texcoords_for_frame(fr.r, fr.frame, !group.mirror_from.isEmpty());
+ const float depth = tile_shader::depth_value(pos, tile_shader::scenery_depth_offset);
+ auto& v = vertexes[k];
+ for (std::size_t j = 0; j < 4; j++)
+ v[j] = { quad[j], texcoords[j], depth };
+ }
+
+ const auto indexes = make_index_array(count);
+ const auto vertex_view = ArrayView{vertexes.data(), count};
+ const auto vert_index_view = ArrayView{indexes.data(), count};
+
+ GL::Mesh mesh{GL::MeshPrimitive::Triangles};
+ mesh.addVertexBuffer(GL::Buffer{vertex_view}, 0, tile_shader::Position{}, tile_shader::TextureCoordinates{}, tile_shader::Depth{})
+ .setIndexBuffer(GL::Buffer{vert_index_view}, 0, GL::MeshIndexType::UnsignedShort)
+ .setCount(std::int32_t(6 * count));
+ scenery_mesh = Utility::move(mesh);
+ return { scenery_mesh, scenery_indexes, count };
+}
+
} // namespace floormat
diff --git a/src/chunk.cpp b/src/chunk.cpp
index cb9760e1..94506f1a 100644
--- a/src/chunk.cpp
+++ b/src/chunk.cpp
@@ -16,6 +16,7 @@ bool chunk::empty(bool force) const noexcept
tile_atlas* chunk::ground_atlas_at(std::size_t i) const noexcept { return _ground_atlases[i].get(); }
tile_atlas* chunk::wall_atlas_at(std::size_t i) const noexcept { return _wall_atlases[i].get(); }
+anim_atlas* chunk::scenery_atlas_at(std::size_t i) const noexcept { return _scenery_atlases[i].get(); }
tile_ref chunk::operator[](std::size_t idx) noexcept { return { *this, std::uint8_t(idx) }; }
tile_proto chunk::operator[](std::size_t idx) const noexcept { return tile_proto(tile_ref { *const_cast<chunk*>(this), std::uint8_t(idx) }); }
diff --git a/src/chunk.hpp b/src/chunk.hpp
index 2cf7278e..d0df8e5d 100644
--- a/src/chunk.hpp
+++ b/src/chunk.hpp
@@ -66,13 +66,19 @@ struct chunk final
void mark_modified() noexcept;
struct ground_mesh_tuple final {
- GL::Mesh& mesh; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
- const ArrayView<const std::uint8_t> ids; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
+ GL::Mesh& mesh;
+ const ArrayView<const std::uint8_t> ids;
const std::size_t size;
};
struct wall_mesh_tuple final {
- GL::Mesh& mesh; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
- const ArrayView<const std::uint16_t> ids; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
+ GL::Mesh& mesh;
+ const ArrayView<const std::uint16_t> ids;
+ const std::size_t size;
+ };
+
+ struct scenery_mesh_tuple final {
+ GL::Mesh& mesh;
+ const ArrayView<const std::uint8_t> ids;
const std::size_t size;
};
@@ -82,6 +88,9 @@ struct chunk final
wall_mesh_tuple ensure_wall_mesh() noexcept;
tile_atlas* wall_atlas_at(std::size_t i) const noexcept;
+ scenery_mesh_tuple ensure_scenery_mesh() noexcept;
+ anim_atlas* scenery_atlas_at(std::size_t i) const noexcept;
+
void ensure_passability() noexcept;
#ifdef FLOORMAT_64
@@ -106,6 +115,7 @@ private:
std::array<std::uint16_t, TILE_COUNT*2> wall_indexes = {};
std::array<variant_t, TILE_COUNT*2> _wall_variants = {};
std::array<std::shared_ptr<anim_atlas>, TILE_COUNT> _scenery_atlases;
+ std::array<std::uint8_t, TILE_COUNT> scenery_indexes = {};
std::array<scenery, TILE_COUNT> _scenery_variants = {};
template<bool> struct lqt_ops;
@@ -113,7 +123,7 @@ private:
std::unique_ptr<lqt> _lqt_move, _lqt_shoot, _lqt_view;
std::vector<loose_quadtree::BoundingBox<std::int16_t>> _bboxes;
- GL::Mesh ground_mesh{NoCreate}, wall_mesh{NoCreate};
+ GL::Mesh ground_mesh{NoCreate}, wall_mesh{NoCreate}, scenery_mesh{NoCreate};
mutable bool _maybe_empty : 1 = true,
_ground_modified : 1 = true,
_walls_modified : 1 = true,