Skip to content

Commit 5eed750

Browse files
committed
Core/vmaps: Improved WMO detection for group models that don't have floor
(cherry picked from commit 28c9474337d0d30bc1d131b12b635c31a98bc97a)
1 parent b4d6ca2 commit 5eed750

File tree

3 files changed

+86
-49
lines changed

3 files changed

+86
-49
lines changed

src/common/Collision/BoundingIntervalHierarchy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ class TC_COMMON_API BIH
113113
delete[] dat.indices;
114114
}
115115
uint32 primCount() const { return uint32(objects.size()); }
116+
G3D::AABox const& bound() const { return bounds; }
116117

117118
template<typename RayCallback>
118119
void intersectRay(const G3D::Ray &r, RayCallback& intersectCallback, float &maxDist, bool stopAtFirst = false) const

src/common/Collision/Models/WorldModel.cpp

Lines changed: 81 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
#include "MapTree.h"
2121
#include "ModelInstance.h"
2222
#include "ModelIgnoreFlags.h"
23+
#include <array>
2324

2425
using G3D::Vector3;
25-
using G3D::Ray;
2626

2727
template<> struct BoundsTrait<VMAP::GroupModel>
2828
{
@@ -416,17 +416,46 @@ namespace VMAP
416416
return callback.hit;
417417
}
418418

419-
bool GroupModel::IsInsideObject(Vector3 const& pos, Vector3 const& down, float& z_dist) const
419+
inline bool IsInsideOrAboveBound(G3D::AABox const& bounds, const G3D::Point3& point)
420420
{
421-
if (triangles.empty() || !iBound.contains(pos))
422-
return false;
423-
Vector3 rPos = pos - 0.1f * down;
424-
float dist = G3D::finf();
425-
G3D::Ray ray(rPos, down);
426-
bool hit = IntersectRay(ray, dist, false);
427-
if (hit)
428-
z_dist = dist - 0.1f;
429-
return hit;
421+
return point.x >= bounds.low().x
422+
&& point.y >= bounds.low().y
423+
&& point.z >= bounds.low().z
424+
&& point.x <= bounds.high().x
425+
&& point.y <= bounds.high().y;
426+
}
427+
428+
GroupModel::InsideResult GroupModel::IsInsideObject(G3D::Ray const& ray, float& z_dist) const
429+
{
430+
if (triangles.empty() || !IsInsideOrAboveBound(iBound, ray.origin()))
431+
return OUT_OF_BOUNDS;
432+
433+
if (meshTree.bound().high().z >= ray.origin().z)
434+
{
435+
float dist = G3D::finf();
436+
if (IntersectRay(ray, dist, false))
437+
{
438+
z_dist = dist - 0.1f;
439+
return INSIDE;
440+
}
441+
if (meshTree.bound().contains(ray.origin()))
442+
return MAYBE_INSIDE;
443+
}
444+
else
445+
{
446+
// some group models don't have any floor to intersect with
447+
// so we should attempt to intersect with a model part below this group
448+
// then find back where we originated from (in WorldModel::GetLocationInfo)
449+
float dist = G3D::finf();
450+
float delta = ray.origin().z - meshTree.bound().high().z;
451+
if (IntersectRay(ray.bumpedRay(delta), dist, false))
452+
{
453+
z_dist = dist - 0.1f + delta;
454+
return ABOVE;
455+
}
456+
}
457+
458+
return OUT_OF_BOUNDS;
430459
}
431460

432461
bool GroupModel::GetLiquidLevel(Vector3 const& pos, float& liqHeight) const
@@ -492,55 +521,60 @@ namespace VMAP
492521
return isc.hit;
493522
}
494523

495-
class WModelAreaCallback {
496-
public:
497-
WModelAreaCallback(std::vector<GroupModel> const& vals, Vector3 const& down) :
498-
prims(vals.begin()), hit(vals.end()), minVol(G3D::finf()), zDist(G3D::finf()), zVec(down) { }
499-
std::vector<GroupModel>::const_iterator prims;
500-
std::vector<GroupModel>::const_iterator hit;
501-
float minVol;
502-
float zDist;
503-
Vector3 zVec;
504-
void operator()(Vector3 const& point, uint32 entry)
524+
class WModelAreaCallback
525+
{
526+
public:
527+
WModelAreaCallback(std::vector<GroupModel> const& vals) :
528+
prims(vals), hit() { }
529+
std::vector<GroupModel> const& prims;
530+
std::array<GroupModel const*, 3> hit;
531+
532+
bool operator()(G3D::Ray const& ray, uint32 entry, float& distance, bool /*stopAtFirstHit*/)
533+
{
534+
float group_Z;
535+
if (GroupModel::InsideResult result = prims[entry].IsInsideObject(ray, group_Z); result != GroupModel::OUT_OF_BOUNDS)
505536
{
506-
float group_Z;
507-
//float pVol = prims[entry].GetBound().volume();
508-
//if (pVol < minVol)
509-
//{
510-
/* if (prims[entry].iBound.contains(point)) */
511-
if (prims[entry].IsInsideObject(point, zVec, group_Z))
537+
if (result != GroupModel::MAYBE_INSIDE)
538+
{
539+
if (group_Z < distance)
512540
{
513-
//minVol = pVol;
514-
//hit = prims + entry;
515-
if (group_Z < zDist)
516-
{
517-
zDist = group_Z;
518-
hit = prims + entry;
519-
}
520-
#ifdef VMAP_DEBUG
521-
GroupModel const& gm = prims[entry];
522-
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(),
523-
gm.GetBound().low().x, gm.GetBound().low().y, gm.GetBound().low().z,
524-
gm.GetBound().high().x, gm.GetBound().high().y, gm.GetBound().high().z, group_Z, point.z);
525-
#endif
541+
distance = group_Z;
542+
hit[result] = &prims[entry];
543+
return true;
526544
}
527-
//}
528-
//std::cout << "trying to intersect '" << prims[entry].name << "'\n";
545+
}
546+
else
547+
hit[result] = &prims[entry];
529548
}
549+
return false;
550+
}
530551
};
531552

532553
bool WorldModel::GetLocationInfo(const G3D::Vector3& p, const G3D::Vector3& down, float& dist, GroupLocationInfo& info) const
533554
{
534555
if (groupModels.empty())
535556
return false;
536557

537-
WModelAreaCallback callback(groupModels, down);
538-
groupTree.intersectPoint(p, callback);
539-
if (callback.hit != groupModels.end())
558+
WModelAreaCallback callback(groupModels);
559+
G3D::Ray r(p - down * 0.1f, down);
560+
float zDist = groupTree.bound().extent().length();
561+
groupTree.intersectRay(r, callback, zDist, false);
562+
if (callback.hit[GroupModel::INSIDE])
563+
{
564+
info.rootId = RootWMOID;
565+
info.hitModel = callback.hit[GroupModel::INSIDE];
566+
dist = zDist;
567+
return true;
568+
}
569+
570+
// some group models don't have any floor to intersect with
571+
// so we should attempt to intersect with a model part below the group `p` is in (stored in GroupModel::ABOVE)
572+
// then find back where we originated from (GroupModel::MAYBE_INSIDE)
573+
if (callback.hit[GroupModel::MAYBE_INSIDE] && callback.hit[GroupModel::ABOVE])
540574
{
541575
info.rootId = RootWMOID;
542-
info.hitModel = &(*callback.hit);
543-
dist = callback.zDist;
576+
info.hitModel = callback.hit[GroupModel::MAYBE_INSIDE];
577+
dist = zDist;
544578
return true;
545579
}
546580
return false;

src/common/Collision/Models/WorldModel.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,14 @@ namespace VMAP
8484
void setMeshData(std::vector<G3D::Vector3> &vert, std::vector<MeshTriangle> &tri);
8585
void setLiquidData(WmoLiquid*& liquid) { iLiquid = liquid; liquid = nullptr; }
8686
bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const;
87-
bool IsInsideObject(const G3D::Vector3 &pos, const G3D::Vector3 &down, float &z_dist) const;
87+
enum InsideResult { INSIDE = 0, MAYBE_INSIDE = 1, ABOVE = 2, OUT_OF_BOUNDS = -1 };
88+
InsideResult IsInsideObject(G3D::Ray const& ray, float& z_dist) const;
8889
bool GetLiquidLevel(const G3D::Vector3 &pos, float &liqHeight) const;
8990
uint32 GetLiquidType() const;
9091
bool writeToFile(FILE* wf);
9192
bool readFromFile(FILE* rf);
92-
const G3D::AABox& GetBound() const { return iBound; }
93+
G3D::AABox const& GetBound() const { return iBound; }
94+
G3D::AABox const& GetMeshTreeBound() const { return meshTree.bound(); }
9395
uint32 GetMogpFlags() const { return iMogpFlags; }
9496
uint32 GetWmoID() const { return iGroupWMOID; }
9597
void getMeshData(std::vector<G3D::Vector3>& outVertices, std::vector<MeshTriangle>& outTriangles, WmoLiquid*& liquid);

0 commit comments

Comments
 (0)