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.

338 lines
8.6 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/>.
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);
}
}
}