1 #include "ChunkRenderer.hpp"
4 #include "../app/Assets.hpp"
5 #include "../graphics/BlockLighting.hpp"
6 #include "../graphics/Viewport.hpp"
11 ChunkRenderer::ChunkRenderer(World &world, int rd)
15 , side_length(2 * rd + 1)
16 , total_length(side_length * side_length * side_length)
18 , stride(1, side_length, side_length * side_length)
19 , models(total_length)
20 , chunks(total_length)
27 void ChunkRenderer::LoadTextures(const AssetLoader &loader, const TextureIndex &tex_index) {
29 loader.LoadTextures(tex_index, block_tex);
30 block_tex.FilterNearest();
34 bool ChunkRenderer::InRange(const Chunk::Pos &pos) const noexcept {
35 return manhattan_radius(pos - base) <= render_dist;
38 int ChunkRenderer::IndexOf(const Chunk::Pos &pos) const noexcept {
44 return mod_pos.x * stride.x
45 + mod_pos.y * stride.y
46 + mod_pos.z * stride.z;
50 void ChunkRenderer::Rebase(const Chunk::Pos &new_base) {
51 if (new_base == base) return;
53 Chunk::Pos diff(new_base - base);
55 if (manhattan_radius(diff) > render_dist) {
56 // that's more than half, so probably not worth shifting
63 Shift(Block::FACE_RIGHT);
67 Shift(Block::FACE_LEFT);
71 Shift(Block::FACE_UP);
75 Shift(Block::FACE_DOWN);
79 Shift(Block::FACE_FRONT);
83 Shift(Block::FACE_BACK);
88 int ChunkRenderer::GetCol(int c) const noexcept {
90 if (c < 0) c += side_length;
94 void ChunkRenderer::Shift(Block::Face f) {
95 int a_axis = Block::Axis(f);
96 int b_axis = (a_axis + 1) % 3;
97 int c_axis = (a_axis + 2) % 3;
98 int dir = Block::Direction(f);
100 int a = GetCol(base[a_axis] + (render_dist * dir));
101 int a_stride = a * stride[a_axis];
102 for (int b = 0; b < side_length; ++b) {
103 int b_stride = b * stride[b_axis];
104 for (int c = 0; c < side_length; ++c) {
105 int bc_stride = b_stride + c * stride[c_axis];
106 int index = a_stride + bc_stride;
108 chunks[index] = nullptr;
111 int neighbor = ((a - dir + side_length) % side_length) * stride[a_axis] + bc_stride;
112 if (chunks[neighbor] && chunks[neighbor]->HasNeighbor(f)) {
113 chunks[index] = &chunks[neighbor]->GetNeighbor(f);
114 chunks[index]->InvalidateModel();
122 void ChunkRenderer::Rescan() {
123 chunks.assign(total_length, nullptr);
128 void ChunkRenderer::Scan() {
129 for (Chunk &chunk : world.Loader().Loaded()) {
130 if (!InRange(chunk.Position())) continue;
131 int index = IndexOf(chunk.Position());
132 if (!chunks[index]) {
133 chunks[index] = &chunk;
134 chunk.InvalidateModel();
140 void ChunkRenderer::Update(int dt) {
141 if (MissingChunks()) {
145 // maximum of 1000 per second too high?
146 for (int i = 0, updates = 0; i < total_length && updates < dt; ++i) {
147 if (chunks[i] && chunks[i]->ShouldUpdateModel()) {
148 chunks[i]->Update(models[i]);
155 void ChunkRenderer::Render(Viewport &viewport) {
156 BlockLighting &chunk_prog = viewport.ChunkProgram();
157 chunk_prog.SetTexture(block_tex);
158 chunk_prog.SetFogDensity(fog_density);
160 for (int i = 0; i < total_length; ++i) {
161 if (!chunks[i]) continue;
162 glm::mat4 m(chunks[i]->Transform(base));
163 glm::mat4 mvp(chunk_prog.GetVP() * m);
164 if (!CullTest(Chunk::Bounds(), mvp)) {
165 if (chunks[i]->ShouldUpdateModel()) {
166 chunks[i]->Update(models[i]);