// 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/vertex_buffer.h"
#include "nel/misc/vector.h"
#include "nel/misc/fast_mem.h"
#include "nel/3d/driver.h"
using namespace NLMISC;
namespace NL3D
{
// --------------------------------------------------
const uint CVertexBuffer::SizeType[NumType]=
{
1*sizeof(double),
1*sizeof(float),
1*sizeof(short),
2*sizeof(double),
2*sizeof(float),
2*sizeof(short),
3*sizeof(double),
3*sizeof(float),
3*sizeof(short),
4*sizeof(double),
4*sizeof(float),
4*sizeof(short),
4*sizeof(char),
};
const uint CVertexBuffer::NumComponentsType[NumType] =
{
1,
1,
1,
2,
2,
2,
3,
3,
3,
4,
4,
4,
4
};
// --------------------------------------------------
const CVertexBuffer::TType CVertexBuffer::DefaultValueType[NumValue]=
{
Float3, // Position
Float3, // Normal
Float2, // TexCoord0
Float2, // TexCoord1
Float2, // TexCoord2
Float2, // TexCoord3
Float2, // TexCoord4
Float2, // TexCoord5
Float2, // TexCoord6
Float2, // TexCoord7
UChar4, // Primary color
UChar4, // Secondary color
Float4, // 4 Weights
UChar4, // PaletteSkin
Float1, // Fog
Float1, // Empty
};
// --------------------------------------------------
void CVertexBuffer::construct()
{
/* ***********************************************
* WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
* It can be loaded/called through CAsyncFileManager for instance
* ***********************************************/
_Flags = 0;
_Capacity = 0;
_NbVerts = 0;
_InternalFlags = 0;
_VertexSize = 0;
_VertexColorFormat = TRGBA;
_LockCounter = 0;
_LockedBuffer = NULL;
_PreferredMemory = RAMPreferred;
_Location = NotResident;
_ResidentSize = 0;
_KeepLocalMemory = false;
// Default routing
uint i;
for (i=0; iVertexBufferPtr = NULL; // Tell the driver info to not restore memory when it will die
// Must kill the drv mirror of this VB.
DrvInfos.kill();
}
// --------------------------------------------------
CVertexBuffer &CVertexBuffer::operator=(const CVertexBuffer &vb)
{
nlassertex (!isLocked(), ("The vertex buffer is locked."));
nlassertex (!vb.isLocked(), ("Source buffer is locked."));
// Single value
_VertexSize = vb._VertexSize;
_Flags = vb._Flags;
_InternalFlags = vb._InternalFlags;
_NbVerts = vb._NbVerts;
_Capacity = vb._Capacity;
_NonResidentVertices = vb._NonResidentVertices;
_VertexColorFormat = vb._VertexColorFormat;
_PreferredMemory = vb._PreferredMemory;
_KeepLocalMemory = vb._KeepLocalMemory;
uint i;
_LockCounter = 0;
_LockedBuffer = NULL;
// Arraies
for (uint value=0; value(this)->lock(srcDatas);
nlassert(dest.getLocation() == NotResident);
CVertexBufferReadWrite destDatas;
dest.lock(destDatas); // will be in vram
NLMISC::CFastMem::memcpy (destDatas.getVertexCoordPointer(), srcDatas.getVertexCoordPointer(), getVertexSize() * getNumVertices());
}
// --------------------------------------------------
bool CVertexBuffer::setVertexFormat(uint32 flags)
{
nlassertex (!isLocked(), ("The vertex buffer is locked."));
uint i;
// Clear extended values
clearValueEx ();
// Position ?
if (flags & PositionFlag)
{
// Add a standard position value
addValueEx (Position, Float3);
}
// Normal ?
if (flags & NormalFlag)
{
// Add a standard normal value
addValueEx (Normal, Float3);
}
// For each uv values
for(i=0 ; i= 2); break;
case Normal: nlassert(numComp == 3); break;
case PrimaryColor: nlassert(numComp == 4); break;
case SecondaryColor: nlassert(numComp == 4); break;
case Weight: nlassert(numComp == 4); break;
case PaletteSkin: nlassert(numComp == 4); break;
case Fog: nlassert(numComp == 4); break;
default: break;
}
}
// --------------------------------------------------
bool CVertexBuffer::hasValueEx(TValue valueId) const
{
return (_Flags & (1 << valueId)) != 0;
}
// --------------------------------------------------
void CVertexBuffer::initEx ()
{
nlassert (!isLocked());
// Calc vertex size and set value's offset
_VertexSize=0;
for (uint value=0; value=1) && ((_Flags & PaletteSkinFlag) == CVertexBuffer::PaletteSkinFlag) )
{
CPaletteSkin &ps= *(CPaletteSkin*)(pointer + stridedId + _Offset[PaletteSkin]);
f.serial(ps);
}
}
// Set touch flags
_InternalFlags = 0;
if(f.isReading())
{
// Force non resident
restoreNonResidentMemory();
}
}
// --------------------------------------------------
void CVertexBuffer::serial(NLMISC::IStream &f)
{
/* ***********************************************
* WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
* It can be loaded/called through CAsyncFileManager for instance
* ***********************************************/
/*
Version 2:
- cut to use serialHeader() serialSubset().
Version 1:
- PaletteSkin version.
Version 0:
- base verison.
*/
nlassert (!isLocked());
sint ver= f.serialVersion(2);
if (ver<2)
{
// old serial method
serialOldV1Minus(f, ver);
}
else
{
// read write the header of the VBuffer.
serialHeader(f);
// read write the entire subset.
serialSubset(f, 0, _NbVerts);
}
}
// --------------------------------------------------
void CVertexBuffer::serialHeader(NLMISC::IStream &f)
{
/* ***********************************************
* WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
* It can be loaded/called through CAsyncFileManager for instance
* ***********************************************/
/*
Version 3:
- Preferred memory.
Version 2:
- Vertex color format management.
Version 1:
- Extended vertex format management.
Version 0:
- base verison of the header serialisation.
*/
sint ver= f.serialVersion(3); // Hulud
// Serial VBuffers format/size.
//=============================
// Flags
uint16 flags=_Flags;
if (ver<1)
{
// Must be reading
nlassert (f.isReading());
// Serial old flags
uint32 oldFlags;
f.serial(oldFlags);
// Remap flags
uint weightCount;
flags=remapV2Flags (oldFlags, weightCount);
// Set default value type
for (uint i=0; i=2)
f.serial (_VertexColorFormat);
if (ver>=3)
{
f.serialEnum(_PreferredMemory);
f.serial(_Name);
}
else
{
// Init preferred memory
if(f.isReading())
{
_PreferredMemory = RAMPreferred;
_Name = "";
}
}
}
// --------------------------------------------------
uint CVertexBuffer:: getNumTexCoordUsed() const
{
for (sint k = (MaxStage - 1); k >= 0; --k)
{
if (_Flags & (TexCoord0Flag << k)) return (uint) (k + 1);
}
return 0;
}
// --------------------------------------------------
uint8 CVertexBuffer::getNumWeight () const
{
// Num weight
switch (_Type[Weight])
{
case Float1:
return 1;
case Float2:
return 2;
case Float3:
return 3;
case Float4:
return 4;
}
// No weight
return 0;
}
// --------------------------------------------------
void CVertexBuffer::serialSubset(NLMISC::IStream &f, uint vertexStart, uint vertexEnd)
{
/* ***********************************************
* WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
* It can be loaded/called through CAsyncFileManager for instance
* ***********************************************/
/*
Version 2:
- UVRouting
Version 1:
- weight is 4 float in standard format.
Version 0:
- base verison of a vbuffer subset serialisation.
*/
sint ver = f.serialVersion(2);
// Serial VBuffers components.
//============================
nlassert(vertexStart<_NbVerts || _NbVerts==0);
nlassert(vertexEnd<=_NbVerts);
for(uint id=vertexStart; id=2)
{
f.serialBuffer (_UVRouting, sizeof(uint8)*MaxStage);
}
else
{
// Reset the table
uint i;
for (i=0; i>16);
#else // NL_LITTLE_ENDIAN
*ptr0 = (value&0x00ff00ff)|((value&0xff00)<<16)|((value&0xff000000)>>16);
#endif // NL_LITTLE_ENDIAN
ptr0 = (uint32*)(((uint8*)ptr0)+_VertexSize);
}
if (ptr1)
{
const register uint32 value = *ptr1;
#ifdef NL_LITTLE_ENDIAN
*ptr1 = (value&0xff00ff00)|((value&0xff)<<16)|((value&0xff0000)>>16);
#else // NL_LITTLE_ENDIAN
*ptr1 = (value&0x00ff00ff)|((value&0xff00)<<16)|((value&0xff000000)>>16);
#endif // NL_LITTLE_ENDIAN
ptr1 = (uint32*)(((uint8*)ptr1)+_VertexSize);
}
}
}
_VertexColorFormat = (uint8)format;
// Force non resident
restoreNonResidentMemory();
}
return true;
}
// --------------------------------------------------
void CVertexBuffer::setPreferredMemory (TPreferredMemory preferredMemory, bool keepLocalMemory)
{
if ((_PreferredMemory != preferredMemory) || (_KeepLocalMemory != keepLocalMemory))
{
_PreferredMemory = preferredMemory;
_KeepLocalMemory = keepLocalMemory;
// Force non resident
restoreNonResidentMemory();
}
}
// --------------------------------------------------
void CVertexBuffer::setLocation (TLocation newLocation)
{
// Upload ?
if (newLocation != NotResident)
{
// The driver must have setuped the driver info
nlassert (DrvInfos);
// Current size of the buffer
const uint size = ((_PreferredMemory==RAMVolatile)||(_PreferredMemory==AGPVolatile))?_NbVerts*_VertexSize:_Capacity*_VertexSize;
// The buffer must not be resident
if (_Location != NotResident)
setLocation (NotResident);
// Copy the buffer containt
uint8 *dest = DrvInfos->lock (0, size, false);
nlassert (size<=_NonResidentVertices.size()); // Internal buffer must have the good size
memcpy (dest, &(_NonResidentVertices[0]), size);
DrvInfos->unlock(0, 0);
// Reset the non resident container if not a static preferred memory and not put in RAM
if ((_PreferredMemory != StaticPreferred) && (_Location != RAMResident) && !_KeepLocalMemory)
contReset(_NonResidentVertices);
// Clear touched flags
resetTouchFlags ();
_Location = newLocation;
_ResidentSize = size;
}
else
{
// Current size of the buffer
const uint size = _Capacity*_VertexSize;
// Resize the non resident buffer
_NonResidentVertices.resize (size);
// If resident in RAM, backup the data in non resident memory
if ((_Location == RAMResident) && (_PreferredMemory != RAMVolatile) && (_PreferredMemory != AGPVolatile) && !_KeepLocalMemory)
{
// The driver must have setuped the driver info
nlassert (DrvInfos);
// Copy the old buffer data
const uint8 *src = DrvInfos->lock (0, _ResidentSize, true);
if (!_NonResidentVertices.empty())
memcpy (&(_NonResidentVertices[0]), src, std::min (size, (uint)_ResidentSize));
DrvInfos->unlock(0, 0);
}
_Location = NotResident;
_ResidentSize = 0;
// Touch the buffer
_InternalFlags |= TouchedAll;
}
}
// --------------------------------------------------
void CVertexBuffer::restoreNonResidentMemory()
{
setLocation (NotResident);
if (DrvInfos)
DrvInfos->VertexBufferPtr = NULL; // Tell the driver info to not restore memory when it will die
// Must kill the drv mirror of this VB.
DrvInfos.kill();
}
// --------------------------------------------------
void CVertexBuffer::fillBuffer ()
{
if (DrvInfos && _KeepLocalMemory)
{
// Copy the local memory in local memory
const uint size = _NbVerts*_VertexSize;
nlassert (size<=_NonResidentVertices.size());
uint8 *dest = DrvInfos->lock (0, size, false);
NLMISC::CFastMem::memcpy (dest, &(_NonResidentVertices[0]), size);
DrvInfos->unlock(0, size);
}
}
// --------------------------------------------------
// CPaletteSkin serial (no version chek).
void CPaletteSkin::serial(NLMISC::IStream &f)
{
f.serial(MatrixId[0], MatrixId[1], MatrixId[2], MatrixId[3]);
}
// --------------------------------------------------
IVBDrvInfos::~IVBDrvInfos()
{
_Driver->removeVBDrvInfoPtr(_DriverIterator);
}
// --------------------------------------------------
// CVertexBufferReadWrite
// --------------------------------------------------
NLMISC::CVector* CVertexBufferReadWrite::getVertexCoordPointer(uint idx)
{
nlassert (_Parent->checkLockedBuffer());
uint8* ptr;
ptr=_Parent->_LockedBuffer;
ptr+=(idx*_Parent->_VertexSize);
return((NLMISC::CVector*)ptr);
}
// --------------------------------------------------
NLMISC::CVector* CVertexBufferReadWrite::getNormalCoordPointer(uint idx)
{
nlassert (_Parent->checkLockedBuffer());
uint8* ptr;
if ( !(_Parent->_Flags & CVertexBuffer::NormalFlag) )
{
return(NULL);
}
ptr=_Parent->_LockedBuffer;
ptr+=_Parent->_Offset[CVertexBuffer::Normal];
ptr+=idx*_Parent->_VertexSize;
return((NLMISC::CVector*)ptr);
}
// --------------------------------------------------
void* CVertexBufferReadWrite::getColorPointer(uint idx)
{
nlassert (_Parent->checkLockedBuffer());
uint8* ptr;
if ( !(_Parent->_Flags & CVertexBuffer::PrimaryColorFlag) )
{
return(NULL);
}
ptr=_Parent->_LockedBuffer;
ptr+=_Parent->_Offset[CVertexBuffer::PrimaryColor];
ptr+=idx*_Parent->_VertexSize;
return((void*)ptr);
}
// --------------------------------------------------
void* CVertexBufferReadWrite::getSpecularPointer(uint idx)
{
nlassert (_Parent->checkLockedBuffer());
uint8* ptr;
if ( !(_Parent->_Flags & CVertexBuffer::SecondaryColorFlag) )
{
return(NULL);
}
ptr=_Parent->_LockedBuffer;
ptr+=_Parent->_Offset[CVertexBuffer::SecondaryColor];
ptr+=idx*_Parent->_VertexSize;
return((void*)ptr);
}
// --------------------------------------------------
NLMISC::CUV* CVertexBufferReadWrite::getTexCoordPointer(uint idx, uint8 stage)
{
nlassert (_Parent->checkLockedBuffer());
uint8* ptr;
if ( !(_Parent->_Flags & (CVertexBuffer::TexCoord0Flag<_LockedBuffer;
ptr+=_Parent->_Offset[CVertexBuffer::TexCoord0+stage];
ptr+=idx*_Parent->_VertexSize;
return((NLMISC::CUV*)ptr);
}
// --------------------------------------------------
float* CVertexBufferReadWrite::getWeightPointer(uint idx, uint8 wgt)
{
nlassert (_Parent->checkLockedBuffer());
uint8* ptr;
nlassert(wgt_Flags & CVertexBuffer::WeightFlag))
return NULL;
ptr=(uint8*)(&_Parent->_LockedBuffer[idx*_Parent->_VertexSize]);
ptr+=_Parent->_Offset[CVertexBuffer::Weight]+wgt*sizeof(float);
return (float*)ptr;
}
// --------------------------------------------------
CPaletteSkin* CVertexBufferReadWrite::getPaletteSkinPointer(uint idx)
{
nlassert (_Parent->checkLockedBuffer());
uint8* ptr;
if ( (_Parent->_Flags & CVertexBuffer::PaletteSkinFlag) != CVertexBuffer::PaletteSkinFlag )
{
return(NULL);
}
ptr=_Parent->_LockedBuffer;
ptr+=_Parent->_Offset[CVertexBuffer::PaletteSkin];
ptr+=idx*_Parent->_VertexSize;
return((CPaletteSkin*)ptr);
}
// --------------------------------------------------
void CVertexBufferReadWrite::touchVertices (uint first, uint last)
{
nlassert (_Parent->checkLockedBuffer());
_First = first;
_Last = last;
}
// --------------------------------------------------
// CVertexBufferRead
// --------------------------------------------------
const NLMISC::CVector* CVertexBufferRead::getVertexCoordPointer(uint idx) const
{
nlassert (_Parent->checkLockedBuffer());
const uint8* ptr;
ptr=_Parent->_LockedBuffer;
ptr+=(idx*_Parent->_VertexSize);
return((const NLMISC::CVector*)ptr);
}
// --------------------------------------------------
const NLMISC::CVector* CVertexBufferRead::getNormalCoordPointer(uint idx) const
{
nlassert (_Parent->checkLockedBuffer());
const uint8* ptr;
if ( !(_Parent->_Flags & CVertexBuffer::NormalFlag) )
{
return(NULL);
}
ptr=_Parent->_LockedBuffer;
ptr+=_Parent->_Offset[CVertexBuffer::Normal];
ptr+=idx*_Parent->_VertexSize;
return((const NLMISC::CVector*)ptr);
}
// --------------------------------------------------
const void* CVertexBufferRead::getColorPointer(uint idx) const
{
nlassert (_Parent->checkLockedBuffer());
const uint8* ptr;
if ( !(_Parent->_Flags & CVertexBuffer::PrimaryColorFlag) )
{
return(NULL);
}
ptr=_Parent->_LockedBuffer;
ptr+=_Parent->_Offset[CVertexBuffer::PrimaryColor];
ptr+=idx*_Parent->_VertexSize;
return((const void*)ptr);
}
// --------------------------------------------------
const void* CVertexBufferRead::getSpecularPointer(uint idx) const
{
nlassert (_Parent->checkLockedBuffer());
const uint8* ptr;
if ( !(_Parent->_Flags & CVertexBuffer::SecondaryColorFlag) )
{
return(NULL);
}
ptr=_Parent->_LockedBuffer;
ptr+=_Parent->_Offset[CVertexBuffer::SecondaryColor];
ptr+=idx*_Parent->_VertexSize;
return((const void*)ptr);
}
// --------------------------------------------------
const NLMISC::CUV* CVertexBufferRead::getTexCoordPointer(uint idx, uint8 stage) const
{
nlassert (_Parent->checkLockedBuffer());
const uint8* ptr;
if ( !(_Parent->_Flags & (CVertexBuffer::TexCoord0Flag<_LockedBuffer;
ptr+=_Parent->_Offset[CVertexBuffer::TexCoord0+stage];
ptr+=idx*_Parent->_VertexSize;
return((const NLMISC::CUV*)ptr);
}
// --------------------------------------------------
const float* CVertexBufferRead::getWeightPointer(uint idx, uint8 wgt) const
{
nlassert (_Parent->checkLockedBuffer());
const uint8* ptr;
nlassert(wgt_Flags & CVertexBuffer::WeightFlag))
return NULL;
ptr=(uint8*)(&_Parent->_LockedBuffer[idx*_Parent->_VertexSize]);
ptr+=_Parent->_Offset[CVertexBuffer::Weight]+wgt*sizeof(float);
return (float*)ptr;
}
// --------------------------------------------------
const CPaletteSkin* CVertexBufferRead::getPaletteSkinPointer(uint idx) const
{
nlassert (_Parent->checkLockedBuffer());
const uint8* ptr;
if ( (_Parent->_Flags & CVertexBuffer::PaletteSkinFlag) != CVertexBuffer::PaletteSkinFlag )
{
return(NULL);
}
ptr=_Parent->_LockedBuffer;
ptr+=_Parent->_Offset[CVertexBuffer::PaletteSkin];
ptr+=idx*_Parent->_VertexSize;
return((const CPaletteSkin*)ptr);
}
// --------------------------------------------------
}