Files
GTASource/game/renderer/TerrainEdgeInfo.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

996 lines
25 KiB
C++

/************************************************************************************************************************/
/* Terrain tessellation open edge info builder. */
/************************************************************************************************************************/
#include <string.h>
#include "TerrainEdgeInfo.h"
#include "grmodel/geometry.h"
#if TEMP_RUNTIME_CALCULATE_TERRAIN_OPEN_EDGE_INFO
/****************************************************************************************************/
/* TerrainOpenEdgeInfoBuilder. */
/****************************************************************************************************/
TerrainOpenEdgeInfoBuilder::TerrainOpenEdgeInfoBuilder(TriangleListInterface &triInterface, VertexInterface &vertInterface)
{
int tri;
// Allocate memory for vertices and all potential edges.
m_pVertices = rage_new Vertex[vertInterface.GetNoOfVertices()];
m_NextFreeEdge = 0;
m_pEdges = rage_new Edge[triInterface.GetNoOfTriangles()*3];
m_NextFreeVertexToEdgeLink = 0;
m_pVertexToEdgeLinks = rage_new VertexToEdgeLink[triInterface.GetNoOfTriangles()*3*2];
// Visit each triangle.
for(tri=0; tri<triInterface.GetNoOfTriangles(); tri++)
{
int triEdge;
int indices[4];
// Collect the tri-th triangle.
triInterface.GetTriangle(tri, indices[0], indices[1], indices[2]);
// Make the info "wrap" round.
indices[3] = indices[0];
// Visit each edge of the triangle.
for(triEdge=0; triEdge<3; triEdge++)
{
int v0 = indices[triEdge];
int v1 = indices[triEdge + 1];
int connectingEdge = m_pVertices[v0].IsConnectedToVertexByEdge(v1);
if(connectingEdge == -1)
{
// Allocate a new edge.
Edge *pNewEdge = &m_pEdges[m_NextFreeEdge];
pNewEdge->m_Index = m_NextFreeEdge;
pNewEdge->m_VertexIndices[0] = v0;
pNewEdge->m_VertexIndices[1] = v1;
pNewEdge->m_Count = 1;
m_NextFreeEdge++;
// Add links from each vertex to this new edge.
VertexToEdgeLink *pLink1 = &m_pVertexToEdgeLinks[m_NextFreeVertexToEdgeLink++];
pLink1->m_pEdge = pNewEdge;
m_pVertices[v0].m_EdgeList.push_back(pLink1);
VertexToEdgeLink *pLink2 = &m_pVertexToEdgeLinks[m_NextFreeVertexToEdgeLink++];
pLink2->m_pEdge = pNewEdge;
m_pVertices[v1].m_EdgeList.push_back(pLink2);
}
else
{
// Increment the count.
m_pEdges[connectingEdge].m_Count++;
}
}
}
//--------------------------------------------------------------------------------------------------//
int vrt;
vertInterface.Begin();
// Visit each vertex.
for(vrt=0; vrt<vertInterface.GetNoOfVertices(); vrt++)
{
bool isConnectedToOpenEdge = false;
// Visit each edge hanging off the vertex.
EdgeList::iterator it = m_pVertices[vrt].m_EdgeList.begin();
EdgeList::iterator end = m_pVertices[vrt].m_EdgeList.end();
// Visit each edge used by the vertex.
while (it != end)
{
Edge *pEdge = (*it)->m_pEdge;
if(pEdge->m_Count == 0x1)
{
isConnectedToOpenEdge = true;
break;
}
it++;
}
if(isConnectedToOpenEdge)
{
vertInterface.MarkVertexAsUsingOpenEdge(vrt);
}
else
{
vertInterface.MarkVertexAsUsingOnlyInternalEdges(vrt);
}
// Dismantle the list of edges hanging from this vertex.
m_pVertices[vrt].m_EdgeList.clear();
}
vertInterface.End();
}
TerrainOpenEdgeInfoBuilder::~TerrainOpenEdgeInfoBuilder()
{
if(m_pVertexToEdgeLinks)
{
delete [] m_pVertexToEdgeLinks;
m_pVertexToEdgeLinks = NULL;
}
if(m_pEdges)
{
delete [] m_pEdges;
m_pEdges = NULL;
}
if(m_pVertices)
{
delete [] m_pVertices;
m_pVertices = NULL;
}
}
/****************************************************************************************************/
/* VertexWelder_NaiveONSquared. */
/****************************************************************************************************/
VertexWelder_NaiveONSquared::VertexWelder_NaiveONSquared(VertexInterface *pVertInterface) : VertexWelder(pVertInterface)
{
int i;
m_NextUniqueVertex = 0;
m_pIndexOfWeldVertices = rage_new int[m_pVertexInterface->GetNoOfVertices()];
m_pRealToWeldVertices = rage_new int[m_pVertexInterface->GetNoOfVertices()];
m_pVertexInterface->Begin();
for(i=0; i<m_pVertexInterface->GetNoOfVertices(); i++)
{
// Check against our list of weld vertices for a match.
int weldedIdx = FindIndexForVertex(i);
if(weldedIdx == -1)
{
// It`s the 1st time we`re encountered this vertex...Record it.
m_pIndexOfWeldVertices[m_NextUniqueVertex] = i;
weldedIdx = m_NextUniqueVertex++;
}
m_pRealToWeldVertices[i] = weldedIdx;
}
m_pVertexInterface->End();
}
VertexWelder_NaiveONSquared::~VertexWelder_NaiveONSquared()
{
if(m_pRealToWeldVertices)
{
delete [] m_pRealToWeldVertices;
m_pRealToWeldVertices = NULL;
}
if(m_pIndexOfWeldVertices)
{
delete [] m_pIndexOfWeldVertices;
m_pIndexOfWeldVertices = NULL;
}
}
int VertexWelder_NaiveONSquared::FindIndexForVertex(int vertIdx)
{
int i;
for(i=0; i<m_NextUniqueVertex; i++)
{
if(m_pVertexInterface->ShouldVertsBeConsideredTheSame(vertIdx, m_pIndexOfWeldVertices[i]))
{
return i;
}
}
return -1;
}
int VertexWelder_NaiveONSquared::NoOfWeldedVertices()
{
return m_NextUniqueVertex;
}
int VertexWelder_NaiveONSquared::RealIndexOfWeldedVert(int idx)
{
return m_pIndexOfWeldVertices[idx];
}
int VertexWelder_NaiveONSquared::RealIndexToWeldedIndex(int realIdx)
{
return m_pRealToWeldVertices[realIdx];
}
//--------------------------------------------------------------------------------------------------//
class VertexInterfaceForWelder : public VertexWelder::VertexInterface
{
public:
VertexInterfaceForWelder(grcVertexBuffer *pvertexBuffer)
{
m_pVertexBuffer = pvertexBuffer;
}
~VertexInterfaceForWelder()
{
}
int GetNoOfVertices()
{
return m_pVertexBuffer->GetVertexCount();
}
void Begin()
{
m_pVertexData = (char *)m_pVertexBuffer->LockRO();
}
bool ShouldVertsBeConsideredTheSame(int a, int b)
{
static float normalThreshold = cosf((1.0f/180.0f)*3.141592654f);
// Position.
Vector3 v3Diff = v3Get(a, 0) - v3Get(b, 0);
if(v3Diff.Dot(v3Diff) > (0.05f*0.05f))
{
return false;
}
// Normal.
Vector3 nA = v3Get(a, 3);
Vector3 nB = v3Get(b, 3);
if(IsInnerProductBetweenUnitVectorLessThan(nA, nB, normalThreshold))
{
return false;
}
return true;
}
void End()
{
m_pVertexBuffer->UnlockRO();
}
public:
Vector4 v4Get(int idx, int offsetInfloats)
{
float *pV = &((float *)(m_pVertexData + idx*m_pVertexBuffer->GetVertexStride()))[offsetInfloats];
return Vector3(pV[0], pV[1], pV[2], pV[3]);
}
Vector3 v3Get(int idx, int offsetInfloats)
{
float *pV = &((float *)(m_pVertexData + idx*m_pVertexBuffer->GetVertexStride()))[offsetInfloats];
return Vector3(pV[0], pV[1], pV[2]);
}
int iGet(int idx, int offsetInfloats)
{
int *pV = &((int *)(m_pVertexData + idx*m_pVertexBuffer->GetVertexStride()))[offsetInfloats];
return pV[0];
}
float fGet(int idx, int offsetInfloats)
{
float *pV = &((float *)(m_pVertexData + idx*m_pVertexBuffer->GetVertexStride()))[offsetInfloats];
return pV[0];
}
float GetMax(float *pV, int count)
{
float ret = pV[0];
for(int i=1; i<count; i++)
{
if(pV[i] > ret)
{
ret = pV[i];
}
}
return ret;
}
float IsInnerProductBetweenUnitVectorLessThan(Vector3 &A, Vector3 &B, float threshold)
{
float ADotB = A.Dot(B);
// Assume threshold is > 0.
if(ADotB < 0.0f)
{
return true;
}
float ModASqr = A.Dot(A);
float ModBSqr = B.Dot(B);
if(ADotB*ADotB < threshold*threshold*ModASqr*ModBSqr)
{
return true;
}
return false;
}
private:
char *m_pVertexData;
grcVertexBuffer *m_pVertexBuffer;
};
/****************************************************************************************************/
/* Build open edge info. */
/****************************************************************************************************/
void BuildTerrainOpenEdgeInfo(grmGeometry &geometry)
{
class grcVertexBuffer_EIBVertexInterface : public TerrainOpenEdgeInfoBuilder::VertexInterface
{
public:
grcVertexBuffer_EIBVertexInterface(grcVertexBuffer *pvertexBuffer) : TerrainOpenEdgeInfoBuilder::VertexInterface()
{
m_pVertexBuffer = pvertexBuffer;
}
~grcVertexBuffer_EIBVertexInterface()
{
}
public:
int GetNoOfVertices()
{
return m_pVertexBuffer->GetVertexCount();
}
void Begin()
{
m_pVertData = (char *)m_pVertexBuffer->LockRO();
}
void End()
{
m_pVertexBuffer->UnlockRO();
#if __D3D11
static_cast<grcVertexBufferD3D11*>(m_pVertexBuffer)->RecreateInternal();
#else
// Need to implement RecreateInternal() on other platforms.
CompileTimeAssert(0);
#endif
}
void MarkVertexAsUsingOpenEdge(int idx)
{
float *pV = (float *)(m_pVertData + idx*m_pVertexBuffer->GetVertexStride());
pV[2] -= TERRAIN_OPEN_EDGE_INFO_COMMON_FLOOR;
pV[2] *= -1.0f;
}
void MarkVertexAsUsingOnlyInternalEdges(int idx)
{
float *pV = (float *)(m_pVertData + idx*m_pVertexBuffer->GetVertexStride());
pV[2] -= TERRAIN_OPEN_EDGE_INFO_COMMON_FLOOR;
}
public:
char *m_pVertData;
grcVertexBuffer *m_pVertexBuffer;
};
//--------------------------------------------------------------------------------------------------//
class grcIndexBuffer_EIBTriangleListInterface : public TerrainOpenEdgeInfoBuilder::TriangleListInterface
{
public:
grcIndexBuffer_EIBTriangleListInterface(grcIndexBuffer *pindexBuffer) : TerrainOpenEdgeInfoBuilder::TriangleListInterface()
{
m_pIndexBuffer = pindexBuffer;
m_pTriIndices = (u16 *)m_pIndexBuffer->LockRO();
}
~grcIndexBuffer_EIBTriangleListInterface()
{
m_pIndexBuffer->UnlockRO();
}
public:
int GetNoOfTriangles()
{
// Assume triangle index buffer.
return m_pIndexBuffer->GetIndexCount()/3;
}
void GetTriangle(int idx, int &a, int &b, int &c)
{
u16 *pTri = &m_pTriIndices[idx*3];
a = (int)pTri[0];
b = (int)pTri[1];
c = (int)pTri[2];
}
public:
u16 *m_pTriIndices;
grcIndexBuffer *m_pIndexBuffer;
};
//--------------------------------------------------------------------------------------------------//
class EIBVertexInterface_ThroughWelder : public TerrainOpenEdgeInfoBuilder::VertexInterface
{
public:
EIBVertexInterface_ThroughWelder(grcVertexBuffer_EIBVertexInterface *pBaseInterface, VertexWelder *pWelder)
{
m_pBaseInterface = pBaseInterface;
m_pWelder = pWelder;
}
~EIBVertexInterface_ThroughWelder()
{
}
public:
int GetNoOfVertices()
{
return m_pWelder->NoOfWeldedVertices();
}
void Begin()
{
// Make space for the edge info, one per welded vertex.
m_pVertexOpenEdgeInfo = rage_new bool[GetNoOfVertices()];
}
void MarkVertexAsUsingOpenEdge(int idx)
{
m_pVertexOpenEdgeInfo[idx] = true;
}
void MarkVertexAsUsingOnlyInternalEdges(int idx)
{
m_pVertexOpenEdgeInfo[idx] = false;
}
void End()
{
int realVtx;
m_pBaseInterface->Begin();
// Visit each of the real vertices...
for(realVtx=0; realVtx<m_pBaseInterface->GetNoOfVertices(); realVtx++)
{
//...And read the edge info for the welded vertex it resolves to.
int idx = m_pWelder->RealIndexToWeldedIndex(realVtx);
if(m_pVertexOpenEdgeInfo[idx] == true)
{
m_pBaseInterface->MarkVertexAsUsingOpenEdge(realVtx);
}
else
{
m_pBaseInterface->MarkVertexAsUsingOnlyInternalEdges(realVtx);
}
}
m_pBaseInterface->End();
delete [] m_pVertexOpenEdgeInfo;
m_pVertexOpenEdgeInfo = NULL;
}
private:
VertexWelder *m_pWelder;
grcVertexBuffer_EIBVertexInterface *m_pBaseInterface;
bool *m_pVertexOpenEdgeInfo;
};
class EIBTriangleListInterface_ThroughWelder : public TerrainOpenEdgeInfoBuilder::TriangleListInterface
{
public:
EIBTriangleListInterface_ThroughWelder(grcIndexBuffer_EIBTriangleListInterface *pBaseInterface, VertexWelder *pWelder)
{
m_pBaseInterface = pBaseInterface;
m_pWelder = pWelder;
}
~EIBTriangleListInterface_ThroughWelder()
{
}
public:
int GetNoOfTriangles()
{
return m_pBaseInterface->GetNoOfTriangles();
}
void GetTriangle(int idx, int &a, int &b, int &c)
{
int realIndices[3];
int weldedIndices[3];
// Get real indices...
m_pBaseInterface->GetTriangle(idx, realIndices[0], realIndices[1], realIndices[2]);
//..Convert to welded ones.
weldedIndices[0] = m_pWelder->RealIndexToWeldedIndex(realIndices[0]);
weldedIndices[1] = m_pWelder->RealIndexToWeldedIndex(realIndices[1]);
weldedIndices[2] = m_pWelder->RealIndexToWeldedIndex(realIndices[2]);
a = weldedIndices[0];
b = weldedIndices[1];
c = weldedIndices[2];
}
private:
grcIndexBuffer_EIBTriangleListInterface *m_pBaseInterface;
VertexWelder *m_pWelder;
};
//--------------------------------------------------------------------------------------------------//
AssertMsg(geometry.GetIndexBuffer(), "BuildTerrainOpenEdgeInfo()...Expects indexed geometry.");
VertexInterfaceForWelder vertexInferfaceForWelder(geometry.GetVertexBuffer());
//DummyVertexWelder dummyVertexWelder(&vertexInferfaceForWelder);
VertexWelder_NaiveONSquared dummyVertexWelder(&vertexInferfaceForWelder);
Printf("BuildTerrainOpenEdgeInfo()...Before weld = %d, after = %d\n", vertexInferfaceForWelder.GetNoOfVertices(), dummyVertexWelder.NoOfWeldedVertices());
grcVertexBuffer_EIBVertexInterface baseVertInterface(geometry.GetVertexBuffer());
grcIndexBuffer_EIBTriangleListInterface baseTriInterface(geometry.GetIndexBuffer());
{
EIBTriangleListInterface_ThroughWelder triInterface(&baseTriInterface, &dummyVertexWelder);
{
EIBVertexInterface_ThroughWelder vertInterface(&baseVertInterface, &dummyVertexWelder);
{
TerrainOpenEdgeInfoBuilder builder(triInterface, vertInterface);
}
}
}
}
#endif //TEMP_RUNTIME_CALCULATE_TERRAIN_OPEN_EDGE_INFO
/****************************************************************************************************/
/* Applies welding to the passed geometry. */
/****************************************************************************************************/
#if 0
void Applywelding(grmGeometry &geometry, VertexWelder *pWelder)
{
#if __D3D11
int vrt;
grcVertexBufferD3D11 *pVB = static_cast <grcVertexBufferD3D11 *>(geometry.GetVertexBuffer());
grcIndexBufferD3D11 *pIB = static_cast <grcIndexBufferD3D11 *>(geometry.GetIndexBuffer());
char *pSourceVertData = (char *)pVB->LockRO();
char *pUniqueVertices = rage_new char[pVB->GetVertexStride()*pWelder->NoOfWeldedVertices()];
char *pDest = pUniqueVertices;
// Form a linear list of the welded vertices.
for(vrt=0; vrt<pWelder->NoOfWeldedVertices(); vrt++)
{
int idx = pWelder->RealIndexOfWeldedVert(vrt);
char *pSrc = &pSourceVertData[idx*pVB->GetVertexStride()];
memcpy((void *)pDest, (void *)pSrc, sizeof(char)*pVB->GetVertexStride());
pDest += pVB->GetVertexStride();
}
// Over-write the initial portion of the vertex data with the unique/welded ones.
memcpy((void *)pSourceVertData, (void *)pUniqueVertices, pWelder->NoOfWeldedVertices()*pVB->GetVertexStride());
pVB->UnlockRO();
pVB->RecreateInternal();
delete [] pUniqueVertices;
//--------------------------------------------------------------------------------------------------//
int idx;
u16 *pIndexData = (u16 *)pIB->LockRO();
// Replace each index with it`s welded version.
for(idx=0; idx<pIB->GetIndexCount(); idx++)
{
int newIdx = pWelder->RealIndexToWeldedIndex((int)pIndexData[idx]);
pIndexData[idx] = (u16)newIdx;
}
pIB->UnlockRO();
pIB->RecreateInternal();
#endif //__D3D11
}
/****************************************************************************************************/
/* General Fvf vertex welder. */
/****************************************************************************************************/
class FvfVertexInterfaceForWelder : public VertexWelder::VertexInterface
{
public:
enum CompareOp
{
LengthOp = 0,
InnerProductOp,
InnerProductPlusWOp,
MaxComponentOp,
StraightCompareOp
};
FvfVertexInterfaceForWelder(grcVertexBuffer *pvertexBuffer)
{
m_BadVertexTypeEncountered = false;
m_pVertexBuffer = pvertexBuffer;
m_pFvf = m_pVertexBuffer->GetFvf();
}
~FvfVertexInterfaceForWelder()
{
}
int GetNoOfVertices()
{
return m_pVertexBuffer->GetVertexCount();
}
void Begin()
{
m_pVertexData = (char *)m_pVertexBuffer->LockRO();
}
void GetSizeOfIntOrFloat(int a, int b, rage::grcFvf::grcFvfChannels channel, int count, float *pDestA, float *pDestB)
{
int i;
float *pVA = (float *)(m_pVertexData + a*m_pVertexBuffer->GetVertexStride() + m_pFvf->GetOffset(channel));
float *pVB = (float *)(m_pVertexData + b*m_pVertexBuffer->GetVertexStride() + m_pFvf->GetOffset(channel));
for(i=0; i<count; i++)
{
pDestA[i] = pVA[i];
pDestB[i] = pVB[i];
}
}
int GetChannelData(int a, int b, rage::grcFvf::grcFvfChannels channel, bool &isInteger, void *pDestA, void *pDestB)
{
int dataCount = 0;
isInteger = false;
switch(m_pFvf->GetDataSizeType(channel))
{
case rage::grcFvf::grcdsHalf:
case rage::grcFvf::grcdsHalf2:
case rage::grcFvf::grcdsHalf3:
case rage::grcFvf::grcdsHalf4:
{
return -1;
}
case rage::grcFvf::grcdsFloat4:
{
dataCount++;
}
case rage::grcFvf::grcdsFloat3:
{
dataCount++;
}
case rage::grcFvf::grcdsFloat2:
{
dataCount++;
}
case rage::grcFvf::grcdsFloat:
{
dataCount++;
GetSizeOfIntOrFloat(a, b, channel, dataCount, (float *)pDestA, (float *)pDestB);
break;
}
case rage::grcFvf::grcdsUBYTE4:
case rage::grcFvf::grcdsColor:
{
dataCount = 1;
isInteger = true;
GetSizeOfIntOrFloat(a, b, channel, 1, (float *)pDestA, (float *)pDestB);
break;
}
case rage::grcFvf::grcdsPackedNormal:
case rage::grcFvf::grcdsEDGE0:
case rage::grcFvf::grcdsEDGE1:
case rage::grcFvf::grcdsEDGE2:
case rage::grcFvf::grcdsShort2:
case rage::grcFvf::grcdsShort4:
case rage::grcFvf::grcdsCount:
{
return -1;
}
}
return dataCount;
}
bool AreChannelsTheSame(int a, int b, rage::grcFvf::grcFvfChannels channel, float epsilon, CompareOp compareOp)
{
if(m_pFvf->IsChannelActive(channel) == false)
{
return true;
}
float vFloatsA[4];
float vFloatsB[4];
bool isInteger = false;
int noOfComponents = GetChannelData(a, b, channel, isInteger, (void *)vFloatsA, (void *)vFloatsB);
if(noOfComponents == -1)
{
m_BadVertexTypeEncountered = true;
return false;
}
switch(compareOp)
{
case LengthOp:
{
int i;
float sum = 0.0f;
if(isInteger == true)
{
m_BadVertexTypeEncountered = true;
return false;
}
for(i=0; i<noOfComponents; i++)
{
float diff = vFloatsA[i] - vFloatsB[i];
sum += diff*diff;
}
if(sum < epsilon*epsilon)
{
return true;
}
return false;
}
case InnerProductOp:
{
if((isInteger == true) || (noOfComponents == 1) || (noOfComponents == 4))
{
m_BadVertexTypeEncountered = true;
return false;
}
if(noOfComponents == 2)
{
Vector2 A(vFloatsA[0], vFloatsA[1]);
Vector2 B(vFloatsB[0], vFloatsB[1]);
return !IsInnerProductBetweenUnitVectorLessThan(A, B, epsilon);
}
else
{
Vector3 A(vFloatsA[0], vFloatsA[1], vFloatsA[2]);
Vector3 B(vFloatsB[0], vFloatsB[1], vFloatsB[2]);
return !IsInnerProductBetweenUnitVectorLessThan(A, B, epsilon);
}
}
case InnerProductPlusWOp:
{
if((isInteger == true) || (noOfComponents != 4))
{
m_BadVertexTypeEncountered = true;
return false;
}
if(vFloatsA[3] != vFloatsB[3])
{
return false;
}
Vector3 A(vFloatsA[0], vFloatsA[1], vFloatsA[2]);
Vector3 B(vFloatsB[0], vFloatsB[1], vFloatsB[2]);
return !IsInnerProductBetweenUnitVectorLessThan(A, B, epsilon);
}
case MaxComponentOp:
{
int i;
float max = -FLT_MAX;
if(isInteger == true)
{
m_BadVertexTypeEncountered = true;
return false;
}
for(i=0; i<noOfComponents; i++)
{
float diff = fabs(vFloatsA[i] - vFloatsB[i]);
if(diff > max)
{
max = diff;
}
}
if(max > epsilon)
{
return false;
}
return true;
}
case StraightCompareOp:
{
if(noOfComponents != 1)
{
m_BadVertexTypeEncountered = true;
return false;
}
if(isInteger)
{
int *piA = (int *)vFloatsA;
int *piB = (int *)vFloatsB;
return (piA[0] == piB[0]);
}
else
{
return (vFloatsA[0] == vFloatsB[0]);
}
}
}
return true;
}
bool ShouldVertsBeConsideredTheSame(int a, int b)
{
static float lengthThreshold = 0.001f;
static float normalThreshold = cosf((1.0f/180.0f)*3.141592654f);
static float UVThreshold = 1.0f/2048.0f;
static float tangentThreshold = cosf((1.0f/180.0f)*3.141592654f);
// grcfcPosition.
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcPosition, lengthThreshold, LengthOp))
{
return false;
}
// grcfcWeight & grcfcBinding.
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcWeight, 0.0f, StraightCompareOp))
{
return false;
}
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcBinding, 0.0f, StraightCompareOp))
{
return false;
}
// grcfcNormal.
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcNormal, normalThreshold, InnerProductOp))
{
return false;
}
// grcfcDiffuse & grcfcSpecular.
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcDiffuse, 0.0f, StraightCompareOp))
{
return false;
}
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcSpecular, 0.0f, StraightCompareOp))
{
return false;
}
// grcfcTexture0 - grcfcTexture7
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcTexture0, UVThreshold, MaxComponentOp))
{
return false;
}
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcTexture1, UVThreshold, MaxComponentOp))
{
return false;
}
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcTexture2, UVThreshold, MaxComponentOp))
{
return false;
}
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcTexture3, UVThreshold, MaxComponentOp))
{
return false;
}
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcTexture4, UVThreshold, MaxComponentOp))
{
return false;
}
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcTexture5, UVThreshold, MaxComponentOp))
{
return false;
}
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcTexture6, UVThreshold, MaxComponentOp))
{
return false;
}
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcTexture7, UVThreshold, MaxComponentOp))
{
return false;
}
// grcfcTangent0 - grcfcTangent1
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcTangent0, normalThreshold, InnerProductPlusWOp))
{
return false;
}
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcTangent1, normalThreshold, InnerProductPlusWOp))
{
return false;
}
// grcfcBinormal0 - grcfcBinormal1
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcBinormal0, normalThreshold, InnerProductOp))
{
return false;
}
if(!AreChannelsTheSame(a, b, rage::grcFvf::grcfcBinormal0, normalThreshold, InnerProductOp))
{
return false;
}
return true;
}
void End()
{
m_pVertexBuffer->UnlockRO();
}
public:
float IsInnerProductBetweenUnitVectorLessThan(Vector3 &A, Vector3 &B, float threshold)
{
float ADotB = A.Dot(B);
// Assume threshold is > 0.
if(ADotB < 0.0f)
{
return true;
}
float ModASqr = A.Dot(A);
float ModBSqr = B.Dot(B);
if(ADotB*ADotB < threshold*threshold*ModASqr*ModBSqr)
{
return true;
}
return false;
}
float IsInnerProductBetweenUnitVectorLessThan(Vector2 &A, Vector2 &B, float threshold)
{
float ADotB = A.Dot(B);
// Assume threshold is > 0.
if(ADotB < 0.0f)
{
return true;
}
float ModASqr = A.Dot(A);
float ModBSqr = B.Dot(B);
if(ADotB*ADotB < threshold*threshold*ModASqr*ModBSqr)
{
return true;
}
return false;
}
bool IsGood()
{
return !m_BadVertexTypeEncountered;
}
private:
char *m_pVertexData;
grcVertexBuffer *m_pVertexBuffer;
const rage::grcFvf *m_pFvf;
bool m_BadVertexTypeEncountered;
};
void TestWeldLandscape(grmGeometry &geometry)
{
/*
{
VertexInterfaceForWelder vertexInferfaceForWelder(geometry.GetVertexBuffer());
VertexWelder_NaiveONSquared dummyVertexWelder(&vertexInferfaceForWelder);
Printf("BuildTerrainOpenEdgeInfo()...Before weld = %d, after = %d\n", vertexInferfaceForWelder.GetNoOfVertices(), dummyVertexWelder.NoOfWeldedVertices());
}
*/
{
FvfVertexInterfaceForWelder vertexInferfaceForWelder(geometry.GetVertexBuffer());
VertexWelder_NaiveONSquared dummyVertexWelder(&vertexInferfaceForWelder);
if(vertexInferfaceForWelder.IsGood())
{
Printf("TestWeldLandscape()...Before weld = %d, after = %d\n", vertexInferfaceForWelder.GetNoOfVertices(), dummyVertexWelder.NoOfWeldedVertices());
}
}
}
bool TestWeld(grmGeometry &geometry)
{
bool ret = false;
/*
{
VertexInterfaceForWelder vertexInferfaceForWelder(geometry.GetVertexBuffer());
VertexWelder_NaiveONSquared dummyVertexWelder(&vertexInferfaceForWelder);
Printf("BuildTerrainOpenEdgeInfo()...Before weld = %d, after = %d\n", vertexInferfaceForWelder.GetNoOfVertices(), dummyVertexWelder.NoOfWeldedVertices());
}
*/
//if(geometry.GetVertexBuffer()->GetFvf()->IsChannelActive(rage::grcFvf::grcfcTangent0) || geometry.GetVertexBuffer()->GetFvf()->IsChannelActive(rage::grcFvf::grcfcTangent1))
{
FvfVertexInterfaceForWelder vertexInferfaceForWelder(geometry.GetVertexBuffer());
VertexWelder_NaiveONSquared dummyVertexWelder(&vertexInferfaceForWelder);
if(vertexInferfaceForWelder.IsGood())
{
if((float)dummyVertexWelder.NoOfWeldedVertices()/(float)vertexInferfaceForWelder.GetNoOfVertices() < 0.9f)
{
Printf("TestWeld()...Before weld = %d, after = %d\n", vertexInferfaceForWelder.GetNoOfVertices(), dummyVertexWelder.NoOfWeldedVertices());
ret = true;
}
}
}
return ret;
}
#endif // 0