// 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); } // -------------------------------------------------- }