// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "std3d.h"
#include "nel/3d/patch.h"
#include "nel/3d/tessellation.h"
#include "nel/3d/bezier_patch.h"
#include "nel/3d/zone.h"
#include "nel/3d/landscape.h"
#include "nel/3d/landscape_profile.h"
#include "nel/3d/patchdlm_context.h"
#include "nel/misc/vector.h"
#include "nel/misc/common.h"
using namespace std;
using namespace NLMISC;
namespace NL3D
{
// ***************************************************************************
// ***************************************************************************
// Patch Texture computation.
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
void CPatch::computeNewFar(const NLMISC::CBSphere &patchSphere, sint &newFar0, sint &newFar1)
{
// Classify the patch.
//========================
float r= (CLandscapeGlobals::RefineCenter-patchSphere.Center).norm() - patchSphere.Radius;
float rr=0.0;
if(r-(CLandscapeGlobals::FarTransition+2*patchSphere.Radius))
{
newFar1= newFar0+1;
}
// Update Texture Info.
//========================
if(newFar0!=Far0 || newFar1!=Far1)
{
// Backup old pass0
CPatchRdrPass *oldPass0=_PatchRdrPassFar0;
CPatchRdrPass *oldPass1=_PatchRdrPassFar1;
// Checks
if (oldPass0==NULL)
nlassert (Far0<=0);
if (oldPass1==NULL)
nlassert (Far1<=0);
float oldFar0UScale=Far0UScale;
float oldFar0VScale=Far0VScale;
float oldFar0UBias=Far0UBias;
float oldFar0VBias=Far0VBias;
uint8 oldFlags=Flags;
// Don't delete the pass0 if the new newFar1 will use it
if ((newFar1==Far0)&&(Far0>0))
_PatchRdrPassFar0=NULL;
// Don't delete the pass1 if the new newFar0 will use it
if ((newFar0==Far1)&&(Far1>0))
_PatchRdrPassFar1=NULL;
// Pass0 have changed ?
if (newFar0!=Far0)
{
// Compute / get the texture Far.
if(newFar0>0)
{
// Free the old pass, don't used any more
if (_PatchRdrPassFar0)
Zone->Landscape->freeFarRenderPass (this, _PatchRdrPassFar0, Far0);
// Can we use the old pass1 ?
if (newFar0==Far1)
{
// Yes, recycle it!
_PatchRdrPassFar0=oldPass1;
// Copy uv coordinates
Far0UScale=Far1UScale;
Far0VScale=Far1VScale;
Far0UBias=Far1UBias;
Far0VBias=Far1VBias;
// Copy rotation flag
Flags&=~NL_PATCH_FAR0_ROTATED; // erase it
if (Flags&NL_PATCH_FAR1_ROTATED)
Flags|=NL_PATCH_FAR0_ROTATED; // copy it
}
else // get a new render pass
{
// Rotation boolean
bool bRot;
_PatchRdrPassFar0=Zone->Landscape->getFarRenderPass(this, newFar0, Far0UScale, Far0VScale, Far0UBias, Far0VBias, bRot);
// Flags is set if the far texture is rotated of 90deg to the left
if (bRot)
Flags|=NL_PATCH_FAR0_ROTATED;
else
Flags&=~NL_PATCH_FAR0_ROTATED;
}
}
else // no more far pass0
{
if (_PatchRdrPassFar0)
{
Zone->Landscape->freeFarRenderPass (this, _PatchRdrPassFar0, Far0);
_PatchRdrPassFar0=NULL;
}
}
}
// Pass1 have changed ?
if (newFar1!=Far1)
{
// Now let's go with pass1
if(newFar1>0)
{
// Delete the pass1 if not used any more
if (_PatchRdrPassFar1)
Zone->Landscape->freeFarRenderPass (this, _PatchRdrPassFar1, Far1);
// Can we use the old pass1 ?
if (newFar1==Far0)
{
// Yes, recycle it!
_PatchRdrPassFar1= oldPass0;
nlassert (_PatchRdrPassFar1);
// Copy uv coordinates
Far1UScale=oldFar0UScale;
Far1VScale=oldFar0VScale;
Far1UBias=oldFar0UBias;
Far1VBias=oldFar0VBias;
// Copy rotation flag
Flags&=~NL_PATCH_FAR1_ROTATED; // erase it
if (oldFlags&NL_PATCH_FAR0_ROTATED)
Flags|=NL_PATCH_FAR1_ROTATED; // copy it
}
else // get a new render pass
{
// Rotation boolean
bool bRot;
_PatchRdrPassFar1=Zone->Landscape->getFarRenderPass(this, newFar1, Far1UScale, Far1VScale, Far1UBias, Far1VBias, bRot);
nlassert (_PatchRdrPassFar1);
// Flags is set if the far texture is rotated of 90deg to the left
if (bRot)
Flags|=NL_PATCH_FAR1_ROTATED;
else
Flags&=~NL_PATCH_FAR1_ROTATED;
}
// Compute info for transition.
float farDist = 0.f;
switch(newFar1)
{
case 1: farDist= CLandscapeGlobals::TileDistNear; break;
case 2: farDist= CLandscapeGlobals::Far0Dist; break;
case 3: farDist= CLandscapeGlobals::Far1Dist; break;
default: nlstop;
};
TransitionSqrMin= sqr(farDist-CLandscapeGlobals::FarTransition);
OOTransitionSqrDelta= 1.0f/(sqr(farDist)-TransitionSqrMin);
}
else // no more far pass1
{
if (_PatchRdrPassFar1)
{
Zone->Landscape->freeFarRenderPass (this, _PatchRdrPassFar1, Far1);
_PatchRdrPassFar1=NULL;
}
}
}
}
// Don't copy Far0 and Far1.
}
// ***************************************************************************
void CPatch::updateTextureFarOnly(const NLMISC::CBSphere &patchSphere)
{
sint newFar0,newFar1;
// compute new Far0 and new Far1.
computeNewFar(patchSphere, newFar0, newFar1);
// Far change.
//------------------
// Set new far values
Far0= newFar0;
Far1= newFar1;
}
// ***************************************************************************
// ***************************************************************************
// Patch / Face rendering.
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
void CPatch::preRender(const NLMISC::CBSphere &patchSphere)
{
// 0. Classify the patch, and update TextureInfo.
//=======================
sint newFar0,newFar1;
// compute new Far0 and new Far1.
computeNewFar(patchSphere, newFar0, newFar1);
// 2. Update vertices in VB
//========================
sint oldFar0= Far0, oldFar1= Far1;
// Test if go in or go out tileMode.
bool changeTileMode;
// this is true if a change is made, and if old or current is in TileMode.
changeTileMode= (newFar0!=oldFar0) && (newFar0==0 || oldFar0==0);
// Or this is true if Far0 / Far1 was invalid before.
changeTileMode= changeTileMode || oldFar0==-1 || oldFar1==-1;
// Pre VB change.
//------------------
// In this case, major change: delete all VB, then recreate after.
if(changeTileMode)
{
// delete the old VB (NB: isRenderClipped()==false here).
// NB: Far0 and Far1 are still unmodified, so deleteVB() will do the good job.
deleteVBAndFaceVector();
}
else
{
// minor change: Far0 UV, or Far1.
// Far0 UV: do nothing here.
// If change in Far1, must delete Far1
if(newFar1!=oldFar1)
{
// NB: Far1 is still unmodified, so deleteVB() will do the good job.
deleteVBAndFaceVectorFar1Only();
}
}
// Far change.
//------------------
// Set new far values
Far0= newFar0;
Far1= newFar1;
// Post VB change.
//------------------
if(changeTileMode)
{
// major change: recreate all the VB.
allocateVBAndFaceVector();
// Then try to refill if possible.
fillVB();
}
else
{
// minor change: Far0 UV, or Far1.
// If change in Far0
if(newFar0!=oldFar0)
{
// Then must recompute VB with good UVs.
// An optimisation is to check if UVs had really change (see UScale ...)
// but the case they don't change is very rare: 1/16.
fillVBFar0Only();
}
// If change in Far1, must allcoate and recompute UVs.
if(newFar1!=oldFar1)
{
allocateVBAndFaceVectorFar1Only();
// try to fill if no reallocation problem
fillVBFar1Only();
}
}
// 3. Append patch to renderList.
//=====================
// This patch is visible. So if good far, append.
if(Far0>0)
{
_PatchRdrPassFar0->appendRdrPatchFar0(this);
}
// Same for Far1.
if(Far1>0)
{
_PatchRdrPassFar1->appendRdrPatchFar1(this);
}
// 4. Clip tess blocks.
//=====================
// If we are in Tile/FarTransition
bool doClipFar= Far0==0 && Far1==1;
// Parse all TessBlocks.
uint nTessBlock= TessBlocks.size();
CTessBlock *pTessBlock= nTessBlock>0? &TessBlocks[0]: NULL ;
for(; nTessBlock>0; pTessBlock++, nTessBlock--)
{
CTessBlock &tblock= *pTessBlock;
// bkup the old result for clipping
bool oldVisibleFar0= tblock.visibleFar0();
bool oldVisibleTile= tblock.visibleTile();
bool oldVisibleFar1= tblock.visibleFar1();
// If this TessBlock is empty, do not need to clip
if(tblock.FaceTileMaterialRefCount==0)
{
// Simply force the clip.
tblock.forceClip();
}
else
{
// clip the tessBlock.
tblock.resetClip();
tblock.clip();
// If we are in Tile/FarTransition
if(doClipFar)
tblock.clipFar(CLandscapeGlobals::RefineCenter, CLandscapeGlobals::TileDistNear, CLandscapeGlobals::FarTransition);
// If TileMode, and if tile visible
if(Far0==0 && tblock.visibleTile() )
{
// Append all tiles (if any) to the renderPass list!
for(uint j=0;jappendTileToEachRenderPass(NumRenderableFaces);
}
}
}
// If change of clip in tessBlock, must update the tessBlock
if( Far0> 0 && oldVisibleFar0 != tblock.visibleFar0() )
{
if( tblock.visibleFar0() )
{
// allocate
updateFar0VBAlloc(tblock.FarVertexList, true);
// fill only if possible.
if(!CLandscapeGlobals::CurrentFar0VBAllocator->reallocationOccurs())
fillFar0VertexListVB(tblock.FarVertexList);
// rebuild triangles index list.
tblock.refillFaceVectorFar0();
}
else
{
// delete
updateFar0VBAlloc(tblock.FarVertexList, false);
}
}
if( Far0==0 && oldVisibleTile != tblock.visibleTile() )
{
if( tblock.visibleTile() )
{
// allocate
updateTileVBAlloc(tblock.NearVertexList, true);
// fill only if possible.
if(!CLandscapeGlobals::CurrentTileVBAllocator->reallocationOccurs())
fillTileVertexListVB(tblock.NearVertexList);
// rebuild triangles index list.
tblock.refillFaceVectorTile();
}
else
{
// delete
updateTileVBAlloc(tblock.NearVertexList, false);
}
}
if( Far1> 0 && oldVisibleFar1 != tblock.visibleFar1() )
{
if( tblock.visibleFar1() )
{
// allocate
updateFar1VBAlloc(tblock.FarVertexList, true);
// fill only if possible.
if(!CLandscapeGlobals::CurrentFar1VBAllocator->reallocationOccurs())
fillFar1VertexListVB(tblock.FarVertexList);
// rebuild triangles index list.
tblock.refillFaceVectorFar1();
}
else
{
// delete
updateFar1VBAlloc(tblock.FarVertexList, false);
}
}
}
}
// Special profiling.
#ifdef NL3D_PROFILE_LAND
#define NL3D_PROFILE_LAND_ADD_FACE_VECTOR(_x, _fv) \
if(_fv) \
{ \
NL3D_PROFILE_LAND_ADD(_x, *_fv); \
}
#else
#define NL3D_PROFILE_LAND_ADD_FACE_VECTOR(_x, _fv)
#endif
// ***************************************************************************
static inline void renderFaceVector(TLandscapeIndexType *fv)
{
/*
Remind that structure of fv is:
uint32 NumTri
uint32 index0Tri0
uint32 index1Tri0
uint32 index2Tri0
uint32 index0Tri1
uint32 index1Tri1
.....
*/
// If not NULL (ie not empty).
if(fv)
{
// TestYoyo: Bench.
/*
extern uint TEMP_NRFV;
extern uint TEMP_NRFVTri;
extern float TEMP_NRFVMeanTri;
extern float TEMP_NRFVDeltaTri;
TEMP_NRFV++;
TEMP_NRFVTri+= *fv;
TEMP_NRFVDeltaTri+= sqr(*fv-TEMP_NRFVMeanTri);
*/
// here we have NumTri>0, because fv!=NULL.
// making lot of render() is slower than copy a block, and render it.
//CLandscapeGlobals::PatchCurrentDriver->renderSimpleTriangles(fv+1, *fv);
CHECK_IBA_RANGE(CLandscapeGlobals::PassTriArrayIBA, NL3D_LandscapeGlobals_PassTriCurPtr, *fv*3 * sizeof(uint32));
#if defined(NL_OS_WINDOWS) && !defined(NL_NO_ASM)
#ifndef NL_LANDSCAPE_INDEX16
__asm
{
mov ebx, fv
mov edi, NL3D_LandscapeGlobals_PassTriCurPtr
mov edx, NL3D_LandscapeGlobals_PassNTri
xor eax, eax // Avoid AGI stall.
mov ecx, [ebx]
lea esi, [ebx+4]
mov eax, ecx // eax= bkup NumTris
lea ecx, [ecx + ecx*2] // ecx= nTriIndex= NumTris*3
// copy tri indices
rep movsd
add edx, eax // edx= NL3D_LandscapeGlobals_PassNTri + NumTri;
// NL3D_LandscapeGlobals_PassTriCurPtr= edi= new ptr after copy
mov NL3D_LandscapeGlobals_PassTriCurPtr, edi
mov NL3D_LandscapeGlobals_PassNTri, edx
}
#else
__asm
{
mov ebx, fv
mov edi, NL3D_LandscapeGlobals_PassTriCurPtr
mov edx, NL3D_LandscapeGlobals_PassNTri
xor eax, eax // Avoid AGI stall.
movzx ecx, word ptr [ebx]
lea esi, [ebx+2]
mov eax, ecx // eax= bkup NumTris
lea ecx, [ecx + ecx*2] // ecx= nTriIndex= NumTris*3
test ecx, 1
jne odd_number
shr ecx, 1
// for alignment, first copy a single word
movsw
dec ecx
rep movsd
movsw
jmp even_number_done
odd_number:
shr ecx, 1
// for alignment, first copy a single word
movsw
rep movsd
even_number_done:
add edx, eax // edx= NL3D_LandscapeGlobals_PassNTri + NumTri;
// NL3D_LandscapeGlobals_PassTriCurPtr= edi= new ptr after copy
mov NL3D_LandscapeGlobals_PassTriCurPtr, edi
mov NL3D_LandscapeGlobals_PassNTri, edx
}
#endif
#else
uint nTriIndex= *fv*3;
// Fill and increment the array.
#ifndef NL_LANDSCAPE_INDEX16
memcpy( NL3D_LandscapeGlobals_PassTriCurPtr, fv+1, nTriIndex * sizeof(uint32) );
NL3D_LandscapeGlobals_PassTriCurPtr= (uint32*)NL3D_LandscapeGlobals_PassTriCurPtr + nTriIndex;
#else
memcpy( NL3D_LandscapeGlobals_PassTriCurPtr, fv+1, nTriIndex * sizeof(uint16) );
NL3D_LandscapeGlobals_PassTriCurPtr= (uint16*)NL3D_LandscapeGlobals_PassTriCurPtr + nTriIndex;
#endif
NL3D_LandscapeGlobals_PassNTri+= *fv;
#endif
}
}
// ***************************************************************************
void CPatch::renderFar0()
{
NL3D_PROFILE_LAND_ADD(ProfNPatchRdrFar0, 1);
#ifdef NL_DEBUG
// Must be visible, and must be called only if the RdrPass is enabled.
nlassert(!isRenderClipped() && _PatchRdrPassFar0);
#endif
// Render tris of MasterBlock.
renderFaceVector(MasterBlock.Far0FaceVector);
// profile
NL3D_PROFILE_LAND_ADD_FACE_VECTOR(ProfNRdrFar0, MasterBlock.Far0FaceVector);
// Render tris of TessBlocks.
uint nTessBlock= TessBlocks.size();
CTessBlock *pTessBlock= nTessBlock>0? &TessBlocks[0]: NULL ;
for(; nTessBlock>0; pTessBlock++, nTessBlock--)
{
CTessBlock &tblock= *pTessBlock;
// if block visible, render
if( tblock.visibleFar0() )
{
renderFaceVector(tblock.Far0FaceVector);
// profile
NL3D_PROFILE_LAND_ADD_FACE_VECTOR(ProfNRdrFar0, tblock.Far0FaceVector);
}
}
}
// ***************************************************************************
void CPatch::renderFar1()
{
NL3D_PROFILE_LAND_ADD(ProfNPatchRdrFar1, 1);
#ifdef NL_DEBUG
// Must be visible, and must be called only if the RdrPass is enabled.
nlassert(!isRenderClipped() && _PatchRdrPassFar1);
#endif
// Render tris of MasterBlock.
renderFaceVector(MasterBlock.Far1FaceVector);
// profile.
NL3D_PROFILE_LAND_ADD_FACE_VECTOR(ProfNRdrFar1, MasterBlock.Far1FaceVector);
// Render tris of TessBlocks.
uint nTessBlock= TessBlocks.size();
CTessBlock *pTessBlock= nTessBlock>0? &TessBlocks[0]: NULL ;
for(; nTessBlock>0; pTessBlock++, nTessBlock--)
{
CTessBlock &tblock= *pTessBlock;
// if block visible, render
if( tblock.visibleFar1() )
{
renderFaceVector(tblock.Far1FaceVector);
// profile.
NL3D_PROFILE_LAND_ADD_FACE_VECTOR(ProfNRdrFar1, tblock.Far1FaceVector);
}
}
}
// ***************************************************************************
void CTileMaterial::renderTile(uint pass)
{
// because precisely inserted in preRender(), and correctly tested, this tile is to be rendered,
// If the pass is enabled.
if(Pass[pass].PatchRdrPass)
{
// render tris of the good faceList.
renderFaceVector(TileFaceVectors[pass]);
// profile.
NL3D_PROFILE_LAND_ADD_FACE_VECTOR(ProfNRdrTile[pass], TileFaceVectors[pass]);
}
}
// ***************************************************************************
void CTileMaterial::renderTilePassRGB0()
{
// because precisely inserted in preRender(), and correctly tested, this tile is to be rendered,
// this pass must be enabled!
nlassert(Pass[NL3D_TILE_PASS_RGB0].PatchRdrPass);
// render tris of the good faceList.
renderFaceVector(TileFaceVectors[NL3D_TILE_PASS_RGB0]);
// profile.
NL3D_PROFILE_LAND_ADD_FACE_VECTOR(ProfNRdrTile[NL3D_TILE_PASS_RGB0], TileFaceVectors[NL3D_TILE_PASS_RGB0]);
}
// ***************************************************************************
void CTileMaterial::renderTilePassLightmap()
{
// because precisely inserted in preRender(), and correctly tested, this tile is to be rendered,
// this pass must be enabled!
nlassert(Pass[NL3D_TILE_PASS_LIGHTMAP].PatchRdrPass);
// render tris of the good faceList, ie the one of PassRGB0, because vertices are reused.
renderFaceVector(TileFaceVectors[NL3D_TILE_PASS_RGB0]);
// profile.
NL3D_PROFILE_LAND_ADD_FACE_VECTOR(ProfNRdrTile[NL3D_TILE_PASS_LIGHTMAP], TileFaceVectors[NL3D_TILE_PASS_RGB0]);
}
// ***************************************************************************
// ***************************************************************************
// FaceVector Allocation
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
void CPatch::createFaceVectorFar1()
{
if(Far1>0)
{
// Create the face for all TessBlocks.
MasterBlock.createFaceVectorFar1(getLandscape()->_FaceVectorManager);
for(uint i=0; i_FaceVectorManager);
}
}
// ***************************************************************************
void CPatch::deleteFaceVectorFar1()
{
if(Far1>0)
{
// delete the face for all TessBlocks.
MasterBlock.deleteFaceVectorFar1(getLandscape()->_FaceVectorManager);
for(uint i=0; i_FaceVectorManager);
}
}
// ***************************************************************************
void CPatch::createFaceVectorFar0OrTile()
{
// If Far Mode.
if(Far0>0)
{
// Create the face for all TessBlocks.
MasterBlock.createFaceVectorFar0(getLandscape()->_FaceVectorManager);
for(uint i=0; i_FaceVectorManager);
}
// Or If Tile Mode.
else if(Far0==0)
{
// Create the face for all TessBlocks.
// No tiles in MasterBlock!
for(uint i=0; i_FaceVectorManager);
}
}
// ***************************************************************************
void CPatch::deleteFaceVectorFar0OrTile()
{
// If Far Mode.
if(Far0>0)
{
// delete the face for all TessBlocks.
MasterBlock.deleteFaceVectorFar0(getLandscape()->_FaceVectorManager);
for(uint i=0; i_FaceVectorManager);
}
// Or If Tile Mode.
else if(Far0==0)
{
// delete the face for all TessBlocks.
// No tiles in MasterBlock!
for(uint i=0; i_FaceVectorManager);
}
}
// ***************************************************************************
void CPatch::recreateTessBlockFaceVector(CTessBlock &block)
{
// Do it Only if patch is visible.
if(!isRenderClipped())
{
// Far0.
// If Far Mode.
if(Far0>0)
{
// Create the face for this TessBlock only.
block.createFaceVectorFar0(getLandscape()->_FaceVectorManager);
}
// Or If Tile Mode.
else if(Far0==0)
{
// No tiles in MasterBlock! So no need to call createFaceVectorTile(), if this block is the MasterBlock.
if(&block != &MasterBlock)
block.createFaceVectorTile(getLandscape()->_FaceVectorManager);
}
// Far1.
if(Far1>0)
{
// Create the face for this TessBlock only.
block.createFaceVectorFar1(getLandscape()->_FaceVectorManager);
}
}
}
// ***************************************************************************
// ***************************************************************************
// VB Allocation
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
void CPatch::updateFar0VBAlloc(CTessList &vertList, bool alloc)
{
// Traverse the vertList.
CTessFarVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessFarVertex*)pVert->Next)
{
if(alloc)
pVert->Index0= CLandscapeGlobals::CurrentFar0VBAllocator->allocateVertex();
else
CLandscapeGlobals::CurrentFar0VBAllocator->deleteVertex(pVert->Index0);
}
}
// ***************************************************************************
void CPatch::updateFar1VBAlloc(CTessList &vertList, bool alloc)
{
// Traverse the vertList.
CTessFarVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessFarVertex*)pVert->Next)
{
if(alloc)
pVert->Index1= CLandscapeGlobals::CurrentFar1VBAllocator->allocateVertex();
else
CLandscapeGlobals::CurrentFar1VBAllocator->deleteVertex(pVert->Index1);
}
}
// ***************************************************************************
void CPatch::updateTileVBAlloc(CTessList &vertList, bool alloc)
{
// Traverse the vertList.
CTessNearVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessNearVertex*)pVert->Next)
{
if(alloc)
pVert->Index= CLandscapeGlobals::CurrentTileVBAllocator->allocateVertex();
else
CLandscapeGlobals::CurrentTileVBAllocator->deleteVertex(pVert->Index);
}
}
// ***************************************************************************
void CPatch::updateVBAlloc(bool alloc)
{
// update Far0.
//=======
if(Far0>0)
{
// alloc Far0 VB.
updateFar0VBAlloc(MasterBlock.FarVertexList, alloc);
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// need update VB only if tessBlock is visible.
if( tblock.visibleFar0() )
updateFar0VBAlloc(tblock.FarVertexList, alloc);
}
}
else if (Far0==0)
{
// alloc Tile VB.
// No Tiles in MasterBlock!!
// Traverse the TessBlocks to add vertices.
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// Add the vertices.
// need update VB only if tessBlock is visible.
if( tblock.visibleTile() )
updateTileVBAlloc(tblock.NearVertexList, alloc);
}
}
// update Far1.
//=======
if(Far1>0)
{
// alloc VB.
updateFar1VBAlloc(MasterBlock.FarVertexList, alloc);
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// need update VB only if tessBlock is visible.
if( tblock.visibleFar1() )
updateFar1VBAlloc(tblock.FarVertexList, alloc);
}
}
}
// ***************************************************************************
void CPatch::deleteVBAndFaceVector()
{
updateVBAlloc(false);
deleteFaceVectorFar1();
deleteFaceVectorFar0OrTile();
}
// ***************************************************************************
void CPatch::allocateVBAndFaceVector()
{
updateVBAlloc(true);
createFaceVectorFar1();
createFaceVectorFar0OrTile();
}
// ***************************************************************************
void CPatch::deleteVBAndFaceVectorFar1Only()
{
if(Far1>0)
{
// alloc VB.
updateFar1VBAlloc(MasterBlock.FarVertexList, false);
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// need update VB only if tessBlock is visible.
if( tblock.visibleFar1() )
updateFar1VBAlloc(tblock.FarVertexList, false);
}
}
deleteFaceVectorFar1();
}
// ***************************************************************************
void CPatch::allocateVBAndFaceVectorFar1Only()
{
if(Far1>0)
{
// alloc VB.
updateFar1VBAlloc(MasterBlock.FarVertexList, true);
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// need update VB only if tessBlock is visible.
if( tblock.visibleFar1() )
updateFar1VBAlloc(tblock.FarVertexList, true);
}
}
createFaceVectorFar1();
}
// ***************************************************************************
void CPatch::debugAllocationMarkIndicesFarList(CTessList &vertList, uint marker)
{
// Traverse the vertList.
CTessFarVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessFarVertex*)pVert->Next)
{
pVert->Index0= marker;
pVert->Index1= marker;
}
}
void CPatch::debugAllocationMarkIndicesNearList(CTessList &vertList, uint marker)
{
// Traverse the vertList.
CTessNearVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessNearVertex*)pVert->Next)
{
pVert->Index= marker;
}
}
void CPatch::debugAllocationMarkIndices(uint marker)
{
sint i;
// Do it For Far.
debugAllocationMarkIndicesFarList(MasterBlock.FarVertexList, marker);
for(i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
debugAllocationMarkIndicesFarList(tblock.FarVertexList, marker);
}
// Do it For Near.
// No Tiles in MasterBlock!!
for(i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
debugAllocationMarkIndicesNearList(tblock.NearVertexList, marker);
}
}
// ***************************************************************************
// ***************************************************************************
// VB Filling.
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
// NB: need to be inlined only for fillFar0VB() in this file.
inline void CPatch::fillFar0VertexVB(CTessFarVertex *pVert)
{
// The Buffers must have been locked
nlassert(CLandscapeGlobals::CurrentFar0VBAllocator);
nlassert(CLandscapeGlobals::CurrentFar0VBAllocator->bufferLocked());
// VBInfo must be OK.
nlassert(!CLandscapeGlobals::CurrentFar0VBAllocator->reallocationOccurs());
static uint8 *CurVBPtr;
// Compute/build the new vertex.
CurVBPtr= (uint8*)CLandscapeGlobals::CurrentFar0VBInfo.VertexCoordPointer;
CurVBPtr+= pVert->Index0 * CLandscapeGlobals::CurrentFar0VBInfo.VertexSize;
// NB: the filling order of data is important, for AGP write combiners.
// compute Uvs.
static CUV uv;
CParamCoord pc= pVert->PCoord;
if (Flags&NL_PATCH_FAR0_ROTATED)
{
uv.U= pc.getT()* Far0UScale + Far0UBias;
uv.V= (1.f-pc.getS())* Far0VScale + Far0VBias;
}
else
{
uv.U= pc.getS()* Far0UScale + Far0UBias;
uv.V= pc.getT()* Far0VScale + Far0VBias;
}
// compute Dynamic lightmap Uv.
static CUV uvDLM;
if(_DLMContext) // (NB: Suppose BTB kill this test).
{
// compute UV with DLM context info.
uvDLM.U= pc.getS()* _DLMContext->DLMUScale + _DLMContext->DLMUBias;
uvDLM.V= pc.getT()* _DLMContext->DLMVScale + _DLMContext->DLMVBias;
}
else
{
// just set UV so the vertex point to a black pixel (see CTextureDLM).
uvDLM.U= 1;
uvDLM.V= 1;
}
// If not VertexProgram (NB: Suppose BTB kill this test).
if( !CLandscapeGlobals::VertexProgramEnabled )
{
// Set Pos. Set it local to the current center of landscape
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar0VBInfo.Accessor, CurVBPtr, sizeof(CVector));
*(CVector*)CurVBPtr= pVert->Src->Pos - CLandscapeGlobals::PZBModelPosition;
// Set Uvs.
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar0VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.TexCoordOff0, sizeof(CUV));
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.TexCoordOff0)= uv;
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar0VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.TexCoordOff1, sizeof(CUV));
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.TexCoordOff1)= uvDLM;
}
else
{
// Else must setup Vertex program inputs
// v[0]== StartPos.
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar0VBInfo.Accessor, CurVBPtr, sizeof(CVector));
*(CVector*)CurVBPtr= pVert->Src->StartPos;
// v[8]== Tex0
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar0VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.TexCoordOff0, sizeof(CUV));
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.TexCoordOff0)= uv;
// v[9]== Tex1
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar0VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.TexCoordOff1, sizeof(CUV));
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.TexCoordOff1)= uvDLM;
// v[10]== GeomInfo.
static CUV geomInfo;
geomInfo.U= pVert->Src->MaxFaceSize * CLandscapeGlobals::OORefineThreshold;
geomInfo.V= pVert->Src->MaxNearLimit;
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar0VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.GeomInfoOff, sizeof(CUV));
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.GeomInfoOff)= geomInfo;
// v[11]== EndPos - StartPos
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar0VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.DeltaPosOff, sizeof(CVector))
*(CVector*)(CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.DeltaPosOff)=
pVert->Src->EndPos - pVert->Src->StartPos;
}
}
// ***************************************************************************
// NB: need to be inlined only for fillFar1VB() in this file.
inline void CPatch::fillFar1VertexVB(CTessFarVertex *pVert)
{
// The Buffers must have been locked
nlassert(CLandscapeGlobals::CurrentFar1VBAllocator);
nlassert(CLandscapeGlobals::CurrentFar1VBAllocator->bufferLocked());
// VBInfo must be OK.
nlassert(!CLandscapeGlobals::CurrentFar1VBAllocator->reallocationOccurs());
static uint8 *CurVBPtr;
// Compute/build the new vertex.
CurVBPtr= (uint8*)CLandscapeGlobals::CurrentFar1VBInfo.VertexCoordPointer;
CurVBPtr+= pVert->Index1 * CLandscapeGlobals::CurrentFar1VBInfo.VertexSize;
// NB: the filling order of data is important, for AGP write combiners.
// compute Uvs.
static CUV uv;
CParamCoord pc= pVert->PCoord;
if (Flags&NL_PATCH_FAR1_ROTATED)
{
uv.U= pc.getT()* Far1UScale + Far1UBias;
uv.V= (1.f-pc.getS())* Far1VScale + Far1VBias;
}
else
{
uv.U= pc.getS()* Far1UScale + Far1UBias;
uv.V= pc.getT()* Far1VScale + Far1VBias;
}
// compute Dynamic lightmap Uv.
static CUV uvDLM;
if(_DLMContext) // (NB: Suppose BTB kill this test).
{
// compute UV with DLM context info.
uvDLM.U= pc.getS()* _DLMContext->DLMUScale + _DLMContext->DLMUBias;
uvDLM.V= pc.getT()* _DLMContext->DLMVScale + _DLMContext->DLMVBias;
}
else
{
// just set UV so the vertex point to a black pixel (see CTextureDLM).
uvDLM.U= 1;
uvDLM.V= 1;
}
// If not VertexProgram (NB: Suppose BTB kill this test).
if( !CLandscapeGlobals::VertexProgramEnabled )
{
// Set Pos. Set it local to the current center of landscape
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar1VBInfo.Accessor, CurVBPtr, sizeof(CVector));
*(CVector*)CurVBPtr= pVert->Src->Pos - CLandscapeGlobals::PZBModelPosition;
// Set Uvs.
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar1VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.TexCoordOff0, sizeof(CUV));
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.TexCoordOff0)= uv;
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar1VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.TexCoordOff1, sizeof(CUV));
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.TexCoordOff1)= uvDLM;
// Set default color.
static CRGBA col(255,255,255,255);
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar1VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.ColorOff, sizeof(CRGBA));
*(CRGBA*)(CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.ColorOff)= col;
}
else
{
// Else must setup Vertex program inputs
// v[0]== StartPos.
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar1VBInfo.Accessor, CurVBPtr, sizeof(CVector));
*(CVector*)CurVBPtr= pVert->Src->StartPos;
// v[8]== Tex0
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar1VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.TexCoordOff0, sizeof(CUV));
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.TexCoordOff0)= uv;
// v[9]== Tex1
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar1VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.TexCoordOff1, sizeof(CUV));
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.TexCoordOff1)= uvDLM;
// v[10]== GeomInfo.
static CUV geomInfo;
geomInfo.U= pVert->Src->MaxFaceSize * CLandscapeGlobals::OORefineThreshold;
geomInfo.V= pVert->Src->MaxNearLimit;
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar1VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.GeomInfoOff, sizeof(CUV));
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.GeomInfoOff)= geomInfo;
// v[11]== EndPos - StartPos
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar1VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.DeltaPosOff, sizeof(CVector))
*(CVector*)(CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.DeltaPosOff)=
pVert->Src->EndPos - pVert->Src->StartPos;
// v[12]== Alpha information
// Hopefully, fillVBFar1Only() is called each Time the Far1 change, in preRender().
// So TransitionSqrMin and OOTransitionSqrDelta in CPath are valid.
geomInfo.U= TransitionSqrMin;
geomInfo.V= OOTransitionSqrDelta;
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar1VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.AlphaInfoOff, sizeof(CUV))
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.AlphaInfoOff)= geomInfo;
}
}
// ***************************************************************************
// NB: need to be inlined only for fillTileVB() in this file.
inline void CPatch::fillTileVertexVB(CTessNearVertex *pVert)
{
// The Buffers must have been locked
nlassert(CLandscapeGlobals::CurrentTileVBAllocator);
nlassert(CLandscapeGlobals::CurrentTileVBAllocator->bufferLocked());
// VBInfo must be OK.
nlassert(!CLandscapeGlobals::CurrentTileVBAllocator->reallocationOccurs());
static uint8 *CurVBPtr;
// Compute/build the new vertex.
CurVBPtr= (uint8*)CLandscapeGlobals::CurrentTileVBInfo.VertexCoordPointer;
CurVBPtr+= pVert->Index * CLandscapeGlobals::CurrentTileVBInfo.VertexSize;
// NB: the filling order of data is important, for AGP write combiners.
// If not VertexProgram (NB: Suppose BTB kill this test).
if( !CLandscapeGlobals::VertexProgramEnabled )
{
// Set Pos. Set it local to the current center of landscape
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentTileVBInfo.Accessor, CurVBPtr, sizeof(CVector))
*(CVector*)CurVBPtr= pVert->Src->Pos - CLandscapeGlobals::PZBModelPosition;
// Set Uvs.
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentTileVBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.TexCoordOff0, sizeof(CUV))
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.TexCoordOff0)= pVert->PUv0;
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentTileVBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.TexCoordOff1, sizeof(CUV))
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.TexCoordOff1)= pVert->PUv1;
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentTileVBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.TexCoordOff2, sizeof(CUV))
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.TexCoordOff2)= pVert->PUv2;
}
else
{
// Else must setup Vertex program inputs
// v[0]== StartPos.
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentTileVBInfo.Accessor, CurVBPtr, sizeof(CVector))
*(CVector*)CurVBPtr= pVert->Src->StartPos;
// v[8]== Tex0
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentTileVBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.TexCoordOff0, sizeof(CUV))
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.TexCoordOff0)= pVert->PUv0;
// v[9]== Tex1
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentTileVBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.TexCoordOff1, sizeof(CUV))
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.TexCoordOff1)= pVert->PUv1;
// v[12]== Tex2
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentTileVBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.TexCoordOff2, sizeof(CUV))
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.TexCoordOff2)= pVert->PUv2;
// v[10]== GeomInfo.
static CUV geomInfo;
geomInfo.U= pVert->Src->MaxFaceSize * CLandscapeGlobals::OORefineThreshold;
geomInfo.V= pVert->Src->MaxNearLimit;
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentTileVBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.GeomInfoOff, sizeof(CUV))
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.GeomInfoOff)= geomInfo;
// v[11]== EndPos - StartPos
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentTileVBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.DeltaPosOff, sizeof(CVector))
*(CVector*)(CurVBPtr + CLandscapeGlobals::CurrentTileVBInfo.DeltaPosOff)=
pVert->Src->EndPos - pVert->Src->StartPos;
}
}
// ***************************************************************************
void CPatch::fillFar0VertexListVB(CTessList &vertList)
{
// Traverse the vertList.
CTessFarVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessFarVertex*)pVert->Next)
{
fillFar0VertexVB(pVert);
}
}
// ***************************************************************************
void CPatch::fillFar1VertexListVB(CTessList &vertList)
{
// Traverse the vertList.
CTessFarVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessFarVertex*)pVert->Next)
{
fillFar1VertexVB(pVert);
}
}
// ***************************************************************************
void CPatch::fillTileVertexListVB(CTessList &vertList)
{
// Traverse the vertList.
CTessNearVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessNearVertex*)pVert->Next)
{
fillTileVertexVB(pVert);
}
}
// ***************************************************************************
void CPatch::fillVB()
{
// Fill Far0.
//=======
// fill only if no reallcoation occurs
if(Far0>0 && !CLandscapeGlobals::CurrentFar0VBAllocator->reallocationOccurs() )
{
// Fill Far0 VB.
fillFar0VertexListVB(MasterBlock.FarVertexList);
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// fill only if tblock visible.
if( tblock.visibleFar0() )
fillFar0VertexListVB(tblock.FarVertexList);
}
}
else if(Far0==0 && !CLandscapeGlobals::CurrentTileVBAllocator->reallocationOccurs() )
{
// Fill Tile VB.
// No Tiles in MasterBlock!!
// Traverse the TessBlocks to add vertices.
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// fill only if tblock visible.
if( tblock.visibleTile() )
fillTileVertexListVB(tblock.NearVertexList);
}
}
// Fill Far1.
//=======
if(Far1>0 && !CLandscapeGlobals::CurrentFar1VBAllocator->reallocationOccurs() )
{
// Fill VB.
fillFar1VertexListVB(MasterBlock.FarVertexList);
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// fill only if tblock visible.
if( tblock.visibleFar1() )
fillFar1VertexListVB(tblock.FarVertexList);
}
}
}
// ***************************************************************************
void CPatch::fillVBIfVisible()
{
if(isRenderClipped()==false)
fillVB();
}
// ***************************************************************************
void CPatch::fillVBFar0Only()
{
if(Far0>0 && !CLandscapeGlobals::CurrentFar0VBAllocator->reallocationOccurs() )
{
// Fill Far0 VB.
fillFar0VertexListVB(MasterBlock.FarVertexList);
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// fill only if tblock visible.
if( tblock.visibleFar0() )
fillFar0VertexListVB(tblock.FarVertexList);
}
}
}
// ***************************************************************************
void CPatch::fillVBFar1Only()
{
if(Far1>0 && !CLandscapeGlobals::CurrentFar1VBAllocator->reallocationOccurs() )
{
// Fill VB.
fillFar1VertexListVB(MasterBlock.FarVertexList);
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// fill only if tblock visible.
if( tblock.visibleFar1() )
fillFar1VertexListVB(tblock.FarVertexList);
}
}
}
// ***************************************************************************
// ***************************************************************************
// VB Software Geomorph / Alpha.
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
void CPatch::computeGeomorphVertexList(CTessList &vertList)
{
// Traverse the vertList.
CTessFarVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessFarVertex*)pVert->Next)
{
// compute geomorph.
pVert->Src->computeGeomPos();
}
}
// ***************************************************************************
void CPatch::computeGeomorphFar0VertexListVB(CTessList &vertList)
{
// Traverse the vertList.
CTessFarVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessFarVertex*)pVert->Next)
{
static uint8 *CurVBPtr;
// Compute/build the new vertex.
CurVBPtr= (uint8*)CLandscapeGlobals::CurrentFar0VBInfo.VertexCoordPointer;
CurVBPtr+= pVert->Index0 * CLandscapeGlobals::CurrentFar0VBInfo.VertexSize;
// Set Geomorphed Position. Set it local to the current center of landscape
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar0VBInfo.Accessor, CurVBPtr, sizeof(CVector))
*(CVector*)CurVBPtr= pVert->Src->Pos - CLandscapeGlobals::PZBModelPosition;
}
}
// ***************************************************************************
void CPatch::computeGeomorphAlphaFar1VertexListVB(CTessList &vertList)
{
// Traverse the vertList.
CTessFarVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessFarVertex*)pVert->Next)
{
static uint8 *CurVBPtr;
// Compute/build the new vertex.
CurVBPtr= (uint8*)CLandscapeGlobals::CurrentFar1VBInfo.VertexCoordPointer;
CurVBPtr+= pVert->Index1 * CLandscapeGlobals::CurrentFar1VBInfo.VertexSize;
// NB: the filling order of data is important, for AGP write combiners.
// Set Geomorphed Position. Set it local to the current center of landscape
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar1VBInfo.Accessor, CurVBPtr, sizeof(CVector))
*(CVector*)CurVBPtr= pVert->Src->Pos - CLandscapeGlobals::PZBModelPosition;
// Set Alpha color.
static CRGBA col(255,255,255,255);
// For Far1, use alpha fro transition.
// Prefer Use Pos, because of caching. So little difference between Soft and VertexProgram mode.
float f= (pVert->Src->Pos - CLandscapeGlobals::RefineCenter).sqrnorm();
f= (f-TransitionSqrMin) * OOTransitionSqrDelta;
clamp(f,0,1);
col.A= (uint8)(f*255);
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar1VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.ColorOff, sizeof(CRGBA))
*(CRGBA*)(CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.ColorOff)= col;
}
}
// ***************************************************************************
void CPatch::computeGeomorphTileVertexListVB(CTessList &vertList)
{
// Traverse the vertList.
CTessNearVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessNearVertex*)pVert->Next)
{
static uint8 *CurVBPtr;
// Compute/build the new vertex.
CurVBPtr= (uint8*)CLandscapeGlobals::CurrentTileVBInfo.VertexCoordPointer;
CurVBPtr+= pVert->Index * CLandscapeGlobals::CurrentTileVBInfo.VertexSize;
// Set Geomorphed Position. Set it local to the current center of landscape
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentTileVBInfo.Accessor, CurVBPtr, sizeof(CVector))
*(CVector*)CurVBPtr= pVert->Src->Pos - CLandscapeGlobals::PZBModelPosition;
}
}
// ***************************************************************************
void CPatch::computeSoftwareGeomorphAndAlpha()
{
if(isRenderClipped())
return;
// Compute Geomorph.
//=======
// Need only to fill CTessVertex, so do it only for FarVertices
// Hence Geomorph is done twice on edges of patches!!.
// If not too near for precise, fast compute of geomorph.
if( TessBlocks.empty() )
{
// Just update all vertices of master block.
computeGeomorphVertexList(MasterBlock.FarVertexList);
}
else
{
// update all vertices of master block.
computeGeomorphVertexList(MasterBlock.FarVertexList);
// update vertices of others block.
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// Precise Clip.
if(!tblock.getClipped())
{
// compute the geomorph of the vertices in the tessblock.
computeGeomorphVertexList(tblock.FarVertexList);
}
}
}
// Fill Far0.
//=======
if(Far0>0)
{
// Fill Far0 VB.
computeGeomorphFar0VertexListVB(MasterBlock.FarVertexList);
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// Precise Clip.
if( tblock.visibleFar0() )
computeGeomorphFar0VertexListVB(tblock.FarVertexList);
}
}
else if(Far0==0)
{
// Fill Tile VB.
// No Tiles in MasterBlock!!
// Traverse the TessBlocks to compute vertices.
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// Precise Clip.
if( tblock.visibleTile() )
computeGeomorphTileVertexListVB(tblock.NearVertexList);
}
}
// Fill Far1.
//=======
if(Far1>0)
{
// Fill VB.
computeGeomorphAlphaFar1VertexListVB(MasterBlock.FarVertexList);
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// Precise Clip.
if( tblock.visibleFar1() )
computeGeomorphAlphaFar1VertexListVB(tblock.FarVertexList);
}
}
}
// ***************************************************************************
// ***************************************************************************
// VB clip Allocate/Filling.
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
void CPatch::updateClipPatchVB(bool renderClipped)
{
// If now the patch is invisible
if(renderClipped)
{
// Then delete vertices.
deleteVBAndFaceVector();
// Now, all vertices in VB are deleted.
// Force clip state of all TessBlocks, so no allocation will be done on Vertices in VB.
if(!TessBlocks.empty())
{
for(uint i=0; i0 because vertices are reallocated in preRender() if a change of Far occurs.
if(!isRenderClipped() && Far0>0 && pVert->OwnerBlock->visibleFar0() )
{
pVert->Index0= CLandscapeGlobals::CurrentFar0VBAllocator->allocateVertex();
}
// Idem for Far1
if(!isRenderClipped() && Far1>0 && pVert->OwnerBlock->visibleFar1())
{
pVert->Index1= CLandscapeGlobals::CurrentFar1VBAllocator->allocateVertex();
}
}
// ***************************************************************************
void CPatch::checkFillVertexVBFar(CTessFarVertex *pVert)
{
nlassert(pVert);
// If visible, and Far0 in !Tile Mode, try to fill.
// NB: must test Far0>0 because vertices are reallocated in preRender() if a change of Far occurs.
if(!isRenderClipped() && Far0>0 && pVert->OwnerBlock->visibleFar0())
{
if( !CLandscapeGlobals::CurrentFar0VBAllocator->reallocationOccurs() )
fillFar0VertexVB(pVert);
}
// Idem for Far1
if(!isRenderClipped() && Far1>0 && pVert->OwnerBlock->visibleFar1())
{
if( !CLandscapeGlobals::CurrentFar1VBAllocator->reallocationOccurs() )
fillFar1VertexVB(pVert);
}
}
// ***************************************************************************
void CPatch::checkCreateVertexVBNear(CTessNearVertex *pVert)
{
nlassert(pVert);
// If visible, and Far0 in Tile Mode, allocate.
// NB: must test Far0==0 because vertices are reallocated in preRender() if a change of Far occurs.
if(!isRenderClipped() && Far0==0 && pVert->OwnerBlock->visibleTile())
{
pVert->Index= CLandscapeGlobals::CurrentTileVBAllocator->allocateVertex();
}
}
// ***************************************************************************
void CPatch::checkFillVertexVBNear(CTessNearVertex *pVert)
{
nlassert(pVert);
// If visible, and Far0 in Tile Mode, try to fill.
// NB: must test Far0==0 because vertices are reallocated in preRender() if a change of Far occurs.
if(!isRenderClipped()&& Far0==0 && pVert->OwnerBlock->visibleTile() )
{
// try to fill.
if( !CLandscapeGlobals::CurrentTileVBAllocator->reallocationOccurs() )
fillTileVertexVB(pVert);
}
}
// ***************************************************************************
void CPatch::checkDeleteVertexVBFar(CTessFarVertex *pVert)
{
nlassert(pVert);
// If visible, and Far0 in !Tile Mode, ok, the vertex exist in VB, so delete.
// NB: must test Far0>0 because vertices are deleted in preRender() if a change of Far occurs.
if(!isRenderClipped() && Far0>0 && pVert->OwnerBlock->visibleFar0() )
{
CLandscapeGlobals::CurrentFar0VBAllocator->deleteVertex(pVert->Index0);
}
// Idem for Far1
if(!isRenderClipped() && Far1>0 && pVert->OwnerBlock->visibleFar1() )
{
CLandscapeGlobals::CurrentFar1VBAllocator->deleteVertex(pVert->Index1);
}
}
// ***************************************************************************
void CPatch::checkDeleteVertexVBNear(CTessNearVertex *pVert)
{
nlassert(pVert);
// If visible, and Far0 in Tile Mode, ok, the vertex exist in VB, so delete.
// NB: must test Far0==0 because vertices are deleted in preRender() if a change of Far occurs.
if(!isRenderClipped() && Far0==0 && pVert->OwnerBlock->visibleTile() )
{
CLandscapeGlobals::CurrentTileVBAllocator->deleteVertex(pVert->Index);
}
}
// ***************************************************************************
// ***************************************************************************
// VB DLM Filling
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
void CPatch::fillFar0DLMUvOnlyVertexListVB(CTessList &vertList)
{
// The Buffers must have been locked
nlassert(CLandscapeGlobals::CurrentFar0VBAllocator);
nlassert(CLandscapeGlobals::CurrentFar0VBAllocator->bufferLocked());
// VBInfo must be OK.
nlassert(!CLandscapeGlobals::CurrentFar0VBAllocator->reallocationOccurs());
static uint8 *CurVBPtr;
static CUV uvDLM;
// If the DLMContext exist
if(_DLMContext)
{
// Traverse the vertList, to compute new uvDLM
CTessFarVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessFarVertex*)pVert->Next)
{
// Compute/build the new vertex.
CurVBPtr= (uint8*)CLandscapeGlobals::CurrentFar0VBInfo.VertexCoordPointer;
CurVBPtr+= pVert->Index0 * CLandscapeGlobals::CurrentFar0VBInfo.VertexSize;
// compute Uvs.
CParamCoord pc= pVert->PCoord;
// compute Dynamic lightmap Uv with DLM context info.
uvDLM.U= pc.getS()* _DLMContext->DLMUScale + _DLMContext->DLMUBias;
uvDLM.V= pc.getT()* _DLMContext->DLMVScale + _DLMContext->DLMVBias;
// Set Uv DLM only (NB: same code for VertexProgram or not, only TexCoordOff1 may change).
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar0VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.TexCoordOff1, sizeof(CUV))
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.TexCoordOff1)= uvDLM;
}
}
// else, reset all Uvs
else
{
// just set UV so the vertex point to a black pixel (see CTextureDLM).
uvDLM.U= 1;
uvDLM.V= 1;
// Traverse the vertList, to reset uv
CTessFarVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessFarVertex*)pVert->Next)
{
// Compute/build the new vertex.
CurVBPtr= (uint8*)CLandscapeGlobals::CurrentFar0VBInfo.VertexCoordPointer;
CurVBPtr+= pVert->Index0 * CLandscapeGlobals::CurrentFar0VBInfo.VertexSize;
// Set Uv DLM only (NB: same code for VertexProgram or not, only TexCoordOff1 may change).
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar0VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.TexCoordOff1, sizeof(CUV))
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar0VBInfo.TexCoordOff1)= uvDLM;
}
}
}
// ***************************************************************************
void CPatch::fillFar1DLMUvOnlyVertexListVB(CTessList &vertList)
{
// The Buffers must have been locked
nlassert(CLandscapeGlobals::CurrentFar1VBAllocator);
nlassert(CLandscapeGlobals::CurrentFar1VBAllocator->bufferLocked());
// VBInfo must be OK.
nlassert(!CLandscapeGlobals::CurrentFar1VBAllocator->reallocationOccurs());
static uint8 *CurVBPtr;
static CUV uvDLM;
// If the DLMContext exist
if(_DLMContext)
{
// Traverse the vertList, to compute new uvDLM
CTessFarVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessFarVertex*)pVert->Next)
{
// Compute/build the new vertex.
CurVBPtr= (uint8*)CLandscapeGlobals::CurrentFar1VBInfo.VertexCoordPointer;
CurVBPtr+= pVert->Index1 * CLandscapeGlobals::CurrentFar1VBInfo.VertexSize;
// compute Uvs.
CParamCoord pc= pVert->PCoord;
// compute Dynamic lightmap Uv with DLM context info.
uvDLM.U= pc.getS()* _DLMContext->DLMUScale + _DLMContext->DLMUBias;
uvDLM.V= pc.getT()* _DLMContext->DLMVScale + _DLMContext->DLMVBias;
// Set Uv DLM only (NB: same code for VertexProgram or not, only TexCoordOff1 may change).
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar1VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.TexCoordOff1, sizeof(CUV))
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.TexCoordOff1)= uvDLM;
}
}
// else, reset all Uvs
else
{
// just set UV so the vertex point to a black pixel (see CTextureDLM).
uvDLM.U= 1;
uvDLM.V= 1;
// Traverse the vertList, to reset uv
CTessFarVertex *pVert;
for(pVert= vertList.begin(); pVert; pVert= (CTessFarVertex*)pVert->Next)
{
// Compute/build the new vertex.
CurVBPtr= (uint8*)CLandscapeGlobals::CurrentFar1VBInfo.VertexCoordPointer;
CurVBPtr+= pVert->Index1 * CLandscapeGlobals::CurrentFar1VBInfo.VertexSize;
// Set Uv DLM only (NB: same code for VertexProgram or not, only TexCoordOff1 may change).
CHECK_VBA_RANGE(CLandscapeGlobals::CurrentFar1VBInfo.Accessor, CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.TexCoordOff1, sizeof(CUV))
*(CUV*)(CurVBPtr + CLandscapeGlobals::CurrentFar1VBInfo.TexCoordOff1)= uvDLM;
}
}
}
// ***************************************************************************
void CPatch::fillVBFarsDLMUvOnly()
{
// Do it for Far0.
if(Far0>0 && !CLandscapeGlobals::CurrentFar0VBAllocator->reallocationOccurs() )
{
// Fill Far0 VB.
fillFar0DLMUvOnlyVertexListVB(MasterBlock.FarVertexList);
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// fill only if tblock visible.
if( tblock.visibleFar0() )
fillFar0DLMUvOnlyVertexListVB(tblock.FarVertexList);
}
}
// Do it for Far1.
if(Far1>0 && !CLandscapeGlobals::CurrentFar1VBAllocator->reallocationOccurs() )
{
// Fill VB.
fillFar1DLMUvOnlyVertexListVB(MasterBlock.FarVertexList);
for(sint i=0; i<(sint)TessBlocks.size(); i++)
{
CTessBlock &tblock= TessBlocks[i];
// fill only if tblock visible.
if( tblock.visibleFar1() )
fillFar1DLMUvOnlyVertexListVB(tblock.FarVertexList);
}
}
}
} // NL3D