Files
noggit-red/src/noggit/WMO.h

394 lines
9.9 KiB
C++

// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#pragma once
#include <math/quaternion.hpp>
#include <math/ray.hpp>
#include <math/vector_3d.hpp>
#include <noggit/MPQ.h>
#include <noggit/ModelInstance.h> // ModelInstance
#include <noggit/ModelManager.h>
#include <noggit/multimap_with_normalized_key.hpp>
#include <noggit/TextureManager.h>
#include <noggit/tool_enums.hpp>
#include <noggit/wmo_liquid.hpp>
#include <opengl/primitives.hpp>
#include <boost/optional.hpp>
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
class WMO;
class WMOGroup;
class WMOInstance;
class WMOManager;
class wmo_liquid;
class Model;
struct wmo_batch
{
int8_t unused[12];
uint32_t index_start;
uint16_t index_count;
uint16_t vertex_start;
uint16_t vertex_end;
uint8_t flags;
uint8_t texture;
};
union wmo_group_flags
{
uint32_t value;
struct
{
uint32_t has_bsp_tree : 1; // 0x1
uint32_t has_light_map : 1; // 0x2
uint32_t has_vertex_color : 1; // 0x4
uint32_t exterior : 1; // 0x8
uint32_t flag_0x10 : 1;
uint32_t flag_0x20 : 1;
uint32_t exterior_lit : 1; // 0x40
uint32_t unreacheable : 1; // 0x80
uint32_t flag_0x100: 1;
uint32_t has_light : 1; // 0x200
uint32_t flag_0x400 : 1;
uint32_t has_doodads : 1; // 0x800
uint32_t has_water : 1; // 0x1000
uint32_t indoor : 1; // 0x2000
uint32_t flag_0x4000 : 1;
uint32_t flag_0x8000 : 1;
uint32_t always_draw : 1; // 0x10000
uint32_t has_mori_morb : 1; // 0x20000, cata+ only (?)
uint32_t skybox : 1; // 0x40000
uint32_t ocean : 1; // 0x80000
uint32_t flag_0x100000 : 1;
uint32_t mount_allowed : 1; // 0x200000
uint32_t flag_0x400000 : 1;
uint32_t flag_0x800000 : 1;
uint32_t use_mocv2_for_texture_blending : 1; // 0x1000000
uint32_t has_two_motv : 1; // 0x2000000
uint32_t antiportal : 1; // 0x4000000
uint32_t unk : 1; // 0x8000000 requires intBatchCount == 0, extBatchCount == 0, UNREACHABLE.
uint32_t unused : 4;
};
};
static_assert ( sizeof (wmo_group_flags) == sizeof (std::uint32_t)
, "bitfields shall be implemented packed"
);
struct wmo_group_header
{
uint32_t group_name; // offset into MOGN
uint32_t descriptive_group_name; // offset into MOGN
wmo_group_flags flags;
float box1[3];
float box2[3];
uint16_t portal_start;
uint16_t portal_count;
uint16_t transparency_batches_count;
uint16_t interior_batch_count;
uint16_t exterior_batch_count;
uint16_t padding_or_batch_type_d; // probably padding, but might be data?
uint8_t fogs[4];
uint32_t group_liquid; // used for MLIQ
uint32_t id;
int32_t unk2, unk3;
};
class WMOGroup
{
public:
WMOGroup(WMO *wmo, MPQFile* 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 math::vector_3d& camera
, bool draw_fog
, bool world_has_skies
);
void drawLiquid ( math::matrix_4x4 const& transform
, liquid_render& render
, bool draw_fog
, int animtime
);
void setupFog (bool draw_fog, std::function<void (bool)> setup_fog);
void intersect (math::ray const&, std::vector<float>* results) const;
// todo: portal culling
bool is_visible( math::matrix_4x4 const& transform_matrix
, math::frustum const& frustum
, float const& cull_distance
, math::vector_3d const& camera
, display_mode display
) const;
std::vector<uint16_t> doodad_ref() const { return _doodad_ref; }
math::vector_3d BoundingBoxMin;
math::vector_3d BoundingBoxMax;
math::vector_3d VertexBoxMin;
math::vector_3d VertexBoxMax;
bool use_outdoor_lights;
std::string name;
bool has_skybox() const { return header.flags.skybox; }
private:
void load_mocv(MPQFile& f, uint32_t size);
void fix_vertex_color_alpha();
WMO *wmo;
wmo_group_header header;
::math::vector_3d center;
float rad;
int32_t num;
int32_t fog;
std::vector<uint16_t> _doodad_ref;
std::unique_ptr<wmo_liquid> lq;
std::vector<wmo_batch> _batches;
std::vector<::math::vector_3d> _vertices;
std::vector<::math::vector_3d> _normals;
std::vector<::math::vector_2d> _texcoords;
std::vector<::math::vector_2d> _texcoords_2;
std::vector<::math::vector_4d> _vertex_colors;
std::vector<uint16_t> _indices;
opengl::scoped::deferred_upload_vertex_arrays<1> _vertex_array;
GLuint const& _vao = _vertex_array[0];
opengl::scoped::deferred_upload_buffers<6> _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];
bool _uploaded = false;
bool _vao_is_setup = false;
void upload();
void setup_vao(opengl::scoped::use_program& wmo_shader);
};
struct WMOLight {
uint32_t flags, color;
math::vector_3d pos;
float intensity;
float unk[5];
float r;
math::vector_4d fcolor;
void init(MPQFile* f);
void setup(GLint light);
static void setupOnce(GLint light, math::vector_3d dir, math::vector_3d light_color);
};
struct WMOPV {
math::vector_3d a, b, c, d;
};
struct WMOPR {
int16_t portal, group, dir, reserved;
};
struct WMODoodadSet {
char name[0x14];
int32_t start;
int32_t size;
int32_t unused;
};
struct WMOFog {
unsigned int flags;
math::vector_3d pos;
float r1, r2, fogend, fogstart;
unsigned int color1;
float f2;
float f3;
unsigned int color2;
// read to here (0x30 bytes)
math::vector_4d color;
void init(MPQFile* f);
void setup();
};
union mohd_flags
{
std::uint16_t flags;
struct
{
std::uint16_t do_not_attenuate_vertices_based_on_distance_to_portal : 1;
std::uint16_t use_unified_render_path : 1;
std::uint16_t use_liquid_type_dbc_id : 1;
std::uint16_t do_not_fix_vertex_color_alpha : 1;
std::uint16_t unused : 12;
};
};
static_assert ( sizeof (mohd_flags) == sizeof (std::uint16_t)
, "bitfields shall be implemented packed"
);
class WMO : public AsyncObject
{
public:
explicit WMO(const std::string& name);
void draw ( opengl::scoped::use_program& wmo_shader
, math::matrix_4x4 const& model_view
, math::matrix_4x4 const& projection
, math::matrix_4x4 const& transform_matrix
, math::matrix_4x4 const& transform_matrix_transposed
, bool boundingbox
, math::frustum const& frustum
, const float& cull_distance
, const math::vector_3d& camera
, bool draw_doodads
, bool draw_fog
, liquid_render& render
, int animtime
, bool world_has_skies
, display_mode display
);
bool draw_skybox( math::matrix_4x4 const& model_view
, math::vector_3d const& camera_pos
, opengl::scoped::use_program& m2_shader
, math::frustum const& frustum
, const float& cull_distance
, int animtime
, bool draw_particles
, math::vector_3d aabb_min
, math::vector_3d aabb_max
, std::map<int, std::pair<math::vector_3d, math::vector_3d>> const& group_extents
) const;
std::vector<float> intersect (math::ray const&) const;
void finishLoading();
std::map<uint32_t, std::vector<wmo_doodad_instance>> doodads_per_group(uint16_t doodadset) const;
bool draw_group_boundingboxes;
bool _finished_upload;
std::vector<WMOGroup> groups;
std::vector<WMOMaterial> materials;
math::vector_3d extents[2];
std::vector<scoped_blp_texture_reference> textures;
std::vector<std::string> models;
std::vector<wmo_doodad_instance> modelis;
std::vector<math::vector_3d> model_nearest_light_vector;
std::vector<WMOLight> lights;
math::vector_4d ambient_light_color;
mohd_flags flags;
std::vector<WMOFog> fogs;
std::vector<WMODoodadSet> doodadsets;
boost::optional<scoped_model_reference> skybox;
bool is_hidden() const { return _hidden; }
void toggle_visibility() { _hidden = !_hidden; }
void show() { _hidden = false ; }
virtual bool is_required_when_saving() const
{
return true;
}
private:
bool _hidden = false;
};
class WMOManager
{
public:
static void report();
static void clear_hidden_wmos();
private:
friend struct scoped_wmo_reference;
static noggit::async_object_multimap_with_normalized_key<WMO> _;
};
struct scoped_wmo_reference
{
scoped_wmo_reference (std::string const& filename)
: _valid (true)
, _filename (filename)
, _wmo (WMOManager::_.emplace (_filename))
{}
scoped_wmo_reference (scoped_wmo_reference const& other)
: _valid (other._valid)
, _filename (other._filename)
, _wmo (WMOManager::_.emplace (_filename))
{}
scoped_wmo_reference& operator= (scoped_wmo_reference const& other)
{
_valid = other._valid;
_filename = other._filename;
_wmo = WMOManager::_.emplace (_filename);
return *this;
}
scoped_wmo_reference (scoped_wmo_reference&& other)
: _valid (other._valid)
, _filename (other._filename)
, _wmo (other._wmo)
{
other._valid = false;
}
scoped_wmo_reference& operator= (scoped_wmo_reference&& other)
{
std::swap(_valid, other._valid);
std::swap(_filename, other._filename);
std::swap(_wmo, other._wmo);
other._valid = false;
return *this;
}
~scoped_wmo_reference()
{
if (_valid)
{
WMOManager::_.erase (_filename);
}
}
WMO* operator->() const
{
return _wmo;
}
WMO* get() const
{
return _wmo;
}
private:
bool _valid;
std::string _filename;
WMO* _wmo;
};