You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1896 lines
59 KiB
C++
1896 lines
59 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
#include "Cry3DEngine_precompiled.h"
|
|
|
|
#include "StatObj.h"
|
|
#include "ObjMan.h"
|
|
#include "MatMan.h"
|
|
#include "VisAreas.h"
|
|
#include "CullBuffer.h"
|
|
#include "3dEngine.h"
|
|
#include "IndexedMesh.h"
|
|
#include "RenderMeshMerger.h"
|
|
#include "MeshCompiler/MeshCompiler.h"
|
|
#include "../RenderDll/Common/Shaders/Vertex.h"
|
|
|
|
CRenderMeshMerger::CRenderMeshMerger()
|
|
{
|
|
m_nTotalVertexCount = 0;
|
|
m_nTotalIndexCount = 0;
|
|
}
|
|
|
|
CRenderMeshMerger::~CRenderMeshMerger()
|
|
{
|
|
}
|
|
|
|
int CRenderMeshMerger::Cmp_Materials(_smart_ptr<IMaterial> pMat1, _smart_ptr<IMaterial> pMat2)
|
|
{
|
|
if (!pMat1 || !pMat2)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
SShaderItem& shaderItem1 = pMat1->GetShaderItem();
|
|
SShaderItem& shaderItem2 = pMat2->GetShaderItem();
|
|
|
|
// vert format
|
|
AZ::Vertex::Format vertexFormat1 = shaderItem1.m_pShader->GetVertexFormat();
|
|
AZ::Vertex::Format vertexFormat2 = shaderItem2.m_pShader->GetVertexFormat();
|
|
|
|
if (vertexFormat1 > vertexFormat2)
|
|
{
|
|
return 1;
|
|
}
|
|
if (vertexFormat1 < vertexFormat2)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
bool bDecal1 = (shaderItem1.m_pShader->GetFlags() & EF_DECAL) != 0;
|
|
bool bDecal2 = (shaderItem2.m_pShader->GetFlags() & EF_DECAL) != 0;
|
|
|
|
// shader
|
|
if (bDecal1 > bDecal2)
|
|
{
|
|
return 1;
|
|
}
|
|
if (bDecal1 < bDecal2)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// shader resources
|
|
if (shaderItem1.m_pShaderResources > shaderItem2.m_pShaderResources)
|
|
{
|
|
return 1;
|
|
}
|
|
if (shaderItem1.m_pShaderResources < shaderItem2.m_pShaderResources)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// shader
|
|
if (shaderItem1.m_pShader > shaderItem2.m_pShader)
|
|
{
|
|
return 1;
|
|
}
|
|
if (shaderItem1.m_pShader < shaderItem2.m_pShader)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// compare mats ptr
|
|
if (pMat1 > pMat2)
|
|
{
|
|
return 1;
|
|
}
|
|
if (pMat1 < pMat2)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int CRenderMeshMerger::Cmp_RenderChunksInfo(const void* v1, const void* v2)
|
|
{
|
|
SRenderMeshInfoInput* in1 = (SRenderMeshInfoInput*)v1;
|
|
SRenderMeshInfoInput* in2 = (SRenderMeshInfoInput*)v2;
|
|
|
|
//assert(pChunk1->LMInfo.iRAETex == pChunk2->LMInfo.iRAETex);
|
|
|
|
_smart_ptr<IMaterial> pMat1 = in1->pMat;
|
|
_smart_ptr<IMaterial> pMat2 = in2->pMat;
|
|
|
|
return Cmp_Materials(pMat1, pMat2);
|
|
}
|
|
|
|
int CRenderMeshMerger::Cmp_RenderChunks_(const void* v1, const void* v2)
|
|
{
|
|
SMergedChunk* pChunk1 = (SMergedChunk*)v1;
|
|
SMergedChunk* pChunk2 = (SMergedChunk*)v2;
|
|
|
|
if (pChunk1->rChunk.nSubObjectIndex < pChunk2->rChunk.nSubObjectIndex)
|
|
{
|
|
return -1;
|
|
}
|
|
if (pChunk1->rChunk.nSubObjectIndex > pChunk2->rChunk.nSubObjectIndex)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
_smart_ptr<IMaterial> pMat1 = pChunk1->pMaterial;
|
|
_smart_ptr<IMaterial> pMat2 = pChunk2->pMaterial;
|
|
|
|
return Cmp_Materials(pMat1, pMat2);
|
|
}
|
|
|
|
void CRenderMeshMerger::IsChunkValid([[maybe_unused]] const CRenderChunk& Ch, [[maybe_unused]] PodArray<SVF_P3S_C4B_T2S>& lstVerts, [[maybe_unused]] PodArray<uint32>& lstIndices)
|
|
{
|
|
#ifdef _DEBUG
|
|
assert(Ch.nFirstIndexId + Ch.nNumIndices <= (uint32)lstIndices.Count());
|
|
assert(Ch.nFirstVertId + Ch.nNumVerts <= (uint16)lstVerts.Count());
|
|
|
|
for (uint32 i = Ch.nFirstIndexId; i < Ch.nFirstIndexId + Ch.nNumIndices; i++)
|
|
{
|
|
assert(lstIndices[i] >= Ch.nFirstVertId && lstIndices[i] < (uint32)(Ch.nFirstVertId + Ch.nNumVerts));
|
|
assert(lstIndices[i] >= 0 && lstIndices[i] < (uint32)lstVerts.Count());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CRenderMeshMerger::MakeRenderMeshInfoListOfAllChunks(SRenderMeshInfoInput* pRMIArray, int nRMICount, SMergeInfo& info)
|
|
{
|
|
for (int nRmi = 0; nRmi < nRMICount; nRmi++)
|
|
{
|
|
SRenderMeshInfoInput* pRMI = &pRMIArray[nRmi];
|
|
IRenderMesh* pRM = pRMI->pMesh;
|
|
|
|
const TRenderChunkArray& chunks = pRM->GetChunks();
|
|
int nChunkCount = chunks.size();
|
|
for (int nChunk = 0; nChunk < nChunkCount; nChunk++)
|
|
{
|
|
const CRenderChunk& renderChunk = chunks[nChunk];
|
|
|
|
if (renderChunk.m_nMatFlags & MTL_FLAG_NODRAW || !renderChunk.pRE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
_smart_ptr<IMaterial> pCustMat;
|
|
if (pRMI->pMat && renderChunk.m_nMatID < pRMI->pMat->GetSubMtlCount())
|
|
{
|
|
pCustMat = pRMI->pMat->GetSubMtl(renderChunk.m_nMatID);
|
|
}
|
|
else
|
|
{
|
|
pCustMat = pRMI->pMat;
|
|
}
|
|
|
|
if (!pCustMat)
|
|
{
|
|
continue;
|
|
}
|
|
if (!pCustMat->GetShaderItem().m_pShader)
|
|
{
|
|
pCustMat = GetMatMan()->GetDefaultMaterial();
|
|
}
|
|
|
|
IShader* pShader = pCustMat->GetShaderItem().m_pShader;
|
|
|
|
if (!pShader)
|
|
{
|
|
continue;
|
|
}
|
|
if (pShader->GetFlags() & EF_NODRAW)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (info.pDecalClipInfo)
|
|
{
|
|
if (pShader->GetFlags() & EF_DECAL)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
SRenderMeshInfoInput RMIChunk = *pRMI;
|
|
RMIChunk.pMat = info.pDecalClipInfo ? NULL : pCustMat;
|
|
RMIChunk.nChunkId = nChunk;
|
|
|
|
m_lstRMIChunks.Add(RMIChunk);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Note: This code used to be inline in CRenderMeshMerger::MakeListOfAllCRenderChunks, but it was for some reason seg faulting the Android GCC 4.9 compiler in Profile.
|
|
void CalculateNormalAndTangent(SPipTangents& basis, SPipNormal& normal, byte* pNorm, byte* pTangs, int nNormStride, int nTangsStride, bool bMatrixHasRotation, int v, SRenderMeshInfoInput* pRMI)
|
|
{
|
|
assert((pTangs) || (!pNorm));
|
|
if (pTangs)
|
|
{
|
|
basis = *(SPipTangents*)&pTangs[nTangsStride * v];
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
if (pNorm)
|
|
{
|
|
normal = *(SPipNormal*)&pNorm[nNormStride * v];
|
|
}
|
|
#endif
|
|
|
|
if (bMatrixHasRotation)
|
|
{
|
|
basis.TransformSafelyBy(pRMI->mat);
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
if (pNorm)
|
|
{
|
|
normal.TransformSafelyBy(pRMI->mat);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CRenderMeshMerger::MakeListOfAllCRenderChunks(SMergeInfo& info)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
m_nTotalVertexCount = 0;
|
|
m_nTotalIndexCount = 0;
|
|
|
|
for (int nEntityId = 0; nEntityId < m_lstRMIChunks.Count(); nEntityId++)
|
|
{
|
|
SRenderMeshInfoInput* pRMI = &m_lstRMIChunks[nEntityId];
|
|
|
|
bool bMatrixHasRotation;
|
|
if (!pRMI->mat.m01 && !pRMI->mat.m02 && !pRMI->mat.m10 && !pRMI->mat.m12 && !pRMI->mat.m20 && !pRMI->mat.m21)
|
|
{
|
|
bMatrixHasRotation = false;
|
|
}
|
|
else
|
|
{
|
|
bMatrixHasRotation = true;
|
|
}
|
|
|
|
Matrix34 matInv = pRMI->mat.GetInverted();
|
|
|
|
IRenderMesh* pRM = pRMI->pMesh;
|
|
if (!pRM->GetVerticesCount())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int nInitVertCout = m_lstVerts.Count();
|
|
|
|
|
|
int nPosStride = 0;
|
|
int nTexStride = 0;
|
|
int nColorStride = 0;
|
|
Vec3* pPos = 0;
|
|
Vec2* pTex = 0;
|
|
UCol* pColor = 0;
|
|
|
|
// get vertices's
|
|
{
|
|
FRAME_PROFILER("CRenderMeshMerger::MakeListOfAllCRenderChunks_GetPosPtr", GetSystem(), PROFILE_3DENGINE);
|
|
|
|
pPos = reinterpret_cast<Vec3*>(pRM->GetPosPtr(nPosStride, FSL_READ));
|
|
pTex = reinterpret_cast<Vec2*>(pRM->GetUVPtr(nTexStride, FSL_READ));
|
|
pColor = reinterpret_cast<UCol*>(pRM->GetColorPtr(nColorStride, FSL_READ));
|
|
}
|
|
|
|
if (!pPos || !pTex || !pColor)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// get tangent basis
|
|
int nNormStride = 0;
|
|
int nTangsStride = 0;
|
|
byte* pNorm = 0;
|
|
byte* pTangs = 0;
|
|
|
|
if (pRM->GetVertexFormat() != eVF_P3S_N4B_C4B_T2S)
|
|
{
|
|
pTangs = pRM->GetTangentPtr(nTangsStride, FSL_READ);
|
|
}
|
|
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
// get normal stream
|
|
{
|
|
pNorm = pRM->GetNormPtr(nNormStride, FSL_READ);
|
|
}
|
|
#endif
|
|
|
|
Vec3 vMin, vMax;
|
|
pRM->GetBBox(vMin, vMax);
|
|
|
|
// get indices
|
|
vtx_idx* pSrcInds = pRM->GetIndexPtr(FSL_READ);
|
|
|
|
Vec3 vOSPos(0, 0, 0);
|
|
Vec3 vOSProjDir(0, 0, 0);
|
|
float fMatrixScale = 1.f;
|
|
float fOSRadius = 0.f;
|
|
|
|
if (info.pDecalClipInfo && info.pDecalClipInfo->fRadius)
|
|
{
|
|
vOSPos = matInv.TransformPoint(info.pDecalClipInfo->vPos);
|
|
vOSProjDir = matInv.TransformVector(info.pDecalClipInfo->vProjDir);
|
|
fMatrixScale = vOSProjDir.GetLength();
|
|
fOSRadius = info.pDecalClipInfo->fRadius * fMatrixScale;
|
|
if (vOSProjDir.GetLength() > 0.01f)
|
|
{
|
|
vOSProjDir.Normalize();
|
|
}
|
|
}
|
|
|
|
CRenderChunk newMatInfo = pRM->GetChunks()[pRMI->nChunkId];
|
|
|
|
#ifdef _DEBUG
|
|
uint32 nIndCount = pRM->GetIndicesCount();
|
|
CRenderChunk Ch = newMatInfo;
|
|
for (uint32 i = Ch.nFirstIndexId; i < Ch.nFirstIndexId + Ch.nNumIndices; i++)
|
|
{
|
|
assert(i >= 0 && i < nIndCount);
|
|
assert(pSrcInds[i] >= Ch.nFirstVertId && pSrcInds[i] < Ch.nFirstVertId + Ch.nNumVerts);
|
|
assert((int)pSrcInds[i] < pRM->GetVerticesCount());
|
|
}
|
|
#endif
|
|
|
|
int nFirstIndexId = m_lstIndices.Count();
|
|
|
|
// add indices
|
|
for (uint32 i = newMatInfo.nFirstIndexId; i < newMatInfo.nFirstIndexId + newMatInfo.nNumIndices; i += 3)
|
|
{
|
|
assert((int)pSrcInds[i + 0] < pRM->GetVerticesCount());
|
|
assert((int)pSrcInds[i + 1] < pRM->GetVerticesCount());
|
|
assert((int)pSrcInds[i + 2] < pRM->GetVerticesCount());
|
|
|
|
assert((int)pSrcInds[i + 0] >= newMatInfo.nFirstVertId && pSrcInds[i + 0] < newMatInfo.nFirstVertId + newMatInfo.nNumVerts);
|
|
assert((int)pSrcInds[i + 1] >= newMatInfo.nFirstVertId && pSrcInds[i + 1] < newMatInfo.nFirstVertId + newMatInfo.nNumVerts);
|
|
assert((int)pSrcInds[i + 2] >= newMatInfo.nFirstVertId && pSrcInds[i + 2] < newMatInfo.nFirstVertId + newMatInfo.nNumVerts);
|
|
|
|
// skip not needed triangles for decals
|
|
if (fOSRadius)
|
|
{
|
|
// get verts
|
|
Vec3 v0 = pPos[nPosStride * pSrcInds[i + 0]];
|
|
Vec3 v1 = pPos[nPosStride * pSrcInds[i + 1]];
|
|
Vec3 v2 = pPos[nPosStride * pSrcInds[i + 2]];
|
|
|
|
if (vOSProjDir.IsZero())
|
|
{ // explosion mode
|
|
// test the face
|
|
float fDot0 = (vOSPos - v0).Dot((v1 - v0).Cross(v2 - v0));
|
|
float fTest = -0.15f;
|
|
if (fDot0 < fTest)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// get triangle normal
|
|
Vec3 vNormal = (v1 - v0).Cross(v2 - v0);
|
|
|
|
// test the face
|
|
if (vNormal.Dot(vOSProjDir) <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// get bbox
|
|
AABB triBox;
|
|
triBox.min = v0;
|
|
triBox.min.CheckMin(v1);
|
|
triBox.min.CheckMin(v2);
|
|
triBox.max = v0;
|
|
triBox.max.CheckMax(v1);
|
|
triBox.max.CheckMax(v2);
|
|
|
|
if (!Overlap::Sphere_AABB(Sphere(vOSPos, fOSRadius), triBox))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else if (info.pClipCellBox)
|
|
{
|
|
// get verts
|
|
Vec3 v0 = pRMI->mat.TransformPoint(pPos[nPosStride * pSrcInds[i + 0]]);
|
|
Vec3 v1 = pRMI->mat.TransformPoint(pPos[nPosStride * pSrcInds[i + 1]]);
|
|
Vec3 v2 = pRMI->mat.TransformPoint(pPos[nPosStride * pSrcInds[i + 2]]);
|
|
|
|
if (!Overlap::AABB_Triangle(*info.pClipCellBox, v0, v1, v2))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
m_lstIndices.Add((int)(pSrcInds[i + 0]) - newMatInfo.nFirstVertId + nInitVertCout);
|
|
m_lstIndices.Add((int)(pSrcInds[i + 1]) - newMatInfo.nFirstVertId + nInitVertCout);
|
|
m_lstIndices.Add((int)(pSrcInds[i + 2]) - newMatInfo.nFirstVertId + nInitVertCout);
|
|
}
|
|
|
|
newMatInfo.nFirstIndexId = nFirstIndexId;
|
|
newMatInfo.nNumIndices = m_lstIndices.Count() - nFirstIndexId;
|
|
|
|
if (!newMatInfo.nNumIndices)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// add vertices
|
|
for (int v = newMatInfo.nFirstVertId; v < (int)newMatInfo.nFirstVertId + (int)newMatInfo.nNumVerts; v++)
|
|
{
|
|
assert(v >= 0 && v < pRM->GetVerticesCount());
|
|
|
|
SVF_P3S_C4B_T2S vert;
|
|
|
|
// set pos
|
|
Vec3 vPos = pPos[nPosStride * v];
|
|
vPos = pRMI->mat.TransformPoint(vPos);
|
|
vert.xyz = vPos - info.vResultOffset;
|
|
|
|
// set uv
|
|
if (pTex)
|
|
{
|
|
vert.st = pTex[nTexStride * v];
|
|
}
|
|
else
|
|
{
|
|
vert.st = Vec2f16(0, 0);
|
|
}
|
|
|
|
vert.color = pColor[nColorStride * v + 0];
|
|
|
|
m_lstVerts.Add(vert);
|
|
|
|
// get tangent basis + normal
|
|
SPipTangents basis = SPipTangents(Vec4sf(0, 0, 0, 0), Vec4sf(0, 0, 0, 0));
|
|
SPipNormal normal = SPipNormal(Vec3(0, 0, 0));
|
|
|
|
CalculateNormalAndTangent(basis, normal, pNorm, pTangs, nNormStride, nTangsStride, bMatrixHasRotation, v, pRMI);
|
|
|
|
m_lstTangBasises.Add(basis);
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.Add(normal);
|
|
#endif
|
|
}
|
|
|
|
// set vert range
|
|
newMatInfo.nFirstVertId = m_lstVerts.Count() - newMatInfo.nNumVerts;
|
|
newMatInfo.pRE = NULL;
|
|
|
|
// assert(IsHeapValid());
|
|
// IsChunkValid(newMatInfo, m_lstVerts, m_lstIndices);
|
|
|
|
if (m_lstChunks.Count())
|
|
{
|
|
assert(m_lstChunks.Last().rChunk.nFirstVertId + m_lstChunks.Last().rChunk.nNumVerts == newMatInfo.nFirstVertId);
|
|
}
|
|
|
|
if (newMatInfo.nNumIndices)
|
|
{
|
|
SMergedChunk mrgChunk;
|
|
mrgChunk.rChunk = newMatInfo;
|
|
mrgChunk.rChunk.nSubObjectIndex = pRMI->nSubObjectIndex;
|
|
mrgChunk.pMaterial = info.pDecalClipInfo ? NULL : pRMI->pMat.get();
|
|
if (pRMI->pMat)
|
|
{
|
|
mrgChunk.rChunk.m_nMatFlags = pRMI->pMat->GetFlags();
|
|
}
|
|
m_lstChunks.Add(mrgChunk);
|
|
}
|
|
|
|
m_nTotalVertexCount += newMatInfo.nNumVerts;
|
|
m_nTotalIndexCount += newMatInfo.nNumIndices;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CRenderMeshMerger::CompactVertices(SMergeInfo& info)
|
|
{
|
|
if (info.bPrintDebugMessages)
|
|
{
|
|
PrintMessage("Removing unused vertices");
|
|
}
|
|
|
|
PodArray<uint32> lstVertUsage;
|
|
lstVertUsage.PreAllocate(m_lstVerts.Count(), m_lstVerts.Count());
|
|
for (int i = 0; i < m_lstIndices.Count(); i++)
|
|
{
|
|
lstVertUsage[m_lstIndices[i]] = 1;
|
|
}
|
|
|
|
PodArray<SVF_P3S_C4B_T2S> lstVertsOptimized;
|
|
lstVertsOptimized.PreAllocate(m_lstVerts.Count());
|
|
PodArray<SPipTangents> lstTangBasisesOptimized;
|
|
lstTangBasisesOptimized.PreAllocate(m_lstVerts.Count());
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
PodArray<SPipNormal> lstNormalsOptimized;
|
|
lstNormalsOptimized.PreAllocate(m_lstVerts.Count());
|
|
#endif
|
|
|
|
int nCurChunkId = 0;
|
|
CRenderChunk* pChBack = &m_lstChunks[0].rChunk;
|
|
int nVertsRemoved = 0;
|
|
PodArray<SMergedChunk> lstChunksBk;
|
|
lstChunksBk.AddList(m_lstChunks);
|
|
for (int i = 0; i < m_lstVerts.Count(); )
|
|
{
|
|
if (lstVertUsage[i])
|
|
{
|
|
lstVertsOptimized.Add(m_lstVerts[i]);
|
|
lstTangBasisesOptimized.Add(m_lstTangBasises[i]);
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
lstNormalsOptimized.Add(m_lstNormals[i]);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
nVertsRemoved++;
|
|
}
|
|
|
|
lstVertUsage[i] = lstVertsOptimized.Count() - 1;
|
|
|
|
i++;
|
|
|
|
if (i >= (int)lstChunksBk[nCurChunkId].rChunk.nFirstVertId + (int)lstChunksBk[nCurChunkId].rChunk.nNumVerts)
|
|
{
|
|
if (nVertsRemoved)
|
|
{
|
|
pChBack->nNumVerts -= nVertsRemoved;
|
|
|
|
for (int nId = nCurChunkId + 1; nId < m_lstChunks.Count(); nId++)
|
|
{
|
|
CRenderChunk& ChBack = m_lstChunks[nId].rChunk;
|
|
ChBack.nFirstVertId -= nVertsRemoved;
|
|
}
|
|
|
|
nVertsRemoved = 0;
|
|
}
|
|
|
|
nCurChunkId++;
|
|
if (nCurChunkId < m_lstChunks.Count())
|
|
{
|
|
pChBack = &m_lstChunks[nCurChunkId].rChunk;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < m_lstIndices.Count(); i++)
|
|
{
|
|
m_lstIndices[i] = lstVertUsage[m_lstIndices[i]];
|
|
}
|
|
|
|
int nOldVertsNum = m_lstVerts.Count();
|
|
|
|
m_lstVerts = lstVertsOptimized;
|
|
m_lstTangBasises = lstTangBasisesOptimized;
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals = lstNormalsOptimized;
|
|
#endif
|
|
|
|
if (info.bPrintDebugMessages)
|
|
{
|
|
PrintMessage("old->new = %d->%d vertices", nOldVertsNum, m_lstVerts.Count());
|
|
}
|
|
|
|
int nBadTrisCount = 0;
|
|
for (int i = 0; i < m_lstIndices.Count(); i += 3)
|
|
{
|
|
if (m_lstIndices[i + 0] == m_lstIndices[i + 1] ||
|
|
m_lstIndices[i + 1] == m_lstIndices[i + 2] ||
|
|
m_lstIndices[i + 2] == m_lstIndices[i + 0])
|
|
{
|
|
nBadTrisCount++;
|
|
}
|
|
}
|
|
|
|
if (nBadTrisCount)
|
|
{
|
|
PrintMessage("CRenderMeshMerger::CompactVertices: Warning: %d bad tris found", nBadTrisCount);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CRenderMeshMerger::ClipByAABB(SMergeInfo& info)
|
|
{
|
|
if (info.bPrintDebugMessages)
|
|
{
|
|
PrintMessage(" Do clipping . . ." /*, m_lstChunks.Count()*/);
|
|
}
|
|
|
|
{ // minimize range
|
|
uint32 nMin = ~0;
|
|
uint32 nMax = 0;
|
|
|
|
for (int i = 0; i < m_lstIndices.Count(); i++)
|
|
{
|
|
if (nMin > m_lstIndices[i])
|
|
{
|
|
nMin = m_lstIndices[i];
|
|
}
|
|
if (nMax < m_lstIndices[i])
|
|
{
|
|
nMax = m_lstIndices[i];
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < m_lstIndices.Count(); i++)
|
|
{
|
|
m_lstIndices[i] -= nMin;
|
|
}
|
|
|
|
if (m_lstVerts.Count() - nMax - 1 > 0)
|
|
{
|
|
m_lstVerts.Delete(nMax + 1, m_lstVerts.Count() - (nMax + 1));
|
|
m_lstTangBasises.Delete(nMax + 1, m_lstTangBasises.Count() - (nMax + 1));
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.Delete(nMax + 1, m_lstNormals.Count() - (nMax + 1));
|
|
#endif
|
|
}
|
|
|
|
m_lstVerts.Delete(0, nMin);
|
|
m_lstTangBasises.Delete(0, nMin);
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.Delete(0, nMin);
|
|
#endif
|
|
}
|
|
|
|
// define clip planes
|
|
Plane planes[6];
|
|
float fClipRadius = info.pDecalClipInfo->fRadius * 1.3f;
|
|
planes[0].SetPlane(Vec3(0, 0, 1), info.pDecalClipInfo->vPos + Vec3(0, 0, fClipRadius));
|
|
planes[1].SetPlane(Vec3(0, 0, -1), info.pDecalClipInfo->vPos + Vec3(0, 0, -fClipRadius));
|
|
planes[2].SetPlane(Vec3(0, 1, 0), info.pDecalClipInfo->vPos + Vec3(0, fClipRadius, 0));
|
|
planes[3].SetPlane(Vec3(0, -1, 0), info.pDecalClipInfo->vPos + Vec3(0, -fClipRadius, 0));
|
|
planes[4].SetPlane(Vec3(1, 0, 0), info.pDecalClipInfo->vPos + Vec3(fClipRadius, 0, 0));
|
|
planes[5].SetPlane(Vec3(-1, 0, 0), info.pDecalClipInfo->vPos + Vec3(-fClipRadius, 0, 0));
|
|
|
|
// clip triangles
|
|
int nOrigCount = m_lstIndices.Count();
|
|
for (int i = 0; i < nOrigCount; i += 3)
|
|
{
|
|
if (ClipTriangle(i, planes, 6))
|
|
{
|
|
i -= 3;
|
|
nOrigCount -= 3;
|
|
}
|
|
}
|
|
|
|
if (m_lstIndices.Count() < 3 || m_lstVerts.Count() < 3)
|
|
{
|
|
return;
|
|
}
|
|
|
|
assert(m_lstTangBasises.Count() == m_lstVerts.Count());
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
assert(m_lstNormals.Count() == m_lstVerts.Count());
|
|
#endif
|
|
|
|
{ // minimize range
|
|
uint32 nMin = ~0;
|
|
uint32 nMax = 0;
|
|
|
|
for (int i = 0; i < m_lstIndices.Count(); i++)
|
|
{
|
|
if (nMin > m_lstIndices[i])
|
|
{
|
|
nMin = m_lstIndices[i];
|
|
}
|
|
if (nMax < m_lstIndices[i])
|
|
{
|
|
nMax = m_lstIndices[i];
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < m_lstIndices.Count(); i++)
|
|
{
|
|
m_lstIndices[i] -= nMin;
|
|
}
|
|
|
|
if (m_lstVerts.Count() - nMax - 1 > 0)
|
|
{
|
|
m_lstVerts.Delete(nMax + 1, m_lstVerts.Count() - (nMax + 1));
|
|
m_lstTangBasises.Delete(nMax + 1, m_lstTangBasises.Count() - (nMax + 1));
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.Delete(nMax + 1, m_lstNormals.Count() - (nMax + 1));
|
|
#endif
|
|
}
|
|
|
|
m_lstVerts.Delete(0, nMin);
|
|
m_lstTangBasises.Delete(0, nMin);
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.Delete(0, nMin);
|
|
#endif
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
assert(m_lstTangBasises.Count() == m_lstVerts.Count());
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
assert(m_lstNormals.Count() == m_lstVerts.Count());
|
|
#endif
|
|
for (int i = 0; i < m_lstIndices.Count(); i++)
|
|
{
|
|
assert(m_lstIndices[i] >= 0 && m_lstIndices[i] < (uint32)m_lstVerts.Count());
|
|
}
|
|
#endif
|
|
|
|
if (m_lstIndices.Count() < 3 || m_lstVerts.Count() < 3)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_lstChunks[0].rChunk.nNumIndices = m_lstIndices.Count();
|
|
m_lstChunks[0].rChunk.nNumVerts = m_lstVerts.Count();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CRenderMeshMerger::ClipTriangle(int nStartIdxId, Plane* pPlanes, int nPlanesNum)
|
|
{
|
|
const PodArray<Vec3>& clipped = m_tmpClipContext.Clip(
|
|
m_lstVerts[m_lstIndices[nStartIdxId + 0]].xyz.ToVec3(),
|
|
m_lstVerts[m_lstIndices[nStartIdxId + 1]].xyz.ToVec3(),
|
|
m_lstVerts[m_lstIndices[nStartIdxId + 2]].xyz.ToVec3(),
|
|
pPlanes, nPlanesNum);
|
|
|
|
if (clipped.Count() < 3)
|
|
{
|
|
m_lstIndices.Delete(nStartIdxId, 3);
|
|
return true; // entire triangle is clipped away
|
|
}
|
|
|
|
if (clipped.Count() == 3)
|
|
{
|
|
if (clipped[0].IsEquivalent(m_lstVerts[m_lstIndices[nStartIdxId + 0]].xyz.ToVec3()))
|
|
{
|
|
if (clipped[1].IsEquivalent(m_lstVerts[m_lstIndices[nStartIdxId + 1]].xyz.ToVec3()))
|
|
{
|
|
if (clipped[2].IsEquivalent(m_lstVerts[m_lstIndices[nStartIdxId + 2]].xyz.ToVec3()))
|
|
{
|
|
return false; // entire triangle is in
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// replace old triangle with several new triangles
|
|
int nStartId = m_lstVerts.Count();
|
|
int nStartIndex = m_lstIndices[nStartIdxId + 0];
|
|
SVF_P3S_C4B_T2S fullVert = m_lstVerts[nStartIndex];
|
|
SPipTangents fullTang = m_lstTangBasises[nStartIndex];
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
SPipNormal fullNorm = m_lstNormals[nStartIndex];
|
|
#endif
|
|
|
|
for (int i = 0; i < clipped.Count(); i++)
|
|
{
|
|
fullVert.xyz = clipped[i];
|
|
m_lstVerts.Add(fullVert);
|
|
m_lstTangBasises.Add(fullTang);
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.Add(fullNorm);
|
|
#endif
|
|
}
|
|
|
|
// put first new triangle into position of original one
|
|
m_lstIndices[nStartIdxId + 0] = nStartId + 0;
|
|
m_lstIndices[nStartIdxId + 1] = nStartId + 1;
|
|
m_lstIndices[nStartIdxId + 2] = nStartId + 2;
|
|
|
|
// put others in the end
|
|
for (int i = 1; i < clipped.Count() - 2; i++)
|
|
{
|
|
m_lstIndices.Add(nStartId + 0);
|
|
m_lstIndices.Add(nStartId + i + 1);
|
|
m_lstIndices.Add(nStartId + i + 2);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CRenderMeshMerger::ClipDecals(SMergeInfo& info)
|
|
{
|
|
if (info.bPrintDebugMessages)
|
|
{
|
|
PrintMessage(" Do clipping . . ." /*, m_lstChunks.Count()*/);
|
|
}
|
|
|
|
{ // minimize range
|
|
uint32 nMin = ~0;
|
|
uint32 nMax = 0;
|
|
|
|
for (int i = 0; i < m_lstIndices.Count(); i++)
|
|
{
|
|
if (nMin > m_lstIndices[i])
|
|
{
|
|
nMin = m_lstIndices[i];
|
|
}
|
|
if (nMax < m_lstIndices[i])
|
|
{
|
|
nMax = m_lstIndices[i];
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < m_lstIndices.Count(); i++)
|
|
{
|
|
m_lstIndices[i] -= nMin;
|
|
}
|
|
|
|
if (m_lstVerts.Count() - nMax - 1 > 0)
|
|
{
|
|
m_lstVerts.Delete(nMax + 1, m_lstVerts.Count() - (nMax + 1));
|
|
m_lstTangBasises.Delete(nMax + 1, m_lstTangBasises.Count() - (nMax + 1));
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.Delete(nMax + 1, m_lstNormals.Count() - (nMax + 1));
|
|
#endif
|
|
}
|
|
|
|
m_lstVerts.Delete(0, nMin);
|
|
m_lstTangBasises.Delete(0, nMin);
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.Delete(0, nMin);
|
|
#endif
|
|
}
|
|
|
|
// define clip planes
|
|
Plane planes[6];
|
|
float fClipRadius = info.pDecalClipInfo->fRadius * 1.3f;
|
|
planes[0].SetPlane(Vec3(0, 0, 1), info.pDecalClipInfo->vPos - info.vResultOffset + Vec3(0, 0, fClipRadius));
|
|
planes[1].SetPlane(Vec3(0, 0, -1), info.pDecalClipInfo->vPos - info.vResultOffset + Vec3(0, 0, -fClipRadius));
|
|
planes[2].SetPlane(Vec3(0, 1, 0), info.pDecalClipInfo->vPos - info.vResultOffset + Vec3(0, fClipRadius, 0));
|
|
planes[3].SetPlane(Vec3(0, -1, 0), info.pDecalClipInfo->vPos - info.vResultOffset + Vec3(0, -fClipRadius, 0));
|
|
planes[4].SetPlane(Vec3(1, 0, 0), info.pDecalClipInfo->vPos - info.vResultOffset + Vec3(fClipRadius, 0, 0));
|
|
planes[5].SetPlane(Vec3(-1, 0, 0), info.pDecalClipInfo->vPos - info.vResultOffset + Vec3(-fClipRadius, 0, 0));
|
|
|
|
// clip triangles
|
|
int nOrigCount = m_lstIndices.Count();
|
|
for (int i = 0; i < nOrigCount; i += 3)
|
|
{
|
|
if (ClipTriangle(i, planes, 6))
|
|
{
|
|
i -= 3;
|
|
nOrigCount -= 3;
|
|
}
|
|
}
|
|
|
|
if (m_lstIndices.Count() < 3 || m_lstVerts.Count() < 3)
|
|
{
|
|
return;
|
|
}
|
|
|
|
assert(m_lstTangBasises.Count() == m_lstVerts.Count());
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
assert(m_lstNormals.Count() == m_lstVerts.Count());
|
|
#endif
|
|
|
|
{ // minimize range
|
|
uint32 nMin = ~0;
|
|
uint32 nMax = 0;
|
|
|
|
for (int i = 0; i < m_lstIndices.Count(); i++)
|
|
{
|
|
if (nMin > m_lstIndices[i])
|
|
{
|
|
nMin = m_lstIndices[i];
|
|
}
|
|
if (nMax < m_lstIndices[i])
|
|
{
|
|
nMax = m_lstIndices[i];
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < m_lstIndices.Count(); i++)
|
|
{
|
|
m_lstIndices[i] -= nMin;
|
|
}
|
|
|
|
if (m_lstVerts.Count() - nMax - 1 > 0)
|
|
{
|
|
m_lstVerts.Delete(nMax + 1, m_lstVerts.Count() - (nMax + 1));
|
|
m_lstTangBasises.Delete(nMax + 1, m_lstTangBasises.Count() - (nMax + 1));
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.Delete(nMax + 1, m_lstNormals.Count() - (nMax + 1));
|
|
#endif
|
|
}
|
|
|
|
m_lstVerts.Delete(0, nMin);
|
|
m_lstTangBasises.Delete(0, nMin);
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.Delete(0, nMin);
|
|
#endif
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
assert(m_lstTangBasises.Count() == m_lstVerts.Count());
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
assert(m_lstNormals.Count() == m_lstVerts.Count());
|
|
#endif
|
|
for (int i = 0; i < m_lstIndices.Count(); i++)
|
|
{
|
|
assert(m_lstIndices[i] >= 0 && m_lstIndices[i] < (uint32)m_lstVerts.Count());
|
|
}
|
|
#endif
|
|
|
|
if (m_lstIndices.Count() < 3 || m_lstVerts.Count() < 3)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_lstChunks[0].rChunk.nNumIndices = m_lstIndices.Count();
|
|
m_lstChunks[0].rChunk.nNumVerts = m_lstVerts.Count();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CRenderMeshMerger::TryMergingChunks(SMergeInfo& info)
|
|
{
|
|
PodArray<SMergedChunk>& lstChunksMerged = m_lstChunksMergedTemp;
|
|
lstChunksMerged.clear();
|
|
lstChunksMerged.reserve(m_lstChunks.size());
|
|
|
|
AZ::Vertex::Format currentVertexFormat;
|
|
for (int nChunkId = 0; nChunkId < m_lstChunks.Count(); nChunkId++)
|
|
{
|
|
SMergedChunk& mergChunk = m_lstChunks[nChunkId];
|
|
|
|
// IsChunkValid(mergChunk, m_lstVerts, m_lstIndices);
|
|
|
|
AZ::Vertex::Format chunkVertexFormat;
|
|
if (info.pDecalClipInfo)
|
|
{
|
|
chunkVertexFormat = AZ::Vertex::Format();
|
|
}
|
|
else if (info.bMergeToOneRenderMesh)
|
|
{
|
|
chunkVertexFormat = currentVertexFormat;
|
|
}
|
|
else
|
|
{
|
|
_smart_ptr<IMaterial> pMat = mergChunk.pMaterial;
|
|
chunkVertexFormat = mergChunk.rChunk.m_vertexFormat;
|
|
}
|
|
|
|
if (!nChunkId ||
|
|
(chunkVertexFormat != currentVertexFormat || Cmp_RenderChunks_(&mergChunk, &m_lstChunks[nChunkId - 1]) ||
|
|
(lstChunksMerged.Last().rChunk.nNumVerts + mergChunk.rChunk.nNumVerts) > 0xFFFF))
|
|
{ // not equal materials - add new chunk
|
|
lstChunksMerged.Add(mergChunk);
|
|
}
|
|
else
|
|
{
|
|
float texelAreaDensity = 0.0f;
|
|
int totalIndices = 0;
|
|
|
|
if (lstChunksMerged.Last().rChunk.m_texelAreaDensity != (float)UINT_MAX)
|
|
{
|
|
texelAreaDensity += lstChunksMerged.Last().rChunk.nNumIndices * lstChunksMerged.Last().rChunk.m_texelAreaDensity;
|
|
totalIndices += lstChunksMerged.Last().rChunk.nNumIndices;
|
|
}
|
|
|
|
if (mergChunk.rChunk.m_texelAreaDensity != (float)UINT_MAX)
|
|
{
|
|
texelAreaDensity += mergChunk.rChunk.nNumIndices * mergChunk.rChunk.m_texelAreaDensity;
|
|
totalIndices += mergChunk.rChunk.nNumIndices;
|
|
}
|
|
|
|
if (totalIndices != 0)
|
|
{
|
|
lstChunksMerged.Last().rChunk.m_texelAreaDensity = texelAreaDensity / totalIndices;
|
|
}
|
|
|
|
lstChunksMerged.Last().rChunk.nNumIndices += mergChunk.rChunk.nNumIndices;
|
|
lstChunksMerged.Last().rChunk.nNumVerts += mergChunk.rChunk.nNumVerts;
|
|
}
|
|
|
|
IsChunkValid(lstChunksMerged.Last().rChunk, m_lstVerts, m_lstIndices);
|
|
|
|
currentVertexFormat = chunkVertexFormat;
|
|
}
|
|
|
|
m_lstChunks = lstChunksMerged;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
_smart_ptr<IRenderMesh> CRenderMeshMerger::MergeRenderMeshes(SRenderMeshInfoInput* pRMIArray, int nRMICount,
|
|
PodArray<SRenderMeshInfoOutput>& outRenderMeshes,
|
|
SMergeInfo& info) PREFAST_SUPPRESS_WARNING(6262) //function uses > 32k stack space
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
if (info.bPrintDebugMessages)
|
|
{
|
|
PrintMessage("MergeRenderMeshs: name: %s, input brushes num: %d", info.sMeshName, nRMICount);
|
|
}
|
|
|
|
m_nTotalVertexCount = 0;
|
|
m_nTotalIndexCount = 0;
|
|
|
|
m_lstRMIChunks.clear();
|
|
m_lstVerts.clear();
|
|
m_lstTangBasises.clear();
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.clear();
|
|
#endif
|
|
m_lstIndices.clear();
|
|
m_lstChunks.clear();
|
|
|
|
// make list of all chunks
|
|
MakeRenderMeshInfoListOfAllChunks(pRMIArray, nRMICount, info);
|
|
|
|
if (info.bPrintDebugMessages)
|
|
{
|
|
PrintMessage("%d render chunks found", m_lstRMIChunks.Count());
|
|
}
|
|
|
|
if (!m_lstRMIChunks.Count())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// sort by materials
|
|
if (!info.pDecalClipInfo)
|
|
{
|
|
qsort(m_lstRMIChunks.GetElements(), m_lstRMIChunks.Count(), sizeof(m_lstRMIChunks[0]), Cmp_RenderChunksInfo);
|
|
}
|
|
|
|
// make list of all CRenderChunks
|
|
MakeListOfAllCRenderChunks(info);
|
|
|
|
// assert(IsHeapValid());
|
|
|
|
if (!m_lstVerts.Count() || !m_lstChunks.Count() || !m_lstIndices.Count())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (info.bPrintDebugMessages)
|
|
{
|
|
PrintMessage("%d chunks left after culling (%d verts, %d indices)", m_lstChunks.Count(), m_lstVerts.Count(), m_lstIndices.Count());
|
|
}
|
|
|
|
// Split chunks that does not fit together.
|
|
TryMergingChunks(info);
|
|
|
|
if (info.bPrintDebugMessages)
|
|
{
|
|
PrintMessage("%d chunks left after merging", m_lstChunks.Count());
|
|
}
|
|
|
|
if (!m_lstChunks.Count())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// now we have list of merged/sorted chunks, indices and vertices's
|
|
// overall amount of vertices's may be more than 0xFFFF
|
|
if (m_lstChunks.Count() == 1 && info.pDecalClipInfo && GetCVars()->e_DecalsClip)
|
|
{
|
|
// clip decals if needed
|
|
ClipDecals(info);
|
|
if (m_lstIndices.Count() < 3 || m_lstVerts.Count() < 3)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// find AABB
|
|
AABB aabb = AABB(m_lstVerts[0].xyz.ToVec3(), m_lstVerts[0].xyz.ToVec3());
|
|
for (int i = 0; i < m_lstVerts.Count(); i++)
|
|
{
|
|
aabb.Add(m_lstVerts[i].xyz.ToVec3());
|
|
}
|
|
|
|
// weld positions
|
|
mesh_compiler::CMeshCompiler meshCompiler;
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
PREFAST_SUPPRESS_WARNING(6385);
|
|
meshCompiler.WeldPos_VF_P3X(m_lstVerts, m_lstTangBasises, m_lstNormals, m_lstIndices, VEC_EPSILON, aabb);
|
|
#else
|
|
PodArray<Vec3> lstNormalsDummy;
|
|
meshCompiler.WeldPos_VF_P3X(m_lstVerts, m_lstTangBasises, lstNormalsDummy, m_lstIndices, VEC_EPSILON, aabb);
|
|
#endif
|
|
|
|
// update chunk
|
|
CRenderChunk& Ch0 = m_lstChunks[0].rChunk;
|
|
Ch0.nFirstIndexId = 0;
|
|
Ch0.nNumIndices = m_lstIndices.Count();
|
|
Ch0.nFirstVertId = 0;
|
|
Ch0.nNumVerts = m_lstVerts.Count();
|
|
}
|
|
|
|
if (/*GetCVars()->e_scene_merging_compact_vertices && */ info.bCompactVertBuffer)
|
|
{ // remove gaps in vertex buffers
|
|
CompactVertices(info);
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
for (int nChunkId = 0; nChunkId < m_lstChunks.Count(); nChunkId++)
|
|
{
|
|
CRenderChunk& Ch0 = m_lstChunks[nChunkId].rChunk;
|
|
IsChunkValid(Ch0, m_lstVerts, m_lstIndices);
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
if (info.bPrintDebugMessages)
|
|
{
|
|
PrintMessage("Making new RenderMeshes");
|
|
}
|
|
|
|
outRenderMeshes.clear();
|
|
|
|
m_lstNewChunks.reserve(m_lstChunks.Count());
|
|
m_lstNewVerts.reserve(m_nTotalVertexCount);
|
|
m_lstNewTangBasises.reserve(m_nTotalVertexCount);
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.reserve(m_nTotalVertexCount);
|
|
#endif
|
|
m_lstNewIndices.reserve(m_nTotalIndexCount);
|
|
|
|
for (int nChunkId = 0; nChunkId < m_lstChunks.Count(); )
|
|
{
|
|
AABB finalBox;
|
|
finalBox.Reset();
|
|
|
|
m_lstNewVerts.clear();
|
|
m_lstNewTangBasises.clear();
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.clear();
|
|
#endif
|
|
m_lstNewIndices.clear();
|
|
m_lstNewChunks.clear();
|
|
|
|
int nVertsNum = 0;
|
|
for (; nChunkId < m_lstChunks.Count(); )
|
|
{
|
|
CRenderChunk Ch = m_lstChunks[nChunkId].rChunk;
|
|
SMergedChunk& mrgChunk = m_lstChunks[nChunkId];
|
|
|
|
assert(m_lstNewVerts.Count() + Ch.nNumVerts <= 0xFFFF);
|
|
|
|
IsChunkValid(Ch, m_lstVerts, m_lstIndices);
|
|
|
|
int nCurrIdxPos = m_lstNewIndices.Count();
|
|
int nCurrVertPos = m_lstNewVerts.Count();
|
|
|
|
m_lstNewVerts.AddList(&m_lstVerts[Ch.nFirstVertId], Ch.nNumVerts);
|
|
m_lstNewTangBasises.AddList(&m_lstTangBasises[Ch.nFirstVertId], Ch.nNumVerts);
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.AddList(&m_lstNormals[Ch.nFirstVertId], Ch.nNumVerts);
|
|
#endif
|
|
|
|
for (uint32 i = Ch.nFirstIndexId; i < Ch.nFirstIndexId + Ch.nNumIndices; i++)
|
|
{
|
|
uint32 nIndex = m_lstIndices[i] - Ch.nFirstVertId + nCurrVertPos;
|
|
assert(nIndex >= 0 && nIndex <= 0xFFFF && nIndex < (uint32)m_lstNewVerts.Count());
|
|
nIndex = nIndex & 0xFFFF;
|
|
m_lstNewIndices.Add(nIndex);
|
|
finalBox.Add(m_lstNewVerts[nIndex].xyz.ToVec3());
|
|
}
|
|
|
|
Ch.nFirstIndexId = nCurrIdxPos;
|
|
Ch.nFirstVertId = nCurrVertPos;
|
|
|
|
nVertsNum += Ch.nNumVerts;
|
|
|
|
{
|
|
assert(Ch.nFirstIndexId + Ch.nNumIndices <= (uint32)m_lstNewIndices.Count());
|
|
assert(Ch.nFirstVertId + Ch.nNumVerts <= (uint16)m_lstNewVerts.Count());
|
|
|
|
#ifdef _DEBUG
|
|
for (uint32 i = Ch.nFirstIndexId; i < Ch.nFirstIndexId + Ch.nNumIndices; i++)
|
|
{
|
|
assert(m_lstNewIndices[i] >= Ch.nFirstVertId && m_lstNewIndices[i] < Ch.nFirstVertId + Ch.nNumVerts);
|
|
assert((int)m_lstNewIndices[i] < m_lstNewVerts.Count());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
SMergedChunk newMergedChunk;
|
|
newMergedChunk.rChunk = Ch;
|
|
newMergedChunk.pMaterial = mrgChunk.pMaterial;
|
|
m_lstNewChunks.Add(newMergedChunk);
|
|
|
|
nChunkId++;
|
|
|
|
if (nChunkId < m_lstChunks.Count())
|
|
{
|
|
if (nVertsNum + m_lstChunks[nChunkId].rChunk.nNumVerts > 0xFFFF)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (info.bMergeToOneRenderMesh)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// detect vert format change
|
|
SShaderItem& shaderItemCur = mrgChunk.pMaterial->GetShaderItem();
|
|
AZ::Vertex::Format nextChunkVertexFormatCurrent = shaderItemCur.m_pShader->GetVertexFormat();
|
|
nextChunkVertexFormatCurrent = mrgChunk.rChunk.m_vertexFormat;
|
|
|
|
SShaderItem& shaderItemNext = m_lstChunks[nChunkId].pMaterial->GetShaderItem();
|
|
AZ::Vertex::Format nextChunkVertFormatNext = shaderItemNext.m_pShader->GetVertexFormat();
|
|
nextChunkVertFormatNext = m_lstChunks[nChunkId].rChunk.m_vertexFormat;
|
|
|
|
if (nextChunkVertFormatNext != nextChunkVertexFormatCurrent)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
IRenderMesh::SInitParamerers params;
|
|
params.pVertBuffer = m_lstNewVerts.GetElements();
|
|
params.nVertexCount = m_lstNewVerts.Count();
|
|
params.vertexFormat = eVF_P3S_C4B_T2S;
|
|
params.pIndices = m_lstNewIndices.GetElements();
|
|
params.nIndexCount = m_lstNewIndices.Count();
|
|
params.pTangents = m_lstNewTangBasises.GetElements();
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
params.pNormals = m_lstNewNormals.GetElements();
|
|
#endif
|
|
params.nPrimetiveType = prtTriangleList;
|
|
params.eType = eRMT_Static;
|
|
params.nRenderChunkCount = 0;
|
|
params.bOnlyVideoBuffer = false;
|
|
params.bPrecache = false;
|
|
|
|
// make new RenderMesh
|
|
_smart_ptr<IRenderMesh> pNewLB = GetRenderer()->CreateRenderMesh(info.sMeshName, info.sMeshType, ¶ms);
|
|
|
|
_smart_ptr<IMaterial> pParentMaterial = NULL;
|
|
|
|
pNewLB->SetBBox(finalBox.min, finalBox.max);
|
|
|
|
if (!info.pUseMaterial)
|
|
{
|
|
// make new parent material
|
|
if (!info.pDecalClipInfo)
|
|
{
|
|
char szMatName[256] = "";
|
|
sprintf_s(szMatName, "%s_Material", info.sMeshName);
|
|
pParentMaterial = GetMatMan()->CreateMaterial(szMatName, MTL_FLAG_MULTI_SUBMTL);
|
|
pParentMaterial->AddRef();
|
|
pParentMaterial->SetSubMtlCount(m_lstChunks.Count());
|
|
}
|
|
|
|
// define chunks
|
|
for (int i = 0; i < m_lstNewChunks.Count(); i++)
|
|
{
|
|
CRenderChunk* pChunk = &m_lstNewChunks[i].rChunk;
|
|
|
|
_smart_ptr<IMaterial> pMat = m_lstNewChunks[i].pMaterial;
|
|
|
|
if (pParentMaterial)
|
|
{
|
|
assert(pMat);
|
|
pParentMaterial->SetSubMtl(i, pMat);
|
|
}
|
|
|
|
assert(pChunk->nFirstIndexId + pChunk->nNumIndices <= (uint32)m_lstNewIndices.Count());
|
|
|
|
pChunk->m_nMatID = i;
|
|
if (pMat)
|
|
{
|
|
pChunk->m_nMatFlags = pMat->GetFlags();
|
|
}
|
|
pNewLB->SetChunk(i, *pChunk);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pParentMaterial = info.pUseMaterial;
|
|
|
|
// define chunks
|
|
for (int i = 0; i < m_lstNewChunks.Count(); i++)
|
|
{
|
|
CRenderChunk* pChunk = &m_lstNewChunks[i].rChunk;
|
|
assert(pChunk->nFirstIndexId + pChunk->nNumIndices <= (uint32)m_lstNewIndices.Count());
|
|
|
|
_smart_ptr<IMaterial> pSubMtl = info.pUseMaterial->GetSafeSubMtl(pChunk->m_nMatID);
|
|
pChunk->m_nMatFlags = pSubMtl->GetFlags();
|
|
|
|
pNewLB->SetChunk(i, *pChunk);
|
|
}
|
|
}
|
|
|
|
SRenderMeshInfoOutput rmi;
|
|
rmi.pMesh = pNewLB;
|
|
rmi.pMat = pParentMaterial;
|
|
if (pParentMaterial)
|
|
{
|
|
pParentMaterial->AddRef();
|
|
}
|
|
|
|
outRenderMeshes.push_back(rmi);
|
|
}
|
|
|
|
if (info.bPrintDebugMessages)
|
|
{
|
|
PrintMessage("%" PRISIZE_T " RenderMeshes created", outRenderMeshes.size());
|
|
}
|
|
|
|
return outRenderMeshes.Count() ? outRenderMeshes[0].pMesh : _smart_ptr<IRenderMesh>(NULL);
|
|
}
|
|
|
|
struct sort_render_chunks_by_material
|
|
{
|
|
bool operator ()(const SMergedChunk& c1, const SMergedChunk& c2) const
|
|
{
|
|
return c1.pMaterial < c2.pMaterial;
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CRenderMeshMerger::GenerateRenderChunks(SRenderMeshInfoInput* pRMIArray, int nRMICount)
|
|
{
|
|
bool bCanMerge = true;
|
|
PodArray<SMergedChunk>& allChunks = m_lstChunksAll;
|
|
|
|
allChunks.clear();
|
|
allChunks.reserve(nRMICount);
|
|
|
|
for (int nRmi = 0; nRmi < nRMICount; nRmi++)
|
|
{
|
|
SRenderMeshInfoInput* pRMI = &pRMIArray[nRmi];
|
|
IRenderMesh* pRM = pRMI->pMesh;
|
|
|
|
// Ignore bad meshes.
|
|
if (pRM->GetVerticesCount() == 0 || pRM->GetIndicesCount() == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const TRenderChunkArray& chunks = pRM->GetChunks();
|
|
int nChunkCount = chunks.size();
|
|
for (int nChunk = 0; nChunk < nChunkCount; nChunk++)
|
|
{
|
|
const CRenderChunk& renderChunk = chunks[nChunk];
|
|
|
|
if (renderChunk.m_nMatFlags & MTL_FLAG_NODRAW || !renderChunk.pRE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (renderChunk.nNumVerts == 0 || renderChunk.nNumIndices == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!pRMI->pMat)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
_smart_ptr<IMaterial> pCustMat = (pRMI->pMat) ? pRMI->pMat->GetSafeSubMtl(renderChunk.m_nMatID) : 0;
|
|
if (!pCustMat)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!pCustMat->GetShaderItem().m_pShader)
|
|
{
|
|
pCustMat = GetMatMan()->GetDefaultMaterial();
|
|
}
|
|
|
|
IShader* pShader = pCustMat->GetShaderItem().m_pShader;
|
|
|
|
if (!pShader)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pShader->GetFlags() & EF_NODRAW)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
SMergedChunk newChunk;
|
|
newChunk.pFromMesh = pRMI;
|
|
newChunk.pMaterial = pCustMat;
|
|
newChunk.rChunk = renderChunk;
|
|
newChunk.rChunk.nSubObjectIndex = pRMI->nSubObjectIndex;
|
|
newChunk.rChunk.pRE = 0;
|
|
|
|
allChunks.push_back(newChunk);
|
|
}
|
|
}
|
|
|
|
// sort by materials
|
|
std::sort(allChunks.begin(), allChunks.end(), sort_render_chunks_by_material());
|
|
|
|
return bCanMerge;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CRenderMeshMerger::MergeRenderChunks()
|
|
{
|
|
PodArray<SMergedChunk>& allChunks = m_lstChunksAll;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Create array of merged chunks.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
PodArray<SMergedChunk>& mergedChunks = m_lstChunks;
|
|
mergedChunks.clear();
|
|
mergedChunks.reserve(allChunks.size());
|
|
|
|
if (allChunks.size() > 0)
|
|
{
|
|
// Add first chunk.
|
|
mergedChunks.push_back(allChunks[0]);
|
|
}
|
|
|
|
for (size_t nChunkId = 1; nChunkId < allChunks.size(); nChunkId++)
|
|
{
|
|
SMergedChunk& currChunk = allChunks[nChunkId];
|
|
|
|
SMergedChunk& prevChunk = mergedChunks.back();
|
|
|
|
if ((currChunk.pMaterial != prevChunk.pMaterial) ||
|
|
((prevChunk.rChunk.nNumVerts + currChunk.rChunk.nNumVerts) > 0xFFFF) ||
|
|
((prevChunk.rChunk.nNumIndices + currChunk.rChunk.nNumIndices) > 0xFFFF))
|
|
{ // not equal materials - add new chunk
|
|
mergedChunks.Add(currChunk);
|
|
}
|
|
else
|
|
{
|
|
float texelAreaDensity = 0.0f;
|
|
int totalIndices = 0;
|
|
|
|
if (prevChunk.rChunk.m_texelAreaDensity != (float)UINT_MAX)
|
|
{
|
|
texelAreaDensity += prevChunk.rChunk.nNumIndices * prevChunk.rChunk.m_texelAreaDensity;
|
|
totalIndices += prevChunk.rChunk.nNumIndices;
|
|
}
|
|
|
|
if (currChunk.rChunk.m_texelAreaDensity != (float)UINT_MAX)
|
|
{
|
|
texelAreaDensity += currChunk.rChunk.nNumIndices * currChunk.rChunk.m_texelAreaDensity;
|
|
totalIndices += currChunk.rChunk.nNumIndices;
|
|
}
|
|
|
|
if (totalIndices != 0)
|
|
{
|
|
prevChunk.rChunk.m_texelAreaDensity = texelAreaDensity / totalIndices;
|
|
}
|
|
else
|
|
{
|
|
prevChunk.rChunk.m_texelAreaDensity = 1.f;
|
|
}
|
|
|
|
prevChunk.rChunk.nNumIndices += currChunk.rChunk.nNumIndices;
|
|
prevChunk.rChunk.nNumVerts += currChunk.rChunk.nNumVerts;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CRenderMeshMerger::MergeBuffers(AABB& bounds)
|
|
{
|
|
FUNCTION_PROFILER_3DENGINE;
|
|
|
|
m_nTotalVertexCount = 0;
|
|
m_nTotalIndexCount = 0;
|
|
|
|
int nNeedVertices = 0;
|
|
int nNeedIndices = 0;
|
|
|
|
PodArray<SMergedChunk>& allChunks = m_lstChunksAll;
|
|
|
|
// Calculate total required sizes.
|
|
for (size_t nChunk = 0; nChunk < allChunks.size(); nChunk++)
|
|
{
|
|
SMergedChunk& renderChunk = allChunks[nChunk];
|
|
nNeedIndices += renderChunk.rChunk.nNumIndices;
|
|
nNeedVertices += renderChunk.rChunk.nNumVerts;
|
|
}
|
|
|
|
PodArray<vtx_idx>& arrIndices = m_lstNewIndices;
|
|
arrIndices.clear();
|
|
arrIndices.reserve(nNeedIndices);
|
|
|
|
PodArray<SVF_P3S_C4B_T2S>& arrVertices = m_lstVerts;
|
|
arrVertices.clear();
|
|
arrVertices.reserve(nNeedVertices);
|
|
|
|
PodArray<SPipTangents>& arrTangents = m_lstTangBasises;
|
|
arrTangents.clear();
|
|
arrTangents.reserve(nNeedVertices);
|
|
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
PodArray<SPipNormal>& arrNormals = m_lstNormals;
|
|
arrNormals.clear();
|
|
arrNormals.reserve(nNeedVertices);
|
|
#endif
|
|
|
|
m_lstMergeBuffersData.clear();
|
|
m_lstMergeBuffersData.resize(allChunks.size());
|
|
|
|
m_tmpAABB = bounds;
|
|
|
|
// do all GetPos etc calls before in the t thread
|
|
for (size_t nChunk = 0; nChunk < allChunks.size(); nChunk++)
|
|
{
|
|
SMergeBuffersData& rMergeBufferData = m_lstMergeBuffersData[nChunk];
|
|
IRenderMesh* pRM = allChunks[nChunk].pFromMesh->pMesh;
|
|
pRM->LockForThreadAccess();
|
|
rMergeBufferData.pPos = pRM->GetPosPtr(rMergeBufferData.nPosStride, FSL_READ);
|
|
rMergeBufferData.pTex = pRM->GetUVPtr(rMergeBufferData.nTexStride, FSL_READ);
|
|
rMergeBufferData.pColor = pRM->GetColorPtr(rMergeBufferData.nColorStride, FSL_READ);
|
|
rMergeBufferData.pTangs = pRM->GetTangentPtr(rMergeBufferData.nTangsStride, FSL_READ);
|
|
rMergeBufferData.nIndCount = pRM->GetIndicesCount();
|
|
rMergeBufferData.pSrcInds = pRM->GetIndexPtr(FSL_READ);
|
|
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
rMergeBufferData.pNorm = pRM->GetNormPtr(rMergeBufferData.nNormStride, FSL_READ);
|
|
#endif
|
|
}
|
|
|
|
MergeBuffersImpl(&m_tmpAABB, m_lstMergeBuffersData.GetElements());
|
|
|
|
// operation on buffers has finished, unlock them again for rendermesh garbage collection
|
|
for (size_t nChunk = 0; nChunk < allChunks.size(); nChunk++)
|
|
{
|
|
IRenderMesh* pRM = allChunks[nChunk].pFromMesh->pMesh;
|
|
pRM->UnLockForThreadAccess();
|
|
}
|
|
|
|
bounds = m_tmpAABB;
|
|
}
|
|
|
|
void CRenderMeshMerger::MergeBuffersImpl(AABB* pBounds, SMergeBuffersData* _arrMergeBuffersData)
|
|
{
|
|
// size for SPU buffers, currently we have 16 buffers, means we need 128 kb
|
|
uint32 nNumMergeChunks = m_lstChunksAll.size();
|
|
|
|
PodArray<SMergedChunk>& allChunks = m_lstChunksAll;
|
|
SMergeBuffersData* arrMergeBuffersData = _arrMergeBuffersData;
|
|
|
|
PodArray<vtx_idx>& arrIndices = m_lstNewIndices;
|
|
PodArray<SVF_P3S_C4B_T2S>& arrVertices = m_lstVerts;
|
|
PodArray<SPipTangents>& arrTangents = m_lstTangBasises;
|
|
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
PodArray<SPipNormal>& arrNormals = m_lstNormals;
|
|
#endif
|
|
|
|
for (size_t nChunk = 0; nChunk < nNumMergeChunks; nChunk++)
|
|
{
|
|
SMergedChunk& renderChunk = allChunks[nChunk];
|
|
SMergeBuffersData& rMergeBufferData = arrMergeBuffersData[nChunk];
|
|
SRenderMeshInfoInput* pRMI = renderChunk.pFromMesh;
|
|
Matrix34& rMatrix = pRMI->mat;
|
|
|
|
bool bMatrixHasRotation;
|
|
if (!rMatrix.m01 && !rMatrix.m02 && !rMatrix.m10 && !rMatrix.m12 && !rMatrix.m20 && !rMatrix.m21)
|
|
{
|
|
bMatrixHasRotation = false;
|
|
}
|
|
else
|
|
{
|
|
bMatrixHasRotation = true;
|
|
}
|
|
|
|
Vec3 vOffset = rMatrix.GetTranslation();
|
|
|
|
IRenderMesh* pRM = pRMI->pMesh;
|
|
|
|
// get streams.
|
|
int nPosStride = rMergeBufferData.nPosStride;
|
|
int nTexStride = rMergeBufferData.nTexStride;
|
|
int nColorStride = rMergeBufferData.nColorStride;
|
|
int nTangsStride = rMergeBufferData.nTangsStride;
|
|
|
|
uint8* pPos = rMergeBufferData.pPos;
|
|
uint8* pTex = rMergeBufferData.pTex;
|
|
uint8* pColor = rMergeBufferData.pColor;
|
|
uint8* pTangs = rMergeBufferData.pTangs;
|
|
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
int nNormStride = rMergeBufferData.nNormStride;
|
|
uint8* pNorm = rMergeBufferData.pNorm;
|
|
#endif
|
|
|
|
if (!pPos || !pTex || !pColor || !pTangs)
|
|
{
|
|
assert(0); // Should not happen.
|
|
continue;
|
|
}
|
|
|
|
Vec3 vMin, vMax;
|
|
pRM->GetBBox(vMin, vMax);
|
|
pBounds->Add(vMin);
|
|
pBounds->Add(vMax);
|
|
|
|
// get indices
|
|
vtx_idx* pInds = rMergeBufferData.pSrcInds;
|
|
|
|
Vec3 vOSPos(0, 0, 0);
|
|
Vec3 vOSProjDir(0, 0, 0);
|
|
|
|
int nLastVertex = arrVertices.size();
|
|
int nLastIndex = arrIndices.size();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// add indices
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int nCurrIndex = nLastIndex;
|
|
arrIndices.resize(nLastIndex + renderChunk.rChunk.nNumIndices);
|
|
int nAdjustedVertexOffset = nLastVertex - renderChunk.rChunk.nFirstVertId;
|
|
int i = renderChunk.rChunk.nFirstIndexId;
|
|
int numInd = renderChunk.rChunk.nFirstIndexId + renderChunk.rChunk.nNumIndices;
|
|
|
|
vtx_idx* __restrict pSrcInds = pInds;
|
|
vtx_idx* __restrict pDstInds = &arrIndices[0];
|
|
|
|
for (; i < numInd; i += 3, nCurrIndex += 3)
|
|
{
|
|
pDstInds[nCurrIndex + 0] = pSrcInds[i + 0] + nAdjustedVertexOffset;
|
|
pDstInds[nCurrIndex + 1] = pSrcInds[i + 1] + nAdjustedVertexOffset;
|
|
pDstInds[nCurrIndex + 2] = pSrcInds[i + 2] + nAdjustedVertexOffset;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
renderChunk.rChunk.nFirstIndexId = nLastIndex;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// add vertices
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int nCurrVertex = nLastVertex;
|
|
arrVertices.resize(nLastVertex + renderChunk.rChunk.nNumVerts);
|
|
arrTangents.resize(nLastVertex + renderChunk.rChunk.nNumVerts);
|
|
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
arrNormals.resize(nLastVertex + renderChunk.rChunk.nNumVerts);
|
|
#endif
|
|
|
|
int v = renderChunk.rChunk.nFirstVertId;
|
|
int numVert = renderChunk.rChunk.nFirstVertId + renderChunk.rChunk.nNumVerts;
|
|
if (bMatrixHasRotation)
|
|
{
|
|
SVF_P3S_C4B_T2S* __restrict pDstVerts = &arrVertices[0];
|
|
SPipTangents* __restrict pDstTangs = &arrTangents[0];
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
SPipNormal* __restrict pDstNorms = &arrNormals[0];
|
|
#endif
|
|
|
|
uint8* pSrcPos = pPos;
|
|
uint8* pSrcTex = pTex;
|
|
uint8* pSrcColor = pColor;
|
|
uint8* pSrcTangs = pTangs;
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
uint8* pSrcNorm = pNorm;
|
|
#endif
|
|
for (; v < numVert; v++, nCurrVertex++)
|
|
{
|
|
SVF_P3S_C4B_T2S& vert = pDstVerts[nCurrVertex];
|
|
SPipTangents& basis = pDstTangs[nCurrVertex];
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
SPipNormal& normal = pDstNorms[nCurrVertex];
|
|
#endif
|
|
// set pos/uv
|
|
Vec3& vPos = (*(Vec3*)&pSrcPos[nPosStride * v]);
|
|
Vec2* pUV = (Vec2*)&pSrcTex[nTexStride * v];
|
|
|
|
vert.xyz = rMatrix.TransformPoint(vPos);
|
|
vert.st = *pUV;
|
|
vert.color.dcolor = *(uint32*)&pSrcColor[nColorStride * v + 0];
|
|
|
|
// get tangent basis
|
|
basis = *(SPipTangents*)&pSrcTangs[nTangsStride * v];
|
|
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
normal = SPipNormal(Vec3(0, 0, 0));
|
|
if (pSrcNorm)
|
|
{
|
|
normal = *(SPipNormal*)&pSrcNorm[nNormStride * v];
|
|
}
|
|
#endif
|
|
|
|
if (bMatrixHasRotation)
|
|
{
|
|
basis.TransformSafelyBy(rMatrix);
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
if (pSrcNorm)
|
|
{
|
|
normal.TransformSafelyBy(rMatrix);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SVF_P3S_C4B_T2S* __restrict pDstVerts = &arrVertices[0];
|
|
SPipTangents* __restrict pDstTangs = &arrTangents[0];
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
SPipNormal* __restrict pDstNorms = &arrNormals[0];
|
|
#endif
|
|
|
|
uint8* pSrcPos = pPos;
|
|
uint8* pSrcTex = pTex;
|
|
uint8* pSrcColor = pColor;
|
|
uint8* pSrcTangs = pTangs;
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
uint8* pSrcNorm = pNorm;
|
|
#endif
|
|
for (; v < numVert; v++, nCurrVertex++)
|
|
{
|
|
SVF_P3S_C4B_T2S& vert = pDstVerts[nCurrVertex];
|
|
SPipTangents& basis = pDstTangs[nCurrVertex];
|
|
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
SPipNormal& normal = pDstNorms[nCurrVertex];
|
|
#endif
|
|
|
|
// set pos/uv
|
|
Vec3& vPos = (*(Vec3*)&pSrcPos[nPosStride * v]);
|
|
Vec2* pUV = (Vec2*)&pSrcTex[nTexStride * v];
|
|
|
|
vert.xyz = rMatrix.TransformPoint(vPos);
|
|
vert.st = *pUV;
|
|
vert.color.dcolor = *(uint32*)&pSrcColor[nColorStride * v];
|
|
|
|
// get tangent basis
|
|
basis = *(SPipTangents*)&pSrcTangs[nTangsStride * v];
|
|
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
normal = SPipNormal(Vec3(0, 0, 0));
|
|
if (pSrcNorm)
|
|
{
|
|
normal = *(SPipNormal*)(&pSrcNorm[nNormStride * v]);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// set vert range
|
|
renderChunk.rChunk.nFirstVertId = nLastVertex;
|
|
|
|
m_nTotalVertexCount += renderChunk.rChunk.nNumVerts;
|
|
m_nTotalIndexCount += renderChunk.rChunk.nNumIndices;
|
|
|
|
renderChunk.rChunk.m_vertexFormat = eVF_P3S_C4B_T2S;
|
|
}
|
|
}
|
|
|
|
void CRenderMeshMerger::Reset()
|
|
{
|
|
stl::free_container(m_lstRMIChunks);
|
|
stl::free_container(m_lstVerts);
|
|
stl::free_container(m_lstTangBasises);
|
|
stl::free_container(m_lstIndices);
|
|
stl::free_container(m_lstChunks);
|
|
stl::free_container(m_lstChunksAll);
|
|
stl::free_container(m_lstNewVerts);
|
|
stl::free_container(m_lstNewTangBasises);
|
|
stl::free_container(m_lstNewIndices);
|
|
stl::free_container(m_lstNewChunks);
|
|
stl::free_container(m_lstChunksMergedTemp);
|
|
stl::free_container(m_tmpRenderChunkArray);
|
|
stl::free_container(m_lstMergeBuffersData);
|
|
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
stl::free_container(m_lstNormals);
|
|
stl::free_container(m_lstNewNormals);
|
|
#endif
|
|
|
|
m_tmpClipContext.Reset();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
_smart_ptr<IRenderMesh> CRenderMeshMerger::MergeRenderMeshes(SRenderMeshInfoInput* pRMIArray, int nRMICount, SMergeInfo& info)
|
|
{
|
|
m_nTotalVertexCount = 0;
|
|
m_nTotalIndexCount = 0;
|
|
|
|
m_lstRMIChunks.clear();
|
|
m_lstVerts.clear();
|
|
m_lstTangBasises.clear();
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
m_lstNormals.clear();
|
|
#endif
|
|
m_lstIndices.clear();
|
|
m_lstNewIndices.clear();
|
|
m_lstChunks.clear();
|
|
|
|
// make list of all chunks
|
|
if (!GenerateRenderChunks(pRMIArray, nRMICount))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
MergeRenderChunks();
|
|
|
|
if (m_lstChunksAll.empty())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Often even single mesh must be merged, when non identity matrix is provided
|
|
/*
|
|
if (m_lstChunks.size() == m_lstChunksAll.size())
|
|
{
|
|
// No reduction of render chunks.
|
|
return 0;
|
|
}
|
|
*/
|
|
|
|
AABB finalBounds;
|
|
finalBounds.Reset();
|
|
MergeBuffers(finalBounds);
|
|
|
|
if (m_lstNewIndices.empty() || m_lstVerts.empty() || m_lstTangBasises.empty())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Repeat merging to properly update vertex ranges.
|
|
MergeRenderChunks();
|
|
|
|
IRenderMesh::SInitParamerers params;
|
|
params.pVertBuffer = &m_lstVerts.front();
|
|
params.nVertexCount = m_lstVerts.size();
|
|
params.vertexFormat = eVF_P3S_C4B_T2S;
|
|
params.pIndices = &m_lstNewIndices.front();
|
|
params.nIndexCount = m_lstNewIndices.size();
|
|
params.pTangents = &m_lstTangBasises.front();
|
|
#if ENABLE_NORMALSTREAM_SUPPORT
|
|
params.pNormals = &m_lstNormals.front();
|
|
#endif
|
|
params.nPrimetiveType = prtTriangleList;
|
|
params.eType = eRMT_Static;
|
|
params.nRenderChunkCount = 0;
|
|
params.bOnlyVideoBuffer = false;
|
|
params.bPrecache = false;
|
|
params.bLockForThreadAccess = true;//calls LockForThreadAccess in the Rendermesh ctor
|
|
|
|
_smart_ptr<IRenderMesh> pRenderMesh = GetRenderer()->CreateRenderMesh(info.sMeshType, info.sMeshName, ¶ms);
|
|
pRenderMesh->SetBBox(finalBounds.min, finalBounds.max);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Setup merged chunks
|
|
//////////////////////////////////////////////////////////////////////////
|
|
m_tmpRenderChunkArray.resize(m_lstChunks.size());
|
|
for (size_t i = 0, num = m_lstChunks.size(); i < num; i++)
|
|
{
|
|
m_tmpRenderChunkArray[i] = m_lstChunks[i].rChunk;
|
|
}
|
|
pRenderMesh->SetRenderChunks(&m_tmpRenderChunkArray.front(), m_tmpRenderChunkArray.size(), false);
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Setup un-merged chunks
|
|
//////////////////////////////////////////////////////////////////////////
|
|
m_tmpRenderChunkArray.resize(m_lstChunksAll.size());
|
|
for (size_t i = 0, num = m_lstChunksAll.size(); i < num; i++)
|
|
{
|
|
m_tmpRenderChunkArray[i] = m_lstChunksAll[i].rChunk;
|
|
}
|
|
pRenderMesh->SetRenderChunks(&m_tmpRenderChunkArray.front(), m_tmpRenderChunkArray.size(), true);
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
pRenderMesh->UnLockForThreadAccess();
|
|
|
|
return pRenderMesh;
|
|
}
|