Files
test/Assets/PerfectWorld/Scripts/Move/EC_CDR.cs
T
2025-10-31 02:20:50 +07:00

587 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using BrewMonster.Scripts.Ornament;
using BrewMonster.Scripts.Player;
using BrewMonster.Scripts.World;
using CSNetwork.GPDataType;
using System;
using UnityEngine;
using WORD = System.UInt16;
namespace BrewMonster.Scripts
{
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;
const float LOCAL_EPSILON = 1e-5f;
//[Flags]
public class CDR_EVN
{
public const int CDR_BRUSH = 0x1,
CDR_TERRAIN = 0x2,
CDR_WATER = 0x4;
}
static LayerMask UsedMask_Ground() => BrushMask | TerrainMask;
static bool CollideWithEnv(env_trace_t pEnvTrc)
{
pEnvTrc.fFraction = 100.0f;
pEnvTrc.bStartSolid = false;
pEnvTrc.dwClsFlag = 0;
if ((pEnvTrc.dwCheckFlag & CDR_EVN.CDR_BRUSH) == CDR_EVN.CDR_BRUSH)
{
BrushTraceInfo bruInfo = new BrushTraceInfo();
bruInfo.Init(pEnvTrc.vStart, pEnvTrc.vDelta, pEnvTrc.vExt);
if (AABBCollideWithBrush(ref bruInfo))
{
pEnvTrc.fFraction = bruInfo.fFraction;
pEnvTrc.vHitNormal = bruInfo.ClipPlane.GetNormal();
pEnvTrc.bStartSolid = bruInfo.bStartSolid;
pEnvTrc.dwClsFlag = CDR_EVN.CDR_BRUSH;
}
}
if ((pEnvTrc.dwCheckFlag & CDR_EVN.CDR_TERRAIN) == CDR_EVN.CDR_TERRAIN)
{
float fFraction = 0f;
A3DVECTOR3 vTerNormal = new A3DVECTOR3();
bool bStart = false;
if (CollideWithTerrain(pEnvTrc.vTerStart, pEnvTrc.vDelta, ref fFraction, ref vTerNormal, ref bStart)
&& (fFraction < pEnvTrc.fFraction))
{
//assert(fFraction >= 0.0f);
//pEnvTrc.fFraction = a_Max(0.0f, fFraction - 1E-4f);
pEnvTrc.fFraction = fFraction;
pEnvTrc.vHitNormal = vTerNormal;
pEnvTrc.bStartSolid = bStart;
pEnvTrc.dwClsFlag = CDR_EVN.CDR_TERRAIN;
}
}
if ((pEnvTrc.dwCheckFlag & CDR_EVN.CDR_WATER) == CDR_EVN.CDR_WATER)
{
float fFraction = 0f;
A3DVECTOR3 vWatNormal = new A3DVECTOR3();
bool bStart = false;
//@todo : TBD: use center or foot? By Kuiwu[10/10/2005]
//if (CollideWithWater(pEnvTrc.vWatStart, pEnvTrc.vDelta, pEnvTrc.bWaterSolid, ref fFraction, ref vWatNormal, ref bStart)
// && fFraction < pEnvTrc.fFraction)
//{
// pEnvTrc.fFraction = fFraction;
// pEnvTrc.vHitNormal = vWatNormal;
// pEnvTrc.bStartSolid = bStart;
// pEnvTrc.dwClsFlag = CDR_EVN.CDR_WATER;
// //# ifdef CDR_DEBUG
// // A3DVECTOR3 vHitPos(pEnvTrc.vWatStart +pEnvTrc.vDelta * fFraction);
// // sprintf(msg, "collide water, fraction %f pos %f %f %f \n", fFraction, vHitPos.x, vHitPos.y, vHitPos.z);
// // OUTPUT_DEBUG_INFO(msg);
// //#endif
//}
}
return (pEnvTrc.fFraction < 1.0f + 1E-4f);
}
// == Thay CollideWithEnv (C++) bằng BoxCast ==
//static bool CollideWithEnv_BoxCast(Vector3 vStart, Vector3 vDelta, Vector3 vExt,
// LayerMask mask,
// out RaycastHit hit, out float fFraction, out Vector3 vHitNormal, out bool bStartSolid,
// float skin = 0.01f)
//{
// hit = default;
// vHitNormal = Vector3.up;
// bStartSolid = false;
// fFraction = 0.0f;
// float dist = vDelta.magnitude;
// if (dist <= 1e-6f) return false;
// var hasHit = Physics.Raycast(vStart, vDelta,out hit, dist, mask);
// if (hasHit)
// {
// bStartSolid = true;
// fFraction = (t - 5E-4f) / vDelta.Normalize();
// return true;
// }
// // start-in-solid
// var overlapped = Physics.OverlapBox(vStart, vExt - Vector3.one * skin, Quaternion.identity, mask, QueryTriggerInteraction.Ignore);
// if (overlapped != null && overlapped.Length > 0)
// {
// bStartSolid = true;
// return true;
// }
// // sweep AABB
// Vector3 dir = vDelta / Mathf.Max(dist, 1e-6f);
// if (Physics.BoxCast(vStart, vExt - Vector3.one * skin, dir, out hit, Quaternion.identity, dist, mask, QueryTriggerInteraction.Ignore))
// {
// fFraction = Mathf.Clamp01(hit.distance / Mathf.Max(dist, 1e-6f));
// vHitNormal = hit.normal;
// return true;
// }
// return false;
//}
// == 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.up;
bSupport = false;
float dist = Mathf.Max(fDeltaY, 0f);
Vector3 origin = vStart /*+ Vector3.down * vExt.y*/;
if (Physics.Raycast(origin, (Vector3.down ).normalized, out RaycastHit hit, 1<<6 | 1<<7))
{
vHitNormal = hit.normal;
//Debug.DrawLine(origin, vHitNormal, Color.black, 10f);
vEnd = new Vector3(vStart.x, hit.point.y + vExt.y, vStart.z);
bSupport = (vHitNormal.y >= 0f);
return true;
}
return true; // không thấy ground → bSupport=false
}
// ======= STATIC OnGroundMove GIỮ NGUYÊN VAI TRÒ TOÀN CỤC (C API) =======
public static void OnGroundMove(ref CDR_INFO CDRInfo)
{
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;
CDRInfo.fMoveDist = 0.0f;
bool bFreeFall = (CDRInfo.vTPNormal.y < CDRInfo.fSlopeThresh);
if (Mathf.Abs(CDRInfo.fYVel) < VEL_EPSILON && Mathf.Abs(CDRInfo.fSpeed) < VEL_EPSILON && !bFreeFall)
return;
float fYVel = CDRInfo.fYVel;
bool bJump = (fYVel > 0.5f);
Vector3 vVelocity = CDRInfo.fSpeed * new Vector3(CDRInfo.vXOZVelDir.x, 0f, CDRInfo.vXOZVelDir.z) + fYVel * Vector3.up;
if (bFreeFall)
{
vVelocity += -CDRInfo.fGravityAccel * CDRInfo.t * Vector3.up;
fYVel += -CDRInfo.fGravityAccel * CDRInfo.t;
}
Vector3 vVelDir = vVelocity;
float fVelSpeed = vVelDir.magnitude;
//vVelDir = vVelDir.normalized;
if (fVelSpeed > 1e-6f)
{
vVelDir /= fVelSpeed;
}
else
{
vVelDir = Vector3.zero;
}
if (!bFreeFall)
{
fVelSpeed = Mathf.Min(fVelSpeed, VEL_MAX_SPEED);
}
vVelocity = vVelDir * fVelSpeed;
float dtp = Vector3.Dot(vVelDir, CDRInfo.vTPNormal);
if (dtp < 0f || !bJump)
{
vVelocity = (vVelDir - CDRInfo.vTPNormal * dtp - CDRInfo.vTPNormal * dtp * 0.01f) * fVelSpeed;
}
CDRInfo.vAbsVelocity = vVelocity;
Vector3 vStart = CDRInfo.vCenter;
Vector3 vExt = CDRInfo.vExtent;
float fTime = CDRInfo.t;
Vector3 vDelta, vNormal = Vector3.up, vFinalPos = vStart;
bool bPull = false;
bool bTryPull = false;
int nTry = 0;
LayerMask mask = UsedMask_Ground();
while (nTry < 1)
{
vDelta = vVelocity * fTime;
float fDeltaDist = vDelta.magnitude;
//if (fDeltaDist < DIST_EPSILON) break;
// TO DO: fix later beacuse logic in CollideWithEnv_BoxCast is wrong
//bool hasHit = CollideWithEnv_BoxCast(vStart, vDelta, vExt, mask,
// out RaycastHit hit, out float fFraction, out Vector3 hitNormal, out bool bStartSolid);
Vector3 posFoot = vStart - Vector3.up * vExt.y;
bool bClear = !Physics.Raycast(posFoot, (posFoot + vDelta).normalized, out RaycastHit hit, fDeltaDist, mask);
nTry++;
if (bClear)
{
Debug.DrawLine(posFoot, posFoot + vDelta, Color.yellow, 10f);
vFinalPos = vStart + vDelta;
CDRInfo.fMoveDist += fDeltaDist;
break;
}
Debug.LogError("Hit");
//if (bStartSolid)
//{
// CDRInfo.fMoveDist = 0f;
// if (CDRInfo.vTPNormal.y < CDRInfo.fSlopeThresh) CDRInfo.vTPNormal = Vector3.up;
// return;
//}
Debug.DrawLine(posFoot, hit.point, Color.green, 10f);
vStart = hit.point + Vector3.up * vExt.y;
vFinalPos = vStart;
//CDRInfo.fMoveDist += (fDeltaDist * fFraction);
//fTime -= fTime * fFraction;
//vNormal = hitNormal;
// Step-up (giữ tinh thần bản gốc)
if (!bFreeFall && !bTryPull && !bJump)
{
//float skin = 0.01f;
posFoot = vStart - Vector3.up * vExt.y + Vector3.up * CDRInfo.fStepHeight;
Vector3 vStartUp = new Vector3(0f, CDRInfo.fStepHeight, 0f);
bPull = !Physics.Raycast(posFoot, (posFoot + vStartUp).normalized, out hit, CDRInfo.fStepHeight, mask);
if (bPull)
{
vStart += Vector3.up * CDRInfo.fStepHeight;
posFoot = vStart - Vector3.up * vExt.y;
//Vector3 vDelta2 = vVelocity;
bool bMove = !Physics.Raycast(posFoot, (posFoot + vVelocity).normalized, out hit, fDeltaDist, mask);
if (!bMove)
{
//vDelta2 *= frac2;
vFinalPos = hit.point + Vector3.up * vExt.y;
}
else
{
Debug.DrawLine(vFinalPos, vFinalPos + vDelta, Color.red, 10f);
vFinalPos += vDelta;
}
//if (vDelta2.sqrMagnitude < (vExt.x * vExt.x * 4f))
//{
// vStart -= Vector3.up * CDRInfo.fStepHeight;
// bPull = false;
//}
}
bTryPull = true;
}
//if (!bPull)
//{
// if (vVelocity.sqrMagnitude > 1e-12f)
// {
// vVelDir = vVelocity.normalized;
// fVelSpeed = vVelocity.magnitude * (1f - nTry * 0.1f);
// dtp = Vector3.Dot(vNormal, vVelDir);
// float fRelSpeed = Mathf.Min(fVelSpeed, 5.0f);
// if (dtp >= 0f && dtp < 1e-4f)
// {
// vVelocity += vNormal * VEL_REFLECT * fRelSpeed;
// }
// else
// {
// vVelocity = (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;
float downDist = 0.3f;
if (bPull) downDist = CDRInfo.fStepHeight + 0.1f;
if (bJump) downDist = 0.0f;
if (downDist > 0f)
{
if (!DoGroundProbe(vFinalPos, vExt, downDist, 1<<6, out Vector3 vEnd, out Vector3 groundNormal, out bool bSupport))
{
CDRInfo.fMoveDist = 0f;
CDRInfo.vTPNormal = Vector3.up;
return;
}
if (bSupport)
{
Debug.LogError("vFinal = " + vFinal + " == vEnd = " + vEnd);
vFinal = vEnd;
if (!bJump) vTPNormal = groundNormal;
}
}
if ((vTPNormal.y >= CDRInfo.fSlopeThresh && fYVel < 0.0f) || (!bJump && fYVel > 0.0f))
fYVel = 0.0f;
CDRInfo.vCenter = vFinal;
CDRInfo.fYVel = fYVel;
if (vTPNormal != Vector3.zero) CDRInfo.vTPNormal = vTPNormal;
}
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;
}
/*
* ray: origin + t* dir, triangle: p(u,v) = (1-u -v)vert[0] + u*vert[1] + v*vert[2]
* @desc :
* @param vDir: normalized direction
* @param bCull: cull back face if true
* @return :
* @note:
* @todo:
* @author: kuiwu [8/10/2005]
* @ref: Tomas Moller's JGT code
*/
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 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
};
}