// --------------------------------------------------------------------------------------------- // Crytek CryENGINE source code // History: // - Created by Marco Corbetta // - Rewritten by Tim Schroeder // - Partial rewrite for editor integration // --------------------------------------------------------------------------------------------- #include "stdafx.h" #include #include #include //#include "IndoorLightPatches.h" #include #include #include "IShader.h" #include "IndoorLightPatches.h" #include "../Material/Material.h" #include "../../CryCommon/CryHeaders.h" //threshold constants static const float scfBaryThreshold = 1.001f; extern CAASettings gAASettings; //!!!projects point into triangle const bool CRadPoly::CheckPointInTriangle(Vec3d &inPosition, const Vec3d &rVert0, const Vec3d &rVert1, const Vec3d &rVert2, float &rOutfAlpha, float &rOutfBeta, float &rfArea3d) { //project into triangle plane Vec3d oneEdge = rVert0 - rVert1; Vec3d secondEdge = rVert0 - rVert2; Vec3d vTrianglePlaneNormal = oneEdge % secondEdge; vTrianglePlaneNormal.Normalize(); Vec3d vDir = inPosition - rVert0; if(vDir * vTrianglePlaneNormal > 0) vDir *= -1; const float cfPlaneDist2 = -(vDir*vTrianglePlaneNormal); Vec3d r = vTrianglePlaneNormal*(cfPlaneDist2); const Vec3d vNewInPos = inPosition = (inPosition + r); rfArea3d = CalcTriangleArea(rVert0,rVert1,rVert2); // to prevent crash if(rfArea3d == 0.0f) { rOutfAlpha = rOutfBeta = 0.0f; return false; } //first sub tri const float a1=CalcTriangleArea(vNewInPos,rVert1,rVert2); //second sub tri const float a2=CalcTriangleArea(rVert0,vNewInPos,rVert2); //third sub tri const float a3=CalcTriangleArea(rVert0,rVert1,vNewInPos); //check sum, must be close to real area const float asum = a1+a2+a3; bool cbBaryFailCond = false; if(fabs(asum - rfArea3d) > rfArea3d*0.01f) cbBaryFailCond = true; //area difference to large if(cbBaryFailCond == false) { //calc alpha beta and gamma components as area ratio rOutfAlpha = a1 / rfArea3d; rOutfBeta = a2 / rfArea3d; cbBaryFailCond = ((rOutfAlpha + rOutfBeta) > scfBaryThreshold)?true:false; //set to fail if barycentric coords are invalid } return cbBaryFailCond; } const Vec3d SComplexOSDot3Texel::TransformIntoTS(Vec3d& rSource)const { assert(pSourcePatch && pSourcePatch->m_lstOriginals.size() == 3);//make sure it is a valid poly const float cfSqLength = rSource.GetLengthSquared(); if(fabs(cfSqLength - 1.f) > 0.01f && cfSqLength > 0.01f)//leave null vector as it is rSource.Normalize(); //correct the values to not let the sum go anywhere but 1.0f float cfAlpha = (fAlpha > 1.0f)?1.0f:fAlpha; float cfBeta = (fBeta > 1.0f)?1.0f:fBeta; float cfGamma = (1.0f-cfAlpha-cfBeta); if(cfGamma < 0.0f) { const float cfError = cfAlpha + cfBeta - 1.0f; if(cfAlpha >= cfError) cfAlpha -= cfError; else cfBeta -= cfError; cfGamma = 0.0f; } Matrix33 matTangentTranf; const CRadVertex& rVert0 = pSourcePatch->m_lstOriginals[0]; const CRadVertex& rVert1 = pSourcePatch->m_lstOriginals[1]; const CRadVertex& rVert2 = pSourcePatch->m_lstOriginals[2]; Vec3d vTNormal = rVert0.m_vTNormal * cfAlpha + rVert1.m_vTNormal * cfBeta + rVert2.m_vTNormal * cfGamma; vTNormal.Normalize(); Vec3d vBinormal = rVert0.m_vBinormal * cfAlpha + rVert1.m_vBinormal * cfBeta + rVert2.m_vBinormal * cfGamma; vBinormal.Normalize(); Vec3d vTangent = rVert0.m_vTangent * cfAlpha + rVert1.m_vTangent * cfBeta + rVert2.m_vTangent * cfGamma; vTangent.Normalize(); matTangentTranf(0,0) = vTNormal.x; matTangentTranf(0,1) = vTNormal.y; matTangentTranf(0,2) = vTNormal.z; matTangentTranf(1,0) = vBinormal.x; matTangentTranf(1,1) = vBinormal.y; matTangentTranf(1,2) = vBinormal.z; matTangentTranf(2,0) = vTangent.x; matTangentTranf(2,1) = vTangent.y; matTangentTranf(2,2) = vTangent.z; Vec3d vResult = (matTangentTranf * rSource); if(fabs(vResult.GetLengthSquared() - 1.f) > 0.01f) vResult.Normalize(); return vResult; } void CRadPoly::ApplyTangentSpaceToVertex(CRadVertex &outVertex, float cfAlpha, float cfBeta) { //correct the values to not let the sum go anywhere but 1.0f cfAlpha = (cfAlpha > 1.0f)?1.0f:cfAlpha; cfBeta = (cfBeta > 1.0f)?1.0f:cfBeta; float cfGamma = (1.0f-cfAlpha-cfBeta); if(cfGamma < 0.0f) { const float cfError = cfAlpha + cfBeta - 1.0f; if(cfAlpha >= cfError) cfAlpha -= cfError; else cfBeta -= cfError; cfGamma = 0.0f; } outVertex.m_vTNormal = m_lstOriginals[0].m_vTNormal * cfAlpha + m_lstOriginals[1].m_vTNormal * cfBeta + m_lstOriginals[2].m_vTNormal * cfGamma; outVertex.m_vTNormal.Normalize(); outVertex.m_vBinormal = m_lstOriginals[0].m_vBinormal * cfAlpha + m_lstOriginals[1].m_vBinormal * cfBeta + m_lstOriginals[2].m_vBinormal * cfGamma; outVertex.m_vBinormal.Normalize(); outVertex.m_vTangent = m_lstOriginals[0].m_vTangent * cfAlpha + m_lstOriginals[1].m_vTangent * cfBeta + m_lstOriginals[2].m_vTangent * cfGamma; outVertex.m_vTangent.Normalize(); } void CRadPoly::ApplyBaryCoordsToVertex(CRadVertex& rDest, const CRadVertex& rSource0, const CRadVertex& rSource1, const CRadVertex& rSource2, float cfAlpha, float cfBeta, const bool cbCOmputeTS) { //correct the values to not let the sum go anywhere but 1.0f cfAlpha = (cfAlpha > 1.0f)?1.0f:cfAlpha; cfBeta = (cfBeta > 1.0f)?1.0f:cfBeta; float cfGamma = (1.0f-cfAlpha-cfBeta); if(cfGamma < 0.0f) { const float cfError = cfAlpha + cfBeta - 1.0f; if(cfAlpha >= cfError) cfAlpha -= cfError; else cfBeta -= cfError; cfGamma = 0.0f; } rDest.m_vPos = rSource0.m_vPos * cfAlpha + rSource1.m_vPos * cfBeta + rSource2.m_vPos * cfGamma; rDest.m_vNormal = rSource0.m_vNormal * cfAlpha + rSource1.m_vNormal * cfBeta + rSource2.m_vNormal * cfGamma; rDest.m_vNormal.Normalize(); if(cbCOmputeTS) { rDest.m_vTNormal = rSource0.m_vTNormal * cfAlpha + rSource1.m_vTNormal * cfBeta + rSource2.m_vTNormal * cfGamma; rDest.m_vTNormal.Normalize(); rDest.m_vBinormal = rSource0.m_vBinormal * cfAlpha + rSource1.m_vBinormal * cfBeta + rSource2.m_vBinormal * cfGamma; rDest.m_vBinormal.Normalize(); rDest.m_vTangent = rSource0.m_vTangent * cfAlpha + rSource1.m_vTangent * cfBeta + rSource2.m_vTangent * cfGamma; rDest.m_vTangent.Normalize(); } } CRadPoly::CRadPoly() : m_pSource(NULL), m_pMergeSource(NULL), m_dwFlags(0), m_nX1(0), m_nY1(0), m_nX2(0), m_nY2(0), m_fX1(0.f), m_fY1(0.f), m_fX2(0.f), m_fY2(0.f), m_nW(0), m_nH(0), m_nAx1(0), m_nAx2(0), m_nAxis(0), m_pLightmapData(NULL), m_pHDRLightmapData(NULL), m_pDominantDirData(NULL), m_pWSDominantDirData(NULL), m_pOcclMapData(NULL){} const float CRadPoly::PointInTriangle(const Vec3d &point, const int ciIndex0, const int ciIndex1) { assert(m_lstOriginals.size() == 3); float fOutfNearestDistance=FLT_MAX; float xt = point[ciIndex0]; float yt = point[ciIndex1]; float Ax,Ay,Bx,By; float s; bool bFront=false; bool bBack=false; int kk2; //check if all the vertices are on the same //side of the edges for (int k=0;k<3;k++) { kk2=(k+1)%3; Ax=m_lstOriginals[k].m_vPos[ciIndex0];Bx=m_lstOriginals[kk2].m_vPos[ciIndex0]; Ay=m_lstOriginals[k].m_vPos[ciIndex1];By=m_lstOriginals[kk2].m_vPos[ciIndex1]; s=((Ay-yt)*(Bx-Ax)-(Ax-xt)*(By-Ay)); float fEdgeLength=sqrtf( sqr(Ax-Bx) + sqr(Ay-By) ); float fDistance=sqrtf(sqr(Ax-xt)+sqr(Ay-yt)) + sqrtf(sqr(Bx-xt)+sqr(By-yt)) - fEdgeLength; if(fDistance=0) bFront=true; else bBack=true; } //k if(bFront != bBack) { return 0.f; } return fOutfNearestDistance; } //check if this polygon has connections with another poly ////////////////////////////////////////////////////////////////////// bool CRadPoly::Connected(CRadPoly *pOther) { static const float epsPos = 0.00001f; //be more correct, vertex position should really be the same CRadVertex *pVert1,*pVert2; for (radpolyit i=m_lstMerged.begin();i!=m_lstMerged.end();i++) { CRadPoly *pPoly=(*i); //pass trough all vertices to check if at least one is connected for (radvertexit v1=pPoly->m_lstOriginals.begin();v1m_lstOriginals.end();v1++) { pVert1=&(*v1); for (radvertexit v2=pOther->m_lstOriginals.begin();v2m_lstOriginals.end();v2++) { pVert2=&(*v2); if (IsEquivalent(pVert1->m_vPos,pVert2->m_vPos,epsPos) ) return (true); } //v2 } //v1 } //i return (false); } CRadPoly::CRadPoly(CRadPoly *pSource) : m_Plane(pSource->m_Plane),m_dwFlags(pSource->m_dwFlags),m_pSource(pSource), m_fX1(pSource->m_fX1),m_fX2(pSource->m_fX2),m_fY1(pSource->m_fY1),m_fY2(pSource->m_fY2), m_nX1(0), m_nY1(0), m_nX2(0), m_nY2(0), m_nW(0), m_nH(0), m_nAx1(0), m_nAx2(0), m_nAxis(0), m_pLightmapData(NULL), m_pHDRLightmapData(NULL), m_pDominantDirData(NULL), m_pWSDominantDirData(NULL),m_pOcclMapData(NULL){} void CRadPoly::CalculateTangentSpace(SUV vUV[3]) { assert(m_lstOriginals.size() == 3);//assure it is the real triangle //now compute tangent space according to face normal const Vec3d vEdge0 = m_lstOriginals[1].m_vPos - m_lstOriginals[0].m_vPos; const Vec3d vEdge1 = m_lstOriginals[2].m_vPos - m_lstOriginals[0].m_vPos; Vec3d vCross = vEdge0 ^ vEdge1; vCross.Normalize(); const real fDeltaU1=vUV[1].u-vUV[0].u; const real fDeltaU2=vUV[2].u-vUV[0].u; const real fDeltaV1=vUV[1].v-vUV[0].v; const real fDeltaV2=vUV[2].v-vUV[0].v; const real div =(fDeltaU1*fDeltaV2-fDeltaU2*fDeltaV1); Vec3d vU,vV; if(div!=0.0f) { // area(u1*v2-u2*v1)/2 const real fAreaMul2=fabsf(fDeltaU1*fDeltaV2-fDeltaU2*fDeltaV1); // weight the tangent vectors by the UV triangles area size (fix problems with base UV assignment) const float a = (float)fDeltaV2/div; const float b = (float)-fDeltaV1/div; const float c = (float)-fDeltaU2/div; const float d = (float)fDeltaU1/div; vU = (vEdge0 * a + vEdge1 * b) * fAreaMul2; vU.Normalize(); vV = (vEdge0 * c + vEdge1 * d) * fAreaMul2; vV.Normalize(); } else { vU = Vec3d(0,0,0); vV = Vec3d(0,0,0); } for(int k=0; k<3; k++) { CRadVertex& rVert = m_lstOriginals[k]; rVert.m_vNormal = -vCross; //use face normal, might get smoothed later on rVert.m_vBinormal = -vV; rVert.m_vTangent = -vU; rVert.m_vTNormal = -vCross; } } bool CRadPoly::InterpolateVertexAt( const float infX, const float infY, CRadVertex &outVertex, SComplexOSDot3Texel& rDot3Texel, const bool cbSubSample, const bool cbSnap) { static const real scfLongRay = (real)1180.0; const real cfAAInfX = cbSubSample? gAASettings.RetrieveRealSamplePos(infX) : infX; const real cfAAInfY = cbSubSample? gAASettings.RetrieveRealSamplePos(infY) : infY; const CRadPoly *pData = m_lstOriginals.empty()? m_pSource : this; const real fPointOn = pData->m_lstOriginals[0].m_vPos[(int)m_nAxis]; const real fXstart = m_fX1 + (cfAAInfX)/(real)(m_nW-1)*(m_fX2-m_fX1);//if AA is used, invert texture coordinate to get back to the right resolution const real fYstart = m_fY1 + (cfAAInfY)/(real)(m_nH-1)*(m_fY2-m_fY1);//if AA is used, invert texture coordinate to get back to the right resolution Vec3d vPos1,vPos2; vPos1[(int)m_nAx1] = vPos2[(int)m_nAx1] = fXstart; vPos1[(int)m_nAx2] = vPos2[(int)m_nAx2] = fYstart; Vec3d& rvPoint = outVertex.m_vPos; //axial plane is fast if (CalcPlaneType2(m_Plane.n) <= PLANE_Z) { rvPoint = vPos1; rvPoint[(int)m_nAxis] = fPointOn; } else { //get the exact point position vPos1[(int)m_nAxis] = fPointOn - scfLongRay; vPos2[(int)m_nAxis] = fPointOn + scfLongRay; const real fDist1 = m_Plane.DistFromPlane(vPos1); const real fDist2 = m_Plane.DistFromPlane(vPos2); if(abs(fDist1 - fDist2) < (real)0.00001) return(false); Vec3d vVert = vPos2 - vPos1; const real fDot = fDist1 / (fDist1 - fDist2); vVert = vVert * fDot; rvPoint = vPos1 + vVert; } outVertex.m_fpX = infX; outVertex.m_fpY = infY; // interpolate normals and tangent base vectors within the triangle CRadPoly* nearestpoly = GetNearestPolyAt(rvPoint); if((nearestpoly == NULL) || !nearestpoly->ApplyBarycentricCoordinates(rvPoint, outVertex, rDot3Texel, false, cbSnap)) return false; return true; } CRadPoly *CRadPoly::GetNearestPolyAt( const Vec3d &inPosition ) { float fBestDistance=FLT_MAX; CRadPoly *pBestPoly=0; for (radpolyit i=m_lstMerged.begin();i!=m_lstMerged.end();i++) { float fDistance=FLT_MAX; CRadPoly *pPoly=(*i); if(pPoly->m_lstOriginals.empty()) pPoly=pPoly->m_pSource; fDistance = pPoly->PointInTriangle(inPosition,m_nAx1,m_nAx2); if(fDistance == 0.f) { //force early out fBestDistance = 0.f; return pPoly; } if(fDistanceGetEntityStatObj(0, NULL); //calc sphere's radius Vec3d vVec=(pObj->GetBoxMax()-pObj->GetBoxMin())/2; m_fRadius=vVec.Length(); m_vCenter = matEtyMtx.TransformPointOLD(pObj->GetBoxMin()) + vVec; m_fRadius2=(m_fRadius*m_fRadius); m_dwHashValue=CalcLocalLightHashValue(); for(int i=0;i<16;i++) CalcNCombineHash(matEtyMtx.data[i],m_dwHashValue); } const bool CRadMesh::FillInValues(IEntityRender *pIEtyRend, const CBrushObject *pBrushObject, const Matrix44& matEtyMtx) { IStatObj *pObj = pIEtyRend->GetEntityStatObj(0, NULL); m_pESource = pIEtyRend; CLeafBuffer *pLB = pObj->GetLeafBuffer(); m_fMaxVariance = 0.f; if (pLB->IsEmpty()) return false; m_sGLMName = pBrushObject->GetName(); //set individual lightmap accuracy and glm name m_fLMAccuracy = 1.f; assert(pBrushObject); CMaterial *pMat = pBrushObject->GetMaterial(); if(pMat) { if((pMat->GetShaderResources().m_ResFlags) & MTLFLAG_2SIDED) { IMatInfo *pMatInfo = pMat->GetMatInfo(); if(pMatInfo) { // Check if the material is not a no_draw one IShader *pEff = pMatInfo->GetShaderItem().m_pShader; if (pEff) { IShader *pShTempl = pEff->GetTemplate(-1); if (pShTempl) { if(!(pShTempl->GetFlags3() & EF3_NODRAW)) m_uiFlags |= DOUBLE_SIDED_MAT; } } } } } else { if(pLB->m_pMats) { list2& rMatInfoList = *pLB->m_pMats; CMatInfo* pElems = rMatInfoList.GetElements(); for(int i=0; iGetTemplate(-1); if (pShTempl) { if(pShTempl->GetFlags3() & EF3_NODRAW) { continue; } } } SRenderShaderResources *pRSR = (pElems[i].GetShaderItem()).m_pShaderResources; if(pRSR && (pRSR->m_ResFlags & MTLFLAG_2SIDED)) { m_uiFlags |= DOUBLE_SIDED_MAT; break; } } } } CVarBlock *pVarBlock = pBrushObject->GetVarBlock(); if(pVarBlock) { pLightmapQualityVar = pVarBlock->FindVariable("LightmapQuality"); if(pLightmapQualityVar && (pLightmapQualityVar->GetType() == IVariable::FLOAT)) { pLightmapQualityVar->Get(m_fLMAccuracy); } IVariable *pVar = pVarBlock->FindVariable("ReceiveLightmap"); if(pVar && (pVar->GetType() == IVariable::BOOL)) { pVar->Get(m_bReceiveLightmap); if(m_bReceiveLightmap) m_uiFlags |= RECEIVES_LIGHTMAP;//necessary since m_bReceiveLightmap will be used for switching it off for other reason as well } } pVarBlock = pBrushObject->GetVarBlock(); if(pVarBlock) { IVariable *pVar = pVarBlock->FindVariable("CastLightmap"); if(pVar && (pVar->GetType() == IVariable::BOOL)) { bool bCastLightmap = false; pVar->Get(bCastLightmap); if(bCastLightmap) m_uiFlags |= CAST_LIGHTMAP; } } //update hash according to flags UpdateHash(((m_uiFlags & CAST_LIGHTMAP)?0x44444444:0) | (m_bReceiveLightmap?0x88888888:0)); // Get indices and vertices int iIdxCnt = 0; unsigned short *pIndices = pLB->GetIndices(&iIdxCnt); int iVtxStride = 0; Vec3d *pVertices = reinterpret_cast (pLB->GetPosPtr(iVtxStride)); // Get NBT int iNormalStride = 0; Vec3d *pNormals = reinterpret_cast (pLB->GetNormalPtr(iNormalStride)); int iTangentStride = 0; Vec3d *pTangents = reinterpret_cast (pLB->GetTangentPtr(iTangentStride)); int iBinormalStride = 0; Vec3d *pBinormals = reinterpret_cast (pLB->GetBinormalPtr(iBinormalStride)); int iTNormalStride = 0; Vec3d *pTNormals = reinterpret_cast (pLB->GetTNormalPtr(iTNormalStride)); int iUVStride = 0; SUV *pUV = reinterpret_cast (pLB->GetUVPtr(iUVStride)); m_uiTexCoordsRequired = pLB->m_SecVertCount; m_lstOldPolys.reserve(iIdxCnt / 3); UINT iCurMat; list2 *pMaterials = pLB->m_pMats; for (iCurMat=0; iCurMatCount(); iCurMat++) { CMatInfo cCurMat = (* pMaterials)[iCurMat]; bool bIsDecalMat = false; // Check if the material is opaque, we don't let non-opaque geometry cast shadows IShader *pEff = cCurMat.shaderItem.m_pShader; if (pEff) { IShader *pShTempl = pEff->GetTemplate(-1); if (pShTempl) { CString sShaderName = pShTempl->GetName(); if(sShaderName.Find("templdecal") != -1) //do not compute decals (i love exceptions) bIsDecalMat = true; if(pShTempl->GetFlags3() & EF3_NODRAW) { continue; } } } // Process all triangles UINT iCurTri = 0; for (iCurTri=0; iCurTrim_Plane.CalcPlane( matEtyMtx.TransformPointOLD (* reinterpret_cast (&(reinterpret_cast(pVertices)[pIndices[iBaseIdx + 2] * iVtxStride]))), matEtyMtx.TransformPointOLD (* reinterpret_cast (&(reinterpret_cast(pVertices)[pIndices[iBaseIdx + 1] * iVtxStride]))), matEtyMtx.TransformPointOLD (* reinterpret_cast (&(reinterpret_cast(pVertices)[pIndices[iBaseIdx + 0] * iVtxStride])))); pPoly->m_lstOriginals.reserve(3); //cache some information Vec3d vNormals[3]; // Add all 3 vertices for (int v=2; v>=0; v--) { CRadVertex pVert; if(!bIsDecalMat)//don't care about decals { pVert.m_vPos = matEtyMtx.TransformPointOLD ((* reinterpret_cast (&(reinterpret_cast(pVertices)[pIndices[iBaseIdx + v] * iVtxStride])))); vNormals[v] = pVert.m_vNormal = GetTransposed44(matEtyMtx) * ((* reinterpret_cast (&(reinterpret_cast(pNormals)[pIndices[iBaseIdx + v] * iNormalStride])))); pVert.m_vBinormal = GetTransposed44(matEtyMtx) * ((* reinterpret_cast (&(reinterpret_cast(pBinormals)[pIndices[iBaseIdx + v] * iBinormalStride])))); pVert.m_vTangent = GetTransposed44(matEtyMtx) * ((* reinterpret_cast (&(reinterpret_cast(pTangents)[pIndices[iBaseIdx + v] * iTangentStride])))); // Modified for smooth shading. We take the vertex normal as it is the // most important component, this guarantees smoothness across texture coordinate // discontinuities when the normals are smooth. Note that we don't orthonormalize the basis // (looks good enough without) pVert.m_vTNormal = GetTransposed44(matEtyMtx) * ((* reinterpret_cast (&(reinterpret_cast(pTNormals)[pIndices[iBaseIdx + v] * iTNormalStride])))); //now check all 4 normals bool bNotNormalized = false; float cfSquareLen = pVert.m_vNormal.GetLengthSquared(); if(!IsNormalized(cfSquareLen)) { const float fVar = fabs(cfSquareLen - 1.f); m_fMaxVariance = (fVar > m_fMaxVariance)?fVar:m_fMaxVariance; bNotNormalized = true; pVert.m_vNormal.Normalize(); } cfSquareLen = pVert.m_vBinormal.GetLengthSquared(); if(!IsNormalized(cfSquareLen)) { const float fVar = fabs(cfSquareLen - 1.f); m_fMaxVariance = (fVar > m_fMaxVariance)?fVar:m_fMaxVariance; bNotNormalized = true; pVert.m_vBinormal.Normalize(); } cfSquareLen = pVert.m_vTangent.GetLengthSquared(); if(!IsNormalized(cfSquareLen)) { const float fVar = fabs(cfSquareLen - 1.f); m_fMaxVariance = (fVar > m_fMaxVariance)?fVar:m_fMaxVariance; bNotNormalized = true; pVert.m_vTangent.Normalize(); } cfSquareLen = pVert.m_vTNormal.GetLengthSquared(); if(!IsNormalized(cfSquareLen)) { const float fVar = fabs(cfSquareLen - 1.f); m_fMaxVariance = (fVar > m_fMaxVariance)?fVar:m_fMaxVariance; bNotNormalized = true; pVert.m_vTNormal.Normalize(); } if(bNotNormalized) m_uiFlags |= NOT_NORMALIZED_NORMALS_FLAG;//mark for later information } // Add to the initial vertices of the polygon pPoly->m_lstOriginals.push_back(pVert); } // v if(bIsDecalMat) { pPoly->m_dwFlags |= (DECAL_FLAG | NOLIGHTMAP_FLAG); } else { //now check for wrong normals if(fabs(vNormals[0] * vNormals[1] + 1.0f) < 0.01f || fabs(vNormals[2] * vNormals[0] + 1.0f) < 0.01f || fabs(vNormals[1] * vNormals[2] + 1.0f) < 0.01f) { SUV uv[3]; //fetch uv's needed by tangent space calc uv[0] = *(reinterpret_cast (&(reinterpret_cast(pUV)[pIndices[iBaseIdx + 0] * iUVStride]))); uv[1] = *(reinterpret_cast (&(reinterpret_cast(pUV)[pIndices[iBaseIdx + 1] * iUVStride]))); uv[2] = *(reinterpret_cast (&(reinterpret_cast(pUV)[pIndices[iBaseIdx + 2] * iUVStride]))); m_uiFlags |= WRONG_NORMALS_FLAG;//mark for later information pPoly->CalculateTangentSpace(uv); } if(pPoly->m_Plane.n.GetLengthSquared() < 0.5f)//plane could not been computed due to identity of points { pPoly->m_Plane.n = (pPoly->m_lstOriginals[0]).m_vNormal;//use one vertex normal instead } } // Add to the original polygons of the mesh m_lstOldPolys.push_back(pPoly); } } return true; } inline DWORD CRadMesh::CalcLocalLightHashValue( void ) const { DWORD dwRet=0; for (std::vector::const_iterator ligIt=m_LocalLights.begin(); ligIt!=m_LocalLights.end(); ligIt++) { LMCompLight *pLight = (*ligIt); CalcNCombineHash(pLight->GetLightHashValue(),dwRet); } return(dwRet); } // ////////////////////////////////////////////////////////////////////// const float DistToLine(const Vec3d& rX0, const Vec3d& rX1, const Vec3d& rPoint) { const float cfDot = (rX0 - rPoint) | (rX1 - rX0); return fabs((rX0 - rPoint).GetLengthSquared()*(rX1 - rX0).GetLengthSquared() - cfDot * cfDot) / (rX1 - rX0).GetLengthSquared(); } // ////////////////////////////////////////////////////////////////////// const Vec3d ProjectOntoEdge(const Vec3d& rV0, const Vec3d& rV1, const Vec3d& rPoint, const float cfDistLV) { Vec3d vPointOnEdge; Vec3d vLine = (rV0 - rV1); const float cfLineLength = vLine.GetLengthSquared(); //we need this later on if(cfLineLength > cfNormalizeThreshold) vLine *= (1.0f/sqrtf(cfLineLength)); //vectors and square length to points on line Vec3d vToV0 = rPoint - rV0; Vec3d vToV1 = rPoint - rV1; float fDistToV0 = vToV0.GetLengthSquared(); float fDistToV1 = vToV1.GetLengthSquared(); //normalize direction if(fDistToV0 >= cfNormalizeThreshold) { vToV0.Normalize(); } //if close enough to one line endpoint, just return this vertex if(fDistToV0 < cfNormalizeThreshold || fDistToV1 < cfNormalizeThreshold) { // the two vectors are on the same line, snap to nearest vertex vPointOnEdge = (fDistToV0 > fDistToV1)?rV1:rV0; return vPointOnEdge; } else { Vec3d vUp = vLine % vToV0; vUp.Normalize(); vUp = vUp % vLine; //now we got the up vector of the plane vPointOnEdge = rPoint + (vUp * -(sqrtf(cfDistLV))); } //now check whether we are outside the line or not fDistToV0 = (vPointOnEdge - rV0).GetLengthSquared(); fDistToV1 = (vPointOnEdge - rV1).GetLengthSquared(); if((fDistToV0 + fDistToV1) > (cfLineLength - 0.01f)) { //we are still outside the edge, snap to the nearest vertex vPointOnEdge = (fDistToV0 > fDistToV1)?rV1:rV0; } return vPointOnEdge; } // ////////////////////////////////////////////////////////////////////// const bool CRadPoly::SnapVertex(Vec3d &inPosition, float &outfAlpha, float &outfBeta, const float cfTriangleArea) { assert(m_lstOriginals.size() == 3); //retrieve references to all triangle vertices const Vec3d& rV0 = m_lstOriginals[0].m_vPos; const Vec3d& rV1 = m_lstOriginals[1].m_vPos; const Vec3d& rV2 = m_lstOriginals[2].m_vPos; //retrieve square distance to all edges const float cfDistLV01 = max(DistToLine(rV0, rV1, inPosition), cfNormalizeThreshold); const float cfDistLV02 = max(DistToLine(rV0, rV2, inPosition), cfNormalizeThreshold); const float cfDistLV12 = max(DistToLine(rV1, rV2, inPosition), cfNormalizeThreshold); //now retrieve index of nearest edge const int ciNearestEdgeIndex = (cfDistLV01 < cfDistLV02)?(cfDistLV01 < cfDistLV12)?0:2:(cfDistLV12 < cfDistLV02)?2:1; //now switch these edges and project vertex accordingly //determine whether it has to be projected onto the line between the two vertices on the edge, or to be snapped to the nearest vertex Vec3d vPointOnEdge; switch(ciNearestEdgeIndex) { //project first onto line and then determine where we are to be inside the edge case 0: { vPointOnEdge = ProjectOntoEdge(rV0, rV1, inPosition, cfDistLV01); } break; case 1: { vPointOnEdge = ProjectOntoEdge(rV0, rV2, inPosition, cfDistLV02); } break; case 2: { vPointOnEdge = ProjectOntoEdge(rV1, rV2, inPosition, cfDistLV12); } break; } //first sub tri const float a1=CalcTriangleArea(vPointOnEdge,rV1,rV2); //second sub tri const float a2=CalcTriangleArea(rV0,vPointOnEdge,rV2); //third sub tri const float a3=CalcTriangleArea(rV0,rV1,vPointOnEdge); //check sum, must be close to real area const float asum = a1+a2+a3; bool cbBaryFailCond = false; if(fabs(asum - cfTriangleArea) > cfTriangleArea*0.01f) cbBaryFailCond = true; //area difference to large if(cbBaryFailCond == false) { inPosition = vPointOnEdge; //calc alpha beta and gamma components as area ratio outfAlpha = a1 / cfTriangleArea; outfBeta = a2 / cfTriangleArea; cbBaryFailCond = ((outfAlpha + outfBeta) > scfBaryThreshold)?true:false; //set to fail if barycentric coords are invalid } return (!cbBaryFailCond); } // ////////////////////////////////////////////////////////////////////// bool CRadPoly::ApplyBarycentricCoordinates( Vec3d &inPosition, CRadVertex &outVertex, SComplexOSDot3Texel& rDot3Texel, const bool cbImmedReturn, const bool cbSnap) { assert(m_lstOriginals.size() == 3); float fArea3d, outfAlpha, outfBeta; //variables filled by the function below const bool cbBaryFailCond = CheckPointInTriangle(inPosition, m_lstOriginals[0].m_vPos, m_lstOriginals[1].m_vPos, m_lstOriginals[2].m_vPos, outfAlpha, outfBeta, fArea3d); rDot3Texel.pSourcePatch = this; //tangent space will come from here //if barycentric coordinates are invalid and the fix is requested, snap vertex onto one triangle edge and retrieve new barycentric coordinates if(cbBaryFailCond) { rDot3Texel.bNotHit = true; //indicate that this texel was not hit properly (to signal subsampling requirement) if(cbImmedReturn) return false; //was used for quick check to cache polygon //snap vertex if no sharing polygons are there if(m_SharingPolygons.size() == 0) { if(cbSnap) { if(!SnapVertex(inPosition,outfAlpha,outfBeta,fArea3d)) return false; } else return false; } else { //try to map into the sharing polygons //compute nearest vertex, rotate into other plane by rotating around cross product between both plane up vectors with angle (sin of cross product) if(!SmoothVertex(outVertex,inPosition, fArea3d, rDot3Texel)) { if(cbSnap) { if(!SnapVertex(inPosition,outfAlpha,outfBeta,fArea3d)) return false; } else return false; } else return true; } } //now apply the barycentric coordinates ApplyBaryCoordsToVertex(outVertex, m_lstOriginals[0], m_lstOriginals[1], m_lstOriginals[2], outfAlpha, outfBeta); rDot3Texel.fAlpha = outfAlpha; rDot3Texel.fBeta = outfBeta; return(true); } const bool CRadPoly::SmoothVertex(CRadVertex &outVertex, const Vec3d &inPosition, const float cfArea3d, SComplexOSDot3Texel& rDot3Texel) { assert(m_lstOriginals.size() == 3); //retrieve references to all triangle vertices const Vec3d& rV0 = m_lstOriginals[0].m_vPos; const Vec3d& rV1 = m_lstOriginals[1].m_vPos; const Vec3d& rV2 = m_lstOriginals[2].m_vPos; //fetch square distances to const float cfDistV01 = fabs((rV0 - inPosition).GetLengthSquared()); const float cfDistV02 = fabs((rV1 - inPosition).GetLengthSquared()); const float cfDistV03 = fabs((rV2 - inPosition).GetLengthSquared()); //now retrieve index of nearest edge const int ciNearestVertexIndex = (cfDistV01 < cfDistV02)?(cfDistV01 < cfDistV03)?0:2:(cfDistV03 < cfDistV02)?2:1; const int ciFarestVertexIndex = (cfDistV01 > cfDistV02)?(cfDistV01 > cfDistV03)?0:2:(cfDistV03 > cfDistV02)?2:1; //get face normal == triangle plane up vector Vec3d oneEdge = rV0 - rV1; Vec3d secondEdge = rV0 - rV2; Vec3d vPlaneNormal = oneEdge % secondEdge; vPlaneNormal.Normalize(); //retrieve direction vectors to all vertices to use for shared vertex, array is used since we have stored a index Vec3d vDir[3]; vDir[0] = inPosition - rV0; vDir[1] = inPosition - rV1; vDir[2] = inPosition - rV2; //now iterate all triangles and rotate direction vector into plane of this triangle //add this to closest vertex and check barycentric coordinates for(SharedIter sharedIter = m_SharingPolygons.begin(); sharedIter != m_SharingPolygons.end(); ++sharedIter) { CRadPoly* pSharedPoly = (*sharedIter).first; //fetch vertex index which are shared and make sure that the nearest vertex index(ciNearestVertexIndex) is contained in that shared edge const unsigned int cuiIndex0 = ((*sharedIter).second) & 0x000000FF; const unsigned int cuiIndex1 = (((*sharedIter).second) & 0x0000FF00)>>8; const bool cbOnlyOneVertexShared = (cuiIndex1 == scuiOneVertexShareFlag); if(!cbOnlyOneVertexShared && (ciNearestVertexIndex != cuiIndex0 && ciNearestVertexIndex != cuiIndex1)) continue;//wrong shared edge if(cbOnlyOneVertexShared && (ciFarestVertexIndex == cuiIndex0)) continue;//wrong shared vertex //first retrieve triangle plane normal const Vec3d& rV02 = pSharedPoly->m_lstOriginals[0].m_vPos; const Vec3d& rV12 = pSharedPoly->m_lstOriginals[1].m_vPos; const Vec3d& rV22 = pSharedPoly->m_lstOriginals[2].m_vPos; //get face normal == triangle plane normal Vec3d oneEdge = rV02 - rV12; Vec3d secondEdge = rV02 - rV22; Vec3d vSharedPlaneNormal = oneEdge % secondEdge; vSharedPlaneNormal.Normalize(); //try to rotate vertex into plane of this shared triangle, if rotations fails, it will just get projected const Vec3d& rSource0 = m_lstOriginals[cuiIndex0].m_vPos; const Vec3d& rSource1 = (cbOnlyOneVertexShared)?m_lstOriginals[0].m_vPos:m_lstOriginals[cuiIndex1].m_vPos; Vec3d vNewDir; float cfDIst0 = 0.f, cfDIst1 = 0.f; if(!cbOnlyOneVertexShared) { cfDIst0 = (rSource0 - inPosition).GetLengthSquared(); cfDIst1 = (rSource1 - inPosition).GetLengthSquared(); vNewDir = (cfDIst0 < cfDIst1)? RotateIntoPlane(vPlaneNormal, vSharedPlaneNormal, inPosition, rSource0) : RotateIntoPlane(vPlaneNormal, vSharedPlaneNormal, inPosition, rSource1); } else vNewDir = RotateIntoPlane(vPlaneNormal, vSharedPlaneNormal, inPosition, rSource0); float bary0, bary1, fArea3d; //variables set up by the function below bool cbBaryFailCond = CheckPointInTriangle(vNewDir, rV02, rV12, rV22, bary0, bary1, fArea3d); if(cbBaryFailCond == true) { //iterate all triangles of this patch CRadPoly *pMergeSource = ((pSharedPoly->m_dwFlags & MERGE_SOURCE_FLAG) != 0)?pSharedPoly : pSharedPoly->m_pMergeSource; //try to rotate into shared triangles of this triangle for(std::vector::iterator sharedSharedIter = pMergeSource->m_lstMerged.begin(); sharedSharedIter != pMergeSource->m_lstMerged.end(); ++sharedSharedIter) { CRadPoly* pSharedSharedPoly = *sharedSharedIter; if(pSharedSharedPoly == pSharedPoly) continue;//don't try the same poly 2 times const Vec3d& rV02 = pSharedSharedPoly->m_lstOriginals[0].m_vPos; const Vec3d& rV12 = pSharedSharedPoly->m_lstOriginals[1].m_vPos; const Vec3d& rV22 = pSharedSharedPoly->m_lstOriginals[2].m_vPos; const bool cbSharedBaryFailCond = CheckPointInTriangle(vNewDir, rV02, rV12, rV22, bary0, bary1, fArea3d);//use the position already retrieved if(cbSharedBaryFailCond == false)//found the triangle which this texel lies in { pSharedPoly = pSharedSharedPoly;//assign to use this one cbBaryFailCond = false;//change bary condition to enter next code block break; } } } if(cbBaryFailCond == true) { //if still not found, try all other shared triangles of this one //try to rotate into shared triangles of this triangle for(SharedIter sharedSharedIter = pSharedPoly->m_SharingPolygons.begin(); sharedSharedIter != pSharedPoly->m_SharingPolygons.end(); ++sharedSharedIter) { CRadPoly* pSharedSharedPoly = (*sharedSharedIter).first; if(pSharedSharedPoly == this) continue;//same poly as above //first retrieve triangle plane normal const Vec3d& rV02 = pSharedSharedPoly->m_lstOriginals[0].m_vPos; const Vec3d& rV12 = pSharedSharedPoly->m_lstOriginals[1].m_vPos; const Vec3d& rV22 = pSharedSharedPoly->m_lstOriginals[2].m_vPos; //get face normal == triangle plane normal Vec3d oneEdge = rV02 - rV12; Vec3d secondEdge = rV02 - rV22; Vec3d vSharedSharedPlaneNormal = oneEdge % secondEdge; vSharedSharedPlaneNormal.Normalize(); //try to rotate vertex into plane of this shared triangle, if rotations fails, it will just get projected if(!cbOnlyOneVertexShared) { vNewDir = (cfDIst0 < cfDIst1)? RotateIntoPlane(vPlaneNormal, vSharedSharedPlaneNormal, inPosition, rSource0) : RotateIntoPlane(vPlaneNormal, vSharedSharedPlaneNormal, inPosition, rSource1); } else vNewDir = RotateIntoPlane(vPlaneNormal, vSharedSharedPlaneNormal, inPosition, rSource0); const bool cbSharedBaryFailCond = CheckPointInTriangle(vNewDir, rV02, rV12, rV22, bary0, bary1, fArea3d); if(cbSharedBaryFailCond == false)//found the triangle which this texel lies in { pSharedPoly = pSharedSharedPoly;//assign to use this one cbBaryFailCond = false;//change bary condition to enter next code block break; } } } if(cbBaryFailCond == false) { //we have found a triangle which the vertex lies really in //keep tangent space from the nearest triangle of this patch //snap vertex to get proper tangent space float fAlpha = 0.f, fBeta = 1.f; Vec3d vCopiedInPos = inPosition;//because it will get altered const bool bSnapped = SnapVertex(vCopiedInPos,fAlpha,fBeta,cfArea3d); if(bSnapped) { //apply different tangent space from this triangle to this vertex ApplyTangentSpaceToVertex(outVertex, fAlpha, fBeta); rDot3Texel.fAlpha = fAlpha; rDot3Texel.fBeta = fBeta; } ApplyBaryCoordsToVertex(outVertex, pSharedPoly->m_lstOriginals[0], pSharedPoly->m_lstOriginals[1], pSharedPoly->m_lstOriginals[2], bary0, bary1, !bSnapped); //apply new position outVertex.m_vPos = vNewDir; if(!bSnapped) { //shouldnt come here at all rDot3Texel.pSourcePatch = pSharedPoly; rDot3Texel.fAlpha = bary0; rDot3Texel.fBeta = bary1; } rDot3Texel.bNotHit = true; //indicate this is coming from another patch return true; } } return false; }