Files
HL2Overcharged/public/disp_common.cpp
2025-05-21 21:20:08 +03:00

1297 lines
35 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "disp_common.h"
#include "disp_powerinfo.h"
#include "builddisp.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
class CNodeVert
{
public:
CNodeVert() {}
CNodeVert(int ix, int iy) { x = ix; y = iy; }
inline int& operator[](int i) { return ((int*)this)[i]; }
inline int const& operator[](int i) const { return ((int*)this)[i]; }
int x, y;
};
static CNodeVert const g_NodeChildLookup[4][2] =
{
{ CNodeVert(0, 0), CNodeVert(1, 1) },
{ CNodeVert(1, 0), CNodeVert(2, 1) },
{ CNodeVert(0, 1), CNodeVert(1, 2) },
{ CNodeVert(1, 1), CNodeVert(2, 2) }
};
static CNodeVert const g_NodeTriWinding[9] =
{
CNodeVert(0, 1),
CNodeVert(0, 0),
CNodeVert(1, 0),
CNodeVert(2, 0),
CNodeVert(2, 1),
CNodeVert(2, 2),
CNodeVert(1, 2),
CNodeVert(0, 2),
CNodeVert(0, 1)
};
// Indexed by CORNER_. These store NEIGHBOREDGE_ defines and tell which edges butt up against the corner.
static int g_CornerEdges[4][2] =
{
{ NEIGHBOREDGE_BOTTOM, NEIGHBOREDGE_LEFT }, // CORNER_LOWER_LEFT
{ NEIGHBOREDGE_TOP, NEIGHBOREDGE_LEFT }, // CORNER_UPPER_LEFT
{ NEIGHBOREDGE_TOP, NEIGHBOREDGE_RIGHT }, // CORNER_UPPER_RIGHT
{ NEIGHBOREDGE_BOTTOM, NEIGHBOREDGE_RIGHT } // CORNER_LOWER_RIGHT
};
int g_EdgeDims[4] =
{
0, // NEIGHBOREDGE_LEFT = X
1, // NEIGHBOREDGE_TOP = Y
0, // NEIGHBOREDGE_RIGHT = X
1 // NEIGHBOREDGE_BOTTOM = Y
};
CShiftInfo g_ShiftInfos[3][3] =
{
{
{ 0, 0, true }, // CORNER_TO_CORNER -> CORNER_TO_CORNER
{ 0, -1, true }, // CORNER_TO_CORNER -> CORNER_TO_MIDPOINT
{ 2, -1, true } // CORNER_TO_CORNER -> MIDPOINT_TO_CORNER
},
{
{ 0, 1, true }, // CORNER_TO_MIDPOINT -> CORNER_TO_CORNER
{ 0, 0, false }, // CORNER_TO_MIDPOINT -> CORNER_TO_MIDPOINT (invalid)
{ 0, 0, false } // CORNER_TO_MIDPOINT -> MIDPOINT_TO_CORNER (invalid)
},
{
{ -1, 1, true }, // MIDPOINT_TO_CORNER -> CORNER_TO_CORNER
{ 0, 0, false }, // MIDPOINT_TO_CORNER -> CORNER_TO_MIDPOINT (invalid)
{ 0, 0, false } // MIDPOINT_TO_CORNER -> MIDPOINT_TO_CORNER (invalid)
}
};
int g_EdgeSideLenMul[4] =
{
0,
1,
1,
0
};
// --------------------------------------------------------------------------------- //
// Helper functions.
// --------------------------------------------------------------------------------- //
inline int SignedBitShift(int val, int shift)
{
if (shift > 0)
return val << shift;
else
return val >> -shift;
}
static inline void RotateVertIndex(
NeighborOrientation neighor,
int sideLengthMinus1,
CVertIndex const &in,
CVertIndex &out)
{
if (neighor == ORIENTATION_CCW_0)
{
out = in;
}
else if (neighor == ORIENTATION_CCW_90)
{
out.x = in.y;
out.y = sideLengthMinus1 - in.x;
}
else if (neighor == ORIENTATION_CCW_180)
{
out.x = sideLengthMinus1 - in.x;
out.y = sideLengthMinus1 - in.y;
}
else
{
out.x = sideLengthMinus1 - in.y;
out.y = in.x;
}
}
static inline void RotateVertIncrement(
NeighborOrientation neighor,
CVertIndex const &in,
CVertIndex &out)
{
if (neighor == ORIENTATION_CCW_0)
{
out = in;
}
else if (neighor == ORIENTATION_CCW_90)
{
out.x = in.y;
out.y = -in.x;
}
else if (neighor == ORIENTATION_CCW_180)
{
out.x = -in.x;
out.y = -in.y;
}
else
{
out.x = -in.y;
out.y = in.x;
}
}
// --------------------------------------------------------------------------------- //
// CDispHelper functions.
// --------------------------------------------------------------------------------- //
int GetEdgeIndexFromPoint(CVertIndex const &index, int iMaxPower)
{
int sideLengthMinus1 = 1 << iMaxPower;
if (index.x == 0)
return NEIGHBOREDGE_LEFT;
else if (index.y == sideLengthMinus1)
return NEIGHBOREDGE_TOP;
else if (index.x == sideLengthMinus1)
return NEIGHBOREDGE_RIGHT;
else if (index.y == 0)
return NEIGHBOREDGE_BOTTOM;
else
return -1;
}
int GetCornerIndexFromPoint(CVertIndex const &index, int iPower)
{
int sideLengthMinus1 = 1 << iPower;
if (index.x == 0 && index.y == 0)
return CORNER_LOWER_LEFT;
else if (index.x == 0 && index.y == sideLengthMinus1)
return CORNER_UPPER_LEFT;
else if (index.x == sideLengthMinus1 && index.y == sideLengthMinus1)
return CORNER_UPPER_RIGHT;
else if (index.x == sideLengthMinus1 && index.y == 0)
return CORNER_LOWER_RIGHT;
else
return -1;
}
int GetNeighborEdgePower(CDispUtilsHelper *pDisp, int iEdge, int iSub)
{
CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor(iEdge);
CDispSubNeighbor *pSub = &pEdge->m_SubNeighbors[iSub];
if (!pSub->IsValid())
return -1;
CDispUtilsHelper *pNeighbor = pDisp->GetDispUtilsByIndex(pSub->GetNeighborIndex());
CShiftInfo *pInfo = &g_ShiftInfos[pSub->m_Span][pSub->m_NeighborSpan];
Assert(pInfo->m_bValid);
return pNeighbor->GetPower() + pInfo->m_PowerShiftAdd;
}
CDispUtilsHelper* SetupEdgeIncrements(
CDispUtilsHelper *pDisp,
int iEdge,
int iSub,
CVertIndex &myIndex,
CVertIndex &myInc,
CVertIndex &nbIndex,
CVertIndex &nbInc,
int &myEnd,
int &iFreeDim)
{
int iEdgeDim = g_EdgeDims[iEdge];
iFreeDim = !iEdgeDim;
CDispNeighbor *pSide = pDisp->GetEdgeNeighbor(iEdge);
CDispSubNeighbor *pSub = &pSide->m_SubNeighbors[iSub];
if (!pSub->IsValid())
return NULL;
CDispUtilsHelper *pNeighbor = pDisp->GetDispUtilsByIndex(pSub->m_iNeighbor);
CShiftInfo *pShiftInfo = &g_ShiftInfos[pSub->m_Span][pSub->m_NeighborSpan];
Assert(pShiftInfo->m_bValid);
// Setup a start point and edge increment (NOTE: just precalculate these
// and store them in the CDispSubNeighbors).
CVertIndex tempInc;
const CPowerInfo *pPowerInfo = pDisp->GetPowerInfo();
myIndex[iEdgeDim] = g_EdgeSideLenMul[iEdge] * pPowerInfo->m_SideLengthM1;
myIndex[iFreeDim] = pPowerInfo->m_MidPoint * iSub;
TransformIntoSubNeighbor(pDisp, iEdge, iSub, myIndex, nbIndex);
int myPower = pDisp->GetPowerInfo()->m_Power;
int nbPower = pNeighbor->GetPowerInfo()->m_Power + pShiftInfo->m_PowerShiftAdd;
myInc[iEdgeDim] = tempInc[iEdgeDim] = 0;
if (nbPower > myPower)
{
myInc[iFreeDim] = 1;
tempInc[iFreeDim] = 1 << (nbPower - myPower);
}
else
{
myInc[iFreeDim] = 1 << (myPower - nbPower);
tempInc[iFreeDim] = 1;
}
RotateVertIncrement(pSub->GetNeighborOrientation(), tempInc, nbInc);
// Walk along the edge.
if (pSub->m_Span == CORNER_TO_MIDPOINT)
myEnd = pDisp->GetPowerInfo()->m_SideLength >> 1;
else
myEnd = pDisp->GetPowerInfo()->m_SideLength - 1;
return pNeighbor;
}
int GetSubNeighborIndex(
CDispUtilsHelper *pDisp,
int iEdge,
CVertIndex const &nodeIndex)
{
const CPowerInfo *pPowerInfo = pDisp->GetPowerInfo();
const CDispNeighbor *pSide = pDisp->GetEdgeNeighbor(iEdge);
// Figure out if this is a vertical or horizontal edge.
int iEdgeDim = g_EdgeDims[iEdge];
int iFreeDim = !iEdgeDim;
int iFreeIndex = nodeIndex[iFreeDim];
// Figure out which of the (up to two) neighbors it lies in.
int iSub = 0;
if (iFreeIndex == pPowerInfo->m_MidPoint)
{
// If it's in the middle, we only are interested if there's one neighbor
// next to us (so we can enable its middle vert). If there are any neighbors
// that touch the midpoint, then we have no need to return them because it would
// touch their corner verts which are always active.
if (pSide->m_SubNeighbors[0].m_Span != CORNER_TO_CORNER)
return -1;
}
else if (iFreeIndex > pPowerInfo->m_MidPoint)
{
iSub = 1;
}
// Make sure we get a valid neighbor.
if (!pSide->m_SubNeighbors[iSub].IsValid())
{
if (iSub == 1 &&
pSide->m_SubNeighbors[0].IsValid() &&
pSide->m_SubNeighbors[0].m_Span == CORNER_TO_CORNER)
{
iSub = 0;
}
else
{
return -1;
}
}
return iSub;
}
void SetupSpan(int iPower, int iEdge, NeighborSpan span, CVertIndex &viStart, CVertIndex &viEnd)
{
int iFreeDim = !g_EdgeDims[iEdge];
const CPowerInfo *pPowerInfo = GetPowerInfo(iPower);
viStart = pPowerInfo->GetCornerPointIndex(iEdge);
viEnd = pPowerInfo->GetCornerPointIndex((iEdge + 1) & 3);;
if (iEdge == NEIGHBOREDGE_RIGHT || iEdge == NEIGHBOREDGE_BOTTOM)
{
// CORNER_TO_MIDPOINT and MIDPOINT_CORNER are defined where the edge moves up or right,
// but pPowerInfo->GetCornerPointIndex walks around the edges clockwise, so on the
// bottom and right edges (where GetCornerPointIndex has us moving down and left) we need to
// reverse the sense here to make sure we return the right span.
if (span == CORNER_TO_MIDPOINT)
viStart[iFreeDim] = pPowerInfo->GetMidPoint();
else if (span == MIDPOINT_TO_CORNER)
viEnd[iFreeDim] = pPowerInfo->GetMidPoint();
}
else
{
if (span == CORNER_TO_MIDPOINT)
viEnd[iFreeDim] = pPowerInfo->GetMidPoint();
else if (span == MIDPOINT_TO_CORNER)
viStart[iFreeDim] = pPowerInfo->GetMidPoint();
}
}
CDispUtilsHelper* TransformIntoSubNeighbor(
CDispUtilsHelper *pDisp,
int iEdge,
int iSub,
CVertIndex const &nodeIndex,
CVertIndex &out
)
{
const CDispSubNeighbor *pSub = &pDisp->GetEdgeNeighbor(iEdge)->m_SubNeighbors[iSub];
// Find the part of pDisp's edge that this neighbor covers.
CVertIndex viSrcStart, viSrcEnd;
SetupSpan(pDisp->GetPower(), iEdge, pSub->GetSpan(), viSrcStart, viSrcEnd);
// Find the corresponding parts on the neighbor.
CDispUtilsHelper *pNeighbor = pDisp->GetDispUtilsByIndex(pSub->GetNeighborIndex());
int iNBEdge = (iEdge + 2 + pSub->GetNeighborOrientation()) & 3;
CVertIndex viDestStart, viDestEnd;
SetupSpan(pNeighbor->GetPower(), iNBEdge, pSub->GetNeighborSpan(), viDestEnd, viDestStart);
// Now map the one into the other.
int iFreeDim = !g_EdgeDims[iEdge];
int fixedPercent = ((nodeIndex[iFreeDim] - viSrcStart[iFreeDim]) * (1 << 16)) / (viSrcEnd[iFreeDim] - viSrcStart[iFreeDim]);
Assert(fixedPercent >= 0 && fixedPercent <= (1 << 16));
int nbDim = g_EdgeDims[iNBEdge];
out[nbDim] = viDestStart[nbDim];
out[!nbDim] = viDestStart[!nbDim] + ((viDestEnd[!nbDim] - viDestStart[!nbDim]) * fixedPercent) / (1 << 16);
Assert(out.x >= 0 && out.x < pNeighbor->GetSideLength());
Assert(out.y >= 0 && out.y < pNeighbor->GetSideLength());
return pNeighbor;
}
CDispUtilsHelper* TransformIntoNeighbor(
CDispUtilsHelper *pDisp,
int iEdge,
CVertIndex const &nodeIndex,
CVertIndex &out
)
{
if (iEdge == -1)
iEdge = GetEdgeIndexFromPoint(nodeIndex, pDisp->GetPower());
int iSub = GetSubNeighborIndex(pDisp, iEdge, nodeIndex);
if (iSub == -1)
return NULL;
CDispUtilsHelper *pRet = TransformIntoSubNeighbor(pDisp, iEdge, iSub, nodeIndex, out);
#if 0
// Debug check.. make sure it comes back to the same point from the other side.
#if defined( _DEBUG )
static bool bTesting = false;
if (pRet && !bTesting)
{
bTesting = true;
// We could let TransformIntoNeighbor figure out the index but if this is a corner vert, then
// it may pick the wrong edge and we'd get a benign assert.
int nbOrientation = pDisp->GetEdgeNeighbor(iEdge)->m_SubNeighbors[iSub].GetNeighborOrientation();
int iNeighborEdge = (iEdge + 2 + nbOrientation) & 3;
CVertIndex testIndex;
CDispUtilsHelper *pTest = TransformIntoNeighbor(pRet, iNeighborEdge, out, testIndex);
Assert(pTest == pDisp);
Assert(testIndex == nodeIndex);
bTesting = false;
}
#endif
#endif
return pRet;
}
bool DoesPointHaveAnyNeighbors(
CDispUtilsHelper *pDisp,
const CVertIndex &index)
{
// See if it connects to a neighbor on the edge.
CVertIndex dummy;
if (TransformIntoNeighbor(pDisp, -1, index, dummy))
return true;
// See if it connects to a neighbor on a corner.
int iCorner = GetCornerIndexFromPoint(index, pDisp->GetPower());
if (iCorner == -1)
return false;
// If there are any neighbors on the specified corner, then the point has neighbors.
if (pDisp->GetCornerNeighbors(iCorner)->m_nNeighbors > 0)
return true;
// Since points on corners touch two edges, we actually want to test two edges to see
// if the point has a neighbor on either edge.
for (int i = 0; i < 2; i++)
{
if (TransformIntoNeighbor(pDisp, g_CornerEdges[iCorner][i], index, dummy))
return true;
}
return false;
}
// ------------------------------------------------------------------------------------ //
// CDispSubEdgeIterator.
// ------------------------------------------------------------------------------------ //
CDispSubEdgeIterator::CDispSubEdgeIterator()
{
m_pNeighbor = 0;
m_FreeDim = m_Index.x = m_Inc.x = m_End = 0; // Setup so Next returns false.
}
void CDispSubEdgeIterator::Start(CDispUtilsHelper *pDisp, int iEdge, int iSub, bool bTouchCorners)
{
m_pNeighbor = SetupEdgeIncrements(pDisp, iEdge, iSub, m_Index, m_Inc, m_NBIndex, m_NBInc, m_End, m_FreeDim);
if (m_pNeighbor)
{
if (bTouchCorners)
{
// Back up our current position by 1 so we hit the corner first, and extend the endpoint
// so we hit the other corner too.
m_Index -= m_Inc;
m_NBIndex -= m_NBInc;
m_End += m_Inc[m_FreeDim];
}
}
else
{
m_FreeDim = m_Index.x = m_Inc.x = m_End = 0; // Setup so Next returns false.
}
}
bool CDispSubEdgeIterator::Next()
{
m_Index += m_Inc;
m_NBIndex += m_NBInc;
// Were we just at the last point on the edge?
return m_Index[m_FreeDim] < m_End;
}
bool CDispSubEdgeIterator::IsLastVert() const
{
return (m_Index[m_FreeDim] + m_Inc[m_FreeDim]) >= m_End;
}
// ------------------------------------------------------------------------------------ //
// CDispEdgeIterator.
// ------------------------------------------------------------------------------------ //
CDispEdgeIterator::CDispEdgeIterator(CDispUtilsHelper *pDisp, int iEdge)
{
m_pDisp = pDisp;
m_iEdge = iEdge;
m_iCurSub = -1;
}
bool CDispEdgeIterator::Next()
{
while (!m_It.Next())
{
// Ok, move up to the next sub.
if (m_iCurSub == 1)
return false;
++m_iCurSub;
m_It.Start(m_pDisp, m_iEdge, m_iCurSub);
}
return true;
}
// ------------------------------------------------------------------------------------ //
// CDispCircumferenceIterator.
// ------------------------------------------------------------------------------------ //
CDispCircumferenceIterator::CDispCircumferenceIterator(int sideLength)
{
m_iCurEdge = -1;
m_SideLengthM1 = sideLength - 1;
}
bool CDispCircumferenceIterator::Next()
{
switch (m_iCurEdge)
{
case -1:
{
m_iCurEdge = NEIGHBOREDGE_LEFT;
m_VertIndex.Init(0, 0);
}
break;
case NEIGHBOREDGE_LEFT:
{
++m_VertIndex.y;
if (m_VertIndex.y == m_SideLengthM1)
m_iCurEdge = NEIGHBOREDGE_TOP;
}
break;
case NEIGHBOREDGE_TOP:
{
++m_VertIndex.x;
if (m_VertIndex.x == m_SideLengthM1)
m_iCurEdge = NEIGHBOREDGE_RIGHT;
}
break;
case NEIGHBOREDGE_RIGHT:
{
--m_VertIndex.y;
if (m_VertIndex.y == 0)
m_iCurEdge = NEIGHBOREDGE_BOTTOM;
}
break;
case NEIGHBOREDGE_BOTTOM:
{
--m_VertIndex.x;
if (m_VertIndex.x == 0)
return false; // Done!
}
break;
}
return true;
}
// Helper function to setup an index either on the edges or the center
// of the box defined by [bottomleft,topRight].
static inline void SetupCoordXY(CNodeVert &out, CNodeVert const &bottomLeft, CNodeVert const &topRight, CNodeVert const &info)
{
for (int i = 0; i < 2; i++)
{
if (info[i] == 0)
out[i] = bottomLeft[i];
else if (info[i] == 1)
out[i] = (bottomLeft[i] + topRight[i]) >> 1;
else
out[i] = topRight[i];
}
}
static unsigned short* DispCommon_GenerateTriIndices_R(
CNodeVert const &bottomLeft,
CNodeVert const &topRight,
unsigned short *indices,
int power,
int sideLength)
{
if (power == 1)
{
// Ok, add triangles. All we do here is follow a list of verts (g_NodeTriWinding)
// around the center vert of this node and make triangles.
int iCurTri = 0;
CNodeVert verts[3];
// verts[0] is always the center vert.
SetupCoordXY(verts[0], bottomLeft, topRight, CNodeVert(1, 1));
int iCurVert = 1;
for (int i = 0; i < 9; i++)
{
SetupCoordXY(verts[iCurVert], bottomLeft, topRight, g_NodeTriWinding[i]);
++iCurVert;
if (iCurVert == 3)
{
for (int iTriVert = 2; iTriVert >= 0; iTriVert--)
{
int index = verts[iTriVert].y * sideLength + verts[iTriVert].x;
*indices = index;
++indices;
}
// Setup for the next triangle.
verts[1] = verts[2];
iCurVert = 2;
iCurTri++;
}
}
}
else
{
// Recurse into the children.
for (int i = 0; i < 4; i++)
{
CNodeVert childBottomLeft, childTopRight;
SetupCoordXY(childBottomLeft, bottomLeft, topRight, g_NodeChildLookup[i][0]);
SetupCoordXY(childTopRight, bottomLeft, topRight, g_NodeChildLookup[i][1]);
indices = DispCommon_GenerateTriIndices_R(childBottomLeft, childTopRight, indices, power - 1, sideLength);
}
}
return indices;
}
// ------------------------------------------------------------------------------------------- //
// CDispUtilsHelper functions.
// ------------------------------------------------------------------------------------------- //
int CDispUtilsHelper::GetPower() const
{
return GetPowerInfo()->GetPower();
}
int CDispUtilsHelper::GetSideLength() const
{
return GetPowerInfo()->GetSideLength();
}
const CVertIndex& CDispUtilsHelper::GetCornerPointIndex(int iCorner) const
{
return GetPowerInfo()->GetCornerPointIndex(iCorner);
}
int CDispUtilsHelper::VertIndexToInt(const CVertIndex &i) const
{
Assert(i.x >= 0 && i.x < GetSideLength() && i.y >= 0 && i.y < GetSideLength());
return i.y * GetSideLength() + i.x;
}
CVertIndex CDispUtilsHelper::GetEdgeMidPoint(int iEdge) const
{
int end = GetSideLength() - 1;
int mid = GetPowerInfo()->GetMidPoint();
if (iEdge == NEIGHBOREDGE_LEFT)
return CVertIndex(0, mid);
else if (iEdge == NEIGHBOREDGE_TOP)
return CVertIndex(mid, end);
else if (iEdge == NEIGHBOREDGE_RIGHT)
return CVertIndex(end, mid);
else if (iEdge == NEIGHBOREDGE_BOTTOM)
return CVertIndex(mid, 0);
Assert(false);
return CVertIndex(0, 0);
}
int DispCommon_GetNumTriIndices(int power)
{
return (1 << power) * (1 << power) * 2 * 3;
}
void DispCommon_GenerateTriIndices(int power, unsigned short *indices)
{
int sideLength = 1 << power;
DispCommon_GenerateTriIndices_R(
CNodeVert(0, 0),
CNodeVert(sideLength, sideLength),
indices,
power,
sideLength + 1);
}
//=============================================================================
//
// Finding neighbors.
//
// This table swaps MIDPOINT_TO_CORNER and CORNER_TO_MIDPOINT.
static NeighborSpan g_SpanFlip[3] = { CORNER_TO_CORNER, MIDPOINT_TO_CORNER, CORNER_TO_MIDPOINT };
static bool g_bEdgeNeighborFlip[4] = { false, false, true, true };
// These map CCoreDispSurface neighbor orientations (which are actually edge indices)
// into our 'degrees of rotation' representation.
static int g_CoreDispNeighborOrientationMap[4][4] =
{
{ ORIENTATION_CCW_180, ORIENTATION_CCW_270, ORIENTATION_CCW_0, ORIENTATION_CCW_90 },
{ ORIENTATION_CCW_90, ORIENTATION_CCW_180, ORIENTATION_CCW_270, ORIENTATION_CCW_0 },
{ ORIENTATION_CCW_0, ORIENTATION_CCW_90, ORIENTATION_CCW_180, ORIENTATION_CCW_270 },
{ ORIENTATION_CCW_270, ORIENTATION_CCW_0, ORIENTATION_CCW_90, ORIENTATION_CCW_180 }
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void ClearNeighborData(CCoreDispInfo *pDisp)
{
for (int i = 0; i < 4; i++)
{
pDisp->GetEdgeNeighbor(i)->SetInvalid();
pDisp->GetCornerNeighbors(i)->SetInvalid();
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void GetDispBox(CCoreDispInfo *pDisp, CDispBox &box)
{
// Calculate the bbox for this displacement.
Vector vMin(1e24, 1e24, 1e24);
Vector vMax(-1e24, -1e24, -1e24);
for (int iVert = 0; iVert < 4; ++iVert)
{
const Vector &vTest = pDisp->GetSurface()->GetPoint(iVert);
VectorMin(vTest, vMin, vMin);
VectorMax(vTest, vMax, vMax);
}
// Puff the box out a little.
static float flPuff = 0.1f;
vMin -= Vector(flPuff, flPuff, flPuff);
vMax += Vector(flPuff, flPuff, flPuff);
box.m_Min = vMin;
box.m_Max = vMax;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SetupDispBoxes(CCoreDispInfo **ppListBase, int nListSize, CUtlVector<CDispBox> &out)
{
out.SetSize(nListSize);
for (int iDisp = 0; iDisp < nListSize; ++iDisp)
{
CCoreDispInfo *pDisp = ppListBase[iDisp];
GetDispBox(pDisp, out[iDisp]);
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline bool DoBBoxesTouch(const CDispBox &a, const CDispBox &b)
{
for (int i = 0; i < 3; i++)
{
if (a.m_Max[i] < b.m_Min[i])
return false;
if (a.m_Min[i] > b.m_Max[i])
return false;
}
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool FindEdge(CCoreDispInfo *pInfo, Vector const &vPoint1, Vector const &vPoint2, int &iEdge)
{
CCoreDispSurface *pSurface = pInfo->GetSurface();
for (iEdge = 0; iEdge < 4; iEdge++)
{
if (VectorsAreEqual(vPoint1, pSurface->GetPoint(iEdge), 0.01f) &&
VectorsAreEqual(vPoint2, pSurface->GetPoint((iEdge + 1) & 3), 0.01f))
{
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
NeighborSpan NeighborSpanFlip(int iEdge, NeighborSpan span)
{
if (g_bEdgeNeighborFlip[iEdge])
return g_SpanFlip[span];
else
return span;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void AddNeighbor(CCoreDispInfo *pMain,
int iEdge, // Which of pMain's sides this is on.
int iSub, // Which sub neighbor this takes up in pSide.
NeighborSpan span, // What span this fills in pMain.
CCoreDispInfo *pOther, int iNeighborEdge, NeighborSpan nbSpan)
{
// The edge iteration before coming in here goes 0-1, 1-2, 2-3, 3-4.
// This flips the sense of CORNER_TO_MIDPOINT/MIDPOINT_TO_CORNER on the right and
// bottom edges and is undone here.
span = NeighborSpanFlip(iEdge, span);
nbSpan = NeighborSpanFlip(iNeighborEdge, nbSpan);
// Get the subspan this fills on our displacement.
CDispSubNeighbor *pSub = &pMain->GetEdgeNeighbor(iEdge)->m_SubNeighbors[iSub];
// Which subspan does this use in the neighbor?
CDispSubNeighbor *pNeighborSub;
if (nbSpan == MIDPOINT_TO_CORNER)
{
pNeighborSub = &pOther->GetEdgeNeighbor(iNeighborEdge)->m_SubNeighbors[1];
}
else
{
pNeighborSub = &pOther->GetEdgeNeighbor(iNeighborEdge)->m_SubNeighbors[0];
}
// Make sure this slot isn't used on either displacement.
if (pSub->IsValid() || pNeighborSub->IsValid())
{
ExecuteOnce(Warning("Found a displacement edge abutting multiple other edges.\n"));
return;
}
// Now just copy the data into each displacement.
pSub->m_iNeighbor = pOther->GetListIndex();
pSub->m_NeighborOrientation = g_CoreDispNeighborOrientationMap[iEdge][iNeighborEdge];
pSub->m_Span = span;
pSub->m_NeighborSpan = nbSpan;
pNeighborSub->m_iNeighbor = pMain->GetListIndex();
pNeighborSub->m_NeighborOrientation = g_CoreDispNeighborOrientationMap[iNeighborEdge][iEdge];
pNeighborSub->m_Span = nbSpan;
pNeighborSub->m_NeighborSpan = span;
#if defined( _DEBUG )
// Walk an iterator over the new connection to make sure it works.
CDispSubEdgeIterator it;
it.Start(pMain, iEdge, iSub);
while (it.Next())
{
CVertIndex nbIndex;
TransformIntoNeighbor(pMain, iEdge, it.GetVertIndex(), nbIndex);
}
#endif
}
//-----------------------------------------------------------------------------
// This function is symmetric wrt pMain and pOther. It sets up valid neighboring data for
// the relationship between both of them.
//-----------------------------------------------------------------------------
void SetupEdgeNeighbors(CCoreDispInfo *pMain, CCoreDispInfo *pOther)
{
// Initialize..
for (int iEdge = 0; iEdge < 4; iEdge++)
{
// Setup the edge points and the midpoint.
Vector pt[2], mid;
pMain->GetSurface()->GetPoint(iEdge, pt[0]);
pMain->GetSurface()->GetPoint((iEdge + 1) & 3, pt[1]);
mid = (pt[0] + pt[1]) * 0.5f;
// Find neighbors.
int iNBEdge;
if (FindEdge(pOther, pt[1], pt[0], iNBEdge))
{
AddNeighbor(pMain, iEdge, 0, CORNER_TO_CORNER, pOther, iNBEdge, CORNER_TO_CORNER);
}
else
{
// Look for one that takes up our whole side.
if (FindEdge(pOther, pt[1], pt[0] * 2 - pt[1], iNBEdge))
{
AddNeighbor(pMain, iEdge, 0, CORNER_TO_CORNER, pOther, iNBEdge, CORNER_TO_MIDPOINT);
}
else if (FindEdge(pOther, pt[1] * 2 - pt[0], pt[0], iNBEdge))
{
AddNeighbor(pMain, iEdge, 0, CORNER_TO_CORNER, pOther, iNBEdge, MIDPOINT_TO_CORNER);
}
else
{
// Ok, look for 1 or two that abut this side.
if (FindEdge(pOther, mid, pt[0], iNBEdge))
{
AddNeighbor(pMain, iEdge, g_bEdgeNeighborFlip[iEdge], CORNER_TO_MIDPOINT, pOther, iNBEdge, CORNER_TO_CORNER);
}
if (FindEdge(pOther, pt[1], mid, iNBEdge))
{
AddNeighbor(pMain, iEdge, !g_bEdgeNeighborFlip[iEdge], MIDPOINT_TO_CORNER, pOther, iNBEdge, CORNER_TO_CORNER);
}
}
}
}
}
//-----------------------------------------------------------------------------
// Returns true if the displacement has an edge neighbor with the given index.
//-----------------------------------------------------------------------------
bool HasEdgeNeighbor(const CCoreDispInfo *pMain, int iNeighbor)
{
for (int i = 0; i < 4; i++)
{
const CDispCornerNeighbors *pCorner = pMain->GetCornerNeighbors(i);
for (int iNB = 0; iNB < pCorner->m_nNeighbors; iNB++)
if (pCorner->m_Neighbors[iNB] == iNeighbor)
return true;
const CDispNeighbor *pEdge = pMain->GetEdgeNeighbor(i);
if (pEdge->m_SubNeighbors[0].GetNeighborIndex() == iNeighbor ||
pEdge->m_SubNeighbors[1].GetNeighborIndex() == iNeighbor)
{
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SetupCornerNeighbors(CCoreDispInfo *pMain, CCoreDispInfo *pOther, int *nOverflows)
{
if (HasEdgeNeighbor(pMain, pOther->GetListIndex()))
return;
// Do these two share a vertex?
int nShared = 0;
int iMainSharedCorner = -1;
int iOtherSharedCorner = -1;
for (int iMainCorner = 0; iMainCorner < 4; iMainCorner++)
{
Vector const &vMainCorner = pMain->GetCornerPoint(iMainCorner);
for (int iOtherCorner = 0; iOtherCorner < 4; iOtherCorner++)
{
Vector const &vOtherCorner = pOther->GetCornerPoint(iOtherCorner);
if (VectorsAreEqual(vMainCorner, vOtherCorner, 0.001f))
{
iMainSharedCorner = iMainCorner;
iOtherSharedCorner = iOtherCorner;
++nShared;
}
}
}
if (nShared == 1)
{
CDispCornerNeighbors *pMainCorner = pMain->GetCornerNeighbors(iMainSharedCorner);
CDispCornerNeighbors *pOtherCorner = pOther->GetCornerNeighbors(iOtherSharedCorner);
if (pMainCorner->m_nNeighbors < MAX_DISP_CORNER_NEIGHBORS &&
pOtherCorner->m_nNeighbors < MAX_DISP_CORNER_NEIGHBORS)
{
pMainCorner->m_Neighbors[pMainCorner->m_nNeighbors++] = pOther->GetListIndex();
pOtherCorner->m_Neighbors[pOtherCorner->m_nNeighbors++] = pMain->GetListIndex();
}
else
{
++(*nOverflows);
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool VerifyNeighborVertConnection(CDispUtilsHelper *pDisp, const CVertIndex &nodeIndex,
const CDispUtilsHelper *pTestNeighbor, const CVertIndex &testNeighborIndex,
int mySide)
{
CVertIndex nbIndex(-1, -1);
CDispUtilsHelper *pNeighbor = NULL;
if ((pNeighbor = TransformIntoNeighbor(pDisp, mySide, nodeIndex, nbIndex)) != NULL)
{
if (pTestNeighbor != pNeighbor || nbIndex != testNeighborIndex)
return false;
CVertIndex testIndex(-1, -1);
int iSide = GetEdgeIndexFromPoint(nbIndex, pNeighbor->GetPowerInfo()->m_Power);
if (iSide == -1)
{
return false;
}
CDispUtilsHelper *pTest = TransformIntoNeighbor(pNeighbor, iSide, nbIndex, testIndex);
if (pTest != pDisp || nodeIndex != testIndex)
{
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void VerifyNeighborConnections(CCoreDispInfo **ppListBase, int nDisps)
{
while (1)
{
bool bHappy = true;
int iDisp;
for (iDisp = 0; iDisp < nDisps; ++iDisp)
{
CCoreDispInfo *pDisp = ppListBase[iDisp];
CDispUtilsHelper *pHelper = pDisp;
for (int iEdge = 0; iEdge < 4; iEdge++)
{
CDispEdgeIterator it(pHelper, iEdge);
while (it.Next())
{
if (!VerifyNeighborVertConnection(pHelper, it.GetVertIndex(), it.GetCurrentNeighbor(), it.GetNBVertIndex(), iEdge))
{
pDisp->GetEdgeNeighbor(iEdge)->SetInvalid();
Warning("Warning: invalid neighbor connection on displacement near (%.2f %.2f %.2f)\n", VectorExpand(pDisp->GetCornerPoint(0)));
bHappy = false;
}
}
}
}
if (bHappy)
break;
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void FindNeighboringDispSurfs(CCoreDispInfo **ppListBase, int nListSize)
{
// First, clear all neighboring data.
int iDisp;
for (iDisp = 0; iDisp < nListSize; ++iDisp)
{
ClearNeighborData(ppListBase[iDisp]);
}
CUtlVector<CDispBox> boxes;
SetupDispBoxes(ppListBase, nListSize, boxes);
int nCornerOverflows = 0;
// Now test all pairs of displacements and setup neighboring relations between them.
for (iDisp = 0; iDisp < nListSize; ++iDisp)
{
CCoreDispInfo *pMain = ppListBase[iDisp];
for (int iDisp2 = iDisp + 1; iDisp2 < nListSize; ++iDisp2)
{
CCoreDispInfo *pOther = ppListBase[iDisp2];
// Trivial reject.
if (!DoBBoxesTouch(boxes[iDisp], boxes[iDisp2]))
continue;
SetupEdgeNeighbors(pMain, pOther);
// NOTE: this must come after SetupEdgeNeighbors because it makes sure not to add
// corner neighbors for disps that are already edge neighbors.
SetupCornerNeighbors(pMain, pOther, &nCornerOverflows);
}
}
if (nCornerOverflows)
{
Warning("Warning: overflowed %d displacement corner-neighbor lists.", nCornerOverflows);
}
// Debug check.. make sure the neighbor connections are intact (make sure that any
// edge vert that gets mapped into a neighbor gets mapped back the same way).
VerifyNeighborConnections(ppListBase, nListSize);
}
//=============================================================================
//
// Allowable verts.
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int IsCorner(CVertIndex const &index, int sideLength)
{
if (index.x == 0)
{
if (index.y == 0)
return true;
else if (index.y == sideLength - 1)
return true;
}
else if (index.x == sideLength - 1)
{
if (index.y == 0)
return true;
else if (index.y == sideLength - 1)
return true;
}
return false;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool IsVertAllowed(CDispUtilsHelper *pDisp, CVertIndex const &sideVert, int iLevel)
{
if (IsCorner(sideVert, pDisp->GetPowerInfo()->GetSideLength()))
return true;
int iSide = GetEdgeIndexFromPoint(sideVert, pDisp->GetPowerInfo()->GetPower());
if (iSide == -1)
return true;
int iSub = GetSubNeighborIndex(pDisp, iSide, sideVert);
if (iSub == -1)
return true;
CDispSubNeighbor *pSub = &pDisp->GetEdgeNeighbor(iSide)->m_SubNeighbors[iSub];
CDispUtilsHelper *pNeighbor = pDisp->GetDispUtilsByIndex(pSub->m_iNeighbor);
Assert(pNeighbor);
// Ok, there is a neighbor.. see if this vertex exists in the neighbor.
CShiftInfo *pShiftInfo = &g_ShiftInfos[pSub->m_Span][pSub->m_NeighborSpan];
Assert(pShiftInfo->m_bValid);
if ((pNeighbor->GetPowerInfo()->GetPower() + pShiftInfo->m_PowerShiftAdd) < (iLevel + 1))
{
return false;
}
// Ok, it exists. Make sure the neighbor hasn't disallowed it.
CVertIndex nbIndex;
TransformIntoSubNeighbor(pDisp, iSide, iSub, sideVert, nbIndex);
CBitVec<MAX_DISPVERTS> &allowedVerts = CCoreDispInfo::FromDispUtils(pNeighbor)->GetAllowedVerts();
return !!allowedVerts.Get(pNeighbor->VertIndexToInt(nbIndex));
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void UnallowVerts_R(CDispUtilsHelper *pDisp, CVertIndex const &nodeIndex, int &nUnallowed)
{
int iNodeIndex = pDisp->VertIndexToInt(nodeIndex);
CCoreDispInfo *pCoreDisp = CCoreDispInfo::FromDispUtils(pDisp);
if (!pCoreDisp->GetAllowedVerts().Get(iNodeIndex))
return;
nUnallowed++;
pCoreDisp->GetAllowedVerts().Clear(iNodeIndex);
for (int iDep = 0; iDep < CVertInfo::NUM_REVERSE_DEPENDENCIES; iDep++)
{
CVertDependency &dep = pDisp->GetPowerInfo()->m_pVertInfo[iNodeIndex].m_ReverseDependencies[iDep];
if (dep.m_iVert.x != -1 && dep.m_iNeighbor == -1)
{
UnallowVerts_R(pDisp, dep.m_iVert, nUnallowed);
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void DisableUnallowedVerts_R(CDispUtilsHelper *pDisp, CVertIndex const &nodeIndex, int iLevel, int &nUnallowed)
{
int iNodeIndex = pDisp->VertIndexToInt(nodeIndex);
// This vertex is not allowed if it is on an edge with a neighbor
// that does not have this vertex.
// Test side verts.
for (int iSide = 0; iSide < 4; iSide++)
{
CVertIndex const &sideVert = pDisp->GetPowerInfo()->m_pSideVerts[iNodeIndex].m_Verts[iSide];
if (!IsVertAllowed(pDisp, sideVert, iLevel))
{
// This vert (and its dependencies) can't exist.
UnallowVerts_R(pDisp, sideVert, nUnallowed);
}
}
#if 0
// Test dependencies.
for (int iDep = 0; iDep < 2; iDep++)
{
CVertDependency const &dep = pDisp->GetPowerInfo()->m_pVertInfo[iNodeIndex].m_Dependencies[iDep];
if (dep.m_iNeighbor == -1 && !IsVertAllowed(pDisp, dep.m_iVert, iLevel))
{
UnallowVerts_R(pDisp, nodeIndex, nUnallowed);
}
}
#endif
// Recurse.
if (iLevel + 1 < pDisp->GetPower())
{
for (int iChild = 0; iChild < 4; iChild++)
{
DisableUnallowedVerts_R(pDisp, pDisp->GetPowerInfo()->m_pChildVerts[iNodeIndex].m_Verts[iChild], iLevel + 1, nUnallowed);
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SetupAllowedVerts(CCoreDispInfo **ppListBase, int nListSize)
{
// Set all verts to allowed to start with.
int iDisp;
for (iDisp = 0; iDisp < nListSize; ++iDisp)
{
ppListBase[iDisp]->GetAllowedVerts().SetAll();
}
// Disable verts that need to be disabled so higher-powered displacements remove
// the necessary triangles when bordering lower-powered displacements.
// It is necessary to loop around here because disabling verts can accumulate into
// neighbors.
bool bContinue;
do
{
bContinue = false;
for (iDisp = 0; iDisp < nListSize; ++iDisp)
{
CDispUtilsHelper *pDisp = ppListBase[iDisp];
int nUnallowed = 0;
DisableUnallowedVerts_R(pDisp, pDisp->GetPowerInfo()->m_RootNode, 0, nUnallowed);
if (nUnallowed)
bContinue = true;
}
} while (bContinue);
}