mirror of
https://github.com/Gigaslav/HL2Overcharged.git
synced 2026-01-01 09:48:38 +03:00
1080 lines
32 KiB
C++
1080 lines
32 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include "vrad.h"
|
|
#include "VRAD_DispColl.h"
|
|
#include "DispColl_Common.h"
|
|
#include "radial.h"
|
|
#include "CollisionUtils.h"
|
|
#include "tier0\dbg.h"
|
|
|
|
#define SAMPLE_BBOX_SLOP 5.0f
|
|
#define TRIEDGE_EPSILON 0.001f
|
|
|
|
float g_flMaxDispSampleSize = 512.0f;
|
|
|
|
static FileHandle_t pDispFile = FILESYSTEM_INVALID_HANDLE;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
CVRADDispColl::CVRADDispColl()
|
|
{
|
|
m_iParent = -1;
|
|
|
|
m_flSampleRadius2 = 0.0f;
|
|
m_flPatchSampleRadius2 = 0.0f;
|
|
|
|
m_flSampleWidth = 0.0f;
|
|
m_flSampleHeight = 0.0f;
|
|
|
|
m_aLuxelCoords.Purge();
|
|
m_aVertNormals.Purge();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
CVRADDispColl::~CVRADDispColl()
|
|
{
|
|
m_aLuxelCoords.Purge();
|
|
m_aVertNormals.Purge();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
bool CVRADDispColl::Create(CCoreDispInfo *pDisp)
|
|
{
|
|
// Base class create.
|
|
if (!CDispCollTree::Create(pDisp))
|
|
return false;
|
|
|
|
// Allocate VRad specific memory.
|
|
m_aLuxelCoords.SetSize(GetSize());
|
|
m_aVertNormals.SetSize(GetSize());
|
|
|
|
// VRad specific base surface data.
|
|
CCoreDispSurface *pSurf = pDisp->GetSurface();
|
|
m_iParent = pSurf->GetHandle();
|
|
|
|
// VRad specific displacement surface data.
|
|
for (int iVert = 0; iVert < m_aVerts.Count(); ++iVert)
|
|
{
|
|
pDisp->GetNormal(iVert, m_aVertNormals[iVert]);
|
|
pDisp->GetLuxelCoord(0, iVert, m_aLuxelCoords[iVert]);
|
|
}
|
|
|
|
// Re-calculate the lightmap size (in uv) so that the luxels give
|
|
// a better world-space uniform approx. due to the non-linear nature
|
|
// of the displacement surface in uv-space
|
|
dface_t *pFace = &g_pFaces[m_iParent];
|
|
if (pFace)
|
|
{
|
|
CalcSampleRadius2AndBox(pFace);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVRADDispColl::CalcSampleRadius2AndBox(dface_t *pFace)
|
|
{
|
|
// Get the luxel sample size.
|
|
texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
|
|
Assert(pTexInfo);
|
|
if (!pTexInfo)
|
|
return;
|
|
|
|
// Todo: Width = Height now, should change all the code to look at one value.
|
|
Vector vecTmp(pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0],
|
|
pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1],
|
|
pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2]);
|
|
float flWidth = 1.0f / VectorLength(vecTmp);
|
|
float flHeight = flWidth;
|
|
|
|
// Save off the sample width and height.
|
|
m_flSampleWidth = flWidth;
|
|
m_flSampleHeight = flHeight;
|
|
|
|
// Calculate the sample radius squared.
|
|
float flSampleRadius = sqrt(((flWidth * flWidth) + (flHeight * flHeight))) * 2.2f;//RADIALDIST2;
|
|
if (flSampleRadius > g_flMaxDispSampleSize)
|
|
{
|
|
flSampleRadius = g_flMaxDispSampleSize;
|
|
}
|
|
m_flSampleRadius2 = flSampleRadius * flSampleRadius;
|
|
|
|
// Calculate the patch radius - the max sample edge length * the number of luxels per edge "chop."
|
|
float flSampleSize = max(m_flSampleWidth, m_flSampleHeight);
|
|
float flPatchSampleRadius = flSampleSize * dispchop * 2.2f;
|
|
if (flPatchSampleRadius > g_MaxDispPatchRadius)
|
|
{
|
|
flPatchSampleRadius = g_MaxDispPatchRadius;
|
|
Warning("Patch Sample Radius Clamped!\n");
|
|
}
|
|
m_flPatchSampleRadius2 = flPatchSampleRadius * flPatchSampleRadius;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Get the min/max of the displacement surface.
|
|
//-----------------------------------------------------------------------------
|
|
void CVRADDispColl::GetSurfaceMinMax(Vector &boxMin, Vector &boxMax)
|
|
{
|
|
// Initialize the minimum and maximum box
|
|
boxMin = m_aVerts[0];
|
|
boxMax = m_aVerts[0];
|
|
|
|
for (int i = 1; i < m_aVerts.Count(); i++)
|
|
{
|
|
if (m_aVerts[i].x < boxMin.x) { boxMin.x = m_aVerts[i].x; }
|
|
if (m_aVerts[i].y < boxMin.y) { boxMin.y = m_aVerts[i].y; }
|
|
if (m_aVerts[i].z < boxMin.z) { boxMin.z = m_aVerts[i].z; }
|
|
|
|
if (m_aVerts[i].x > boxMax.x) { boxMax.x = m_aVerts[i].x; }
|
|
if (m_aVerts[i].y > boxMax.y) { boxMax.y = m_aVerts[i].y; }
|
|
if (m_aVerts[i].z > boxMax.z) { boxMax.z = m_aVerts[i].z; }
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Find the minor projection axes based on the given normal.
|
|
//-----------------------------------------------------------------------------
|
|
void CVRADDispColl::GetMinorAxes(Vector const &vecNormal, int &nAxis0, int &nAxis1)
|
|
{
|
|
nAxis0 = 0;
|
|
nAxis1 = 1;
|
|
|
|
if (FloatMakePositive(vecNormal.x) > FloatMakePositive(vecNormal.y))
|
|
{
|
|
if (FloatMakePositive(vecNormal.x) > FloatMakePositive(vecNormal.z))
|
|
{
|
|
nAxis0 = 1;
|
|
nAxis1 = 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FloatMakePositive(vecNormal.y) > FloatMakePositive(vecNormal.z))
|
|
{
|
|
nAxis0 = 0;
|
|
nAxis1 = 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
void CVRADDispColl::BaseFacePlaneToDispUV(Vector const &vecPlanePt, Vector2D &dispUV)
|
|
{
|
|
PointInQuadToBarycentric(m_vecSurfPoints[0], m_vecSurfPoints[3], m_vecSurfPoints[2], m_vecSurfPoints[1], vecPlanePt, dispUV);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
void CVRADDispColl::DispUVToSurfPoint(Vector2D const &dispUV, Vector &vecPoint, float flPushEps)
|
|
{
|
|
// Check to see that the point is on the surface.
|
|
if (dispUV.x < 0.0f || dispUV.x > 1.0f || dispUV.y < 0.0f || dispUV.y > 1.0f)
|
|
return;
|
|
|
|
// Get the displacement power.
|
|
int nWidth = ((1 << m_nPower) + 1);
|
|
int nHeight = nWidth;
|
|
|
|
// Scale the U, V coordinates to the displacement grid size.
|
|
float flU = dispUV.x * static_cast<float>(nWidth - 1.000001f);
|
|
float flV = dispUV.y * static_cast<float>(nHeight - 1.000001f);
|
|
|
|
// Find the base U, V.
|
|
int nSnapU = static_cast<int>(flU);
|
|
int nSnapV = static_cast<int>(flV);
|
|
|
|
// Use this to get the triangle orientation.
|
|
bool bOdd = (((nSnapV * nWidth) + nSnapU) % 2 == 1);
|
|
|
|
// Top Left to Bottom Right
|
|
if (bOdd)
|
|
{
|
|
DispUVToSurf_TriTLToBR(vecPoint, flPushEps, flU, flV, nSnapU, nSnapV, nWidth, nHeight);
|
|
}
|
|
// Bottom Left to Top Right
|
|
else
|
|
{
|
|
DispUVToSurf_TriBLToTR(vecPoint, flPushEps, flU, flV, nSnapU, nSnapV, nWidth, nHeight);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVRADDispColl::DispUVToSurf_TriTLToBR(Vector &vecPoint, float flPushEps,
|
|
float flU, float flV, int nSnapU, int nSnapV,
|
|
int nWidth, int nHeight)
|
|
{
|
|
int nNextU = nSnapU + 1;
|
|
int nNextV = nSnapV + 1;
|
|
if (nNextU == nWidth) { --nNextU; }
|
|
if (nNextV == nHeight) { --nNextV; }
|
|
|
|
float flFracU = flU - static_cast<float>(nSnapU);
|
|
float flFracV = flV - static_cast<float>(nSnapV);
|
|
|
|
if ((flFracU + flFracV) >= (1.0f + TRIEDGE_EPSILON))
|
|
{
|
|
int nIndices[3];
|
|
nIndices[0] = nNextV * nWidth + nSnapU;
|
|
nIndices[1] = nNextV * nWidth + nNextU;
|
|
nIndices[2] = nSnapV * nWidth + nNextU;
|
|
|
|
Vector edgeU = m_aVerts[nIndices[0]] - m_aVerts[nIndices[1]];
|
|
Vector edgeV = m_aVerts[nIndices[2]] - m_aVerts[nIndices[1]];
|
|
vecPoint = m_aVerts[nIndices[1]] + edgeU * (1.0f - flFracU) + edgeV * (1.0f - flFracV);
|
|
|
|
if (flPushEps != 0.0f)
|
|
{
|
|
Vector vecNormal;
|
|
vecNormal = CrossProduct(edgeU, edgeV);
|
|
VectorNormalize(vecNormal);
|
|
vecPoint += (vecNormal * flPushEps);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int nIndices[3];
|
|
nIndices[0] = nSnapV * nWidth + nSnapU;
|
|
nIndices[1] = nNextV * nWidth + nSnapU;
|
|
nIndices[2] = nSnapV * nWidth + nNextU;
|
|
|
|
Vector edgeU = m_aVerts[nIndices[2]] - m_aVerts[nIndices[0]];
|
|
Vector edgeV = m_aVerts[nIndices[1]] - m_aVerts[nIndices[0]];
|
|
vecPoint = m_aVerts[nIndices[0]] + edgeU * flFracU + edgeV * flFracV;
|
|
|
|
if (flPushEps != 0.0f)
|
|
{
|
|
Vector vecNormal;
|
|
vecNormal = CrossProduct(edgeU, edgeV);
|
|
VectorNormalize(vecNormal);
|
|
vecPoint += (vecNormal * flPushEps);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVRADDispColl::DispUVToSurf_TriBLToTR(Vector &vecPoint, float flPushEps,
|
|
float flU, float flV, int nSnapU, int nSnapV,
|
|
int nWidth, int nHeight)
|
|
{
|
|
int nNextU = nSnapU + 1;
|
|
int nNextV = nSnapV + 1;
|
|
if (nNextU == nWidth) { --nNextU; }
|
|
if (nNextV == nHeight) { --nNextV; }
|
|
|
|
float flFracU = flU - static_cast<float>(nSnapU);
|
|
float flFracV = flV - static_cast<float>(nSnapV);
|
|
|
|
if (flFracU < flFracV)
|
|
{
|
|
int nIndices[3];
|
|
nIndices[0] = nSnapV * nWidth + nSnapU;
|
|
nIndices[1] = nNextV * nWidth + nSnapU;
|
|
nIndices[2] = nNextV * nWidth + nNextU;
|
|
|
|
Vector edgeU = m_aVerts[nIndices[2]] - m_aVerts[nIndices[1]];
|
|
Vector edgeV = m_aVerts[nIndices[0]] - m_aVerts[nIndices[1]];
|
|
vecPoint = m_aVerts[nIndices[1]] + edgeU * flFracU + edgeV * (1.0f - flFracV);
|
|
|
|
if (flPushEps != 0.0f)
|
|
{
|
|
Vector vecNormal;
|
|
vecNormal = CrossProduct(edgeV, edgeU);
|
|
VectorNormalize(vecNormal);
|
|
vecPoint += (vecNormal * flPushEps);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int nIndices[3];
|
|
nIndices[0] = nSnapV * nWidth + nSnapU;
|
|
nIndices[1] = nNextV * nWidth + nNextU;
|
|
nIndices[2] = nSnapV * nWidth + nNextU;
|
|
|
|
Vector edgeU = m_aVerts[nIndices[0]] - m_aVerts[nIndices[2]];
|
|
Vector edgeV = m_aVerts[nIndices[1]] - m_aVerts[nIndices[2]];
|
|
vecPoint = m_aVerts[nIndices[2]] + edgeU * (1.0f - flFracU) + edgeV * flFracV;
|
|
|
|
if (flPushEps != 0.0f)
|
|
{
|
|
Vector vecNormal;
|
|
vecNormal = CrossProduct(edgeV, edgeU);
|
|
VectorNormalize(vecNormal);
|
|
vecPoint += (vecNormal * flPushEps);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
void CVRADDispColl::DispUVToSurfNormal(Vector2D const &dispUV, Vector &vecNormal)
|
|
{
|
|
// Check to see that the point is on the surface.
|
|
if (dispUV.x < 0.0f || dispUV.x > 1.0f || dispUV.y < 0.0f || dispUV.y > 1.0f)
|
|
return;
|
|
|
|
// Get the displacement power.
|
|
int nWidth = ((1 << m_nPower) + 1);
|
|
int nHeight = nWidth;
|
|
|
|
// Scale the U, V coordinates to the displacement grid size.
|
|
float flU = dispUV.x * static_cast<float>(nWidth - 1.000001f);
|
|
float flV = dispUV.y * static_cast<float>(nHeight - 1.000001f);
|
|
|
|
// Find the base U, V.
|
|
int nSnapU = static_cast<int>(flU);
|
|
int nSnapV = static_cast<int>(flV);
|
|
|
|
int nNextU = nSnapU + 1;
|
|
int nNextV = nSnapV + 1;
|
|
if (nNextU == nWidth) { --nNextU; }
|
|
if (nNextV == nHeight) { --nNextV; }
|
|
|
|
float flFracU = flU - static_cast<float>(nSnapU);
|
|
float flFracV = flV - static_cast<float>(nSnapV);
|
|
|
|
// Get the four normals "around" the "spot"
|
|
int iQuad[VRAD_QUAD_SIZE];
|
|
iQuad[0] = (nSnapV * nWidth) + nSnapU;
|
|
iQuad[1] = (nNextV * nWidth) + nSnapU;
|
|
iQuad[2] = (nNextV * nWidth) + nNextU;
|
|
iQuad[3] = (nSnapV * nWidth) + nNextU;
|
|
|
|
// Find the blended normal (bi-linear).
|
|
Vector vecTmpNormals[2], vecBlendedNormals[2], vecDispNormals[4];
|
|
|
|
for (int iVert = 0; iVert < VRAD_QUAD_SIZE; ++iVert)
|
|
{
|
|
GetVertNormal(iQuad[iVert], vecDispNormals[iVert]);
|
|
}
|
|
|
|
vecTmpNormals[0] = vecDispNormals[0] * (1.0f - flFracU);
|
|
vecTmpNormals[1] = vecDispNormals[3] * flFracU;
|
|
vecBlendedNormals[0] = vecTmpNormals[0] + vecTmpNormals[1];
|
|
VectorNormalize(vecBlendedNormals[0]);
|
|
|
|
vecTmpNormals[0] = vecDispNormals[1] * (1.0f - flFracU);
|
|
vecTmpNormals[1] = vecDispNormals[2] * flFracU;
|
|
vecBlendedNormals[1] = vecTmpNormals[0] + vecTmpNormals[1];
|
|
VectorNormalize(vecBlendedNormals[1]);
|
|
|
|
vecTmpNormals[0] = vecBlendedNormals[0] * (1.0f - flFracV);
|
|
vecTmpNormals[1] = vecBlendedNormals[1] * flFracV;
|
|
|
|
vecNormal = vecTmpNormals[0] + vecTmpNormals[1];
|
|
VectorNormalize(vecNormal);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : float
|
|
//-----------------------------------------------------------------------------
|
|
float CVRADDispColl::CreateParentPatches(void)
|
|
{
|
|
// Save the total surface area of the displacement.
|
|
float flTotalArea = 0.0f;
|
|
|
|
// Get the number of displacement subdivisions.
|
|
int nInterval = GetWidth();
|
|
|
|
Vector vecPoints[4];
|
|
vecPoints[0].Init(m_aVerts[0].x, m_aVerts[0].y, m_aVerts[0].z);
|
|
vecPoints[1].Init(m_aVerts[(nInterval*(nInterval - 1))].x, m_aVerts[(nInterval*(nInterval - 1))].y, m_aVerts[(nInterval*(nInterval - 1))].z);
|
|
vecPoints[2].Init(m_aVerts[((nInterval*nInterval) - 1)].x, m_aVerts[((nInterval*nInterval) - 1)].y, m_aVerts[((nInterval*nInterval) - 1)].z);
|
|
vecPoints[3].Init(m_aVerts[(nInterval - 1)].x, m_aVerts[(nInterval - 1)].y, m_aVerts[(nInterval - 1)].z);
|
|
|
|
// Create and initialize the patch.
|
|
int iPatch = g_Patches.AddToTail();
|
|
if (iPatch == g_Patches.InvalidIndex())
|
|
return flTotalArea;
|
|
|
|
// Keep track of the area of the patches.
|
|
float flArea = 0.0f;
|
|
if (!InitParentPatch(iPatch, vecPoints, flArea))
|
|
{
|
|
g_Patches.Remove(iPatch);
|
|
flArea = 0.0f;
|
|
}
|
|
|
|
// Return the displacement area.
|
|
return flArea;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : iParentPatch -
|
|
// nLevel -
|
|
//-----------------------------------------------------------------------------
|
|
void CVRADDispColl::CreateChildPatchesFromRoot(int iParentPatch, int *pChildPatch)
|
|
{
|
|
// Initialize the child patch indices.
|
|
pChildPatch[0] = g_Patches.InvalidIndex();
|
|
pChildPatch[1] = g_Patches.InvalidIndex();
|
|
|
|
// Get the number of displacement subdivisions.
|
|
int nInterval = GetWidth();
|
|
|
|
// Get the parent patch.
|
|
CPatch *pParentPatch = &g_Patches[iParentPatch];
|
|
if (!pParentPatch)
|
|
return;
|
|
|
|
// Split along the longest edge.
|
|
Vector vecEdges[4];
|
|
vecEdges[0] = pParentPatch->winding->p[1] - pParentPatch->winding->p[0];
|
|
vecEdges[1] = pParentPatch->winding->p[2] - pParentPatch->winding->p[1];
|
|
vecEdges[2] = pParentPatch->winding->p[3] - pParentPatch->winding->p[2];
|
|
vecEdges[3] = pParentPatch->winding->p[3] - pParentPatch->winding->p[0];
|
|
|
|
// Should the patch be subdivided - check the area.
|
|
float flMaxLength = max(m_flSampleWidth, m_flSampleHeight);
|
|
float flMinEdgeLength = flMaxLength * dispchop;
|
|
|
|
// Find the longest edge.
|
|
float flEdgeLength = 0.0f;
|
|
int iLongEdge = -1;
|
|
for (int iEdge = 0; iEdge < 4; ++iEdge)
|
|
{
|
|
float flLength = vecEdges[iEdge].Length();
|
|
if (flEdgeLength < flLength)
|
|
{
|
|
flEdgeLength = vecEdges[iEdge].Length();
|
|
iLongEdge = iEdge;
|
|
}
|
|
}
|
|
|
|
// Small enough already, return.
|
|
if (flEdgeLength < flMinEdgeLength)
|
|
return;
|
|
|
|
// Test area as well so we don't allow slivers.
|
|
float flMinArea = (dispchop * flMaxLength) * (dispchop * flMaxLength);
|
|
Vector vecNormal = vecEdges[3].Cross(vecEdges[0]);
|
|
float flTestArea = VectorNormalize(vecNormal);
|
|
if (flTestArea < flMinArea)
|
|
return;
|
|
|
|
// Get the points for the first triangle.
|
|
int iPoints[3];
|
|
Vector vecPoints[3];
|
|
float flArea;
|
|
|
|
iPoints[0] = (nInterval * nInterval) - 1;
|
|
iPoints[1] = 0;
|
|
iPoints[2] = nInterval * (nInterval - 1);
|
|
for (int iPoint = 0; iPoint < 3; ++iPoint)
|
|
{
|
|
VectorCopy(m_aVerts[iPoints[iPoint]], vecPoints[iPoint]);
|
|
}
|
|
|
|
// Create and initialize the patch.
|
|
pChildPatch[0] = g_Patches.AddToTail();
|
|
if (pChildPatch[0] == g_Patches.InvalidIndex())
|
|
return;
|
|
|
|
if (!InitPatch(pChildPatch[0], iParentPatch, 0, vecPoints, iPoints, flArea))
|
|
{
|
|
g_Patches.Remove(pChildPatch[0]);
|
|
pChildPatch[0] = g_Patches.InvalidIndex();
|
|
return;
|
|
}
|
|
|
|
// Get the points for the second triangle.
|
|
iPoints[0] = 0;
|
|
iPoints[1] = (nInterval * nInterval) - 1;
|
|
iPoints[2] = nInterval - 1;
|
|
for (int iPoint = 0; iPoint < 3; ++iPoint)
|
|
{
|
|
VectorCopy(m_aVerts[iPoints[iPoint]], vecPoints[iPoint]);
|
|
}
|
|
|
|
// Create and initialize the patch.
|
|
pChildPatch[1] = g_Patches.AddToTail();
|
|
if (pChildPatch[1] == g_Patches.InvalidIndex())
|
|
{
|
|
g_Patches.Remove(pChildPatch[0]);
|
|
pChildPatch[0] = g_Patches.InvalidIndex();
|
|
return;
|
|
}
|
|
|
|
if (!InitPatch(pChildPatch[1], iParentPatch, 1, vecPoints, iPoints, flArea))
|
|
{
|
|
g_Patches.Remove(pChildPatch[0]);
|
|
pChildPatch[0] = g_Patches.InvalidIndex();
|
|
g_Patches.Remove(pChildPatch[1]);
|
|
pChildPatch[1] = g_Patches.InvalidIndex();
|
|
return;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : flMinArea -
|
|
// Output : float
|
|
//-----------------------------------------------------------------------------
|
|
void CVRADDispColl::CreateChildPatches(int iParentPatch, int nLevel)
|
|
{
|
|
// Get the parent patch.
|
|
CPatch *pParentPatch = &g_Patches[iParentPatch];
|
|
if (!pParentPatch)
|
|
return;
|
|
|
|
// The root face is a quad - special case.
|
|
if (pParentPatch->winding->numpoints == 4)
|
|
{
|
|
int iChildPatch[2];
|
|
CreateChildPatchesFromRoot(iParentPatch, iChildPatch);
|
|
if (iChildPatch[0] != g_Patches.InvalidIndex() && iChildPatch[1] != g_Patches.InvalidIndex())
|
|
{
|
|
CreateChildPatches(iChildPatch[0], 0);
|
|
CreateChildPatches(iChildPatch[1], 0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Calculate the the area of the patch (triangle!).
|
|
Assert(pParentPatch->winding->numpoints == 3);
|
|
if (pParentPatch->winding->numpoints != 3)
|
|
return;
|
|
|
|
// Should the patch be subdivided - check the area.
|
|
float flMaxLength = max(m_flSampleWidth, m_flSampleHeight);
|
|
float flMinEdgeLength = flMaxLength * dispchop;
|
|
|
|
// Split along the longest edge.
|
|
Vector vecEdges[3];
|
|
vecEdges[0] = pParentPatch->winding->p[1] - pParentPatch->winding->p[0];
|
|
vecEdges[1] = pParentPatch->winding->p[2] - pParentPatch->winding->p[0];
|
|
vecEdges[2] = pParentPatch->winding->p[2] - pParentPatch->winding->p[1];
|
|
|
|
// Find the longest edge.
|
|
float flEdgeLength = 0.0f;
|
|
int iLongEdge = -1;
|
|
for (int iEdge = 0; iEdge < 3; ++iEdge)
|
|
{
|
|
if (flEdgeLength < vecEdges[iEdge].Length())
|
|
{
|
|
flEdgeLength = vecEdges[iEdge].Length();
|
|
iLongEdge = iEdge;
|
|
}
|
|
}
|
|
|
|
// Small enough already, return.
|
|
if (flEdgeLength < flMinEdgeLength)
|
|
return;
|
|
|
|
// Test area as well so we don't allow slivers.
|
|
float flMinArea = (dispchop * flMaxLength) * (dispchop * flMaxLength) * 0.5f;
|
|
Vector vecNormal = vecEdges[1].Cross(vecEdges[0]);
|
|
float flTestArea = VectorNormalize(vecNormal);
|
|
flTestArea *= 0.5f;
|
|
if (flTestArea < flMinArea)
|
|
return;
|
|
|
|
// Check to see if any more displacement verts exist - go to subdivision if not.
|
|
if (nLevel >= (m_nPower * 2))
|
|
{
|
|
CreateChildPatchesSub(iParentPatch);
|
|
return;
|
|
}
|
|
|
|
int nChildIndices[2][3];
|
|
int nNewIndex = (pParentPatch->indices[1] + pParentPatch->indices[0]) / 2;
|
|
nChildIndices[0][0] = pParentPatch->indices[2];
|
|
nChildIndices[0][1] = pParentPatch->indices[0];
|
|
nChildIndices[0][2] = nNewIndex;
|
|
|
|
nChildIndices[1][0] = pParentPatch->indices[1];
|
|
nChildIndices[1][1] = pParentPatch->indices[2];
|
|
nChildIndices[1][2] = nNewIndex;
|
|
|
|
Vector vecChildPoints[2][3];
|
|
for (int iTri = 0; iTri < 2; ++iTri)
|
|
{
|
|
for (int iPoint = 0; iPoint < 3; ++iPoint)
|
|
{
|
|
VectorCopy(m_aVerts[nChildIndices[iTri][iPoint]], vecChildPoints[iTri][iPoint]);
|
|
}
|
|
}
|
|
|
|
// Create and initialize the children patches.
|
|
int iChildPatch[2] = { -1, -1 };
|
|
for (int iChild = 0; iChild < 2; ++iChild)
|
|
{
|
|
iChildPatch[iChild] = g_Patches.AddToTail();
|
|
|
|
float flArea = 0.0f;
|
|
if (!InitPatch(iChildPatch[iChild], iParentPatch, iChild, vecChildPoints[iChild], nChildIndices[iChild], flArea))
|
|
{
|
|
if (iChild == 0)
|
|
{
|
|
pParentPatch->child1 = g_Patches.InvalidIndex();
|
|
g_Patches.Remove(iChildPatch[iChild]);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pParentPatch->child1 = g_Patches.InvalidIndex();
|
|
pParentPatch->child2 = g_Patches.InvalidIndex();
|
|
g_Patches.Remove(iChildPatch[iChild]);
|
|
g_Patches.Remove(iChildPatch[0]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Continue creating children patches.
|
|
int nNewLevel = ++nLevel;
|
|
CreateChildPatches(iChildPatch[0], nNewLevel);
|
|
CreateChildPatches(iChildPatch[1], nNewLevel);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : flMinArea -
|
|
// Output : float
|
|
//-----------------------------------------------------------------------------
|
|
void CVRADDispColl::CreateChildPatchesSub(int iParentPatch)
|
|
{
|
|
// Get the parent patch.
|
|
CPatch *pParentPatch = &g_Patches[iParentPatch];
|
|
if (!pParentPatch)
|
|
return;
|
|
|
|
// Calculate the the area of the patch (triangle!).
|
|
Assert(pParentPatch->winding->numpoints == 3);
|
|
if (pParentPatch->winding->numpoints != 3)
|
|
return;
|
|
|
|
// Should the patch be subdivided - check the area.
|
|
float flMaxLength = max(m_flSampleWidth, m_flSampleHeight);
|
|
float flMinEdgeLength = flMaxLength * dispchop;
|
|
|
|
// Split along the longest edge.
|
|
Vector vecEdges[3];
|
|
vecEdges[0] = pParentPatch->winding->p[1] - pParentPatch->winding->p[0];
|
|
vecEdges[1] = pParentPatch->winding->p[2] - pParentPatch->winding->p[1];
|
|
vecEdges[2] = pParentPatch->winding->p[0] - pParentPatch->winding->p[2];
|
|
|
|
// Find the longest edge.
|
|
float flEdgeLength = 0.0f;
|
|
int iLongEdge = -1;
|
|
for (int iEdge = 0; iEdge < 3; ++iEdge)
|
|
{
|
|
if (flEdgeLength < vecEdges[iEdge].Length())
|
|
{
|
|
flEdgeLength = vecEdges[iEdge].Length();
|
|
iLongEdge = iEdge;
|
|
}
|
|
}
|
|
|
|
// Small enough already, return.
|
|
if (flEdgeLength < flMinEdgeLength)
|
|
return;
|
|
|
|
// Test area as well so we don't allow slivers.
|
|
float flMinArea = (dispchop * flMaxLength) * (dispchop * flMaxLength) * 0.5f;
|
|
Vector vecNormal = vecEdges[1].Cross(vecEdges[0]);
|
|
float flTestArea = VectorNormalize(vecNormal);
|
|
flTestArea *= 0.5f;
|
|
if (flTestArea < flMinArea)
|
|
return;
|
|
|
|
// Create children patchs - 2 of them.
|
|
Vector vecChildPoints[2][3];
|
|
switch (iLongEdge)
|
|
{
|
|
case 0:
|
|
{
|
|
vecChildPoints[0][0] = pParentPatch->winding->p[0];
|
|
vecChildPoints[0][1] = (pParentPatch->winding->p[0] + pParentPatch->winding->p[1]) * 0.5f;
|
|
vecChildPoints[0][2] = pParentPatch->winding->p[2];
|
|
|
|
vecChildPoints[1][0] = (pParentPatch->winding->p[0] + pParentPatch->winding->p[1]) * 0.5f;
|
|
vecChildPoints[1][1] = pParentPatch->winding->p[1];
|
|
vecChildPoints[1][2] = pParentPatch->winding->p[2];
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
vecChildPoints[0][0] = pParentPatch->winding->p[0];
|
|
vecChildPoints[0][1] = pParentPatch->winding->p[1];
|
|
vecChildPoints[0][2] = (pParentPatch->winding->p[1] + pParentPatch->winding->p[2]) * 0.5f;
|
|
|
|
vecChildPoints[1][0] = (pParentPatch->winding->p[1] + pParentPatch->winding->p[2]) * 0.5f;
|
|
vecChildPoints[1][1] = pParentPatch->winding->p[2];
|
|
vecChildPoints[1][2] = pParentPatch->winding->p[0];
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
vecChildPoints[0][0] = pParentPatch->winding->p[0];
|
|
vecChildPoints[0][1] = pParentPatch->winding->p[1];
|
|
vecChildPoints[0][2] = (pParentPatch->winding->p[0] + pParentPatch->winding->p[2]) * 0.5f;
|
|
|
|
vecChildPoints[1][0] = (pParentPatch->winding->p[0] + pParentPatch->winding->p[2]) * 0.5f;
|
|
vecChildPoints[1][1] = pParentPatch->winding->p[1];
|
|
vecChildPoints[1][2] = pParentPatch->winding->p[2];
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// Create and initialize the children patches.
|
|
int iChildPatch[2] = { 0, 0 };
|
|
int nChildIndices[3] = { -1, -1, -1 };
|
|
for (int iChild = 0; iChild < 2; ++iChild)
|
|
{
|
|
iChildPatch[iChild] = g_Patches.AddToTail();
|
|
|
|
float flArea = 0.0f;
|
|
if (!InitPatch(iChildPatch[iChild], iParentPatch, iChild, vecChildPoints[iChild], nChildIndices, flArea))
|
|
{
|
|
if (iChild == 0)
|
|
{
|
|
pParentPatch->child1 = g_Patches.InvalidIndex();
|
|
g_Patches.Remove(iChildPatch[iChild]);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pParentPatch->child1 = g_Patches.InvalidIndex();
|
|
pParentPatch->child2 = g_Patches.InvalidIndex();
|
|
g_Patches.Remove(iChildPatch[iChild]);
|
|
g_Patches.Remove(iChildPatch[0]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Continue creating children patches.
|
|
CreateChildPatchesSub(iChildPatch[0]);
|
|
CreateChildPatchesSub(iChildPatch[1]);
|
|
}
|
|
|
|
int PlaneTypeForNormal(Vector& normal)
|
|
{
|
|
vec_t ax, ay, az;
|
|
|
|
// NOTE: should these have an epsilon around 1.0?
|
|
if (normal[0] == 1.0 || normal[0] == -1.0)
|
|
return PLANE_X;
|
|
if (normal[1] == 1.0 || normal[1] == -1.0)
|
|
return PLANE_Y;
|
|
if (normal[2] == 1.0 || normal[2] == -1.0)
|
|
return PLANE_Z;
|
|
|
|
ax = fabs(normal[0]);
|
|
ay = fabs(normal[1]);
|
|
az = fabs(normal[2]);
|
|
|
|
if (ax >= ay && ax >= az)
|
|
return PLANE_ANYX;
|
|
if (ay >= ax && ay >= az)
|
|
return PLANE_ANYY;
|
|
return PLANE_ANYZ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : iPatch -
|
|
// iParentPatch -
|
|
// iChild -
|
|
// *pPoints -
|
|
// *pIndices -
|
|
// &flArea -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CVRADDispColl::InitParentPatch(int iPatch, Vector *pPoints, float &flArea)
|
|
{
|
|
// Get the current patch.
|
|
CPatch *pPatch = &g_Patches[iPatch];
|
|
if (!pPatch)
|
|
return false;
|
|
|
|
// Clear the patch data.
|
|
memset(pPatch, 0, sizeof(CPatch));
|
|
|
|
// This is a parent.
|
|
pPatch->ndxNext = g_FacePatches.Element(GetParentIndex());
|
|
g_FacePatches[GetParentIndex()] = iPatch;
|
|
pPatch->faceNumber = GetParentIndex();
|
|
|
|
// Initialize parent and children indices.
|
|
pPatch->child1 = g_Patches.InvalidIndex();
|
|
pPatch->child2 = g_Patches.InvalidIndex();
|
|
pPatch->parent = g_Patches.InvalidIndex();
|
|
pPatch->ndxNextClusterChild = g_Patches.InvalidIndex();
|
|
pPatch->ndxNextParent = g_Patches.InvalidIndex();
|
|
|
|
Vector vecEdges[2];
|
|
vecEdges[0] = pPoints[1] - pPoints[0];
|
|
vecEdges[1] = pPoints[3] - pPoints[0];
|
|
|
|
// Calculate the triangle normal and area.
|
|
Vector vecNormal = vecEdges[1].Cross(vecEdges[0]);
|
|
flArea = VectorNormalize(vecNormal);
|
|
|
|
// Initialize the patch scale.
|
|
pPatch->scale[0] = pPatch->scale[1] = 1.0f;
|
|
|
|
// Set the patch chop - minchop (that is what the minimum area is based on).
|
|
pPatch->chop = dispchop;
|
|
|
|
// Displacements are not sky!
|
|
pPatch->sky = false;
|
|
|
|
// Copy the winding.
|
|
Vector vecCenter(0.0f, 0.0f, 0.0f);
|
|
pPatch->winding = AllocWinding(4);
|
|
pPatch->winding->numpoints = 4;
|
|
for (int iPoint = 0; iPoint < 4; ++iPoint)
|
|
{
|
|
VectorCopy(pPoints[iPoint], pPatch->winding->p[iPoint]);
|
|
VectorAdd(pPoints[iPoint], vecCenter, vecCenter);
|
|
}
|
|
|
|
// Set the origin and normal.
|
|
VectorScale(vecCenter, (1.0f / 4.0f), vecCenter);
|
|
VectorCopy(vecCenter, pPatch->origin);
|
|
VectorCopy(vecNormal, pPatch->normal);
|
|
|
|
// Create the plane.
|
|
pPatch->plane = new dplane_t;
|
|
if (!pPatch->plane)
|
|
return false;
|
|
|
|
VectorCopy(vecNormal, pPatch->plane->normal);
|
|
pPatch->plane->dist = vecNormal.Dot(pPoints[0]);
|
|
pPatch->plane->type = PlaneTypeForNormal(pPatch->plane->normal);
|
|
pPatch->planeDist = pPatch->plane->dist;
|
|
|
|
// Set the area.
|
|
pPatch->area = flArea;
|
|
|
|
// Calculate the mins/maxs.
|
|
Vector vecMin(FLT_MAX, FLT_MAX, FLT_MAX);
|
|
Vector vecMax(FLT_MIN, FLT_MIN, FLT_MIN);
|
|
for (int iPoint = 0; iPoint < 4; ++iPoint)
|
|
{
|
|
for (int iAxis = 0; iAxis < 3; ++iAxis)
|
|
{
|
|
vecMin[iAxis] = min(vecMin[iAxis], pPoints[iPoint][iAxis]);
|
|
vecMax[iAxis] = max(vecMax[iAxis], pPoints[iPoint][iAxis]);
|
|
}
|
|
}
|
|
|
|
VectorCopy(vecMin, pPatch->mins);
|
|
VectorCopy(vecMax, pPatch->maxs);
|
|
VectorCopy(vecMin, pPatch->face_mins);
|
|
VectorCopy(vecMax, pPatch->face_maxs);
|
|
|
|
// Check for bumpmap.
|
|
dface_t *pFace = dfaces + pPatch->faceNumber;
|
|
texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
|
|
pPatch->needsBumpmap = pTexInfo->flags & SURF_BUMPLIGHT ? true : false;
|
|
|
|
// Misc...
|
|
pPatch->m_IterationKey = 0;
|
|
|
|
// Calculate the base light, area, and reflectivity.
|
|
BaseLightForFace(&g_pFaces[pPatch->faceNumber], pPatch->baselight, &pPatch->basearea, pPatch->reflectivity);
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *pPatch -
|
|
// *pPoints -
|
|
// &vecNormal -
|
|
// flArea -
|
|
//-----------------------------------------------------------------------------
|
|
bool CVRADDispColl::InitPatch(int iPatch, int iParentPatch, int iChild, Vector *pPoints, int *pIndices, float &flArea)
|
|
{
|
|
// Get the current patch.
|
|
CPatch *pPatch = &g_Patches[iPatch];
|
|
if (!pPatch)
|
|
return false;
|
|
|
|
// Clear the patch data.
|
|
memset(pPatch, 0, sizeof(CPatch));
|
|
|
|
// Setup the parent if we are not the parent.
|
|
CPatch *pParentPatch = NULL;
|
|
if (iParentPatch != g_Patches.InvalidIndex())
|
|
{
|
|
// Get the parent patch.
|
|
pParentPatch = &g_Patches[iParentPatch];
|
|
if (!pParentPatch)
|
|
return false;
|
|
}
|
|
|
|
// Attach the face to the correct lists.
|
|
if (!pParentPatch)
|
|
{
|
|
// This is a parent.
|
|
pPatch->ndxNext = g_FacePatches.Element(GetParentIndex());
|
|
g_FacePatches[GetParentIndex()] = iPatch;
|
|
pPatch->faceNumber = GetParentIndex();
|
|
}
|
|
else
|
|
{
|
|
pPatch->ndxNext = g_Patches.InvalidIndex();
|
|
pPatch->faceNumber = pParentPatch->faceNumber;
|
|
|
|
// Attach to the parent patch.
|
|
if (iChild == 0)
|
|
{
|
|
pParentPatch->child1 = iPatch;
|
|
}
|
|
else
|
|
{
|
|
pParentPatch->child2 = iPatch;
|
|
}
|
|
}
|
|
|
|
// Initialize parent and children indices.
|
|
pPatch->child1 = g_Patches.InvalidIndex();
|
|
pPatch->child2 = g_Patches.InvalidIndex();
|
|
pPatch->ndxNextClusterChild = g_Patches.InvalidIndex();
|
|
pPatch->ndxNextParent = g_Patches.InvalidIndex();
|
|
pPatch->parent = iParentPatch;
|
|
|
|
// Get triangle edges.
|
|
Vector vecEdges[3];
|
|
vecEdges[0] = pPoints[1] - pPoints[0];
|
|
vecEdges[1] = pPoints[2] - pPoints[0];
|
|
vecEdges[2] = pPoints[2] - pPoints[1];
|
|
|
|
// Find the longest edge.
|
|
// float flEdgeLength = 0.0f;
|
|
// for ( int iEdge = 0; iEdge < 3; ++iEdge )
|
|
// {
|
|
// if ( flEdgeLength < vecEdges[iEdge].Length() )
|
|
// {
|
|
// flEdgeLength = vecEdges[iEdge].Length();
|
|
// }
|
|
// }
|
|
|
|
// Calculate the triangle normal and area.
|
|
Vector vecNormal = vecEdges[1].Cross(vecEdges[0]);
|
|
flArea = VectorNormalize(vecNormal);
|
|
flArea *= 0.5f;
|
|
|
|
// Initialize the patch scale.
|
|
pPatch->scale[0] = pPatch->scale[1] = 1.0f;
|
|
|
|
// Set the patch chop - minchop (that is what the minimum area is based on).
|
|
pPatch->chop = dispchop;
|
|
|
|
// Displacements are not sky!
|
|
pPatch->sky = false;
|
|
|
|
// Copy the winding.
|
|
Vector vecCenter(0.0f, 0.0f, 0.0f);
|
|
pPatch->winding = AllocWinding(3);
|
|
pPatch->winding->numpoints = 3;
|
|
for (int iPoint = 0; iPoint < 3; ++iPoint)
|
|
{
|
|
VectorCopy(pPoints[iPoint], pPatch->winding->p[iPoint]);
|
|
VectorAdd(pPoints[iPoint], vecCenter, vecCenter);
|
|
|
|
pPatch->indices[iPoint] = static_cast<short>(pIndices[iPoint]);
|
|
}
|
|
|
|
// Set the origin and normal.
|
|
VectorScale(vecCenter, (1.0f / 3.0f), vecCenter);
|
|
VectorCopy(vecCenter, pPatch->origin);
|
|
VectorCopy(vecNormal, pPatch->normal);
|
|
|
|
// Create the plane.
|
|
pPatch->plane = new dplane_t;
|
|
if (!pPatch->plane)
|
|
return false;
|
|
|
|
VectorCopy(vecNormal, pPatch->plane->normal);
|
|
pPatch->plane->dist = vecNormal.Dot(pPoints[0]);
|
|
pPatch->plane->type = PlaneTypeForNormal(pPatch->plane->normal);
|
|
pPatch->planeDist = pPatch->plane->dist;
|
|
|
|
// Set the area.
|
|
pPatch->area = flArea;
|
|
|
|
// Calculate the mins/maxs.
|
|
Vector vecMin(FLT_MAX, FLT_MAX, FLT_MAX);
|
|
Vector vecMax(FLT_MIN, FLT_MIN, FLT_MIN);
|
|
for (int iPoint = 0; iPoint < 3; ++iPoint)
|
|
{
|
|
for (int iAxis = 0; iAxis < 3; ++iAxis)
|
|
{
|
|
vecMin[iAxis] = min(vecMin[iAxis], pPoints[iPoint][iAxis]);
|
|
vecMax[iAxis] = max(vecMax[iAxis], pPoints[iPoint][iAxis]);
|
|
}
|
|
}
|
|
|
|
VectorCopy(vecMin, pPatch->mins);
|
|
VectorCopy(vecMax, pPatch->maxs);
|
|
|
|
if (!pParentPatch)
|
|
{
|
|
VectorCopy(vecMin, pPatch->face_mins);
|
|
VectorCopy(vecMax, pPatch->face_maxs);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(pParentPatch->face_mins, pPatch->face_mins);
|
|
VectorCopy(pParentPatch->face_maxs, pPatch->face_maxs);
|
|
}
|
|
|
|
// Check for bumpmap.
|
|
dface_t *pFace = dfaces + pPatch->faceNumber;
|
|
texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
|
|
pPatch->needsBumpmap = pTexInfo->flags & SURF_BUMPLIGHT ? true : false;
|
|
|
|
// Misc...
|
|
pPatch->m_IterationKey = 0;
|
|
|
|
// Get the base light for the face.
|
|
if (!pParentPatch)
|
|
{
|
|
BaseLightForFace(&g_pFaces[pPatch->faceNumber], pPatch->baselight, &pPatch->basearea, pPatch->reflectivity);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(pParentPatch->baselight, pPatch->baselight);
|
|
pPatch->basearea = pParentPatch->basearea;
|
|
pPatch->reflectivity = pParentPatch->reflectivity;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CVRADDispColl::AddPolysForRayTrace(void)
|
|
{
|
|
if (!(m_nContents & MASK_OPAQUE))
|
|
return;
|
|
|
|
for (int ndxTri = 0; ndxTri < m_aTris.Size(); ndxTri++)
|
|
{
|
|
CDispCollTri *tri = m_aTris.Base() + ndxTri;
|
|
int v[3];
|
|
for (int ndxv = 0; ndxv < 3; ndxv++)
|
|
v[ndxv] = tri->GetVert(ndxv);
|
|
|
|
Vector fullCoverage;
|
|
fullCoverage.x = 1.0f;
|
|
g_RtEnv.AddTriangle(TRACE_ID_OPAQUE, m_aVerts[v[0]], m_aVerts[v[1]], m_aVerts[v[2]], fullCoverage);
|
|
}
|
|
} |