Separate WMO rendering into its own component | Update Model.cpp, WMO.cpp, and 9 more files...
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
110
src/noggit/WMO.h
110
src/noggit/WMO.h
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
393
src/noggit/rendering/WMOGroupRender.cpp
Normal file
393
src/noggit/rendering/WMOGroupRender.cpp
Normal 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++;
|
||||
}
|
||||
}
|
||||
94
src/noggit/rendering/WMOGroupRender.hpp
Normal file
94
src/noggit/rendering/WMOGroupRender.hpp
Normal 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
|
||||
158
src/noggit/rendering/WMORender.cpp
Normal file
158
src/noggit/rendering/WMORender.cpp
Normal 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;
|
||||
}
|
||||
59
src/noggit/rendering/WMORender.hpp
Normal file
59
src/noggit/rendering/WMORender.hpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user