old local changes, mainly low-res wdl WMO support and some optimizations

This commit is contained in:
T1ti
2025-06-08 23:49:22 +02:00
parent 4bd344a0fb
commit 40d513fcc2
26 changed files with 795 additions and 157 deletions

View File

@@ -13,18 +13,40 @@ namespace math
float tmin (std::numeric_limits<float>::lowest()); float tmin (std::numeric_limits<float>::lowest());
float tmax (std::numeric_limits<float>::max()); float tmax (std::numeric_limits<float>::max());
auto calculate_tmin_tmax = [](float origin, float direction, float min, float max, float& tmin, float& tmax) { if (_direction.x != 0.0f)
if (direction != 0.0f) { {
float t1 = (min - origin) / direction; float tx1 = (min.x - _origin.x) * _inverted_direction.x;
float t2 = (max - origin) / direction; float tx2 = (max.x - _origin.x) * _inverted_direction.x;
tmin = std::max(tmin, std::min(t1, t2)); // float const tx1((min.x - _origin.x) / _direction.x);
tmax = std::min(tmax, std::max(t1, t2)); // float const tx2((max.x - _origin.x) / _direction.x);
}
};
calculate_tmin_tmax(_origin.x, _direction.x, min.x, max.x, tmin, tmax); tmin = std::max(tmin, std::min(tx1, tx2));
calculate_tmin_tmax(_origin.y, _direction.y, min.y, max.y, tmin, tmax); tmax = std::min(tmax, std::max(tx1, tx2));
calculate_tmin_tmax(_origin.z, _direction.z, min.z, max.z, tmin, tmax); }
if (_direction.y != 0.0f)
{
// float const ty1((min.y - _origin.y) / _direction.y);
// float const ty2((max.y - _origin.y) / _direction.y);
float ty1 = (min.y - _origin.y) * _inverted_direction.y;
float ty2 = (max.y - _origin.y) * _inverted_direction.y;
tmin = std::max(tmin, std::min(ty1, ty2));
tmax = std::min(tmax, std::max(ty1, ty2));
}
if (_direction.z != 0.0f)
{
// float const tz1((min.z - _origin.z) / _direction.z);
// float const tz2((max.z - _origin.z) / _direction.z);
float tz1 = (min.z - _origin.z) * _inverted_direction.z;
float tz2 = (max.z - _origin.z) * _inverted_direction.z;
tmin = std::max(tmin, std::min(tz1, tz2));
tmax = std::min(tmax, std::max(tz1, tz2));
}
if (tmax >= tmin) if (tmax >= tmin)
{ {

View File

@@ -16,6 +16,10 @@ namespace math
{ {
assert(false); assert(false);
} }
// pre compute ivnerted direction
_inverted_direction.x = (_direction.x != 0.0f) ? 1.0f / _direction.x : std::numeric_limits<float>::max();
_inverted_direction.y = (_direction.y != 0.0f) ? 1.0f / _direction.y : std::numeric_limits<float>::max();
_inverted_direction.z = (_direction.z != 0.0f) ? 1.0f / _direction.z : std::numeric_limits<float>::max();
} }
ray (glm::mat4x4 const& transform, ray const& other) ray (glm::mat4x4 const& transform, ray const& other)
@@ -35,8 +39,14 @@ namespace math
return _origin + _direction * distance; return _origin + _direction * distance;
} }
glm::vec3 const origin() const
{
return _origin;
}
private: private:
glm::vec3 _origin; glm::vec3 const _origin;
glm::vec3 _direction; glm::vec3 const _direction;
glm::vec3 _inverted_direction;
}; };
} }

View File

@@ -89,11 +89,12 @@ void AsyncLoader::process()
_state_changed.notify_all(); _state_changed.notify_all();
} }
} }
catch (BlizzardArchive::Exceptions::FileReadFailedError const&) catch (BlizzardArchive::Exceptions::FileReadFailedError const& e)
{ {
std::lock_guard<std::mutex> const lock(_guard); std::lock_guard<std::mutex> const lock(_guard);
object->error_on_loading(); object->error_on_loading();
// LogError << e.what() << std::endl;
if (object->is_required_when_saving()) if (object->is_required_when_saving())
{ {
@@ -107,7 +108,8 @@ void AsyncLoader::process()
std::lock_guard<std::mutex> const lock(_guard); std::lock_guard<std::mutex> const lock(_guard);
object->error_on_loading(); object->error_on_loading();
LogError << "Caught unknown error." << std::endl; std::string const reason{ util::exception_to_string(std::current_exception()) };
LogError << "Caught unknown error: " << reason << std::endl;
if (object->is_required_when_saving()) if (object->is_required_when_saving())
{ {

View File

@@ -78,7 +78,7 @@ public:
// MapChunkHeader header; // MapChunkHeader header;
// uint32_t nLayers = 0; // uint32_t nLayers = 0;
float xbase, ybase, zbase; // global coords float xbase, ybase, zbase; // global coords. height = ybase
mcnk_flags header_flags; mcnk_flags header_flags;
bool use_big_alphamap; bool use_big_alphamap;

View File

@@ -3311,6 +3311,10 @@ selection_result MapView::intersect_result(bool terrain_only)
, _draw_hidden_models.get() , _draw_hidden_models.get()
, _draw_wmo_exterior.get() , _draw_wmo_exterior.get()
, _draw_model_animations.get() , _draw_model_animations.get()
, false
, false
, 0.0f
, true // !_draw_wmo_exterior.get() // invert so that we only cast interiors if exterior is hidden
) )
); );

View File

@@ -226,19 +226,19 @@ bool Model::isAnimated(const BlizzardArchive::ClientFile& f, ModelHeader& header
} }
glm::vec3 fixCoordSystem(glm::vec3 v) inline glm::vec3 fixCoordSystem(glm::vec3 v)
{ {
return glm::vec3(v.x, v.z, -v.y); return glm::vec3(v.x, v.z, -v.y);
} }
namespace namespace
{ {
glm::vec3 fixCoordSystem2(glm::vec3 v) inline glm::vec3 fixCoordSystem2(glm::vec3 v)
{ {
return glm::vec3(v.x, v.z, v.y); return glm::vec3(v.x, v.z, v.y);
} }
glm::quat fixCoordSystemQuat(glm::quat v) inline glm::quat fixCoordSystemQuat(glm::quat v)
{ {
return glm::quat(-v.x, -v.z, v.y, v.w); return glm::quat(-v.x, -v.z, v.y, v.w);
} }
@@ -895,7 +895,13 @@ void Bone::calcMatrix(glm::mat4x4 const& model_view
} }
std::vector<std::pair<float, std::tuple<int, int, int>>> Model::intersect (glm::mat4x4 const& model_view, math::ray const& ray, int animtime, bool calc_anims) std::vector<std::pair<float, std::tuple<int, int, int>>> Model::intersect (
glm::mat4x4 const& model_view
, math::ray const& ray
, int animtime
, bool calc_anims
, bool first_occurence
, bool only_opaque_tris)
{ {
std::vector<std::pair<float, std::tuple<int, int, int>>> results; std::vector<std::pair<float, std::tuple<int, int, int>>> results;
@@ -934,6 +940,12 @@ std::vector<std::pair<float, std::tuple<int, int, int>>> Model::intersect (glm::
bool const has_transformed_verts = !transformed_verts.empty(); bool const has_transformed_verts = !transformed_verts.empty();
for (auto const& pass : _renderer.renderPasses()) for (auto const& pass : _renderer.renderPasses())
{ {
// todo
if (only_opaque_tris && pass.blend_mode != 0)
{
}
for (int i (pass.index_start); i < pass.index_start + pass.index_count; i += 3) for (int i (pass.index_start); i < pass.index_start + pass.index_count; i += 3)
{ {
std::optional<float> distance; std::optional<float> distance;
@@ -950,13 +962,12 @@ std::vector<std::pair<float, std::tuple<int, int, int>>> Model::intersect (glm::
transformed_verts[_indices[static_cast<std::size_t>(i + 2)]].position); transformed_verts[_indices[static_cast<std::size_t>(i + 2)]].position);
} }
glm::vec3 debug_vert1 = _vertices[_indices[static_cast<std::size_t>(i + 0)]].position; ////
glm::vec3 debug_vert2 = _vertices[_indices[static_cast<std::size_t>(i + 1)]].position; ////
glm::vec3 debug_vert3 = _vertices[_indices[static_cast<std::size_t>(i + 2)]].position; ////
if (distance) if (distance)
{ {
results.emplace_back (*distance, std::make_tuple(i, i + 1, 1 + 2)); results.emplace_back (*distance, std::make_tuple(i, i + 1, 1 + 2));
if (first_occurence)
return results;
} }
} }
} }

View File

@@ -52,7 +52,7 @@ enum M2GlobalFlags
// TODO : MOP + // TODO : MOP +
}; };
glm::vec3 fixCoordSystem(glm::vec3 v); inline glm::vec3 fixCoordSystem(glm::vec3 v);
class Bone { class Bone {
Animation::M2Value<glm::vec3> trans; Animation::M2Value<glm::vec3> trans;
@@ -159,7 +159,13 @@ public:
Model(const std::string& name, Noggit::NoggitRenderContext context ); Model(const std::string& name, Noggit::NoggitRenderContext context );
std::vector<std::pair<float, std::tuple<int, int, int>>> intersect (glm::mat4x4 const& model_view, math::ray const&, int animtime, bool calc_anims); std::vector<std::pair<float, std::tuple<int, int, int>>> intersect (
glm::mat4x4 const& model_view
, math::ray const&
, int animtime
, bool calc_anims
, bool first_occurence
, bool only_opaque_tris);
void updateEmitters(float dt); void updateEmitters(float dt);

View File

@@ -144,6 +144,8 @@ void ModelInstance::intersect (glm::mat4x4 const& model_view
, selection_result* results , selection_result* results
, int animtime , int animtime
, bool animate , bool animate
, bool first_occurence
, bool only_opaque_tris
) )
{ {
if (!finishedLoading() || model->loading_failed()) if (!finishedLoading() || model->loading_failed())
@@ -162,7 +164,7 @@ void ModelInstance::intersect (glm::mat4x4 const& model_view
return; return;
} }
for (auto&& result : model->intersect (model_view, subray, animtime, animate)) for (auto&& result : model->intersect (model_view, subray, animtime, animate, first_occurence, only_opaque_tris))
{ {
//! \todo why is only sc important? these are relative to subray, //! \todo why is only sc important? these are relative to subray,
//! so should be inverted by model_matrix? //! so should be inverted by model_matrix?

View File

@@ -22,9 +22,6 @@ class WMOInstance;
class ModelInstance : public SceneObject class ModelInstance : public SceneObject
{ {
public: public:
constexpr static float min_scale() { return 1.f / 1024.f; };
constexpr static float max_scale() { return static_cast<float>((1 << 16) - 1) / 1024.f; };
scoped_model_reference model; scoped_model_reference model;
glm::vec3 light_color = { 1.f, 1.f, 1.f }; glm::vec3 light_color = { 1.f, 1.f, 1.f };
@@ -89,6 +86,8 @@ public:
, selection_result* , selection_result*
, int animtime , int animtime
, bool animate , bool animate
, bool first_occurence
, bool only_opaque_tris
); );
bool isInFrustum(math::frustum const& frustum); bool isInFrustum(math::frustum const& frustum);

View File

@@ -30,6 +30,9 @@ class MapTile;
class SceneObject : public Selectable class SceneObject : public Selectable
{ {
public: public:
constexpr static float min_scale() { return 1.f / 1024.f; };
constexpr static float max_scale() { return static_cast<float>((1 << 16) - 1) / 1024.f; };
SceneObject(SceneObjectTypes type, Noggit::NoggitRenderContext context); SceneObject(SceneObjectTypes type, Noggit::NoggitRenderContext context);
[[nodiscard]] [[nodiscard]]
@@ -79,25 +82,25 @@ public:
glm::vec3 const getServerPos() { return glm::vec3(ZEROPOINT - pos.z, ZEROPOINT - pos.x, pos.y); } glm::vec3 const getServerPos() { return glm::vec3(ZEROPOINT - pos.z, ZEROPOINT - pos.x, pos.y); }
bool _grouped = false;
public: public:
glm::vec3 pos; glm::vec3 pos;
glm::vec3 dir; glm::vec3 dir;
float scale = 1.f; float scale = 1.f; // Note : max scale is uint16 max / 1024 = 63.999
unsigned int uid; unsigned int uid;
int frame; int frame;
// Note : First, need to check if the tile that contained it was rendered too // Note : First, need to check if the tile that contained it was rendered too
bool _rendered_last_frame = false; bool _rendered_last_frame = false;
bool _grouped = false;
protected: protected:
SceneObjectTypes _type; SceneObjectTypes _type;
glm::mat4x4 _transform_mat = glm::mat4x4(); glm::mat4x4 _transform_mat = glm::mat4x4();
glm::mat4x4 _transform_mat_inverted = glm::mat4x4(); glm::mat4x4 _transform_mat_inverted = glm::mat4x4();
std::array<glm::vec3, 2> extents; // axis aligned bounding box mni and max corners std::array<glm::vec3, 2> extents; // axis aligned bounding box min and max corners
float bounding_radius; float bounding_radius;
Noggit::NoggitRenderContext _context; Noggit::NoggitRenderContext _context;

View File

@@ -355,7 +355,7 @@ void WMO::waitForChildrenLoaded()
} }
} }
std::vector<float> WMO::intersect (math::ray const& ray, bool do_exterior) const std::vector<float> WMO::intersect (math::ray const& ray, bool do_exterior, bool do_interior, bool first_occurence) const
{ {
std::vector<float> results; std::vector<float> results;
@@ -367,14 +367,17 @@ std::vector<float> WMO::intersect (math::ray const& ray, bool do_exterior) const
for (auto& group : groups) for (auto& group : groups)
{ {
if (!do_exterior && !group.is_indoor()) if (!do_exterior && !group.is_indoor())
continue; continue;
group.intersect (ray, &results); else if (!do_interior && group.is_indoor())
continue;
group.intersect (ray, &results, first_occurence);
} }
if (!do_exterior && results.size()) if (!do_exterior && results.size())
{ {
// dirty way to find the furthest face and ignore invisible faces, cleaner way would be to do a direction check on faces // dirty way to find the furthest face and ignore back culled(invisible) faces, cleaner way would be to do a direction check on faces
// float max = *std::max_element(std::begin(results), std::end(results)); // float max = *std::max_element(std::begin(results), std::end(results));
// results.clear(); // results.clear();
// results.push_back(max); // results.push_back(max);
@@ -1116,7 +1119,7 @@ bool WMOGroup::is_visible( glm::mat4x4 const& transform
} }
void WMOGroup::intersect (math::ray const& ray, std::vector<float>* results) const void WMOGroup::intersect (math::ray const& ray, std::vector<float>* results, bool first_occurence) const
{ {
if (!ray.intersect_bounds (VertexBoxMin, VertexBoxMax)) if (!ray.intersect_bounds (VertexBoxMin, VertexBoxMax))
{ {
@@ -1126,7 +1129,7 @@ void WMOGroup::intersect (math::ray const& ray, std::vector<float>* results) con
//! \todo Also allow clicking on doodads and liquids. //! \todo Also allow clicking on doodads and liquids.
for (auto&& batch : _batches) for (auto&& batch : _batches)
{ {
for (size_t i (batch.index_start); i < batch.index_start + batch.index_count; i += 3) for (int i (batch.index_start); i < batch.index_start + batch.index_count; i += 3)
{ {
// TODO : only intersect visible triangles // TODO : only intersect visible triangles
// TODO : option to only check collision // TODO : option to only check collision
@@ -1138,6 +1141,8 @@ void WMOGroup::intersect (math::ray const& ray, std::vector<float>* results) con
) )
{ {
results->emplace_back (*distance); results->emplace_back (*distance);
if (first_occurence)
return;
} }
} }
} }

View File

@@ -182,7 +182,7 @@ public:
void setupFog (bool draw_fog, std::function<void (bool)> setup_fog); void setupFog (bool draw_fog, std::function<void (bool)> setup_fog);
void intersect (math::ray const&, std::vector<float>* results) const; void intersect (math::ray const&, std::vector<float>* results, bool first_occurence) const;
// todo: portal culling // todo: portal culling
[[nodiscard]] [[nodiscard]]
@@ -310,7 +310,7 @@ public:
explicit WMO(BlizzardArchive::Listfile::FileKey const& file_key, Noggit::NoggitRenderContext context ); explicit WMO(BlizzardArchive::Listfile::FileKey const& file_key, Noggit::NoggitRenderContext context );
[[nodiscard]] [[nodiscard]]
std::vector<float> intersect (math::ray const&, bool do_exterior = true) const; std::vector<float> intersect (math::ray const&, bool do_exterior = true, bool do_interior = true, bool first_occurence = false) const;
void finishLoading() override; void finishLoading() override;

View File

@@ -37,8 +37,8 @@ WMOInstance::WMOInstance(BlizzardArchive::Listfile::FileKey const& file_key, ENT
scale = 1.0f; scale = 1.0f;
} }
extents[0] = glm::vec3(d->extents[0][0], d->extents[0][1], d->extents[0][2]); extents[0] = d->extents[0];
extents[1] = glm::vec3(d->extents[1][0], d->extents[1][1], d->extents[1][2]); extents[1] = d->extents[1];
_need_recalc_extents = true; _need_recalc_extents = true;
updateTransformMatrix(); updateTransformMatrix();
@@ -80,6 +80,7 @@ void WMOInstance::draw ( OpenGL::Scoped::use_program& wmo_shader
, bool draw_exterior , bool draw_exterior
, bool render_selection_aabb , bool render_selection_aabb
, bool render_group_bounds , bool render_group_bounds
, bool /*render_lowres*/
) )
{ {
if (!wmo->finishedLoading() || wmo->loading_failed()) if (!wmo->finishedLoading() || wmo->loading_failed())
@@ -113,23 +114,50 @@ void WMOInstance::draw ( OpenGL::Scoped::use_program& wmo_shader
wmo_shader.uniform("transform", _transform_mat); wmo_shader.uniform("transform", _transform_mat);
wmo->renderer()->draw( wmo_shader // render WDL model
, model_view [[unlikely]]
, projection if (render_low_res && lowResWmo.has_value()
, _transform_mat && lowResWmo.value()->get()->finishedLoading() && !lowResWmo.value()->get()->loading_failed())
, is_selected || _grouped {
, frustum lowResWmo.value()->get()->renderer()->draw(wmo_shader
, cull_distance , model_view
, camera , projection
, draw_doodads , _transform_mat
, draw_fog , is_selected || _grouped
, animtime , frustum
, world_has_skies , cull_distance
, display , camera
, !draw_exterior , draw_doodads
, render_group_bounds , draw_fog
, _grouped , animtime
); , world_has_skies
, display
, !draw_exterior
, render_group_bounds
, _grouped
);
}
else // regular model
{
wmo->renderer()->draw( wmo_shader
, model_view
, projection
, _transform_mat
, is_selected || _grouped
, frustum
, cull_distance
, camera
, draw_doodads
, draw_fog
, animtime
, world_has_skies
, display
, !draw_exterior
, render_group_bounds
, _grouped
);
}
} }
// axis aligned bounding box (extents) // axis aligned bounding box (extents)
@@ -150,7 +178,7 @@ void WMOInstance::draw ( OpenGL::Scoped::use_program& wmo_shader
} }
} }
void WMOInstance::intersect (math::ray const& ray, selection_result* results, bool do_exterior) void WMOInstance::intersect (math::ray const& ray, selection_result* results, bool do_exterior, bool do_interior, bool first_occurence)
{ {
if (!finishedLoading() || wmo->loading_failed()) if (!finishedLoading() || wmo->loading_failed())
return; return;
@@ -164,7 +192,7 @@ void WMOInstance::intersect (math::ray const& ray, selection_result* results, bo
math::ray subray(_transform_mat_inverted, ray); math::ray subray(_transform_mat_inverted, ray);
for (auto&& result : wmo->intersect(subray, do_exterior)) for (auto&& result : wmo->intersect(subray, do_exterior, do_interior, first_occurence))
{ {
results->emplace_back (result, this); results->emplace_back (result, this);
} }
@@ -303,6 +331,24 @@ void WMOInstance::recalcExtents()
bounding_radius = glm::distance(wmo->extents[1], wmo->extents[0]) * scale / 2.0f; bounding_radius = glm::distance(wmo->extents[1], wmo->extents[0]) * scale / 2.0f;
// Update wdl if needed
[[unlikely]]
if (lowResWmo.has_value())
{
lowResInstance->pos[0] = pos.x;
lowResInstance->pos[1] = pos.y;
lowResInstance->pos[2] = pos.z;
lowResInstance->rot[0] = dir.x;
lowResInstance->rot[1] = dir.y;
lowResInstance->rot[2] = dir.z;
lowResInstance->scale = scale * 1024.0f;
lowResInstance->extents[0] = extents[0];
lowResInstance->extents[1] = extents[1];
}
_need_recalc_extents = false; _need_recalc_extents = false;
} }

View File

@@ -18,7 +18,14 @@ public:
uint16_t mFlags; uint16_t mFlags;
uint16_t mNameset; uint16_t mNameset;
uint16_t doodadset() const { return _doodadset; } // TODO figure out a better structure for this
// bool hasLowResModel = false;
std::optional<scoped_wmo_reference*> lowResWmo;
ENTRY_MODF* lowResInstance = nullptr; // assume this is never nullptr when lowResWmo has a value
bool render_low_res = false;
uint16_t doodadset() const { return _doodadset; };
void change_doodadset(uint16_t doodad_set); void change_doodadset(uint16_t doodad_set);
[[nodiscard]] [[nodiscard]]
@@ -51,7 +58,12 @@ public:
, _doodadset (other._doodadset) , _doodadset (other._doodadset)
, _doodads_per_group(other._doodads_per_group) , _doodads_per_group(other._doodads_per_group)
, _need_doodadset_update(other._need_doodadset_update) , _need_doodadset_update(other._need_doodadset_update)
, _update_group_extents(other._update_group_extents)
, _need_recalc_extents(other._need_recalc_extents) , _need_recalc_extents(other._need_recalc_extents)
// , hasLowResModel(other.hasLowResModel)
, render_low_res(other.render_low_res)
, lowResInstance(other.lowResInstance)
, lowResWmo(other.lowResWmo)
{ {
std::swap (extents, other.extents); std::swap (extents, other.extents);
pos = other.pos; pos = other.pos;
@@ -82,6 +94,11 @@ public:
std::swap(_transform_mat_inverted, other._transform_mat_inverted); std::swap(_transform_mat_inverted, other._transform_mat_inverted);
std::swap(_context, other._context); std::swap(_context, other._context);
std::swap(_need_recalc_extents, other._need_recalc_extents); std::swap(_need_recalc_extents, other._need_recalc_extents);
std::swap(_update_group_extents, other._update_group_extents);
// std::swap(hasLowResModel, other.hasLowResModel);
std::swap(render_low_res, other.render_low_res);
std::swap(lowResInstance, other.lowResInstance);
std::swap(lowResWmo, other.lowResWmo);
return *this; return *this;
} }
@@ -102,9 +119,10 @@ public:
, bool draw_exterior , bool draw_exterior
, bool render_selection_aabb , bool render_selection_aabb
, bool render_group_bounds , bool render_group_bounds
, bool render_low_res
); );
void intersect (math::ray const&, selection_result*, bool do_exterior = true); void intersect (math::ray const&, selection_result*, bool do_exterior = true, bool do_interior = true, bool first_occurence = false);
std::array<glm::vec3, 2> const& getExtents() override; // axis aligned std::array<glm::vec3, 2> const& getExtents() override; // axis aligned
std::array<glm::vec3, 2> const& getLocalExtents() const; std::array<glm::vec3, 2> const& getLocalExtents() const;

View File

@@ -108,7 +108,7 @@ World::World(const std::string& name, int map_id, Noggit::NoggitRenderContext co
, _model_instance_storage(this) , _model_instance_storage(this)
, _tile_update_queue(this) , _tile_update_queue(this)
, mapIndex(name, map_id, this, context, create_empty) , mapIndex(name, map_id, this, context, create_empty)
, horizon(name, &mapIndex) , horizon(name, this)
, mWmoFilename(mapIndex.globalWMOName) , mWmoFilename(mapIndex.globalWMOName)
, mWmoEntry(mapIndex.wmoEntry) , mWmoEntry(mapIndex.wmoEntry)
, animtime(0) , animtime(0)
@@ -120,6 +120,17 @@ World::World(const std::string& name, int map_id, Noggit::NoggitRenderContext co
{ {
LogDebug << "Loading world \"" << name << "\"." << std::endl; LogDebug << "Loading world \"" << name << "\"." << std::endl;
_loaded_tiles_buffer[0] = std::make_pair<std::pair<int, int>, MapTile*>(std::make_pair(0, 0), nullptr); _loaded_tiles_buffer[0] = std::make_pair<std::pair<int, int>, MapTile*>(std::make_pair(0, 0), nullptr);
// initialize wdl models here
if (horizon.wmos.size() < horizon.lWMOInstances.size())
{
for (int i = 0; i < horizon.mWMOFilenames.size(); ++i)
{
// auto instance = horizon.lWMOInstances[i];
auto& filepath = horizon.mWMOFilenames[i];
horizon.wmos.push_back(scoped_wmo_reference(filepath, _context));
}
}
} }
void World::LoadSavedSelectionGroups() void World::LoadSavedSelectionGroups()
@@ -778,6 +789,8 @@ void World::scale_selected_models(float v, object_scaling_type type)
if (!_selected_model_count) if (!_selected_model_count)
return; return;
v = std::clamp(v, SceneObject::min_scale(), SceneObject::max_scale());
bool modern_features = Noggit::Application::NoggitApplication::instance()->getConfiguration()->modern_features; bool modern_features = Noggit::Application::NoggitApplication::instance()->getConfiguration()->modern_features;
for (auto& entry : _current_selection) for (auto& entry : _current_selection)
@@ -1131,14 +1144,18 @@ bool World::isInIndoorWmoGroup(std::array<glm::vec3, 2> obj_bounds, glm::mat4x4
selection_result World::intersect (glm::mat4x4 const& model_view selection_result World::intersect (glm::mat4x4 const& model_view
, math::ray const& ray , math::ray const& ray
, bool pOnlyMap , const bool pOnlyMap
, bool do_objects , const bool do_objects
, bool draw_terrain , const bool draw_terrain
, bool draw_wmo , const bool draw_wmo
, bool draw_models , const bool draw_models
, bool draw_hidden_models , const bool draw_hidden_models
, bool draw_wmo_exterior , const bool draw_wmo_exterior
, bool animate , const bool animate
, const bool first_object_occurence
, const bool opaque_only_tris
, const float obj_distance_max
, const bool do_wmo_interiors
) )
{ {
ZoneScopedN("World::intersect()"); ZoneScopedN("World::intersect()");
@@ -1172,28 +1189,82 @@ selection_result World::intersect (glm::mat4x4 const& model_view
if (!pOnlyMap && do_objects) if (!pOnlyMap && do_objects)
{ {
if (draw_models) if (!draw_models && !draw_wmo)
{ return std::move(results);
ZoneScopedN("World::intersect() : intersect M2s");
_model_instance_storage.for_each_m2_instance([&] (ModelInstance& model_instance)
{
if (draw_hidden_models || !model_instance.model->is_hidden())
{
model_instance.intersect(model_view, ray, &results, animtime, animate);
}
});
}
if (draw_wmo) ////////////// Optimized version, can iterate the same objects multiple times if they are on borders though
// store in a set container to avoid duplicates, this is pretty slow, doing a few extra rays is much faster if duplicates aren't a problem
// std::unordered_set<ModelInstance*> modelInstances;
// std::unordered_set<WMOInstance*> wmoInstances;
for (auto& pair : _loaded_tiles_buffer)
{ {
ZoneScopedN("World::intersect() : intersect WMOs"); MapTile* tile = pair.second;
_model_instance_storage.for_each_wmo_instance([&] (WMOInstance& wmo_instance)
if (!tile)
break;
TileIndex index{ static_cast<std::size_t>(pair.first.first)
, static_cast<std::size_t>(pair.first.second) };
// add some distance check ?
// if (tile-> > )
// continue;
if (!mapIndex.tileLoaded(index) || mapIndex.tileAwaitingLoading(index))
continue;
if (!tile->finishedLoading())
continue;
tile->recalcCombinedExtents();
if (!ray.intersect_bounds(tile->getCombinedExtents()[0], tile->getCombinedExtents()[1]))
{ {
if (draw_hidden_models || !wmo_instance.wmo->is_hidden()) continue;
}
for (auto& pair : tile->getObjectInstances())
{
if (pair.second[0]->which() == eMODEL && draw_models)
{ {
wmo_instance.intersect(ray, &results, draw_wmo_exterior);
for (auto& instance : pair.second)
{
auto model_instance = static_cast<ModelInstance*>(instance);
if (obj_distance_max != 0.0f)
{
const float distance = glm::distance(ray.origin(), instance->pos);
if ((distance - instance->getBoundingRadius()) > obj_distance_max)
continue;
}
if (draw_hidden_models || !model_instance->model->is_hidden())
// modelInstances.insert(model_instance);
model_instance->intersect(model_view, ray, &results, animtime, animate, first_object_occurence, opaque_only_tris);
}
} }
}); else if (pair.second[0]->which() == eWMO && draw_wmo)
{
for (auto& instance : pair.second)
{
auto wmo_instance = static_cast<WMOInstance*>(instance);
if (obj_distance_max != 0.0f)
{
const float distance = glm::distance(ray.origin(), instance->pos);
if ((distance - instance->getBoundingRadius()) > obj_distance_max)
continue;
}
if (draw_hidden_models || !wmo_instance->wmo->is_hidden())
// wmoInstances.insert(wmo_instance);
wmo_instance->intersect(ray, &results, draw_wmo_exterior, do_wmo_interiors, first_object_occurence);
}
}
}
} }
} }
@@ -2147,7 +2218,25 @@ std::uint32_t World::add_model_instance(ModelInstance model_instance, bool from_
std::uint32_t World::add_wmo_instance(WMOInstance wmo_instance, bool from_reloading, bool action) std::uint32_t World::add_wmo_instance(WMOInstance wmo_instance, bool from_reloading, bool action)
{ {
ZoneScoped; ZoneScoped;
// Check if WMO has a low resolution model
// also sets up all attributes currently
bool haslowres = horizon.wmoHasLowRes(&wmo_instance);
return _model_instance_storage.add_wmo_instance(std::move(wmo_instance), from_reloading, action); return _model_instance_storage.add_wmo_instance(std::move(wmo_instance), from_reloading, action);
// if (haslowres)
// {
// const auto obj = get_model(uid_after);
// assert(obj);
// if (obj)
// {
// WMOInstance* instance = static_cast<WMOInstance*>(std::get<selected_object_type>(obj.value()));
//
// int breakpoint = 0;
// }
// }
//
// return uid_after;
} }
std::optional<selection_type> World::get_model(std::uint32_t uid) std::optional<selection_type> World::get_model(std::uint32_t uid)
@@ -2695,6 +2784,8 @@ void World::clear_shadows(glm::vec3 const& pos)
}); });
} }
constexpr float HALFSHADOWSIZE = (TEXDETAILSIZE / 2.0f);
void World::swapTexture(glm::vec3 const& pos, scoped_blp_texture_reference tex) void World::swapTexture(glm::vec3 const& pos, scoped_blp_texture_reference tex)
{ {
ZoneScoped; ZoneScoped;
@@ -3735,6 +3826,7 @@ void World::select_objects_in_area(
glm::mat4 VPmatrix = projection * view; glm::mat4 VPmatrix = projection * view;
glm::mat4x4 const invertedProjViewMatrix = glm::inverse(VPmatrix); glm::mat4x4 const invertedProjViewMatrix = glm::inverse(VPmatrix);
auto const transposed_view = glm::transpose(view);
constexpr int max_position_raycast_processing = 10000; constexpr int max_position_raycast_processing = 10000;
constexpr int max_bounds_raycast_processing = 5000; // when selecting large amount of objects, avoid doing complex ray calculations to not freeze constexpr int max_bounds_raycast_processing = 5000; // when selecting large amount of objects, avoid doing complex ray calculations to not freeze
@@ -3864,7 +3956,7 @@ void World::select_objects_in_area(
// processed_obj_count++; // processed_obj_count++;
// 3: check if center point is occluded by terrain // 3: check if center point is occluded by terrain
if (processed_obj_count < max_position_raycast_processing && if (processed_obj_count < max_position_raycast_processing &&
!is_point_occluded_by_terrain(aabb_center, view, VPmatrix, viewport_width, viewport_height, camera_position)) !is_point_occluded_by_terrain(aabb_center, transposed_view, VPmatrix, viewport_width, viewport_height, camera_position))
{ {
// if not occluded success! select it and skip other checks // if not occluded success! select it and skip other checks
add_to_selection(instance, false, false); add_to_selection(instance, false, false);
@@ -3997,7 +4089,7 @@ void World::select_objects_in_area(
// 4.5 2nd raycast. Check if center of the intersection box is visible // 4.5 2nd raycast. Check if center of the intersection box is visible
// TODO : for WMOs this is way to generous due to their more complex shape, it would be better to iterate the bounding box of each group // TODO : for WMOs this is way to generous due to their more complex shape, it would be better to iterate the bounding box of each group
if (!is_point_occluded_by_terrain(intersectionCenter_pos, view, VPmatrix, viewport_width, viewport_height if (!is_point_occluded_by_terrain(intersectionCenter_pos, transposed_view, VPmatrix, viewport_width, viewport_height
, camera_position, (distance - instance->getBoundingRadius()))) , camera_position, (distance - instance->getBoundingRadius())))
{ {
// if not occluded success! select it and skip other checks // if not occluded success! select it and skip other checks
@@ -4061,7 +4153,7 @@ void World::select_objects_in_area(
continue; continue;
bool corner_occluded = is_point_occluded_by_terrain(corner bool corner_occluded = is_point_occluded_by_terrain(corner
, view , transposed_view
, VPmatrix , VPmatrix
, viewport_width , viewport_width
, viewport_height , viewport_height
@@ -4095,7 +4187,7 @@ void World::select_objects_in_area(
bool World::is_point_occluded_by_terrain(const glm::vec3& point, bool World::is_point_occluded_by_terrain(const glm::vec3& point,
const glm::mat4x4& view, const glm::mat4x4& transposed_view,
const glm::mat4& VPmatrix, const glm::mat4& VPmatrix,
float viewport_width, float viewport_width,
float viewport_height, float viewport_height,
@@ -4117,7 +4209,7 @@ bool World::is_point_occluded_by_terrain(const glm::vec3& point,
// intersect only terrain with a ray to object's position // intersect only terrain with a ray to object's position
selection_result terrain_intersect_results selection_result terrain_intersect_results
(intersect (intersect
(glm::transpose(view) (transposed_view
, ray , ray
, true , true
, false , false

View File

@@ -101,14 +101,18 @@ public:
selection_result intersect (glm::mat4x4 const& model_view selection_result intersect (glm::mat4x4 const& model_view
, math::ray const& , math::ray const&
, bool only_map , const bool only_map
, bool do_objects , const bool do_objects
, bool draw_terrain , const bool draw_terrain
, bool draw_wmo , const bool draw_wmo
, bool draw_models , const bool draw_models
, bool draw_hidden_models , const bool draw_hidden_models
, bool draw_wmo_exterior , const bool draw_wmo_exterior
, bool animate , const bool animate
, const bool first_object_occurence = false
, const bool opaque_only_tris = false
, const float obj_distance_max = 0.0f
, const bool do_wmo_interiors = true
); );
MapChunk* getChunkAt(glm::vec3 const& pos); MapChunk* getChunkAt(glm::vec3 const& pos);
@@ -143,6 +147,7 @@ public:
void remove_from_selection(std::uint32_t uid, bool skip_group = false, bool update_pivot = true); void remove_from_selection(std::uint32_t uid, bool skip_group = false, bool update_pivot = true);
void reset_selection(); void reset_selection();
void delete_selected_models(); void delete_selected_models();
// note : height is Y axis.
glm::vec3 get_ground_height(glm::vec3 pos); glm::vec3 get_ground_height(glm::vec3 pos);
void range_add_to_selection(glm::vec3 const& pos, float radius, bool remove); void range_add_to_selection(glm::vec3 const& pos, float radius, bool remove);
Noggit::world_model_instances_storage& getModelInstanceStorage() { return _model_instance_storage; }; Noggit::world_model_instances_storage& getModelInstanceStorage() { return _model_instance_storage; };
@@ -244,6 +249,7 @@ public:
void paintGroundEffectExclusion(glm::vec3 const& pos, float radius, bool exclusion); void paintGroundEffectExclusion(glm::vec3 const& pos, float radius, bool exclusion);
void setBaseTexture(glm::vec3 const& pos); void setBaseTexture(glm::vec3 const& pos);
void clear_shadows(glm::vec3 const& pos); void clear_shadows(glm::vec3 const& pos);
void bake_shadows(glm::vec3 const& pos, int mode, const glm::mat4x4& view);
void clearTextures(glm::vec3 const& pos); void clearTextures(glm::vec3 const& pos);
void swapTexture(glm::vec3 const& pos, scoped_blp_texture_reference tex); void swapTexture(glm::vec3 const& pos, scoped_blp_texture_reference tex);
void swapTextureGlobal(scoped_blp_texture_reference tex); void swapTextureGlobal(scoped_blp_texture_reference tex);

View File

@@ -9,6 +9,7 @@
#include <noggit/World.h> #include <noggit/World.h>
#include <opengl/context.hpp> #include <opengl/context.hpp>
#include <opengl/context.inl> #include <opengl/context.inl>
#include <noggit/Misc.h>
#include <sstream> #include <sstream>
#include <bitset> #include <bitset>
@@ -92,7 +93,7 @@ static inline uint32_t color_for_height (int16_t height)
namespace Noggit namespace Noggit
{ {
map_horizon::map_horizon(const std::string& basename, const MapIndex * const index) map_horizon::map_horizon(const std::string& basename, World * const world)
{ {
std::stringstream filename; std::stringstream filename;
filename << "World\\Maps\\" << basename << "\\" << basename << ".wdl"; filename << "World\\Maps\\" << basename << "\\" << basename << ".wdl";
@@ -126,10 +127,12 @@ map_horizon::map_horizon(const std::string& basename, const MapIndex * const ind
break; break;
} }
// todo: handle those too ?
case 'MWMO': case 'MWMO':
{ {
{ {
// TODO : use WMID instead for proper string parsing.
char const* lCurPos = reinterpret_cast<char const*>(wdl_file.getPointer()); char const* lCurPos = reinterpret_cast<char const*>(wdl_file.getPointer());
char const* lEnd = lCurPos + size; char const* lEnd = lCurPos + size;
@@ -143,21 +146,21 @@ map_horizon::map_horizon(const std::string& basename, const MapIndex * const ind
break; break;
} }
case 'MWID': case 'MWID':
wdl_file.seekRelative(size);
break;
// TODO // TODO
wdl_file.seekRelative(size); // jump to end of chunk
break;
case 'MODF': case 'MODF':
{ {
wdl_file.seekRelative(size); ENTRY_MODF const* modf_ptr = reinterpret_cast<ENTRY_MODF const*>(wdl_file.getPointer());
for (unsigned int i = 0; i < size / sizeof(ENTRY_MODF); ++i)
{
lWMOInstances.push_back(modf_ptr[i]);
if (lWMOInstances[i].scale == 0.0f)
lWMOInstances[i].scale = 1024.0f;
}
wdl_file.seekRelative(size); // jump to end of chunk
break; break;
// {
// ENTRY_MODF const* modf_ptr = reinterpret_cast<ENTRY_MODF const*>(wdl_file.getPointer());
// for (unsigned int i = 0; i < size / sizeof(ENTRY_MODF); ++i)
// {
// lWMOInstances.push_back(modf_ptr[i]);
// }
// }
// break;
} }
case 'MAOF': case 'MAOF':
{ {
@@ -212,9 +215,27 @@ map_horizon::map_horizon(const std::string& basename, const MapIndex * const ind
} }
} while (!done && !wdl_file.isEof()); } while (!done && !wdl_file.isEof());
constexpr bool _load_models = true;
if (_load_models)
{
// - Load WMOs -----------------------------------------
// Don't load them to storage, they share UIDs wth regular models
// for rendering in unloaded tiles
for (auto const& object : lWMOInstances)
{
// world->add_wmo_instance(WMOInstance(mWMOFilenames[object.nameID],
// &object, world->getRenderContext()), false, false);
// auto& filepath = mWMOFilenames[object.nameID];
// wmos.push_back(scoped_wmo_reference(filepath, world->getRenderContext()));
}
}
wdl_file.close(); wdl_file.close();
set_minimap(index); set_minimap(&world->mapIndex);
} }
void map_horizon::update_minimap_tile(int y, int x, bool has_data = false ) void map_horizon::update_minimap_tile(int y, int x, bool has_data = false )
@@ -378,25 +399,95 @@ void map_horizon::save_wdl(World* world, bool regenerate)
curPos += 8 + 0x4; curPos += 8 + 0x4;
// } // }
// WMO objects export code is copy pasta from MapTile
struct filenameOffsetThing
{
int nameID;
int filenamePosition;
};
filenameOffsetThing nullyThing = { 0, 0 };
std::map<std::string, filenameOffsetThing> lObjects;
// avoid duplicates, not really necessary here as we directly used MWMO string list
for (auto const& filename : mWMOFilenames)
{
if (lObjects.find(filename) == lObjects.end())
{
lObjects.emplace(filename, nullyThing);
}
}
int lID = 0;
for (auto& object : lObjects)
{
object.second.nameID = lID++;
}
// MWMO // MWMO
// { // {
int lMWMO_Position = curPos;
wdlFile.Extend(8); wdlFile.Extend(8);
SetChunkHeader(wdlFile, curPos, 'MWMO', 0); SetChunkHeader(wdlFile, curPos, 'MWMO', 0);
curPos += 8; curPos += 8;
// MWMO data
for (auto& object : lObjects)
{
object.second.filenamePosition = wdlFile.GetPointer<sChunkHeader>(lMWMO_Position)->mSize;
wdlFile.Insert(curPos, static_cast<unsigned long>(object.first.size() + 1), misc::normalize_adt_filename(object.first).c_str());
curPos += static_cast<int>(object.first.size() + 1);
wdlFile.GetPointer<sChunkHeader>(lMWMO_Position)->mSize += static_cast<int>(object.first.size() + 1);
LogDebug << "Added WDL object \"" << object.first << "\"." << std::endl;
}
// } // }
// MWID // MWID
// { // {
wdlFile.Extend(8); int lMWID_Size = static_cast<int>(4 * lObjects.size());
SetChunkHeader(wdlFile, curPos, 'MWID', 0); wdlFile.Extend(8 + lMWID_Size);
curPos += 8; SetChunkHeader(wdlFile, curPos, 'MWID', lMWID_Size);
// MWID data
auto const lMWID_Data = wdlFile.GetPointer<int>(curPos + 8);
lID = 0;
for (auto const& object : lObjects)
lMWID_Data[lID++] = object.second.filenamePosition;
curPos += 8 + lMWID_Size;
// } // }
// TODO : MODF // MODF
// { // {
wdlFile.Extend(8); int lMODF_Size = static_cast<int>(0x40 * lWMOInstances.size());
SetChunkHeader(wdlFile, curPos, 'MODF', 0); wdlFile.Extend(8 + lMODF_Size);
curPos += 8; SetChunkHeader(wdlFile, curPos, 'MODF', lMODF_Size);
// MODF data
auto const lMODF_Data = wdlFile.GetPointer<ENTRY_MODF>(curPos + 8);
lID = 0;
for (auto const& object : lWMOInstances)
{
auto filename_to_offset_and_name = lObjects.find(mWMOFilenames[object.nameID]);
if (filename_to_offset_and_name == lObjects.end())
{
LogError << "There is a problem with saving the WDL objects. We have an object that somehow changed the name during the saving function." << std::endl;
return;
}
lMODF_Data[lID] = object;
// only need to update name id
lMODF_Data[lID].nameID = filename_to_offset_and_name->second.nameID;
lID++;
}
LogDebug << "Added " << lID << " wmos to WDL MODF" << std::endl;
curPos += 8 + lMODF_Size;
// } // }
//uint32_t mare_offsets[64][64] = { 0 }; //uint32_t mare_offsets[64][64] = { 0 };
@@ -482,6 +573,46 @@ void map_horizon::save_wdl(World* world, bool regenerate)
set_minimap(&world->mapIndex); set_minimap(&world->mapIndex);
} }
bool map_horizon::wmoHasLowRes(WMOInstance* instance)
{
assert(instance->lowResWmo.has_value() == false);
if (instance->lowResWmo.has_value())
return true;
int i = 0;
for (auto& lowres_wmo : lWMOInstances)
{
if (lowres_wmo.uniqueID == instance->uid)
{
auto low_res_model = mWMOFilenames[lowres_wmo.nameID];
// TODO check positions
// need to convert coords?
auto dir = math::degrees::vec3{ math::degrees(
lowres_wmo.rot[0])._, math::degrees(lowres_wmo.rot[1])._, math::degrees(lowres_wmo.rot[2])._ };
if (misc::vec3d_equals(glm::vec3(lowres_wmo.pos[0], lowres_wmo.pos[1], lowres_wmo.pos[2]), instance->pos)
&& misc::deg_vec3d_equals(dir, instance->dir)
&& misc::float_equals( (lowres_wmo.scale / 1024.0f), instance->scale))
{
// instance->lowResWmo = scoped_wmo_reference(low_res_model, instance->wmo->_context);
instance->lowResInstance = &lowres_wmo;
instance->lowResWmo = &wmos[instance->lowResInstance->nameID];
return true;
}
else
{
assert(false);
}
}
i++;
}
return false;
}
map_horizon::minimap::minimap(const map_horizon& horizon) map_horizon::minimap::minimap(const map_horizon& horizon)
{ {
std::vector<uint32_t> texture(1024 * 1024); std::vector<uint32_t> texture(1024 * 1024);

View File

@@ -78,7 +78,7 @@ public:
minimap(const map_horizon& horizon); minimap(const map_horizon& horizon);
}; };
map_horizon(const std::string& basename, const MapIndex * const index); map_horizon(const std::string& basename, World * const world);
void update_minimap_tile(int y, int x, bool has_data); void update_minimap_tile(int y, int x, bool has_data);
@@ -94,14 +94,21 @@ public:
void save_wdl(World* world, bool regenerate = false); void save_wdl(World* world, bool regenerate = false);
bool wmoHasLowRes(WMOInstance* instance);
// void updateWmoLowRes(WMOInstance* instance);
// note : access those two with ENTRY_MODF.nameID
// TODO make a proper structure instead of raw access to wow chunks
std::vector<std::string> mWMOFilenames;
std::vector<ENTRY_MODF> lWMOInstances;
std::vector<scoped_wmo_reference> wmos;
private: private:
int16_t getWdlheight(MapTile* tile, float x, float y); int16_t getWdlheight(MapTile* tile, float x, float y);
std::string _filename; std::string _filename;
std::vector<std::string> mWMOFilenames;
// std::vector<ENTRY_MODF> lWMOInstances;
std::unique_ptr<map_horizon_tile> _tiles[64][64]; std::unique_ptr<map_horizon_tile> _tiles[64][64];
}; };

View File

@@ -9,6 +9,8 @@
#include <noggit/project/CurrentProject.hpp> #include <noggit/project/CurrentProject.hpp>
#include <noggit/application/NoggitApplication.hpp> #include <noggit/application/NoggitApplication.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <QDir> #include <QDir>
#include <QBuffer> #include <QBuffer>
#include <QCryptographicHash> #include <QCryptographicHash>
@@ -618,7 +620,8 @@ void WorldRender::draw (glm::mat4x4 const& model_view
// make this check per WMO or global WMO with tiles may not work // make this check per WMO or global WMO with tiles may not work
bool disable_cull = false; bool disable_cull = false;
if (_world->mapIndex.hasAGlobalWMO() && !wmos_to_draw.size()) // WDT wmo
if (_world->mapIndex.hasAGlobalWMO() && wmos_to_draw.empty())
{ {
auto global_wmo = _world->_model_instance_storage.get_wmo_instance(_world->mWmoEntry.uniqueID); auto global_wmo = _world->_model_instance_storage.get_wmo_instance(_world->mWmoEntry.uniqueID);
if (global_wmo.has_value()) if (global_wmo.has_value())
@@ -627,7 +630,91 @@ void WorldRender::draw (glm::mat4x4 const& model_view
disable_cull = true; disable_cull = true;
} }
} }
// draw wdl models in horizon/fog
if (render_settings.draw_fog)
{
// initialize the models here
// if (_world->horizon.wmos.size() < _world->horizon.lWMOInstances.size())
// {
// for (int i = 0; i < _world->horizon.lWMOInstances.size(); ++i)
// {
// auto instance = _world->horizon.lWMOInstances[i];
// auto& filepath = _world->horizon.mWMOFilenames[instance.nameID];
// _world->horizon.wmos.push_back(scoped_wmo_reference(filepath, _world->_context));
// }
// }
for (int i = 0; i < _world->horizon.lWMOInstances.size(); ++i)
{
auto& instance = _world->horizon.lWMOInstances[i];
auto model = _world->horizon.wmos[instance.nameID];
if (!model->finishedLoading() || model->loading_failed())
{
continue;
}
auto pos = glm::vec3(instance.pos[0], instance.pos[1], instance.pos[2]);
float dist = glm::distance(camera_pos, pos);
if (render_settings.draw_fog)
{
// Fog : only render if between cull distance and render distance ?
if (dist < _cull_distance || dist > _view_distance)
{
continue;
}
}
// else if (dist > _view_distance)
// continue;
// calc transform everytime for now
glm::mat4x4 matrix = glm::mat4x4(1.0f);
matrix = glm::translate(matrix, pos);
matrix *= glm::eulerAngleYZX(
glm::radians(instance.rot[1] - math::degrees(90.0f)._),
glm::radians(-instance.rot[0]),
glm::radians(instance.rot[2])
);
float scale = instance.scale / 1024.0f;
if (scale != 1.0f)
matrix = glm::scale(matrix, glm::vec3(scale, scale, scale));
wmo_program.uniform("transform", matrix);
model->renderer()->draw(wmo_program
, model_view
, projection
, matrix
, false
, frustum
, _view_distance
, camera_pos
, false
, render_settings.draw_fog
, _world->animtime
, _skies->hasSkies()
, render_settings.display_mode
, !render_settings.draw_wmo_exterior
, false
, false
);
// auto instance = _world->_model_instance_storage.get_wmo_instance(_world->horizon.lWMOInstances[i].uniqueID);
// if (instance.has_value())
// {
// wmos_to_draw.push_back(instance.value());
// }
}
}
// TODO setting
bool constexpr draw_wdl_models = false;
for (auto& instance: wmos_to_draw) for (auto& instance: wmos_to_draw)
{ {
@@ -695,7 +782,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
, render_settings.draw_wmo_exterior , render_settings.draw_wmo_exterior
, render_settings.render_select_wmo_aabb , render_settings.render_select_wmo_aabb
, render_settings.render_select_wmo_groups_bounds , render_settings.render_select_wmo_groups_bounds
, draw_wdl_models // draw wdl model in render dist
); );
} }
} }
@@ -1232,7 +1319,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
} }
// Draw Sky/Light spheres // Draw Sky/Light spheres
glCullFace(GL_FRONT); gl.cullFace(GL_FRONT);
if (!render_settings.draw_only_inside_light_sphere) if (!render_settings.draw_only_inside_light_sphere)
{ {
for (Sky const& sky : skies()->skies) for (Sky const& sky : skies()->skies)
@@ -1271,7 +1358,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
} }
// now draw the current light (light that we're inside of) // now draw the current light (light that we're inside of)
glCullFace(GL_BACK); gl.cullFace(GL_BACK);
for (Sky const& sky : skies()->skies) for (Sky const& sky : skies()->skies)
{ {
if (sky.global) if (sky.global)
@@ -1994,7 +2081,7 @@ bool WorldRender::saveMinimap(TileIndex const& tile_idx, MinimapRenderSettings*
, 0.f, 0.f, 0.f, 1.f , 0.f, 0.f, 0.f, 1.f
)); ));
glFinish(); gl.finish();
drawMinimap(mTile drawMinimap(mTile
, look_at , look_at
@@ -2064,13 +2151,13 @@ bool WorldRender::saveMinimap(TileIndex const& tile_idx, MinimapRenderSettings*
if (use_md5) if (use_md5)
{ {
QCryptographicHash md5_hash(QCryptographicHash::Md5); QCryptographicHash md5_hash(QCryptographicHash::Md5);
// auto data = reinterpret_cast<char*>(blp_image); // md5_hash.addData(reinterpret_cast<char*>(blp_image), file_size);
md5_hash.addData(reinterpret_cast<char*>(blp_image), file_size); QByteArray data(reinterpret_cast<const char*>(blp_image), file_size);
md5_hash.addData(data);
auto resulthex = md5_hash.result().toHex().toStdString() + ".blp"; auto resulthex = md5_hash.result().toHex().toStdString() + ".blp";
tex_name = resulthex; tex_name = resulthex;
} }
QFile file(dir.filePath(tex_name.c_str())); QFile file(dir.filePath(tex_name.c_str()));
file.open(QIODevice::WriteOnly); file.open(QIODevice::WriteOnly);

View File

@@ -118,7 +118,7 @@ namespace Noggit
}); });
QObject::connect(mapView(), &MapView::selectionUpdated, [=](auto) { QObject::connect(mapView(), &MapView::selectionUpdated, [=](auto) {
_objectEditor->update_selection_ui(mapView()->getWorld()); _objectEditor->update_selection_ui();
}); });
using AssetBrowser = Noggit::Ui::Tools::AssetBrowser::Ui::AssetBrowserWidget; using AssetBrowser = Noggit::Ui::Tools::AssetBrowser::Ui::AssetBrowserWidget;

View File

@@ -112,10 +112,38 @@ namespace Noggit
_doodadSetSelector = new QComboBox(this); _doodadSetSelector = new QComboBox(this);
_nameSetSelector = new QComboBox(this); _nameSetSelector = new QComboBox(this);
QLabel* horizon_label = new QLabel("Low Res Model:", this);
QHBoxLayout* horizon_layout = new QHBoxLayout;
_horizonModel = new QLineEdit(this);
_horizonModel->setToolTip("Sets a model that will be rendered in the horizon fog, allowing to render further than normal render distance."
"Used to show landmarks from far away.");
horizon_layout->addWidget(_horizonModel, 1);
QToolButton* validateButton = new QToolButton;
validateButton->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogApplyButton));
// validateButton->setIconSize(QSize(24, 24));
validateButton->setAutoRaise(true); // Gives it a flat toolbar-style appearance
validateButton->setToolTip("Apply Low Res Model Filepath change.");
horizon_layout->addWidget(validateButton, 0);
_previewButton = new QToolButton;
_previewButton->setIcon(Noggit::Ui::FontNoggitIcon(Noggit::Ui::FontNoggit::Icons::VISIBILITY_HIDDEN_MODELS));
_previewButton->setToolTip("Preview Low Res Model");
_previewButton->setCheckable(true);
_previewButton->setChecked(false);
// previewButton->setIconSize(QSize(24, 24)); // Set icon size
// previewButton->setAutoRaise(true);
horizon_layout->addWidget(_previewButton, 0);
layout->addWidget(_wmo_group); layout->addWidget(_wmo_group);
wmo_layout->addRow("Doodad Set:", _doodadSetSelector); wmo_layout->addRow("Doodad Set:", _doodadSetSelector);
wmo_layout->addRow("Name Set:", _nameSetSelector); wmo_layout->addRow("Name Set:", _nameSetSelector);
wmo_layout->addRow(horizon_label);
wmo_layout->addRow(horizon_layout);
auto clipboard_box = new QGroupBox("Clipboard"); auto clipboard_box = new QGroupBox("Clipboard");
// clipboard_box->setWindowIcon(Noggit::Ui::FontAwesomeIcon(Noggit::Ui::FontAwesome::clipboard)); // clipboard_box->setWindowIcon(Noggit::Ui::FontAwesomeIcon(Noggit::Ui::FontAwesome::clipboard));
@@ -178,8 +206,8 @@ namespace Noggit
rotRangeEnd->setRange (-180.f, 180.f); rotRangeEnd->setRange (-180.f, 180.f);
tiltRangeStart->setRange (-180.f, 180.f); tiltRangeStart->setRange (-180.f, 180.f);
tiltRangeEnd->setRange (-180.f, 180.f); tiltRangeEnd->setRange (-180.f, 180.f);
scaleRangeStart->setRange (-180.f, 180.f); scaleRangeStart->setRange (SceneObject::min_scale(), SceneObject::max_scale());
scaleRangeEnd->setRange (-180.f, 180.f); scaleRangeEnd->setRange (SceneObject::min_scale(), SceneObject::max_scale());
rotation_layout->addWidget(rotRangeStart, 0, 0); rotation_layout->addWidget(rotRangeStart, 0, 0);
rotation_layout->addWidget(rotRangeEnd, 0 ,1); rotation_layout->addWidget(rotRangeEnd, 0 ,1);
@@ -538,7 +566,11 @@ namespace Noggit
, &QPushButton::clicked , &QPushButton::clicked
, [=]() { , [=]() {
_map_view->getAssetBrowserWidget()->set_browse_mode(Tools::AssetBrowser::asset_browse_mode::world); _map_view->getAssetBrowserWidget()->set_browse_mode(Tools::AssetBrowser::asset_browse_mode::world);
mapView->getAssetBrowser()->setVisible(mapView->getAssetBrowser()->isHidden()); // mapView->getAssetBrowser()->setVisible(mapView->getAssetBrowser()->isHidden());
mapView->getAssetBrowser()->setFloating(true); // if it's not docked
mapView->getAssetBrowser()->resize(600, 400);
mapView->getAssetBrowser()->move(100, 100); // make sure it's on screen
mapView->getAssetBrowser()->show();
} }
); );
@@ -566,6 +598,118 @@ namespace Noggit
NOGGIT_ACTION_MGR->endAction(); NOGGIT_ACTION_MGR->endAction();
}); });
QObject::connect(validateButton, &QToolButton::clicked, [&]
{
if (_horizonModel->text().isEmpty())
_previewButton->setEnabled(false);
auto last_entry = _map_view->getWorld()->get_last_selected_model();
// for (auto& selection : selected)
if (last_entry)
{
if (last_entry.value().index() != eEntry_Object)
{
return;
}
auto obj = std::get<selected_object_type>(last_entry.value());
if (obj->which() == eWMO)
{
WMOInstance* wi = static_cast<WMOInstance*>(obj);
// verify if model exists if not empty
if (!_horizonModel->text().isEmpty())
{
if (!Noggit::Application::NoggitApplication::instance()->clientData()->exists(_horizonModel->text().toStdString()))
{
QMessageBox::warning
(nullptr
, "Warning"
, QString::fromStdString(_horizonModel->text().toStdString() + " not found.")
);
return;
}
}
auto world = _map_view->getWorld();
auto lowresmodel = wi->lowResWmo;
if (lowresmodel.has_value()) // had data
{
// same model
if (lowresmodel.value()->get()->file_key().filepath() == _horizonModel->text().toStdString())
return;
if (_horizonModel->text().isEmpty()) // empty text but had a model
{
// TODO, deleting elements.
wi->lowResWmo = std::nullopt;
// delete wi->lowResInstance;
wi->lowResInstance = nullptr;
wi->render_low_res = false;
}
else // text not empty
{
// add a model instead of replacing, because a filename can be referenced by multiple.
// TODO store instances filenames directly for this reason instead of using raw MWMO/MODF
world->horizon.mWMOFilenames.push_back(_horizonModel->text().toStdString());
world->horizon.wmos.emplace_back(scoped_wmo_reference(_horizonModel->text().toStdString(), _map_view->getRenderContext()));
wi->lowResInstance->nameID = world->horizon.mWMOFilenames.size() - 1;
wi->lowResWmo = &world->horizon.wmos.back();
}
}
else if (!_horizonModel->text().isEmpty()) // if had no wdl data
{
world->horizon.mWMOFilenames.push_back(_horizonModel->text().toStdString());
world->horizon.wmos.emplace_back(scoped_wmo_reference(_horizonModel->text().toStdString(), _map_view->getRenderContext()));
world->horizon.lWMOInstances.emplace_back(ENTRY_MODF());
auto& modf_instance = world->horizon.lWMOInstances.back();
modf_instance.nameID = world->horizon.mWMOFilenames.size() - 1;
modf_instance.uniqueID = wi->uid;
modf_instance.scale = 1024;
modf_instance.nameSet = 0;
modf_instance.flags = 0;
modf_instance.doodadSet = 0;
wi->lowResInstance = &modf_instance;
wi->lowResWmo = &world->horizon.wmos.back();
// recalc extents to update pos, rot, extents in modf_instance
wi->recalcExtents();
}
world->horizon.save_wdl(world, false);
}
}
}
);
QObject::connect(_previewButton, &QToolButton::toggled, this, [&](bool checked)
{
auto last_entry = _map_view->getWorld()->get_last_selected_model();
// for (auto& selection : selected)
if (last_entry)
{
if (last_entry.value().index() != eEntry_Object)
{
return;
}
auto obj = std::get<selected_object_type>(last_entry.value());
if (obj->which() == eWMO)
{
_wmo_group->setDisabled(false);
_wmo_group->setHidden(false);
WMOInstance* wi = static_cast<WMOInstance*>(obj);
wi->render_low_res = checked;
}
}
}
);
auto mv_pos = mapView->pos(); auto mv_pos = mapView->pos();
auto mv_size = mapView->size(); auto mv_size = mapView->size();
@@ -930,11 +1074,20 @@ namespace Noggit
return QSize(215, height()); return QSize(215, height());
} }
void object_editor::update_selection_ui(World* world) void object_editor::update_selection_ui()
{ {
_wmo_group->setDisabled(true); _wmo_group->setDisabled(true);
_wmo_group->hide(); _wmo_group->hide();
QSignalBlocker const previewblocker(_previewButton);
_previewButton->setChecked(false);
_previewButton->setDisabled(true);
auto world = _map_view->getWorld();
if (world->has_multiple_model_selected())
return;
auto last_entry = world->get_last_selected_model(); auto last_entry = world->get_last_selected_model();
// for (auto& selection : selected) // for (auto& selection : selected)
if (last_entry) if (last_entry)
@@ -985,6 +1138,18 @@ namespace Noggit
} }
_nameSetSelector->insertItems(0, namesetnames); _nameSetSelector->insertItems(0, namesetnames);
_nameSetSelector->setCurrentIndex(wi->mNameset); _nameSetSelector->setCurrentIndex(wi->mNameset);
auto lowresmodel = wi->lowResWmo;
if (lowresmodel.has_value())
{
_horizonModel->setText(lowresmodel.value()->get()->file_key().filepath().c_str());
_previewButton->setDisabled(false);
}
else
_horizonModel->setText("");
if (wi->render_low_res)
_previewButton->setChecked(true);
} }
} }
} }

View File

@@ -83,7 +83,7 @@ namespace Noggit
helper_models* helper_models_widget; helper_models* helper_models_widget;
QSize sizeHint() const override; QSize sizeHint() const override;
void update_selection_ui(World* world); void update_selection_ui();
signals: signals:
void objectPaletteBtnPressed(); void objectPaletteBtnPressed();
@@ -102,6 +102,8 @@ namespace Noggit
QGroupBox* _wmo_group; QGroupBox* _wmo_group;
QComboBox* _doodadSetSelector; QComboBox* _doodadSetSelector;
QComboBox* _nameSetSelector; QComboBox* _nameSetSelector;
QLineEdit* _horizonModel;
QToolButton* _previewButton;
QSettings* _settings; QSettings* _settings;

View File

@@ -222,13 +222,17 @@ namespace Noggit
//! \todo Get actual color from sky. //! \todo Get actual color from sky.
//! \todo Get actual radius. //! \todo Get actual radius.
//! \todo Inner and outer radius? //! \todo Inner and outer radius?
painter.setPen (Qt::blue); // painter.setPen (Qt::blue);
auto sky_noon_color = sky.colorFor(LIGHT_GLOBAL_DIFFUSE, 1441);
auto color = QColor::fromRgbF(sky_noon_color.r, sky_noon_color.g, sky_noon_color.b);
painter.setPen(color);
auto radius = sky.r2 * scale_factor;
painter.drawEllipse ( QPointF ( sky.pos.x * scale_factor painter.drawEllipse ( QPointF ( sky.pos.x * scale_factor
, sky.pos.z * scale_factor , sky.pos.z * scale_factor
) )
, 10.0 // radius , radius
, 10.0 , radius
); );
} }
} }

View File

@@ -192,7 +192,7 @@ void PreviewRenderer::draw()
wmo_program, model_view(), projection(), frustum, culldistance, wmo_program, model_view(), projection(), frustum, culldistance,
_camera.position, _draw_boxes.get(), _draw_models.get() _camera.position, _draw_boxes.get(), _draw_models.get()
, false, false, 0, false, display_mode::in_3D , false, false, 0, false, display_mode::in_3D
, true, true, false, false , true, true, false, false, false
); );
auto doodads = wmo_instance.get_doodads(true); auto doodads = wmo_instance.get_doodads(true);
@@ -450,10 +450,10 @@ QPixmap* PreviewRenderer::renderToPixmap()
if (async_loader->is_loading()) if (async_loader->is_loading())
{ {
// wait for the loader to finish // wait for the loader to finish
// do do
// { {
// std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(1));
// } while (async_loader->is_loading()); } while (async_loader->is_loading());
// redraw // redraw
gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -694,6 +694,7 @@ void PreviewRenderer::unloadOpenglData()
return; return;
} }
assert(context() != nullptr);
makeCurrent(); makeCurrent();
OpenGL::context::scoped_setter const _ (::gl, context()); OpenGL::context::scoped_setter const _ (::gl, context());

View File

@@ -144,6 +144,11 @@ void ViewportGizmo::handleTransformGizmo(MapView* map_view
} }
} }
// Clamp scale to allowed values
new_scale.x = std::clamp(new_scale.x, SceneObject::min_scale(), SceneObject::max_scale());
new_scale.y = std::clamp(new_scale.y, SceneObject::min_scale(), SceneObject::max_scale());
new_scale.z = std::clamp(new_scale.z, SceneObject::min_scale(), SceneObject::max_scale());
NOGGIT_ACTION_MGR->beginAction(map_view, Noggit::ActionFlags::eOBJECTS_TRANSFORMED, NOGGIT_ACTION_MGR->beginAction(map_view, Noggit::ActionFlags::eOBJECTS_TRANSFORMED,
Noggit::ActionModalityControllers::eLMB); Noggit::ActionModalityControllers::eLMB);

View File

@@ -55,6 +55,7 @@ namespace Noggit
} }
// the uid is already used for another model/wmo, use a new one // the uid is already used for another model/wmo, use a new one
LogDebug << "UID " << uid << " is already in use (" << instance.model->file_key().stringRepr() << ")" << std::endl;
_uid_duplicates_found = true; _uid_duplicates_found = true;
instance.uid = _world->mapIndex.newGUID(); instance.uid = _world->mapIndex.newGUID();
@@ -74,7 +75,15 @@ namespace Noggit
if (from_reloading || uid_after != uid) if (from_reloading || uid_after != uid)
{ {
_world->updateTilesWMO(&_wmos.at(uid_after), model_update::add); WMOInstance* wmo_instance = &_wmos.at(uid_after);
_world->updateTilesWMO(wmo_instance, model_update::add);
// update WDL uid if it changed
[[unlikely]]
if (wmo_instance->lowResWmo.has_value())
{
wmo_instance->lowResInstance->uniqueID = uid_after;
}
} }
return uid_after; return uid_after;
@@ -104,6 +113,7 @@ namespace Noggit
} }
// the uid is already used for another model/wmo, use a new one // the uid is already used for another model/wmo, use a new one
LogDebug << "UID " << uid << " is already in use (" << instance.wmo->file_key().stringRepr() << ")" << std::endl;
_uid_duplicates_found = true; _uid_duplicates_found = true;
instance.uid = _world->mapIndex.newGUID(); instance.uid = _world->mapIndex.newGUID();