Files
noggit-red/src/noggit/WMO.cpp
2022-11-22 23:03:18 +01:00

1180 lines
27 KiB
C++
Executable File

// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#include <math/frustum.hpp>
#include <noggit/AsyncLoader.h>
#include <noggit/Log.h> // LogDebug
#include <noggit/ModelManager.h> // ModelManager
#include <noggit/TextureManager.h> // TextureManager, Texture
#include <noggit/WMO.h>
#include <noggit/World.h>
#include <noggit/rendering/Primitives.hpp>
#include <noggit/application/NoggitApplication.hpp>
#include <opengl/scoped.hpp>
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>
WMO::WMO(BlizzardArchive::Listfile::FileKey const& file_key, Noggit::NoggitRenderContext context)
: AsyncObject(file_key)
, _context(context)
, _renderer(this)
{
}
void WMO::finishLoading ()
{
BlizzardArchive::ClientFile f(_file_key.filepath(), Noggit::Application::NoggitApplication::instance()->clientData());
if (f.isEof()) {
LogError << "Error loading WMO \"" << _file_key.stringRepr() << "\"." << std::endl;
return;
}
uint32_t fourcc;
uint32_t size;
float ff[3];
char const* ddnames = nullptr;
char const* groupnames = nullptr;
// - MVER ----------------------------------------------
uint32_t version;
f.read (&fourcc, 4);
f.seekRelative (4);
f.read (&version, 4);
assert (fourcc == 'MVER' && version == 17);
// - MOHD ----------------------------------------------
f.read (&fourcc, 4);
f.seekRelative (4);
assert (fourcc == 'MOHD');
CArgb ambient_color;
unsigned int nTextures, nGroups, nP, nLights, nModels, nDoodads, nDoodadSets;
// header
f.read (&nTextures, 4);
f.read (&nGroups, 4);
f.read (&nP, 4);
f.read (&nLights, 4);
f.read (&nModels, 4);
f.read (&nDoodads, 4);
f.read (&nDoodadSets, 4);
f.read (&ambient_color, 4);
f.read (&WmoId, 4);
f.read (ff, 12);
extents[0] = ::glm::vec3 (ff[0], ff[1], ff[2]);
f.read (ff, 12);
extents[1] = ::glm::vec3 (ff[0], ff[1], ff[2]);
f.read(&flags, 2);
f.seekRelative (2);
ambient_light_color.x = static_cast<float>(ambient_color.r) / 255.f;
ambient_light_color.y = static_cast<float>(ambient_color.g) / 255.f;
ambient_light_color.z = static_cast<float>(ambient_color.b) / 255.f;
ambient_light_color.w = static_cast<float>(ambient_color.a) / 255.f;
// - MOTX ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MOTX');
std::vector<char> texbuf (size);
f.read (texbuf.data(), texbuf.size());
// - MOMT ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MOMT');
std::size_t const num_materials (size / 0x40);
materials.resize (num_materials);
std::map<std::uint32_t, std::size_t> texture_offset_to_inmem_index;
auto load_texture
( [&] (std::uint32_t ofs)
{
char const* texture
(texbuf[ofs] ? &texbuf[ofs] : "textures/shanecube.blp");
auto const mapping
(texture_offset_to_inmem_index.emplace(ofs, textures.size()));
if (mapping.second)
{
textures.emplace_back(texture, _context);
}
return mapping.first->second;
}
);
for (size_t i(0); i < num_materials; ++i)
{
f.read(&materials[i], sizeof(WMOMaterial));
uint32_t shader = materials[i].shader;
bool use_second_texture = (shader == 6 || shader == 5 || shader == 3);
materials[i].texture1 = load_texture(materials[i].texture_offset_1);
if (use_second_texture)
{
materials[i].texture2 = load_texture(materials[i].texture_offset_2);
}
}
// - MOGN ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MOGN');
groupnames = reinterpret_cast<char const*> (f.getPointer ());
f.seekRelative (size);
// - MOGI ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MOGI');
groups.reserve(nGroups);
for (size_t i (0); i < nGroups; ++i) {
groups.emplace_back (this, &f, i, groupnames);
}
// - MOSB ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MOSB');
if (size > 4)
{
std::string path = BlizzardArchive::ClientData::normalizeFilenameInternal(std::string (reinterpret_cast<char const*>(f.getPointer ())));
auto from = std::string("mdx");
auto to = std::string("m2");
size_t start_pos = 0;
while ((start_pos = path.find(from, start_pos)) != std::string::npos) {
path.replace(start_pos, from.length(), to);
start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
}
if (path.length())
{
if (Noggit::Application::NoggitApplication::instance()->clientData()->exists(path))
{
skybox = scoped_model_reference(path, _context);
}
}
}
f.seekRelative (size);
// - MOPV ----------------------------------------------
f.read (&fourcc, 4);
f.read(&size, 4);
assert (fourcc == 'MOPV');
f.seekRelative (size);
/*
std::vector<glm::vec3> portal_vertices;
for (size_t i (0); i < size / 12; ++i) {
f.read (ff, 12);
portal_vertices.push_back(glm::vec3(ff[0], ff[2], -ff[1]));
}
*/
// - MOPT ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MOPT');
f.seekRelative (size);
// - MOPR ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert(fourcc == 'MOPR');
f.seekRelative (size);
// - MOVV ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MOVV');
f.seekRelative (size);
// - MOVB ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MOVB');
f.seekRelative (size);
// - MOLT ----------------------------------------------
f.read (&fourcc, 4);
f.seekRelative (4);
assert (fourcc == 'MOLT');
lights.reserve(nLights);
for (size_t i (0); i < nLights; ++i) {
WMOLight l;
l.init (&f);
lights.push_back (l);
}
// - MODS ----------------------------------------------
f.read (&fourcc, 4);
f.seekRelative (4);
assert (fourcc == 'MODS');
doodadsets.reserve(nDoodadSets);
for (size_t i (0); i < nDoodadSets; ++i) {
WMODoodadSet dds;
f.read (&dds, 32);
doodadsets.push_back (dds);
}
// - MODN ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MODN');
if (size)
{
ddnames = reinterpret_cast<char const*> (f.getPointer ());
f.seekRelative (size);
}
// - MODD ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MODD');
modelis.reserve(size / 0x28);
for (size_t i (0); i < size / 0x28; ++i)
{
struct
{
uint32_t name_offset : 24;
uint32_t flag_AcceptProjTex : 1;
uint32_t flag_0x2 : 1;
uint32_t flag_0x4 : 1;
uint32_t flag_0x8 : 1;
uint32_t flags_unused : 4;
} x;
size_t after_entry (f.getPos() + 0x28);
f.read (&x, sizeof (x));
modelis.emplace_back(ddnames + x.name_offset, &f, _context);
model_nearest_light_vector.emplace_back();
f.seek (after_entry);
}
// - MFOG ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MFOG');
int nfogs = size / 0x30;
fogs.reserve(nfogs);
for (size_t i (0); i < nfogs; ++i)
{
WMOFog fog;
fog.init (&f);
fogs.push_back (std::move(fog));
}
for (auto& group : groups)
group.load();
finished = true;
_state_changed.notify_all();
}
void WMO::waitForChildrenLoaded()
{
for (auto& tex : textures)
{
tex.get()->wait_until_loaded();
}
for (auto& doodad : modelis)
{
doodad.model->wait_until_loaded();
doodad.model->waitForChildrenLoaded();
}
}
std::vector<float> WMO::intersect (math::ray const& ray) const
{
std::vector<float> results;
if (!finishedLoading() || loading_failed())
{
return results;
}
for (auto& group : groups)
{
group.intersect (ray, &results);
}
return results;
}
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;
if (doodadset >= doodadsets.size())
{
LogError << "Invalid doodadset for instance of wmo " << _file_key.stringRepr() << std::endl;
return doodads;
}
auto const& dset = doodadsets[doodadset];
uint32_t start = dset.start, end = start + dset.size;
for (int i = 0; i < groups.size(); ++i)
{
for (uint16_t ref : groups[i].doodad_ref())
{
if (ref >= start && ref < end)
{
doodads[i].push_back(modelis[ref]);
}
}
}
return doodads;
}
void WMOLight::init(BlizzardArchive::ClientFile* f)
{
char type[4];
f->read(&type, 4);
f->read(&color, 4);
f->read(&pos, 12);
f->read(&intensity, 4);
f->read(unk, 4 * 5);
f->read(&r, 4);
pos = glm::vec3(pos.x, pos.z, -pos.y);
// rgb? bgr? hm
float fa = ((color & 0xff000000) >> 24) / 255.0f;
float fr = ((color & 0x00ff0000) >> 16) / 255.0f;
float fg = ((color & 0x0000ff00) >> 8) / 255.0f;
float fb = ((color & 0x000000ff)) / 255.0f;
fcolor = glm::vec4(fr, fg, fb, fa);
fcolor *= intensity;
fcolor.w = 1.0f;
/*
// light logging
gLog("Light %08x @ (%4.2f,%4.2f,%4.2f)\t %4.2f, %4.2f, %4.2f, %4.2f, %4.2f, %4.2f, %4.2f\t(%d,%d,%d,%d)\n",
color, pos.x, pos.y, pos.z, intensity,
unk[0], unk[1], unk[2], unk[3], unk[4], r,
type[0], type[1], type[2], type[3]);
*/
}
void WMOLight::setup(GLint)
{
// not used right now -_-
}
void WMOLight::setupOnce(GLint, glm::vec3, glm::vec3)
{
//glm::vec4position(dir, 0);
//glm::vec4position(0,1,0,0);
//glm::vec4ambient = glm::vec4(light_color * 0.3f, 1);
//glm::vec4diffuse = glm::vec4(light_color, 1);
//gl.enable(light);
}
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
f->read(&flags, 4);
float ff[3];
f->read(ff, 12);
VertexBoxMax = glm::vec3(ff[0], ff[1], ff[2]);
f->read(ff, 12);
VertexBoxMin = glm::vec3(ff[0], ff[1], ff[2]);
int nameOfs;
f->read(&nameOfs, 4);
//! \todo get proper name from group header and/or dbc?
if (nameOfs > 0) {
name = std::string(names + nameOfs);
}
else name = "(no name)";
}
WMOGroup::WMOGroup(WMOGroup const& other)
: BoundingBoxMin(other.BoundingBoxMin)
, BoundingBoxMax(other.BoundingBoxMax)
, VertexBoxMin(other.VertexBoxMin)
, VertexBoxMax(other.VertexBoxMax)
, use_outdoor_lights(other.use_outdoor_lights)
, name(other.name)
, wmo(other.wmo)
, header(other.header)
, center(other.center)
, rad(other.rad)
, num(other.num)
, fog(other.fog)
, _doodad_ref(other._doodad_ref)
, _batches(other._batches)
, _vertices(other._vertices)
, _normals(other._normals)
, _texcoords(other._texcoords)
, _texcoords_2(other._texcoords_2)
, _vertex_colors(other._vertex_colors)
, _indices(other._indices)
, _renderer(this)
{
if (other.lq)
{
lq = std::make_unique<wmo_liquid>(*other.lq.get());
}
}
namespace
{
glm::vec4 colorFromInt(unsigned int col)
{
GLubyte r, g, b, a;
a = (col & 0xFF000000) >> 24;
r = (col & 0x00FF0000) >> 16;
g = (col & 0x0000FF00) >> 8;
b = (col & 0x000000FF);
return glm::vec4(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
}
}
void WMOGroup::load()
{
// open group file
std::stringstream curNum;
curNum << "_" << std::setw (3) << std::setfill ('0') << num;
std::string fname = wmo->file_key().filepath();
fname.insert (fname.find (".wmo"), curNum.str ());
BlizzardArchive::ClientFile f(fname, Noggit::Application::NoggitApplication::instance()->clientData());
if (f.isEof()) {
LogError << "Error loading WMO \"" << fname << "\"." << std::endl;
return;
}
uint32_t fourcc;
uint32_t size;
// - MVER ----------------------------------------------
f.read (&fourcc, 4);
f.seekRelative (4);
uint32_t version;
f.read (&version, 4);
assert (fourcc == 'MVER' && version == 17);
// - MOGP ----------------------------------------------
f.read (&fourcc, 4);
f.seekRelative (4);
assert (fourcc == 'MOGP');
f.read (&header, sizeof (wmo_group_header));
unsigned fog_index = header.fogs[0];
// downport hack
if (fog_index >= wmo->fogs.size())
{
fog_index = 0;
}
WMOFog &wf = wmo->fogs[fog_index];
if (wf.r2 <= 0) fog = -1; // default outdoor fog..?
else fog = header.fogs[0];
BoundingBoxMin = ::glm::vec3 (header.box1[0], header.box1[2], -header.box1[1]);
BoundingBoxMax = ::glm::vec3 (header.box2[0], header.box2[2], -header.box2[1]);
// - MOPY ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MOPY');
f.seekRelative (size);
// - MOVI ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MOVI');
_indices.resize (size / sizeof (uint16_t));
f.read (_indices.data (), size);
// - MOVT ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MOVT');
// let's hope it's padded to 12 bytes, not 16...
::glm::vec3 const* vertices = reinterpret_cast< ::glm::vec3 const*>(f.getPointer ());
VertexBoxMin = ::glm::vec3 (std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
VertexBoxMax = ::glm::vec3 (std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
rad = 0;
_vertices.resize(size / sizeof (::glm::vec3));
for (size_t i = 0; i < _vertices.size(); ++i)
{
_vertices[i] = glm::vec3(vertices[i].x, vertices[i].z, -vertices[i].y);
::glm::vec3& v = _vertices[i];
if (v.x < VertexBoxMin.x) VertexBoxMin.x = v.x;
if (v.y < VertexBoxMin.y) VertexBoxMin.y = v.y;
if (v.z < VertexBoxMin.z) VertexBoxMin.z = v.z;
if (v.x > VertexBoxMax.x) VertexBoxMax.x = v.x;
if (v.y > VertexBoxMax.y) VertexBoxMax.y = v.y;
if (v.z > VertexBoxMax.z) VertexBoxMax.z = v.z;
}
center = (VertexBoxMax + VertexBoxMin) * 0.5f;
rad = (VertexBoxMax - center).length () + 300.0f;;
f.seekRelative (size);
// - MONR ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MONR');
_normals.resize (size / sizeof (::glm::vec3));
f.read (_normals.data(), size);
for (auto& n : _normals)
{
n = {n.x, n.z, -n.y};
}
// - MOTV ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MOTV');
_texcoords.resize (size / sizeof (glm::vec2));
f.read (_texcoords.data (), size);
// - MOBA ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
assert (fourcc == 'MOBA');
_batches.resize (size / sizeof (wmo_batch));
f.read (_batches.data (), size);
_renderer.initRenderBatches();
// - MOLR ----------------------------------------------
if (header.flags.has_light)
{
f.read (&fourcc, 4);
f.read (&size, 4);
if (fourcc != 'MOLR')
{
LogError << "Broken header in WMO \"" << fname << "\". Trying to continue reading." << std::endl;
f.seek (f.getPos() - 8);
}
else
{
f.seekRelative (size);
}
}
// - MODR ----------------------------------------------
if (header.flags.has_doodads)
{
f.read (&fourcc, 4);
f.read (&size, 4);
if (fourcc != 'MODR')
{
LogError << "Broken header in WMO \"" << fname << "\". Trying to continue reading." << std::endl;
f.seek (f.getPos() - 8);
}
else
{
_doodad_ref.resize (size / sizeof (int16_t));
f.read (_doodad_ref.data (), size);
}
}
// - MOBN ----------------------------------------------
if (header.flags.has_bsp_tree)
{
f.read (&fourcc, 4);
f.read (&size, 4);
if (fourcc != 'MOBN')
{
LogError << "Broken header in WMO \"" << fname << "\". Trying to continue reading." << std::endl;
f.seek (f.getPos() - 8);
}
else
{
f.seekRelative (size);
}
}
// - MOBR ----------------------------------------------
if (header.flags.has_bsp_tree)
{
f.read (&fourcc, 4);
f.read (&size, 4);
if (fourcc != 'MOBR')
{
LogError << "Broken header in WMO \"" << fname << "\". Trying to continue reading." << std::endl;
f.seek (f.getPos() - 8);
}
else
{
f.seekRelative (size);
}
}
if (header.flags.flag_0x400)
{
// - MPBV ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
if (fourcc != 'MPBV')
{
LogError << "Broken header in WMO \"" << fname << "\". Trying to continue reading." << std::endl;
f.seek (f.getPos() - 8);
}
else
{
f.seekRelative (size);
}
// - MPBP ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
if (fourcc != 'MPBP')
{
LogError << "Broken header in WMO \"" << fname << "\". Trying to continue reading." << std::endl;
f.seek (f.getPos() - 8);
}
else
{
f.seekRelative (size);
}
// - MPBI ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
if (fourcc != 'MPBI')
{
LogError << "Broken header in WMO \"" << fname << "\". Trying to continue reading." << std::endl;
f.seek (f.getPos() - 8);
}
else
{
f.seekRelative (size);
}
// - MPBG ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
if (fourcc != 'MPBG')
{
LogError << "Broken header in WMO \"" << fname << "\". Trying to continue reading." << std::endl;
f.seek (f.getPos() - 8);
}
else
{
f.seekRelative (size);
}
}
// - MOCV ----------------------------------------------
if (header.flags.has_vertex_color)
{
f.read (&fourcc, 4);
f.read (&size, 4);
if (fourcc != 'MOCV')
{
LogError << "Broken header in WMO \"" << fname << "\". Trying to continue reading." << std::endl;
f.seek (f.getPos() - 8);
}
else
{
load_mocv(f, size);
}
}
// - MLIQ ----------------------------------------------
if (header.flags.has_water)
{
f.read (&fourcc, 4);
f.read (&size, 4);
if (fourcc != 'MLIQ')
{
LogError << "Broken header in WMO \"" << fname << "\". Trying to continue reading." << std::endl;
f.seek (f.getPos() - 8);
}
else
{
WMOLiquidHeader hlq;
f.read(&hlq, 0x1E);
lq = std::make_unique<wmo_liquid> ( &f
, hlq
, wmo->materials[hlq.material_id]
, header.group_liquid
, (bool)wmo->flags.use_liquid_type_dbc_id
, (bool)header.flags.ocean
);
// creating the wmo liquid doesn't move the position
f.seekRelative(size - 0x1E);
}
}
if (header.flags.has_mori_morb)
{
// - MORI ----------------------------------------------
f.read (&fourcc, 4);
f.read (&size, 4);
if (fourcc != 'MORI')
{
LogError << "Broken header in WMO \"" << fname << "\". Trying to continue reading." << std::endl;
f.seek (f.getPos() - 8);
}
else
{
f.seekRelative (size);
}
// - MORB ----------------------------------------------
f.read(&fourcc, 4);
f.read(&size, 4);
if (fourcc != 'MORB')
{
LogError << "Broken header in WMO \"" << fname << "\". Trying to continue reading." << std::endl;
f.seek (f.getPos() - 8);
}
else
{
f.seekRelative (size);
}
}
// - MOTV ----------------------------------------------
if (header.flags.has_two_motv)
{
f.read (&fourcc, 4);
f.read (&size, 4);
if (fourcc != 'MOTV')
{
LogError << "Broken header in WMO \"" << fname << "\". Trying to continue reading." << std::endl;
f.seek (f.getPos() - 8);
}
else
{
_texcoords_2.resize(size / sizeof(glm::vec2));
f.read(_texcoords_2.data(), size);
}
}
// - MOCV ----------------------------------------------
if (header.flags.use_mocv2_for_texture_blending)
{
f.read (&fourcc, 4);
f.read (&size, 4);
if (fourcc != 'MOCV')
{
LogError << "Broken header in WMO \"" << fname << "\". Trying to continue reading." << std::endl;
f.seek (f.getPos() - 8);
}
else
{
std::vector<CImVector> mocv_2(size / sizeof(CImVector));
f.read(mocv_2.data(), size);
for (int i = 0; i < mocv_2.size(); ++i)
{
float alpha = static_cast<float>(mocv_2[i].a) / 255.f;
// the second mocv is used for texture blending only
if (header.flags.has_vertex_color)
{
_vertex_colors[i].w = alpha;
}
else // no vertex coloring, only texture blending with the alpha
{
_vertex_colors.emplace_back(0.f, 0.f, 0.f, alpha);
}
}
}
}
//dl_light = 0;
// "real" lighting?
if (header.flags.indoor && header.flags.has_vertex_color)
{
::glm::vec3 dirmin(1, 1, 1);
float lenmin;
for (auto doodad : _doodad_ref)
{
if (doodad >= wmo->modelis.size())
{
continue;
LogError << "The WMO file currently loaded is potentially corrupt. Non-existing doodad referenced." << std::endl;
}
lenmin = 999999.0f * 999999.0f;
ModelInstance& mi = wmo->modelis[doodad];
for (unsigned int j = 0; j < wmo->lights.size(); j++)
{
WMOLight& l = wmo->lights[j];
::glm::vec3 dir = l.pos - mi.pos;
float ll = glm::length(dir) * glm::length(dir);
if (ll < lenmin)
{
lenmin = ll;
dirmin = dir;
}
}
wmo->model_nearest_light_vector[doodad] = dirmin;
}
use_outdoor_lights = false;
}
else
{
use_outdoor_lights = true;
}
}
void WMOGroup::load_mocv(BlizzardArchive::ClientFile& f, uint32_t size)
{
uint32_t const* colors = reinterpret_cast<uint32_t const*> (f.getPointer());
_vertex_colors.resize(size / sizeof(uint32_t));
for (size_t i(0); i < size / sizeof(uint32_t); ++i)
{
_vertex_colors[i] = colorFromInt(colors[i]);
}
if (wmo->flags.do_not_fix_vertex_color_alpha)
{
int interior_batchs_start = 0;
if (header.transparency_batches_count > 0)
{
interior_batchs_start = _batches[header.transparency_batches_count - 1].vertex_end + 1;
}
for (int n = interior_batchs_start; n < _vertex_colors.size(); ++n)
{
_vertex_colors[n].w = header.flags.exterior ? 1.f : 0.f;
}
}
else
{
fix_vertex_color_alpha();
}
// there's no read so this is required
f.seekRelative(size);
}
void WMOGroup::fix_vertex_color_alpha()
{
int interior_batchs_start = 0;
if (header.transparency_batches_count > 0)
{
interior_batchs_start = _batches[header.transparency_batches_count - 1].vertex_end + 1;
}
glm::vec4 wmo_ambient_color;
if (wmo->flags.use_unified_render_path)
{
wmo_ambient_color = {0.f, 0.f, 0.f, 0.f};
}
else
{
wmo_ambient_color = wmo->ambient_light_color;
// w is not used, set it to 0 to avoid changing the vertex color alpha
wmo_ambient_color.w = 0.f;
}
for (int i = 0; i < _vertex_colors.size(); ++i)
{
auto& color = _vertex_colors[i];
float r = color.x;
float g = color.y;
float b = color.z;
float a = color.w;
// I removed the color = color/2 because it's just multiplied by 2 in the shader afterward in blizzard's code
if (i >= interior_batchs_start)
{
r += ((r * a / 64.f) - wmo_ambient_color.x);
g += ((g * a / 64.f) - wmo_ambient_color.y);
r += ((b * a / 64.f) - wmo_ambient_color.z);
}
else
{
r -= wmo_ambient_color.x;
g -= wmo_ambient_color.y;
b -= wmo_ambient_color.z;
r = (r * (1.f - a));
g = (g * (1.f - a));
b = (b * (1.f - a));
}
color.x = std::min(255.f, std::max(0.f, r));
color.y = std::min(255.f, std::max(0.f, g));
color.z = std::min(255.f, std::max(0.f, b));
color.w = 1.f; // default value used in the shader so I simplified it here,
// it can be overriden by the 2nd mocv chunk
}
}
bool WMOGroup::is_visible( glm::mat4x4 const& transform
, math::frustum const& frustum
, float const& cull_distance
, glm::vec3 const& camera
, display_mode display
) const
{
glm::vec3 pos = transform * glm::vec4(center, 0);
if (!frustum.intersects(pos + BoundingBoxMin, pos + BoundingBoxMax))
{
return false;
}
float dist = display == display_mode::in_3D
? glm::distance(pos, camera) - rad
: std::abs(pos.y - camera.y) - rad;
return (dist < cull_distance);
}
void WMOGroup::intersect (math::ray const& ray, std::vector<float>* results) const
{
if (!ray.intersect_bounds (VertexBoxMin, VertexBoxMax))
{
return;
}
//! \todo Also allow clicking on doodads and liquids.
for (auto&& batch : _batches)
{
for (size_t i (batch.index_start); i < batch.index_start + batch.index_count; i += 3)
{
if ( auto&& distance
= ray.intersect_triangle ( _vertices[_indices[i + 0]]
, _vertices[_indices[i + 1]]
, _vertices[_indices[i + 2]]
)
)
{
results->emplace_back (*distance);
}
}
}
}
/*
void WMOGroup::drawLiquid ( glm::mat4x4 const& transform
, liquid_render& render
, bool // draw_fog
, int animtime
)
{
// draw liquid
//! \todo culling for liquid boundingbox or something
if (lq)
{
gl.enable(GL_BLEND);
gl.depthMask(GL_TRUE);
lq->draw ( transform, render, animtime);
gl.disable(GL_BLEND);
}
}
*/
void WMOGroup::setupFog (bool draw_fog, std::function<void (bool)> setup_fog)
{
if (use_outdoor_lights || fog == -1) {
setup_fog (draw_fog);
}
else {
wmo->fogs[fog].setup();
}
}
void WMOFog::init(BlizzardArchive::ClientFile* f)
{
f->read(this, 0x30);
color = glm::vec4(((color1 & 0x00FF0000) >> 16) / 255.0f, ((color1 & 0x0000FF00) >> 8) / 255.0f,
(color1 & 0x000000FF) / 255.0f, ((color1 & 0xFF000000) >> 24) / 255.0f);
float temp;
temp = pos.y;
pos.y = pos.z;
pos.z = -temp;
fogstart = fogstart * fogend * 1.5f;
fogend *= 1.5;
}
void WMOFog::setup()
{
}
decltype (WMOManager::_) WMOManager::_;
void WMOManager::report()
{
std::string output = "Still in the WMO manager:\n";
_.apply ( [&] (BlizzardArchive::Listfile::FileKey const& key, WMO const&)
{
output += " - " + key.stringRepr() + "\n";
}
);
LogDebug << output;
}
void WMOManager::clear_hidden_wmos()
{
_.apply ( [&] (BlizzardArchive::Listfile::FileKey const&, WMO& wmo)
{
wmo.show();
}
);
}
void WMOManager::unload_all(Noggit::NoggitRenderContext context)
{
_.context_aware_apply(
[&] (BlizzardArchive::Listfile::FileKey const&, WMO& wmo)
{
wmo.renderer()->unload();
}
, context
);
}