Files

186 lines
6.1 KiB
C#

// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using Framework.GameMath;
using System.Numerics;
namespace Game.Collision
{
public class DynamicMapTree
{
public DynamicMapTree()
{
impl = new DynTreeImpl();
}
public void Insert(GameObjectModel mdl)
{
impl.Insert(mdl);
}
public void Remove(GameObjectModel mdl)
{
impl.Remove(mdl);
}
public bool Contains(GameObjectModel mdl)
{
return impl.Contains(mdl);
}
public void Balance()
{
impl.Balance();
}
public void Update(uint diff)
{
impl.Update(diff);
}
public bool GetIntersectionTime(Ray ray, Vector3 endPos, PhaseShift phaseShift, ref float maxDist)
{
float distance = maxDist;
DynamicTreeIntersectionCallback callback = new(phaseShift);
impl.IntersectRay(ray, callback, ref distance, endPos);
if (callback.DidHit())
maxDist = distance;
return callback.DidHit();
}
public bool GetObjectHitPos(Vector3 startPos, Vector3 endPos, ref Vector3 resultHitPos, float modifyDist, PhaseShift phaseShift)
{
bool result;
float maxDist = (endPos - startPos).Length();
// valid map coords should *never ever* produce float overflow, but this would produce NaNs too
Cypher.Assert(maxDist < float.MaxValue);
// prevent NaN values which can cause BIH intersection to enter infinite loop
if (maxDist < 1e-10f)
{
resultHitPos = endPos;
return false;
}
Vector3 dir = (endPos - startPos) / maxDist; // direction with length of 1
Ray ray = new(startPos, dir);
float dist = maxDist;
if (GetIntersectionTime(ray, endPos, phaseShift, ref dist))
{
resultHitPos = startPos + dir * dist;
if (modifyDist < 0)
{
if ((resultHitPos - startPos).Length() > -modifyDist)
resultHitPos += dir * modifyDist;
else
resultHitPos = startPos;
}
else
resultHitPos += dir * modifyDist;
result = true;
}
else
{
resultHitPos = endPos;
result = false;
}
return result;
}
public bool IsInLineOfSight(Vector3 startPos, Vector3 endPos, PhaseShift phaseShift)
{
float maxDist = (endPos - startPos).Length();
if (!MathFunctions.fuzzyGt(maxDist, 0))
return true;
Ray r = new(startPos, (endPos - startPos) / maxDist);
DynamicTreeLosCallback callback = new(phaseShift);
impl.IntersectRay(r, callback, ref maxDist, endPos);
return !callback.DidHit();
}
public float GetHeight(float x, float y, float z, float maxSearchDist, PhaseShift phaseShift)
{
Vector3 v = new(x, y, z);
Ray r = new(v, new Vector3(0, 0, -1));
DynamicTreeIntersectionCallback callback = new(phaseShift);
impl.IntersectZAllignedRay(r, callback, ref maxSearchDist);
if (callback.DidHit())
return v.Z - maxSearchDist;
else
return float.NegativeInfinity;
}
public bool GetAreaAndLiquidData(float x, float y, float z, PhaseShift phaseShift, byte? reqLiquidType, out AreaAndLiquidData data)
{
data = new();
Vector3 v = new(x, y, z + 0.5f);
DynamicTreeLocationInfoCallback intersectionCallBack = new(phaseShift);
impl.IntersectPoint(v, intersectionCallBack);
if (intersectionCallBack.GetLocationInfo().hitModel != null)
{
data.floorZ = intersectionCallBack.GetLocationInfo().ground_Z;
uint liquidType = intersectionCallBack.GetLocationInfo().hitModel.GetLiquidType();
float liquidLevel = 0;
if (!reqLiquidType.HasValue || (Global.DB2Mgr.GetLiquidFlags(liquidType) & reqLiquidType.Value) != 0)
if (intersectionCallBack.GetHitModel().GetLiquidLevel(v, intersectionCallBack.GetLocationInfo(), ref liquidLevel))
data.liquidInfo = new AreaAndLiquidData.LiquidInfo(liquidType, liquidLevel);
data.areaInfo = new((int)intersectionCallBack.GetLocationInfo().hitModel.GetWmoID(), intersectionCallBack.GetHitModel().GetNameSetId(), intersectionCallBack.GetLocationInfo().rootId, intersectionCallBack.GetLocationInfo().hitModel.GetMogpFlags(), 0);
return true;
}
return false;
}
DynTreeImpl impl;
}
public class DynTreeImpl : RegularGrid2D<GameObjectModel, BIHWrap<GameObjectModel>>
{
public DynTreeImpl()
{
rebalance_timer = new TimeTracker(200);
unbalanced_times = 0;
}
public override void Insert(GameObjectModel mdl)
{
base.Insert(mdl);
++unbalanced_times;
}
public override void Remove(GameObjectModel mdl)
{
base.Remove(mdl);
++unbalanced_times;
}
public override void Balance()
{
base.Balance();
unbalanced_times = 0;
}
public void Update(uint difftime)
{
if (Empty())
return;
rebalance_timer.Update(difftime);
if (rebalance_timer.Passed())
{
rebalance_timer.Reset(200);
if (unbalanced_times > 0)
Balance();
}
}
TimeTracker rebalance_timer;
int unbalanced_times;
}
}