move tile rendering to separate class | Update MapTile.cpp, MapTile.h, and 7 more files...

This commit is contained in:
Skarn
2022-01-05 22:08:59 +03:00
parent e2ba831605
commit 822ea99238
9 changed files with 712 additions and 605 deletions

View File

@@ -43,6 +43,7 @@ MapTile::MapTile( int pX
, tile_mode mode
)
: AsyncObject(pFilename)
, _renderer(this)
, index(TileIndex(pX, pZ))
, xbase(pX * TILESIZE)
, zbase(pZ * TILESIZE)
@@ -360,14 +361,7 @@ void MapTile::finishLoading()
mChunks[x][z] = std::make_unique<MapChunk> (this, &theFile, mBigAlpha, _mode, _context);
auto& chunk = mChunks[x][z];
auto& chunk_render_instance = _chunk_instance_data[nextChunk];
chunk_render_instance.ChunkHoles_DrawImpass_TexLayerCount_CantPaint[0] = chunk->holes;
chunk_render_instance.ChunkHoles_DrawImpass_TexLayerCount_CantPaint[1] = chunk->header_flags.flags.impass;
chunk_render_instance.ChunkHoles_DrawImpass_TexLayerCount_CantPaint[2] = chunk->texture_set->num();
chunk_render_instance.ChunkHoles_DrawImpass_TexLayerCount_CantPaint[3] = 0;
chunk_render_instance.AreaIDColor_Pad2_DrawSelection[0] = chunk->areaID;
chunk_render_instance.AreaIDColor_Pad2_DrawSelection[3] = 0;
_renderer.initChunkData(chunk.get());
}
theFile.close();
@@ -421,312 +415,6 @@ void MapTile::convert_alphamap(bool to_big_alpha)
}
}
void MapTile::draw (OpenGL::Scoped::use_program& mcnk_shader
, const glm::vec3& camera
, bool show_unpaintable_chunks
, bool draw_paintability_overlay
, bool is_selected
)
{
ZoneScopedN(NOGGIT_CURRENT_FUNCTION);
static constexpr unsigned NUM_SAMPLERS = 11;
if (!finished)
[[unlikely]]
{
return;
}
if (!_uploaded)
[[unlikely]]
{
uploadTextures();
_buffers.upload();
gl.bindBuffer(GL_UNIFORM_BUFFER, _chunk_instance_data_ubo);
gl.bindBufferRange(GL_UNIFORM_BUFFER, OpenGL::ubo_targets::CHUNK_INSTANCE_DATA,
_chunk_instance_data_ubo, 0, sizeof(OpenGL::ChunkInstanceDataUniformBlock) * 256);
gl.bufferData(GL_UNIFORM_BUFFER, sizeof(OpenGL::ChunkInstanceDataUniformBlock) * 256, NULL, GL_DYNAMIC_DRAW);
MapTileDrawCall& draw_call = _draw_calls.emplace_back();
draw_call.start_chunk = 0;
draw_call.n_chunks = 256;
std::fill(draw_call.samplers.begin(), draw_call.samplers.end(), -1);
gl.genQueries(1, &_tile_occlusion_query);
_uploaded = true;
}
bool alphamap_bound = false;
bool heightmap_bound = false;
bool shadowmap_bound = false;
bool mccv_bound = false;
_texture_not_loaded = false;
// figure out if we need to update based on paintability
bool need_paintability_update = false;
if (_requires_paintability_recalc && draw_paintability_overlay && show_unpaintable_chunks)
[[unlikely]]
{
auto cur_tex = Noggit::Ui::selected_texture::get();
for (int j = 0; j < 16; ++j)
{
for (int i = 0; i < 16; ++i)
{
auto& chunk = mChunks[j][i];
bool cant_paint = cur_tex && !chunk->canPaintTexture(*cur_tex);
if (chunk->currently_paintable != !cant_paint)
{
chunk->currently_paintable = !cant_paint;
_chunk_instance_data[i * 16 + j].ChunkHoles_DrawImpass_TexLayerCount_CantPaint[3] = cant_paint;
need_paintability_update = true;
}
}
}
_requires_paintability_recalc = false;
}
// run chunk updates. running this when splitdraw call detected unused sampler configuration as well.
if (_chunk_update_flags || is_selected != _selected || need_paintability_update || _requires_sampler_reset || _texture_not_loaded)
{
gl.bindBuffer(GL_UNIFORM_BUFFER, _chunk_instance_data_ubo);
if (_requires_sampler_reset)
[[unlikely]]
{
_draw_calls.clear();
MapTileDrawCall& draw_call = _draw_calls.emplace_back();
std::fill(draw_call.samplers.begin(), draw_call.samplers.end(), -1);
draw_call.start_chunk = 0;
draw_call.n_chunks = 256;
}
_selected = is_selected;
for (int i = 0; i < 256; ++i)
{
int chunk_x = i / 16;
int chunk_y = i % 16;
auto& chunk = mChunks[chunk_y][chunk_x];
_chunk_instance_data[i].ChunkXYZBase_Pad1 = {chunk->xbase, chunk->ybase, chunk->zbase, 1.0};
unsigned flags = chunk->getUpdateFlags();
if (flags & ChunkUpdateFlags::ALPHAMAP || _requires_sampler_reset || _texture_not_loaded)
{
gl.activeTexture(GL_TEXTURE0 + 3);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, _alphamap_tex);
alphamap_bound = true;
chunk->texture_set->uploadAlphamapData();
if (!_split_drawcall && !fillSamplers(chunk.get(), i, _draw_calls.size() - 1))
{
_split_drawcall = true;
}
}
if (!flags)
continue;
if (flags & ChunkUpdateFlags::VERTEX || flags & ChunkUpdateFlags::NORMALS)
{
heightmap_bound = true;
if (flags & ChunkUpdateFlags::VERTEX)
{
chunk->updateVerticesData();
}
gl.activeTexture(GL_TEXTURE0 + 0);
gl.bindTexture(GL_TEXTURE_2D, _height_tex);
gl.texSubImage2D(GL_TEXTURE_2D, 0, 0, i, mapbufsize, 1, GL_RGBA, GL_FLOAT, _chunk_heightmap_buffer.data() + i * mapbufsize * 4);
}
if (flags & ChunkUpdateFlags::MCCV)
{
mccv_bound = true;
gl.activeTexture(GL_TEXTURE0 + 1);
gl.bindTexture(GL_TEXTURE_2D, _mccv_tex);
chunk->update_vertex_colors();
}
if (flags & ChunkUpdateFlags::SHADOW)
{
shadowmap_bound = true;
gl.activeTexture(GL_TEXTURE0 + 2);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, _shadowmap_tex);
chunk->update_shadows();
}
if (flags & ChunkUpdateFlags::HOLES)
{
_chunk_instance_data[i].ChunkHoles_DrawImpass_TexLayerCount_CantPaint[0] = chunk->holes;
}
if (flags & ChunkUpdateFlags::FLAGS)
{
_chunk_instance_data[i].ChunkHoles_DrawImpass_TexLayerCount_CantPaint[1] = chunk->header_flags.flags.impass;
for (int k = 0; k < chunk->texture_set->num(); ++k)
{
unsigned layer_flags = chunk->texture_set->flag(k);
auto flag_view = reinterpret_cast<MCLYFlags*>(&layer_flags);
_chunk_instance_data[i].ChunkTexDoAnim[k] = flag_view->animation_enabled;
_chunk_instance_data[i].ChunkTexAnimSpeed[k] = flag_view->animation_speed;
_chunk_instance_data[i].ChunkTexAnimDir[k] = flag_view->animation_rotation;
}
_chunk_instance_data[i].ChunkTexDoAnim[1] = chunk->header_flags.flags.impass;
}
if (flags & ChunkUpdateFlags::AREA_ID)
{
_chunk_instance_data[i].AreaIDColor_Pad2_DrawSelection[0] = chunk->areaID;
}
_chunk_instance_data[i].AreaIDColor_Pad2_DrawSelection[3] = _selected;
chunk->endChunkUpdates();
if (_texture_not_loaded)
chunk->registerChunkUpdate(ChunkUpdateFlags::ALPHAMAP);
}
_requires_sampler_reset = false;
if (_split_drawcall)
{
_draw_calls.clear();
MapTileDrawCall& draw_call = _draw_calls.emplace_back();
std::fill(draw_call.samplers.begin(), draw_call.samplers.end(), -1);
draw_call.start_chunk = 0;
draw_call.n_chunks = 0;
for (int i = 0; i < 256; ++i)
{
auto& chunk = mChunks[i % 16][i / 16];
if (!fillSamplers(chunk.get(), i, _draw_calls.size() - 1))
{
MapTileDrawCall& previous_draw_call = _draw_calls[_draw_calls.size() - 1];
unsigned new_start = previous_draw_call.start_chunk + previous_draw_call.n_chunks;
MapTileDrawCall& new_draw_call = _draw_calls.emplace_back();
std::fill(new_draw_call.samplers.begin(), new_draw_call.samplers.end(), -1);
new_draw_call.start_chunk = new_start;
new_draw_call.n_chunks = 1;
fillSamplers(chunk.get(), i, _draw_calls.size() - 1);
}
else
{
MapTileDrawCall& last_draw_call = _draw_calls.back();
last_draw_call.n_chunks++;
assert(last_draw_call.n_chunks <= 256);
}
}
if (_draw_calls.size() <= 1)
{
_split_drawcall = false;
_requires_sampler_reset = true;
}
}
endChunkUpdates();
if (_texture_not_loaded)
registerChunkUpdate(ChunkUpdateFlags::ALPHAMAP);
gl.bufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(OpenGL::ChunkInstanceDataUniformBlock) * 256, &_chunk_instance_data);
}
recalcExtents();
// do not draw anything when textures did not finish loading
if (_texture_not_loaded)
[[unlikely]]
{
gl.bindBufferRange(GL_UNIFORM_BUFFER, OpenGL::ubo_targets::CHUNK_INSTANCE_DATA,
_chunk_instance_data_ubo, 0, sizeof(OpenGL::ChunkInstanceDataUniformBlock) * 256);
return;
}
gl.bindBufferRange(GL_UNIFORM_BUFFER, OpenGL::ubo_targets::CHUNK_INSTANCE_DATA,
_chunk_instance_data_ubo, 0, sizeof(OpenGL::ChunkInstanceDataUniformBlock) * 256);
for (auto& draw_call : _draw_calls)
{
if (!alphamap_bound)
{
gl.activeTexture(GL_TEXTURE0 + 3);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, _alphamap_tex);
}
if (!shadowmap_bound)
{
gl.activeTexture(GL_TEXTURE0 + 2);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, _shadowmap_tex);
}
if (!mccv_bound)
{
gl.activeTexture(GL_TEXTURE0 + 1);
gl.bindTexture(GL_TEXTURE_2D, _mccv_tex);
}
if (!heightmap_bound)
{
gl.activeTexture(GL_TEXTURE0 + 0);
gl.bindTexture(GL_TEXTURE_2D, _height_tex);
}
float tile_center_x = xbase + TILESIZE / 2.0f;
float tile_center_z = zbase + TILESIZE / 2.0f;
bool is_lod = misc::dist(tile_center_x, tile_center_z, camera.x, camera.z) > TILESIZE * 3;
mcnk_shader.uniform("lod_level", int(is_lod));
assert(draw_call.n_chunks <= 256);
mcnk_shader.uniform("base_instance", static_cast<int>(draw_call.start_chunk));
for (int i = 0; i < NUM_SAMPLERS; ++i)
{
gl.activeTexture(GL_TEXTURE0 + 5 + i);
if (draw_call.samplers[i] < 0)
{
gl.bindTexture(GL_TEXTURE_2D_ARRAY, 0);
continue;
}
gl.bindTexture(GL_TEXTURE_2D_ARRAY, draw_call.samplers[i]);
}
if (is_lod)
{
gl.drawElementsInstanced(GL_TRIANGLES, 192, GL_UNSIGNED_SHORT, reinterpret_cast<void*>(768 * sizeof(std::uint16_t)), draw_call.n_chunks);
}
else
{
gl.drawElementsInstanced(GL_TRIANGLES, 768, GL_UNSIGNED_SHORT, nullptr, draw_call.n_chunks);
}
}
}
bool MapTile::intersect (math::ray const& ray, selection_result* results) const
{
@@ -1809,81 +1497,8 @@ void MapTile::setVertexColorImage(QImage const& image, int mode)
}
}
void MapTile::unload()
{
if (_uploaded)
{
_chunk_texture_arrays.unload();
_buffers.unload();
_uploaded = false;
gl.deleteQueries(1, &_tile_occlusion_query);
}
if (_mfbo_buffer_are_setup)
{
_mfbo_vaos.unload();
_mfbo_vbos.unload();
_mfbo_buffer_are_setup = false;
}
_chunk_update_flags = ChunkUpdateFlags::VERTEX | ChunkUpdateFlags::ALPHAMAP
| ChunkUpdateFlags::SHADOW | ChunkUpdateFlags::MCCV
| ChunkUpdateFlags::NORMALS| ChunkUpdateFlags::HOLES
| ChunkUpdateFlags::AREA_ID| ChunkUpdateFlags::FLAGS;
}
void MapTile::uploadTextures()
{
_chunk_texture_arrays.upload();
gl.activeTexture(GL_TEXTURE0 + 0);
gl.bindTexture(GL_TEXTURE_2D, _height_tex);
gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, mapbufsize, 256, 0, GL_RGBA, GL_FLOAT,nullptr);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
//gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//const GLint swizzleMask[] = {GL_RED, GL_RED, GL_RED, GL_ONE};
//gl.texParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
gl.bindTexture(GL_TEXTURE_2D, 0);
gl.activeTexture(GL_TEXTURE0 + 2);
gl.bindTexture(GL_TEXTURE_2D, _mccv_tex);
gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, mapbufsize, 256, 0, GL_RGB, GL_FLOAT, nullptr);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
//gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
gl.bindTexture(GL_TEXTURE_2D, 0);
gl.activeTexture(GL_TEXTURE0 + 4);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, _alphamap_tex);
gl.texImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGB, 64, 64, 256, 0, GL_RGB, GL_FLOAT,nullptr);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, 0);
gl.activeTexture(GL_TEXTURE0 + 3);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, _shadowmap_tex);
gl.texImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RED, 64, 64, 256, 0, GL_RED, GL_UNSIGNED_BYTE,nullptr);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, 0);
}
void MapTile::recalcExtents()
{
@@ -1966,133 +1581,11 @@ void MapTile::recalcObjectInstanceExtents()
}
void MapTile::doTileOcclusionQuery(OpenGL::Scoped::use_program& occlusion_shader)
{
if (_tile_occlusion_query_in_use || !_uploaded)
return;
_tile_occlusion_query_in_use = true;
gl.beginQuery(GL_ANY_SAMPLES_PASSED, _tile_occlusion_query);
occlusion_shader.uniform("aabb", _combined_extents.data(), _combined_extents.size());
gl.drawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, nullptr);
gl.endQuery(GL_ANY_SAMPLES_PASSED);
}
bool MapTile::getTileOcclusionQueryResult(glm::vec3 const& camera)
{
// returns true if tile is not occluded by other tiles
if (!_tile_occlusion_query_in_use)
[[unlikely]]
{
return !tile_occluded;
}
if (!_uploaded)
return !tile_occluded;
if (misc::pointInside(camera, _combined_extents))
{
_tile_occlusion_query_in_use = false;
return true;
}
GLint result;
gl.getQueryObjectiv(_tile_occlusion_query, GL_QUERY_RESULT_AVAILABLE, &result);
if (result != GL_TRUE)
{
return tile_occlusion_cull_override || !tile_occluded;
}
if (!tile_occlusion_cull_override)
{
gl.getQueryObjectiv(_tile_occlusion_query, GL_QUERY_RESULT, &result);
}
else
{
result = true;
}
_tile_occlusion_query_in_use = false;
tile_occlusion_cull_override = false;
return static_cast<bool>(result);
}
void MapTile::calcCamDist(glm::vec3 const& camera)
{
_cam_dist = glm::distance(camera, _center);
}
bool MapTile::fillSamplers(MapChunk* chunk, unsigned chunk_index, unsigned int draw_call_index)
{
MapTileDrawCall& draw_call = _draw_calls[draw_call_index];
_chunk_instance_data[chunk_index].ChunkHoles_DrawImpass_TexLayerCount_CantPaint[2] = chunk->texture_set->num();
static constexpr unsigned NUM_SAMPLERS = 11;
_chunk_instance_data[chunk_index].ChunkTextureSamplers[0] = 0;
_chunk_instance_data[chunk_index].ChunkTextureSamplers[1] = 0;
_chunk_instance_data[chunk_index].ChunkTextureSamplers[2] = 0;
_chunk_instance_data[chunk_index].ChunkTextureSamplers[3] = 0;
_chunk_instance_data[chunk_index].ChunkTextureArrayIDs[0] = -1;
_chunk_instance_data[chunk_index].ChunkTextureArrayIDs[1] = -1;
_chunk_instance_data[chunk_index].ChunkTextureArrayIDs[2] = -1;
_chunk_instance_data[chunk_index].ChunkTextureArrayIDs[3] = -1;
auto& chunk_textures = (*chunk->texture_set->getTextures());
for (int k = 0; k < chunk->texture_set->num(); ++k)
{
chunk_textures[k]->upload();
if (!chunk_textures[k]->is_uploaded())
{
_texture_not_loaded = true;
continue;
}
GLuint tex_array = (*chunk->texture_set->getTextures())[k]->texture_array();
int tex_index = (*chunk->texture_set->getTextures())[k]->array_index();
int sampler_id = -1;
for (int n = 0; n < draw_call.samplers.size(); ++n)
{
if (draw_call.samplers[n] == tex_array)
{
sampler_id = n;
break;
}
else if (draw_call.samplers[n] < 0)
{
draw_call.samplers[n] = tex_array;
sampler_id = n;
break;
}
}
// If there are not enough sampler slots (11) we have to split the drawcall :(.
// Extremely infrequent for terrain. Never for Blizzard terrain as their tilesets
// use uniform BLP format per map.
if (sampler_id < 0)
[[unlikely]]
{
return false;
}
_chunk_instance_data[chunk_index].ChunkTextureSamplers[k] = sampler_id;
_chunk_instance_data[chunk_index].ChunkTextureArrayIDs[k] = (*chunk->texture_set->getTextures())[k]->is_specular() ? tex_index : -tex_index;
}
return true;
}
void MapTile::recalcCombinedExtents()
{
if (!_combined_extents_dirty)

View File

@@ -14,6 +14,7 @@
#include <noggit/ContextObject.hpp>
#include <noggit/Misc.h>
#include <external/tsl/robin_map.h>
#include <noggit/rendering/TileRender.hpp>
#include <map>
#include <string>
@@ -26,17 +27,20 @@ namespace math
struct vector_3d;
}
namespace Noggit::Rendering
{
class TileRender;
}
class World;
struct MapTileDrawCall
{
std::array<int, 11> samplers;
unsigned start_chunk;
unsigned n_chunks;
};
class MapTile : public AsyncObject
{
friend class Noggit::Rendering::TileRender;
friend class MapChunk;
friend class TextureSet;
public:
MapTile( int x0
, int z0
@@ -51,8 +55,8 @@ public:
);
~MapTile();
void finishLoading();
void waitForChildrenLoaded();
void finishLoading() override;
void waitForChildrenLoaded() override;
//! \todo on destruction, unload ModelInstances and WMOInstances on this tile:
// a) either keep up the information what tiles the instances are on at all times
@@ -76,28 +80,25 @@ public:
void convert_alphamap(bool to_big_alpha);
//! \brief Get chunk for sub offset x,z.
[[nodiscard]]
MapChunk* getChunk(unsigned int x, unsigned int z);
//! \todo map_index style iterators
[[nodiscard]]
std::vector<MapChunk*> chunks_in_range (glm::vec3 const& pos, float radius) const;
[[nodiscard]]
std::vector<MapChunk*> chunks_in_rect (glm::vec3 const& pos, float radius) const;
const TileIndex index;
float xbase, zbase;
std::atomic<bool> changed;
unsigned objects_frustum_cull_test = 0;
bool tile_occluded = false;
bool tile_frustum_culled = true;
bool tile_occlusion_cull_override = true;
void draw (OpenGL::Scoped::use_program& mcnk_shader
, const glm::vec3& camera
, bool show_unpaintable_chunks
, bool draw_paintability_overlay
, bool is_selected
);
bool intersect (math::ray const&, selection_result*) const;
void drawWater ( math::frustum const& frustum
, const glm::vec3& camera
, bool camera_moved
@@ -161,40 +162,27 @@ public:
std::array<glm::vec3, 2>& getExtents() { return _extents; };
std::array<glm::vec3, 2>& getCombinedExtents() { return _combined_extents; };
void unload();
GLuint getAlphamapTextureHandle() { return _alphamap_tex; };
World* getWorld() { return _world; };
void notifyTileRendererOnSelectedTextureChange() { _requires_paintability_recalc = true; };
[[nodiscard]]
tsl::robin_map<AsyncObject*, std::vector<SceneObject*>> const& getObjectInstances() const { return object_instances; };
void doTileOcclusionQuery(OpenGL::Scoped::use_program& occlusion_shader);
bool getTileOcclusionQueryResult(glm::vec3 const& camera);
void discardTileOcclusionQuery() { _tile_occlusion_query_in_use = false; }
float camDist() { return _cam_dist; }
void calcCamDist(glm::vec3 const& camera);
void markExtentsDirty() { _extents_dirty = true; }
void tagCombinedExtents(bool state) { _combined_extents_dirty = state; };
private:
Noggit::Rendering::TileRender* renderer() { return &_renderer; };
void uploadTextures();
bool fillSamplers(MapChunk* chunk, unsigned chunk_index, unsigned draw_call_index);
private:
tile_mode _mode;
bool _tile_is_being_reloaded;
bool _uploaded = false;
bool _selected = false;
bool _split_drawcall = false;
bool _requires_sampler_reset = false;
bool _requires_paintability_recalc = true;
bool _requires_object_extents_recalc = true;
bool _texture_not_loaded = false;
bool _extents_dirty = true;
bool _combined_extents_dirty = true;
bool _requires_object_extents_recalc = true;
std::array<glm::vec3, 2> _extents;
std::array<glm::vec3, 2> _object_instance_extents;
@@ -206,34 +194,8 @@ private:
glm::vec3 mMinimumValues[3 * 3];
glm::vec3 mMaximumValues[3 * 3];
bool _mfbo_buffer_are_setup = false;
OpenGL::Scoped::deferred_upload_vertex_arrays<2> _mfbo_vaos;
GLuint const& _mfbo_bottom_vao = _mfbo_vaos[0];
GLuint const& _mfbo_top_vao = _mfbo_vaos[1];
OpenGL::Scoped::deferred_upload_buffers<3> _mfbo_vbos;
GLuint const& _mfbo_bottom_vbo = _mfbo_vbos[0];
GLuint const& _mfbo_top_vbo = _mfbo_vbos[1];
GLuint const& _mfbo_indices = _mfbo_vbos[2];
OpenGL::Scoped::deferred_upload_textures<4> _chunk_texture_arrays;
GLuint const& _height_tex = _chunk_texture_arrays[0];
GLuint const& _mccv_tex = _chunk_texture_arrays[1];
GLuint const& _shadowmap_tex = _chunk_texture_arrays[2];
GLuint const& _alphamap_tex = _chunk_texture_arrays[3];
GLuint _tile_occlusion_query;
bool _tile_occlusion_query_in_use = false;
OpenGL::Scoped::deferred_upload_buffers<1> _buffers;
GLuint const& _chunk_instance_data_ubo = _buffers[0];
OpenGL::ChunkInstanceDataUniformBlock _chunk_instance_data[256];
std::array<float, 145 * 256 * 4> _chunk_heightmap_buffer;
unsigned _chunk_update_flags;
std::vector<MapTileDrawCall> _draw_calls;
// MHDR:
int mFlags;
bool mBigAlpha;
@@ -247,12 +209,22 @@ private:
tsl::robin_map<AsyncObject*, std::vector<SceneObject*>> object_instances; // only includes M2 and WMO. perhaps a medium common ancestor then?
std::unique_ptr<MapChunk> mChunks[16][16];
std::array<float, 145 * 256 * 4> _chunk_heightmap_buffer;
bool _load_models;
World* _world;
bool _mfbo_buffer_are_setup = false;
OpenGL::Scoped::deferred_upload_vertex_arrays<2> _mfbo_vaos;
GLuint const& _mfbo_bottom_vao = _mfbo_vaos[0];
GLuint const& _mfbo_top_vao = _mfbo_vaos[1];
OpenGL::Scoped::deferred_upload_buffers<3> _mfbo_vbos;
GLuint const& _mfbo_bottom_vbo = _mfbo_vbos[0];
GLuint const& _mfbo_top_vbo = _mfbo_vbos[1];
GLuint const& _mfbo_indices = _mfbo_vbos[2];
Noggit::Rendering::TileRender _renderer;
Noggit::NoggitRenderContext _context;
friend class MapChunk;
friend class TextureSet;
};

View File

@@ -5123,7 +5123,7 @@ void MapView::unloadOpenglData(bool from_manager)
for (MapTile* tile : _world->mapIndex.loaded_tiles())
{
tile->unload();
tile->renderer()->unload();
tile->Water.unload();
for (int i = 0; i < 16; ++i)

View File

@@ -86,11 +86,11 @@ void WMOInstance::draw ( OpenGL::Scoped::use_program& wmo_shader
{
for (auto& tile : getTiles())
{
if (tile->objects_frustum_cull_test && !tile->tile_occluded)
if (tile->renderer()->objectsFrustumCullTest() && !tile->renderer()->isOccluded())
{
region_visible = tile->objects_frustum_cull_test;
region_visible = tile->renderer()->objectsFrustumCullTest();
if (tile->objects_frustum_cull_test > 1)
if (tile->renderer()->objectsFrustumCullTest() > 1)
break;
}
}

View File

@@ -2956,7 +2956,7 @@ void World::notifyTileRendererOnSelectedTextureChange()
for (MapTile* tile : mapIndex.loaded_tiles())
{
tile->notifyTileRendererOnSelectedTextureChange();
tile->renderer()->notifyTileRendererOnSelectedTextureChange();
}
}

View File

@@ -352,8 +352,8 @@ public:
bool need_model_updates = false;
void loadAllTiles();
unsigned getNumLoadedTiles() { return _n_loaded_tiles; };
unsigned getNumRenderedTiles() { return _n_rendered_tiles; };
unsigned getNumLoadedTiles() const { return _n_loaded_tiles; };
unsigned getNumRenderedTiles() const { return _n_rendered_tiles; };
protected:
void update_models_by_filename();

View File

@@ -0,0 +1,540 @@
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#include <noggit/rendering/TileRender.hpp>
#include <noggit/MapTile.h>
#include <noggit/MapChunk.h>
#include <noggit/ui/TexturingGUI.h>
#include <external/tracy/Tracy.hpp>
using namespace Noggit::Rendering;
TileRender::TileRender(MapTile* map_tile)
: _map_tile(map_tile)
{
}
void TileRender::upload()
{
uploadTextures();
_buffers.upload();
gl.bindBuffer(GL_UNIFORM_BUFFER, _chunk_instance_data_ubo);
gl.bindBufferRange(GL_UNIFORM_BUFFER, OpenGL::ubo_targets::CHUNK_INSTANCE_DATA,
_chunk_instance_data_ubo, 0, sizeof(OpenGL::ChunkInstanceDataUniformBlock) * 256);
gl.bufferData(GL_UNIFORM_BUFFER, sizeof(OpenGL::ChunkInstanceDataUniformBlock) * 256, NULL, GL_DYNAMIC_DRAW);
MapTileDrawCall& draw_call = _draw_calls.emplace_back();
draw_call.start_chunk = 0;
draw_call.n_chunks = 256;
std::fill(draw_call.samplers.begin(), draw_call.samplers.end(), -1);
gl.genQueries(1, &_tile_occlusion_query);
_uploaded = true;
}
void TileRender::unload()
{
if (_uploaded)
{
_chunk_texture_arrays.unload();
_buffers.unload();
_uploaded = false;
gl.deleteQueries(1, &_tile_occlusion_query);
}
_map_tile->_chunk_update_flags = ChunkUpdateFlags::VERTEX | ChunkUpdateFlags::ALPHAMAP
| ChunkUpdateFlags::SHADOW | ChunkUpdateFlags::MCCV
| ChunkUpdateFlags::NORMALS| ChunkUpdateFlags::HOLES
| ChunkUpdateFlags::AREA_ID| ChunkUpdateFlags::FLAGS;
}
void TileRender::draw (OpenGL::Scoped::use_program& mcnk_shader
, const glm::vec3& camera
, bool show_unpaintable_chunks
, bool draw_paintability_overlay
, bool is_selected
)
{
ZoneScopedN(NOGGIT_CURRENT_FUNCTION);
static constexpr unsigned NUM_SAMPLERS = 11;
if (!_map_tile->finished)
[[unlikely]]
{
return;
}
if (!_uploaded)
[[unlikely]]
{
upload();
}
bool alphamap_bound = false;
bool heightmap_bound = false;
bool shadowmap_bound = false;
bool mccv_bound = false;
_texture_not_loaded = false;
// figure out if we need to update based on paintability
bool need_paintability_update = false;
if (_requires_paintability_recalc && draw_paintability_overlay && show_unpaintable_chunks)
[[unlikely]]
{
auto cur_tex = Noggit::Ui::selected_texture::get();
for (int j = 0; j < 16; ++j)
{
for (int i = 0; i < 16; ++i)
{
auto& chunk = _map_tile->mChunks[j][i];
bool cant_paint = cur_tex && !chunk->canPaintTexture(*cur_tex);
if (chunk->currently_paintable != !cant_paint)
{
chunk->currently_paintable = !cant_paint;
_chunk_instance_data[i * 16 + j].ChunkHoles_DrawImpass_TexLayerCount_CantPaint[3] = cant_paint;
need_paintability_update = true;
}
}
}
_requires_paintability_recalc = false;
}
// run chunk updates. running this when splitdraw call detected unused sampler configuration as well.
if (_map_tile->_chunk_update_flags || is_selected != _selected || need_paintability_update || _requires_sampler_reset || _texture_not_loaded)
{
gl.bindBuffer(GL_UNIFORM_BUFFER, _chunk_instance_data_ubo);
if (_requires_sampler_reset)
[[unlikely]]
{
_draw_calls.clear();
MapTileDrawCall& draw_call = _draw_calls.emplace_back();
std::fill(draw_call.samplers.begin(), draw_call.samplers.end(), -1);
draw_call.start_chunk = 0;
draw_call.n_chunks = 256;
}
_selected = is_selected;
for (int i = 0; i < 256; ++i)
{
int chunk_x = i / 16;
int chunk_y = i % 16;
auto& chunk = _map_tile->mChunks[chunk_y][chunk_x];
_chunk_instance_data[i].ChunkXYZBase_Pad1 = {chunk->xbase, chunk->ybase, chunk->zbase, 1.0};
unsigned flags = chunk->getUpdateFlags();
if (flags & ChunkUpdateFlags::ALPHAMAP || _requires_sampler_reset || _texture_not_loaded)
{
gl.activeTexture(GL_TEXTURE0 + 3);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, _alphamap_tex);
alphamap_bound = true;
chunk->texture_set->uploadAlphamapData();
if (!_split_drawcall && !fillSamplers(chunk.get(), i, _draw_calls.size() - 1))
{
_split_drawcall = true;
}
}
if (!flags)
continue;
if (flags & ChunkUpdateFlags::VERTEX || flags & ChunkUpdateFlags::NORMALS)
{
heightmap_bound = true;
if (flags & ChunkUpdateFlags::VERTEX)
{
chunk->updateVerticesData();
}
gl.activeTexture(GL_TEXTURE0 + 0);
gl.bindTexture(GL_TEXTURE_2D, _height_tex);
gl.texSubImage2D(GL_TEXTURE_2D, 0, 0, i, mapbufsize, 1, GL_RGBA,
GL_FLOAT, _map_tile->_chunk_heightmap_buffer.data() + i * mapbufsize * 4);
}
if (flags & ChunkUpdateFlags::MCCV)
{
mccv_bound = true;
gl.activeTexture(GL_TEXTURE0 + 1);
gl.bindTexture(GL_TEXTURE_2D, _mccv_tex);
chunk->update_vertex_colors();
}
if (flags & ChunkUpdateFlags::SHADOW)
{
shadowmap_bound = true;
gl.activeTexture(GL_TEXTURE0 + 2);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, _shadowmap_tex);
chunk->update_shadows();
}
if (flags & ChunkUpdateFlags::HOLES)
{
_chunk_instance_data[i].ChunkHoles_DrawImpass_TexLayerCount_CantPaint[0] = chunk->holes;
}
if (flags & ChunkUpdateFlags::FLAGS)
{
_chunk_instance_data[i].ChunkHoles_DrawImpass_TexLayerCount_CantPaint[1] = chunk->header_flags.flags.impass;
for (int k = 0; k < chunk->texture_set->num(); ++k)
{
unsigned layer_flags = chunk->texture_set->flag(k);
auto flag_view = reinterpret_cast<MCLYFlags*>(&layer_flags);
_chunk_instance_data[i].ChunkTexDoAnim[k] = flag_view->animation_enabled;
_chunk_instance_data[i].ChunkTexAnimSpeed[k] = flag_view->animation_speed;
_chunk_instance_data[i].ChunkTexAnimDir[k] = flag_view->animation_rotation;
}
_chunk_instance_data[i].ChunkTexDoAnim[1] = chunk->header_flags.flags.impass;
}
if (flags & ChunkUpdateFlags::AREA_ID)
{
_chunk_instance_data[i].AreaIDColor_Pad2_DrawSelection[0] = chunk->areaID;
}
_chunk_instance_data[i].AreaIDColor_Pad2_DrawSelection[3] = _selected;
chunk->endChunkUpdates();
if (_texture_not_loaded)
chunk->registerChunkUpdate(ChunkUpdateFlags::ALPHAMAP);
}
_requires_sampler_reset = false;
if (_split_drawcall)
{
_draw_calls.clear();
MapTileDrawCall& draw_call = _draw_calls.emplace_back();
std::fill(draw_call.samplers.begin(), draw_call.samplers.end(), -1);
draw_call.start_chunk = 0;
draw_call.n_chunks = 0;
for (int i = 0; i < 256; ++i)
{
auto& chunk = _map_tile->mChunks[i % 16][i / 16];
if (!fillSamplers(chunk.get(), i, _draw_calls.size() - 1))
{
MapTileDrawCall& previous_draw_call = _draw_calls[_draw_calls.size() - 1];
unsigned new_start = previous_draw_call.start_chunk + previous_draw_call.n_chunks;
MapTileDrawCall& new_draw_call = _draw_calls.emplace_back();
std::fill(new_draw_call.samplers.begin(), new_draw_call.samplers.end(), -1);
new_draw_call.start_chunk = new_start;
new_draw_call.n_chunks = 1;
fillSamplers(chunk.get(), i, _draw_calls.size() - 1);
}
else
{
MapTileDrawCall& last_draw_call = _draw_calls.back();
last_draw_call.n_chunks++;
assert(last_draw_call.n_chunks <= 256);
}
}
if (_draw_calls.size() <= 1)
{
_split_drawcall = false;
_requires_sampler_reset = true;
}
}
_map_tile->endChunkUpdates();
if (_texture_not_loaded)
_map_tile->registerChunkUpdate(ChunkUpdateFlags::ALPHAMAP);
gl.bufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(OpenGL::ChunkInstanceDataUniformBlock) * 256,
&_chunk_instance_data);
}
_map_tile->recalcExtents();
// do not draw anything when textures did not finish loading
if (_texture_not_loaded)
[[unlikely]]
{
gl.bindBufferRange(GL_UNIFORM_BUFFER, OpenGL::ubo_targets::CHUNK_INSTANCE_DATA,
_chunk_instance_data_ubo, 0, sizeof(OpenGL::ChunkInstanceDataUniformBlock) * 256);
return;
}
gl.bindBufferRange(GL_UNIFORM_BUFFER, OpenGL::ubo_targets::CHUNK_INSTANCE_DATA,
_chunk_instance_data_ubo, 0, sizeof(OpenGL::ChunkInstanceDataUniformBlock) * 256);
for (auto& draw_call : _draw_calls)
{
if (!alphamap_bound)
{
gl.activeTexture(GL_TEXTURE0 + 3);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, _alphamap_tex);
}
if (!shadowmap_bound)
{
gl.activeTexture(GL_TEXTURE0 + 2);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, _shadowmap_tex);
}
if (!mccv_bound)
{
gl.activeTexture(GL_TEXTURE0 + 1);
gl.bindTexture(GL_TEXTURE_2D, _mccv_tex);
}
if (!heightmap_bound)
{
gl.activeTexture(GL_TEXTURE0 + 0);
gl.bindTexture(GL_TEXTURE_2D, _height_tex);
}
float tile_center_x = _map_tile->xbase + TILESIZE / 2.0f;
float tile_center_z = _map_tile->zbase + TILESIZE / 2.0f;
bool is_lod = misc::dist(tile_center_x, tile_center_z, camera.x, camera.z) > TILESIZE * 3;
mcnk_shader.uniform("lod_level", int(is_lod));
assert(draw_call.n_chunks <= 256);
mcnk_shader.uniform("base_instance", static_cast<int>(draw_call.start_chunk));
for (int i = 0; i < NUM_SAMPLERS; ++i)
{
gl.activeTexture(GL_TEXTURE0 + 5 + i);
if (draw_call.samplers[i] < 0)
{
gl.bindTexture(GL_TEXTURE_2D_ARRAY, 0);
continue;
}
gl.bindTexture(GL_TEXTURE_2D_ARRAY, draw_call.samplers[i]);
}
if (is_lod)
{
gl.drawElementsInstanced(GL_TRIANGLES, 192, GL_UNSIGNED_SHORT,
reinterpret_cast<void*>(768 * sizeof(std::uint16_t)), draw_call.n_chunks);
}
else
{
gl.drawElementsInstanced(GL_TRIANGLES, 768, GL_UNSIGNED_SHORT, nullptr,
draw_call.n_chunks);
}
}
}
void TileRender::uploadTextures()
{
_chunk_texture_arrays.upload();
gl.activeTexture(GL_TEXTURE0 + 0);
gl.bindTexture(GL_TEXTURE_2D, _height_tex);
gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, mapbufsize,
256, 0, GL_RGBA, GL_FLOAT,nullptr);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
//gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//const GLint swizzleMask[] = {GL_RED, GL_RED, GL_RED, GL_ONE};
//gl.texParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
gl.bindTexture(GL_TEXTURE_2D, 0);
gl.activeTexture(GL_TEXTURE0 + 2);
gl.bindTexture(GL_TEXTURE_2D, _mccv_tex);
gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, mapbufsize,
256, 0, GL_RGB, GL_FLOAT, nullptr);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
//gl.texParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
gl.bindTexture(GL_TEXTURE_2D, 0);
gl.activeTexture(GL_TEXTURE0 + 4);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, _alphamap_tex);
gl.texImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGB, 64, 64,
256, 0, GL_RGB, GL_FLOAT,nullptr);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, 0);
gl.activeTexture(GL_TEXTURE0 + 3);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, _shadowmap_tex);
gl.texImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RED, 64, 64, 256,
0, GL_RED, GL_UNSIGNED_BYTE,nullptr);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, 0);
}
void TileRender::doTileOcclusionQuery(OpenGL::Scoped::use_program& occlusion_shader)
{
if (_tile_occlusion_query_in_use || !_uploaded)
return;
_tile_occlusion_query_in_use = true;
gl.beginQuery(GL_ANY_SAMPLES_PASSED, _tile_occlusion_query);
occlusion_shader.uniform("aabb", _map_tile->_combined_extents.data(), _map_tile->_combined_extents.size());
gl.drawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, nullptr);
gl.endQuery(GL_ANY_SAMPLES_PASSED);
}
bool TileRender::getTileOcclusionQueryResult(glm::vec3 const& camera)
{
// returns true if tile is not occluded by other tiles
if (!_tile_occlusion_query_in_use)
[[unlikely]]
{
return !_tile_occluded;
}
if (!_uploaded)
return !_tile_occluded;
if (misc::pointInside(camera, _map_tile->_combined_extents))
{
_tile_occlusion_query_in_use = false;
return true;
}
GLint result;
gl.getQueryObjectiv(_tile_occlusion_query, GL_QUERY_RESULT_AVAILABLE, &result);
if (result != GL_TRUE)
{
return _tile_occlusion_cull_override || !_tile_occluded;
}
if (!_tile_occlusion_cull_override)
{
gl.getQueryObjectiv(_tile_occlusion_query, GL_QUERY_RESULT, &result);
}
else
{
result = true;
}
_tile_occlusion_query_in_use = false;
_tile_occlusion_cull_override = false;
return static_cast<bool>(result);
}
bool TileRender::fillSamplers(MapChunk* chunk, unsigned chunk_index, unsigned int draw_call_index)
{
MapTileDrawCall& draw_call = _draw_calls[draw_call_index];
_chunk_instance_data[chunk_index].ChunkHoles_DrawImpass_TexLayerCount_CantPaint[2] = chunk->texture_set->num();
static constexpr unsigned NUM_SAMPLERS = 11;
_chunk_instance_data[chunk_index].ChunkTextureSamplers[0] = 0;
_chunk_instance_data[chunk_index].ChunkTextureSamplers[1] = 0;
_chunk_instance_data[chunk_index].ChunkTextureSamplers[2] = 0;
_chunk_instance_data[chunk_index].ChunkTextureSamplers[3] = 0;
_chunk_instance_data[chunk_index].ChunkTextureArrayIDs[0] = -1;
_chunk_instance_data[chunk_index].ChunkTextureArrayIDs[1] = -1;
_chunk_instance_data[chunk_index].ChunkTextureArrayIDs[2] = -1;
_chunk_instance_data[chunk_index].ChunkTextureArrayIDs[3] = -1;
auto& chunk_textures = (*chunk->texture_set->getTextures());
for (int k = 0; k < chunk->texture_set->num(); ++k)
{
chunk_textures[k]->upload();
if (!chunk_textures[k]->is_uploaded())
{
_texture_not_loaded = true;
continue;
}
GLuint tex_array = (*chunk->texture_set->getTextures())[k]->texture_array();
int tex_index = (*chunk->texture_set->getTextures())[k]->array_index();
int sampler_id = -1;
for (int n = 0; n < draw_call.samplers.size(); ++n)
{
if (draw_call.samplers[n] == tex_array)
{
sampler_id = n;
break;
}
else if (draw_call.samplers[n] < 0)
{
draw_call.samplers[n] = tex_array;
sampler_id = n;
break;
}
}
// If there are not enough sampler slots (11) we have to split the drawcall :(.
// Extremely infrequent for terrain. Never for Blizzard terrain as their tilesets
// use uniform BLP format per map.
if (sampler_id < 0)
[[unlikely]]
{
return false;
}
_chunk_instance_data[chunk_index].ChunkTextureSamplers[k] = sampler_id;
_chunk_instance_data[chunk_index].ChunkTextureArrayIDs[k] = (*chunk->texture_set->getTextures())[k]->is_specular() ? tex_index : -tex_index;
}
return true;
}
void TileRender::initChunkData(MapChunk* chunk)
{
auto& chunk_render_instance = _chunk_instance_data[chunk->px * 16 + chunk->py];
chunk_render_instance.ChunkHoles_DrawImpass_TexLayerCount_CantPaint[0] = chunk->holes;
chunk_render_instance.ChunkHoles_DrawImpass_TexLayerCount_CantPaint[1] = chunk->header_flags.flags.impass;
chunk_render_instance.ChunkHoles_DrawImpass_TexLayerCount_CantPaint[2] = chunk->texture_set->num();
chunk_render_instance.ChunkHoles_DrawImpass_TexLayerCount_CantPaint[3] = 0;
chunk_render_instance.AreaIDColor_Pad2_DrawSelection[0] = chunk->areaID;
chunk_render_instance.AreaIDColor_Pad2_DrawSelection[3] = 0;
}

View File

@@ -0,0 +1,101 @@
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#ifndef NOGGIT_TILERENDER_HPP
#define NOGGIT_TILERENDER_HPP
#include <noggit/rendering/BaseRender.hpp>
#include <opengl/scoped.hpp>
#include <opengl/shader.hpp>
#include <array>
class MapTile;
class MapChunk;
namespace Noggit::Rendering
{
struct MapTileDrawCall
{
std::array<int, 11> samplers;
unsigned start_chunk;
unsigned n_chunks;
};
class TileRender : public BaseRender
{
public:
explicit TileRender(MapTile* map_tile);
void upload() override;
void unload() override;
void draw (OpenGL::Scoped::use_program& mcnk_shader
, const glm::vec3& camera
, bool show_unpaintable_chunks
, bool draw_paintability_overlay
, bool is_selected
);
void doTileOcclusionQuery(OpenGL::Scoped::use_program& occlusion_shader);
bool getTileOcclusionQueryResult(glm::vec3 const& camera);
void discardTileOcclusionQuery() { _tile_occlusion_query_in_use = false; }
void notifyTileRendererOnSelectedTextureChange() { _requires_paintability_recalc = true; };
void initChunkData(MapChunk* chunk);
[[nodiscard]]
unsigned objectsFrustumCullTest() const { return _objects_frustum_cull_test; };
void setObjectsFrustumCullTest(unsigned state) { _objects_frustum_cull_test = state; };
[[nodiscard]]
bool isOccluded() const { return _tile_occluded; } ;
void setOccluded(bool state) { _tile_occluded = state; };
[[nodiscard]]
bool isFrustumCulled() const{ return _tile_frustum_culled; };
void setFrustumCulled(bool state) {_tile_frustum_culled = state; };
[[nodiscard]]
bool isOverridingOcclusionCulling() const { return _tile_occlusion_cull_override; };
void setOverrideOcclusionCulling(bool state) { _tile_frustum_culled = state; };
private:
void uploadTextures();
bool fillSamplers(MapChunk* chunk, unsigned chunk_index, unsigned draw_call_index);
MapTile* _map_tile;
bool _uploaded = false;
bool _selected = false;
bool _split_drawcall = false;
bool _requires_sampler_reset = false;
bool _requires_paintability_recalc = true;
bool _texture_not_loaded = false;
// culling
unsigned _objects_frustum_cull_test = 0;
bool _tile_occluded = false;
bool _tile_frustum_culled = true;
bool _tile_occlusion_cull_override = true;
// drawing
std::vector<MapTileDrawCall> _draw_calls;
OpenGL::Scoped::deferred_upload_textures<4> _chunk_texture_arrays;
GLuint const& _height_tex = _chunk_texture_arrays[0];
GLuint const& _mccv_tex = _chunk_texture_arrays[1];
GLuint const& _shadowmap_tex = _chunk_texture_arrays[2];
GLuint const& _alphamap_tex = _chunk_texture_arrays[3];
GLuint _tile_occlusion_query;
bool _tile_occlusion_query_in_use = false;
OpenGL::Scoped::deferred_upload_buffers<1> _buffers;
GLuint const& _chunk_instance_data_ubo = _buffers[0];
OpenGL::ChunkInstanceDataUniformBlock _chunk_instance_data[256];
};
}
#endif //NOGGIT_TILERENDER_HPP

View File

@@ -103,9 +103,9 @@ void WorldRender::draw (glm::mat4x4 const& model_view
{
auto& tile_extents = tile->getCombinedExtents();
tile->calcCamDist(camera_pos);
tile->tile_frustum_culled = false;
tile->objects_frustum_cull_test = 2;
tile->tile_occluded = false;
tile->renderer()->setFrustumCulled(false);
tile->renderer()->setObjectsFrustumCullTest(2);
tile->renderer()->setOccluded(false);
_world->_loaded_tiles_buffer[tile_counter] = std::make_pair(std::make_pair(tile->index.x, tile->index.z), tile);
tile_counter++;
@@ -119,27 +119,27 @@ void WorldRender::draw (glm::mat4x4 const& model_view
tile->calcCamDist(camera_pos);
_world->_loaded_tiles_buffer[tile_counter] = std::make_pair(std::make_pair(tile->index.x, tile->index.z), tile);
tile->objects_frustum_cull_test = 1;
tile->renderer()->setObjectsFrustumCullTest(1);
if (frustum.contains(tile_extents[0]) && frustum.contains(tile_extents[1]))
{
tile->objects_frustum_cull_test++;
tile->renderer()->setObjectsFrustumCullTest( tile->renderer()->objectsFrustumCullTest() + 1);
}
if (tile->tile_frustum_culled)
if (tile->renderer()->isFrustumCulled())
{
tile->tile_occlusion_cull_override = true;
tile->discardTileOcclusionQuery();
tile->tile_occluded = false;
tile->renderer()->setOverrideOcclusionCulling(true);
tile->renderer()->discardTileOcclusionQuery();
tile->renderer()->setOccluded(false);
}
tile->tile_frustum_culled = false;
tile->renderer()->setFrustumCulled(false);
tile_counter++;
}
else
{
tile->tile_frustum_culled = true;
tile->objects_frustum_cull_test = 0;
tile->renderer()->setFrustumCulled(true);
tile->renderer()->setObjectsFrustumCullTest(0);
}
_world->_n_loaded_tiles++;
@@ -277,12 +277,13 @@ void WorldRender::draw (glm::mat4x4 const& model_view
}
if (minimap_render)
tile->tile_occluded = false;
tile->renderer()->setOccluded(false);
if (tile->tile_occluded && !tile->getChunkUpdateFlags() && !tile->tile_occlusion_cull_override)
if (tile->renderer()->isOccluded() && !tile->getChunkUpdateFlags() && !tile->renderer()->isOverridingOcclusionCulling())
continue;
tile->draw (mcnk_shader
tile->renderer()->draw(
mcnk_shader
, camera_pos
, show_unpaintable_chunks
, draw_paintability_overlay
@@ -354,9 +355,9 @@ void WorldRender::draw (glm::mat4x4 const& model_view
}
if (minimap_render)
tile->tile_occluded = false;
tile->renderer()->setOccluded(false);
if (tile->tile_occluded && !tile->getChunkUpdateFlags() && !tile->tile_occlusion_cull_override)
if (tile->renderer()->isOccluded() && !tile->getChunkUpdateFlags() && !tile->renderer()->isOverridingOcclusionCulling())
continue;
// early dist check
@@ -373,7 +374,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
// memory allocation heuristic. all objects will pass if tile is entirely in frustum.
// otherwise we only allocate for a half
if (tile->objects_frustum_cull_test > 1)
if (tile->renderer()->objectsFrustumCullTest() > 1)
{
instances.reserve(instances.size() + pair.second.size());
}
@@ -395,7 +396,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
auto m2_instance = static_cast<ModelInstance*>(instance);
if ((tile->objects_frustum_cull_test > 1 || m2_instance->isInFrustum(frustum)) && m2_instance->isInRenderDist(_cull_distance, camera_pos, display))
if ((tile->renderer()->objectsFrustumCullTest() > 1 || m2_instance->isInFrustum(frustum)) && m2_instance->isInRenderDist(_cull_distance, camera_pos, display))
{
instances.push_back(m2_instance->transformMatrix());
}
@@ -408,7 +409,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
// memory allocation heuristic. all objects will pass if tile is entirely in frustum.
// otherwise we only allocate for a half
if (tile->objects_frustum_cull_test > 1)
if (tile->renderer()->objectsFrustumCullTest() > 1)
{
wmos_to_draw.reserve(wmos_to_draw.size() + pair.second.size());
}
@@ -429,7 +430,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
auto wmo_instance = static_cast<WMOInstance*>(instance);
if (tile->objects_frustum_cull_test > 1 || frustum.intersects(wmo_instance->extents[1], wmo_instance->extents[0]))
if (tile->renderer()->objectsFrustumCullTest() > 1 || frustum.intersects(wmo_instance->extents[1], wmo_instance->extents[0]))
{
wmos_to_draw.push_back(wmo_instance);
}
@@ -498,8 +499,8 @@ void WorldRender::draw (glm::mat4x4 const& model_view
break;
}
tile->tile_occluded = !tile->getTileOcclusionQueryResult(camera_pos);
tile->doTileOcclusionQuery(occluder_shader);
tile->renderer()->setOccluded(!tile->renderer()->getTileOcclusionQueryResult(camera_pos));
tile->renderer()->doTileOcclusionQuery(occluder_shader);
}
gl.enable(GL_CULL_FACE);
@@ -752,7 +753,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
if (!tile)
break;
if (tile->tile_occluded && !tile->Water.needsUpdate() && !tile->tile_occlusion_cull_override)
if (tile->renderer()->isOccluded() && !tile->Water.needsUpdate() && !tile->renderer()->isOverridingOcclusionCulling())
continue;
tile->drawWater ( frustum