1078 lines
40 KiB
C#
1078 lines
40 KiB
C#
using BrewMonster.Network;
|
||
using BrewMonster.Scripts;
|
||
using BrewMonster.Scripts.Ornament;
|
||
using BrewMonster.Scripts.World;
|
||
using CSNetwork.GPDataType;
|
||
using System;
|
||
using UnityEngine;
|
||
using static BrewMonster.CECHostMove;
|
||
using WORD = System.UInt16;
|
||
|
||
namespace BrewMonster
|
||
{
|
||
public static class EC_CDR
|
||
{
|
||
// Cho phép CECHostMove gán mask theo scene (giữ linh hoạt nhưng không phá cấu trúc)
|
||
public static LayerMask BrushMask { get; set; } = 1 << 7;
|
||
public static LayerMask TerrainMask { get; set; } = 1 << 6;
|
||
public static LayerMask WaterMask { get; set; } = 1 << 8;
|
||
|
||
public static RaycastHit[] hits = new RaycastHit[5];
|
||
public static RaycastHit[] fHitsTerrain = new RaycastHit[5];
|
||
public static RaycastHit[] fHitsWater = new RaycastHit[5];
|
||
|
||
const float LOCAL_EPSILON = 1e-5f;
|
||
const float FLY_MAX_HEIGHT = 800.0f; // ·ÉÐеÄ×î´ó¸ß¶È£¡
|
||
|
||
const float VEL_EPSILON = 1e-4f;
|
||
const float DIST_EPSILON = 1e-4f;
|
||
const float NORMAL_EPSILON = 1e-2f;
|
||
const float VEL_MAX_SPEED = 200f;
|
||
const float VEL_REFLECT = 0.3f;
|
||
|
||
// change this array when some new submap can go!
|
||
static uint[,] available_maps =
|
||
{
|
||
{0, 0, 0, 0, 0, 0, 0, 0},
|
||
{0, 1, 1, 1, 1, 1, 1, 1},
|
||
{0, 1, 1, 1, 1, 1, 1, 1},
|
||
{0, 1, 1, 1, 1, 1, 1, 0},
|
||
{0, 1, 1, 1, 1, 1, 1, 0},
|
||
{0, 1, 1, 1, 1, 1, 1, 0},
|
||
{0, 1, 1, 1, 1, 1, 1, 0},
|
||
{0, 1, 1, 1, 1, 1, 1, 0},
|
||
{0, 1, 1, 1, 1, 1, 1, 0},
|
||
{0, 0, 0, 0, 0, 1, 1, 0},
|
||
{1, 1, 0, 0, 0, 0, 0, 0},
|
||
};
|
||
|
||
static uint[,] available_maps4x4 =
|
||
{
|
||
{0, 0, 0, 0},
|
||
{0, 1, 1, 0},
|
||
{0, 1, 1, 0},
|
||
{0, 0, 0, 0},
|
||
};
|
||
|
||
static uint[,] available_maps3x3 =
|
||
{
|
||
{0, 0, 0},
|
||
{0, 1, 0},
|
||
{0, 0, 0},
|
||
};
|
||
|
||
static uint[,] available_maps2x2 =
|
||
{
|
||
{1, 1},
|
||
{0, 0},
|
||
};
|
||
|
||
static uint[,] available_maps_137 =
|
||
{
|
||
{0, 0, 0, 0, 0, 0},
|
||
{0, 1, 1, 1, 0, 0},
|
||
{0, 0, 0, 0, 0, 0},
|
||
};
|
||
|
||
static uint[,] available_maps_161 =
|
||
{
|
||
{0, 0, 0, 0},
|
||
{0, 1, 1, 0},
|
||
{0, 0, 0, 0},
|
||
};
|
||
|
||
static uint[,] available_maps_162 =
|
||
{
|
||
{0, 0, 0, 0},
|
||
{0, 1, 1, 0},
|
||
{0, 0, 0, 0},
|
||
};
|
||
|
||
static uint[,] available_maps_163 =
|
||
{
|
||
{0, 0, 0, 0},
|
||
{0, 1, 1, 0},
|
||
{0, 1, 0, 0},
|
||
{0, 0, 0, 0},
|
||
};
|
||
|
||
//[Flags]
|
||
public static class CDR_EVN
|
||
{
|
||
public const int CDR_BRUSH = 0x1,
|
||
CDR_TERRAIN = 0x2,
|
||
CDR_WATER = 0x4;
|
||
}
|
||
|
||
static LayerMask UsedMask_Ground() => TerrainMask;
|
||
|
||
public static bool CollideWithEnv(ref env_trace_t pEnvTrc)
|
||
{
|
||
pEnvTrc.fFraction = 100.0f;
|
||
pEnvTrc.bStartSolid = false;
|
||
pEnvTrc.dwClsFlag = 0;
|
||
Vector3 vStart = EC_Utility.ToVector3(pEnvTrc.vStart);
|
||
Vector3 vExt = EC_Utility.ToVector3(pEnvTrc.vExt);
|
||
Vector3 vDelta = EC_Utility.ToVector3(pEnvTrc.vDelta);
|
||
Vector3 vTerStart = EC_Utility.ToVector3(pEnvTrc.vTerStart);
|
||
Vector3 vWatStart = EC_Utility.ToVector3(pEnvTrc.vWatStart);
|
||
Vector3 dir = Vector3.zero;
|
||
int countHits = 0;
|
||
|
||
if ((pEnvTrc.dwCheckFlag & CDR_EVN.CDR_BRUSH) == CDR_EVN.CDR_BRUSH)
|
||
{
|
||
dir = vDelta;
|
||
countHits = Physics.BoxCastNonAlloc(vStart, vExt, dir.normalized, hits, Quaternion.identity, vDelta.magnitude, 1 << 7);
|
||
if (countHits > 0 && Vector3.Distance(hits[0].point, vStart) > 0.0009f)
|
||
{
|
||
pEnvTrc.fFraction = (hits[0].distance - vExt.x) / vDelta.magnitude;
|
||
pEnvTrc.vHitNormal = EC_Utility.ToA3DVECTOR3(hits[0].normal);
|
||
pEnvTrc.dwClsFlag = CDR_EVN.CDR_BRUSH;
|
||
}
|
||
}
|
||
if ((pEnvTrc.dwCheckFlag & CDR_EVN.CDR_TERRAIN) == CDR_EVN.CDR_TERRAIN)
|
||
{
|
||
float fFractionTerrain = 100f;
|
||
dir = vTerStart + vDelta;
|
||
|
||
countHits = 0;
|
||
countHits = Physics.RaycastNonAlloc(vTerStart, dir.normalized, fHitsTerrain, vDelta.magnitude, 1 << 6);
|
||
if (countHits > 0 && Vector3.Distance(fHitsTerrain[0].point, vTerStart) > 0.0009f)
|
||
{
|
||
fFractionTerrain = (hits[0].distance) / vDelta.magnitude;
|
||
pEnvTrc.vHitNormal = EC_Utility.ToA3DVECTOR3(hits[0].normal);
|
||
pEnvTrc.dwClsFlag = CDR_EVN.CDR_TERRAIN;
|
||
}
|
||
if (fFractionTerrain < pEnvTrc.fFraction)
|
||
{
|
||
hits = fHitsTerrain;
|
||
pEnvTrc.fFraction = fFractionTerrain;
|
||
}
|
||
}
|
||
|
||
if ((pEnvTrc.dwCheckFlag & CDR_EVN.CDR_WATER) == CDR_EVN.CDR_WATER)
|
||
{
|
||
float fFraction = 100f;
|
||
Vector3 vWatNormal = Vector3.zero;
|
||
bool bStart = false;
|
||
if (pEnvTrc.bWaterSolid && (vDelta.y > LOCAL_EPSILON))
|
||
{
|
||
fFraction = 0;
|
||
}
|
||
else if (!pEnvTrc.bWaterSolid && (vDelta.y < -LOCAL_EPSILON))
|
||
{
|
||
fFraction = 0;
|
||
}
|
||
else if (vDelta.y < LOCAL_EPSILON && vDelta.y > -LOCAL_EPSILON)
|
||
{ //parallel the water plane
|
||
fFraction = 0;
|
||
}
|
||
else
|
||
{
|
||
float h0 = 0f;
|
||
int countHits0 = Physics.RaycastNonAlloc(vWatStart, Vector3.down, fHitsWater, 1000f, 1 << 8);
|
||
if (countHits0 > 0)
|
||
{
|
||
h0 = fHitsWater[0].point.y;
|
||
}
|
||
float h1 = 0f;
|
||
countHits0 = Physics.RaycastNonAlloc((vWatStart + vDelta), Vector3.down, fHitsWater, 1000f, 1 << 8);
|
||
if(countHits0 > 0)
|
||
{
|
||
h1 = fHitsWater[0].point.y;
|
||
}
|
||
float hWater = Mathf.Max(h0, h1);
|
||
vWatNormal = Vector3.up;
|
||
float t = (hWater - vWatStart.y) / pEnvTrc.vDelta.y;
|
||
if (t >= 0.0f && t <= 1.0f)
|
||
{
|
||
fFraction = Mathf.Max(0.0f, t - 1E-2f);
|
||
if (pEnvTrc.bWaterSolid && h0 > vStart.y)
|
||
{
|
||
fFraction = 0.0f;
|
||
bStart = true;
|
||
}
|
||
|
||
if (!pEnvTrc.bWaterSolid && h0 < vStart.y)
|
||
{
|
||
fFraction = 0.0f;
|
||
bStart = true;
|
||
}
|
||
|
||
if(fFraction < pEnvTrc.fFraction)
|
||
{
|
||
pEnvTrc.fFraction = fFraction;
|
||
pEnvTrc.vHitNormal = EC_Utility.ToA3DVECTOR3(vWatNormal);
|
||
pEnvTrc.bStartSolid = bStart;
|
||
pEnvTrc.dwClsFlag = CDR_EVN.CDR_WATER;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return (pEnvTrc.fFraction < 1.0f + 1E-4f);
|
||
}
|
||
|
||
// == Thay RetrieveSupportPlane (C++) bằng Raycast xuống ==
|
||
static bool DoGroundProbe(Vector3 vStart, Vector3 vExt, float fDeltaY, LayerMask mask,
|
||
out Vector3 vEnd, out Vector3 vHitNormal, out bool bSupport,
|
||
float skin = 0.01f)
|
||
{
|
||
vEnd = vStart;
|
||
vHitNormal = Vector3.zero;
|
||
bSupport = false;
|
||
|
||
float dist = fDeltaY + vExt.y;//Mathf.Max(fDeltaY, 0f) + vExt.y;
|
||
Vector3 origin = vStart /*+ Vector3.down * vExt.y*/;
|
||
int countHits = Physics.RaycastNonAlloc(origin, Vector3.down, hits, dist, mask);
|
||
if (countHits > 0)
|
||
{
|
||
vHitNormal = hits[0].normal;
|
||
vEnd = new Vector3(vStart.x, hits[0].point.y + vExt.y, vStart.z);
|
||
bSupport = (vHitNormal.y >= 0f);
|
||
return true;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// ======= STATIC OnGroundMove – GIỮ NGUYÊN VAI TRÒ TOÀN CỤC (C API) =======
|
||
public static void OnGroundMove(ref CDR_INFO CDRInfo)
|
||
{
|
||
|
||
CDRInfo.fMoveDist = 0.0f;
|
||
|
||
bool bFreeFall = (CDRInfo.vTPNormal.y < CDRInfo.fSlopeThresh);
|
||
if (CDRInfo.fYVel < VEL_EPSILON && CDRInfo.fYVel > -VEL_EPSILON && CDRInfo.fSpeed < VEL_EPSILON && CDRInfo.fSpeed > -VEL_EPSILON && !bFreeFall)
|
||
return;
|
||
|
||
float fYVel = CDRInfo.fYVel;
|
||
bool bJump = (fYVel > 0.5f);
|
||
|
||
Vector3 vVelocity = CDRInfo.fSpeed * EC_Utility.ToVector3(CDRInfo.vXOZVelDir) + fYVel * Vector3.up;
|
||
|
||
if (bFreeFall)
|
||
{
|
||
vVelocity += -CDRInfo.fGravityAccel * CDRInfo.t * Vector3.up;
|
||
fYVel += -CDRInfo.fGravityAccel * CDRInfo.t;
|
||
}
|
||
A3DVECTOR3 vVelDir = EC_Utility.ToA3DVECTOR3(vVelocity);
|
||
float fVelSpeed = vVelDir.Normalize();
|
||
if (!bFreeFall)
|
||
{
|
||
if (fVelSpeed > VEL_MAX_SPEED)
|
||
{
|
||
fVelSpeed = VEL_MAX_SPEED;
|
||
}
|
||
}
|
||
vVelocity = EC_Utility.ToVector3(vVelDir) * fVelSpeed;
|
||
float dtp = DotProduct(vVelDir, CDRInfo.vTPNormal);
|
||
if (dtp < 0f || !bJump)
|
||
{
|
||
vVelocity = EC_Utility.ToVector3((vVelDir -(CDRInfo.vTPNormal) * dtp - (CDRInfo.vTPNormal) * dtp * 0.01f) * fVelSpeed);
|
||
}
|
||
|
||
CDRInfo.vAbsVelocity = EC_Utility.ToA3DVECTOR3(vVelocity);
|
||
|
||
Vector3 vStart = EC_Utility.ToVector3(CDRInfo.vCenter);
|
||
Vector3 vExt = EC_Utility.ToVector3(CDRInfo.vExtent);
|
||
float fTime = CDRInfo.t;
|
||
|
||
Vector3 vDelta, vNormal = Vector3.zero, vFinalPos = vStart;
|
||
bool bPull = false;
|
||
bool bTryPull = false;
|
||
|
||
int nTry = 0;
|
||
LayerMask mask = UsedMask_Ground();
|
||
|
||
env_trace_t trcInfo = new env_trace_t();
|
||
trcInfo.dwCheckFlag = CDR_EVN.CDR_TERRAIN | CDR_EVN.CDR_BRUSH;
|
||
trcInfo.vExt = CDRInfo.vExtent;
|
||
|
||
int countHits = 0;
|
||
while (nTry < 1)
|
||
{
|
||
vDelta = vVelocity * (fTime);
|
||
float fDeltaDist = vDelta.magnitude;
|
||
Vector3 posFoot = vStart - Vector3.up * vExt.y;
|
||
vFinalPos = vStart;
|
||
if (fDeltaDist < DIST_EPSILON) break;
|
||
countHits = Physics.RaycastNonAlloc(vStart, (Vector3.down * vExt.y).normalized, hits, vExt.y, mask);
|
||
if (countHits > 0)
|
||
{
|
||
if (hits[0].point.y >= posFoot.y)
|
||
{
|
||
posFoot.y = hits[0].point.y;
|
||
}
|
||
}
|
||
//Debug.LogError("fDeltaDist = " + fDeltaDist + " vVelocity = " + vVelocity + " fTime = " + fTime + " speed = " + (fDeltaDist / fTime) + " posFoot + vDelta = " + (posFoot + vDelta) + " posFoot = " + posFoot);
|
||
countHits = Physics.RaycastNonAlloc(posFoot, (posFoot + vDelta).normalized, hits, fDeltaDist, mask);
|
||
bool bClear = !(countHits > 0);
|
||
nTry++;
|
||
if (bClear || (countHits > 0 && Vector3.Distance(hits[0].point, posFoot) < 0.0009f)) // Is 0.0009f the tolerance used to check if two points are the same?
|
||
{
|
||
vFinalPos = vStart + vDelta;
|
||
CDRInfo.fMoveDist += fDeltaDist;
|
||
break;
|
||
}
|
||
vStart = hits[0].point + Vector3.up * vExt.y;
|
||
|
||
vFinalPos = vStart;
|
||
countHits = Physics.RaycastNonAlloc(vStart, (Vector3.down).normalized, hits, vExt.y, mask);
|
||
if (countHits > 0)
|
||
{
|
||
vNormal = hits[0].normal;
|
||
}
|
||
else
|
||
{
|
||
vNormal = Vector3.zero;
|
||
}
|
||
|
||
// Step-up (giữ tinh thần bản gốc)
|
||
if (!bFreeFall && !bTryPull && !bJump)
|
||
{
|
||
posFoot = vStart - Vector3.up * vExt.y;
|
||
countHits = Physics.RaycastNonAlloc(vStart, (vStart + Vector3.down).normalized, hits, vExt.y, mask);
|
||
if (countHits > 0)
|
||
{
|
||
if (hits[0].point.y > posFoot.y)
|
||
{
|
||
posFoot.y = hits[0].point.y;
|
||
}
|
||
}
|
||
countHits = Physics.RaycastNonAlloc(posFoot, (Vector3.up).normalized, hits, CDRInfo.fStepHeight, mask);
|
||
bPull = !(countHits > 0);
|
||
if (bPull)
|
||
{
|
||
vStart += Vector3.up * CDRInfo.fStepHeight;
|
||
posFoot = vStart - Vector3.up * vExt.y;
|
||
fDeltaDist = (vVelocity.normalized).magnitude;
|
||
countHits = Physics.RaycastNonAlloc(posFoot, (posFoot + vVelocity).normalized, hits, fDeltaDist, mask);
|
||
bool bMove = !(countHits > 0);
|
||
|
||
if (!bMove)
|
||
{
|
||
fDeltaDist *= Vector3.Distance(vFinalPos, (hits[0].point + Vector3.up * vExt.y)) / fDeltaDist;
|
||
vFinalPos = hits[0].point + Vector3.up * vExt.y;
|
||
}
|
||
else
|
||
{
|
||
vFinalPos += vDelta;
|
||
}
|
||
if (fDeltaDist < (vExt.x * vExt.x * 4))
|
||
{
|
||
vFinalPos.y -= CDRInfo.fStepHeight;
|
||
bPull = false;
|
||
}
|
||
}
|
||
bTryPull = true;
|
||
}
|
||
|
||
if (!bPull)
|
||
{
|
||
fVelSpeed = Normalize(EC_Utility.ToA3DVECTOR3(vVelocity), ref vVelDir);
|
||
fVelSpeed *= (1 - nTry * 0.1f);
|
||
dtp = Vector3.Dot(vNormal, EC_Utility.ToVector3(vVelDir));
|
||
float fRelSpeed = Mathf.Min(fVelSpeed, 5.0f);
|
||
|
||
if (dtp >= 0f && dtp < 1e-4f)
|
||
{
|
||
vVelocity += vNormal * VEL_REFLECT * fRelSpeed;
|
||
}
|
||
else
|
||
{
|
||
vVelocity = (EC_Utility.ToVector3(vVelDir) - vNormal * dtp) * fVelSpeed - vNormal * dtp * VEL_REFLECT * fRelSpeed;
|
||
}
|
||
|
||
if (fYVel > VEL_EPSILON)
|
||
{
|
||
if (vNormal.y >= CDRInfo.fSlopeThresh || vNormal.y < -NORMAL_EPSILON)
|
||
{
|
||
fYVel = 0f;
|
||
}
|
||
}
|
||
else if (fYVel < -VEL_EPSILON)
|
||
{
|
||
if (vNormal.y >= CDRInfo.fSlopeThresh)
|
||
{
|
||
fYVel = 0f;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// “vertical ground trace” – thay RetrieveSupportPlane
|
||
Vector3 vTPNormal = Vector3.zero;
|
||
Vector3 vFinal = vFinalPos;
|
||
//mask = BrushMask;
|
||
|
||
float downDist = 0.3f;
|
||
if (bPull) downDist = CDRInfo.fStepHeight + 0.1f;
|
||
if (bJump) downDist = 0.0f;
|
||
|
||
if (downDist > 0f)
|
||
{
|
||
if (!DoGroundProbe(vFinalPos, vExt, downDist, mask, out Vector3 vEnd, out Vector3 groundNormal, out bool bSupport))
|
||
{
|
||
CDRInfo.fMoveDist = 0f;
|
||
CDRInfo.vTPNormal = new A3DVECTOR3(0, 1, 0);
|
||
return;
|
||
}
|
||
|
||
if (bSupport)
|
||
{
|
||
vFinal = vEnd;
|
||
if (!bJump)
|
||
{
|
||
vTPNormal = groundNormal;
|
||
}
|
||
}
|
||
}
|
||
|
||
if ((vTPNormal.y >= CDRInfo.fSlopeThresh && fYVel < 0.0f) || (!bJump && fYVel > 0.0f))
|
||
{
|
||
fYVel = 0.0f;
|
||
}
|
||
|
||
vDelta = vFinalPos - EC_Utility.ToVector3(CDRInfo.vCenter);
|
||
CDRInfo.fMoveDist = vDelta.magnitude;
|
||
|
||
CDRInfo.vCenter = EC_Utility.ToA3DVECTOR3(vFinal);
|
||
CDRInfo.fYVel = fYVel;
|
||
CDRInfo.vTPNormal = EC_Utility.ToA3DVECTOR3(vTPNormal);
|
||
}
|
||
|
||
// Get normalize
|
||
static float Normalize(A3DVECTOR3 vIn, ref A3DVECTOR3 vOut)
|
||
{
|
||
float fMag = vIn.Magnitude();
|
||
if (fMag < 1e-6 && fMag > -1e-6)
|
||
{
|
||
vOut.Clear();
|
||
fMag = 0.0f;
|
||
}
|
||
else
|
||
{
|
||
float f = 1.0f / fMag;
|
||
vOut = vIn * f;
|
||
}
|
||
return fMag;
|
||
}
|
||
|
||
static bool SegmentTriangleIntersect(A3DVECTOR3 vStart, A3DVECTOR3 vDelta, A3DVECTOR3[] vert, ref float fFraction, bool bCull)
|
||
{
|
||
float dist = 0f;
|
||
A3DVECTOR3 vDir = new A3DVECTOR3(vDelta);
|
||
|
||
dist = vDir.Normalize();
|
||
if (dist < 1E-5f)
|
||
{
|
||
//assert(0 && "too small dist!");
|
||
fFraction = 0.0f;
|
||
return true;
|
||
}
|
||
|
||
float t = 0f, u = 0f, v = 0f;
|
||
|
||
if (RayTriangleIntersect(vStart, vDir, vert, ref t, ref u, ref v, bCull) && (t >= 0.0f) && (t <= dist))
|
||
{
|
||
//fFraction = t/dist;
|
||
//fFraction = a_Max( 0.0f, fFraction -1E-4f); //put back
|
||
fFraction = (t - 5E-4f) / dist;
|
||
AAssist.a_ClampFloor(ref fFraction, 0.0f);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
static bool RayTriangleIntersect(A3DVECTOR3 vOrigin, A3DVECTOR3 vDir, A3DVECTOR3[] vert, ref float t, ref float u, ref float v, bool bCull)
|
||
{
|
||
// find vectors for two edges sharing vert0
|
||
A3DVECTOR3 edge1 = vert[1] - vert[0];
|
||
A3DVECTOR3 edge2 = vert[2] - vert[0];
|
||
// begin calculating determinant - also used to calculate U parameter
|
||
A3DVECTOR3 pvec = A3DVECTOR3.CrossProduct(vDir, edge2);
|
||
// if determinant is near zero, ray lies in plane of triangle
|
||
float det = A3DVECTOR3.DotProduct(edge1, pvec);
|
||
if (bCull)
|
||
{
|
||
if (det < LOCAL_EPSILON)
|
||
return false;
|
||
// From here, det is > 0.
|
||
// Calculate distance from vert0 to ray origin
|
||
A3DVECTOR3 tvec = vOrigin - vert[0];
|
||
// Calculate U parameter and test bounds
|
||
u = A3DVECTOR3.DotProduct(tvec, pvec);
|
||
if ((u < 0.0f) || (u > det))
|
||
return false;
|
||
|
||
// prepare to test V parameter
|
||
A3DVECTOR3 qvec = A3DVECTOR3.CrossProduct(tvec, edge1);
|
||
// calculate V parameter and test bounds
|
||
v = A3DVECTOR3.DotProduct(vDir, qvec);
|
||
if ((v < 0.0f) || (u + v > det))
|
||
return false;
|
||
|
||
// calculate t, ray intersects triangle
|
||
t = A3DVECTOR3.DotProduct(edge2, qvec);
|
||
// Det > 0 so we can early exit here
|
||
// Intersection point is valid if distance is positive
|
||
// (else it can just be a face behind the orig point)
|
||
if (t < 0.0f)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
float OneOverDet = 1.0f / det;
|
||
|
||
|
||
t *= OneOverDet;
|
||
u *= OneOverDet;
|
||
v *= OneOverDet;
|
||
}
|
||
|
||
else
|
||
{
|
||
if (det > -LOCAL_EPSILON && det < LOCAL_EPSILON)
|
||
return false;
|
||
float OneOverDet = 1.0f / det;
|
||
// Calculate distance from vert0 to ray origin
|
||
A3DVECTOR3 tvec = vOrigin - vert[0];
|
||
// calculate U parameter and test bounds
|
||
u = A3DVECTOR3.DotProduct(tvec, pvec) * OneOverDet;
|
||
if ((u < 0.0f) || (u > 1.0f))
|
||
return false;
|
||
// prepare to test V parameter
|
||
A3DVECTOR3 qvec = A3DVECTOR3.CrossProduct(tvec, edge1);
|
||
// calculate V parameter and test bounds
|
||
v = A3DVECTOR3.DotProduct(vDir, qvec) * OneOverDet;
|
||
if ((v < 0.0f) || (u + v > 1.0f))
|
||
return false;
|
||
// calculate t, ray intersects triangle
|
||
t = A3DVECTOR3.DotProduct(edge2, qvec) * OneOverDet;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public static bool CollideWithTerrain(A3DVECTOR3 vStart, A3DVECTOR3 vDelta, ref float fFraction, ref A3DVECTOR3 vHitNormal, ref bool bStart)
|
||
{
|
||
|
||
CECWorld pWorld = CECWorld.Instance; //g_pGame.GetGameRun().GetWorld();
|
||
A3DTerrain2 pTerrain = pWorld.GetTerrain();
|
||
bStart = false;
|
||
float h1 = pTerrain.GetPosHeight(vStart, ref vHitNormal);
|
||
if (h1 > vStart.y + 1E-4f)
|
||
{//start under terrain
|
||
bStart = true;
|
||
fFraction = 0.0f;
|
||
return true;
|
||
}
|
||
|
||
int nWid, nHei; // in grid, 2 meters
|
||
float fMag = vDelta.Magnitude();
|
||
|
||
nWid = (int)Math.Ceiling(fMag / 2.0f);
|
||
nWid = Math.Max(3, nWid);
|
||
nHei = nWid;
|
||
int nTriangles = nWid * nHei * 2;
|
||
A3DVECTOR3[] pVerts = new A3DVECTOR3[(nWid + 1) * (nHei + 1)];
|
||
//assert(pVerts != NULL);
|
||
//memset(pVerts, 0, sizeof(A3DVECTOR3)* (nWid + 1) * (nHei + 1));
|
||
WORD[] pIndices = new WORD[nTriangles * 3];
|
||
//assert(pIndices != NULL);
|
||
//memset(pIndices, 0, sizeof(WORD)* nTriangles * 3);
|
||
if (!pTerrain.GetFacesOfArea(vStart, nWid, nHei, ref pVerts, ref pIndices))
|
||
{
|
||
//a_freetemp(pVerts);
|
||
// a_freetemp(pIndices);
|
||
return false;
|
||
}
|
||
int i;
|
||
A3DVECTOR3[] vert = new A3DVECTOR3[3];
|
||
//@note : Here init the fraction. By Kuiwu[9/10/2005]
|
||
fFraction = 100.0f;
|
||
float tmpFraction = fFraction;
|
||
|
||
for (i = 0; i < nTriangles; i++)
|
||
{
|
||
vert[0] = pVerts[pIndices[i * 3]];
|
||
vert[1] = pVerts[pIndices[i * 3 + 1]];
|
||
vert[2] = pVerts[pIndices[i * 3 + 2]];
|
||
//A3DVECTOR3 vPt;
|
||
|
||
//@note: Tomas Moller's JGT code : By Kuiwu[9/10/2005]
|
||
//@note: discard the engine version because it put back the hit point too much. By Kuiwu[13/10/2005]
|
||
// if(CLS_RayToTriangle(vStart, vDelta, *vert[0], *vert[1], *vert[2], vPt, true, &tmpFraction)
|
||
// && (tmpFraction <= 1.0f) && (tmpFraction < fFraction))
|
||
if (SegmentTriangleIntersect(vStart, vDelta, vert, ref tmpFraction, true) && (tmpFraction < fFraction))
|
||
{
|
||
//get the triangle normal
|
||
A3DVECTOR3 vEdge1 = vert[1] - vert[0];
|
||
A3DVECTOR3 vEdge2 = vert[2] - vert[0];
|
||
vHitNormal = A3DVECTOR3.CrossProduct(vEdge1, vEdge2);
|
||
vHitNormal.Normalize();
|
||
|
||
//@note : may be redundant, but to assure. By Kuiwu[17/10/2005]
|
||
A3DVECTOR3 vDir = new A3DVECTOR3();
|
||
A3DVECTOR3.Normalize(vDelta, out vDir);
|
||
if (A3DVECTOR3.DotProduct(vHitNormal, vDir) > 0.01f)
|
||
{//leave the hit plane
|
||
//assert(0 && "hit a plane with same direction!");
|
||
continue;
|
||
}
|
||
|
||
fFraction = Math.Max(0.0f, tmpFraction);
|
||
}
|
||
|
||
}
|
||
//a_freetemp(pVerts);
|
||
// a_freetemp(pIndices);
|
||
|
||
return (fFraction <= 1.0f);
|
||
|
||
}
|
||
|
||
public static bool AABBCollideWithBrush(ref BrushTraceInfo pInfo)
|
||
{
|
||
//TO DO: fix later
|
||
return false;
|
||
//CECWorld pWorld = CECWorld.Instance; //g_pGame.GetGameRun().GetWorld();
|
||
|
||
//CECOrnamentMan pOrnMan = pWorld.GetOrnamentMan();
|
||
//bool bBrush = pOrnMan.TraceWithBrush(ref pInfo);
|
||
|
||
//BrushTraceInfo info = pInfo;
|
||
//CECMatterMan pMatterMan = pWorld.GetMatterMan();
|
||
//if (pMatterMan.TraceWithBrush(ref info) && info.fFraction < pInfo.fFraction)
|
||
//{
|
||
// pInfo = info;
|
||
// bBrush = true;
|
||
//}
|
||
|
||
//info = pInfo;
|
||
//CECNPCMan pNPCMan = pWorld.GetNPCMan();
|
||
//if (pNPCMan.TraceWithBrush(ref info) && info.fFraction < pInfo.fFraction)
|
||
//{
|
||
// pInfo = info;
|
||
// bBrush = true;
|
||
//}
|
||
|
||
//return bBrush;
|
||
}
|
||
|
||
public static void OnAirMove(ref ON_AIR_CDR_INFO awmInfo)
|
||
{
|
||
//assert(0 && "Not ready yet");
|
||
|
||
if (awmInfo.bOnAir)
|
||
{
|
||
AirMove(ref awmInfo);
|
||
}
|
||
else
|
||
{
|
||
WaterMove(ref awmInfo);
|
||
}
|
||
}
|
||
|
||
static void AirMove(ref ON_AIR_CDR_INFO awmInfo)
|
||
{
|
||
float DIST_EPSILON = 1e-4f;
|
||
int MAX_TRY = 1;
|
||
float VEL_REFLECT = 0.0f;
|
||
float fTime = awmInfo.t;
|
||
//@todo : is it necessary to clamp the speed? By Kuiwu[20/9/2005]
|
||
float fSpeed = awmInfo.fSpeed;
|
||
if (fSpeed * fTime < DIST_EPSILON)
|
||
{
|
||
//@todo : set the output param. By Kuiwu[20/9/2005]
|
||
return;
|
||
}
|
||
|
||
A3DVECTOR3 vStart = new A3DVECTOR3(awmInfo.vCenter);
|
||
A3DVECTOR3 vExt = new A3DVECTOR3(awmInfo.vExtent);
|
||
A3DVECTOR3 vVelDir = new A3DVECTOR3(awmInfo.vVelDir);
|
||
float dtp = 0f;
|
||
A3DVECTOR3 vVelocity = new A3DVECTOR3(vVelDir * fSpeed);
|
||
|
||
if ((dtp = A3DVECTOR3.DotProduct(vVelDir, awmInfo.vTPNormal)) < 0.0f)
|
||
{
|
||
|
||
//vVelocity = (vVelDir - awmInfo.vTPNormal * dtp - awmInfo.vTPNormal*dtp * 0.01f) * fSpeed;
|
||
vVelocity = (vVelDir - awmInfo.vTPNormal * dtp) * fSpeed;
|
||
}
|
||
|
||
A3DVECTOR3 vDelta = new A3DVECTOR3(vVelocity * fTime),
|
||
vNormal = new A3DVECTOR3(),
|
||
vFinalPos = new A3DVECTOR3(vStart);
|
||
int nTry = 0;
|
||
bool bClear = true;
|
||
env_trace_t trcInfo = new env_trace_t();
|
||
trcInfo.bWaterSolid = true;
|
||
trcInfo.dwCheckFlag = CDR_EVN.CDR_TERRAIN | CDR_EVN.CDR_BRUSH | CDR_EVN.CDR_WATER;
|
||
trcInfo.vExt = vExt;
|
||
RaycastHit hit;
|
||
while (nTry < MAX_TRY)
|
||
{
|
||
if (vDelta.SquaredMagnitude() < DIST_EPSILON)
|
||
{
|
||
break;
|
||
}
|
||
trcInfo.vStart = vStart;
|
||
trcInfo.vDelta = vDelta;
|
||
trcInfo.vTerStart = vStart;
|
||
trcInfo.vTerStart.y -= vExt.y;
|
||
trcInfo.vWatStart = vStart;
|
||
trcInfo.vWatStart.y -= vExt.y;
|
||
//bClear = !CollideWithEnv(&trcInfo);
|
||
bClear = !Physics.BoxCast(EC_Utility.ToVector3(vStart),
|
||
EC_Utility.ToVector3(vExt),
|
||
EC_Utility.ToVector3(vStart + vVelDir).normalized,
|
||
out hit,
|
||
Quaternion.identity,
|
||
EC_Utility.ToVector3(vDelta).magnitude,
|
||
UsedMask_Ground());
|
||
++nTry;
|
||
if (bClear)
|
||
{
|
||
vFinalPos = vStart + vDelta;
|
||
}
|
||
else
|
||
{
|
||
vFinalPos = EC_Utility.ToA3DVECTOR3(hit.point);
|
||
vNormal = EC_Utility.ToA3DVECTOR3(hit.normal);
|
||
}
|
||
//vStart += vDelta * trcInfo.fFraction;
|
||
//vFinalPos = vStart;
|
||
//fTime -= fTime * trcInfo.fFraction;
|
||
//vNormal = trcInfo.vHitNormal;
|
||
//fSpeed = A3DVECTOR3.Normalize(vVelocity,out vVelDir);
|
||
//fSpeed *= (1 - nTry * 0.1f);
|
||
//dtp = A3DVECTOR3.DotProduct(vNormal, vVelDir);
|
||
//vVelocity = (vVelDir - vNormal * dtp - vNormal * dtp * VEL_REFLECT) * fSpeed;
|
||
//vDelta = vVelocity * fTime;
|
||
|
||
}
|
||
|
||
//@note : prevent moving to the invalid area. By Kuiwu[20/9/2005]
|
||
if (!IsPosInAvailableMap(vFinalPos))
|
||
{
|
||
//@todo : set some flag to notify the caller? By Kuiwu[20/9/2005]
|
||
return;
|
||
}
|
||
//too high
|
||
if (vFinalPos.y > FLY_MAX_HEIGHT - 2.0f)
|
||
{
|
||
return;
|
||
}
|
||
|
||
//see if meet height thresh
|
||
Vector3 posVStart = EC_Utility.ToVector3(vFinalPos);
|
||
float fDeltaY = awmInfo.fHeightThresh + 0.1f;
|
||
|
||
if (!Physics.Raycast(posVStart,
|
||
(posVStart + Vector3.down * fDeltaY).normalized,
|
||
out hit,
|
||
fDeltaY,
|
||
UsedMask_Ground()))
|
||
{
|
||
awmInfo.vCenter = vFinalPos;
|
||
awmInfo.vTPNormal = vNormal;
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
vFinalPos = EC_Utility.ToA3DVECTOR3(hit.point);
|
||
vNormal = EC_Utility.ToA3DVECTOR3(hit.normal);
|
||
awmInfo.vCenter = vFinalPos;
|
||
awmInfo.vTPNormal = vNormal;
|
||
}
|
||
}
|
||
|
||
static void WaterMove(ref ON_AIR_CDR_INFO awmInfo)
|
||
{
|
||
float fTime = awmInfo.t;
|
||
//@todo : is it necessary to clamp the speed? By Kuiwu[20/9/2005]
|
||
float fSpeed = awmInfo.fSpeed;
|
||
if (fSpeed * fTime < DIST_EPSILON)
|
||
{
|
||
//@todo : set the output param. By Kuiwu[20/9/2005]
|
||
return;
|
||
}
|
||
|
||
A3DVECTOR3 vStart = awmInfo.vCenter;
|
||
A3DVECTOR3 vExt = awmInfo.vExtent;
|
||
A3DVECTOR3 vVelDir = awmInfo.vVelDir;
|
||
float dtp = 0f;
|
||
A3DVECTOR3 vVelocity = vVelDir* fSpeed;
|
||
|
||
if ((dtp = DotProduct(vVelDir, awmInfo.vTPNormal)) < 0.0f)
|
||
{
|
||
vVelocity = (vVelDir - awmInfo.vTPNormal * dtp - awmInfo.vTPNormal * dtp * 0.01f) * fSpeed;
|
||
}
|
||
|
||
A3DVECTOR3 vDelta = (vVelocity* fTime), vNormal, vFinalPos = vStart;
|
||
int nTry = 0;
|
||
bool bClear = true;
|
||
env_trace_t trcInfo = new env_trace_t();
|
||
trcInfo.bWaterSolid = false;
|
||
trcInfo.dwCheckFlag = CDR_EVN.CDR_TERRAIN | CDR_EVN.CDR_BRUSH | CDR_EVN.CDR_WATER;
|
||
trcInfo.vExt = vExt;
|
||
|
||
while (nTry < 1)
|
||
{
|
||
if (vDelta.SquaredMagnitude() < DIST_EPSILON)
|
||
{
|
||
break;
|
||
}
|
||
trcInfo.vStart = vStart;
|
||
trcInfo.vDelta = vDelta;
|
||
trcInfo.vTerStart = vStart;
|
||
trcInfo.vTerStart.y -= vExt.y;
|
||
trcInfo.vWatStart = vStart;
|
||
trcInfo.vWatStart.y += awmInfo.fUnderWaterDistThresh; //shoulder
|
||
|
||
|
||
bClear = !CollideWithEnv(ref trcInfo);
|
||
|
||
++nTry;
|
||
if (bClear || (trcInfo.bStartSolid && ((trcInfo.dwClsFlag & CDR_EVN.CDR_WATER) != CDR_EVN.CDR_WATER)))
|
||
{
|
||
vFinalPos = vStart + vDelta;
|
||
break;
|
||
}
|
||
vStart += vDelta * trcInfo.fFraction;
|
||
fTime -= fTime * trcInfo.fFraction;
|
||
fSpeed = Normalize(vVelocity,ref vVelDir);
|
||
fSpeed *= (1 - nTry * 0.1f);
|
||
if ((trcInfo.dwClsFlag & CDR_EVN.CDR_WATER) == CDR_EVN.CDR_WATER)
|
||
{
|
||
if (trcInfo.bStartSolid)
|
||
{//rescue from solid
|
||
//@note : it may cause some problems. By Kuiwu[11/10/2005]
|
||
float fHWater = 0f;
|
||
int countHits0 = Physics.RaycastNonAlloc(EC_Utility.ToVector3(vStart), Vector3.down, fHitsWater, 1000f, 1 << 8);
|
||
if(countHits0 > 0)
|
||
{
|
||
fHWater = fHitsWater[0].point.y;
|
||
}
|
||
vStart.y = fHWater;
|
||
}
|
||
vVelDir.y = 0.0f;
|
||
vVelocity = vVelDir * fSpeed;
|
||
}
|
||
else
|
||
{
|
||
vNormal = trcInfo.vHitNormal;
|
||
dtp = DotProduct(vNormal, vVelDir);
|
||
vVelocity = (vVelDir - vNormal * dtp - vNormal * dtp * VEL_REFLECT) * fSpeed;
|
||
}
|
||
vDelta = vVelocity * fTime;
|
||
vFinalPos = vStart;
|
||
|
||
}
|
||
|
||
//@note : prevent moving to the invalid area. By Kuiwu[20/9/2005]
|
||
if (!IsPosInAvailableMap(vFinalPos))
|
||
{
|
||
//@todo : set some flag to notify the caller? By Kuiwu[20/9/2005]
|
||
return;
|
||
}
|
||
|
||
|
||
//see if meet height thresh
|
||
Vector3 v3Start = EC_Utility.ToVector3(vFinalPos);
|
||
Vector3 v3Ext = EC_Utility.ToVector3(vExt);
|
||
float fDeltaY = awmInfo.fHeightThresh + 0.1f;
|
||
|
||
if (!DoGroundProbe(v3Start, v3Ext, fDeltaY, UsedMask_Ground(), out Vector3 vEnd, out Vector3 groundNormal, out bool bSupport))
|
||
{
|
||
return;
|
||
}
|
||
|
||
A3DVECTOR3 vTpNormal = new A3DVECTOR3(0.0f);
|
||
A3DVECTOR3 vOverTp = vFinalPos;
|
||
bool bAdjust = false;
|
||
awmInfo.bMeetHeightThresh = true;
|
||
|
||
if (bSupport)
|
||
{
|
||
bAdjust = true;
|
||
vOverTp = EC_Utility.ToA3DVECTOR3(vEnd);
|
||
vTpNormal = EC_Utility.ToA3DVECTOR3(groundNormal);
|
||
}
|
||
|
||
if (bAdjust && (vOverTp.y + awmInfo.fHeightThresh > vFinalPos.y))
|
||
{
|
||
float fHWater = 0f;
|
||
int countHits = Physics.RaycastNonAlloc(EC_Utility.ToVector3(vFinalPos), Vector3.down, fHitsWater, 1000f, 1 << 8);
|
||
if (countHits > 0)
|
||
{
|
||
fHWater = fHitsWater[0].point.y;
|
||
}
|
||
|
||
float fAllow = fHWater - awmInfo.fUnderWaterDistThresh - vFinalPos.y;
|
||
if (fAllow > 1E-4f)
|
||
{
|
||
vDelta.Clear();
|
||
vDelta.y = vOverTp.y + awmInfo.fHeightThresh - vFinalPos.y;
|
||
fAllow = EC_Utility.a_Min(fAllow, 0.15f);
|
||
AAssist.a_ClampRoof(ref vDelta.y, fAllow);
|
||
Vector3 dir = EC_Utility.ToVector3(vDelta);
|
||
countHits = Physics.BoxCastNonAlloc(v3Start, v3Ext, dir.normalized, hits, Quaternion.identity, dir.magnitude, 1 << 7);
|
||
if (countHits > 0)
|
||
{
|
||
vFinalPos += EC_Utility.ToA3DVECTOR3(hits[0].point);
|
||
}
|
||
else
|
||
{
|
||
vFinalPos += vDelta;
|
||
}
|
||
awmInfo.bMeetHeightThresh = (vFinalPos.y - vOverTp.y > awmInfo.fHeightThresh);
|
||
}
|
||
}
|
||
|
||
awmInfo.vCenter = vFinalPos;
|
||
awmInfo.vTPNormal = vTpNormal;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
// Note by wenfeng, 05-09-16
|
||
// This function is only for the Big world but not applicable for the
|
||
// Instance world!
|
||
//
|
||
//////////////////////////////////////////////////////////////////////////
|
||
static bool IsPosInAvailableMap(A3DVECTOR3 vPos)
|
||
{
|
||
float x, z;
|
||
int su, sv;
|
||
|
||
//bool bFlag = true;
|
||
CECWorld pWorld = EC_Game.GetGameRun().GetWorld();
|
||
if (pWorld != null)
|
||
{
|
||
int idInst = pWorld.GetInstanceID();
|
||
CECInstance pInst = EC_Game.GetGameRun().GetInstance(idInst);
|
||
if (pInst == null)
|
||
return false;
|
||
|
||
x = vPos.x + pInst.GetColNum() * 512.0f;
|
||
z = pInst.GetRowNum() * 512.0f - vPos.z;
|
||
su = (int)(x / 1024.0f);
|
||
sv = (int)(z / 1024.0f);
|
||
|
||
if (su >= pInst.GetColNum() || su < 0 ||
|
||
|
||
sv >= pInst.GetRowNum() || sv < 0)
|
||
return false;
|
||
|
||
switch (idInst)
|
||
{
|
||
case 1:
|
||
return available_maps[sv, su] != 0 && vPos.x <= 3877.0f; // ½ûÖ¹ëÊ×åÁÙ½ü¿´µ½µØÍ¼±ßÔµ
|
||
|
||
case 121:
|
||
case 122:
|
||
|
||
return available_maps4x4[sv, su] != 0 ? true : false;
|
||
|
||
case 118:
|
||
case 119:
|
||
case 120:
|
||
case 123:
|
||
case 125:
|
||
|
||
return available_maps3x3[sv, su] != 0 ? true : false;
|
||
|
||
case 134:
|
||
|
||
return available_maps2x2[sv, su] != 0 ? true : false;
|
||
|
||
case 137:
|
||
return available_maps_137[sv, su] != 0 ? true : false;
|
||
|
||
case 161:
|
||
return available_maps_161[sv, su] != 0;
|
||
|
||
case 162:
|
||
return available_maps_162[sv, su] != 0 ? true : false;
|
||
|
||
case 163:
|
||
return available_maps_163[sv, su] != 0 ? true : false;
|
||
|
||
default:
|
||
return true;
|
||
}
|
||
}
|
||
|
||
else
|
||
return true;
|
||
}
|
||
|
||
static float DotProduct(A3DVECTOR3 v1, A3DVECTOR3 v2) { return v1.x* v2.x + v1.y* v2.y + v1.z* v2.z;
|
||
}
|
||
}
|
||
public struct OtherPlayer_Move_Info
|
||
{
|
||
// Bounding sphere of avator
|
||
public A3DVECTOR3 vCenter;
|
||
public A3DVECTOR3 vExts;
|
||
public float fStepHeight;
|
||
public A3DVECTOR3 vVelocity;
|
||
public float t;
|
||
public bool bTraceGround; // Whether trace the ground
|
||
public bool bTestTrnOnly; // Trace terrain only
|
||
public A3DVECTOR3 vecGroundNormal; // if bTraceGround is true, this will contain the ground normal when returned
|
||
};
|
||
|
||
//@desc :used to trace the environment, brush&terrain&water By Kuiwu[8/10/2005]
|
||
public struct env_trace_t
|
||
{
|
||
public A3DVECTOR3 vStart; // brush start
|
||
public A3DVECTOR3 vExt;
|
||
public A3DVECTOR3 vDelta;
|
||
public A3DVECTOR3 vTerStart;
|
||
public A3DVECTOR3 vWatStart;
|
||
public uint dwCheckFlag;
|
||
public bool bWaterSolid;
|
||
|
||
public float fFraction;
|
||
public A3DVECTOR3 vHitNormal;
|
||
public bool bStartSolid; //start in solid
|
||
public uint dwClsFlag; //collision flag
|
||
};
|
||
|
||
// for on-air move case
|
||
//@note : change to AABB. By Kuiwu[22/9/2005]
|
||
public struct ON_AIR_CDR_INFO
|
||
{
|
||
public A3DVECTOR3 vCenter;
|
||
public A3DVECTOR3 vExtent;
|
||
|
||
// Hold a height from the surface of terrain or building
|
||
public float fHeightThresh;
|
||
|
||
// Velocity Info
|
||
public A3DVECTOR3 vVelDir;
|
||
public float fSpeed;
|
||
|
||
// time span ( sec )
|
||
public float t;
|
||
|
||
//@note : SlopeThresh seems useless on air or under water. By Kuiwu[22/9/2005]
|
||
// Slope Thresh
|
||
//float fSlopeThresh;
|
||
|
||
// Distance Thresh under the water surface
|
||
public float fUnderWaterDistThresh;
|
||
|
||
public A3DVECTOR3 vTPNormal;
|
||
|
||
// On air or water, true if on air, false for on water case.
|
||
public bool bOnAir;
|
||
|
||
// After the move action is done, If the fHeightThresh
|
||
// still be satisfied, bMeetHeightThresh is set to true.
|
||
public bool bMeetHeightThresh;
|
||
};
|
||
}
|