You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1293 lines
36 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
///////////////////////////////////////////////////////////////////////////////
// //
// plGBufferGroup Class Functions //
// Cyan, Inc. //
// //
//// Version History //////////////////////////////////////////////////////////
// //
// 2.21.2001 mcn - Created. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "hsWindows.h"
#include "HeadSpin.h"
#include "plGBufferGroup.h"
#include "hsStream.h"
#include "../plSurface/hsGMaterial.h"
#include "../plDrawable/plGeometrySpan.h"
#include "plPipeline.h"
#include "hsGDeviceRef.h"
#include "plProfile.h"
#include "plVertCoder.h"
plProfile_CreateMemCounter("Buf Group Vertices", "Memory", MemBufGrpVertex);
plProfile_CreateMemCounter("Buf Group Indices", "Memory", MemBufGrpIndex);
plProfile_CreateTimer("Refill Vertex", "Draw", DrawRefillVertex);
plProfile_CreateTimer("Refill Index", "Draw", DrawRefillIndex);
const UInt32 plGBufferGroup::kMaxNumVertsPerBuffer = 32000;
const UInt32 plGBufferGroup::kMaxNumIndicesPerBuffer = 32000;
//// plGBufferTriangle Read and Write /////////////////////////////////////////
void plGBufferTriangle::Read( hsStream *s )
{
fIndex1 = s->ReadSwap16();
fIndex2 = s->ReadSwap16();
fIndex3 = s->ReadSwap16();
fSpanIndex = s->ReadSwap16();
fCenter.Read( s );
}
void plGBufferTriangle::Write( hsStream *s )
{
s->WriteSwap16( fIndex1 );
s->WriteSwap16( fIndex2 );
s->WriteSwap16( fIndex3 );
s->WriteSwap16( fSpanIndex );
fCenter.Write( s );
}
//// plGBufferCell Read/Write /////////////////////////////////////////////////
void plGBufferCell::Read( hsStream *s )
{
fVtxStart = s->ReadSwap32();
fColorStart = s->ReadSwap32();
fLength = s->ReadSwap32();
}
void plGBufferCell::Write( hsStream *s )
{
s->WriteSwap32( fVtxStart );
s->WriteSwap32( fColorStart );
s->WriteSwap32( fLength );
}
//// Constructor //////////////////////////////////////////////////////////////
plGBufferGroup::plGBufferGroup( UInt8 format, hsBool vertsVolatile, hsBool idxVolatile, int LOD )
{
fVertBuffStorage.Reset();
fIdxBuffStorage.Reset();
fColorBuffStorage.Reset();
fVertexBufferRefs.Reset();
fIndexBufferRefs.Reset();
fCells.Reset();
fNumVerts = fNumIndices = 0;
fFormat = format;
fStride = ICalcVertexSize( fLiteStride );
fVertsVolatile = vertsVolatile;
fIdxVolatile = idxVolatile;
fLOD = LOD;
}
//// Destructor ///////////////////////////////////////////////////////////////
plGBufferGroup::~plGBufferGroup()
{
UInt32 i;
CleanUp();
for( i = 0; i < fVertexBufferRefs.GetCount(); i++ )
hsRefCnt_SafeUnRef( fVertexBufferRefs[ i ] );
for( i = 0; i < fIndexBufferRefs.GetCount(); i++ )
hsRefCnt_SafeUnRef( fIndexBufferRefs[ i ] );
fVertexBufferRefs.Reset();
fIndexBufferRefs.Reset();
}
void plGBufferGroup::DirtyVertexBuffer(int i)
{
if( (i < fVertexBufferRefs.GetCount()) && fVertexBufferRefs[i] )
fVertexBufferRefs[i]->SetDirty(true);
}
void plGBufferGroup::DirtyIndexBuffer(int i)
{
if( (i < fIndexBufferRefs.GetCount()) && fIndexBufferRefs[i] )
fIndexBufferRefs[i]->SetDirty(true);
}
//// TidyUp ///////////////////////////////////////////////////////////////////
void plGBufferGroup::TidyUp( void )
{
/* if( fVertBuffStorage.GetCount() == 0 && fNumVerts > 0 )
return; // Already tidy'd!
// IConvertToStorage();
*/
}
void plGBufferGroup::PurgeVertBuffer(UInt32 idx)
{
if( AreVertsVolatile() )
return;
//#define MF_TOSSER
#ifdef MF_TOSSER
plProfile_DelMem(MemBufGrpVertex, fVertBuffSizes[idx]);
delete [] fVertBuffStorage[idx];
fVertBuffStorage[idx] = nil;
plProfile_DelMem(MemBufGrpVertex, fColorBuffCounts[idx] * sizeof(plGBufferColor));
delete [] fColorBuffStorage[idx];
fColorBuffStorage[idx] = nil;
delete fCells[idx];
fCells[idx] = nil;
#endif // MF_TOSSER
return;
}
void plGBufferGroup::PurgeIndexBuffer(UInt32 idx)
{
if( AreIdxVolatile() )
return;
return;
}
//// CleanUp //////////////////////////////////////////////////////////////////
void plGBufferGroup::CleanUp( void )
{
int i;
// Clean up the storage
for( i = 0; i < fVertBuffStorage.GetCount(); i++ )
{
plProfile_DelMem(MemBufGrpVertex, fVertBuffSizes[i]);
delete [] fVertBuffStorage[ i ];
}
for( i = 0; i < fIdxBuffStorage.GetCount(); i++ )
{
plProfile_DelMem(MemBufGrpIndex, fIdxBuffCounts[i] * sizeof(UInt16));
delete [] fIdxBuffStorage[ i ];
}
for( i = 0; i < fColorBuffStorage.GetCount(); i++ )
{
plProfile_DelMem(MemBufGrpVertex, fColorBuffCounts[i] * sizeof(plGBufferColor));
delete [] fColorBuffStorage[ i ];
}
for( i = 0; i < fCells.GetCount(); i++ )
delete fCells[ i ];
fVertBuffStorage.Reset();
fVertBuffSizes.Reset();
fVertBuffStarts.Reset();
fVertBuffEnds.Reset();
fIdxBuffStorage.Reset();
fIdxBuffCounts.Reset();
fIdxBuffStarts.Reset();
fIdxBuffEnds.Reset();
fColorBuffStorage.Reset();
fColorBuffCounts.Reset();
fCells.Reset();
}
//// SetVertexBufferRef ///////////////////////////////////////////////////////
void plGBufferGroup::SetVertexBufferRef( UInt32 index, hsGDeviceRef *vb )
{
hsAssert( index < fVertexBufferRefs.GetCount() + 1, "Vertex buffers must be assigned linearly!" );
if( (int)index > (int)fVertexBufferRefs.GetCount() - 1 )
{
fVertexBufferRefs.Append( vb );
hsRefCnt_SafeRef( vb );
}
else
{
hsRefCnt_SafeAssign( fVertexBufferRefs[ index ], vb );
}
}
//// SetIndexBufferRef ////////////////////////////////////////////////////////
void plGBufferGroup::SetIndexBufferRef( UInt32 index, hsGDeviceRef *ib )
{
hsAssert( index < fIndexBufferRefs.GetCount() + 1, "Index buffers must be assigned linearly!" );
if( (int)index > (int)fIndexBufferRefs.GetCount() - 1 )
{
fIndexBufferRefs.Append( ib );
hsRefCnt_SafeRef( ib );
}
else
{
hsRefCnt_SafeAssign( fIndexBufferRefs[ index ], ib );
}
}
//// PrepForRendering /////////////////////////////////////////////////////////
void plGBufferGroup::PrepForRendering( plPipeline *pipe, hsBool adjustForNvidiaLighting )
{
ISendStorageToBuffers( pipe, adjustForNvidiaLighting );
// The following line was taken out so we'd keep our data around, allowing
// us to rebuild the buffer if necessary on the fly
// CleanUp();
}
hsGDeviceRef* plGBufferGroup::GetVertexBufferRef(UInt32 i)
{
if( i >= fVertexBufferRefs.GetCount() )
fVertexBufferRefs.ExpandAndZero(i+1);
return fVertexBufferRefs[i];
}
hsGDeviceRef* plGBufferGroup::GetIndexBufferRef(UInt32 i)
{
if( i >= fIndexBufferRefs.GetCount() )
fIndexBufferRefs.ExpandAndZero(i+1);
return fIndexBufferRefs[i];
}
//// ISendStorageToBuffers ////////////////////////////////////////////////////
void plGBufferGroup::ISendStorageToBuffers( plPipeline *pipe, hsBool adjustForNvidiaLighting )
{
plProfile_BeginTiming(DrawRefillVertex);
/// Creating or refreshing?
int i;
for( i = 0; i < fVertBuffStorage.GetCount(); i++ )
{
pipe->CheckVertexBufferRef(this, i);
}
plProfile_EndTiming(DrawRefillVertex);
plProfile_BeginTiming(DrawRefillIndex);
for( i = 0; i < fIdxBuffStorage.GetCount(); i++ )
{
pipe->CheckIndexBufferRef(this, i);
}
plProfile_EndTiming(DrawRefillIndex);
}
//// ICalcVertexSize //////////////////////////////////////////////////////////
UInt8 plGBufferGroup::ICalcVertexSize( UInt8 &liteStride )
{
UInt8 size;
size = sizeof( float ) * ( 3 + 3 ); // pos + normal
size += sizeof( float ) * 3 * GetNumUVs();
switch( fFormat & kSkinWeightMask )
{
case kSkinNoWeights: fNumSkinWeights = 0; break;
case kSkin1Weight: fNumSkinWeights = 1; break;
case kSkin2Weights: fNumSkinWeights = 2; break;
case kSkin3Weights: fNumSkinWeights = 3; break;
default: hsAssert( false, "Bad weight count in ICalcVertexSize()" );
}
if( fNumSkinWeights )
{
size += sizeof( float ) * fNumSkinWeights;
if( fFormat & kSkinIndices )
size += sizeof( UInt32 );
}
liteStride = size;
size += sizeof( DWORD ) * 2; // diffuse + specular
return size;
}
//// I/O Functions ////////////////////////////////////////////////////////////
void plGBufferGroup::Read( hsStream *s )
{
UInt32 totalDynSize, i, count, temp = 0, j;
UInt8 *vData;
UInt16 *iData;
plGBufferColor *cData;
s->ReadSwap( &fFormat );
totalDynSize = s->ReadSwap32();
fStride = ICalcVertexSize( fLiteStride );
fVertBuffSizes.Reset();
fVertBuffStarts.Reset();
fVertBuffEnds.Reset();
fColorBuffCounts.Reset();
fIdxBuffCounts.Reset();
fIdxBuffStarts.Reset();
fIdxBuffEnds.Reset();
fVertBuffStorage.Reset();
fIdxBuffStorage.Reset();
plVertCoder coder;
/// Create buffers and read in as we go
count = s->ReadSwap32();
for( i = 0; i < count; i++ )
{
if( fFormat & kEncoded )
{
const UInt16 numVerts = s->ReadSwap16();
const UInt32 size = numVerts * fStride;
fVertBuffSizes.Append(size);
fVertBuffStarts.Append(0);
fVertBuffEnds.Append(-1);
vData = TRACKED_NEW UInt8[size];
fVertBuffStorage.Append( vData );
plProfile_NewMem(MemBufGrpVertex, temp);
coder.Read(s, vData, fFormat, fStride, numVerts);
fColorBuffCounts.Append(0);
fColorBuffStorage.Append(nil);
}
else
{
temp = s->ReadSwap32();
fVertBuffSizes.Append( temp );
fVertBuffStarts.Append(0);
fVertBuffEnds.Append(-1);
vData = TRACKED_NEW UInt8[ temp ];
hsAssert( vData != nil, "Not enough memory to read in vertices" );
s->Read( temp, (void *)vData );
fVertBuffStorage.Append( vData );
plProfile_NewMem(MemBufGrpVertex, temp);
temp = s->ReadSwap32();
fColorBuffCounts.Append( temp );
if( temp > 0 )
{
cData = TRACKED_NEW plGBufferColor[ temp ];
s->Read( temp * sizeof( plGBufferColor ), (void *)cData );
plProfile_NewMem(MemBufGrpVertex, temp * sizeof(plGBufferColor));
}
else
cData = nil;
fColorBuffStorage.Append( cData );
}
}
count = s->ReadSwap32();
for( i = 0; i < count; i++ )
{
temp = s->ReadSwap32();
fIdxBuffCounts.Append( temp );
fIdxBuffStarts.Append(0);
fIdxBuffEnds.Append(-1);
iData = TRACKED_NEW UInt16[ temp ];
hsAssert( iData != nil, "Not enough memory to read in indices" );
s->ReadSwap16( temp, (UInt16 *)iData );
fIdxBuffStorage.Append( iData );
plProfile_NewMem(MemBufGrpIndex, temp * sizeof(UInt16));
}
/// Read in cell arrays, one per vBuffer
for( i = 0; i < fVertBuffStorage.GetCount(); i++ )
{
temp = s->ReadSwap32();
fCells.Append( TRACKED_NEW hsTArray<plGBufferCell> );
fCells[ i ]->SetCount( temp );
for( j = 0; j < temp; j++ )
(*fCells[ i ])[ j ].Read( s );
}
}
//#define VERT_LOG
void plGBufferGroup::Write( hsStream *s )
{
UInt32 totalDynSize, i, j;
#define MF_VERTCODE_ENABLED
#ifdef MF_VERTCODE_ENABLED
fFormat |= kEncoded;
#endif // MF_VERTCODE_ENABLED
#ifdef VERT_LOG
hsUNIXStream log;
log.Open("log\\GBuf.log", "ab");
#endif
/// Calc total dynamic data size, for fun
totalDynSize = 0;
for( i = 0; i < fVertBuffSizes.GetCount(); i++ )
totalDynSize += fVertBuffSizes[ i ];
for( i = 0; i < fIdxBuffCounts.GetCount(); i++ )
totalDynSize += sizeof( UInt16 ) * fIdxBuffCounts[ i ];
s->WriteSwap( fFormat );
s->WriteSwap32( totalDynSize );
plVertCoder coder;
/// Write out dyanmic data
s->WriteSwap32( (UInt32)fVertBuffStorage.GetCount() );
for( i = 0; i < fVertBuffStorage.GetCount(); i++ )
{
#ifdef MF_VERTCODE_ENABLED
hsAssert(fCells[i]->GetCount() == 1, "Data must be interleaved for compression");
UInt32 numVerts = fVertBuffSizes[i] / fStride;
s->WriteSwap16((UInt16)numVerts);
coder.Write(s, fVertBuffStorage[i], fFormat, fStride, (UInt16)numVerts);
#ifdef VERT_LOG
char buf[256];
sprintf(buf, "Vert Buff: %u bytes, idx=%u\r\n", fVertBuffSizes[i], i);
log.WriteString(buf);
for (int xx = 0; xx < fVertBuffSizes[i] / 4; xx++)
{
float* buff32 = (float*)fVertBuffStorage[i];
buff32 += xx;
sprintf(buf, "[%d]%f\r\n", xx*4, *buff32);
log.WriteString(buf);
}
#endif
#else // MF_VERTCODE_ENABLED
s->WriteSwap32( fVertBuffSizes[ i ] );
s->Write( fVertBuffSizes[ i ], (void *)fVertBuffStorage[ i ] );
s->WriteSwap32( fColorBuffCounts[ i ] );
s->Write( fColorBuffCounts[ i ] * sizeof( plGBufferColor ), (void *)fColorBuffStorage[ i ] );
#endif // MF_VERTCODE_ENABLED
}
s->WriteSwap32( (UInt32)fIdxBuffCounts.GetCount() );
for( i = 0; i < fIdxBuffStorage.GetCount(); i++ )
{
s->WriteSwap32( fIdxBuffCounts[ i ] );
s->WriteSwap16( fIdxBuffCounts[ i ], fIdxBuffStorage[ i ] );
}
/// Write out cell arrays
for( i = 0; i < fVertBuffStorage.GetCount(); i++ )
{
s->WriteSwap32( fCells[ i ]->GetCount() );
for( j = 0; j < fCells[ i ]->GetCount(); j++ )
(*fCells[ i ])[ j ].Write( s );
}
#ifdef VERT_LOG
log.Close();
#endif
// All done!
}
///////////////////////////////////////////////////////////////////////////////
//// Editing Functions ////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//// DeleteVertsFromStorage ///////////////////////////////////////////////////
// Deletes a span of verts from the vertex storage. Remember to Prep this
// group after doing this!
// Note: does NOT adjust index storage, since we don't know inside here
// which indices to adjust. Have to call that separately.
// Note 2: for simplicity sake, we only do this for groups with ONE interleaved
// cell. Doing this for multiple separated cells would be, literally, hell.
void plGBufferGroup::DeleteVertsFromStorage( UInt32 which, UInt32 start, UInt32 length )
{
UInt8 *dstPtr, *srcPtr;
UInt32 amount;
hsAssert( fCells[ which ]->GetCount() == 1, "Cannot delete verts on a mixed buffer group" );
// Adjust cell 0
(*fCells[ which ])[ 0 ].fLength -= length;
start *= fStride;
length *= fStride;
if( start + length < fVertBuffSizes[ which ] )
{
dstPtr = &( fVertBuffStorage[ which ][ start ] );
srcPtr = &( fVertBuffStorage[ which ][ start + length ] );
amount = ( fVertBuffSizes[ which ] ) - ( start + length );
memmove( dstPtr, srcPtr, amount );
}
fVertBuffSizes[ which ] -= length;
plProfile_DelMem(MemBufGrpVertex, length);
if( fVertexBufferRefs.GetCount() > which && fVertexBufferRefs[ which ] != nil )
{
hsRefCnt_SafeUnRef(fVertexBufferRefs[which]);
fVertexBufferRefs[which] = nil;
}
}
//// AdjustIndicesInStorage ///////////////////////////////////////////////////
// Adjusts indices >= a given threshold by a given delta. Use it to adjust
// indices after vertex deletion. Affects only the given buffer.
void plGBufferGroup::AdjustIndicesInStorage( UInt32 which, UInt16 threshhold, Int16 delta )
{
int i;
for( i = 0; i < fIdxBuffCounts[ which ]; i++ )
{
if( fIdxBuffStorage[ which ][ i ] >= threshhold )
fIdxBuffStorage[ which ][ i ] += delta;
}
if( fIndexBufferRefs.GetCount() > which && fIndexBufferRefs[ which ] != nil )
fIndexBufferRefs[ which ]->SetDirty( true );
}
//// DeleteIndicesFromStorage /////////////////////////////////////////////////
// Deletes a span of indices from the index storage. Remember to Prep this
// group after doing this!
void plGBufferGroup::DeleteIndicesFromStorage( UInt32 which, UInt32 start, UInt32 length )
{
UInt16 *dstPtr, *srcPtr;
UInt32 amount;
hsAssert( start + length <= fIdxBuffCounts[ which ], "Illegal range to DeleteIndicesFromStorage()" );
if( start + length < fIdxBuffCounts[ which ] )
{
dstPtr = &( fIdxBuffStorage[ which ][ start ] );
srcPtr = &( fIdxBuffStorage[ which ][ start + length ] );
amount = fIdxBuffCounts[ which ] - ( start + length );
memmove( dstPtr, srcPtr, amount * sizeof( UInt16 ) );
}
fIdxBuffCounts[ which ] -= length;
plProfile_DelMem(MemBufGrpIndex, length * sizeof(UInt16));
if( fIndexBufferRefs.GetCount() > which && fIndexBufferRefs[ which ] != nil )
{
hsRefCnt_SafeUnRef(fIndexBufferRefs[which]);
fIndexBufferRefs[which] = nil;
}
}
//// GetNumPrimaryVertsLeft ///////////////////////////////////////////////////
// Base on the cells, so we can take instanced cells into account
UInt32 plGBufferGroup::GetNumPrimaryVertsLeft( void ) const
{
return GetNumVertsLeft( 0 );
}
//// GetNumVertsLeft //////////////////////////////////////////////////////////
// Base on the cells, so we can take instanced cells into account
UInt32 plGBufferGroup::GetNumVertsLeft( UInt32 idx ) const
{
if( idx >= fCells.GetCount() )
return kMaxNumVertsPerBuffer;
UInt32 i, total = kMaxNumVertsPerBuffer;
for( i = 0; i < fCells[ idx ]->GetCount(); i++ )
total -= (*fCells[ idx ])[ i ].fLength;
return total;
}
//// IMakeCell ////////////////////////////////////////////////////////////////
// Get a cell from the given cell array.
UInt32 plGBufferGroup::IMakeCell( UInt32 vbIndex, UInt8 flags, UInt32 vStart, UInt32 cStart, UInt32 len, UInt32 *offset )
{
hsTArray<plGBufferCell> *cells = fCells[ vbIndex ];
if( !(flags & kReserveInterleaved) )
{
/// Note that there are THREE types of cells: interleaved (colorStart == -1),
/// first of an instance group (colorStart != -1 && vStart != -1) and
/// an instance (colorStart != -1 && vStart == -1 ). To simplify things,
/// we never merge any separated cells
if( flags & kReserveSeparated )
cells->Append( plGBufferCell( vStart, cStart, len ) );
else
cells->Append( plGBufferCell( (UInt32)-1, cStart, len ) );
*offset = 0;
}
else
{
/// Merge if the last cell was an interleaved cell
if( cells->GetCount() > 0 && (*cells)[ cells->GetCount() - 1 ].fColorStart == (UInt32)-1 )
{
*offset = (*cells)[ cells->GetCount() - 1 ].fLength;
(*cells)[ cells->GetCount() - 1 ].fLength += len;
}
else
{
cells->Append( plGBufferCell( vStart, (UInt32)-1, len ) );
*offset = 0;
}
}
return cells->GetCount() - 1;
}
//// ReserveVertStorage ///////////////////////////////////////////////////////
// Takes a length to reserve in a vertex buffer and returns the buffer index
// and starting position. Basically does what AppendToVertStorage() used to
// do except it doesn't actually copy any data into the space reserved.
hsBool plGBufferGroup::ReserveVertStorage( UInt32 numVerts, UInt32 *vbIndex, UInt32 *cell, UInt32 *offset, UInt8 flags )
{
UInt8 *storagePtr = nil;
UInt32 cStartIdx = 0, vStartIdx = 0;
plGBufferColor *cStoragePtr = nil;
int i;
if( numVerts >= kMaxNumVertsPerBuffer )
{
hsAssert( false, "Egad, why on earth are you adding that many verts???" );
return false;
}
/// Find a spot
if( !(flags & kReserveIsolate) )
{
for( i = 0; i < fVertBuffStorage.GetCount(); i++ )
{
if( GetNumVertsLeft( i ) >= numVerts )
break;
}
}
else
{
i = fVertBuffStorage.GetCount();
}
if( i == fVertBuffStorage.GetCount() )
{
if( (flags & kReserveInterleaved) || (flags & kReserveSeparated) )
{
fVertBuffStorage.Append( nil );
fVertBuffSizes.Append( 0 );
}
fVertBuffStarts.Append(0);
fVertBuffEnds.Append(-1);
fColorBuffStorage.Append( nil );
fColorBuffCounts.Append( 0 );
fCells.Append( TRACKED_NEW hsTArray<plGBufferCell> );
}
*vbIndex = i;
if( !(flags & kReserveInterleaved) )
{
// Splitting the data into vertex and color storage
if( flags & kReserveSeparated )
{
/// Increase the storage size
vStartIdx = fVertBuffSizes[ i ];
storagePtr = TRACKED_NEW UInt8[ fVertBuffSizes[ i ] + numVerts * fLiteStride ];
if( fVertBuffSizes[ i ] > 0 )
memcpy( storagePtr, fVertBuffStorage[ i ], fVertBuffSizes[ i ] );
fVertBuffSizes[ i ] += numVerts * fLiteStride;
plProfile_NewMem(MemBufGrpVertex, numVerts * fLiteStride);
}
/// Color too
cStartIdx = fColorBuffCounts[ i ];
cStoragePtr = TRACKED_NEW plGBufferColor[ fColorBuffCounts[ i ] + numVerts ];
if( fColorBuffCounts[ i ] > 0 )
memcpy( cStoragePtr, fColorBuffStorage[ i ], fColorBuffCounts[ i ] * sizeof( plGBufferColor ) );
}
else
{
// Interleaved
/// Increase the storage size
vStartIdx = fVertBuffSizes[ i ];
storagePtr = TRACKED_NEW UInt8[ fVertBuffSizes[ i ] + numVerts * fStride ];
if( fVertBuffSizes[ i ] > 0 )
memcpy( storagePtr, fVertBuffStorage[ i ], fVertBuffSizes[ i ] );
fVertBuffSizes[ i ] += numVerts * fStride;
plProfile_NewMem(MemBufGrpVertex, numVerts * fStride);
}
/// Switch over
if( storagePtr != nil )
{
if( fVertBuffStorage[ i ] != nil )
delete [] fVertBuffStorage[ i ];
fVertBuffStorage[ i ] = storagePtr;
}
if( cStoragePtr != nil )
{
if( fColorBuffStorage[ i ] != nil )
delete [] fColorBuffStorage[ i ];
fColorBuffStorage[ i ] = cStoragePtr;
fColorBuffCounts[ i ] += numVerts;
plProfile_NewMem(MemBufGrpVertex, numVerts * sizeof(plGBufferColor));
}
if( fVertexBufferRefs.GetCount() > i && fVertexBufferRefs[ i ] != nil )
{
hsRefCnt_SafeUnRef(fVertexBufferRefs[i]);
fVertexBufferRefs[i] = nil;
}
/// Append a cell entry
*cell = IMakeCell( i, flags, vStartIdx, cStartIdx, numVerts, offset );
/// All done!
return true;
}
//// AppendToVertStorage //////////////////////////////////////////////////////
// Given an opaque array of vertex data, puts it into the first available spot
// in fVertStorage. If there is no array on fVertStorage that can hold them,
// we create a new one. Returns the index of the storage array and the
// starting index into the array. Note that we basically stick it wherever
// we can, instead of at the end.
// Updated 4.30.2001 mcn to take in a plGeometrySpan as a source rather than
// raw data, since the plGeometrySpan no longer stores data in exactly the
// same format.
// Updated 6.15.2001 mcn to use ReserveVertStorage().
void plGBufferGroup::AppendToVertStorage( plGeometrySpan *srcSpan, UInt32 *vbIndex, UInt32 *cell, UInt32 *offset )
{
if( !ReserveVertStorage( srcSpan->fNumVerts, vbIndex, cell, offset, kReserveInterleaved ) )
return;
StuffToVertStorage( srcSpan, *vbIndex, *cell, *offset, kReserveInterleaved );
}
//// AppendToVertAndColorStorage //////////////////////////////////////////////
// Same as AppendToVertStorage(), but writes only the verts to the vertex
// storage and the colors to the separate color storage.
void plGBufferGroup::AppendToVertAndColorStorage( plGeometrySpan *srcSpan, UInt32 *vbIndex, UInt32 *cell, UInt32 *offset )
{
if( !ReserveVertStorage( srcSpan->fNumVerts, vbIndex, cell, offset, kReserveSeparated ) )
return;
StuffToVertStorage( srcSpan, *vbIndex, *cell, *offset, kReserveSeparated );
}
//// AppendToColorStorage /////////////////////////////////////////////////////
// Same as AppendToVertStorage(), but writes JUST to color storage.
void plGBufferGroup::AppendToColorStorage( plGeometrySpan *srcSpan, UInt32 *vbIndex, UInt32 *cell, UInt32 *offset, UInt32 origCell )
{
if( !ReserveVertStorage( srcSpan->fNumVerts, vbIndex, cell, offset, kReserveColors ) )
return;
(*fCells[ *vbIndex ])[ *cell ].fVtxStart = (*fCells[ *vbIndex ])[ origCell ].fVtxStart;
StuffToVertStorage( srcSpan, *vbIndex, *cell, *offset, kReserveColors );
}
//// IGetStartVtxPointer //////////////////////////////////////////////////////
// Get the start vertex and color buffer pointers for a given cell and offset
void plGBufferGroup::IGetStartVtxPointer( UInt32 vbIndex, UInt32 cell, UInt32 offset, UInt8 *&tempPtr, plGBufferColor *&cPtr )
{
hsAssert( vbIndex < fVertBuffStorage.GetCount(), "Invalid vbIndex in StuffToVertStorage()" );
hsAssert( cell < fCells[ vbIndex ]->GetCount(), "Invalid cell in StuffToVertStorage()" );
tempPtr = fVertBuffStorage[ vbIndex ];
cPtr = fColorBuffStorage[ vbIndex ];
tempPtr += (*fCells[ vbIndex ])[ cell ].fVtxStart;
cPtr += (*fCells[ vbIndex ])[ cell ].fColorStart;
if( offset > 0 )
{
tempPtr += offset * ( ( (*fCells[ vbIndex ])[ cell ].fColorStart == (UInt32)-1 ) ? fStride : fLiteStride );
cPtr += offset;
}
}
//// GetVertBufferCount ///////////////////////////////////////////////////////
UInt32 plGBufferGroup::GetVertBufferCount( UInt32 idx ) const
{
return GetVertStartFromCell( idx, fCells[ idx ]->GetCount(), 0 );
}
//// GetVertStartFromCell /////////////////////////////////////////////////////
UInt32 plGBufferGroup::GetVertStartFromCell( UInt32 vbIndex, UInt32 cell, UInt32 offset ) const
{
UInt32 i, numVerts;
hsAssert( vbIndex < fVertBuffStorage.GetCount(), "Invalid vbIndex in StuffToVertStorage()" );
hsAssert( cell <= fCells[ vbIndex ]->GetCount(), "Invalid cell in StuffToVertStorage()" );
numVerts = 0;
for( i = 0; i < cell; i++ )
numVerts += (*fCells[ vbIndex ])[ i ].fLength;
numVerts += offset;
return numVerts;
}
//// StuffToVertStorage ///////////////////////////////////////////////////////
// Stuffs data from a plGeometrySpan into the specified location in the
// specified vertex buffer.
void plGBufferGroup::StuffToVertStorage( plGeometrySpan *srcSpan, UInt32 vbIndex, UInt32 cell, UInt32 offset, UInt8 flags )
{
UInt8 *tempPtr, stride;
plGBufferColor *cPtr;
int i, j, numVerts;
plGBufferCell *cellPtr;
hsAssert( vbIndex < fVertBuffStorage.GetCount(), "Invalid vbIndex in StuffToVertStorage()" );
hsAssert( cell < fCells[ vbIndex ]->GetCount(), "Invalid cell in StuffToVertStorage()" );
IGetStartVtxPointer( vbIndex, cell, offset, tempPtr, cPtr );
cellPtr = &(*fCells[ vbIndex ])[ cell ];
stride = ( cellPtr->fColorStart != (UInt32)-1 ) ? fLiteStride : fStride;
numVerts = srcSpan->fNumVerts;
/// Copy the data over
for( i = 0; i < numVerts; i++ )
{
hsPoint3 pos;
float weights[ 3 ];
UInt32 weightIndices;
hsVector3 norm;
UInt32 color, specColor;
hsPoint3 uvs[ plGeometrySpan::kMaxNumUVChannels ];
float *fPtr;
UInt32 *dPtr;
// Gotta swap the data around, since plGeometrySpans store the data slightly differently
if( flags & kReserveColors )
{
/// Just do colors
srcSpan->ExtractVertex( i, &pos, &norm, &color, &specColor );
cPtr->fDiffuse = color;
cPtr->fSpecular = specColor;
}
else
{
/// Do verts, possibly colors as well
srcSpan->ExtractVertex( i, &pos, &norm, &color, &specColor );
if( ( fFormat & kSkinWeightMask ) != kSkinNoWeights )
srcSpan->ExtractWeights( i, weights, &weightIndices );
for( j = 0; j < GetNumUVs(); j++ )
srcSpan->ExtractUv( i, j, &uvs[ j ] );
// Stuff it in now
fPtr = (float *)tempPtr;
fPtr[ 0 ] = pos.fX;
fPtr[ 1 ] = pos.fY;
fPtr[ 2 ] = pos.fZ;
fPtr += 3;
if( fNumSkinWeights > 0 )
{
for( j = 0; j < fNumSkinWeights; j++ )
{
*fPtr = weights[ j ];
fPtr++;
}
if( fNumSkinWeights > 1 )
{
dPtr = (UInt32 *)fPtr;
*dPtr = weightIndices;
dPtr++;
fPtr = (float *)dPtr;
}
}
fPtr[ 0 ] = norm.fX;
fPtr[ 1 ] = norm.fY;
fPtr[ 2 ] = norm.fZ;
fPtr += 3;
if( flags & kReserveInterleaved )
{
dPtr = (UInt32 *)fPtr;
dPtr[ 0 ] = color;
dPtr[ 1 ] = specColor;
dPtr += 2;
fPtr = (float *)dPtr;
}
else
{
cPtr->fDiffuse = color;
cPtr->fSpecular = specColor;
}
for( j = 0; j < GetNumUVs(); j++ )
{
fPtr[ 0 ] = uvs[ j ].fX;
fPtr[ 1 ] = uvs[ j ].fY;
fPtr[ 2 ] = uvs[ j ].fZ;
fPtr += 3;
}
}
tempPtr += stride;
cPtr++;
}
if( ( vbIndex < fVertexBufferRefs.GetCount() ) && fVertexBufferRefs[ vbIndex ] )
fVertexBufferRefs[ vbIndex ]->SetDirty( true );
}
//// ReserveIndexStorage //////////////////////////////////////////////////////
// Same as ReserveVertStorage(), only for indices :)
hsBool plGBufferGroup::ReserveIndexStorage( UInt32 numIndices, UInt32 *ibIndex, UInt32 *ibStart, UInt16 **dataPtr )
{
UInt16 *storagePtr;
int i;
if( numIndices >= kMaxNumIndicesPerBuffer )
{
hsAssert( false, "Egad, why on earth are you adding that many indices???" );
return false;
}
/// Find a spot
for( i = 0; i < fIdxBuffStorage.GetCount(); i++ )
{
if( fIdxBuffCounts[ i ] + numIndices < kMaxNumIndicesPerBuffer )
break;
}
if( i == fIdxBuffStorage.GetCount() )
{
fIdxBuffStorage.Append( nil );
fIdxBuffCounts.Append( 0 );
fIdxBuffStarts.Append(0);
fIdxBuffEnds.Append(-1);
}
*ibIndex = i;
*ibStart = fIdxBuffCounts[ i ];
/// Increase the storage size
storagePtr = TRACKED_NEW UInt16[ fIdxBuffCounts[ i ] + numIndices ];
if( fIdxBuffCounts[ i ] > 0 )
memcpy( storagePtr, fIdxBuffStorage[ i ], fIdxBuffCounts[ i ] * sizeof( UInt16 ) );
if( dataPtr != nil )
*dataPtr = storagePtr + fIdxBuffCounts[ i ];
/// Switch over
i = *ibIndex;
if( fIdxBuffStorage[ i ] != nil )
delete [] fIdxBuffStorage[ i ];
fIdxBuffStorage[ i ] = storagePtr;
fIdxBuffCounts[ i ] += numIndices;
plProfile_NewMem(MemBufGrpIndex, numIndices * sizeof(UInt16));
/// All done!
if( fIndexBufferRefs.GetCount() > i && fIndexBufferRefs[ i ] != nil )
{
hsRefCnt_SafeUnRef(fIndexBufferRefs[i]);
fIndexBufferRefs[i] = nil;
}
return true;
}
//// AppendToIndexStorage /////////////////////////////////////////////////////
// Same as AppendToVertStorage, only for the index buffers and indices. Duh :)
void plGBufferGroup::AppendToIndexStorage( UInt32 numIndices, UInt16 *data, UInt32 addToAll,
UInt32 *ibIndex, UInt32 *ibStart )
{
UInt16 *tempPtr;
int i;
if( !ReserveIndexStorage( numIndices, ibIndex, ibStart, &tempPtr ) )
return;
/// Copy new indices over, offsetting them as we were told to
for( i = 0; i < numIndices; i++ )
tempPtr[ i ] = data[ i ] + (UInt16)addToAll;
/// All done!
}
//// ConvertToTriList /////////////////////////////////////////////////////////
// Returns an array of plGBufferTriangles representing the span of indices
// specified.
plGBufferTriangle *plGBufferGroup::ConvertToTriList( Int16 spanIndex, UInt32 whichIdx, UInt32 whichVtx, UInt32 whichCell, UInt32 start, UInt32 numTriangles )
{
plGBufferTriangle *array;
UInt16 *storagePtr;
UInt8 *vertStgPtr, stride;
float *vertPtr;
int i, j;
hsPoint3 center;
UInt32 offsetBy;
plGBufferColor *wastePtr;
/// Sanity checks
hsAssert( whichIdx < fIdxBuffStorage.GetCount(), "Invalid index buffer ID to ConvertToTriList()" );
hsAssert( whichVtx < fVertBuffStorage.GetCount(), "Invalid vertex buffer ID to ConvertToTriList()" );
hsAssert( start < fIdxBuffCounts[ whichIdx ], "Invalid start index to ConvertToTriList()" );
hsAssert( start + numTriangles * 3 <= fIdxBuffCounts[ whichIdx ], "Invalid count to ConvertToTriList()" );
hsAssert( whichCell < fCells[ whichVtx ]->GetCount(), "Invalid cell to ConvertToTriList()" );
/// Create the array and fill it
array = TRACKED_NEW plGBufferTriangle[ numTriangles ];
hsAssert( array != nil, "Not enough memory to create triangle data in ConvertToTriList()" );
storagePtr = fIdxBuffStorage[ whichIdx ];
IGetStartVtxPointer( whichVtx, whichCell, 0, vertStgPtr, wastePtr );
offsetBy = GetVertStartFromCell( whichVtx, whichCell, 0 );
stride = ( (*fCells[ whichVtx ])[ whichCell ].fColorStart == (UInt32)-1 ) ? fStride : fLiteStride;
for( i = 0, j = 0; i < numTriangles; i++, j += 3 )
{
center.fX = center.fY = center.fZ = 0;
vertPtr = (float *)( vertStgPtr + stride * ( storagePtr[ start + j + 0 ] - offsetBy ) );
center.fX += vertPtr[ 0 ];
center.fY += vertPtr[ 1 ];
center.fZ += vertPtr[ 2 ];
vertPtr = (float *)( vertStgPtr + stride * ( storagePtr[ start + j + 1 ] - offsetBy ) );
center.fX += vertPtr[ 0 ];
center.fY += vertPtr[ 1 ];
center.fZ += vertPtr[ 2 ];
vertPtr = (float *)( vertStgPtr + stride * ( storagePtr[ start + j + 2 ] - offsetBy ) );
center.fX += vertPtr[ 0 ];
center.fY += vertPtr[ 1 ];
center.fZ += vertPtr[ 2 ];
center.fX /= 3.0f;
center.fY /= 3.0f;
center.fZ /= 3.0f;
array[ i ].fSpanIndex = spanIndex;
array[ i ].fIndex1 = storagePtr[ start + j + 0 ];
array[ i ].fIndex2 = storagePtr[ start + j + 1 ];
array[ i ].fIndex3 = storagePtr[ start + j + 2 ];
array[ i ].fCenter = center;
}
/// All done!
return array;
}
//// StuffFromTriList /////////////////////////////////////////////////////////
// Stuffs the indices from an array of plGBufferTriangles into the index
// storage.
void plGBufferGroup::StuffFromTriList( UInt32 which, UInt32 start, UInt32 numTriangles, UInt16 *data )
{
UInt16 *storagePtr;
/// Sanity checks
hsAssert( which < fIdxBuffStorage.GetCount(), "Invalid index buffer ID to StuffFromTriList()" );
hsAssert( start < fIdxBuffCounts[ which ], "Invalid start index to StuffFromTriList()" );
hsAssert( start + numTriangles * 3 <= fIdxBuffCounts[ which ], "Invalid count to StuffFromTriList()" );
/// This is easy--just stuff!
storagePtr = fIdxBuffStorage[ which ];
#define MF_SPEED_THIS_UP
#ifndef MF_SPEED_THIS_UP
int i, j;
for( i = 0, j = 0; i < numTriangles; i++, j += 3 )
{
storagePtr[ start + j ] = data[ i ].fIndex1;
storagePtr[ start + j + 1 ] = data[ i ].fIndex2;
storagePtr[ start + j + 2 ] = data[ i ].fIndex3;
}
#else // MF_SPEED_THIS_UP
memcpy( storagePtr + start, data, numTriangles * 3 * sizeof(*data) );
#endif // MF_SPEED_THIS_UP
/// All done! Just make sure we refresh before we render...
if( fIndexBufferRefs.GetCount() > which && fIndexBufferRefs[ which ] != nil )
fIndexBufferRefs[ which ]->SetDirty( true );
}
//// StuffTri /////////////////////////////////////////////////////////////////
void plGBufferGroup::StuffTri( UInt32 iBuff, UInt32 iTri, UInt16 idx0, UInt16 idx1, UInt16 idx2 )
{
/// Sanity checks
hsAssert( iBuff < fIdxBuffStorage.GetCount(), "Invalid index buffer ID to StuffFromTriList()" );
hsAssert( iTri < fIdxBuffCounts[ iBuff ], "Invalid start index to StuffFromTriList()" );
fIdxBuffStorage[ iBuff ][ iTri + 0 ] = idx0;
fIdxBuffStorage[ iBuff ][ iTri + 1 ] = idx1;
fIdxBuffStorage[ iBuff ][ iTri + 2 ] = idx2;
}
//// Accessor Functions ///////////////////////////////////////////////////////
hsPoint3 &plGBufferGroup::Position( int iBuff, UInt32 cell, int iVtx )
{
UInt8 *vertStgPtr;
plGBufferColor *cPtr;
IGetStartVtxPointer( iBuff, cell, iVtx, vertStgPtr, cPtr );
return *(hsPoint3 *)( vertStgPtr + 0 );
}
hsVector3 &plGBufferGroup::Normal( int iBuff, UInt32 cell, int iVtx )
{
UInt8 *vertStgPtr;
plGBufferColor *cPtr;
IGetStartVtxPointer( iBuff, cell, iVtx, vertStgPtr, cPtr );
return *(hsVector3 *)( vertStgPtr + sizeof( hsPoint3 ) );
}
UInt32 &plGBufferGroup::Color( int iBuff, UInt32 cell, int iVtx )
{
UInt8 *vertStgPtr;
plGBufferColor *cPtr;
IGetStartVtxPointer( iBuff, cell, iVtx, vertStgPtr, cPtr );
if( (*fCells[ iBuff ])[ cell ].fColorStart != (UInt32)-1 )
return *(UInt32 *)( &cPtr->fDiffuse );
else
return *(UInt32 *)( vertStgPtr + 2 * sizeof( hsPoint3 ) );
}
UInt32 &plGBufferGroup::Specular( int iBuff, UInt32 cell, int iVtx )
{
UInt8 *vertStgPtr;
plGBufferColor *cPtr;
IGetStartVtxPointer( iBuff, cell, iVtx, vertStgPtr, cPtr );
if( (*fCells[ iBuff ])[ cell ].fColorStart != (UInt32)-1 )
return *(UInt32 *)( &cPtr->fSpecular );
else
return *(UInt32 *)( vertStgPtr + 2 * sizeof( hsPoint3 ) );
}
hsPoint3 &plGBufferGroup::UV( int iBuff, UInt32 cell, int iVtx, int channel )
{
UInt8 *vertStgPtr;
plGBufferColor *cPtr;
IGetStartVtxPointer( iBuff, cell, iVtx, vertStgPtr, cPtr );
vertStgPtr += 2 * sizeof( hsPoint3 ) + channel * sizeof( hsPoint3 );
if( (*fCells[ iBuff ])[ cell ].fColorStart != (UInt32)-1 )
return *(hsPoint3 *)( vertStgPtr );
else
return *(hsPoint3 *)( vertStgPtr + 2 * sizeof( UInt32 ) );
}