Separate WMO rendering into its own component | Update Model.cpp, WMO.cpp, and 9 more files...

This commit is contained in:
Skarn
2022-01-14 23:42:12 +03:00
parent 75806861d4
commit 18075bf841
11 changed files with 821 additions and 624 deletions

View File

@@ -651,7 +651,7 @@ bool ModelRenderPass::prepare_draw(OpenGL::Scoped::use_program& m2_shader, Model
}
// opacity
if (transparency_combo_index != 0xFFFF)
if (transparency_combo_index != 0xFFFF && transparency_combo_index < m->_transparency_lookup.size())
{
auto& transparency (m->_transparency[m->_transparency_lookup[transparency_combo_index]].trans);
if (transparency.uses (0))

View File

@@ -23,6 +23,7 @@
WMO::WMO(BlizzardArchive::Listfile::FileKey const& file_key, Noggit::NoggitRenderContext context)
: AsyncObject(file_key)
, _context(context)
, _renderer(this)
{
}
@@ -352,87 +353,6 @@ void WMO::waitForChildrenLoaded()
}
}
void WMO::draw ( OpenGL::Scoped::use_program& wmo_shader
, glm::mat4x4 const& model_view
, glm::mat4x4 const& projection
, glm::mat4x4 const& transform_matrix
, bool boundingbox
, math::frustum const& frustum
, const float& cull_distance
, const glm::vec3& camera
, bool // draw_doodads
, bool draw_fog
, int animtime
, bool world_has_skies
, display_mode display
)
{
if (!finishedLoading())
[[unlikely]]
{
return;
}
wmo_shader.uniform("ambient_color",glm::vec3(ambient_light_color));
for (auto& group : groups)
{
/*
if (!group.is_visible(transform_matrix, frustum, cull_distance, camera, display))
{
continue;
}
*/
group.draw ( wmo_shader
, frustum
, cull_distance
, camera
, draw_fog
, world_has_skies
);
/*
group.drawLiquid ( transform_matrix_transposed
, render
, draw_fog
, animtime
);
*/
}
if (boundingbox)
{
//OpenGL::Scoped::bool_setter<GL_BLEND, GL_TRUE> const blend;
//gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
for (auto& group : groups)
{
OpenGL::primitives::wire_box::getInstance(_context).draw( model_view
, projection
, transform_matrix
, {1.0f, 1.0f, 1.0f, 1.0f}
, group.BoundingBoxMin
, group.BoundingBoxMax
);
}
OpenGL::primitives::wire_box::getInstance(_context).draw ( model_view
, projection
, transform_matrix
, {1.0f, 0.0f, 0.0f, 1.0f}
, glm::vec3(extents[0].x, extents[0].z, -extents[0].y)
, glm::vec3(extents[1].x, extents[1].z, -extents[1].y)
);
}
}
std::vector<float> WMO::intersect (math::ray const& ray) const
{
std::vector<float> results;
@@ -450,67 +370,8 @@ std::vector<float> WMO::intersect (math::ray const& ray) const
return results;
}
bool WMO::draw_skybox (glm::mat4x4 const& model_view
, glm::vec3 const& camera_pos
, OpenGL::Scoped::use_program& m2_shader
, math::frustum const& frustum
, const float& cull_distance
, int animtime
, bool draw_particles
, glm::vec3 aabb_min
, glm::vec3 aabb_max
, std::map<int, std::pair<glm::vec3, glm::vec3>> const& group_extents
) const
{
if (!skybox || !math::is_inside_of(camera_pos,aabb_min, aabb_max))
{
return false;
}
for (int i=0; i<groups.size(); ++i)
{
auto const& g = groups[i];
if (!g.has_skybox())
{
continue;
}
auto& extent(group_extents.at(i));
if (math::is_inside_of(camera_pos, extent.first, extent.second))
{
ModelInstance sky(skybox.value()->file_key().filepath(), _context);
sky.pos = camera_pos;
sky.scale = 2.f;
sky.recalcExtents();
OpenGL::M2RenderState model_render_state;
model_render_state.tex_arrays = {0, 0};
model_render_state.tex_indices = {0, 0};
model_render_state.tex_unit_lookups = {-1, -1};
gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gl.disable(GL_BLEND);
gl.depthMask(GL_TRUE);
m2_shader.uniform("blend_mode", 0);
m2_shader.uniform("unfogged", static_cast<int>(model_render_state.unfogged));
m2_shader.uniform("unlit", static_cast<int>(model_render_state.unlit));
m2_shader.uniform("tex_unit_lookup_1", 0);
m2_shader.uniform("tex_unit_lookup_2", 0);
m2_shader.uniform("pixel_shader", 0);
skybox->get()->draw(model_view, sky, m2_shader, model_render_state, frustum, cull_distance, camera_pos, animtime, display_mode::in_3D);
return true;
}
}
return false;
}
std::map<uint32_t, std::vector<wmo_doodad_instance>> WMO::doodads_per_group(uint16_t doodadset) const
{
std::map<uint32_t, std::vector<wmo_doodad_instance>> doodads;
@@ -538,14 +399,6 @@ std::map<uint32_t, std::vector<wmo_doodad_instance>> WMO::doodads_per_group(uint
return doodads;
}
void WMO::unload()
{
for (auto& group : groups)
{
group.unload();
}
}
void WMOLight::init(BlizzardArchive::ClientFile* f)
{
char type[4];
@@ -599,6 +452,7 @@ void WMOLight::setupOnce(GLint, glm::vec3, glm::vec3)
WMOGroup::WMOGroup(WMO *_wmo, BlizzardArchive::ClientFile* f, int _num, char const* names)
: wmo(_wmo)
, num(_num)
, _renderer(this)
{
// extract group info from f
std::uint32_t flags; // not used, the flags are in the group header
@@ -639,8 +493,7 @@ WMOGroup::WMOGroup(WMOGroup const& other)
, _texcoords_2(other._texcoords_2)
, _vertex_colors(other._vertex_colors)
, _indices(other._indices)
, _render_batch_mapping(other._render_batch_mapping)
, _render_batches(other._render_batches)
, _renderer(this)
{
if (other.lq)
{
@@ -661,257 +514,6 @@ namespace
}
}
void WMOGroup::upload()
{
// render batches
bool texture_not_uploaded = false;
std::size_t batch_counter = 0;
for (auto& batch : _batches)
{
WMOMaterial const& mat (wmo->materials.at (batch.texture));
auto& tex1 = wmo->textures.at(mat.texture1);
tex1->wait_until_loaded();
tex1->upload();
std::uint32_t tex_array0 = tex1->texture_array();
std::uint32_t array_index0 = tex1->array_index();
std::uint32_t tex_array1 = 0;
std::uint32_t array_index1 = 0;
bool use_tex2 = mat.shader == 6 || mat.shader == 5 || mat.shader == 3;
if (use_tex2)
{
auto& tex2 = wmo->textures.at(mat.texture2);
tex2->wait_until_loaded();
tex2->upload();
tex_array1 = tex2->texture_array();
array_index1 = tex2->array_index();
}
_render_batches[batch_counter].tex_array0 = tex_array0;
_render_batches[batch_counter].tex_array1 = tex_array1;
_render_batches[batch_counter].tex0 = array_index0;
_render_batches[batch_counter].tex1 = array_index1;
batch_counter++;
}
if (texture_not_uploaded)
{
return;
}
_draw_calls.clear();
WMOCombinedDrawCall* draw_call = nullptr;
std::vector<WMORenderBatch*> _used_batches;
batch_counter = 0;
for (auto& batch : _batches)
{
WMOMaterial& mat = wmo->materials.at(batch.texture);
bool backface_cull = !mat.flags.unculled;
bool use_tex2 = mat.shader == 6 || mat.shader == 5 || mat.shader == 3;
bool create_draw_call = false;
if (draw_call && draw_call->backface_cull == backface_cull && batch.index_start == draw_call->index_start + draw_call->index_count)
{
// identify if we can fit this batch into current draw_call
unsigned n_required_slots = use_tex2 ? 2 : 1;
unsigned n_avaliable_slots = draw_call->samplers.size() - draw_call->n_used_samplers;
unsigned n_slots_to_be_occupied = 0;
std::vector<int>::iterator it2;
auto it = std::find(draw_call->samplers.begin(), draw_call->samplers.end(), _render_batches[batch_counter].tex_array0);
if (it == draw_call->samplers.end())
{
if (n_avaliable_slots)
n_slots_to_be_occupied++;
else
create_draw_call = true;
}
if (!create_draw_call && use_tex2)
{
it2 = std::find(draw_call->samplers.begin(), draw_call->samplers.end(), _render_batches[batch_counter].tex_array1);
if (it2 == draw_call->samplers.end())
{
if (n_slots_to_be_occupied < n_avaliable_slots)
n_slots_to_be_occupied++;
else
create_draw_call = true;
}
}
if (!create_draw_call)
{
if (it != draw_call->samplers.end())
{
_render_batches[batch_counter].tex_array0 = it - draw_call->samplers.begin();
}
else
{
draw_call->samplers[draw_call->n_used_samplers] = _render_batches[batch_counter].tex_array0;
_render_batches[batch_counter].tex_array0 = draw_call->n_used_samplers;
draw_call->n_used_samplers++;
}
if (use_tex2)
{
if (it2 != draw_call->samplers.end())
{
_render_batches[batch_counter].tex_array1 = it2 - draw_call->samplers.begin();
}
else
{
draw_call->samplers[draw_call->n_used_samplers] = _render_batches[batch_counter].tex_array1;
_render_batches[batch_counter].tex_array1 = draw_call->n_used_samplers;
draw_call->n_used_samplers++;
}
}
}
}
else
{
create_draw_call = true;
}
if (create_draw_call)
{
// create new combined draw call
draw_call = &_draw_calls.emplace_back();
draw_call->samplers = std::vector<int>{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
draw_call->index_start = batch.index_start;
draw_call->index_count = 0;
draw_call->n_used_samplers = use_tex2 ? 2 : 1;
draw_call->backface_cull = backface_cull;
draw_call->samplers[0] = _render_batches[batch_counter].tex_array0;
_render_batches[batch_counter].tex_array0 = 0;
if (use_tex2)
[[unlikely]]
{
draw_call->samplers[1] = _render_batches[batch_counter].tex_array1;
_render_batches[batch_counter].tex_array1 = 1;
}
}
draw_call->index_count += batch.index_count;
batch_counter++;
}
// opengl resources
_vertex_array.upload();
_buffers.upload();
gl.genTextures(1, &_render_batch_tex);
gl.bufferData<GL_ARRAY_BUFFER> ( _vertices_buffer
, _vertices.size() * sizeof (*_vertices.data())
, _vertices.data()
, GL_STATIC_DRAW
);
gl.bufferData<GL_ARRAY_BUFFER> ( _normals_buffer
, _normals.size() * sizeof (*_normals.data())
, _normals.data()
, GL_STATIC_DRAW
);
gl.bufferData<GL_ARRAY_BUFFER> ( _texcoords_buffer
, _texcoords.size() * sizeof (*_texcoords.data())
, _texcoords.data()
, GL_STATIC_DRAW
);
gl.bufferData<GL_ARRAY_BUFFER> ( _render_batch_mapping_buffer
, _render_batch_mapping.size() * sizeof(unsigned)
, _render_batch_mapping.data()
, GL_STATIC_DRAW
);
gl.bindBuffer(GL_TEXTURE_BUFFER, _render_batch_tex_buffer);
gl.bufferData(GL_TEXTURE_BUFFER, _render_batches.size() * sizeof(WMORenderBatch),_render_batches.data(), GL_STATIC_DRAW);
gl.bindTexture(GL_TEXTURE_BUFFER, _render_batch_tex);
gl.texBuffer(GL_TEXTURE_BUFFER, GL_RGBA32UI, _render_batch_tex_buffer);
gl.bufferData<GL_ELEMENT_ARRAY_BUFFER, std::uint16_t>(_indices_buffer, _indices, GL_STATIC_DRAW);
if (header.flags.has_two_motv)
{
gl.bufferData<GL_ARRAY_BUFFER, glm::vec2> ( _texcoords_buffer_2
, _texcoords_2
, GL_STATIC_DRAW
);
}
gl.bufferData<GL_ARRAY_BUFFER> ( _vertex_colors_buffer
, _vertex_colors.size() * sizeof (*_vertex_colors.data())
, _vertex_colors.data()
, GL_STATIC_DRAW
);
// free unused data
_normals.clear();
_texcoords.clear();
_texcoords_2.clear();
_vertex_colors.clear();
_render_batches.clear();
_render_batch_mapping.clear();
_uploaded = true;
}
void WMOGroup::unload()
{
_vertex_array.unload();
_buffers.unload();
gl.deleteTextures(1, &_render_batch_tex);
_uploaded = false;
_vao_is_setup = false;
}
void WMOGroup::setup_vao(OpenGL::Scoped::use_program& wmo_shader)
{
OpenGL::Scoped::index_buffer_manual_binder indices (_indices_buffer);
{
OpenGL::Scoped::vao_binder const _ (_vao);
wmo_shader.attrib("position", _vertices_buffer, 3, GL_FLOAT, GL_FALSE, 0, 0);
wmo_shader.attrib("normal", _normals_buffer, 3, GL_FLOAT, GL_FALSE, 0, 0);
wmo_shader.attrib("texcoord", _texcoords_buffer, 2, GL_FLOAT, GL_FALSE, 0, 0);
wmo_shader.attribi("batch_mapping", _render_batch_mapping_buffer, 1, GL_UNSIGNED_INT, 0, 0);
if (header.flags.has_two_motv)
{
wmo_shader.attrib("texcoord_2", _texcoords_buffer_2, 2, GL_FLOAT, GL_FALSE, 0, 0);
}
// even if the 2 flags are set there's only one vertex color vector, the 2nd chunk is used for alpha only
if (header.flags.has_vertex_color || header.flags.use_mocv2_for_texture_blending)
{
wmo_shader.attrib("vertex_color", _vertex_colors_buffer, 4, GL_FLOAT, GL_FALSE, 0, 0);
}
indices.bind();
}
_vao_is_setup = true;
}
void WMOGroup::load()
{
@@ -1059,66 +661,7 @@ void WMOGroup::load()
_batches.resize (size / sizeof (wmo_batch));
f.read (_batches.data (), size);
_render_batch_mapping.resize(_vertices.size());
std::fill(_render_batch_mapping.begin(), _render_batch_mapping.end(), 0);
_render_batches.resize(_batches.size());
std::size_t batch_counter = 0;
for (auto& batch : _batches)
{
for (std::size_t i = 0; i < (batch.vertex_end - batch.vertex_start + 1); ++i)
{
_render_batch_mapping[batch.vertex_start + i] = batch_counter + 1;
}
std::uint32_t flags = 0;
if (header.flags.exterior_lit || header.flags.exterior)
{
flags |= WMORenderBatchFlags::eWMOBatch_ExteriorLit;
}
if (header.flags.has_vertex_color || header.flags.use_mocv2_for_texture_blending)
{
flags |= WMORenderBatchFlags::eWMOBatch_HasMOCV;
}
WMOMaterial const& mat (wmo->materials.at (batch.texture));
if (mat.flags.unlit)
{
flags |= WMORenderBatchFlags::eWMOBatch_Unlit;
}
if (mat.flags.unfogged)
{
flags |= WMORenderBatchFlags::eWMOBatch_Unfogged;
}
std::uint32_t alpha_test;
switch (mat.blend_mode)
{
case 1:
alpha_test = 1; // 224/255
break;
case 2:
case 3:
case 4:
case 5:
case 6:
alpha_test = 2;
break;
case 0:
default:
alpha_test = 0;
break;
}
_render_batches[batch_counter] = WMORenderBatch{flags, mat.shader, 0, 0, 0, 0, alpha_test, 0};
batch_counter++;
}
_renderer.initRenderBatches();
// - MOLR ----------------------------------------------
if (header.flags.has_light)
@@ -1528,70 +1071,6 @@ bool WMOGroup::is_visible( glm::mat4x4 const& transform
return (dist < cull_distance);
}
void WMOGroup::draw( OpenGL::Scoped::use_program& wmo_shader
, math::frustum const& // frustum
, const float& //cull_distance
, const glm::vec3& //camera
, bool // draw_fog
, bool // world_has_skies
)
{
if (!_uploaded)
[[unlikely]]
{
upload();
if (!_uploaded)
[[unlikely]]
{
return;
}
}
if (!_vao_is_setup)
[[unlikely]]
{
setup_vao(wmo_shader);
}
OpenGL::Scoped::vao_binder const _ (_vao);
gl.activeTexture(GL_TEXTURE0);
gl.bindTexture(GL_TEXTURE_BUFFER, _render_batch_tex);
bool backface_cull = true;
gl.enable(GL_CULL_FACE);
for (auto& draw_call : _draw_calls)
{
if (backface_cull != draw_call.backface_cull)
{
if (draw_call.backface_cull)
{
gl.enable(GL_CULL_FACE);
}
else
{
gl.disable(GL_CULL_FACE);
}
backface_cull = draw_call.backface_cull;
}
for(std::size_t i = 0; i < draw_call.samplers.size(); ++i)
{
if (draw_call.samplers[i] < 0)
break;
gl.activeTexture(GL_TEXTURE0 + 1 + i);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, draw_call.samplers[i]);
}
gl.drawElements (GL_TRIANGLES, draw_call.index_count, GL_UNSIGNED_SHORT, reinterpret_cast<void*>(sizeof(std::uint16_t)*draw_call.index_start));
}
}
void WMOGroup::intersect (math::ray const& ray, std::vector<float>* results) const
{
@@ -1694,7 +1173,7 @@ void WMOManager::unload_all(Noggit::NoggitRenderContext context)
_.context_aware_apply(
[&] (BlizzardArchive::Listfile::FileKey const&, WMO& wmo)
{
wmo.unload();
wmo.renderer()->unload();
}
, context
);

View File

@@ -9,6 +9,8 @@
#include <noggit/tool_enums.hpp>
#include <noggit/wmo_liquid.hpp>
#include <noggit/ContextObject.hpp>
#include <noggit/rendering/WMOGroupRender.hpp>
#include <noggit/rendering/WMORender.hpp>
#include <opengl/primitives.hpp>
#include <ClientFile.hpp>
#include <optional>
@@ -27,35 +29,12 @@ class WMOManager;
class wmo_liquid;
class Model;
struct WMORenderBatch
namespace Noggit::Rendering
{
std::uint32_t flags;
std::uint32_t shader;
std::uint32_t tex_array0;
std::uint32_t tex_array1;
std::uint32_t tex0;
std::uint32_t tex1;
std::uint32_t alpha_test_mode;
std::uint32_t _pad1;
};
class WMOGroupRender;
class WMORender;
}
enum WMORenderBatchFlags
{
eWMOBatch_ExteriorLit = 0x1,
eWMOBatch_HasMOCV = 0x2,
eWMOBatch_Unlit = 0x4,
eWMOBatch_Unfogged = 0x8,
eWMOBatch_Collision = 0x10
};
struct WMOCombinedDrawCall
{
std::vector<int> samplers;
std::uint32_t index_start = 0;
std::uint32_t index_count = 0;
std::uint32_t n_used_samplers = 0;
bool backface_cull = false;
};
struct wmo_batch
{
@@ -131,20 +110,14 @@ struct wmo_group_header
class WMOGroup
{
friend class Noggit::Rendering::WMOGroupRender;
public:
WMOGroup(WMO *wmo, BlizzardArchive::ClientFile* f, int num, char const* names);
WMOGroup(WMOGroup const&);
void load();
void draw( OpenGL::Scoped::use_program& wmo_shader
, math::frustum const& frustum
, const float& cull_distance
, const glm::vec3& camera
, bool draw_fog
, bool world_has_skies
);
/*
void drawLiquid ( glm::mat4x4 const& transform
, liquid_render& render
@@ -159,6 +132,7 @@ public:
void intersect (math::ray const&, std::vector<float>* results) const;
// todo: portal culling
[[nodiscard]]
bool is_visible( glm::mat4x4 const& transform_matrix
, math::frustum const& frustum
, float const& cull_distance
@@ -166,6 +140,7 @@ public:
, display_mode display
) const;
[[nodiscard]]
std::vector<uint16_t> doodad_ref() const { return _doodad_ref; }
glm::vec3 BoundingBoxMin;
@@ -176,9 +151,12 @@ public:
bool use_outdoor_lights;
std::string name;
[[nodiscard]]
bool has_skybox() const { return header.flags.skybox; }
void unload();
[[nodiscard]]
Noggit::Rendering::WMOGroupRender* renderer() { return &_renderer; };
private:
void load_mocv(BlizzardArchive::ClientFile& f, uint32_t size);
@@ -200,31 +178,9 @@ private:
std::vector<glm::vec2> _texcoords;
std::vector<glm::vec2> _texcoords_2;
std::vector<glm::vec4> _vertex_colors;
std::vector<unsigned> _render_batch_mapping;
std::vector<uint16_t> _indices;
std::vector<WMORenderBatch> _render_batches;
std::vector<WMOCombinedDrawCall> _draw_calls;
OpenGL::Scoped::deferred_upload_vertex_arrays<1> _vertex_array;
GLuint const& _vao = _vertex_array[0];
OpenGL::Scoped::deferred_upload_buffers<8> _buffers;
GLuint const& _vertices_buffer = _buffers[0];
GLuint const& _normals_buffer = _buffers[1];
GLuint const& _texcoords_buffer = _buffers[2];
GLuint const& _texcoords_buffer_2 = _buffers[3];
GLuint const& _vertex_colors_buffer = _buffers[4];
GLuint const& _indices_buffer = _buffers[5];
GLuint const& _render_batch_mapping_buffer = _buffers[6];
GLuint const& _render_batch_tex_buffer = _buffers[7];
GLuint _render_batch_tex;
bool _uploaded = false;
bool _vao_is_setup = false;
void upload();
void setup_vao(OpenGL::Scoped::use_program& wmo_shader);
Noggit::Rendering::WMOGroupRender _renderer;
};
struct WMOLight {
@@ -289,36 +245,11 @@ static_assert ( sizeof (mohd_flags) == sizeof (std::uint16_t)
class WMO : public AsyncObject
{
friend class WMORender;
public:
explicit WMO(BlizzardArchive::Listfile::FileKey const& file_key, Noggit::NoggitRenderContext context );
void draw ( OpenGL::Scoped::use_program& wmo_shader
, glm::mat4x4 const& model_view
, glm::mat4x4 const& projection
, glm::mat4x4 const& transform_matrix
, bool boundingbox
, math::frustum const& frustum
, const float& cull_distance
, const glm::vec3& camera
, bool draw_doodads
, bool draw_fog
, int animtime
, bool world_has_skies
, display_mode display
);
bool draw_skybox(glm::mat4x4 const& model_view
, glm::vec3 const& camera_pos
, OpenGL::Scoped::use_program& m2_shader
, math::frustum const& frustum
, const float& cull_distance
, int animtime
, bool draw_particles
, glm::vec3 aabb_min
, glm::vec3 aabb_max
, std::map<int, std::pair<glm::vec3, glm::vec3>> const& group_extents
) const;
[[nodiscard]]
std::vector<float> intersect (math::ray const&) const;
@@ -326,8 +257,6 @@ public:
void waitForChildrenLoaded() override;
void unload();
[[nodiscard]]
std::map<uint32_t, std::vector<wmo_doodad_instance>> doodads_per_group(uint16_t doodadset) const;
@@ -364,8 +293,13 @@ public:
return true;
}
[[nodiscard]]
Noggit::Rendering::WMORender* renderer() { return &_renderer; }
private:
bool _hidden = false;
Noggit::Rendering::WMORender _renderer;
};
class WMOManager

View File

@@ -103,7 +103,7 @@ void WMOInstance::draw ( OpenGL::Scoped::use_program& wmo_shader
wmo_shader.uniform("transform", _transform_mat);
wmo->draw ( wmo_shader
wmo->renderer()->draw( wmo_shader
, model_view
, projection
, _transform_mat
@@ -278,6 +278,9 @@ void WMOInstance::update_doodads()
{
for (auto& doodad : group_doodads.second)
{
if (!doodad.need_matrix_update())
continue;
doodad.update_transform_matrix_wmo(this);
}
}
@@ -348,7 +351,7 @@ std::map<uint32_t, std::vector<wmo_doodad_instance>>* WMOInstance::get_doodads(b
{
for (auto& doodad : _doodads_per_group[i])
{
if (doodad.need_matrix_update())
if (doodad.finishedLoading() && doodad.need_matrix_update())
{
doodad.update_transform_matrix_wmo(this);
}

View File

@@ -0,0 +1,393 @@
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#include "WMOGroupRender.hpp"
#include <noggit/WMO.h>
using namespace Noggit::Rendering;
WMOGroupRender::WMOGroupRender(WMOGroup* wmo_group)
: _wmo_group(wmo_group)
{
}
void WMOGroupRender::upload()
{
// render batches
bool texture_not_uploaded = false;
std::size_t batch_counter = 0;
for (auto& batch : _wmo_group->_batches)
{
WMOMaterial const& mat (_wmo_group->wmo->materials.at (batch.texture));
auto& tex1 = _wmo_group->wmo->textures.at(mat.texture1);
tex1->wait_until_loaded();
tex1->upload();
std::uint32_t tex_array0 = tex1->texture_array();
std::uint32_t array_index0 = tex1->array_index();
std::uint32_t tex_array1 = 0;
std::uint32_t array_index1 = 0;
bool use_tex2 = mat.shader == 6 || mat.shader == 5 || mat.shader == 3;
if (use_tex2)
{
auto& tex2 = _wmo_group->wmo->textures.at(mat.texture2);
tex2->wait_until_loaded();
tex2->upload();
tex_array1 = tex2->texture_array();
array_index1 = tex2->array_index();
}
_render_batches[batch_counter].tex_array0 = tex_array0;
_render_batches[batch_counter].tex_array1 = tex_array1;
_render_batches[batch_counter].tex0 = array_index0;
_render_batches[batch_counter].tex1 = array_index1;
batch_counter++;
}
if (texture_not_uploaded)
{
return;
}
_draw_calls.clear();
WMOCombinedDrawCall* draw_call = nullptr;
std::vector<WMORenderBatch*> _used_batches;
batch_counter = 0;
for (auto& batch : _wmo_group->_batches)
{
WMOMaterial& mat = _wmo_group->wmo->materials.at(batch.texture);
bool backface_cull = !mat.flags.unculled;
bool use_tex2 = mat.shader == 6 || mat.shader == 5 || mat.shader == 3;
bool create_draw_call = false;
if (draw_call && draw_call->backface_cull == backface_cull && batch.index_start == draw_call->index_start + draw_call->index_count)
{
// identify if we can fit this batch into current draw_call
unsigned n_required_slots = use_tex2 ? 2 : 1;
unsigned n_avaliable_slots = draw_call->samplers.size() - draw_call->n_used_samplers;
unsigned n_slots_to_be_occupied = 0;
std::vector<int>::iterator it2;
auto it = std::find(draw_call->samplers.begin(), draw_call->samplers.end(), _render_batches[batch_counter].tex_array0);
if (it == draw_call->samplers.end())
{
if (n_avaliable_slots)
n_slots_to_be_occupied++;
else
create_draw_call = true;
}
if (!create_draw_call && use_tex2)
{
it2 = std::find(draw_call->samplers.begin(), draw_call->samplers.end(), _render_batches[batch_counter].tex_array1);
if (it2 == draw_call->samplers.end())
{
if (n_slots_to_be_occupied < n_avaliable_slots)
n_slots_to_be_occupied++;
else
create_draw_call = true;
}
}
if (!create_draw_call)
{
if (it != draw_call->samplers.end())
{
_render_batches[batch_counter].tex_array0 = it - draw_call->samplers.begin();
}
else
{
draw_call->samplers[draw_call->n_used_samplers] = _render_batches[batch_counter].tex_array0;
_render_batches[batch_counter].tex_array0 = draw_call->n_used_samplers;
draw_call->n_used_samplers++;
}
if (use_tex2)
{
if (it2 != draw_call->samplers.end())
{
_render_batches[batch_counter].tex_array1 = it2 - draw_call->samplers.begin();
}
else
{
draw_call->samplers[draw_call->n_used_samplers] = _render_batches[batch_counter].tex_array1;
_render_batches[batch_counter].tex_array1 = draw_call->n_used_samplers;
draw_call->n_used_samplers++;
}
}
}
}
else
{
create_draw_call = true;
}
if (create_draw_call)
{
// create new combined draw call
draw_call = &_draw_calls.emplace_back();
draw_call->samplers = std::vector<int>{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
draw_call->index_start = batch.index_start;
draw_call->index_count = 0;
draw_call->n_used_samplers = use_tex2 ? 2 : 1;
draw_call->backface_cull = backface_cull;
draw_call->samplers[0] = _render_batches[batch_counter].tex_array0;
_render_batches[batch_counter].tex_array0 = 0;
if (use_tex2)
[[unlikely]]
{
draw_call->samplers[1] = _render_batches[batch_counter].tex_array1;
_render_batches[batch_counter].tex_array1 = 1;
}
}
draw_call->index_count += batch.index_count;
batch_counter++;
}
// opengl resources
_vertex_array.upload();
_buffers.upload();
gl.genTextures(1, &_render_batch_tex);
gl.bufferData<GL_ARRAY_BUFFER> ( _vertices_buffer
, _wmo_group->_vertices.size() * sizeof (*_wmo_group->_vertices.data())
, _wmo_group->_vertices.data()
, GL_STATIC_DRAW
);
gl.bufferData<GL_ARRAY_BUFFER> ( _normals_buffer
, _wmo_group->_normals.size() * sizeof (*_wmo_group->_normals.data())
, _wmo_group->_normals.data()
, GL_STATIC_DRAW
);
gl.bufferData<GL_ARRAY_BUFFER> ( _texcoords_buffer
, _wmo_group->_texcoords.size() * sizeof (*_wmo_group->_texcoords.data())
, _wmo_group->_texcoords.data()
, GL_STATIC_DRAW
);
gl.bufferData<GL_ARRAY_BUFFER> ( _render_batch_mapping_buffer
, _render_batch_mapping.size() * sizeof(unsigned)
, _render_batch_mapping.data()
, GL_STATIC_DRAW
);
gl.bindBuffer(GL_TEXTURE_BUFFER, _render_batch_tex_buffer);
gl.bufferData(GL_TEXTURE_BUFFER, _render_batches.size() * sizeof(WMORenderBatch),_render_batches.data(), GL_STATIC_DRAW);
gl.bindTexture(GL_TEXTURE_BUFFER, _render_batch_tex);
gl.texBuffer(GL_TEXTURE_BUFFER, GL_RGBA32UI, _render_batch_tex_buffer);
gl.bufferData<GL_ELEMENT_ARRAY_BUFFER, std::uint16_t>(_indices_buffer, _wmo_group->_indices, GL_STATIC_DRAW);
if (_wmo_group->header.flags.has_two_motv)
{
gl.bufferData<GL_ARRAY_BUFFER, glm::vec2> ( _texcoords_buffer_2
, _wmo_group->_texcoords_2
, GL_STATIC_DRAW
);
}
gl.bufferData<GL_ARRAY_BUFFER> ( _vertex_colors_buffer
, _wmo_group->_vertex_colors.size() * sizeof (*_wmo_group->_vertex_colors.data())
, _wmo_group->_vertex_colors.data()
, GL_STATIC_DRAW
);
// free unused data
_wmo_group->_normals.clear();
_wmo_group->_texcoords.clear();
_wmo_group->_texcoords_2.clear();
_wmo_group->_vertex_colors.clear();
_render_batches.clear();
_render_batch_mapping.clear();
_uploaded = true;
}
void WMOGroupRender::unload()
{
_vertex_array.unload();
_buffers.unload();
gl.deleteTextures(1, &_render_batch_tex);
_uploaded = false;
_vao_is_setup = false;
}
void WMOGroupRender::setupVao(OpenGL::Scoped::use_program& wmo_shader)
{
OpenGL::Scoped::index_buffer_manual_binder indices (_indices_buffer);
{
OpenGL::Scoped::vao_binder const _ (_vao);
wmo_shader.attrib("position", _vertices_buffer, 3, GL_FLOAT, GL_FALSE, 0, 0);
wmo_shader.attrib("normal", _normals_buffer, 3, GL_FLOAT, GL_FALSE, 0, 0);
wmo_shader.attrib("texcoord", _texcoords_buffer, 2, GL_FLOAT, GL_FALSE, 0, 0);
wmo_shader.attribi("batch_mapping", _render_batch_mapping_buffer, 1, GL_UNSIGNED_INT, 0, 0);
if (_wmo_group->header.flags.has_two_motv)
{
wmo_shader.attrib("texcoord_2", _texcoords_buffer_2, 2, GL_FLOAT, GL_FALSE, 0, 0);
}
// even if the 2 flags are set there's only one vertex color vector, the 2nd chunk is used for alpha only
if (_wmo_group->header.flags.has_vertex_color || _wmo_group->header.flags.use_mocv2_for_texture_blending)
{
wmo_shader.attrib("vertex_color", _vertex_colors_buffer, 4, GL_FLOAT, GL_FALSE, 0, 0);
}
indices.bind();
}
_vao_is_setup = true;
}
void WMOGroupRender::draw(OpenGL::Scoped::use_program& wmo_shader
, math::frustum const& // frustum
, const float& //cull_distance
, const glm::vec3& //camera
, bool // draw_fog
, bool // world_has_skies
)
{
if (!_uploaded)
[[unlikely]]
{
upload();
if (!_uploaded)
[[unlikely]]
{
return;
}
}
if (!_vao_is_setup)
[[unlikely]]
{
setupVao(wmo_shader);
}
OpenGL::Scoped::vao_binder const _ (_vao);
gl.activeTexture(GL_TEXTURE0);
gl.bindTexture(GL_TEXTURE_BUFFER, _render_batch_tex);
bool backface_cull = true;
gl.enable(GL_CULL_FACE);
for (auto& draw_call : _draw_calls)
{
if (backface_cull != draw_call.backface_cull)
{
if (draw_call.backface_cull)
{
gl.enable(GL_CULL_FACE);
}
else
{
gl.disable(GL_CULL_FACE);
}
backface_cull = draw_call.backface_cull;
}
for(std::size_t i = 0; i < draw_call.samplers.size(); ++i)
{
if (draw_call.samplers[i] < 0)
break;
gl.activeTexture(GL_TEXTURE0 + 1 + i);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, draw_call.samplers[i]);
}
gl.drawElements (GL_TRIANGLES, draw_call.index_count, GL_UNSIGNED_SHORT, reinterpret_cast<void*>(sizeof(std::uint16_t)*draw_call.index_start));
}
}
void WMOGroupRender::initRenderBatches()
{
_render_batch_mapping.resize(_wmo_group->_vertices.size());
std::fill(_render_batch_mapping.begin(), _render_batch_mapping.end(), 0);
_render_batches.resize(_wmo_group->_batches.size());
std::size_t batch_counter = 0;
for (auto& batch : _wmo_group->_batches)
{
for (std::size_t i = 0; i < (batch.vertex_end - batch.vertex_start + 1); ++i)
{
_render_batch_mapping[batch.vertex_start + i] = batch_counter + 1;
}
std::uint32_t flags = 0;
if (_wmo_group->header.flags.exterior_lit || _wmo_group->header.flags.exterior)
{
flags |= WMORenderBatchFlags::eWMOBatch_ExteriorLit;
}
if (_wmo_group->header.flags.has_vertex_color || _wmo_group->header.flags.use_mocv2_for_texture_blending)
{
flags |= WMORenderBatchFlags::eWMOBatch_HasMOCV;
}
WMOMaterial const& mat (_wmo_group->wmo->materials.at (batch.texture));
if (mat.flags.unlit)
{
flags |= WMORenderBatchFlags::eWMOBatch_Unlit;
}
if (mat.flags.unfogged)
{
flags |= WMORenderBatchFlags::eWMOBatch_Unfogged;
}
std::uint32_t alpha_test;
switch (mat.blend_mode)
{
case 1:
alpha_test = 1; // 224/255
break;
case 2:
case 3:
case 4:
case 5:
case 6:
alpha_test = 2;
break;
case 0:
default:
alpha_test = 0;
break;
}
_render_batches[batch_counter] = WMORenderBatch{flags, mat.shader, 0, 0, 0, 0, alpha_test, 0};
batch_counter++;
}
}

View File

@@ -0,0 +1,94 @@
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#ifndef NOGGIT_WMOGROUPRENDER_HPP
#define NOGGIT_WMOGROUPRENDER_HPP
#include <noggit/rendering/BaseRender.hpp>
#include <opengl/shader.hpp>
#include <opengl/scoped.hpp>
#include <math/frustum.hpp>
class WMOGroup;
namespace Noggit::Rendering
{
struct WMORenderBatch
{
std::uint32_t flags;
std::uint32_t shader;
std::uint32_t tex_array0;
std::uint32_t tex_array1;
std::uint32_t tex0;
std::uint32_t tex1;
std::uint32_t alpha_test_mode;
std::uint32_t _pad1;
};
enum WMORenderBatchFlags
{
eWMOBatch_ExteriorLit = 0x1,
eWMOBatch_HasMOCV = 0x2,
eWMOBatch_Unlit = 0x4,
eWMOBatch_Unfogged = 0x8,
eWMOBatch_Collision = 0x10
};
struct WMOCombinedDrawCall
{
std::vector<int> samplers;
std::uint32_t index_start = 0;
std::uint32_t index_count = 0;
std::uint32_t n_used_samplers = 0;
bool backface_cull = false;
};
class WMOGroupRender : public BaseRender
{
public:
WMOGroupRender(WMOGroup* wmo_group);
void upload() override;
void unload() override;
void draw( OpenGL::Scoped::use_program& wmo_shader
, math::frustum const& frustum
, const float& cull_distance
, const glm::vec3& camera
, bool draw_fog
, bool world_has_skies
);
void initRenderBatches();
private:
void setupVao(OpenGL::Scoped::use_program& wmo_shader);
WMOGroup* _wmo_group;
std::vector<unsigned> _render_batch_mapping;
std::vector<WMORenderBatch> _render_batches;
std::vector<WMOCombinedDrawCall> _draw_calls;
OpenGL::Scoped::deferred_upload_vertex_arrays<1> _vertex_array;
GLuint const& _vao = _vertex_array[0];
OpenGL::Scoped::deferred_upload_buffers<8> _buffers;
GLuint const& _vertices_buffer = _buffers[0];
GLuint const& _normals_buffer = _buffers[1];
GLuint const& _texcoords_buffer = _buffers[2];
GLuint const& _texcoords_buffer_2 = _buffers[3];
GLuint const& _vertex_colors_buffer = _buffers[4];
GLuint const& _indices_buffer = _buffers[5];
GLuint const& _render_batch_mapping_buffer = _buffers[6];
GLuint const& _render_batch_tex_buffer = _buffers[7];
GLuint _render_batch_tex;
bool _uploaded = false;
bool _vao_is_setup = false;
};
}
#endif //NOGGIT_WMOGROUPRENDER_HPP

View File

@@ -0,0 +1,158 @@
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#include "WMORender.hpp"
#include <noggit/WMO.h>
using namespace Noggit::Rendering;
WMORender::WMORender(WMO* wmo)
: _wmo(wmo)
{
}
void WMORender::upload()
{
}
void WMORender::unload()
{
for (auto& group : _wmo->groups)
{
group.renderer()->unload();
}
}
void WMORender::draw(OpenGL::Scoped::use_program& wmo_shader
, glm::mat4x4 const& model_view
, glm::mat4x4 const& projection
, glm::mat4x4 const& transform_matrix
, bool boundingbox
, math::frustum const& frustum
, const float& cull_distance
, const glm::vec3& camera
, bool // draw_doodads
, bool draw_fog
, int animtime
, bool world_has_skies
, display_mode display
)
{
if (!_wmo->finishedLoading())
[[unlikely]]
{
return;
}
wmo_shader.uniform("ambient_color",glm::vec3(_wmo->ambient_light_color));
for (auto& group : _wmo->groups)
{
/*
if (!group.is_visible(transform_matrix, frustum, cull_distance, camera, display))
{
continue;
}
*/
group.renderer()->draw(wmo_shader
, frustum
, cull_distance
, camera
, draw_fog
, world_has_skies
);
/*
group.drawLiquid ( transform_matrix_transposed
, render
, draw_fog
, animtime
);
*/
}
if (boundingbox)
{
//OpenGL::Scoped::bool_setter<GL_BLEND, GL_TRUE> const blend;
//gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
for (auto& group : _wmo->groups)
{
OpenGL::primitives::wire_box::getInstance(_wmo->_context).draw( model_view
, projection
, transform_matrix
, {1.0f, 1.0f, 1.0f, 1.0f}
, group.BoundingBoxMin
, group.BoundingBoxMax
);
}
OpenGL::primitives::wire_box::getInstance(_wmo->_context).draw ( model_view
, projection
, transform_matrix
, {1.0f, 0.0f, 0.0f, 1.0f}
, glm::vec3(_wmo->extents[0].x, _wmo->extents[0].z, -_wmo->extents[0].y)
, glm::vec3(_wmo->extents[1].x, _wmo->extents[1].z, -_wmo->extents[1].y)
);
}
}
bool WMORender::drawSkybox(const glm::mat4x4& model_view, const glm::vec3& camera_pos,
OpenGL::Scoped::use_program& m2_shader, const math::frustum& frustum,
const float& cull_distance, int animtime, bool draw_particles, glm::vec3 aabb_min,
glm::vec3 aabb_max,
const std::map<int, std::pair<glm::vec3, glm::vec3>>& group_extents) const
{
if (!_wmo->skybox || !math::is_inside_of(camera_pos,aabb_min, aabb_max))
{
return false;
}
for (int i=0; i < _wmo->groups.size(); ++i)
{
auto const& g = _wmo->groups[i];
if (!g.has_skybox())
{
continue;
}
auto& extent(group_extents.at(i));
if (math::is_inside_of(camera_pos, extent.first, extent.second))
{
ModelInstance sky(_wmo->skybox.value()->file_key().filepath(), _wmo->_context);
sky.pos = camera_pos;
sky.scale = 2.f;
sky.recalcExtents();
OpenGL::M2RenderState model_render_state;
model_render_state.tex_arrays = {0, 0};
model_render_state.tex_indices = {0, 0};
model_render_state.tex_unit_lookups = {-1, -1};
gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gl.disable(GL_BLEND);
gl.depthMask(GL_TRUE);
m2_shader.uniform("blend_mode", 0);
m2_shader.uniform("unfogged", static_cast<int>(model_render_state.unfogged));
m2_shader.uniform("unlit", static_cast<int>(model_render_state.unlit));
m2_shader.uniform("tex_unit_lookup_1", 0);
m2_shader.uniform("tex_unit_lookup_2", 0);
m2_shader.uniform("pixel_shader", 0);
_wmo->skybox->get()->draw(model_view, sky, m2_shader, model_render_state, frustum, cull_distance, camera_pos, animtime, display_mode::in_3D);
return true;
}
}
return false;
}

View File

@@ -0,0 +1,59 @@
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#ifndef NOGGIT_WMORENDER_HPP
#define NOGGIT_WMORENDER_HPP
#include <noggit/rendering/BaseRender.hpp>
#include <noggit/tool_enums.hpp>
#include <opengl/scoped.hpp>
#include <opengl/shader.hpp>
#include <glm/glm.hpp>
#include <math/frustum.hpp>
#include <map>
class WMO;
namespace Noggit::Rendering
{
class WMORender : public BaseRender
{
public:
WMORender(WMO* wmo);
void upload() override;
void unload() override;
void draw(OpenGL::Scoped::use_program& wmo_shader
, glm::mat4x4 const& model_view
, glm::mat4x4 const& projection
, glm::mat4x4 const& transform_matrix
, bool boundingbox
, math::frustum const& frustum
, const float& cull_distance
, const glm::vec3& camera
, bool draw_doodads
, bool draw_fog
, int animtime
, bool world_has_skies
, display_mode display
);
bool drawSkybox(glm::mat4x4 const& model_view
, glm::vec3 const& camera_pos
, OpenGL::Scoped::use_program& m2_shader
, math::frustum const& frustum
, const float& cull_distance
, int animtime
, bool draw_particles
, glm::vec3 aabb_min
, glm::vec3 aabb_max
, std::map<int, std::pair<glm::vec3, glm::vec3>> const& group_extents
) const;
private:
WMO* _wmo;
};
}
#endif //NOGGIT_WMORENDER_HPP

View File

@@ -189,7 +189,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
wmo.recalcExtents();
}
hadSky = wmo.wmo->draw_skybox( model_view
hadSky = wmo.wmo->renderer()->drawSkybox(model_view
, camera_pos
, m2_shader
, frustum
@@ -334,6 +334,8 @@ void WorldRender::draw (glm::mat4x4 const& model_view
tsl::robin_map<Model*, std::vector<glm::mat4x4>> models_to_draw;
std::vector<WMOInstance*> wmos_to_draw;
// frame counter loop. pretty hacky but works
// this is used to make sure no object is processed more than once within a frame
static int frame = 0;
if (frame == std::numeric_limits<int>::max())
@@ -365,6 +367,8 @@ void WorldRender::draw (glm::mat4x4 const& model_view
if (tile->camDist() > _cull_distance)
continue;
// TODO: subject to potential generalization
for (auto& pair : tile->getObjectInstances())
{
if (pair.second[0]->which() == eMODEL)
@@ -453,6 +457,48 @@ void WorldRender::draw (glm::mat4x4 const& model_view
{
bool is_hidden = instance->wmo->is_hidden();
bool is_exclusion_filtered = false;
// minimap render exclusion filters
// per-model
if (minimap_render && minimap_render_settings->use_filters)
{
if (instance->instance_model()->file_key().hasFilepath())
{
for(int i = 0; i < minimap_render_settings->wmo_model_filter_exclude->count(); ++i)
{
auto item = reinterpret_cast<Ui::MinimapWMOModelFilterEntry*>(
minimap_render_settings->wmo_model_filter_exclude->itemWidget(
minimap_render_settings->wmo_model_filter_exclude->item(i)));
if (item->getFileName().toStdString() == instance->instance_model()->file_key().filepath())
{
is_exclusion_filtered = true;
break;
}
}
}
// per-instance
for(int i = 0; i < minimap_render_settings->wmo_instance_filter_exclude->count(); ++i)
{
auto item = reinterpret_cast<Ui::MinimapInstanceFilterEntry*>(
minimap_render_settings->wmo_instance_filter_exclude->itemWidget(
minimap_render_settings->wmo_instance_filter_exclude->item(i)));
if (item->getUid() == instance->uid)
{
is_exclusion_filtered = true;
break;
}
}
// skip model rendering if excluded by filter
if (is_exclusion_filtered)
continue;
}
if (draw_hidden_models || !is_hidden)
{
instance->draw(wmo_program
@@ -538,7 +584,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
bool draw_doodads_wmo = draw_wmo && draw_wmo_doodads;
// M2s / models
if (draw_models || draw_doodads_wmo)
if (draw_models || draw_doodads_wmo || (minimap_render && minimap_render_settings->use_filters))
{
ZoneScopedN("World::draw() : Draw M2s");
@@ -555,7 +601,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
std::unordered_map<Model*, std::size_t> model_boxes_to_draw;
{
if (draw_models)
if (draw_models || (minimap_render && minimap_render_settings->use_filters))
{
OpenGL::Scoped::use_program m2_shader {*_m2_instanced_program.get()};
@@ -576,10 +622,37 @@ void WorldRender::draw (glm::mat4x4 const& model_view
for (auto& pair : models_to_draw)
{
bool is_inclusion_filtered = false;
// minimap render inclusion filters
// per-model
if (minimap_render && minimap_render_settings->use_filters)
{
if (pair.first->file_key().hasFilepath())
{
for(int i = 0; i < minimap_render_settings->m2_model_filter_include->count(); ++i)
{
auto item = reinterpret_cast<Ui::MinimapM2ModelFilterEntry*>(
minimap_render_settings->m2_model_filter_include->itemWidget(
minimap_render_settings->m2_model_filter_include->item(i)));
if (item->getFileName().toStdString() == pair.first->file_key().filepath())
{
is_inclusion_filtered = true;
break;
}
}
}
// skip model rendering if excluded by filter
if (!is_inclusion_filtered)
continue;
}
if (draw_hidden_models || !pair.first->is_hidden())
{
pair.first->draw( model_view
, reinterpret_cast<std::vector<glm::mat4x4> const&>(pair.second)
, pair.second
, m2_shader
, model_render_state
, frustum

View File

@@ -118,9 +118,11 @@ namespace OpenGL
gl.bindBuffer (type, _old);
}
inline index_buffer_manual_binder::index_buffer_manual_binder (GLuint buffer)
: _buffer (buffer)
{}
inline index_buffer_manual_binder::index_buffer_manual_binder(GLuint buffer)
: _buffer (buffer)
{
}
inline void index_buffer_manual_binder::bind()
{

View File

@@ -4,6 +4,7 @@
#include <opengl/shader.fwd.hpp>
#include <opengl/types.hpp>
#include <opengl/texture.hpp>
#include <initializer_list>
#include <map>
#include <set>
@@ -11,6 +12,7 @@
#include <vector>
#include <array>
#include <cstdint>
#include <optional>
#include <unordered_map>
#include <external/tsl/robin_map.h>
#include <glm/vec2.hpp>