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.

1211 lines
32 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==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plGeometrySpan Class Functions //
// //
//// Version History /////////////////////////////////////////////////////////
// //
// Created 3.8.2001 mcn //
// //
//////////////////////////////////////////////////////////////////////////////
#include "hsWindows.h"
#include "hsTypes.h"
#include "plGeometrySpan.h"
#include "plSurface/hsGMaterial.h"
#include "plSurface/plLayerInterface.h"
#include "hsBitVector.h"
//// Static Data /////////////////////////////////////////////////////////////
hsBitVector plGeometrySpan::fInstanceGroupIDFlags;
UInt32 plGeometrySpan::fHighestReadInstanceGroup = 0;
hsTArray<hsTArray<plGeometrySpan *> *> plGeometrySpan::fInstanceGroups;
//// Constructor and Destructor //////////////////////////////////////////////
plGeometrySpan::plGeometrySpan()
{
IClearMembers();
}
plGeometrySpan::plGeometrySpan( const plGeometrySpan *instance )
{
IClearMembers();
MakeInstanceOf( instance );
}
plGeometrySpan::~plGeometrySpan()
{
ClearBuffers();
}
void plGeometrySpan::IClearMembers( void )
{
fVertexData = nil;
fIndexData = nil;
fMaterial = nil;
fNumVerts = fNumIndices = 0;
fBaseMatrix = fNumMatrices = 0;
fLocalUVWChans = 0;
fMaxBoneIdx = 0;
fPenBoneIdx = 0;
fCreating = false;
fFogEnviron = nil;
fProps = 0;
fMinDist = fMaxDist = -1.f;
fWaterHeight = 0;
fMultColor = nil;
fAddColor = nil;
fDiffuseRGBA = nil;
fSpecularRGBA = nil;
fInstanceRefs = nil;
fInstanceGroupID = kNoGroupID;
fSpanRefIndex = (UInt32)-1;
fLocalToOBB.Reset();
fOBBToLocal.Reset();
fDecalLevel = 0;
fMaxOwner = nil;
}
//// ClearBuffers ////////////////////////////////////////////////////////////
void plGeometrySpan::ClearBuffers( void )
{
// If UserOwned, the actual buffer data belongs to someone else (like a BufferGroup).
// Just erase our knowledge of it and move on.
if( fProps & kUserOwned )
{
IClearMembers();
return;
}
bool removeData = true;
// If we have an instanceRefs array, remove ourselves from
// the array. If we are the last in the array, remove the array itself
if( fInstanceRefs != nil )
{
if( fInstanceRefs->GetCount() == 1 )
{
delete fInstanceRefs;
// Remove the group ID flag as well
IClearGroupID( fInstanceGroupID );
}
else
{
int idx = fInstanceRefs->Find( this );
hsAssert( idx != fInstanceRefs->kMissingIndex, "Invalid instance ref data in plGeometrySpan::ClearBuffers()" );
fInstanceRefs->Remove( idx );
removeData = false; // Don't remove data until we're the last one
}
fInstanceRefs = nil;
fInstanceGroupID = kNoGroupID;
}
if( removeData )
{
delete [] fVertexData;
fVertexData = nil;
delete [] fMultColor;
delete [] fAddColor;
fMultColor = nil;
fAddColor = nil;
}
delete [] fIndexData;
delete [] fDiffuseRGBA;
delete [] fSpecularRGBA;
fIndexData = nil;
fDiffuseRGBA = fSpecularRGBA = nil;
}
//// MakeInstanceOf //////////////////////////////////////////////////////////
// Note: instancing copies the index buffer but just copies the POINTERS
// for the vertex buffers and source colors. This is because the indices
// will eventually change but the vertices won't.
void plGeometrySpan::MakeInstanceOf( const plGeometrySpan *instance )
{
hsTArray<plGeometrySpan *> *array;
ClearBuffers();
/// Adjust the instanceRefs array
array = instance->fInstanceRefs;
if( array == nil )
{
// Go find a new groupID
instance->fInstanceGroupID = IAllocateNewGroupID();
instance->fInstanceRefs = array = TRACKED_NEW hsTArray<plGeometrySpan *>;
// Go figure, it won't append the const version to the array...this is a cheat,
// but then, so is making fInstanceRefs mutable :)
array->Append( (plGeometrySpan *)instance );
}
fInstanceGroupID = instance->fInstanceGroupID;
array->Append( this );
fInstanceRefs = array;
/// Copy over the data
IDuplicateUniqueData( instance );
fVertexData = instance->fVertexData;
fMultColor = instance->fMultColor;
fAddColor = instance->fAddColor;
/// All done!
}
void plGeometrySpan::IUnShareData()
{
if( fVertexData )
{
UInt8* oldVtxData = fVertexData;
UInt32 size = GetVertexSize( fFormat );
fVertexData = TRACKED_NEW UInt8[ size * fNumVerts ];
memcpy( fVertexData, oldVtxData, size * fNumVerts );
}
if( fMultColor )
{
hsColorRGBA* oldMult = fMultColor;
fMultColor = TRACKED_NEW hsColorRGBA[ fNumVerts ];
memcpy( fMultColor, oldMult, sizeof(hsColorRGBA) * fNumVerts );
}
if( fAddColor )
{
hsColorRGBA* oldAdd = fAddColor;
fAddColor = TRACKED_NEW hsColorRGBA[ fNumVerts ];
memcpy( fAddColor, oldAdd, sizeof(hsColorRGBA) * fNumVerts );
}
}
void plGeometrySpan::BreakInstance()
{
hsAssert(fInstanceRefs, "Breaking instancing when I'm not instanced");
int idx = fInstanceRefs->Find(this);
hsAssert(idx != fInstanceRefs->kMissingIndex, "I'm not in my own instance refs list");
fInstanceRefs->Remove(idx);
hsAssert(fInstanceRefs->GetCount(), "Don't BreakInstance if I'm the last one, use UnInstance instead");
fInstanceGroupID = IAllocateNewGroupID();
fInstanceRefs = TRACKED_NEW hsTArray<plGeometrySpan*>;
fInstanceRefs->Append(this);
IUnShareData();
fProps |= plGeometrySpan::kInstanced;
}
void plGeometrySpan::ChangeInstance(plGeometrySpan* newInstance)
{
hsAssert(fInstanceRefs, "Changing instancing when I'm not instanced");
int idx = fInstanceRefs->Find(this);
hsAssert(idx != fInstanceRefs->kMissingIndex, "I'm not in my own instance refs list");
fInstanceRefs->Remove(idx);
fInstanceGroupID = newInstance->fInstanceGroupID;
fInstanceRefs = newInstance->fInstanceRefs;
fInstanceRefs->Append(this);
fVertexData = newInstance->fVertexData;
fMultColor = newInstance->fMultColor;
fAddColor = newInstance->fAddColor;
fProps |= plGeometrySpan::kInstanced;
}
void plGeometrySpan::UnInstance()
{
hsAssert(fInstanceRefs, "UnInstancing a non-instance");
int idx = fInstanceRefs->Find(this);
hsAssert(idx != fInstanceRefs->kMissingIndex, "I'm not in my own instance refs list");
fInstanceRefs->Remove(idx);
// If we're the last one, we just take ownership of the shared data,
// else we make our own copy of it.
if( !fInstanceRefs->GetCount() )
{
delete fInstanceRefs;
}
else
{
IUnShareData();
}
fInstanceRefs = nil;
fInstanceGroupID = kNoGroupID;
fProps &= ~plGeometrySpan::kInstanced;
}
//// IAllocateNewGroupID /////////////////////////////////////////////////////
// Static function that allocates a new groupID by finding an empty slot in
// the bitVector, then marking it as used and returning that bit #
UInt32 plGeometrySpan::IAllocateNewGroupID( void )
{
UInt32 id;
for( id = 0; id < fInstanceGroupIDFlags.GetSize(); id++ )
{
if( !fInstanceGroupIDFlags.IsBitSet( id ) )
break;
}
fInstanceGroupIDFlags.SetBit( id, true );
return id + 1;
}
//// IClearGroupID ///////////////////////////////////////////////////////////
// Done with this groupID, so clear the entry in the bit table.
void plGeometrySpan::IClearGroupID( UInt32 groupID )
{
fInstanceGroupIDFlags.ClearBit( groupID - 1 );
}
//// IGetInstanceGroup ///////////////////////////////////////////////////////
// This does the whole hash table thing lookup during read. If:
// - The group ID does not yet exist:
// - Allocates a new hsTArray, puts it into the hash table if expectedCount > 1,
// sets the groupID flag, and returns a pointer to the array.
// - The group ID does exist:
// - If the array count is less than expectedCount - 1, returns the array
// - else sets the hash entry to nil and returns the array.
// Since we want to clear the hash table as soon as possible, but don't want
// to search the entire hash table every time to make sure its empty, we
// keep an ID of the highest element in the hash table that's set; every time
// we remove an entry, we decrement this ID until we hit a used pointer again;
// if we don't find one, we reset the array.
hsTArray<plGeometrySpan *> *plGeometrySpan::IGetInstanceGroup( UInt32 groupID, UInt32 expectedCount )
{
hsTArray<plGeometrySpan *> *array;
groupID--; // Make it our array index
if( fInstanceGroups.GetCount() <= groupID || fInstanceGroups[ groupID ] == nil )
{
// Not yet in the list--make a new hsTArray
array = TRACKED_NEW hsTArray<plGeometrySpan *>;
fInstanceGroupIDFlags.SetBit( groupID, true );
if( expectedCount > 1 )
{
if( fInstanceGroups.GetCount() <= groupID )
fInstanceGroups.ExpandAndZero( groupID + 1 );
fInstanceGroups[ groupID ] = array;
if( fHighestReadInstanceGroup < groupID + 1 )
fHighestReadInstanceGroup = groupID + 1;
}
return array;
}
else
{
// In the list...get it, but are we done with it?
array = fInstanceGroups[ groupID ];
if( expectedCount == array->GetCount() + 1 ) // I.E. next Append() will make it ==
{
// Done with it, remove from hash table
fInstanceGroups[ groupID ] = nil;
// Find new fHighestReadInstanceGroup
for( ; fHighestReadInstanceGroup > 0 && fInstanceGroups[ fHighestReadInstanceGroup - 1 ] == nil;
fHighestReadInstanceGroup-- );
if( fHighestReadInstanceGroup == 0 )
fInstanceGroups.Reset();
}
// Either way, return the array
return array;
}
}
//// IDuplicateUniqueData ////////////////////////////////////////////////////
// Does the copy on all unique data--i.e. data copied for both clones and
// instances.
void plGeometrySpan::IDuplicateUniqueData( const plGeometrySpan *source )
{
fCreating = source->fCreating;
fVertAccum = source->fVertAccum;
fIndexAccum = source->fIndexAccum;
fMaterial = source->fMaterial;
fLocalToWorld = source->fLocalToWorld;
fWorldToLocal = source->fWorldToLocal;
fLocalBounds = source->fLocalBounds;
fWorldBounds = source->fWorldBounds;
fFogEnviron = source->fFogEnviron;
fBaseMatrix = source->fBaseMatrix;
fNumMatrices = source->fNumMatrices;
fLocalUVWChans = source->fLocalUVWChans;
fFormat = source->fFormat;
fProps = source->fProps;
fNumVerts = source->fNumVerts;
fNumIndices = source->fNumIndices;
fDecalLevel = source->fDecalLevel;
if( source->fIndexData != nil )
{
fIndexData = TRACKED_NEW UInt16[ fNumIndices ];
memcpy( fIndexData, source->fIndexData, sizeof( UInt16 ) * fNumIndices );
}
else
fIndexData = nil;
if( source->fDiffuseRGBA )
{
fDiffuseRGBA = TRACKED_NEW UInt32[ fNumVerts ];
memcpy( fDiffuseRGBA, source->fDiffuseRGBA, sizeof( UInt32 ) * fNumVerts );
}
else
fDiffuseRGBA = nil;
if( source->fSpecularRGBA )
{
fSpecularRGBA = TRACKED_NEW UInt32[ fNumVerts ];
memcpy( fSpecularRGBA, source->fSpecularRGBA, sizeof( UInt32 ) * fNumVerts );
}
else
fSpecularRGBA = nil;
fLocalToOBB = source->fLocalToOBB;
fOBBToLocal = source->fOBBToLocal;
}
//// CopyFrom ////////////////////////////////////////////////////////////////
// Duplicate this span from a given span.
void plGeometrySpan::CopyFrom( const plGeometrySpan *source )
{
UInt32 size;
// Just to make sure
ClearBuffers();
IDuplicateUniqueData( source );
fInstanceRefs = nil;
fInstanceGroupID = kNoGroupID;
if( source->fVertexData != nil )
{
size = GetVertexSize( fFormat );
fVertexData = TRACKED_NEW UInt8[ size * fNumVerts ];
memcpy( fVertexData, source->fVertexData, size * fNumVerts );
}
else
fVertexData = nil;
if( source->fMultColor )
{
fMultColor = TRACKED_NEW hsColorRGBA[ fNumVerts ];
memcpy( fMultColor, source->fMultColor, sizeof(hsColorRGBA) * fNumVerts );
}
else
{
fMultColor = nil;
}
if( source->fAddColor )
{
fAddColor = TRACKED_NEW hsColorRGBA[ fNumVerts ];
memcpy( fAddColor, source->fAddColor, sizeof(hsColorRGBA) * fNumVerts );
}
else
{
fAddColor = nil;
}
}
//// Read ////////////////////////////////////////////////////////////////////
void plGeometrySpan::Read( hsStream *stream )
{
UInt32 size, i;
hsAssert( !fCreating, "Cannot read geometry span while creating" );
// Just to make sure
ClearBuffers();
fCreating = false;
// WARNING: can't read in the material here, so hopefully the owner will set it for us...
// Same with the fog environ
fLocalToWorld.Read( stream );
fWorldToLocal.Read( stream );
fLocalBounds.Read( stream );
fWorldBounds = fLocalBounds;
fWorldBounds.Transform(&fLocalToWorld);
fOBBToLocal.Read(stream);
fLocalToOBB.Read(stream);
fBaseMatrix = stream->ReadSwap32();
fNumMatrices = stream->ReadByte();
fLocalUVWChans = stream->ReadSwap16();
fMaxBoneIdx = stream->ReadSwap16();
fPenBoneIdx = stream->ReadSwap16();
fMinDist = stream->ReadSwapScalar();
fMaxDist = stream->ReadSwapScalar();
fFormat = stream->ReadByte();
fProps = stream->ReadSwap32();
fNumVerts = stream->ReadSwap32();
fNumIndices = stream->ReadSwap32();
// FIXME MAJOR VERSION
// remove these two lines. No more patches.
stream->ReadSwap32();
stream->ReadByte();
fDecalLevel = stream->ReadSwap32();
if( fProps & kWaterHeight )
fWaterHeight = stream->ReadSwapScalar();
if( fNumVerts > 0 )
{
size = GetVertexSize( fFormat );
fVertexData = TRACKED_NEW UInt8[ size * fNumVerts ];
stream->Read( size * fNumVerts, fVertexData );
fMultColor = TRACKED_NEW hsColorRGBA[ fNumVerts ];
fAddColor = TRACKED_NEW hsColorRGBA[ fNumVerts ];
for( i = 0; i < fNumVerts; i++ )
{
fMultColor[ i ].Read( stream );
fAddColor[ i ].Read( stream );
}
fDiffuseRGBA = TRACKED_NEW UInt32[ fNumVerts ];
fSpecularRGBA = TRACKED_NEW UInt32[ fNumVerts ];
stream->ReadSwap32( fNumVerts, fDiffuseRGBA );
stream->ReadSwap32( fNumVerts, fSpecularRGBA );
}
else
{
fVertexData = nil;
fMultColor = nil;
fAddColor = nil;
fDiffuseRGBA = nil;
fSpecularRGBA = nil;
}
if( fNumIndices > 0 )
{
fIndexData = TRACKED_NEW UInt16[ fNumIndices ];
stream->ReadSwap16( fNumIndices, fIndexData );
}
else
fIndexData = nil;
// Read the group ID, then look up our instanceRef array from it
fInstanceGroupID = stream->ReadSwap32();
if( fInstanceGroupID != kNoGroupID )
{
UInt32 count = stream->ReadSwap32();
fInstanceRefs = IGetInstanceGroup( fInstanceGroupID, count );
fInstanceRefs->Append( this );
hsAssert( fInstanceRefs != nil, "Cannot locate fInstanceRefs on plGeometrySpan::Read()" );
}
}
//// Write ///////////////////////////////////////////////////////////////////
void plGeometrySpan::Write( hsStream *stream )
{
UInt32 size, i;
hsAssert( !fCreating, "Cannot write geometry span while creating" );
// WARNING: can't write out the material here, so hopefully the owner will do it for us...
// Same with the fog environ
fLocalToWorld.Write( stream );
fWorldToLocal.Write( stream );
fLocalBounds.Write( stream );
fOBBToLocal.Write(stream);
fLocalToOBB.Write(stream);
stream->WriteSwap32( fBaseMatrix );
stream->WriteByte( fNumMatrices );
stream->WriteSwap16(fLocalUVWChans);
stream->WriteSwap16(fMaxBoneIdx);
stream->WriteSwap16((UInt16)fPenBoneIdx);
stream->WriteSwapScalar(fMinDist);
stream->WriteSwapScalar(fMaxDist);
stream->WriteByte( fFormat );
stream->WriteSwap32( fProps );
stream->WriteSwap32( fNumVerts );
stream->WriteSwap32( fNumIndices );
// FIXME MAJOR VERSION
// Remove these two lines.
stream->WriteSwap32(0);
stream->WriteByte(0);
stream->WriteSwap32( fDecalLevel );
if( fProps & kWaterHeight )
stream->WriteSwapScalar(fWaterHeight);
if( fNumVerts > 0 )
{
size = GetVertexSize( fFormat );
stream->Write( size * fNumVerts, fVertexData );
for( i = 0; i < fNumVerts; i++ )
{
fMultColor[ i ].Write( stream );
fAddColor[ i ].Write( stream );
}
stream->WriteSwap32( fNumVerts, fDiffuseRGBA );
stream->WriteSwap32( fNumVerts, fSpecularRGBA );
}
if( fNumIndices > 0 )
{
stream->WriteSwap16( fNumIndices, fIndexData );
}
// Write the groupID as well as the count for instanceRefs. This way
stream->WriteSwap32( fInstanceGroupID );
if( fInstanceGroupID != kNoGroupID )
stream->WriteSwap32( fInstanceRefs->GetCount() );
else
{
hsAssert( fInstanceRefs == nil, "Nil instanceRefs array but no group ID, non sequitur" );
}
}
//// GetVertexSize ///////////////////////////////////////////////////////////
UInt32 plGeometrySpan::GetVertexSize( UInt8 format )
{
UInt32 size;
size = sizeof( float ) * ( 3 + 3 ); // pos + normal
// Taken out 8.6.2001 mcn - Diffuse and specular are now separate arrays
// size += sizeof( DWORD ) * 2; // diffuse + specular
size += sizeof( float ) * 3 * CalcNumUVs( format );
switch( format & kSkinWeightMask )
{
case kSkinNoWeights: break;
case kSkin1Weight: size += sizeof( float ) * 1; break;
case kSkin2Weights: size += sizeof( float ) * 2; break;
case kSkin3Weights: size += sizeof( float ) * 3; break;
default: hsAssert( false, "Bad weight count in GetVertexSize()" );
}
if( format & kSkinIndices )
size += sizeof(UInt32);
return size;
}
//// BeginCreate //////////////////////////////////////////////////////////////
void plGeometrySpan::BeginCreate( hsGMaterial *material, const hsMatrix44 &l2wMatrix, UInt8 format )
{
fCreating = true;
fMaterial = material;
fLocalToWorld = l2wMatrix;
fLocalToWorld.GetInverse( &fWorldToLocal );
fFormat = format;
}
//// AddVertex ////////////////////////////////////////////////////////////////
// Note: uvPtrArray is an array of pointers to hsPoint3s. If a pointer is nil,
// that UV channel (and all above them) are not used. The array of pointers
// MUST be of size kMaxNumUVChannels.
UInt16 plGeometrySpan::AddVertex( hsPoint3 *position, hsPoint3 *normal, hsColorRGBA& multColor, hsColorRGBA& addColor,
hsPoint3 **uvPtrArray, float weight1, float weight2, float weight3, UInt32 indices )
{
AddVertex( position, normal, 0, 0, uvPtrArray, weight1, weight2, weight3, indices );
int idx = fVertAccum.GetCount() - 1;
TempVertex& vert = fVertAccum[idx];
vert.fMultColor = multColor;
vert.fAddColor = addColor;
return idx;
}
UInt16 plGeometrySpan::AddVertex( hsPoint3 *position, hsPoint3 *normal, UInt32 hexColor, UInt32 specularColor,
hsPoint3 **uvPtrArray, float weight1, float weight2, float weight3, UInt32 indices )
{
TempVertex vert;
int i, numWeights;
hsAssert( fCreating, "Calling AddVertex() on a non-creating plGeometrySpan!" );
// UV channels
if( uvPtrArray != nil )
{
for( i = 0; i < kMaxNumUVChannels; i++ )
{
if( uvPtrArray[ i ] == nil )
break;
vert.fUVs[ i ] = *(uvPtrArray[ i ]);
}
}
else
i = 0;
hsAssert( GetNumUVs() == i, "Incorrect number of UVs passed to AddVertex()" );
switch( fFormat & kSkinWeightMask )
{
case kSkin3Weights:
numWeights = 3;
vert.fWeights[ 0 ] = weight1;
vert.fWeights[ 1 ] = weight2;
vert.fWeights[ 2 ] = weight3;
vert.fIndices = indices;
break;
case kSkin2Weights:
numWeights = 2;
vert.fWeights[ 0 ] = weight1;
vert.fWeights[ 1 ] = weight2;
vert.fIndices = indices;
break;
case kSkin1Weight:
numWeights = 1;
vert.fWeights[ 0 ] = weight1;
vert.fIndices = indices;
break;
default:
case kSkinNoWeights:
numWeights = 0;
break;
}
vert.fPosition = *position;
vert.fNormal = *normal;
vert.fColor = hexColor;
vert.fSpecularColor = specularColor;
vert.fMultColor.Set( 1.f, 1.f, 1.f, 1.f );
vert.fAddColor.Set( 0, 0, 0, 1.f );
fVertAccum.Append( vert );
return fVertAccum.GetCount() - 1;
}
//// AddIndex Variations //////////////////////////////////////////////////////
void plGeometrySpan::AddIndex( UInt16 index )
{
hsAssert( fCreating, "Calling AddIndex() on a non-creating plGeometrySpan!" );
fIndexAccum.Append( index );
}
void plGeometrySpan::AddTriIndices( UInt16 index1, UInt16 index2, UInt16 index3 )
{
hsAssert( fCreating, "Calling AddTriIndices() on a non-creating plGeometrySpan!" );
fIndexAccum.Append( index1 );
fIndexAccum.Append( index2 );
fIndexAccum.Append( index3 );
}
//// AddTriangle //////////////////////////////////////////////////////////////
void plGeometrySpan::AddTriangle( hsPoint3 *vert1, hsPoint3 *vert2, hsPoint3 *vert3, UInt32 color )
{
hsVector3 twoTo1, twoTo3, normal;
hsPoint3 normalPt;
hsAssert( fCreating, "Calling AddTriangle() on a non-creating plGeometrySpan!" );
twoTo1.Set( vert1, vert2 );
twoTo3.Set( vert3, vert2 );
normal = twoTo1 % twoTo3;
normalPt.fX = normal.fX;
normalPt.fY = normal.fY;
normalPt.fZ = normal.fZ;
AddIndex( AddVertex( vert1, &normalPt, color, 0 ) );
AddIndex( AddVertex( vert2, &normalPt, color, 0 ) );
AddIndex( AddVertex( vert3, &normalPt, color, 0 ) );
}
//// AddVertexArray ///////////////////////////////////////////////////////////
void plGeometrySpan::AddVertexArray( UInt32 count, hsPoint3 *positions, hsVector3 *normals, UInt32 *colors, hsPoint3* uvws, int uvwsPerVtx )
{
hsAssert( fCreating, "Calling AddTriIndices() on a non-creating plGeometrySpan!" );
UInt32 i, dest;
// This test actually does work, even if it's bad form...
hsAssert( GetNumUVs() == uvwsPerVtx, "Calling wrong AddVertex() for plGeometrySpan format" );
dest = fVertAccum.GetCount();
fVertAccum.SetCount( dest + count );
for( i = 0; i < count; i++, dest++ )
{
fVertAccum[ dest ].fPosition = positions[ i ];
if( normals != nil )
{
// Stupid hsPoint3...I COULD change it, but why?
fVertAccum[ dest ].fNormal.fX = normals[ i ].fX;
fVertAccum[ dest ].fNormal.fY = normals[ i ].fY;
fVertAccum[ dest ].fNormal.fZ = normals[ i ].fZ;
}
else
fVertAccum[ dest ].fNormal.Set( 0, 0, 0 );
if( colors != nil )
fVertAccum[ dest ].fColor = colors[ i ];
else
fVertAccum[ dest ].fColor = 0xffffffff;
if( uvws && uvwsPerVtx )
{
int j;
for( j = 0; j < uvwsPerVtx; j++ )
fVertAccum[ dest ].fUVs[ j ] = uvws[j];
uvws += uvwsPerVtx;
}
}
}
//// AddIndexArray ////////////////////////////////////////////////////////////
void plGeometrySpan::AddIndexArray( UInt32 count, UInt16 *indices )
{
hsAssert( fCreating, "Calling AddTriIndices() on a non-creating plGeometrySpan!" );
UInt32 i, dest;
dest = fIndexAccum.GetCount();
fIndexAccum.SetCount( dest + count );
for( i = 0; i < count; i++, dest++ )
fIndexAccum[ dest ] = indices[ i ];
}
//// EndCreate ////////////////////////////////////////////////////////////////
void plGeometrySpan::EndCreate( void )
{
hsBounds3Ext bounds;
UInt32 i, size;
UInt8 *tempPtr;
hsAssert( fCreating, "Calling EndCreate() on a non-creating plGeometrySpan!" );
/// If we're empty, just clean up and return
if( fVertAccum.GetCount() <= 0 )
{
delete [] fVertexData;
fVertexData = nil;
fNumVerts = 0;
delete [] fIndexData;
fIndexData = nil;
fNumIndices = 0;
fVertAccum.Reset();
fIndexAccum.Reset();
fCreating = false;
return;
}
/// Convert vertices
bounds.MakeEmpty();
size = GetVertexSize( fFormat );
if( fVertexData == nil || fNumVerts < fVertAccum.GetCount() )
{
if( fVertexData != nil )
delete [] fVertexData;
fNumVerts = fVertAccum.GetCount();
fVertexData = TRACKED_NEW UInt8[ size * fNumVerts ];
delete [] fMultColor;
fMultColor = TRACKED_NEW hsColorRGBA[ fNumVerts ];
delete [] fAddColor;
fAddColor = TRACKED_NEW hsColorRGBA[ fNumVerts ];
delete [] fDiffuseRGBA;
delete [] fSpecularRGBA;
fDiffuseRGBA = TRACKED_NEW UInt32[ fNumVerts ];
fSpecularRGBA = TRACKED_NEW UInt32[ fNumVerts ];
}
else
fNumVerts = fVertAccum.GetCount();
for( i = 0, tempPtr = fVertexData; i < fNumVerts; i++ )
{
// Get position, normal, color
memcpy( tempPtr, &fVertAccum[ i ], 6 * sizeof(float) );
tempPtr += 6 * sizeof(float);
// Get Uvs
int numUvs = GetNumUVs();
if( numUvs > 0 )
{
memcpy( tempPtr, &fVertAccum[ i ].fUVs[ 0 ], numUvs * 3 * sizeof(float) );
tempPtr += numUvs * 3 * sizeof(float);
}
// Get Weights
if( fFormat & kSkinWeightMask )
{
int numWeights = 0;
switch( fFormat & kSkinWeightMask )
{
case kSkin1Weight: numWeights = 1; break;
case kSkin2Weights: numWeights = 2; break;
case kSkin3Weights: numWeights = 3; break;
default: hsAssert(false, "Garbage for weight format"); break;
}
memcpy( tempPtr, &fVertAccum[ i ].fWeights[ 0 ], numWeights * sizeof(float) );
tempPtr += numWeights * sizeof(float);
if( fFormat & kSkinIndices )
{
memcpy( tempPtr, &fVertAccum[ i ].fIndices, sizeof(UInt32) );
tempPtr += sizeof(UInt32);
}
}
fMultColor[i] = fVertAccum[i].fMultColor;
fAddColor[i] = fVertAccum[i].fAddColor;
fDiffuseRGBA[ i ] = fVertAccum[ i ].fColor;
fSpecularRGBA[ i ] = fVertAccum[ i ].fSpecularColor;
hsPoint3 pBnd = fLocalToOBB * fVertAccum[i].fPosition;
bounds.Union(&pBnd);
}
bounds.Transform(&fOBBToLocal);
if( fProps & kWaterHeight )
{
AdjustBounds(bounds);
}
fLocalBounds = bounds;
fWorldBounds = fLocalBounds;
fWorldBounds.Transform(&fLocalToWorld);
/// Convert indices
if( fIndexAccum.GetCount() == 0 ) // Allowed for patches
{
delete [] fIndexData;
fIndexData = nil;
fNumIndices = 0;
}
else if( fIndexData == nil || fNumIndices < fIndexAccum.GetCount() )
{
if( fIndexData != nil )
delete [] fIndexData;
fNumIndices = fIndexAccum.GetCount();
fIndexData = TRACKED_NEW UInt16[ fNumIndices ];
}
else
fNumIndices = fIndexAccum.GetCount();
for( i = 0; i < fNumIndices; i++ )
fIndexData[ i ] = fIndexAccum[ i ];
/// Cleanup
fVertAccum.Reset();
fIndexAccum.Reset();
fCreating = false;
}
void plGeometrySpan::AdjustBounds(hsBounds3Ext& bnd) const
{
hsBounds3Ext wBnd = bnd;
wBnd.Transform(&fLocalToWorld);
const hsScalar kMaxWaveHeight(5.f);
hsBounds3Ext rebound;
rebound.MakeEmpty();
hsPoint3 pos = wBnd.GetMins();
pos.fZ = fWaterHeight - kMaxWaveHeight;
rebound.Union(&pos);
pos = wBnd.GetMaxs();
pos.fZ = fWaterHeight + kMaxWaveHeight;
rebound.Union(&pos);
bnd = rebound;
bnd.Transform(&fWorldToLocal);
}
//// ExtractVertex ////////////////////////////////////////////////////////////
void plGeometrySpan::ExtractInitColor( UInt32 index, hsColorRGBA *multColor, hsColorRGBA *addColor) const
{
if( multColor )
*multColor = fMultColor[index];
if( addColor )
*addColor = fAddColor[index];
}
// Extracts the given vertex out of the vertex buffer and into the pointers
// given.
void plGeometrySpan::ExtractVertex( UInt32 index, hsPoint3 *pos, hsVector3 *normal, hsColorRGBA *color, hsColorRGBA *specColor )
{
UInt32 hex, spec;
ExtractVertex( index, pos, normal, &hex, &spec );
color->a = ( ( hex >> 24 ) & 0xff ) / 255.0f;
color->r = ( ( hex >> 16 ) & 0xff ) / 255.0f;
color->g = ( ( hex >> 8 ) & 0xff ) / 255.0f;
color->b = ( ( hex >> 0 ) & 0xff ) / 255.0f;
if( specColor != nil )
{
specColor->a = ( ( spec >> 24 ) & 0xff ) / 255.0f;
specColor->r = ( ( spec >> 16 ) & 0xff ) / 255.0f;
specColor->g = ( ( spec >> 8 ) & 0xff ) / 255.0f;
specColor->b = ( ( spec >> 0 ) & 0xff ) / 255.0f;
}
}
//// ExtractVertex ////////////////////////////////////////////////////////////
// Hex-color version of ExtractVertex.
void plGeometrySpan::ExtractVertex( UInt32 index, hsPoint3 *pos, hsVector3 *normal, UInt32 *color, UInt32 *specColor )
{
UInt8 *basePtr;
float *fPtr;
/// Where?
hsAssert( index < fNumVerts, "Invalid index passed to ExtractVertex()" );
basePtr = fVertexData + index * GetVertexSize( fFormat );
/// Copy over point and normal
fPtr = (float *)basePtr;
pos->fX = fPtr[ 0 ];
pos->fY = fPtr[ 1 ];
pos->fZ = fPtr[ 2 ];
normal->fX = fPtr[ 3 ];
normal->fY = fPtr[ 4 ];
normal->fZ = fPtr[ 5 ];
fPtr += 6;
/// Diffuse color
*color = fDiffuseRGBA[ index ];
if( specColor != nil )
*specColor = fSpecularRGBA[ index ];
}
//// ExtractUv ////////////////////////////////////////////////////////////////
void plGeometrySpan::ExtractUv( UInt32 vIdx, UInt8 uvIdx, hsPoint3 *uv )
{
UInt8 *basePtr;
float *fPtr;
/// Where?
hsAssert( vIdx < fNumVerts, "Invalid index passed to ExtractVertex()" );
hsAssert( uvIdx < GetNumUVs(), "Invalid UV index passed to ExtractVertex()" );
basePtr = fVertexData + vIdx * GetVertexSize( fFormat );
/// Skip over point, normal, and color and specular color
basePtr += 12 + 12;
fPtr = (float *)basePtr;
fPtr += 3 * uvIdx;
uv->fX = *fPtr++;
uv->fY = *fPtr++;
uv->fZ = *fPtr;
}
//// ExtractWeights ///////////////////////////////////////////////////////////
// Gets the weights out of the vertex data.
void plGeometrySpan::ExtractWeights( UInt32 vIdx, float *weightArray, UInt32 *indices )
{
UInt8 *basePtr;
float *fPtr;
UInt32 *dPtr;
int numWeights;
/// Where?
hsAssert( vIdx < fNumVerts, "Invalid index passed to ExtractVertex()" );
basePtr = fVertexData + vIdx * GetVertexSize( fFormat );
/// Copy over weights
basePtr += sizeof( float ) * ( 6 + 3 * GetNumUVs() );
fPtr = (float *)basePtr;
switch( fFormat & kSkinWeightMask )
{
case kSkinNoWeights: hsAssert( false, "ExtractWeights() called on a span with no weights" ); break;
case kSkin1Weight: numWeights = 1; break;
case kSkin2Weights: numWeights = 2; break;
case kSkin3Weights: numWeights = 3; break;
default: hsAssert( false, "Bad number of weights in ExtractWeights()" );
}
memcpy( weightArray, fPtr, sizeof( float ) * numWeights );
if( fFormat & kSkinIndices )
{
fPtr += numWeights;
dPtr = (UInt32 *)fPtr;
*indices = *dPtr;
}
}
//// StuffVertex //////////////////////////////////////////////////////////////
// Stuffs the given vertex data into the vertex buffer. Vertex must already
// exist!
void plGeometrySpan::StuffVertex( UInt32 index, hsPoint3 *pos, hsPoint3 *normal, hsColorRGBA *color, hsColorRGBA *specColor )
{
UInt8 *basePtr;
float *fPtr;
/// Where?
hsAssert( index < fNumVerts, "Invalid index passed to StuffVertex()" );
basePtr = fVertexData + index * GetVertexSize( fFormat );
/// Copy over point and normal
fPtr = (float *)basePtr;
fPtr[ 0 ] = pos->fX;
fPtr[ 1 ] = pos->fY;
fPtr[ 2 ] = pos->fZ;
fPtr[ 3 ] = normal->fX;
fPtr[ 4 ] = normal->fY;
fPtr[ 5 ] = normal->fZ;
fPtr += 6;
/// Diffuse color
StuffVertex( index, color, specColor );
}
void plGeometrySpan::StuffVertex( UInt32 index, hsColorRGBA *color, hsColorRGBA *specColor )
{
UInt8 r, g, b, a;
/// Where?
hsAssert( index < fNumVerts, "Invalid index passed to StuffVertex()" );
a = (UInt8)( color->a >= 1 ? 255 : color->a <= 0 ? 0 : color->a * 255.0 );
r = (UInt8)( color->r >= 1 ? 255 : color->r <= 0 ? 0 : color->r * 255.0 );
g = (UInt8)( color->g >= 1 ? 255 : color->g <= 0 ? 0 : color->g * 255.0 );
b = (UInt8)( color->b >= 1 ? 255 : color->b <= 0 ? 0 : color->b * 255.0 );
fDiffuseRGBA[ index ] = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | ( b );
if( specColor != nil )
{
a = (UInt8)( specColor->a >= 1 ? 255 : specColor->a <= 0 ? 0 : specColor->a * 255.0 );
r = (UInt8)( specColor->r >= 1 ? 255 : specColor->r <= 0 ? 0 : specColor->r * 255.0 );
g = (UInt8)( specColor->g >= 1 ? 255 : specColor->g <= 0 ? 0 : specColor->g * 255.0 );
b = (UInt8)( specColor->b >= 1 ? 255 : specColor->b <= 0 ? 0 : specColor->b * 255.0 );
fSpecularRGBA[ index ] = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | ( b );
}
}