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