//========= 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(nWidth - 1.000001f); float flV = dispUV.y * static_cast(nHeight - 1.000001f); // Find the base U, V. int nSnapU = static_cast(flU); int nSnapV = static_cast(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(nSnapU); float flFracV = flV - static_cast(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(nSnapU); float flFracV = flV - static_cast(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(nWidth - 1.000001f); float flV = dispUV.y * static_cast(nHeight - 1.000001f); // Find the base U, V. int nSnapU = static_cast(flU); int nSnapV = static_cast(flV); int nNextU = nSnapU + 1; int nNextV = nSnapV + 1; if (nNextU == nWidth) { --nNextU; } if (nNextV == nHeight) { --nNextV; } float flFracU = flU - static_cast(nSnapU); float flFracV = flV - static_cast(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(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); } }