Skip to content

Commit

Permalink
Core/vmaps: Improved WMO detection for group models that don't have f…
Browse files Browse the repository at this point in the history
…loor

(cherry picked from commit 28c9474)
  • Loading branch information
Shauren committed Nov 19, 2024
1 parent b4d6ca2 commit 5eed750
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 49 deletions.
1 change: 1 addition & 0 deletions src/common/Collision/BoundingIntervalHierarchy.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,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
Expand Down
128 changes: 81 additions & 47 deletions src/common/Collision/Models/WorldModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
#include "MapTree.h"
#include "ModelInstance.h"
#include "ModelIgnoreFlags.h"
#include <array>

using G3D::Vector3;
using G3D::Ray;

template<> struct BoundsTrait<VMAP::GroupModel>
{
Expand Down Expand Up @@ -416,17 +416,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
Expand Down Expand Up @@ -492,55 +521,60 @@ 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
{
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;
Expand Down
6 changes: 4 additions & 2 deletions src/common/Collision/Models/WorldModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,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; }
void getMeshData(std::vector<G3D::Vector3>& outVertices, std::vector<MeshTriangle>& outTriangles, WmoLiquid*& liquid);
Expand Down

0 comments on commit 5eed750

Please sign in to comment.