diff --git a/src/noggit/Model.cpp b/src/noggit/Model.cpp index ef77c06c..3c54ccb6 100644 --- a/src/noggit/Model.cpp +++ b/src/noggit/Model.cpp @@ -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)) diff --git a/src/noggit/WMO.cpp b/src/noggit/WMO.cpp index 89a77895..e2c59660 100644 --- a/src/noggit/WMO.cpp +++ b/src/noggit/WMO.cpp @@ -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 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 WMO::intersect (math::ray const& ray) const { std::vector results; @@ -450,67 +370,8 @@ std::vector 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> const& group_extents - ) const -{ - - if (!skybox || !math::is_inside_of(camera_pos,aabb_min, aabb_max)) - { - return false; - } - - for (int i=0; ifile_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(model_render_state.unfogged)); - m2_shader.uniform("unlit", static_cast(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> WMO::doodads_per_group(uint16_t doodadset) const { std::map> doodads; @@ -538,14 +399,6 @@ std::map> 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 _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::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{-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 ( _vertices_buffer - , _vertices.size() * sizeof (*_vertices.data()) - , _vertices.data() - , GL_STATIC_DRAW - ); - - gl.bufferData ( _normals_buffer - , _normals.size() * sizeof (*_normals.data()) - , _normals.data() - , GL_STATIC_DRAW - ); - - gl.bufferData ( _texcoords_buffer - , _texcoords.size() * sizeof (*_texcoords.data()) - , _texcoords.data() - , GL_STATIC_DRAW - ); - - gl.bufferData ( _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(_indices_buffer, _indices, GL_STATIC_DRAW); - - if (header.flags.has_two_motv) - { - gl.bufferData ( _texcoords_buffer_2 - , _texcoords_2 - , GL_STATIC_DRAW - ); - } - - gl.bufferData ( _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(sizeof(std::uint16_t)*draw_call.index_start)); - - } - -} void WMOGroup::intersect (math::ray const& ray, std::vector* 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 ); diff --git a/src/noggit/WMO.h b/src/noggit/WMO.h index ffeeb6b9..ada8de5b 100644 --- a/src/noggit/WMO.h +++ b/src/noggit/WMO.h @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -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 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* 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 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 _texcoords; std::vector _texcoords_2; std::vector _vertex_colors; - std::vector _render_batch_mapping; std::vector _indices; - std::vector _render_batches; - std::vector _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> const& group_extents - ) const; - [[nodiscard]] std::vector intersect (math::ray const&) const; @@ -326,8 +257,6 @@ public: void waitForChildrenLoaded() override; - void unload(); - [[nodiscard]] std::map> 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 diff --git a/src/noggit/WMOInstance.cpp b/src/noggit/WMOInstance.cpp index 9b3ca875..7446f88b 100644 --- a/src/noggit/WMOInstance.cpp +++ b/src/noggit/WMOInstance.cpp @@ -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>* 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); } diff --git a/src/noggit/rendering/WMOGroupRender.cpp b/src/noggit/rendering/WMOGroupRender.cpp new file mode 100644 index 00000000..52792700 --- /dev/null +++ b/src/noggit/rendering/WMOGroupRender.cpp @@ -0,0 +1,393 @@ +// This file is part of Noggit3, licensed under GNU General Public License (version 3). + +#include "WMOGroupRender.hpp" +#include + +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 _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::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{-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 ( _vertices_buffer + , _wmo_group->_vertices.size() * sizeof (*_wmo_group->_vertices.data()) + , _wmo_group->_vertices.data() + , GL_STATIC_DRAW + ); + + gl.bufferData ( _normals_buffer + , _wmo_group->_normals.size() * sizeof (*_wmo_group->_normals.data()) + , _wmo_group->_normals.data() + , GL_STATIC_DRAW + ); + + gl.bufferData ( _texcoords_buffer + , _wmo_group->_texcoords.size() * sizeof (*_wmo_group->_texcoords.data()) + , _wmo_group->_texcoords.data() + , GL_STATIC_DRAW + ); + + gl.bufferData ( _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(_indices_buffer, _wmo_group->_indices, GL_STATIC_DRAW); + + if (_wmo_group->header.flags.has_two_motv) + { + gl.bufferData ( _texcoords_buffer_2 + , _wmo_group->_texcoords_2 + , GL_STATIC_DRAW + ); + } + + gl.bufferData ( _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(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++; + } +} diff --git a/src/noggit/rendering/WMOGroupRender.hpp b/src/noggit/rendering/WMOGroupRender.hpp new file mode 100644 index 00000000..a1d5745b --- /dev/null +++ b/src/noggit/rendering/WMOGroupRender.hpp @@ -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 +#include +#include +#include + +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 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 _render_batch_mapping; + std::vector _render_batches; + std::vector _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 diff --git a/src/noggit/rendering/WMORender.cpp b/src/noggit/rendering/WMORender.cpp new file mode 100644 index 00000000..fb27c66f --- /dev/null +++ b/src/noggit/rendering/WMORender.cpp @@ -0,0 +1,158 @@ +// This file is part of Noggit3, licensed under GNU General Public License (version 3). + +#include "WMORender.hpp" +#include + +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 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>& 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(model_render_state.unfogged)); + m2_shader.uniform("unlit", static_cast(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; +} diff --git a/src/noggit/rendering/WMORender.hpp b/src/noggit/rendering/WMORender.hpp new file mode 100644 index 00000000..3670fcd5 --- /dev/null +++ b/src/noggit/rendering/WMORender.hpp @@ -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 +#include +#include +#include +#include +#include + +#include + +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> const& group_extents + ) const; + + private: + WMO* _wmo; + }; +} + +#endif //NOGGIT_WMORENDER_HPP diff --git a/src/noggit/rendering/WorldRender.cpp b/src/noggit/rendering/WorldRender.cpp index 2d4bdc6f..68c54e88 100644 --- a/src/noggit/rendering/WorldRender.cpp +++ b/src/noggit/rendering/WorldRender.cpp @@ -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> models_to_draw; std::vector 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::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( + 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( + 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_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( + 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 const&>(pair.second) + , pair.second , m2_shader , model_render_state , frustum diff --git a/src/opengl/scoped.ipp b/src/opengl/scoped.ipp index 60008f7c..c2e56971 100644 --- a/src/opengl/scoped.ipp +++ b/src/opengl/scoped.ipp @@ -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() { diff --git a/src/opengl/shader.hpp b/src/opengl/shader.hpp index 51e40de7..76998401 100644 --- a/src/opengl/shader.hpp +++ b/src/opengl/shader.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -11,6 +12,7 @@ #include #include #include +#include #include #include #include