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.
354 lines
9.5 KiB
354 lines
9.5 KiB
14 years ago
|
/*==LICENSE==*
|
||
|
|
||
|
CyanWorlds.com Engine - MMOG client, server and tools
|
||
13 years ago
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
||
14 years ago
|
|
||
|
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
|
||
13 years ago
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
14 years ago
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
13 years ago
|
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.
|
||
14 years ago
|
|
||
|
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 "hsTypes.h"
|
||
|
#include "plMorphDelta.h"
|
||
|
|
||
|
#include "hsStream.h"
|
||
|
#include "hsMemory.h"
|
||
|
|
||
|
#include "plAccessGeometry.h"
|
||
|
#include "plAccessSpan.h"
|
||
|
#include "plAccessVtxSpan.h"
|
||
|
#include "plGeometrySpan.h"
|
||
|
|
||
|
#include "plTweak.h"
|
||
|
|
||
|
static const hsScalar kMinWeight = 1.e-2f;
|
||
|
|
||
|
plMorphSpan::plMorphSpan()
|
||
|
: fUVWs(nil),
|
||
|
fNumUVWChans(0)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
plMorphSpan::~plMorphSpan()
|
||
|
{
|
||
|
delete [] fUVWs;
|
||
|
}
|
||
|
|
||
|
plMorphDelta::plMorphDelta()
|
||
|
: fWeight(0)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
plMorphDelta::~plMorphDelta()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
plMorphDelta::plMorphDelta(const plMorphDelta& src)
|
||
|
{
|
||
|
*this = src;
|
||
|
}
|
||
|
|
||
|
plMorphDelta& plMorphDelta::operator=(const plMorphDelta& src)
|
||
|
{
|
||
|
SetNumSpans(src.GetNumSpans());
|
||
|
int i;
|
||
|
for( i = 0; i < fSpans.GetCount(); i++ )
|
||
|
{
|
||
|
SetDeltas(i, src.fSpans[i].fDeltas, src.fSpans[i].fNumUVWChans, src.fSpans[i].fUVWs);
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
void plMorphDelta::Apply(hsTArray<plAccessSpan>& dst, hsScalar weight /* = -1.f */) const
|
||
|
{
|
||
|
if( weight == -1.f)
|
||
|
weight = fWeight; // None passed in, use our stored value
|
||
|
|
||
|
if( weight <= kMinWeight )
|
||
|
return;
|
||
|
|
||
|
// Easy
|
||
|
// For each span
|
||
|
int iSpan;
|
||
|
for( iSpan = 0; iSpan < fSpans.GetCount(); iSpan++ )
|
||
|
{
|
||
|
plAccessVtxSpan& vtxDst = dst[iSpan].AccessVtx();
|
||
|
|
||
|
plMorphSpan& span = fSpans[iSpan];
|
||
|
|
||
|
// For each vertDelta
|
||
|
const hsPoint3* uvwDel = span.fUVWs;
|
||
|
int iDelta;
|
||
|
for( iDelta = 0; iDelta < span.fDeltas.GetCount(); iDelta++ )
|
||
|
{
|
||
|
const plVertDelta& delta = span.fDeltas[iDelta];
|
||
|
// Add delPos * wgt to position
|
||
|
// Add delNorm * wgt to normal
|
||
|
vtxDst.Position(delta.fIdx) += delta.fPos * weight;
|
||
|
vtxDst.Normal(delta.fIdx) += delta.fNorm * weight;
|
||
|
|
||
|
// Leave skin weights and indices alone?
|
||
|
|
||
|
// Skip color for now, since diffuse and specular are
|
||
|
// ignored on the avatar?
|
||
|
// // Add delDiff * wgt to diffuse
|
||
|
// // Add delSpec * wgt to specular
|
||
|
|
||
|
// For each UVW
|
||
|
hsPoint3* uvws = vtxDst.UVWs(delta.fIdx);
|
||
|
int iUVW;
|
||
|
for( iUVW = 0; iUVW < span.fNumUVWChans; iUVW++ )
|
||
|
{
|
||
|
// Add delUVW * wgt to uvw
|
||
|
*uvws += *uvwDel * weight;
|
||
|
uvws++;
|
||
|
uvwDel++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MorphDelta - ComputeDeltas
|
||
|
void plMorphDelta::ComputeDeltas(const hsTArray<plAccessSpan>& base, const hsTArray<plAccessSpan>& moved)
|
||
|
{
|
||
|
SetNumSpans(base.GetCount());
|
||
|
|
||
|
// For each span
|
||
|
{
|
||
|
// for( i = 0; i < numVerts; i++ )
|
||
|
{
|
||
|
// NOTE: we want to discard zero deltas, but a
|
||
|
// delta in any channel forces us to save the whole thing.
|
||
|
// But we don't want to compare to zero (because we'll end
|
||
|
// up with a lot of near zero deltas), but the epsilon we
|
||
|
// compare to needs to be different for comparing something
|
||
|
// like a normal delta and a position delta.
|
||
|
//
|
||
|
// For position, normal, color and all uvws
|
||
|
// Calc del and delLenSq
|
||
|
// If any delLenSq big enough, set nonZero to true
|
||
|
// If nonZero
|
||
|
{
|
||
|
// Append to deltas (i, del's)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MorphDelta - ComputeDeltas
|
||
|
void plMorphDelta::ComputeDeltas(const hsTArray<plGeometrySpan*>& base, const hsTArray<plGeometrySpan*>& moved, const hsMatrix44& d2b, const hsMatrix44& d2bTInv)
|
||
|
{
|
||
|
SetNumSpans(base.GetCount());
|
||
|
|
||
|
hsPoint3 delUVWs[8];
|
||
|
|
||
|
// For each span
|
||
|
int iSpan;
|
||
|
for( iSpan = 0; iSpan < base.GetCount(); iSpan++ )
|
||
|
{
|
||
|
plAccessSpan baseAcc;
|
||
|
plAccessGeometry::Instance()->AccessSpanFromGeometrySpan(baseAcc, base[iSpan]);
|
||
|
plAccessSpan movedAcc;
|
||
|
plAccessGeometry::Instance()->AccessSpanFromGeometrySpan(movedAcc, moved[iSpan]);
|
||
|
|
||
|
plAccPosNormUVWIterator baseIter(&baseAcc.AccessVtx());
|
||
|
plAccPosNormUVWIterator movedIter(&movedAcc.AccessVtx());
|
||
|
|
||
|
|
||
|
plMorphSpan& dst = fSpans[iSpan];
|
||
|
|
||
|
const UInt16 numUVWs = baseAcc.AccessVtx().NumUVWs();
|
||
|
|
||
|
hsTArray<plVertDelta> deltas;
|
||
|
hsTArray<hsPoint3> uvws;
|
||
|
deltas.SetCount(0);
|
||
|
uvws.SetCount(0);
|
||
|
|
||
|
|
||
|
int iVert = 0;;
|
||
|
for( baseIter.Begin(), movedIter.Begin(); baseIter.More(); baseIter.Advance(), movedIter.Advance() )
|
||
|
{
|
||
|
// NOTE: we want to discard zero deltas, but a
|
||
|
// delta in any channel forces us to save the whole thing.
|
||
|
// But we don't want to compare to zero (because we'll end
|
||
|
// up with a lot of near zero deltas), but the epsilon we
|
||
|
// compare to needs to be different for comparing something
|
||
|
// like a normal delta and a position delta.
|
||
|
//
|
||
|
// For position, normal, color and all uvws
|
||
|
// Calc del and delLenSq
|
||
|
// If any delLenSq big enough, set nonZero to true
|
||
|
hsBool nonZero = false;
|
||
|
|
||
|
// These are actually min del SQUARED.
|
||
|
plConst(hsScalar) kMinDelPos(1.e-4f); // From Budtpueller's Handbook of Constants
|
||
|
plConst(hsScalar) kMinDelNorm(3.e-2f); // About 10 degrees
|
||
|
plConst(hsScalar) kMinDelUVW(1.e-4f); // From BHC
|
||
|
hsPoint3 mPos = d2b * *movedIter.Position();
|
||
|
hsVector3 delPos( &mPos, baseIter.Position());
|
||
|
hsScalar delPosSq = delPos.MagnitudeSquared();
|
||
|
if( delPosSq > kMinDelPos )
|
||
|
nonZero = true;
|
||
|
else
|
||
|
delPos.Set(0,0,0);
|
||
|
|
||
|
|
||
|
hsVector3 delNorm = (d2bTInv * *movedIter.Normal()) - *baseIter.Normal();
|
||
|
hsScalar delNormSq = delNorm.MagnitudeSquared();
|
||
|
if( delNormSq > kMinDelNorm )
|
||
|
nonZero = true;
|
||
|
else
|
||
|
delNorm.Set(0,0,0);
|
||
|
|
||
|
int i;
|
||
|
for( i = 0; i < numUVWs; i++ )
|
||
|
{
|
||
|
delUVWs[i] = *movedIter.UVW(i) - *baseIter.UVW(i);
|
||
|
hsScalar delUVWSq = delUVWs[i].MagnitudeSquared();
|
||
|
if( delUVWSq > kMinDelUVW )
|
||
|
nonZero = true;
|
||
|
else
|
||
|
delUVWs[i].Set(0,0,0);
|
||
|
}
|
||
|
|
||
|
if( nonZero )
|
||
|
{
|
||
|
// Append to deltas (i, del's)
|
||
|
plVertDelta del;
|
||
|
del.fIdx = iVert;
|
||
|
del.fPos = delPos;
|
||
|
del.fNorm = delNorm;
|
||
|
deltas.Append(del);
|
||
|
|
||
|
for( i = 0; i < numUVWs; i++ )
|
||
|
uvws.Append(delUVWs[i]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nonZero = false; // Breakpoint.
|
||
|
}
|
||
|
|
||
|
iVert++;
|
||
|
}
|
||
|
SetDeltas(iSpan, deltas, numUVWs, uvws.AcquireArray());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void plMorphDelta::SetNumSpans(int n)
|
||
|
{
|
||
|
fSpans.Reset();
|
||
|
fSpans.SetCount(n);
|
||
|
}
|
||
|
|
||
|
|
||
|
void plMorphDelta::AllocDeltas(int iSpan, int nDel, int nUVW)
|
||
|
{
|
||
|
fSpans[iSpan].fDeltas.SetCount(nDel);
|
||
|
fSpans[iSpan].fNumUVWChans = nUVW;
|
||
|
|
||
|
delete [] fSpans[iSpan].fUVWs;
|
||
|
|
||
|
int uvwCnt = nDel * nUVW;
|
||
|
if( uvwCnt )
|
||
|
fSpans[iSpan].fUVWs = TRACKED_NEW hsPoint3[uvwCnt];
|
||
|
else
|
||
|
fSpans[iSpan].fUVWs = nil;
|
||
|
}
|
||
|
|
||
|
void plMorphDelta::SetDeltas(int iSpan, const hsTArray<plVertDelta>& deltas, int numUVWChans, const hsPoint3* uvws)
|
||
|
{
|
||
|
AllocDeltas(iSpan, deltas.GetCount(), numUVWChans);
|
||
|
if( deltas.GetCount() )
|
||
|
{
|
||
|
HSMemory::BlockMove(&deltas[0], fSpans[iSpan].fDeltas.AcquireArray(), deltas.GetCount() * sizeof(plVertDelta));
|
||
|
|
||
|
if( numUVWChans )
|
||
|
HSMemory::BlockMove(uvws, fSpans[iSpan].fUVWs, deltas.GetCount() * numUVWChans * sizeof(*uvws));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void plMorphDelta::Read(hsStream* s, hsResMgr* mgr)
|
||
|
{
|
||
|
fWeight = s->ReadSwapScalar();
|
||
|
|
||
|
int n = s->ReadSwap32();
|
||
|
SetNumSpans(n);
|
||
|
int iSpan;
|
||
|
for( iSpan = 0; iSpan < n; iSpan++ )
|
||
|
{
|
||
|
int nDel = s->ReadSwap32();
|
||
|
int nUVW = s->ReadSwap32();
|
||
|
AllocDeltas(iSpan, nDel, nUVW);
|
||
|
if( nDel )
|
||
|
{
|
||
|
s->Read(nDel * sizeof(plVertDelta), fSpans[iSpan].fDeltas.AcquireArray());
|
||
|
if( nUVW )
|
||
|
s->Read(nDel * nUVW * sizeof(hsPoint3), fSpans[iSpan].fUVWs);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void plMorphDelta::Write(hsStream* s, hsResMgr* mgr)
|
||
|
{
|
||
|
s->WriteSwapScalar(fWeight);
|
||
|
|
||
|
s->WriteSwap32(fSpans.GetCount());
|
||
|
|
||
|
int iSpan;
|
||
|
for( iSpan = 0; iSpan < fSpans.GetCount(); iSpan++ )
|
||
|
{
|
||
|
int nDel = fSpans[iSpan].fDeltas.GetCount();
|
||
|
int nUVW = fSpans[iSpan].fNumUVWChans;
|
||
|
s->WriteSwap32(nDel);
|
||
|
s->WriteSwap32(nUVW);
|
||
|
|
||
|
if( nDel )
|
||
|
{
|
||
|
// Initialize our padding here, so we don't write random data
|
||
|
for (int i = 0; i < nDel; i++)
|
||
|
{
|
||
|
plVertDelta& delta = fSpans[iSpan].fDeltas[i];
|
||
|
delta.fPadding = 0;
|
||
|
}
|
||
|
|
||
|
s->Write(nDel * sizeof(plVertDelta), fSpans[iSpan].fDeltas.AcquireArray());
|
||
|
|
||
|
if( nUVW )
|
||
|
s->Write(nDel * nUVW * sizeof(hsPoint3), fSpans[iSpan].fUVWs);
|
||
|
}
|
||
|
}
|
||
|
}
|