aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2024-03-25 12:18:37 +0100
committerShauren <shauren.trinity@gmail.com>2024-03-25 12:18:37 +0100
commit28c9474337d0d30bc1d131b12b635c31a98bc97a (patch)
tree98b4d81cc7b9a98b4891fac0ef357d7e9a5ba7eb /src
parenta83d4491f80079376054628fda64b33771ff85be (diff)
Core/vmaps: Improved WMO detection for group models that don't have floor
Diffstat (limited to 'src')
-rw-r--r--src/common/Collision/BoundingIntervalHierarchy.h1
-rw-r--r--src/common/Collision/Models/WorldModel.cpp129
-rw-r--r--src/common/Collision/Models/WorldModel.h6
3 files changed, 86 insertions, 50 deletions
diff --git a/src/common/Collision/BoundingIntervalHierarchy.h b/src/common/Collision/BoundingIntervalHierarchy.h
index 9ea072a19db..88c936dd0d4 100644
--- a/src/common/Collision/BoundingIntervalHierarchy.h
+++ b/src/common/Collision/BoundingIntervalHierarchy.h
@@ -112,6 +112,7 @@ class TC_COMMON_API BIH
delete[] dat.indices;
}
uint32 primCount() const { return uint32(objects.size()); }
+ G3D::AABox const& bound() const { return bounds; }
template<typename RayCallback>
void intersectRay(const G3D::Ray &r, RayCallback& intersectCallback, float &maxDist, bool stopAtFirst = false) const
diff --git a/src/common/Collision/Models/WorldModel.cpp b/src/common/Collision/Models/WorldModel.cpp
index 27e724af69c..8917d459145 100644
--- a/src/common/Collision/Models/WorldModel.cpp
+++ b/src/common/Collision/Models/WorldModel.cpp
@@ -18,11 +18,10 @@
#include "WorldModel.h"
#include "VMapDefinitions.h"
#include "MapTree.h"
-#include "ModelInstance.h"
#include "ModelIgnoreFlags.h"
+#include <array>
using G3D::Vector3;
-using G3D::Ray;
template<> struct BoundsTrait<VMAP::GroupModel>
{
@@ -416,17 +415,46 @@ namespace VMAP
return callback.hit;
}
- bool GroupModel::IsInsideObject(Vector3 const& pos, Vector3 const& down, float& z_dist) const
+ inline bool IsInsideOrAboveBound(G3D::AABox const& bounds, const G3D::Point3& point)
{
- if (triangles.empty() || !iBound.contains(pos))
- return false;
- Vector3 rPos = pos - 0.1f * down;
- float dist = G3D::finf();
- G3D::Ray ray(rPos, down);
- bool hit = IntersectRay(ray, dist, false);
- if (hit)
- z_dist = dist - 0.1f;
- return hit;
+ return point.x >= bounds.low().x
+ && point.y >= bounds.low().y
+ && point.z >= bounds.low().z
+ && point.x <= bounds.high().x
+ && point.y <= bounds.high().y;
+ }
+
+ GroupModel::InsideResult GroupModel::IsInsideObject(G3D::Ray const& ray, float& z_dist) const
+ {
+ if (triangles.empty() || !IsInsideOrAboveBound(iBound, ray.origin()))
+ return OUT_OF_BOUNDS;
+
+ if (meshTree.bound().high().z >= ray.origin().z)
+ {
+ float dist = G3D::finf();
+ if (IntersectRay(ray, dist, false))
+ {
+ z_dist = dist - 0.1f;
+ return INSIDE;
+ }
+ if (meshTree.bound().contains(ray.origin()))
+ return MAYBE_INSIDE;
+ }
+ else
+ {
+ // some group models don't have any floor to intersect with
+ // so we should attempt to intersect with a model part below this group
+ // then find back where we originated from (in WorldModel::GetLocationInfo)
+ float dist = G3D::finf();
+ float delta = ray.origin().z - meshTree.bound().high().z;
+ if (IntersectRay(ray.bumpedRay(delta), dist, false))
+ {
+ z_dist = dist - 0.1f + delta;
+ return ABOVE;
+ }
+ }
+
+ return OUT_OF_BOUNDS;
}
bool GroupModel::GetLiquidLevel(Vector3 const& pos, float& liqHeight) const
@@ -485,41 +513,33 @@ namespace VMAP
return isc.hit;
}
- class WModelAreaCallback {
- public:
- WModelAreaCallback(std::vector<GroupModel> const& vals, Vector3 const& down) :
- prims(vals.begin()), hit(vals.end()), minVol(G3D::finf()), zDist(G3D::finf()), zVec(down) { }
- std::vector<GroupModel>::const_iterator prims;
- std::vector<GroupModel>::const_iterator hit;
- float minVol;
- float zDist;
- Vector3 zVec;
- void operator()(Vector3 const& point, uint32 entry)
+ class WModelAreaCallback
+ {
+ public:
+ WModelAreaCallback(std::vector<GroupModel> const& vals) :
+ prims(vals), hit() { }
+ std::vector<GroupModel> const& prims;
+ std::array<GroupModel const*, 3> hit;
+
+ bool operator()(G3D::Ray const& ray, uint32 entry, float& distance, bool /*stopAtFirstHit*/)
+ {
+ float group_Z;
+ if (GroupModel::InsideResult result = prims[entry].IsInsideObject(ray, group_Z); result != GroupModel::OUT_OF_BOUNDS)
{
- float group_Z;
- //float pVol = prims[entry].GetBound().volume();
- //if (pVol < minVol)
- //{
- /* if (prims[entry].iBound.contains(point)) */
- if (prims[entry].IsInsideObject(point, zVec, group_Z))
+ if (result != GroupModel::MAYBE_INSIDE)
+ {
+ if (group_Z < distance)
{
- //minVol = pVol;
- //hit = prims + entry;
- if (group_Z < zDist)
- {
- zDist = group_Z;
- hit = prims + entry;
- }
-#ifdef VMAP_DEBUG
- GroupModel const& gm = prims[entry];
- printf("%10u %8X %7.3f, %7.3f, %7.3f | %7.3f, %7.3f, %7.3f | z=%f, p_z=%f\n", gm.GetWmoID(), gm.GetMogpFlags(),
- gm.GetBound().low().x, gm.GetBound().low().y, gm.GetBound().low().z,
- gm.GetBound().high().x, gm.GetBound().high().y, gm.GetBound().high().z, group_Z, point.z);
-#endif
+ distance = group_Z;
+ hit[result] = &prims[entry];
+ return true;
}
- //}
- //std::cout << "trying to intersect '" << prims[entry].name << "'\n";
+ }
+ else
+ hit[result] = &prims[entry];
}
+ return false;
+ }
};
bool WorldModel::GetLocationInfo(const G3D::Vector3& p, const G3D::Vector3& down, float& dist, GroupLocationInfo& info) const
@@ -527,13 +547,26 @@ namespace VMAP
if (groupModels.empty())
return false;
- WModelAreaCallback callback(groupModels, down);
- groupTree.intersectPoint(p, callback);
- if (callback.hit != groupModels.end())
+ WModelAreaCallback callback(groupModels);
+ G3D::Ray r(p - down * 0.1f, down);
+ float zDist = groupTree.bound().extent().length();
+ groupTree.intersectRay(r, callback, zDist, false);
+ if (callback.hit[GroupModel::INSIDE])
+ {
+ info.rootId = RootWMOID;
+ info.hitModel = callback.hit[GroupModel::INSIDE];
+ dist = zDist;
+ return true;
+ }
+
+ // some group models don't have any floor to intersect with
+ // so we should attempt to intersect with a model part below the group `p` is in (stored in GroupModel::ABOVE)
+ // then find back where we originated from (GroupModel::MAYBE_INSIDE)
+ if (callback.hit[GroupModel::MAYBE_INSIDE] && callback.hit[GroupModel::ABOVE])
{
info.rootId = RootWMOID;
- info.hitModel = &(*callback.hit);
- dist = callback.zDist;
+ info.hitModel = callback.hit[GroupModel::MAYBE_INSIDE];
+ dist = zDist;
return true;
}
return false;
diff --git a/src/common/Collision/Models/WorldModel.h b/src/common/Collision/Models/WorldModel.h
index 52bce830ef1..48e75f3b797 100644
--- a/src/common/Collision/Models/WorldModel.h
+++ b/src/common/Collision/Models/WorldModel.h
@@ -90,12 +90,14 @@ namespace VMAP
void setMeshData(std::vector<G3D::Vector3> &vert, std::vector<MeshTriangle> &tri);
void setLiquidData(WmoLiquid*& liquid) { iLiquid = liquid; liquid = nullptr; }
bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const;
- bool IsInsideObject(const G3D::Vector3 &pos, const G3D::Vector3 &down, float &z_dist) const;
+ enum InsideResult { INSIDE = 0, MAYBE_INSIDE = 1, ABOVE = 2, OUT_OF_BOUNDS = -1 };
+ InsideResult IsInsideObject(G3D::Ray const& ray, float& z_dist) const;
bool GetLiquidLevel(const G3D::Vector3 &pos, float &liqHeight) const;
uint32 GetLiquidType() const;
bool writeToFile(FILE* wf);
bool readFromFile(FILE* rf);
- const G3D::AABox& GetBound() const { return iBound; }
+ G3D::AABox const& GetBound() const { return iBound; }
+ G3D::AABox const& GetMeshTreeBound() const { return meshTree.bound(); }
uint32 GetMogpFlags() const { return iMogpFlags; }
uint32 GetWmoID() const { return iGroupWMOID; }
std::vector<G3D::Vector3> const& GetVertices() const { return vertices; }