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.
784 lines
26 KiB
784 lines
26 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==*/ |
|
////////////////////////////////////////////////////////////////////////////// |
|
// // |
|
// plDrawableSpans Class Export-only Functions // |
|
// // |
|
//// Version History ///////////////////////////////////////////////////////// |
|
// // |
|
// Created 4.3.2001 mcn // |
|
// // |
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
#include "HeadSpin.h" |
|
#include "plDrawableSpans.h" |
|
#include "hsStream.h" |
|
#include "hsResMgr.h" |
|
#include "plPipeline.h" |
|
#include "plGeometrySpan.h" |
|
|
|
#include "plSpaceTree.h" |
|
#include "plSpaceTreeMaker.h" // This is fun and amusing and wonderful to have here. |
|
// Keep it here forever. |
|
#include "plSurface/hsGMaterial.h" |
|
#include "plPipeline/plFogEnvironment.h" |
|
#include "pnMessage/plRefMsg.h" |
|
#include "pnMessage/plNodeRefMsg.h" // for NodeRefMsg |
|
#include "plMessage/plDeviceRecreateMsg.h" |
|
#include "plPipeline/plGBufferGroup.h" |
|
#include "plSurface/hsGMaterial.h" |
|
#include "plSurface/plLayerInterface.h" |
|
#include "plGImage/plBitmap.h" |
|
#include "plGLight/plLightInfo.h" |
|
#include "plgDispatch.h" |
|
|
|
#include "plStatusLog/plStatusLog.h" |
|
|
|
//#define VERT_LOG |
|
|
|
//// Write /////////////////////////////////////////////////////////////////// |
|
|
|
void plDrawableSpans::Write( hsStream* s, hsResMgr* mgr ) |
|
{ |
|
uint32_t i, j, count; |
|
|
|
// Make sure we're optimized before we write (should be tho) |
|
// Optimize(); |
|
|
|
// Make sure all the garbage is cleaned up |
|
if( fNeedCleanup ) |
|
IRemoveGarbage(); |
|
|
|
// Parent write |
|
plDrawable::Write(s, mgr); |
|
|
|
s->WriteLE32( fProps ); |
|
s->WriteLE32( fCriteria ); |
|
s->WriteLE32( fRenderLevel.fLevel ); |
|
|
|
/// Write out the material keys |
|
s->WriteLE32( fMaterials.GetCount() ); |
|
for( i = 0; i < fMaterials.GetCount(); i++ ) |
|
mgr->WriteKey( s, fMaterials[ i ] ); |
|
|
|
/// Write out the icicles |
|
s->WriteLE32( fIcicles.GetCount() ); |
|
for( i = 0; i < fIcicles.GetCount(); i++ ) |
|
fIcicles[ i ].Write( s ); |
|
|
|
/// Write out the patches |
|
// FIXME MAJOR VERSION |
|
// no more patches, remove this line |
|
s->WriteLE32(0); |
|
|
|
/// Write out the index table based on the pointer array |
|
count = fSpans.GetCount(); |
|
s->WriteLE32( count ); |
|
for( i = 0; i < count; i++ ) |
|
{ |
|
uint8_t *icicle = (uint8_t *)fSpans[ i ], *base = (uint8_t *)fIcicles.AcquireArray(); |
|
j = (uint32_t)( icicle - base ) / sizeof( plIcicle ); |
|
s->WriteLE32( j ); |
|
} |
|
|
|
/// Write out the common keys |
|
for( i = 0; i < count; i++ ) |
|
{ |
|
// The fog environ key |
|
mgr->WriteKey( s, fSpans[ i ]->fFogEnvironment ); |
|
} |
|
|
|
/// Write out the bounds and stuff |
|
if( count > 0 ) |
|
{ |
|
fLocalBounds.Write(s); |
|
fWorldBounds.Write(s); |
|
fMaxWorldBounds.Write(s); |
|
} |
|
|
|
for( i = 0; i < count; i++ ) |
|
{ |
|
if( fSpans[i]->fProps & plSpan::kPropHasPermaLights ) |
|
{ |
|
uint32_t lcnt = fSpans[i]->fPermaLights.GetCount(); |
|
s->WriteLE32(lcnt); |
|
int j; |
|
for( j = 0; j < lcnt; j++ ) |
|
mgr->WriteKey( s, fSpans[i]->fPermaLights[j]); |
|
} |
|
if( fSpans[i]->fProps & plSpan::kPropHasPermaProjs ) |
|
{ |
|
uint32_t lcnt = fSpans[i]->fPermaProjs.GetCount(); |
|
s->WriteLE32(lcnt); |
|
int j; |
|
for( j = 0; j < lcnt; j++ ) |
|
mgr->WriteKey( s, fSpans[i]->fPermaProjs[j]); |
|
} |
|
} |
|
|
|
/// Write out the source spans if necessary |
|
s->WriteLE32( fSourceSpans.GetCount() ); |
|
if( fSourceSpans.GetCount() > 0 ) |
|
{ |
|
for( i = 0; i < fSourceSpans.GetCount(); i++ ) |
|
fSourceSpans[ i ]->Write( s ); |
|
} |
|
|
|
count = fLocalToWorlds.GetCount(); |
|
s->WriteLE32(count); |
|
for( i = 0; i < count; i++ ) |
|
{ |
|
fLocalToWorlds[i].Write(s); |
|
fWorldToLocals[i].Write(s); |
|
|
|
fLocalToBones[i].Write(s); |
|
fBoneToLocals[i].Write(s); |
|
} |
|
|
|
// Write out the drawInterface index arrays |
|
s->WriteLE32( fDIIndices.GetCount() ); |
|
for( i = 0; i < fDIIndices.GetCount(); i++ ) |
|
{ |
|
plDISpanIndex *array = fDIIndices[ i ]; |
|
|
|
s->WriteLE32( array->fFlags ); |
|
s->WriteLE32( array->GetCount() ); |
|
for( j = 0; j < array->GetCount(); j++ ) |
|
s->WriteLE32( (*array)[ j ] ); |
|
} |
|
|
|
// Write the groups out |
|
count = fGroups.GetCount(); |
|
|
|
s->WriteLE( count ); |
|
for( i = 0; i < count; i++ ) |
|
{ |
|
#ifdef VERT_LOG |
|
|
|
hsUNIXStream log; |
|
log.Open("log\\GBuf.log", "ab"); |
|
char buf[256]; |
|
sprintf(buf, "Drawable Span: %s, GroupNum: %u\r\n", GetKeyName(), i); |
|
log.WriteString(buf); |
|
log.Close(); |
|
#endif |
|
fGroups[ i ]->Write( s ); |
|
} |
|
|
|
/// Other stuff |
|
mgr->WriteCreatable(s, fSpaceTree); |
|
|
|
mgr->WriteKey(s, fSceneNode); |
|
|
|
/// All done! |
|
} |
|
|
|
//// AddDISpans ////////////////////////////////////////////////////////////// |
|
// Adds a drawInterface's geometry spans to the list to be collapsed into |
|
// buffers. |
|
|
|
uint32_t plDrawableSpans::AddDISpans( hsTArray<plGeometrySpan *> &spans, uint32_t index ) |
|
{ |
|
int i; |
|
uint32_t spanIdx; |
|
plSpan *span; |
|
hsBounds3Ext bounds; |
|
|
|
|
|
/// Do garbage cleanup first |
|
if( fNeedCleanup ) |
|
IRemoveGarbage(); |
|
|
|
if (index == (uint32_t)-1) // need a new one |
|
{ |
|
/// Create a lookup entry |
|
index = fDIIndices.GetCount(); |
|
fDIIndices.Append( new plDISpanIndex ); |
|
fDIIndices[ index ]->fFlags = plDISpanIndex::kNone; |
|
} |
|
plDISpanIndex *spanLookup = fDIIndices[ index ]; |
|
|
|
|
|
/// Add the geometry spans to our list. Also add our internal span |
|
/// copies |
|
for( i = 0; i < spans.GetCount(); i++ ) |
|
{ |
|
spanLookup->Append( fSourceSpans.GetCount() ); |
|
spans[ i ]->fSpanRefIndex = fSourceSpans.GetCount(); |
|
fSourceSpans.Append( spans[ i ] ); |
|
|
|
spanIdx = fIcicles.GetCount(); |
|
fIcicles.Append( plIcicle() ); |
|
plIcicle *icicle = &fIcicles[ spanIdx ]; |
|
span = (plSpan *)icicle; |
|
|
|
/// Set common stuff |
|
IAssignMatIdxToSpan( span, spans[ i ]->fMaterial ); |
|
span->fLocalToWorld = spans[ i ]->fLocalToWorld; |
|
span->fWorldToLocal = spans[ i ]->fWorldToLocal; |
|
span->fProps |= ( spans[ i ]->fProps & plGeometrySpan::kPropRunTimeLight ) ? plSpan::kPropRunTimeLight : 0; |
|
if( spans[i]->fProps & plGeometrySpan::kPropNoShadowCast ) |
|
span->fProps |= plSpan::kPropNoShadowCast; |
|
if( spans[i]->fProps & plGeometrySpan::kPropNoShadow ) |
|
span->fProps |= plSpan::kPropNoShadow; |
|
if( spans[i]->fProps & plGeometrySpan::kPropForceShadow ) |
|
span->fProps |= plSpan::kPropForceShadow; |
|
if( spans[i]->fProps & plGeometrySpan::kPropReverseSort ) |
|
span->fProps |= plSpan::kPropReverseSort; |
|
if( spans[i]->fProps & plGeometrySpan::kPartialSort ) |
|
span->fProps |= plSpan::kPartialSort; |
|
if( spans[i]->fProps & plGeometrySpan::kVisLOS ) |
|
{ |
|
span->fProps |= plSpan::kVisLOS; |
|
fProps |= plDrawable::kPropHasVisLOS; |
|
} |
|
|
|
span->fNumMatrices = spans[ i ]->fNumMatrices; |
|
span->fBaseMatrix = spans[ i ]->fBaseMatrix; |
|
span->fLocalUVWChans = spans[i]->fLocalUVWChans; |
|
span->fMaxBoneIdx = spans[i]->fMaxBoneIdx; |
|
span->fPenBoneIdx = (uint16_t)(spans[i]->fPenBoneIdx); |
|
|
|
bounds = spans[ i ]->fLocalBounds; |
|
span->fLocalBounds = bounds; |
|
bounds.Transform( &span->fLocalToWorld ); |
|
span->fWorldBounds = bounds; |
|
span->fFogEnvironment = spans[ i ]->fFogEnviron; |
|
|
|
/// Add to our source indices |
|
fSpans.Append( span ); |
|
fSpanSourceIndices.Append( spanIdx ); |
|
} |
|
|
|
/// Rebuild the pointer array |
|
IRebuildSpanArray(); |
|
|
|
SetSpaceTree(nil); |
|
|
|
fOptimized = false; |
|
|
|
return index; |
|
} |
|
|
|
//// Optimize //////////////////////////////////////////////////////////////// |
|
|
|
void plDrawableSpans::Optimize( void ) |
|
{ |
|
int i; |
|
|
|
if( fOptimized ) |
|
return; |
|
|
|
/// Sort all the source spans |
|
if( !(fProps & kPropNoReSort) ) |
|
ISortSourceSpans(); |
|
|
|
/// Pack the source spans into spans and indices |
|
IPackSourceSpans(); |
|
|
|
/// Now that we're done with the source spans, get rid of them! (BLEAH!) |
|
for( i = 0; i < fSourceSpans.GetCount(); i++ ) |
|
delete fSourceSpans[ i ]; |
|
fSourceSpans.Reset(); |
|
|
|
if( fCriteria & kCritSortSpans ) |
|
{ |
|
if( !(fProps & kPropNoReSort) ) |
|
fProps |= kPropSortSpans; |
|
} |
|
if( fCriteria & kCritSortFaces ) |
|
{ |
|
fProps |= kPropSortFaces; |
|
} |
|
|
|
/// Now we do a pass at the buffer groups, asking them to tidy up |
|
for( i = 0; i < fGroups.GetCount(); i++ ) |
|
fGroups[ i ]->TidyUp(); |
|
|
|
// Look to see if we have any materials we aren't using anymore. |
|
for( i = 0; i < fMaterials.GetCount(); i++ ) |
|
{ |
|
ICheckToRemoveMaterial(i); |
|
} |
|
|
|
fReadyToRender = false; |
|
|
|
// Make the space tree (hierarchical bounds). |
|
plSpaceTreeMaker maker; |
|
maker.Reset(); |
|
for( i = 0; i < GetNumSpans(); i++ ) |
|
{ |
|
maker.AddLeaf( fSpans[ i ]->fWorldBounds, fSpans[ i ]->fProps & plSpan::kPropNoDraw ); |
|
} |
|
plSpaceTree* tree = maker.MakeTree(); |
|
SetSpaceTree(tree); |
|
|
|
/// All done! |
|
fOptimized = true; |
|
} |
|
|
|
static plStatusLog* IStartLog(const char* name, int numSpans) |
|
{ |
|
static char buff[256]; |
|
sprintf(buff, "x%s.log", name); |
|
|
|
plStatusLog* statusLog = plStatusLogMgr::GetInstance().CreateStatusLog( |
|
plStatusLogMgr::kDefaultNumLines, |
|
buff, |
|
plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe ); |
|
return statusLog; |
|
} |
|
|
|
static plStatusLog* IEndLog(plStatusLog* statusLog) |
|
{ |
|
delete statusLog; |
|
return nil; |
|
} |
|
|
|
static void ILogSpan(plStatusLog* statusLog, plGeometrySpan* geo, plVertexSpan* span, plGBufferGroup* group) |
|
{ |
|
if( span->fTypeMask & plSpan::kIcicleSpan ) |
|
{ |
|
plIcicle* ice = (plIcicle*)span; |
|
if( geo->fProps & plGeometrySpan::kFirstInstance ) |
|
{ |
|
plGBufferCell* cell = group->GetCell(span->fVBufferIdx, span->fCellIdx); |
|
uint32_t stride = group->GetVertexSize(); |
|
uint32_t ptr = cell->fVtxStart + span->fCellOffset * stride; |
|
|
|
statusLog->AddLineF("From obj <%s> mat <%s> size %d bytes grp=%d (%d offset)", |
|
geo->fMaxOwner ? geo->fMaxOwner : "<unknown>", |
|
geo->fMaterial ? geo->fMaterial->GetKey()->GetName().c_str() : "<unknown>", |
|
geo->GetVertexSize(geo->fFormat) * geo->fNumVerts + sizeof(uint16_t) * geo->fNumIndices, |
|
span->fGroupIdx, |
|
ptr |
|
); |
|
// span->fVBufferIdx, |
|
// span->fCellIdx, |
|
// span->fCellOffset, |
|
// span->fVStartIdx, |
|
// span->fVLength, |
|
// ice->fIBufferIdx, |
|
// ice->fIStartIdx, |
|
// ice->fILength |
|
} |
|
else |
|
{ |
|
statusLog->AddLineF("Instanced obj <%s> mat <%s> grp=%d (%d/%d/%d/%d/%d/%d/%d/%d)", |
|
geo->fMaxOwner ? geo->fMaxOwner : "<unknown>", |
|
geo->fMaterial ? geo->fMaterial->GetKey()->GetName().c_str() : "<unknown>", |
|
span->fGroupIdx, |
|
span->fVBufferIdx, |
|
span->fCellIdx, |
|
span->fCellOffset, |
|
span->fVStartIdx, |
|
span->fVLength, |
|
ice->fIBufferIdx, |
|
ice->fIStartIdx, |
|
ice->fILength |
|
); |
|
} |
|
} |
|
else |
|
{ |
|
if( geo->fProps & plGeometrySpan::kFirstInstance ) |
|
{ |
|
statusLog->AddLineF("From obj <%s> mat <%s> size %d bytes grp=%d (%d/%d/%d/%d/%d)", |
|
geo->fMaxOwner ? geo->fMaxOwner : "<unknown>", |
|
geo->fMaterial ? geo->fMaterial->GetKey()->GetName().c_str() : "<unknown>", |
|
geo->GetVertexSize(geo->fFormat) * geo->fNumVerts + sizeof(uint16_t) * geo->fNumIndices, |
|
span->fGroupIdx, |
|
span->fVBufferIdx, |
|
span->fCellIdx, |
|
span->fCellOffset, |
|
span->fVStartIdx, |
|
span->fVLength |
|
); |
|
} |
|
else |
|
{ |
|
statusLog->AddLineF("Instanced obj <%s> mat <%s> grp=%d (%d/%d/%d/%d/%d)", |
|
geo->fMaxOwner ? geo->fMaxOwner : "<unknown>", |
|
geo->fMaterial ? geo->fMaterial->GetKey()->GetName().c_str() : "<unknown>", |
|
span->fGroupIdx, |
|
span->fVBufferIdx, |
|
span->fCellIdx, |
|
span->fCellOffset, |
|
span->fVStartIdx, |
|
span->fVLength |
|
); |
|
} |
|
} |
|
} |
|
|
|
//// IPackSourceSpans //////////////////////////////////////////////////////// |
|
// Takes the array of source spans and converts them to our internal icicle |
|
// spans, vertex buffers and index buffers. |
|
|
|
void plDrawableSpans::IPackSourceSpans( void ) |
|
{ |
|
int i, j; |
|
hsBounds3Ext bounds; |
|
hsBitVector doneSpans; |
|
|
|
|
|
/// Calc bounds |
|
fLocalBounds.MakeEmpty(); |
|
fWorldBounds.MakeEmpty(); |
|
|
|
for( i = 0; i < fSourceSpans.GetCount(); i++ ) |
|
{ |
|
hsBounds3Ext bnd = fSourceSpans[ i ]->fLocalBounds; |
|
bnd.Transform( &fSourceSpans[ i ]->fLocalToWorld ); |
|
fWorldBounds.Union( &bnd ); |
|
} |
|
|
|
fLocalBounds = fWorldBounds; |
|
fLocalBounds.Transform( &fWorldToLocal ); |
|
fMaxWorldBounds = fWorldBounds; |
|
|
|
// It could be that instance refs in spans that we (the drawable) own |
|
// are actually spans in some other drawable. That's not currently handled. |
|
// (Making that case handled would involve rewriting the instancing implementation |
|
// to not suck ass). |
|
// So for each instance set, we make two lists of instance refs, the ones also |
|
// in this drawable (refsHere), and ones not (refsThere). If there are refsThere, |
|
// we split out the refsHere into a new instance group, removing them from the |
|
// refsThere list. If refsThere still contains spans from separate drawables, |
|
// that will be dealt with in those drawables' Optimize calls. |
|
doneSpans.Clear(); |
|
for( i = 0; i < fSourceSpans.GetCount(); i++ ) |
|
{ |
|
if( !doneSpans.IsBitSet(i) ) |
|
{ |
|
plGeometrySpan* span = fSourceSpans[i]; |
|
if( span->fInstanceRefs ) |
|
{ |
|
hsTArray<plGeometrySpan*>& refs = *(span->fInstanceRefs); |
|
hsTArray<plGeometrySpan*> refsHere; |
|
hsTArray<plGeometrySpan*> refsThere; |
|
|
|
int k; |
|
for( k = 0; k < refs.GetCount(); k++ ) |
|
{ |
|
plGeometrySpan* other = refs[k]; |
|
if( other != span ) |
|
{ |
|
int idx = fSourceSpans.Find(other); |
|
if( fSourceSpans.kMissingIndex == idx ) |
|
{ |
|
refsThere.Append(other); |
|
} |
|
else |
|
{ |
|
refsHere.Append(other); |
|
} |
|
} |
|
} |
|
if( refsThere.GetCount() ) |
|
{ |
|
if( refsHere.GetCount() ) |
|
{ |
|
span->BreakInstance(); |
|
|
|
// Okay, got to form a new instance group out of refsHere. |
|
for( k = 0; k < refsHere.GetCount(); k++ ) |
|
{ |
|
plGeometrySpan* other = refsHere[k]; |
|
|
|
other->ChangeInstance(span); |
|
|
|
doneSpans.SetBit(other->fSpanRefIndex); |
|
} |
|
} |
|
else |
|
{ |
|
span->UnInstance(); |
|
} |
|
if( refsThere.GetCount() == 1 ) |
|
{ |
|
refsThere[0]->UnInstance(); |
|
} |
|
} |
|
} |
|
doneSpans.SetBit(i); |
|
} |
|
} |
|
|
|
/// Now pack the spans |
|
doneSpans.Clear(); |
|
for( i = 0; i < fSourceSpans.GetCount(); i++ ) |
|
{ |
|
// Now we fill the rest of the data in for our span |
|
if( !doneSpans.IsBitSet( i ) ) |
|
{ |
|
if( fSourceSpans[ i ]->fProps & plGeometrySpan::kInstanced ) |
|
{ |
|
// Instanced spans--convert the first as normal, then convert the rest |
|
// using the first as reference |
|
doneSpans.SetBit( i, true ); |
|
|
|
plIcicle *baseIcicle = (plIcicle *)fSpans[ i ]; |
|
IConvertGeoSpanToIcicle( fSourceSpans[ i ], baseIcicle, 0, baseIcicle ); |
|
|
|
// Loop through the rest |
|
for( j = 0; j < fSourceSpans[ i ]->fInstanceRefs->GetCount(); j++ ) |
|
{ |
|
plGeometrySpan *other = (*fSourceSpans[ i ]->fInstanceRefs)[ j ]; |
|
if( other == fSourceSpans[ i ] ) |
|
continue; |
|
|
|
#if 0 // What exactly is this supposed to be doing? My guess is, NADA. |
|
if( IConvertGeoSpanToIcicle( other, (plIcicle *)fSpans[ other->fSpanRefIndex ], 0, baseIcicle ) ) |
|
baseIcicle = (plIcicle *)fSpans[ other->fSpanRefIndex ]; |
|
#else // What exactly is this supposed to be doing? My guess is, NADA. |
|
IConvertGeoSpanToIcicle( other, (plIcicle *)fSpans[ other->fSpanRefIndex ], 0, baseIcicle ); |
|
#endif // What exactly is this supposed to be doing? My guess is, NADA. |
|
doneSpans.SetBit( other->fSpanRefIndex, true ); |
|
} |
|
|
|
} |
|
else |
|
{ |
|
// Do normal, uninstanced conversion |
|
IConvertGeoSpanToIcicle( fSourceSpans[ i ], (plIcicle *)fSpans[ i ], 0 ); |
|
doneSpans.SetBit( i, true ); |
|
} |
|
} |
|
} |
|
|
|
#ifdef VERT_LOG |
|
hsTArray<plGeometrySpan*> order; |
|
order.SetCount(fSourceSpans.GetCount()); |
|
for( i = 0; i < fSourceSpans.GetCount(); i++ ) |
|
{ |
|
order[fSourceSpans[i]->fSpanRefIndex] = fSourceSpans[i]; |
|
} |
|
|
|
plStatusLog* statusLog = IStartLog(GetKey()->GetName(), fSourceSpans.GetCount()); |
|
for( i = 0; i < order.GetCount(); i++ ) |
|
{ |
|
plVertexSpan* vSpan = (plVertexSpan*)fSpans[i]; |
|
ILogSpan(statusLog, order[i], vSpan, fGroups[vSpan->fGroupIdx]); |
|
} |
|
statusLog = IEndLog(statusLog); |
|
#endif |
|
} |
|
|
|
//// ISortSourceSpans //////////////////////////////////////////////////////// |
|
// Does our actual optimization path by resorting all the spans into the |
|
// most efficient order possible. Also has to re-order the span lookup |
|
// table. |
|
|
|
void plDrawableSpans::ISortSourceSpans( void ) |
|
{ |
|
hsTArray<uint32_t> spanReorderTable, spanInverseTable; |
|
int i, j, idx; |
|
plGeometrySpan *tmpSpan; |
|
uint32_t tmpIdx; |
|
plSpan *tmpSpanPtr; |
|
|
|
|
|
// Init the reorder table |
|
for( i = 0; i < fSourceSpans.GetCount(); i++ ) |
|
spanReorderTable.Append( i ); |
|
|
|
// Do a nice, if naiive, sort by material (hehe by the pointers, no less) |
|
for( i = 0; i < fSourceSpans.GetCount() - 1; i++ ) |
|
{ |
|
for( j = i + 1, idx = i; j < fSourceSpans.GetCount(); j++ ) |
|
{ |
|
if( ICompareSpans( fSourceSpans[ j ], fSourceSpans[ idx ] ) < 0 ) |
|
idx = j; |
|
} |
|
|
|
// Swap idx with i, so we get the smallest pointer on top |
|
if( i != idx ) |
|
{ |
|
// Swap both the source span and our internal span |
|
tmpSpan = fSourceSpans[ i ]; |
|
fSourceSpans[ i ] = fSourceSpans[ idx ]; |
|
fSourceSpans[ idx ] = tmpSpan; |
|
|
|
fSourceSpans[ i ]->fSpanRefIndex = i; |
|
fSourceSpans[ idx ]->fSpanRefIndex = idx; |
|
|
|
tmpSpanPtr = fSpans[ i ]; |
|
fSpans[ i ] = fSpans[ idx ]; |
|
fSpans[ idx ] = tmpSpanPtr; |
|
|
|
// Also swap the entries in the reorder table |
|
tmpIdx = spanReorderTable[ i ]; |
|
spanReorderTable[ i ] = spanReorderTable[ idx ]; |
|
spanReorderTable[ idx ] = tmpIdx; |
|
} |
|
|
|
// Next! |
|
} |
|
|
|
|
|
/// Problem: our reorder table is inversed(y->x instead of x->y). Either we search for numbers, |
|
/// or we just flip it first... |
|
spanInverseTable.SetCountAndZero( spanReorderTable.GetCount() ); |
|
for( i = 0; i < spanReorderTable.GetCount(); i++ ) |
|
spanInverseTable[ spanReorderTable[ i ] ] = i; |
|
|
|
/// Now update our span xlate table |
|
for( i = 0; i < fDIIndices.GetCount(); i++ ) |
|
{ |
|
if( !fDIIndices[ i ]->IsMatrixOnly() ) |
|
{ |
|
for( j = 0; j < fDIIndices[ i ]->GetCount(); j++ ) |
|
{ |
|
idx = (*fDIIndices[ i ])[ j ]; |
|
|
|
(*fDIIndices[ i ])[ j ] = spanInverseTable[ idx ]; |
|
} |
|
} |
|
} |
|
|
|
/// Use our pointer array to rebuild the icicle array (UUUUGLY) |
|
hsTArray<plIcicle> tempIcicles; |
|
plIcicle *newIcicle; |
|
|
|
tempIcicles.SetCount( fIcicles.GetCount() ); |
|
|
|
for( i = 0, newIcicle = tempIcicles.AcquireArray(); |
|
i < fSpans.GetCount(); i++ ) |
|
{ |
|
*newIcicle = *( (plIcicle *)fSpans[ i ] ); |
|
fSpans[ i ] = newIcicle; |
|
newIcicle++; |
|
} |
|
|
|
/// Swap the two arrays out. This will basically swap the actual memory blocks, so be careful... |
|
fIcicles.Swap( tempIcicles ); |
|
tempIcicles.Reset(); |
|
} |
|
|
|
//// ICompareSpans /////////////////////////////////////////////////////////// |
|
// Sorting function for ISortSpans(). Kinda like strcmp(): returns -1 if |
|
// span1 < span2, 1 if span1 > span2, 0 if "equal". |
|
|
|
short plDrawableSpans::ICompareSpans( plGeometrySpan *span1, plGeometrySpan *span2 ) |
|
{ |
|
hsBool b1, b2; |
|
int i, j, numLayers; |
|
plBitmap *t1, *t2; |
|
|
|
|
|
/// Quick check--identical materials are easy to compare :) |
|
if( span1->fMaterial == span2->fMaterial ) |
|
return 0; |
|
|
|
/// Compare features from most to least important... |
|
|
|
// Any decal span should come after a non-decal |
|
if( span1->fDecalLevel < span2->fDecalLevel ) |
|
return -1; |
|
if( span1->fDecalLevel > span2->fDecalLevel ) |
|
return 1; |
|
// Ok, they're equal decal-wise, so find something else to judge on |
|
|
|
// Most important: is one of the materials an alpha blend? (if so, gotta |
|
// put at end, so it's "bigger") |
|
if( span1->fMaterial->GetNumLayers() > 0 && |
|
( span1->fMaterial->GetLayer( 0 )->GetState().fBlendFlags & hsGMatState::kBlendMask ) != 0 ) |
|
b1 = true; |
|
else |
|
b1 = false; |
|
|
|
if( span2->fMaterial->GetNumLayers() > 0 && |
|
( span2->fMaterial->GetLayer( 0 )->GetState().fBlendFlags & hsGMatState::kBlendMask ) != 0 ) |
|
b2 = true; |
|
else |
|
b2 = false; |
|
|
|
if( b1 != b2 ) |
|
return( b1 ? 1 : -1 ); |
|
|
|
// Next is texture (name). We do this kinda like strings: compare the first layer's |
|
// textures and go upwards, so that we group materials together starting with the |
|
// base layer's texture and going upwards |
|
numLayers = span1->fMaterial->GetNumLayers(); |
|
if( span2->fMaterial->GetNumLayers() < numLayers ) |
|
numLayers = span2->fMaterial->GetNumLayers(); |
|
|
|
for( i = 0; i < numLayers; i++ ) |
|
{ |
|
t1 = span1->fMaterial->GetLayer( i )->GetTexture(); |
|
t2 = span2->fMaterial->GetLayer( i )->GetTexture(); |
|
|
|
if( t1 != nil && t2 == nil ) |
|
return 1; |
|
else if( t1 == nil && t2 != nil ) |
|
return -1; |
|
else if( t1 == nil && t2 == nil ) |
|
break; // Textures equal up to here--keep going with rest of tests |
|
|
|
if( !t1->GetKeyName().IsNull() && !t2->GetKeyName().IsNull() ) |
|
{ |
|
j = t1->GetKeyName().Compare( t2->GetKeyName(), plString::kCaseInsensitive ); |
|
if( j != 0 ) |
|
return (short)j; |
|
} |
|
} |
|
|
|
// Finally, by material itself. |
|
if( !span1->fMaterial->GetKeyName().IsNull() && !span2->fMaterial->GetKeyName().IsNull() ) |
|
{ |
|
j = span1->fMaterial->GetKeyName().Compare( span2->fMaterial->GetKeyName(), plString::kCaseInsensitive ); |
|
if( j != 0 ) |
|
return (short)j; |
|
} |
|
|
|
if( span1->fLocalToWorld.fFlags != span2->fLocalToWorld.fFlags ) |
|
{ |
|
if( span1->fLocalToWorld.fFlags ) |
|
return -1; |
|
else |
|
return 1; |
|
} |
|
|
|
/// Equal in our book... |
|
return 0; |
|
}
|
|
|