//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "vrad.h" #include "utlvector.h" #include "cmodel.h" #include "BSPTreeData.h" #include "VRAD_DispColl.h" #include "CollisionUtils.h" #include "lightmap.h" #include "Radial.h" #include "CollisionUtils.h" #include "mathlib/bumpvects.h" #include "utlrbtree.h" #include "tier0/fasttimer.h" #include "disp_vrad.h" class CBSPDispRayDistanceEnumerator; //============================================================================= // // Displacement/Face List // class CBSPDispFaceListEnumerator : public ISpatialLeafEnumerator, public IBSPTreeDataEnumerator { public: //========================================================================= // // Construction/Deconstruction // CBSPDispFaceListEnumerator() {}; virtual ~CBSPDispFaceListEnumerator() { m_DispList.Purge(); m_FaceList.Purge(); } // ISpatialLeafEnumerator bool EnumerateLeaf(int ndxLeaf, int context); // IBSPTreeDataEnumerator bool FASTCALL EnumerateElement(int userId, int context); public: CUtlVector m_DispList; CUtlVector m_FaceList; }; //============================================================================= // // RayEnumerator // class CBSPDispRayEnumerator : public ISpatialLeafEnumerator, public IBSPTreeDataEnumerator { public: // ISpatialLeafEnumerator bool EnumerateLeaf(int ndxLeaf, int context); // IBSPTreeDataEnumerator bool FASTCALL EnumerateElement(int userId, int context); }; //============================================================================= // // VRad Displacement Manager // class CVRadDispMgr : public IVRadDispMgr { public: //========================================================================= // // Construction/Deconstruction // CVRadDispMgr(); virtual ~CVRadDispMgr(); // creation/destruction void Init(void); void Shutdown(void); // "CalcPoints" bool BuildDispSamples(lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace); bool BuildDispLuxels(lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace); bool BuildDispSamplesAndLuxels_DoFast(lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace); // patching functions void MakePatches(void); void SubdividePatch(int iPatch); // pre "FinalLightFace" void InsertSamplesDataIntoHashTable(void); void InsertPatchSampleDataIntoHashTable(void); // "FinalLightFace" radial_t *BuildLuxelRadial(int ndxFace, int ndxStyle, bool bBump); bool SampleRadial(int ndxFace, radial_t *pRadial, Vector const &vPos, int ndxLxl, LightingValue_t *pLightSample, int sampleCount, bool bPatch); radial_t *BuildPatchRadial(int ndxFace, bool bBump); // utility void GetDispSurfNormal(int ndxFace, Vector &pt, Vector &ptNormal, bool bInside); void GetDispSurf(int ndxFace, CVRADDispColl **ppDispTree); // bsp tree functions bool ClipRayToDisp(DispTested_t &dispTested, Ray_t const &ray); bool ClipRayToDispInLeaf(DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf); void ClipRayToDispInLeaf(DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf, float& dist, dface_t*& pFace, Vector2D& luxelCoord); void ClipRayToDispInLeaf(DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf, float& dist, Vector *pNormal); void StartRayTest(DispTested_t &dispTested); void AddPolysForRayTrace(void); // general timing -- should be moved!! void StartTimer(const char *name); void EndTimer(void); //========================================================================= // // Enumeration Methods // bool DispRay_EnumerateLeaf(int ndxLeaf, int context); bool DispRay_EnumerateElement(int userId, int context); bool DispRayDistance_EnumerateElement(int userId, CBSPDispRayDistanceEnumerator* pEnum); bool DispFaceList_EnumerateLeaf(int ndxLeaf, int context); bool DispFaceList_EnumerateElement(int userId, int context); private: //========================================================================= // // BSP Tree Helpers // void InsertDispIntoTree(int ndxDisp); void RemoveDispFromTree(int ndxDisp); //========================================================================= // // Displacement Data Loader (from .bsp) // void UnserializeDisps(void); void DispBuilderInit(CCoreDispInfo *pBuilderDisp, dface_t *pFace, int ndxFace); //========================================================================= // // Sampling Helpers // void RadialLuxelBuild(CVRADDispColl *pDispTree, radial_t *pRadial, int ndxStyle, bool bBump); void RadialLuxelAddSamples(int ndxFace, Vector const &luxelPt, Vector const &luxelNormal, float radius, radial_t *pRadial, int ndxRadial, bool bBump, int lightStyle); void RadialPatchBuild(CVRADDispColl *pDispTree, radial_t *pRadial, bool bBump); void RadialLuxelAddPatch(int ndxFace, Vector const &luxelPt, Vector const &luxelNormal, float radius, radial_t *pRadial, int ndxRadial, bool bBump, CUtlVector &interestingPatches); bool IsNeighbor(int iDispFace, int iNeighborFace); void GetInterestingPatchesForLuxels( int ndxFace, CUtlVector &interestingPatches, float patchSampleRadius); private: struct DispCollTree_t { CVRADDispColl *m_pDispTree; BSPTreeDataHandle_t m_Handle; }; struct EnumContext_t { DispTested_t *m_pDispTested; Ray_t const *m_pRay; }; CUtlVector m_DispTrees; IBSPTreeData *m_pBSPTreeData; CBSPDispRayEnumerator m_EnumDispRay; CBSPDispFaceListEnumerator m_EnumDispFaceList; int sampleCount; Vector *m_pSamplePos; CFastTimer m_Timer; }; //----------------------------------------------------------------------------- // Purpose: expose IVRadDispMgr to vrad //----------------------------------------------------------------------------- static CVRadDispMgr s_DispMgr; IVRadDispMgr *StaticDispMgr(void) { return &s_DispMgr; } //============================================================================= // // Displacement/Face List // // ISpatialLeafEnumerator bool CBSPDispFaceListEnumerator::EnumerateLeaf(int ndxLeaf, int context) { return s_DispMgr.DispFaceList_EnumerateLeaf(ndxLeaf, context); } // IBSPTreeDataEnumerator bool FASTCALL CBSPDispFaceListEnumerator::EnumerateElement(int userId, int context) { return s_DispMgr.DispFaceList_EnumerateElement(userId, context); } //============================================================================= // // RayEnumerator // bool CBSPDispRayEnumerator::EnumerateLeaf(int ndxLeaf, int context) { return s_DispMgr.DispRay_EnumerateLeaf(ndxLeaf, context); } bool FASTCALL CBSPDispRayEnumerator::EnumerateElement(int userId, int context) { return s_DispMgr.DispRay_EnumerateElement(userId, context); } //----------------------------------------------------------------------------- // Here's an enumerator that we use for testing against disps in a leaf... //----------------------------------------------------------------------------- class CBSPDispRayDistanceEnumerator : public IBSPTreeDataEnumerator { public: CBSPDispRayDistanceEnumerator() : m_Distance(1.0f), m_pSurface(0) {} // IBSPTreeDataEnumerator bool FASTCALL EnumerateElement(int userId, int context) { return s_DispMgr.DispRayDistance_EnumerateElement(userId, this); } float m_Distance; dface_t* m_pSurface; DispTested_t *m_pDispTested; Ray_t const *m_pRay; Vector2D m_LuxelCoord; Vector m_Normal; }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- CVRadDispMgr::CVRadDispMgr() { m_pBSPTreeData = CreateBSPTreeData(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- CVRadDispMgr::~CVRadDispMgr() { DestroyBSPTreeData(m_pBSPTreeData); } //----------------------------------------------------------------------------- // Insert a displacement into the tree for collision //----------------------------------------------------------------------------- void CVRadDispMgr::InsertDispIntoTree(int ndxDisp) { DispCollTree_t &dispTree = m_DispTrees[ndxDisp]; CDispCollTree *pDispTree = dispTree.m_pDispTree; // get the bounding box of the tree Vector boxMin, boxMax; pDispTree->GetBounds(boxMin, boxMax); // add the displacement to the tree so we will collide against it dispTree.m_Handle = m_pBSPTreeData->Insert(ndxDisp, boxMin, boxMax); } //----------------------------------------------------------------------------- // Remove a displacement from the tree for collision //----------------------------------------------------------------------------- void CVRadDispMgr::RemoveDispFromTree(int ndxDisp) { // release the tree handle if (m_DispTrees[ndxDisp].m_Handle != TREEDATA_INVALID_HANDLE) { m_pBSPTreeData->Remove(m_DispTrees[ndxDisp].m_Handle); m_DispTrees[ndxDisp].m_Handle = TREEDATA_INVALID_HANDLE; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::Init(void) { // initialize the bsp tree m_pBSPTreeData->Init(ToolBSPTree()); // read in displacements that have been compiled into the bsp file UnserializeDisps(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::Shutdown(void) { // remove all displacements from the tree for (int ndxDisp = m_DispTrees.Size(); ndxDisp >= 0; ndxDisp--) { RemoveDispFromTree(ndxDisp); } // shutdown the bsp tree m_pBSPTreeData->Shutdown(); // purge the displacement collision tree list m_DispTrees.Purge(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::DispBuilderInit(CCoreDispInfo *pBuilderDisp, dface_t *pFace, int ndxFace) { // get the .bsp displacement ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo]; if (!pDisp) return; // // initlialize the displacement base surface // CCoreDispSurface *pSurf = pBuilderDisp->GetSurface(); pSurf->SetPointCount(4); pSurf->SetHandle(ndxFace); pSurf->SetContents(pDisp->contents); Vector pt[4]; int ndxPt; for (ndxPt = 0; ndxPt < 4; ndxPt++) { int eIndex = dsurfedges[pFace->firstedge + ndxPt]; if (eIndex < 0) { pSurf->SetPoint(ndxPt, dvertexes[dedges[-eIndex].v[1]].point); } else { pSurf->SetPoint(ndxPt, dvertexes[dedges[eIndex].v[0]].point); } VectorCopy(pSurf->GetPoint(ndxPt), pt[ndxPt]); } // // calculate the displacement surface normal // Vector vFaceNormal; pSurf->GetNormal(vFaceNormal); for (ndxPt = 0; ndxPt < 4; ndxPt++) { pSurf->SetPointNormal(ndxPt, vFaceNormal); } // set the surface initial point info pSurf->SetPointStart(pDisp->startPosition); pSurf->FindSurfPointStartIndex(); pSurf->AdjustSurfPointData(); Vector vecTmp(texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][0], texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][1], texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][2]); int nLuxelsPerWorldUnit = static_cast(1.0f / VectorLength(vecTmp)); Vector vecU(texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][0], texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][1], texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][2]); Vector vecV(texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][0], texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][1], texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][2]); pSurf->CalcLuxelCoords(nLuxelsPerWorldUnit, false, vecU, vecV); pBuilderDisp->SetNeighborData(pDisp->m_EdgeNeighbors, pDisp->m_CornerNeighbors); CDispVert *pVerts = &g_DispVerts[pDisp->m_iDispVertStart]; CDispTri *pTris = &g_DispTris[pDisp->m_iDispTriStart]; // // initialize the displacement data // pBuilderDisp->InitDispInfo( pDisp->power, pDisp->minTess, pDisp->smoothingAngle, pVerts, pTris); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::UnserializeDisps(void) { // temporarily create the "builder" displacements CUtlVector builderDisps; for (int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp) { CCoreDispInfo *pDisp = new CCoreDispInfo; if (!pDisp) { builderDisps.Purge(); return; } int nIndex = builderDisps.AddToTail(); pDisp->SetListIndex(nIndex); builderDisps[nIndex] = pDisp; } // Set them up as CDispUtilsHelpers. for (int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp) { builderDisps[iDisp]->SetDispUtilsHelperInfo(builderDisps.Base(), g_dispinfo.Count()); } // // find all faces with displacement data and initialize // for (int ndxFace = 0; ndxFace < numfaces; ndxFace++) { dface_t *pFace = &g_pFaces[ndxFace]; if (ValidDispFace(pFace)) { DispBuilderInit(builderDisps[pFace->dispinfo], pFace, ndxFace); } } // generate the displacement surfaces for (int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp) { builderDisps[iDisp]->Create(); } // smooth edge normals SmoothNeighboringDispSurfNormals(builderDisps.Base(), g_dispinfo.Count()); // // create the displacement collision tree and add it to the bsp tree // CVRADDispColl *pDispTrees = new CVRADDispColl[g_dispinfo.Count()]; if (!pDispTrees) return; m_DispTrees.AddMultipleToTail(g_dispinfo.Count()); for (int iDisp = 0; iDisp < g_dispinfo.Count(); iDisp++) { pDispTrees[iDisp].Create(builderDisps[iDisp]); m_DispTrees[iDisp].m_pDispTree = &pDispTrees[iDisp]; m_DispTrees[iDisp].m_Handle = TREEDATA_INVALID_HANDLE; InsertDispIntoTree(iDisp); } // free "builder" disps builderDisps.Purge(); } //----------------------------------------------------------------------------- // Purpose: create a set of patches for each displacement surface to transfer // bounced light around with //----------------------------------------------------------------------------- void CVRadDispMgr::MakePatches(void) { // Collect stats - keep track of the total displacement surface area. float flTotalArea = 0.0f; // Create patches for all of the displacements. int nTreeCount = m_DispTrees.Size(); for (int iTree = 0; iTree < nTreeCount; ++iTree) { // Get the current displacement collision tree. CVRADDispColl *pDispTree = m_DispTrees[iTree].m_pDispTree; if (!pDispTree) continue; flTotalArea += pDispTree->CreateParentPatches(); } // Print stats. qprintf("%i Displacements\n", nTreeCount); qprintf("%i Square Feet [%.2f Square Inches]\n", (int)(flTotalArea / 144.0f), flTotalArea); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::SubdividePatch(int iPatch) { // Get the current patch to subdivide. CPatch *pPatch = &g_Patches[iPatch]; if (!pPatch) return; // Create children patches. DispCollTree_t &dispTree = m_DispTrees[g_pFaces[pPatch->faceNumber].dispinfo]; CVRADDispColl *pTree = dispTree.m_pDispTree; if (pTree) { pTree->CreateChildPatches(iPatch, 0); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::StartRayTest(DispTested_t &dispTested) { if (m_DispTrees.Size() > 0) { if (dispTested.m_pTested == 0) { dispTested.m_pTested = new int[m_DispTrees.Size()]; memset(dispTested.m_pTested, 0, m_DispTrees.Size() * sizeof(int)); dispTested.m_Enum = 0; } ++dispTested.m_Enum; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CVRadDispMgr::ClipRayToDisp(DispTested_t &dispTested, Ray_t const &ray) { StartRayTest(dispTested); EnumContext_t ctx; ctx.m_pRay = &ray; ctx.m_pDispTested = &dispTested; // If it got through without a hit, it returns true return !m_pBSPTreeData->EnumerateLeavesAlongRay(ray, &m_EnumDispRay, (int)&ctx); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CVRadDispMgr::ClipRayToDispInLeaf(DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf) { EnumContext_t ctx; ctx.m_pRay = &ray; ctx.m_pDispTested = &dispTested; return !m_pBSPTreeData->EnumerateElementsInLeaf(ndxLeaf, &m_EnumDispRay, (int)&ctx); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::ClipRayToDispInLeaf(DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf, float& dist, dface_t*& pFace, Vector2D& luxelCoord) { CBSPDispRayDistanceEnumerator rayTestEnum; rayTestEnum.m_pRay = &ray; rayTestEnum.m_pDispTested = &dispTested; m_pBSPTreeData->EnumerateElementsInLeaf(ndxLeaf, &rayTestEnum, 0); dist = rayTestEnum.m_Distance; pFace = rayTestEnum.m_pSurface; if (pFace) { Vector2DCopy(rayTestEnum.m_LuxelCoord, luxelCoord); } } void CVRadDispMgr::ClipRayToDispInLeaf(DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf, float& dist, Vector *pNormal) { CBSPDispRayDistanceEnumerator rayTestEnum; rayTestEnum.m_pRay = &ray; rayTestEnum.m_pDispTested = &dispTested; m_pBSPTreeData->EnumerateElementsInLeaf(ndxLeaf, &rayTestEnum, 0); dist = rayTestEnum.m_Distance; if (rayTestEnum.m_pSurface) { *pNormal = rayTestEnum.m_Normal; } } void CVRadDispMgr::AddPolysForRayTrace(void) { int nTreeCount = m_DispTrees.Size(); for (int iTree = 0; iTree < nTreeCount; ++iTree) { // Get the current displacement collision tree. CVRADDispColl *pDispTree = m_DispTrees[iTree].m_pDispTree; // Add the triangles of the tree to the RT environment pDispTree->AddPolysForRayTrace(); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::GetDispSurfNormal(int ndxFace, Vector &pt, Vector &ptNormal, bool bInside) { // get the displacement surface data DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo]; CVRADDispColl *pDispTree = dispTree.m_pDispTree; // find the parameterized displacement indices Vector2D uv; pDispTree->BaseFacePlaneToDispUV(pt, uv); if (bInside) { if (uv[0] < 0.0f || uv[0] > 1.0f) { Msg("Disp UV (%f) outside bounds!\n", uv[0]); } if (uv[1] < 0.0f || uv[1] > 1.0f) { Msg("Disp UV (%f) outside bounds!\n", uv[1]); } } if (uv[0] < 0.0f) { uv[0] = 0.0f; } if (uv[0] > 1.0f) { uv[0] = 1.0f; } if (uv[1] < 0.0f) { uv[1] = 0.0f; } if (uv[1] > 1.0f) { uv[1] = 1.0f; } // get the normal at "pt" pDispTree->DispUVToSurfNormal(uv, ptNormal); // get the new "pt" pDispTree->DispUVToSurfPoint(uv, pt, 1.0f); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::GetDispSurf(int ndxFace, CVRADDispColl **ppDispTree) { DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo]; *ppDispTree = dispTree.m_pDispTree; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CVRadDispMgr::DispRay_EnumerateLeaf(int ndxLeaf, int context) { return m_pBSPTreeData->EnumerateElementsInLeaf(ndxLeaf, &m_EnumDispRay, context); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CVRadDispMgr::DispRay_EnumerateElement(int userId, int context) { DispCollTree_t &dispTree = m_DispTrees[userId]; EnumContext_t *pCtx = (EnumContext_t*)context; // don't test twice (check tested value) if (pCtx->m_pDispTested->m_pTested[userId] == pCtx->m_pDispTested->m_Enum) return true; // set the tested value pCtx->m_pDispTested->m_pTested[userId] = pCtx->m_pDispTested->m_Enum; // false mean stop iterating -- return false if we hit! (NOTE: opposite return // result of the collision tree's ray test, thus the !) CBaseTrace trace; trace.fraction = 1.0f; return (!dispTree.m_pDispTree->AABBTree_Ray(*pCtx->m_pRay, pCtx->m_pRay->InvDelta(), &trace, true)); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CVRadDispMgr::DispRayDistance_EnumerateElement(int userId, CBSPDispRayDistanceEnumerator* pCtx) { DispCollTree_t &dispTree = m_DispTrees[userId]; // don't test twice (check tested value) if (pCtx->m_pDispTested->m_pTested[userId] == pCtx->m_pDispTested->m_Enum) return true; // set the tested value pCtx->m_pDispTested->m_pTested[userId] = pCtx->m_pDispTested->m_Enum; // Test the ray, if it's closer than previous tests, use it! RayDispOutput_t output; output.ndxVerts[0] = -1; output.ndxVerts[1] = -1; output.ndxVerts[2] = -1; output.ndxVerts[3] = -1; output.u = -1.0f; output.v = -1.0f; output.dist = FLT_MAX; if (dispTree.m_pDispTree->AABBTree_Ray(*pCtx->m_pRay, output)) { if (output.dist < pCtx->m_Distance) { pCtx->m_Distance = output.dist; pCtx->m_pSurface = &g_pFaces[dispTree.m_pDispTree->GetParentIndex()]; // Get the luxel coordinate ComputePointFromBarycentric( dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[0]), dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[1]), dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[2]), output.u, output.v, pCtx->m_LuxelCoord); Vector v0, v1, v2; dispTree.m_pDispTree->GetVert(output.ndxVerts[0], v0); dispTree.m_pDispTree->GetVert(output.ndxVerts[1], v1); dispTree.m_pDispTree->GetVert(output.ndxVerts[2], v2); Vector e0 = v1 - v0; Vector e1 = v2 - v0; pCtx->m_Normal = CrossProduct(e0, e1); VectorNormalize(pCtx->m_Normal); } } return true; } //----------------------------------------------------------------------------- // Test a ray against a particular dispinfo //----------------------------------------------------------------------------- /* float CVRadDispMgr::ClipRayToDisp( Ray_t const &ray, int dispinfo ) { assert( m_DispTrees.IsValidIndex(dispinfo) ); RayDispOutput_t output; if (!m_DispTrees[dispinfo].m_pDispTree->AABBTree_Ray( ray, output )) return 1.0f; return output.dist; } */ //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CVRadDispMgr::DispFaceList_EnumerateLeaf(int ndxLeaf, int context) { // // add the faces found in this leaf to the face list // dleaf_t *pLeaf = &dleafs[ndxLeaf]; for (int ndxFace = 0; ndxFace < pLeaf->numleaffaces; ndxFace++) { // get the current face index int ndxLeafFace = pLeaf->firstleafface + ndxFace; // check to see if the face already lives in the list int ndx; int size = m_EnumDispFaceList.m_FaceList.Size(); for (ndx = 0; ndx < size; ndx++) { if (m_EnumDispFaceList.m_FaceList[ndx] == ndxLeafFace) break; } if (ndx == size) { int ndxList = m_EnumDispFaceList.m_FaceList.AddToTail(); m_EnumDispFaceList.m_FaceList[ndxList] = ndxLeafFace; } } return m_pBSPTreeData->EnumerateElementsInLeaf(ndxLeaf, &m_EnumDispFaceList, context); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CVRadDispMgr::DispFaceList_EnumerateElement(int userId, int context) { DispCollTree_t &dispTree = m_DispTrees[userId]; CVRADDispColl *pDispTree = dispTree.m_pDispTree; if (!pDispTree) return false; // check to see if the displacement already lives in the list int ndx; int size = m_EnumDispFaceList.m_DispList.Size(); for (ndx = 0; ndx < size; ndx++) { if (m_EnumDispFaceList.m_DispList[ndx] == pDispTree) break; } if (ndx == size) { int ndxList = m_EnumDispFaceList.m_DispList.AddToTail(); m_EnumDispFaceList.m_DispList[ndxList] = pDispTree; } return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- inline void GetSampleLight(facelight_t *pFaceLight, int ndxStyle, bool bBumped, int ndxSample, LightingValue_t *pSampleLight) { // SampleLight[0].Init( 20.0f, 10.0f, 10.0f ); // return; // get sample from bumped lighting data if (bBumped) { for (int ndxBump = 0; ndxBump < (NUM_BUMP_VECTS + 1); ndxBump++) { pSampleLight[ndxBump] = pFaceLight->light[ndxStyle][ndxBump][ndxSample]; } } // just a generally lit surface else { pSampleLight[0] = pFaceLight->light[ndxStyle][0][ndxSample]; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void AddSampleLightToRadial(Vector const &samplePos, Vector const &sampleNormal, LightingValue_t *pSampleLight, float sampleRadius2, Vector const &luxelPos, Vector const &luxelNormal, radial_t *pRadial, int ndxRadial, bool bBumped, bool bNeighborBumped) { // check normals to see if sample contributes any light at all float angle = sampleNormal.Dot(luxelNormal); if (angle < 0.15f) return; // calculate the light vector Vector vSegment = samplePos - luxelPos; // get the distance to the light float dist = vSegment.Length(); float dist2 = dist * dist; // Check to see if the light is within the influence. float influence = 1.0f - (dist2 / (sampleRadius2)); if (influence <= 0.0f) return; influence *= angle; if (bBumped) { if (bNeighborBumped) { for (int ndxBump = 0; ndxBump < (NUM_BUMP_VECTS + 1); ndxBump++) { pRadial->light[ndxBump][ndxRadial].AddWeighted(pSampleLight[ndxBump], influence); } pRadial->weight[ndxRadial] += influence; } else { influence *= 0.05f; for (int ndxBump = 0; ndxBump < (NUM_BUMP_VECTS + 1); ndxBump++) { pRadial->light[ndxBump][ndxRadial].AddWeighted(pSampleLight[0], influence); } pRadial->weight[ndxRadial] += influence; } } else { pRadial->light[0][ndxRadial].AddWeighted(pSampleLight[0], influence); pRadial->weight[ndxRadial] += influence; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CVRadDispMgr::IsNeighbor(int iFace, int iNeighborFace) { if (iFace == iNeighborFace) return true; faceneighbor_t *pFaceNeighbor = &faceneighbor[iFace]; for (int iNeighbor = 0; iNeighbor < pFaceNeighbor->numneighbors; iNeighbor++) { if (pFaceNeighbor->neighbor[iNeighbor] == iNeighborFace) return true; } return false; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::RadialLuxelAddSamples(int ndxFace, Vector const &luxelPt, Vector const &luxelNormal, float radius, radial_t *pRadial, int ndxRadial, bool bBump, int lightStyle) { // calculate one over the voxel size float ooVoxelSize = 1.0f / SAMPLEHASH_VOXEL_SIZE; // // find voxel info // int voxelMin[3], voxelMax[3]; for (int axis = 0; axis < 3; axis++) { voxelMin[axis] = (int)((luxelPt[axis] - radius) * ooVoxelSize); voxelMax[axis] = (int)((luxelPt[axis] + radius) * ooVoxelSize) + 1; } SampleData_t sampleData; for (int ndxZ = voxelMin[2]; ndxZ < voxelMax[2] + 1; ndxZ++) { for (int ndxY = voxelMin[1]; ndxY < voxelMax[1] + 1; ndxY++) { for (int ndxX = voxelMin[0]; ndxX < voxelMax[0] + 1; ndxX++) { sampleData.x = ndxX * 100; sampleData.y = ndxY * 10; sampleData.z = ndxZ; UtlHashHandle_t handle = g_SampleHashTable.Find(sampleData); if (handle != g_SampleHashTable.InvalidHandle()) { SampleData_t *pSampleData = &g_SampleHashTable.Element(handle); int count = pSampleData->m_Samples.Count(); for (int ndx = 0; ndx < count; ndx++) { SampleHandle_t sampleHandle = pSampleData->m_Samples.Element(ndx); int ndxSample = (sampleHandle & 0x0000ffff); int ndxFaceLight = ((sampleHandle >> 16) & 0x0000ffff); facelight_t *pFaceLight = &facelight[ndxFaceLight]; if (pFaceLight && IsNeighbor(ndxFace, ndxFaceLight)) { // // check for similar lightstyles // dface_t *pFace = &g_pFaces[ndxFaceLight]; if (pFace) { int ndxNeighborStyle = -1; for (int ndxLightStyle = 0; ndxLightStyle < MAXLIGHTMAPS; ndxLightStyle++) { if (pFace->styles[ndxLightStyle] == lightStyle) { ndxNeighborStyle = ndxLightStyle; break; } } if (ndxNeighborStyle == -1) continue; // is this surface bumped??? bool bNeighborBump = texinfo[pFace->texinfo].flags & SURF_BUMPLIGHT ? true : false; LightingValue_t sampleLight[NUM_BUMP_VECTS + 1]; GetSampleLight(pFaceLight, ndxNeighborStyle, bNeighborBump, ndxSample, sampleLight); AddSampleLightToRadial(pFaceLight->sample[ndxSample].pos, pFaceLight->sample[ndxSample].normal, sampleLight, radius*radius, luxelPt, luxelNormal, pRadial, ndxRadial, bBump, bNeighborBump); } } } } } } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::RadialLuxelBuild(CVRADDispColl *pDispTree, radial_t *pRadial, int ndxStyle, bool bBump) { // // get data lighting data // int ndxFace = pDispTree->GetParentIndex(); dface_t *pFace = &g_pFaces[ndxFace]; facelight_t *pFaceLight = &facelight[ndxFace]; // get the influence radius float radius2 = pDispTree->GetSampleRadius2(); float radius = (float)sqrt(radius2); int radialSize = pRadial->w * pRadial->h; for (int ndxRadial = 0; ndxRadial < radialSize; ndxRadial++) { RadialLuxelAddSamples(ndxFace, pFaceLight->luxel[ndxRadial], pFaceLight->luxelNormals[ndxRadial], radius, pRadial, ndxRadial, bBump, pFace->styles[ndxStyle]); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- radial_t *CVRadDispMgr::BuildLuxelRadial(int ndxFace, int ndxStyle, bool bBump) { // allocate the radial radial_t *pRadial = AllocateRadial(ndxFace); if (!pRadial) return NULL; // // step 1: get the displacement surface to be lit // DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo]; CVRADDispColl *pDispTree = dispTree.m_pDispTree; if (!pDispTree) return NULL; // step 2: build radial luxels RadialLuxelBuild(pDispTree, pRadial, ndxStyle, bBump); // step 3: return the built radial return pRadial; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CVRadDispMgr::SampleRadial(int ndxFace, radial_t *pRadial, Vector const &vPos, int ndxLxl, LightingValue_t *pLightSample, int sampleCount, bool bPatch) { bool bGoodSample = true; for (int count = 0; count < sampleCount; count++) { pLightSample[count].Zero(); if (pRadial->weight[ndxLxl] > 0.0f) { pLightSample[count].AddWeighted(pRadial->light[count][ndxLxl], (1.0f / pRadial->weight[ndxLxl])); } else { // error, luxel has no samples (not for patches) if (!bPatch) { // Yes, 2550 is correct! // pLightSample[count].Init( 2550.0f, 0.0f, 2550.0f ); if (count == 0) bGoodSample = false; } } } return bGoodSample; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void GetPatchLight(CPatch *pPatch, bool bBump, Vector *pPatchLight) { VectorCopy(pPatch->totallight.light[0], pPatchLight[0]); if (bBump) { for (int ndxBump = 1; ndxBump < (NUM_BUMP_VECTS + 1); ndxBump++) { VectorCopy(pPatch->totallight.light[ndxBump], pPatchLight[ndxBump]); } } } extern void GetBumpNormals(const float* sVect, const float* tVect, const Vector& flatNormal, const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS]); extern void PreGetBumpNormalsForDisp(texinfo_t *pTexinfo, Vector &vecU, Vector &vecV, Vector &vecNormal); //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void AddPatchLightToRadial(Vector const &patchOrigin, Vector const &patchNormal, Vector *pPatchLight, float patchRadius2, Vector const &luxelPos, Vector const &luxelNormal, radial_t *pRadial, int ndxRadial, bool bBump, bool bNeighborBump) { // calculate the light vector Vector vSegment = patchOrigin - luxelPos; // get the distance to the light float dist = vSegment.Length(); float dist2 = dist * dist; // Check to see if the light is within the sample influence. float influence = 1.0f - (dist2 / (patchRadius2)); if (influence <= 0.0f) return; if (bBump) { Vector normals[NUM_BUMP_VECTS + 1]; normals[0] = luxelNormal; texinfo_t *pTexinfo = &texinfo[g_pFaces[pRadial->facenum].texinfo]; Vector vecTexU, vecTexV; PreGetBumpNormalsForDisp(pTexinfo, vecTexU, vecTexV, normals[0]); GetBumpNormals(vecTexU, vecTexV, normals[0], normals[0], &normals[1]); if (bNeighborBump) { float flScale = patchNormal.Dot(normals[0]); flScale = max(0.0f, flScale); float flBumpInfluence = influence * flScale; for (int ndxBump = 0; ndxBump < (NUM_BUMP_VECTS + 1); ndxBump++) { pRadial->light[ndxBump][ndxRadial].AddWeighted(pPatchLight[ndxBump], flBumpInfluence); } pRadial->weight[ndxRadial] += flBumpInfluence; } else { float flScale = patchNormal.Dot(normals[0]); flScale = max(0.0f, flScale); float flBumpInfluence = influence * flScale * 0.05f; for (int ndxBump = 0; ndxBump < (NUM_BUMP_VECTS + 1); ndxBump++) { pRadial->light[ndxBump][ndxRadial].AddWeighted(pPatchLight[0], flBumpInfluence); } pRadial->weight[ndxRadial] += flBumpInfluence; } } else { float flScale = patchNormal.Dot(luxelNormal); flScale = max(0.0f, flScale); influence *= flScale; pRadial->light[0][ndxRadial].AddWeighted(pPatchLight[0], influence); // add the weight value pRadial->weight[ndxRadial] += influence; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::RadialLuxelAddPatch(int ndxFace, Vector const &luxelPt, Vector const &luxelNormal, float radius, radial_t *pRadial, int ndxRadial, bool bBump, CUtlVector &interestingPatches) { #ifdef SAMPLEHASH_QUERY_ONCE for (int i = 0; i < interestingPatches.Count(); i++) { CPatch *pPatch = interestingPatches[i]; bool bNeighborBump = texinfo[g_pFaces[pPatch->faceNumber].texinfo].flags & SURF_BUMPLIGHT ? true : false; Vector patchLight[NUM_BUMP_VECTS + 1]; GetPatchLight(pPatch, bBump, patchLight); AddPatchLightToRadial(pPatch->origin, pPatch->normal, patchLight, radius*radius, luxelPt, luxelNormal, pRadial, ndxRadial, bBump, bNeighborBump); } #else // calculate one over the voxel size float ooVoxelSize = 1.0f / SAMPLEHASH_VOXEL_SIZE; // // find voxel info // int voxelMin[3], voxelMax[3]; for (int axis = 0; axis < 3; axis++) { voxelMin[axis] = (int)((luxelPt[axis] - radius) * ooVoxelSize); voxelMax[axis] = (int)((luxelPt[axis] + radius) * ooVoxelSize) + 1; } unsigned short curIterationKey = IncrementPatchIterationKey(); PatchSampleData_t patchData; for (int ndxZ = voxelMin[2]; ndxZ < voxelMax[2] + 1; ndxZ++) { for (int ndxY = voxelMin[1]; ndxY < voxelMax[1] + 1; ndxY++) { for (int ndxX = voxelMin[0]; ndxX < voxelMax[0] + 1; ndxX++) { patchData.x = ndxX * 100; patchData.y = ndxY * 10; patchData.z = ndxZ; UtlHashHandle_t handle = g_PatchSampleHashTable.Find(patchData); if (handle != g_PatchSampleHashTable.InvalidHandle()) { PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element(handle); int count = pPatchData->m_ndxPatches.Count(); for (int ndx = 0; ndx < count; ndx++) { int ndxPatch = pPatchData->m_ndxPatches.Element(ndx); CPatch *pPatch = &g_Patches.Element(ndxPatch); if (pPatch && pPatch->m_IterationKey != curIterationKey) { pPatch->m_IterationKey = curIterationKey; if (IsNeighbor(ndxFace, pPatch->faceNumber)) { bool bNeighborBump = texinfo[g_pFaces[pPatch->faceNumber].texinfo].flags & SURF_BUMPLIGHT ? true : false; Vector patchLight[NUM_BUMP_VECTS + 1]; GetPatchLight(pPatch, bBump, patchLight); AddPatchLightToRadial(pPatch->origin, pPatch->normal, patchLight, radius*radius, luxelPt, luxelNormal, pRadial, ndxRadial, bBump, bNeighborBump); } } } } } } } #endif } void CVRadDispMgr::GetInterestingPatchesForLuxels( int ndxFace, CUtlVector &interestingPatches, float patchSampleRadius) { facelight_t *pFaceLight = &facelight[ndxFace]; // Get the max bounds of all voxels that these luxels touch. Vector vLuxelMin(FLT_MAX, FLT_MAX, FLT_MAX); Vector vLuxelMax(-FLT_MAX, -FLT_MAX, -FLT_MAX); for (int i = 0; i < pFaceLight->numluxels; i++) { VectorMin(pFaceLight->luxel[i], vLuxelMin, vLuxelMin); VectorMax(pFaceLight->luxel[i], vLuxelMax, vLuxelMax); } int allVoxelMin[3], allVoxelMax[3]; for (int axis = 0; axis < 3; axis++) { allVoxelMin[axis] = (int)((vLuxelMin[axis] - patchSampleRadius) / SAMPLEHASH_VOXEL_SIZE); allVoxelMax[axis] = (int)((vLuxelMax[axis] + patchSampleRadius) / SAMPLEHASH_VOXEL_SIZE) + 1; } int allVoxelSize[3] = { allVoxelMax[0] - allVoxelMin[0], allVoxelMax[1] - allVoxelMin[1], allVoxelMax[2] - allVoxelMin[2] }; // Now figure out exactly which voxels these luxels touch. CUtlVector voxelBits; voxelBits.SetSize(((allVoxelSize[0] * allVoxelSize[1] * allVoxelSize[2]) + 7) / 8); memset(voxelBits.Base(), 0, voxelBits.Count()); for (int i = 0; i < pFaceLight->numluxels; i++) { int voxelMin[3], voxelMax[3]; for (int axis = 0; axis < 3; axis++) { voxelMin[axis] = (int)((pFaceLight->luxel[i][axis] - patchSampleRadius) / SAMPLEHASH_VOXEL_SIZE); voxelMax[axis] = (int)((pFaceLight->luxel[i][axis] + patchSampleRadius) / SAMPLEHASH_VOXEL_SIZE) + 1; } for (int x = voxelMin[0]; x < voxelMax[0]; x++) { for (int y = voxelMin[1]; y < voxelMax[1]; y++) { for (int z = voxelMin[2]; z < voxelMax[2]; z++) { int iBit = (z - allVoxelMin[2])*(allVoxelSize[0] * allVoxelSize[1]) + (y - allVoxelMin[1])*allVoxelSize[0] + (x - allVoxelMin[0]); voxelBits[iBit >> 3] |= (1 << (iBit & 7)); } } } } // Now get the list of patches that touch those voxels. unsigned short curIterationKey = IncrementPatchIterationKey(); for (int x = 0; x < allVoxelSize[0]; x++) { for (int y = 0; y < allVoxelSize[1]; y++) { for (int z = 0; z < allVoxelSize[2]; z++) { // Make sure this voxel has any luxels that care about it. int iBit = z*(allVoxelSize[0] * allVoxelSize[1]) + y*allVoxelSize[0] + x; unsigned char val = voxelBits[iBit >> 3] & (1 << (iBit & 7)); if (!val) continue; PatchSampleData_t patchData; patchData.x = (x + allVoxelMin[0]) * 100; patchData.y = (y + allVoxelMin[1]) * 10; patchData.z = (z + allVoxelMin[2]); UtlHashHandle_t handle = g_PatchSampleHashTable.Find(patchData); if (handle != g_PatchSampleHashTable.InvalidHandle()) { PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element(handle); // For all patches that touch this hash table element.. for (int ndx = 0; ndx < pPatchData->m_ndxPatches.Count(); ndx++) { int ndxPatch = pPatchData->m_ndxPatches.Element(ndx); CPatch *pPatch = &g_Patches.Element(ndxPatch); // If we haven't touched the patch already and it's a valid neighbor, then we want to use it. if (pPatch && pPatch->m_IterationKey != curIterationKey) { pPatch->m_IterationKey = curIterationKey; if (IsNeighbor(ndxFace, pPatch->faceNumber)) { interestingPatches.AddToTail(pPatch); } } } } } } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::RadialPatchBuild(CVRADDispColl *pDispTree, radial_t *pRadial, bool bBump) { // // get data lighting data // int ndxFace = pDispTree->GetParentIndex(); facelight_t *pFaceLight = &facelight[ndxFace]; // get the influence radius float radius2 = pDispTree->GetPatchSampleRadius2(); float radius = (float)sqrt(radius2); CUtlVector interestingPatches; #ifdef SAMPLEHASH_QUERY_ONCE GetInterestingPatchesForLuxels(ndxFace, interestingPatches, radius); #endif int radialSize = pRadial->w * pRadial->h; for (int ndxRadial = 0; ndxRadial < radialSize; ndxRadial++) { RadialLuxelAddPatch( ndxFace, pFaceLight->luxel[ndxRadial], pFaceLight->luxelNormals[ndxRadial], radius, pRadial, ndxRadial, bBump, interestingPatches); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- radial_t *CVRadDispMgr::BuildPatchRadial(int ndxFace, bool bBump) { // allocate the radial radial_t *pRadial = AllocateRadial(ndxFace); if (!pRadial) return NULL; // // step 1: get the displacement surface to be lit // DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo]; CVRADDispColl *pDispTree = dispTree.m_pDispTree; if (!pDispTree) return NULL; // step 2: build radial of patch light RadialPatchBuild(pDispTree, pRadial, bBump); // step 3: return the built radial return pRadial; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool SampleInSolid(sample_t *pSample) { int ndxLeaf = PointLeafnum(pSample->pos); return (dleafs[ndxLeaf].contents == CONTENTS_SOLID); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::InsertSamplesDataIntoHashTable(void) { int totalSamples = 0; #if 0 int totalSamplesInSolid = 0; #endif for (int ndxFace = 0; ndxFace < numfaces; ndxFace++) { dface_t *pFace = &g_pFaces[ndxFace]; facelight_t *pFaceLight = &facelight[ndxFace]; if (!pFace || !pFaceLight) continue; if (texinfo[pFace->texinfo].flags & TEX_SPECIAL) continue; #if 0 bool bDisp = (pFace->dispinfo != -1); #endif // // for each sample // for (int ndxSample = 0; ndxSample < pFaceLight->numsamples; ndxSample++) { sample_t *pSample = &pFaceLight->sample[ndxSample]; if (pSample) { #if 0 if (bDisp) { // test sample to see if the displacement samples resides in solid if (SampleInSolid(pSample)) { totalSamplesInSolid++; continue; } } #endif // create the sample handle SampleHandle_t sampleHandle = ndxSample; sampleHandle |= (ndxFace << 16); SampleData_AddSample(pSample, sampleHandle); } } totalSamples += pFaceLight->numsamples; } #if 0 // not implemented yet!!! Msg("%d samples in solid\n", totalSamplesInSolid); #endif // log the distribution SampleData_Log(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::InsertPatchSampleDataIntoHashTable(void) { // don't insert patch samples if we are not bouncing light if (numbounce <= 0) return; int totalPatchSamples = 0; for (int ndxFace = 0; ndxFace < numfaces; ndxFace++) { dface_t *pFace = &g_pFaces[ndxFace]; facelight_t *pFaceLight = &facelight[ndxFace]; if (!pFace || !pFaceLight) continue; if (texinfo[pFace->texinfo].flags & TEX_SPECIAL) continue; // // for each patch // CPatch *pNextPatch = NULL; if (g_FacePatches.Element(ndxFace) != g_FacePatches.InvalidIndex()) { for (CPatch *pPatch = &g_Patches.Element(g_FacePatches.Element(ndxFace)); pPatch; pPatch = pNextPatch) { // next patch pNextPatch = NULL; if (pPatch->ndxNext != g_Patches.InvalidIndex()) { pNextPatch = &g_Patches.Element(pPatch->ndxNext); } // skip patches with children if (pPatch->child1 != g_Patches.InvalidIndex()) continue; int ndxPatch = pPatch - g_Patches.Base(); PatchSampleData_AddSample(pPatch, ndxPatch); totalPatchSamples++; } } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::StartTimer(const char *name) { Msg(name); m_Timer.Start(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CVRadDispMgr::EndTimer(void) { m_Timer.End(); CCycleCount duration = m_Timer.GetDuration(); double seconds = duration.GetSeconds(); Msg("Done<%1.4lf sec>\n", seconds); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CVRadDispMgr::BuildDispSamples(lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace) { // get the tree assosciated with the face DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo]; CVRADDispColl *pDispTree = dispTree.m_pDispTree; if (!pDispTree) return false; // lightmap size int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0] + 1; int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1] + 1; // calculate the steps in uv space float stepU = 1.0f / (float)width; float stepV = 1.0f / (float)height; float halfStepU = stepU * 0.5f; float halfStepV = stepV * 0.5f; // // build the winding points (used to generate world space winding and // calculate the area of the "sample") // int ndxU, ndxV; CUtlVector samples; samples.SetCount(SINGLEMAP); sample_t *pSamples = samples.Base(); CUtlVector worldPoints; worldPoints.SetCount(SINGLEMAP); Vector *pWorldPoints = worldPoints.Base(); for (ndxV = 0; ndxV < (height + 1); ndxV++) { for (ndxU = 0; ndxU < (width + 1); ndxU++) { int ndx = (ndxV * (width + 1)) + ndxU; Vector2D uv(ndxU * stepU, ndxV * stepV); pDispTree->DispUVToSurfPoint(uv, pWorldPoints[ndx], 0.0f); } } for (ndxV = 0; ndxV < height; ndxV++) { for (ndxU = 0; ndxU < width; ndxU++) { // build the winding winding_t *pWinding = AllocWinding(4); if (pWinding) { pWinding->numpoints = 4; pWinding->p[0] = pWorldPoints[(ndxV*(width + 1)) + ndxU]; pWinding->p[1] = pWorldPoints[((ndxV + 1)*(width + 1)) + ndxU]; pWinding->p[2] = pWorldPoints[((ndxV + 1)*(width + 1)) + (ndxU + 1)]; pWinding->p[3] = pWorldPoints[(ndxV*(width + 1)) + (ndxU + 1)]; // calculate the area float area = WindingArea(pWinding); int ndxSample = (ndxV * width) + ndxU; pSamples[ndxSample].w = pWinding; pSamples[ndxSample].area = area; } else { Msg("BuildDispSamples: WARNING - failed winding allocation\n"); } } } // // build the samples points (based on s, t and sampleoffset (center of samples); // generates world space position and normal) // for (ndxV = 0; ndxV < height; ndxV++) { for (ndxU = 0; ndxU < width; ndxU++) { int ndxSample = (ndxV * width) + ndxU; pSamples[ndxSample].s = ndxU; pSamples[ndxSample].t = ndxV; pSamples[ndxSample].coord[0] = (ndxU * stepU) + halfStepU; pSamples[ndxSample].coord[1] = (ndxV * stepV) + halfStepV; pDispTree->DispUVToSurfPoint(pSamples[ndxSample].coord, pSamples[ndxSample].pos, 1.0f); pDispTree->DispUVToSurfNormal(pSamples[ndxSample].coord, pSamples[ndxSample].normal); } } // // copy over samples // pFaceLight->numsamples = width * height; pFaceLight->sample = (sample_t*)calloc(pFaceLight->numsamples, sizeof(*pFaceLight->sample)); if (!pFaceLight->sample) return false; memcpy(pFaceLight->sample, pSamples, pFaceLight->numsamples * sizeof(*pFaceLight->sample)); // statistics - warning?! if (pFaceLight->numsamples == 0) { Msg("BuildDispSamples: WARNING - no samples %d\n", pLightInfo->face - g_pFaces); } return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CVRadDispMgr::BuildDispLuxels(lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace) { // get the tree assosciated with the face DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo]; CVRADDispColl *pDispTree = dispTree.m_pDispTree; if (!pDispTree) return false; // lightmap size int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0] + 1; int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1] + 1; // calcuate actual luxel points pFaceLight->numluxels = width * height; pFaceLight->luxel = (Vector*)calloc(pFaceLight->numluxels, sizeof(*pFaceLight->luxel)); pFaceLight->luxelNormals = (Vector*)calloc(pFaceLight->numluxels, sizeof(Vector)); if (!pFaceLight->luxel || !pFaceLight->luxelNormals) return false; float stepU = 1.0f / (float)(width - 1); float stepV = 1.0f / (float)(height - 1); for (int ndxV = 0; ndxV < height; ndxV++) { for (int ndxU = 0; ndxU < width; ndxU++) { int ndxLuxel = (ndxV * width) + ndxU; Vector2D uv(ndxU * stepU, ndxV * stepV); pDispTree->DispUVToSurfPoint(uv, pFaceLight->luxel[ndxLuxel], 1.0f); pDispTree->DispUVToSurfNormal(uv, pFaceLight->luxelNormals[ndxLuxel]); } } return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CVRadDispMgr::BuildDispSamplesAndLuxels_DoFast(lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace) { // get the tree assosciated with the face DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo]; CVRADDispColl *pDispTree = dispTree.m_pDispTree; if (!pDispTree) return false; // lightmap size int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0] + 1; int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1] + 1; // calcuate actual luxel points pFaceLight->numsamples = width * height; pFaceLight->sample = (sample_t*)calloc(pFaceLight->numsamples, sizeof(*pFaceLight->sample)); if (!pFaceLight->sample) return false; pFaceLight->numluxels = width * height; pFaceLight->luxel = (Vector*)calloc(pFaceLight->numluxels, sizeof(*pFaceLight->luxel)); pFaceLight->luxelNormals = (Vector*)calloc(pFaceLight->numluxels, sizeof(Vector)); if (!pFaceLight->luxel || !pFaceLight->luxelNormals) return false; float stepU = 1.0f / (float)(width - 1); float stepV = 1.0f / (float)(height - 1); float halfStepU = stepU * 0.5f; float halfStepV = stepV * 0.5f; for (int ndxV = 0; ndxV < height; ndxV++) { for (int ndxU = 0; ndxU < width; ndxU++) { int ndx = (ndxV * width) + ndxU; pFaceLight->sample[ndx].s = ndxU; pFaceLight->sample[ndx].t = ndxV; pFaceLight->sample[ndx].coord[0] = (ndxU * stepU) + halfStepU; pFaceLight->sample[ndx].coord[1] = (ndxV * stepV) + halfStepV; pDispTree->DispUVToSurfPoint(pFaceLight->sample[ndx].coord, pFaceLight->sample[ndx].pos, 1.0f); pDispTree->DispUVToSurfNormal(pFaceLight->sample[ndx].coord, pFaceLight->sample[ndx].normal); pFaceLight->luxel[ndx] = pFaceLight->sample[ndx].pos; pFaceLight->luxelNormals[ndx] = pFaceLight->sample[ndx].normal; } } return true; }