// 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/landscapevb_allocator.h"
#include "nel/3d/driver.h"
#include "nel/misc/fast_mem.h"
using namespace std;
using namespace NLMISC;
namespace NL3D
{
/*
Once a reallocation of a VBHard occurs, how many vertices we add to the re-allocation, to avoid
as possible reallocations.
*/
#define NL3D_LANDSCAPE_VERTEX_ALLOCATE_SECURITY 1024
/*
The start size of the array.
*/
#define NL3D_LANDSCAPE_VERTEX_ALLOCATE_START 4048
#define NL3D_VERTEX_FREE_MEMORY_RESERVE 1024
/*// 65000 is a maximum because of GeForce limitations.
#define NL3D_VERTEX_MAX_VERTEX_VBHARD 40000*/
// ***************************************************************************
CLandscapeVBAllocator::CLandscapeVBAllocator(TType type, const std::string &vbName)
{
_Type= type;
_VBName= vbName;
_VertexFreeMemory.reserve(NL3D_VERTEX_FREE_MEMORY_RESERVE);
_ReallocationOccur= false;
_NumVerticesAllocated= 0;
_BufferLocked= false;
_LastFarVB = NULL;
_LastNearVB = NULL;
for(uint i=0;iisVertexProgramSupported() && !_Driver->isVertexProgramEmulated());
// must reallocate the VertexBuffer.
if( _NumVerticesAllocated>0 )
allocateVertexBuffer(_NumVerticesAllocated);
}
}
// ***************************************************************************
void CLandscapeVBAllocator::clear()
{
// clear list.
_VertexFreeMemory.clear();
_NumVerticesAllocated= 0;
// delete the VB.
deleteVertexBuffer();
// delete vertex Program, if any
deleteVertexProgram();
// clear other states.
_ReallocationOccur= false;
_Driver= NULL;
}
// ***************************************************************************
void CLandscapeVBAllocator::resetReallocation()
{
_ReallocationOccur= false;
}
// ***************************************************************************
// ***************************************************************************
// allocation.
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
uint CLandscapeVBAllocator::allocateVertex()
{
// if no more free, allocate.
if( _VertexFreeMemory.size()==0 )
{
// enlarge capacity.
uint newResize;
if(_NumVerticesAllocated==0)
newResize= NL3D_LANDSCAPE_VERTEX_ALLOCATE_START;
else
newResize= NL3D_LANDSCAPE_VERTEX_ALLOCATE_SECURITY;
_NumVerticesAllocated+= newResize;
// re-allocate VB.
#ifdef NL_LANDSCAPE_INDEX16
nlassert(_NumVerticesAllocated <= 65535);
#endif
allocateVertexBuffer(_NumVerticesAllocated);
// resize infos on vertices.
_VertexInfos.resize(_NumVerticesAllocated);
// Fill list of free elements.
for(uint i=0;isetupNullPointers();
_LastFarVB = NULL;
if (_LastNearVB)
_LastNearVB->setupNullPointers();
_LastNearVB = NULL;
_BufferLocked= false;
}
}
// ***************************************************************************
// ***************************************************************************
// VertexBuffer mgt.
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
void CLandscapeVBAllocator::activate(uint vpId)
{
nlassert(_Driver);
nlassert(!_BufferLocked);
// If enabled, activate Vertex program first.
if(_VertexProgram[vpId])
{
//nlinfo("\nSTARTVP\n%s\nENDVP\n", _VertexProgram[vpId]->getProgram().c_str());
nlverify(_Driver->activeVertexProgram(_VertexProgram[vpId]));
}
_Driver->activeVertexBuffer(_VB);
}
// ***************************************************************************
void CLandscapeVBAllocator::deleteVertexBuffer()
{
// must unlock VBhard before.
unlockBuffer();
// delete the soft one.
_VB.deleteAllVertices();
}
// ***************************************************************************
void CLandscapeVBAllocator::allocateVertexBuffer(uint32 numVertices)
{
// no allocation must be done if the Driver is not setuped, or if the driver has been deleted by refPtr.
nlassert(_Driver);
// allocate() =>_ReallocationOccur= true;
_ReallocationOccur= true;
// must unlock VBhard before.
unlockBuffer();
// This always works.
_VB.setPreferredMemory(CVertexBuffer::AGPPreferred, false);
_VB.setNumVertices(numVertices);
_VB.setName (_VBName);
}
// ***************************************************************************
// ***************************************************************************
// Vertex Program.
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
/*
Common Part. Inputs and constants.
Inputs
--------
Standard:
v[0] == StartPos. Hence, It is the SplitPoint of the father face.
v[8] == Tex0 (xy)
v[9] == Tex1 (xy) (different meanings for Far and Tile).
v[13] == Tex2 (xy) (just for Tile mode).
Geomorph:
v[10] == { GeomFactor, MaxNearLimit }
* where GeomFactor == max(SizeFaceA, SizeFaceB) * OORefineThreshold.
It's means vertices are re-computed when the RefineThreshold setup change.
* MaxNearLimit= max(nearLimitFaceA, nearLimitFaceB)
v[11].xyz == EndPos-StartPos
Alpha: NB: Since only useful for Far1, v[12] is not in the VB for Far0 and Tile VertexBuffer.
v[12] == { TransitionSqrMin, OOTransitionSqrDelta}
* TransitionSqrMin, OOTransitionSqrDelta : Alpha transition, see preRender().
There is only 3 values possibles. It depends on Far1 type. Changed in preRender()
Constant:
--------
Setuped at beginning of CLandscape::render()
c[0..3]= ModelViewProjection Matrix.
c[4]= {0, 1, 0.5, 0}
c[5]= RefineCenter
c[6]= {TileDistFarSqr, OOTileDistDeltaSqr, *, *}
c[7]= ???
c[8..11]= ModelView Matrix (for Fog).
c[12]= PZBModelPosition: landscape center / delta Position to apply before multipliying by mviewMatrix
Fog Note:
-----------
Fog is computed on geomorphed position R1.
R1.w==1, and suppose that ModelViewMatrix has no Projection Part.
Then Homogenous-coordinate == Non-Homogenous-coordinate.
Hence we need only (FogVector*R1).z to get the FogC value.
=> computed in just on instruction.
*/
// ***********************
/*
Common start of the program for Far0, Far1 and Tile mode.
It compute the good Geomorphed position.
At the end of this program, nothing is written in the output register, and we have in the Temp Registers:
- R0= scratch
- R1= CurrentPos geomorphed
- R2.x= sqrDist= (startPos - RefineCenter).sqrnorm(). Useful for alpha computing.
Pgr Len= 18.
NB: 9 ope for normal errorMetric, and 9 ope for smoothing with TileNear.
The C code for this Program is: (v[] means data is a vertex input, c[] means it is a constant)
{
// Compute Basic ErrorMetric.
sqrDist= (v[StartPos] - c[RefineCenter]).sqrnorm()
ErrorMetric= v[GeomFactor] / sqrDist
// Compute ErrorMetric modified by TileNear transition.
f= (c[TileDistFarSqr] - sqrDist) * c[OOTileDistDeltaSqr]
clamp(f, 0, 1);
// ^4 gives better smooth result
f= sqr(f); f= sqr(f);
// interpolate the errorMetric
ErrorMetricModified= v[MaxNearLimit]*f + ErrorMetric*(1-f);
// Take the max errorMetric.
ErrorMetric= max(ErrorMetric, ErrorMetricModified);
// Interpolate StartPos to EndPos, between 1 and 2.
f= ErrorMetric - 1;
clamp(f, 0, 1);
R1= f * v[EndPos-StartPos] + StartPos;
}
*/
const char* NL3D_LandscapeCommonStartProgram=
"!!VP1.0 \n\
# compute Basic geomorph into R0.x \n\
ADD R0, v[0], -c[5]; # R0 = startPos - RefineCenter \n\
DP3 R2.x, R0, R0; # R2.x= sqrDist= (startPos - RefineCenter).sqrnorm()\n\
RCP R0.x, R2.x; # R0.x= 1 / sqrDist \n\
MUL R0.x, v[10].x, R0.x; # R0.x= ErrorMetric= GeomFactor / sqrDist \n\
\n\
# compute Transition Factor To TileNear Geomorph, into R0.z \n\
ADD R0.z, c[6].x, -R2.x; # R0.z= TileDistFarSqr - sqrDist \n\
MUL R0.z, R0.z, c[6].y; # R0.z= f= (TileDistFarSqr - sqrDist ) * OOTileDistDeltaSqr \n\
MAX R0.z, R0.z, c[4].x; \n\
MIN R0.z, R0.z, c[4].y; # R0.z= f= clamp(f, 0, 1); \n\
MUL R0.z, R0.z, R0.z; \n\
MUL R0.z, R0.z, R0.z; # R0.z= finalFactor= f^4 \n\
\n\
# Apply the transition factor to the ErrorMetric => R0.w= ErrorMetricModified. \n\
ADD R0.w, v[10].y, -R0.x; # R0.w= maxNearLimit - ErrorMetric \n\
MAD R0.w, R0.z, R0.w, R0.x; # R0.w= finalFactor * (maxNearLimit - ErrorMetric) + ErrorMetric \n\
\n\
# R0.w may be < R0.x; (when the point is very near). Must take the bigger errorMetric. \n\
MAX R0.x, R0.x, R0.w; # R0.x= ErrorMetric Max \n\
\n\
# apply geomorph into R1 \n\
ADD R0.x, R0.x, -c[4].y; # R0.x= ErrorMetric Max - 1 \n\
MAX R0.x, R0.x, c[4].x; \n\
MIN R0.x, R0.x, c[4].y; # R0.x= geomBlend= clamp(R0.x, 0, 1); \n\
\n\
# NB: Can't use MAD R1.xyz, v[11], R0.x, v[0], because can't acces 2 inputs in one op. \n\
# Hence, can use a MAD to Sub the Landscape Center _PZBModelPosition \n\
# write to R1.w is useless (but needed to avoid a read error when multiplied by 0) \n\
# in the next instruction \n\
MAD R1.xyzw, v[11], R0.x, -c[12]; \n\
# set w to 1 by using c[4] = { 0, 1, 0.5, 0} \n\
MAD R1, R1, c[4].yyyx, v[0]; # R1= geomBlend * (EndPos-StartPos) + StartPos \n\
";
// ***********************
// Test Speed.
/*"!!VP1.0 \n\
# compute Basic geomorph into R0.x \n\
ADD R0, v[0], -c[5]; # R0 = startPos - RefineCenter \n\
DP3 R2.x, R0, R0; # R2.x= sqrDist= (startPos - RefineCenter).sqrnorm()\n\
MOV R1, v[0]; # R1= geomBlend * (EndPos-StartPos) + StartPos \n\
";
*/
const string NL3D_LandscapeTestSpeedProgram=
" MOV R1, R1; \n MOV R1, R1; \n MOV R1, R1; \n MOV R1, R1; \n MOV R1, R1; \n\
MOV R1, R1; \n MOV R1, R1; \n MOV R1, R1; \n MOV R1, R1; \n MOV R1, R1; \n\
";
// ***********************
/*
Far0:
just project, copy uv0 and uv1
NB: leave o[COL0] undefined because the material don't care diffuse RGBA here
*/
// ***********************
const char* NL3D_LandscapeFar0EndProgram=
" # compute in Projection space \n\
DP4 o[HPOS].x, c[0], R1; \n\
DP4 o[HPOS].y, c[1], R1; \n\
DP4 o[HPOS].z, c[2], R1; \n\
DP4 o[HPOS].w, c[3], R1; \n\
MOV o[TEX0], v[8]; \n\
MOV o[TEX1], v[9]; \n\
DP4 o[FOGC].x, c[10], R1; \n\
END \n\
";
// ***********************
/*
Far1:
Compute Alpha transition.
Project, copy uv0 and uv1,
NB: leave o[COL0] RGB undefined because the material don't care diffuse RGB
*/
// ***********************
const char* NL3D_LandscapeFar1EndProgram=
" # compute Alpha Transition \n\
ADD R0.x, R2.x, -v[12].x; # R0.x= sqrDist-TransitionSqrMin \n\
MUL R0.x, R0.x, v[12].y; # R0.x= (sqrDist-TransitionSqrMin) * OOTransitionSqrDelta \n\
MAX R0.x, R0.x, c[4].x; \n\
MIN o[COL0].w, R0.x, c[4].y; # col.A= clamp(R0.x, 0, 1); \n\
\n\
# compute in Projection space \n\
DP4 o[HPOS].x, c[0], R1; \n\
DP4 o[HPOS].y, c[1], R1; \n\
DP4 o[HPOS].z, c[2], R1; \n\
DP4 o[HPOS].w, c[3], R1; \n\
MOV o[TEX0], v[8]; \n\
MOV o[TEX1], v[9]; \n\
DP4 o[FOGC].x, c[10], R1; \n\
END \n\
";
// ***********************
/*
Tile:
just project, copy uv0, uv1.
NB: leave o[COL0] undefined because the material don't care diffuse RGBA here
*/
// ***********************
const char* NL3D_LandscapeTileEndProgram=
" # compute in Projection space \n\
DP4 o[HPOS].x, c[0], R1; \n\
DP4 o[HPOS].y, c[1], R1; \n\
DP4 o[HPOS].z, c[2], R1; \n\
DP4 o[HPOS].w, c[3], R1; \n\
MOV o[TEX0], v[8]; \n\
MOV o[TEX1], v[9]; \n\
DP4 o[FOGC].x, c[10], R1; \n\
END \n\
";
/// Same version but write Tex0 to take uv2, ie v[13], for lightmap pass
const char* NL3D_LandscapeTileLightMapEndProgram=
" # compute in Projection space \n\
DP4 o[HPOS].x, c[0], R1; \n\
DP4 o[HPOS].y, c[1], R1; \n\
DP4 o[HPOS].z, c[2], R1; \n\
DP4 o[HPOS].w, c[3], R1; \n\
MOV o[TEX0], v[12]; \n\
MOV o[TEX1], v[9]; \n\
DP4 o[FOGC].x, c[10], R1; \n\
END \n\
";
// ***************************************************************************
void CLandscapeVBAllocator::deleteVertexProgram()
{
for(uint i=0;i