/*==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 .
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==*/
#include
#include "hsTypes.h"
#include "plGeoSpanDice.h"
#include "plGeometrySpan.h"
plGeoSpanDice::plGeoSpanDice()
: fMinFaces(0),
fMaxFaces(0)
{
fMaxSize.Set(0,0,0);
}
plGeoSpanDice::~plGeoSpanDice()
{
}
hsBool plGeoSpanDice::Dice(hsTArray& spans) const
{
int startingCount = spans.GetCount();
hsTArray out;
hsTArray next;
while(spans.GetCount())
{
int i;
for( i = 0; i < spans.GetCount(); i++ )
{
if( !IHalf(spans[i], next) )
{
out.Append(spans[i]);
}
}
spans.Swap(next);
next.SetCount(0);
}
spans.Swap(out);
return spans.GetCount() != startingCount;
}
hsBool plGeoSpanDice::INeedSplitting(plGeometrySpan* src) const
{
// Do we have enough faces to bother?
if( fMinFaces )
{
if( src->fNumIndices < fMinFaces * 3 )
return false;
}
// Do we have enough faces to bother no matter how small we are?
if( fMaxFaces )
{
if( src->fNumIndices > fMaxFaces * 3 )
return true;
}
// Are we big enough to bother?
if( (fMaxSize.fX > 0) || (fMaxSize.fY > 0) || (fMaxSize.fZ > 0) )
{
hsPoint3 size = src->fLocalBounds.GetMaxs() - src->fLocalBounds.GetMins();
if( size.fX > fMaxSize.fX )
return true;
if( size.fY > fMaxSize.fY )
return true;
if( size.fZ > fMaxSize.fZ )
return true;
}
return false;
}
hsBool plGeoSpanDice::IHalf(plGeometrySpan* src, hsTArray& out, int exclAxis) const
{
if( !INeedSplitting(src) )
return false;
int iAxis = ISelectAxis(exclAxis, src);
// Ran out of axes to try.
if( iAxis < 0 )
return false;
hsScalar midPoint = src->fLocalBounds.GetCenter()[iAxis];
hsTArray loTris;
hsTArray hiTris;
UInt16* indexData = src->fIndexData;
int numTris = src->fNumIndices / 3;
int stride = src->GetVertexSize(src->fFormat);
int i;
for( i = 0; i < numTris; i++ )
{
hsPoint3& pos0 = *(hsPoint3*)(src->fVertexData + *indexData++ * stride);
hsPoint3& pos1 = *(hsPoint3*)(src->fVertexData + *indexData++ * stride);
hsPoint3& pos2 = *(hsPoint3*)(src->fVertexData + *indexData++ * stride);
if( (pos0[iAxis] >= midPoint)
&&(pos1[iAxis] >= midPoint)
&&(pos2[iAxis] >= midPoint) )
{
hiTris.Append(i);
}
else
{
loTris.Append(i);
}
}
// This axis isn't working out, try another.
if( !hiTris.GetCount() || !loTris.GetCount() )
return IHalf(src, out, exclAxis | (1 << iAxis));
plGeometrySpan* loDst = IExtractTris(src, loTris);
plGeometrySpan* hiDst = IExtractTris(src, hiTris);
delete src;
out.Append(loDst);
out.Append(hiDst);
return true;
}
int plGeoSpanDice::ISelectAxis(int exclAxis, plGeometrySpan* src) const
{
int iAxis = -1;
hsScalar maxDim = 0;
int i;
for( i = 0; i < 3; i++ )
{
// Check to see if we've already tried this one.
if( exclAxis & (1 << i) )
continue;
hsScalar dim = src->fLocalBounds.GetMaxs()[i] - src->fLocalBounds.GetMins()[i];
if( dim > maxDim )
{
maxDim = dim;
iAxis = i;
}
}
return iAxis;
}
plGeometrySpan* plGeoSpanDice::IExtractTris(plGeometrySpan* src, hsTArray& tris) const
{
// First off, find out how many and which vers we're talking here.
// Easiest way is while we're building the LUTs we'll want later anyway.
hsTArray fwdLUT;
fwdLUT.SetCount(src->fNumVerts);
memset(fwdLUT.AcquireArray(), -1, src->fNumVerts * sizeof(*fwdLUT.AcquireArray()));
hsTArray bckLUT;
bckLUT.SetCount(0);
int i;
for( i = 0; i < tris.GetCount(); i++ )
{
UInt16* idx = src->fIndexData + tris[i] * 3;
if( fwdLUT[*idx] < 0 )
{
fwdLUT[*idx] = bckLUT.GetCount();
bckLUT.Append(*idx);
}
idx++;
if( fwdLUT[*idx] < 0 )
{
fwdLUT[*idx] = bckLUT.GetCount();
bckLUT.Append(*idx);
}
idx++;
if( fwdLUT[*idx] < 0 )
{
fwdLUT[*idx] = bckLUT.GetCount();
bckLUT.Append(*idx);
}
}
int numVerts = bckLUT.GetCount();
int numTris = tris.GetCount();
plGeometrySpan* dst = IAllocSpace(src, numVerts, numTris);
// Okay, set the index data.
UInt16* idxTrav = dst->fIndexData;
for( i = 0; i < tris.GetCount(); i++ )
{
*idxTrav++ = fwdLUT[src->fIndexData[ tris[i] * 3 + 0] ];
*idxTrav++ = fwdLUT[src->fIndexData[ tris[i] * 3 + 1] ];
*idxTrav++ = fwdLUT[src->fIndexData[ tris[i] * 3 + 2] ];
}
// Copy over the basic vertex data
// We'll update the bounds as we go.
int stride = src->GetVertexSize(src->fFormat);
hsBounds3Ext localBnd;
localBnd.MakeEmpty();
UInt8* vtxTrav = dst->fVertexData;
for( i = 0; i < numVerts; i++ )
{
memcpy(vtxTrav, src->fVertexData + bckLUT[i] * stride, stride);
hsPoint3* pos = (hsPoint3*)vtxTrav;
localBnd.Union(pos);
vtxTrav += stride;
}
if( src->fProps & plGeometrySpan::kWaterHeight )
{
src->AdjustBounds(localBnd);
}
dst->fLocalBounds = localBnd;
// Now the rest of this optional garbage.
if( src->fMultColor )
{
for( i = 0; i < numVerts; i++ )
{
dst->fMultColor[i] = src->fMultColor[bckLUT[i]];
}
}
if( src->fAddColor )
{
for( i = 0; i < numVerts; i++ )
{
dst->fAddColor[i] = src->fAddColor[bckLUT[i]];
}
}
if( src->fDiffuseRGBA )
{
for( i = 0; i < numVerts; i++ )
{
dst->fDiffuseRGBA[i] = src->fDiffuseRGBA[bckLUT[i]];
}
}
if( src->fSpecularRGBA )
{
for( i = 0; i < numVerts; i++ )
{
dst->fSpecularRGBA[i] = src->fSpecularRGBA[bckLUT[i]];
}
}
dst->fMaxOwner = src->fMaxOwner;
return dst;
}
plGeometrySpan* plGeoSpanDice::IAllocSpace(plGeometrySpan* src, int numVerts, int numTris) const
{
plGeometrySpan* dst = TRACKED_NEW plGeometrySpan;
// Do a structure copy here. That's okay, because we're going to immediately
// fix up the pointers and counters that shouldn't have been copied. If
// plGeometrySpan ever gets a copy constructor, this'll blow wide open.
*dst = *src;
int stride = src->GetVertexSize(src->fFormat);
dst->fNumIndices = numTris * 3;
dst->fIndexData = TRACKED_NEW UInt16[dst->fNumIndices];
dst->fNumVerts = numVerts;
dst->fVertexData = TRACKED_NEW UInt8[numVerts * stride];
if( src->fMultColor )
{
dst->fMultColor = TRACKED_NEW hsColorRGBA[numVerts];
}
if( src->fAddColor )
{
dst->fAddColor = TRACKED_NEW hsColorRGBA[numVerts];
}
if( src->fDiffuseRGBA )
{
dst->fDiffuseRGBA = TRACKED_NEW UInt32[numVerts];
}
if( src->fSpecularRGBA )
{
dst->fSpecularRGBA = TRACKED_NEW UInt32[numVerts];
}
return dst;
}