Files
VDH 47f4033dff Merge branch 'develop' into feature/skill-data
# Conflicts:
#	Assets/PerfectWorld/Scripts/Managers/A3DTerrain2.cs
#	Assets/PerfectWorld/Scripts/Managers/CECManager.cs
#	Assets/PerfectWorld/Scripts/Managers/EC_HPWork.cs
#	Assets/PerfectWorld/Scripts/Managers/EC_HPWorkStand.cs
#	Assets/PerfectWorld/Scripts/Managers/EC_HPWorkTrace.cs
#	Assets/PerfectWorld/Scripts/Managers/aabbcd.cs
#	Assets/PerfectWorld/Scripts/Move/CECPlayer.cs
#	Assets/PerfectWorld/Scripts/Move/EC_CDR.cs
#	Assets/PerfectWorld/Scripts/PlayerState/PlayerIdleState.cs
#	Assets/PerfectWorld/Scripts/PlayerState/PlayerMoveState.cs
#	Assets/Scripts/CECHostPlayer.cs
#	Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Fallback.asset
2025-12-04 17:58:10 +07:00

544 lines
21 KiB
C#

using CSNetwork.GPDataType;
using NUnit.Framework;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using ARectF = BrewMonster.Scripts.ARect<float>;
using ARectI = BrewMonster.Scripts.ARect<int>;
using BlockArray = System.Collections.Generic.List<BrewMonster.Scripts.World.A3DTerrain2Block>;
using WORD = System.UInt16;
namespace BrewMonster.Scripts.World
{
public class A3DTerrain2Block
{
// Trerrain2 vertex format when use lightmap
public struct A3DTRN2VERTEX1
{
public float vPosX; // PositionX
public float vPosY; // PositionY
public float vPosZ; // PositionZ
public float vNormalX; // NormalX
public float vNormalY; // NormalY
public float vNormalZ; // NormalZ
public float u1, v1; // Texture coordinates project on xz
public float u2, v2; // Texture coordinates project on xy
public float u3, v3; // Texture coordinates project on yz
public float u4, v4; // Texture coordinate of layer mask
};
// Terrain2 vertex format when use vertex-light (not lightmap)
public struct A3DTRN2VERTEX2
{
public float vPosX; // Position X
public float vPosY; // Position Y
public float vPosZ; // Position Z
public uint dwDiffuse; // Diffuse color
public uint dwSpecular; // Specular color
public float u1, v1; // Texture coordinates project on xz
public float u2, v2; // Texture coordinates project on xy
public float u3, v3; // Texture coordinates project on yz
public float u4, v4; // Texture coordinate of layer mask
};
public bool m_bDataLoaded = false; // Data loaded flag
public bool IsDataLoaded() { return m_bDataLoaded; }
public uint m_dwBlockFlags; // Block flags
public A3DAABB m_aabbBlock; // Block postion and size in whole world
public A3DTRN2VERTEX1[] m_aVertices1; // Vertex buffer, ps version
public A3DTRN2VERTEX2[] m_aVertices2; // Vertex buffer, non-ps version
public A3DTerrain2 m_pTerrain; // Terrain object
public int m_iBlockGrid; // Each block has m_iBlockGrid * m_iBlockGrid terrain grids
// Get block's position and size in whole world
public A3DAABB GetBlockAABB() { return m_aabbBlock; }
// Get flag for this whole block unable for GetPosHeight
public bool IsNoPosHeight()
{
return (m_dwBlockFlags & Block_flags_masks.T2BKFLAG_NOPOSHEIGHT) != 0;
}
// Get vertex
public A3DVECTOR3 GetVertexPos(int n)
{
if (m_aVertices1 != null && m_aVertices1.Length > n)
return new A3DVECTOR3(m_aVertices1[n].vPosX, m_aVertices1[n].vPosY, m_aVertices1[n].vPosZ);
else
{
//ASSERT(m_aVertices2);
return new A3DVECTOR3(m_aVertices2[n].vPosX, m_aVertices2[n].vPosY, m_aVertices2[n].vPosZ);
}
}
/* Copy block vertices to destination buffer. This function works like BitBlt.
Copy vertex positions in a rectangle grid area to destination buffer, every
vertex is like a pixel in BitBlt;
pDestBuf: destination vertex buffer
iWid, iHei: vertex rectangle area's size
iDestPitch: destination vertex buffer pitch in A3DVECTOR3
sx, sy: grid rectangle area's left-top corner in block
*/
public bool CopyVertexPos(ref A3DVECTOR3[] pDestBuf, int iWid, int iHei,
int iDestPitch, int sx, int sy)
{
WORD[] aIndexMaps = m_pTerrain.GetLODManager().GetIndexMaps();
int pDestLine = 0;
int iSrcLine = sy * (m_iBlockGrid + 1) + sx;
if (m_pTerrain.UseLightmapTech())
{
//ASSERT(m_aVertices1);
for (int i = 0; i < iHei; i++)
{
int destIndex = pDestLine;
int iSrc = iSrcLine;
pDestLine += iDestPitch;
iSrcLine += m_iBlockGrid + 1;
for (int j = 0; j < iWid; j++, destIndex++, iSrc++)
{
// Note: here we map index
A3DTRN2VERTEX1 pSrc = m_aVertices1[aIndexMaps[iSrc]];
pDestBuf[destIndex].x = pSrc.vPosX;
pDestBuf[destIndex].y = pSrc.vPosY;
pDestBuf[destIndex].z = pSrc.vPosZ;
}
}
}
else
{
for (int i = 0; i < iHei; i++)
{
int pDest = pDestLine;
int iSrc = iSrcLine;
pDestLine += iDestPitch;
iSrcLine += m_iBlockGrid + 1;
for (int j = 0; j < iWid; j++, pDest++, iSrc++)
{
var pSrc = m_aVertices2[aIndexMaps[iSrc]];
pDestBuf[pDest].x = pSrc.vPosX;
pDestBuf[pDest].y = pSrc.vPosY;
pDestBuf[pDest].z = pSrc.vPosZ;
}
}
}
return true;
}
/* Copy grid faces to destination buffer. This function works like BitBlt.
Copy face in a rectangle grid area to destination buffer, every face is
like a pixel in BitBlt;
pDestVert: destination vertex buffer
pDestIdx: destination index buffer
iWid, iHei: grid rectangle area's size
iDstVertPitch: destination vertex buffer pitch in A3DVECTOR3
iBaseVert: Index of left-top corner vertex in vertex buffer
sx, sy: grid rectangle area's left-top corner in block
*/
public bool CopyFaces(ref A3DVECTOR3[] pDestVert, ref WORD[] pDestIdx, int iWid, int iHei,
int iDstVertPitch, int iBaseVert, int sx, int sy)
{
// Copy vertices at first. Notice the difference of iWid and iHei in this
// function and in CopyVertexPos()
CopyVertexPos(ref pDestVert, iWid + 1, iHei + 1, iDstVertPitch, sx, sy);
// Build indices
int pIndices = 0;
for (int i = 0; i < iHei; i++)
{
WORD v0 = (WORD)iBaseVert;
iBaseVert += iDstVertPitch;
for (int j = 0; j < iWid; j++, pIndices += 6, v0++)
{
// v3 v0----v1
// | \ \ |
// | \ \ |
// | \ \ |
// | \ \ |
// v5---v4 v2
pDestIdx[pIndices] = v0;
pDestIdx[pIndices + 1] = (WORD)(v0 + 1);
pDestIdx[pIndices + 2] = (WORD)(v0 + iDstVertPitch + 1);
pDestIdx[pIndices + 3] = v0;
pDestIdx[pIndices + 4] = (WORD)(v0 + iDstVertPitch + 1);
pDestIdx[pIndices + 5] = (WORD)(v0 + iDstVertPitch);
}
}
// If this area contain block's right-top corner grid or left-bottom
// corner grid, then we have to adjust index so that they are in
// rendering order
if (sx == 0 && sy + iHei == m_iBlockGrid)
{
// Contain left-bottom corner grid
// v0----v1
// | / |
// | / |
// | / |
// | / |
// v2----v3
int offer = (iHei - 1) * iWid * 6;
WORD v0 = pDestIdx[offer];
WORD v1 = pDestIdx[offer + 1];
WORD v2 = pDestIdx[offer + 5];
WORD v3 = pDestIdx[offer + 2];
pDestIdx[offer] = v0;
pDestIdx[offer + 1] = v1;
pDestIdx[offer + 2] = v2;
pDestIdx[offer + 3] = v1;
pDestIdx[offer + 4] = v3;
pDestIdx[offer + 5] = v2;
}
if (sx + iWid == m_iBlockGrid && sy == 0)
{
// Contain right-top corner grid
// v0----v1
// | / |
// | / |
// | / |
// | / |
// v2----v3
int offer = (iWid - 1) * 6;
WORD v0 = pDestIdx[offer];
WORD v1 = pDestIdx[offer + 1];
WORD v2 = pDestIdx[offer + 5];
WORD v3 = pDestIdx[offer + 2];
pDestIdx[offer + 0] = v0;
pDestIdx[offer + 1] = v1;
pDestIdx[offer + 2] = v2;
pDestIdx[offer + 3] = v1;
pDestIdx[offer + 4] = v3;
pDestIdx[offer + 5] = v2;
}
return true;
}
}
public class A3DTerrain2
{
public ACTBLOCKS m_pCurActBlocks; // Currently active block array
public ARectF m_rcTerrain; // Whole terrain area in logic unit (metres)
public float m_fBlockSize = 0; // Block size (on x and z axis) in logic unit (metres)
public int m_iBlockGrid = 0; // Each block has m_iBlockGrid * m_iBlockGrid terrain grids
public float m_fGridSize; // Terrain grid size (on x and z axis) in logic unit (metres)
public A3DTerrain2LOD m_pLODMan; // LOD manager
public int m_iNumActBlockRow; // Row and column number of active blocks
public int m_iNumAllBlockRow = 0; // Row number of all blocks
public int m_iNumAllBlockCol = 0; // Column number of all blocks
public bool m_bVertexLight = false; // true, force to use vertex light rather than lightmap
public List<MeshFilter> m_lstMesh;
// Check lightmap or vertex-light is used
public bool UseLightmapTech() { return !m_bVertexLight; }
public A3DTerrain2LOD GetLODManager() { return m_pLODMan; }
// Get grid faces of specified area
public bool GetFacesOfArea(A3DVECTOR3 vCenter, int iGridWid, int iGridLen, ref A3DVECTOR3[] pVertBuf, ref WORD[] pIdxBuf)
{
//if (m_pCurActBlocks.rcArea.IsEmpty())
// return false;
if(m_lstMesh == null || m_lstMesh.Count == 0)
{
return false;
}
float halfW = (iGridWid * m_fGridSize) / 2f;
float halfL = (iGridLen * m_fGridSize) / 2f;
Bounds area = new Bounds(EC_Utility.ToVector3(vCenter), new Vector3(halfW * 2, 1000f, halfL * 2));
float fInvGridSize = 1.0f / m_fGridSize;
ARectI rcGrid = new ARectI();
rcGrid.left = (int)((vCenter.x - m_rcTerrain.left) * fInvGridSize) - (iGridWid >> 1);
rcGrid.top = (int)((m_rcTerrain.top - vCenter.z) * fInvGridSize) - (iGridLen >> 1);
rcGrid.right = rcGrid.left + iGridWid;
rcGrid.bottom = rcGrid.top + iGridLen;
return GetFacesOfArea(rcGrid, ref pVertBuf, ref pIdxBuf);
}
public bool GetFacesOfArea(ARectI rcGridArea, ref A3DVECTOR3[] pVertBuf, ref WORD[] pIdxBuf)
{
if (m_pCurActBlocks.rcArea.IsEmpty())
return false;
float fInvGridSize = 1.0f / m_fGridSize;
ARectI rcGrid = rcGridArea;
int iMaxGrid = m_iNumAllBlockCol * m_iBlockGrid;
AAssist.a_Clamp(ref rcGrid.left, 0, iMaxGrid);
AAssist.a_Clamp(ref rcGrid.right, 0, iMaxGrid);
iMaxGrid = m_iNumAllBlockRow * m_iBlockGrid;
AAssist.a_Clamp(ref rcGrid.top, 0, iMaxGrid);
AAssist.a_Clamp(ref rcGrid.bottom, 0, iMaxGrid);
if (rcGrid.IsEmpty())
return false;
ARectI rcBlock = new ARectI();
rcBlock.left = rcGrid.left / m_iBlockGrid;
rcBlock.top = rcGrid.top / m_iBlockGrid;
rcBlock.right = (rcGrid.right - 1) / m_iBlockGrid;
rcBlock.bottom = (rcGrid.bottom - 1) / m_iBlockGrid;
int r, c;
// Ensure all blocks in active area and have been loaded
for (r = rcBlock.top; r <= rcBlock.bottom; r++)
{
for (c = rcBlock.left; c <= rcBlock.right; c++)
{
if (!m_pCurActBlocks.rcArea.PtInRect(c, r) ||
m_pCurActBlocks.GetBlock(r, c, false) == null)
return false;
}
}
int pDestIdx = 0;
A3DVECTOR3[] pVertDest = new A3DVECTOR3[0];
WORD[] pIdxTemp = new WORD[0];
for (r = rcBlock.top; r <= rcBlock.bottom; r++)
{
ARectI rect = new ARectI();
rect.left = rcBlock.left * m_iBlockGrid;
rect.top = r * m_iBlockGrid;
rect.right = rect.left + m_iBlockGrid;
rect.bottom = rect.top + m_iBlockGrid;
for (c = rcBlock.left; c <= rcBlock.right; c++)
{
A3DTerrain2Block pBlock = m_pCurActBlocks.GetBlock(r, c, false);
//ASSERT(pBlock);
ARectI rc = rcGrid & rect;
//ASSERT(!rc.IsEmpty());
int dx = rc.left - rcGrid.left;
int dy = rc.top - rcGrid.top;
int iBaseVert = dy * (rcGrid.Width() + 1) + dx;
pVertDest = pVertBuf.Skip(iBaseVert).ToArray();
pIdxTemp = pIdxBuf.Skip(pDestIdx).ToArray();
int sx = rc.left - rect.left;
int sy = rc.top - rect.top;
pBlock.CopyFaces(ref pVertDest, ref pIdxTemp, rc.Width(), rc.Height(), rcGrid.Width() + 1, iBaseVert, sx, sy);
for (int i = iBaseVert; i < pVertBuf.Length; i++)
{
pVertBuf[i] = pVertDest[i - iBaseVert];
}
for (int i = 0; i < pIdxBuf.Length; i++)
{
pIdxBuf[i] = pIdxTemp[i - pDestIdx];
}
pDestIdx += rc.Width() * rc.Height() * 6;
rect.left += m_iBlockGrid;
rect.right += m_iBlockGrid;
}
}
return true;
}
// Get height and normal of specified position
public float GetPosHeight(A3DVECTOR3 vPos, ref A3DVECTOR3 pvNormal)
{
// Give a default value to normal
if (pvNormal != null)
pvNormal.Clear();
if (m_pCurActBlocks.rcArea.IsEmpty())
return 0.0f;
// Currenly active area AABB
ARectF rcActive = new ARectF();
rcActive.left = m_rcTerrain.left + m_pCurActBlocks.rcArea.left * m_fBlockSize;
rcActive.top = m_rcTerrain.top - m_pCurActBlocks.rcArea.top * m_fBlockSize;
rcActive.right = rcActive.left + m_pCurActBlocks.rcArea.Width() * m_fBlockSize;
rcActive.bottom = rcActive.top - m_pCurActBlocks.rcArea.Height() * m_fBlockSize;
if (vPos.x < rcActive.left || vPos.x > rcActive.right ||
vPos.z > rcActive.top || vPos.z < rcActive.bottom)
return 0.0f;
// Get block this position is in
float fInvBlockSize = 1.0f / m_fBlockSize;
int iCol = (int)((vPos.x - rcActive.left) * fInvBlockSize);
int iRow = (int)(-(vPos.z - rcActive.top) * fInvBlockSize);
AAssist.a_Clamp(ref iCol, 0, m_pCurActBlocks.rcArea.Width() - 1);
AAssist.a_Clamp(ref iRow, 0, m_pCurActBlocks.rcArea.Height() - 1);
int iBlock = iRow * m_pCurActBlocks.rcArea.Width() + iCol;
A3DTerrain2Block pBlock = m_pCurActBlocks.aBlocks[iBlock];
if (pBlock == null || !pBlock.IsDataLoaded())
return 0.0f;
// If whole block is a hole, return as if there is no block here
if (pBlock.IsNoPosHeight())
return 0.0f;
// Get block's AABB
A3DAABB aabb = pBlock.GetBlockAABB();
// Get grid this position is in
float fInvGridSize = 1.0f / m_fGridSize;
iCol = (int)((vPos.x - aabb.Mins.x) * fInvGridSize);
iRow = (int)(-(vPos.z - aabb.Maxs.z) * fInvGridSize);
AAssist.a_Clamp(ref iCol, 0, m_iBlockGrid - 1);
AAssist.a_Clamp(ref iRow, 0, m_iBlockGrid - 1);
int iGrid = iRow * m_iBlockGrid + iCol;
GRID Grid = m_pLODMan.GetGrids()[iGrid];
A3DVECTOR3 v0 = pBlock.GetVertexPos(Grid.v0);
A3DVECTOR3 v1 = pBlock.GetVertexPos(Grid.v1);
A3DVECTOR3 v2 = pBlock.GetVertexPos(Grid.v2);
A3DVECTOR3 v3 = pBlock.GetVertexPos(Grid.v3);
A3DVECTOR3 v4 = pBlock.GetVertexPos(Grid.v4);
A3DVECTOR3 v5 = pBlock.GetVertexPos(Grid.v5);
A3DVECTOR3 vDest;
float dx, dz;
if (iGrid == m_iBlockGrid - 1 || iGrid == m_iBlockGrid * (m_iBlockGrid - 1))
{
// The grid is on right-top corner or left-bottom corner
dx = vPos.x - v2.x;
dz = vPos.z - v2.z;
if (dx > dz)
{
vDest = v5 + (v4 - v5) * (dx / (v4.x - v5.x));
vDest += (v3 - v4) * (dz / (v3.z - v4.z));
if (pvNormal != null)
{
pvNormal = A3DVECTOR3.CrossProduct(v4 - v3, v5 - v3);
pvNormal.Normalize();
}
}
else
{
vDest = v2 + (v0 - v2) * (dz / (v0.z - v2.z));
vDest += (v1 - v0) * (dx / (v1.x - v0.x));
if (pvNormal != null)
{
pvNormal = A3DVECTOR3.CrossProduct(v1 - v0, v2 - v0);
pvNormal.Normalize();
}
}
}
else
{
dx = vPos.x - v0.x;
dz = vPos.z - v0.z;
if (dx > -dz)
{
vDest = v0 + (v1 - v0) * (dx / (v1.x - v0.x));
vDest += (v2 - v1) * (dz / (v2.z - v1.z));
if (pvNormal != null)
{
pvNormal = A3DVECTOR3.CrossProduct(v1 - v0, v2 - v0);
pvNormal.Normalize();
}
}
else
{
vDest = v3 + (v5 - v3) * (dz / (v5.z - v3.z));
vDest += (v4 - v5) * (dx / (v4.x - v5.x));
if (pvNormal != null)
{
pvNormal = A3DVECTOR3.CrossProduct(v4 - v3, v5 - v3);
pvNormal.Normalize();
}
}
}
return vDest.y;
}
}
// Active blocks
public struct ACTBLOCKS
{
public ARectI rcArea; // Active area represented in blocks
public BlockArray aBlocks; // Active block array
public ACTBLOCKS(ARectI rcArea, BlockArray aBlocks)
{
this.rcArea = rcArea;
this.aBlocks = aBlocks;
}
// Get block object at specified row, column.
public A3DTerrain2Block GetBlock(int r, int c, bool bClear)
{
//ASSERT(rcArea.PtInRect(c, r));
int iIndex = GetBlockIndex(r, c);
A3DTerrain2Block pBlock = aBlocks[iIndex];
if (bClear)
aBlocks[iIndex] = null;
return pBlock;
}
// Set block object at specified row, column
void SetBlock(int r, int c, A3DTerrain2Block pBlock)
{
//ASSERT(rcArea.PtInRect(c, r));
int iIndex = GetBlockIndex(r, c);
aBlocks[iIndex] = pBlock;
}
// Get block index in aBlocks
int GetBlockIndex(int r, int c)
{
return (r - rcArea.top) * rcArea.Width() + c - rcArea.left;
}
};
// Block flags & masks
public class Block_flags_masks
{
public static uint T2BKFLAG_DEFAULT = 0x00, // The block flag default value
T2BKFLAG_NORENDER = 0x01, // The whole block is not rendered
T2BKFLAG_NOTRACE = 0x02, // The whole block is out of RayTrace
T2BKFLAG_NOPOSHEIGHT = 0x04, // The whole block is unable to GetPosHeight
T2BKFLAG_NORENDERWITHWATER = 0x08; // The whole block is not rendered with water
};
// Vertex indices in a terrain grid
public struct GRID
{
public int v0, v1, v2; // Face 1
public int v3, v4, v5; // Face 2
};
}