1
0
mirror of https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git synced 2025-07-17 10:52:46 +00:00

Initial Commit of CyanWorlds.com Engine Open Source Client/Plugin

This commit is contained in:
JWPlatt
2011-03-12 12:34:52 -05:00
commit a20a222fc2
3976 changed files with 1301355 additions and 0 deletions

View File

@ -0,0 +1,494 @@
/*==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 "hsInterp.h"
#include "../plTransform/hsAffineParts.h"
#include "hsColorRGBA.h"
#include "hsPoint2.h"
//
///////////////////////////////////////////////////////
// linear interpolation
///////////////////////////////////////////////////////
//
void hsInterp::LinInterp(hsScalar k1, hsScalar k2, hsScalar t, hsScalar* result)
{
*result = k1 + t * (k2 - k1);
}
void hsInterp::LinInterp(const hsScalarTriple* k1, const hsScalarTriple* k2, hsScalar t,
hsScalarTriple* result)
{
if (t==0.0)
*result = *k1;
else
if (t==1.0)
*result = *k2;
else
{
LinInterp(k1->fX, k2->fX, t, &result->fX);
LinInterp(k1->fY, k2->fY, t, &result->fY);
LinInterp(k1->fZ, k2->fZ, t, &result->fZ);
}
}
void hsInterp::LinInterp(const hsColorRGBA* k1, const hsColorRGBA* k2, hsScalar t,
hsColorRGBA* result, UInt32 flags)
{
if (t==0.0)
{
// copy
result->r = k1->r;
result->g = k1->g;
result->b = k1->b;
if (!(flags & kIgnoreAlpha))
result->a = k1->a;
return;
}
if (t==1.0)
{
result->r = k2->r;
result->g = k2->g;
result->b = k2->b;
if (!(flags & kIgnoreAlpha))
result->a = k2->a;
return;
}
LinInterp(k1->r, k2->r, t, &result->r);
LinInterp(k1->g, k2->g, t, &result->g);
LinInterp(k1->b, k2->b, t, &result->b);
if (!(flags & kIgnoreAlpha))
LinInterp(k1->a, k2->a, t, &result->a);
}
void hsInterp::LinInterp(const hsMatrix33* k1, const hsMatrix33* k2, hsScalar t,
hsMatrix33* result, UInt32 flags)
{
if (t==0.0)
{
// copy
result->fMap[0][0] = k1->fMap[0][0];
result->fMap[0][1] = k1->fMap[0][1];
result->fMap[0][2] = k1->fMap[0][2];
result->fMap[1][0] = k1->fMap[1][0];
result->fMap[1][1] = k1->fMap[1][1];
result->fMap[1][2] = k1->fMap[1][2];
if (!(flags & kIgnoreLastMatRow))
{
result->fMap[2][0] = k1->fMap[2][0];
result->fMap[2][1] = k1->fMap[2][1];
result->fMap[2][2] = k1->fMap[2][2];
}
return;
}
if (t==1.0)
{
// copy
result->fMap[0][0] = k2->fMap[0][0];
result->fMap[0][1] = k2->fMap[0][1];
result->fMap[0][2] = k2->fMap[0][2];
result->fMap[1][0] = k2->fMap[1][0];
result->fMap[1][1] = k2->fMap[1][1];
result->fMap[1][2] = k2->fMap[1][2];
if (!(flags & kIgnoreLastMatRow))
{
result->fMap[2][0] = k2->fMap[2][0];
result->fMap[2][1] = k2->fMap[2][1];
result->fMap[2][2] = k2->fMap[2][2];
}
return;
}
LinInterp(k1->fMap[0][0], k2->fMap[0][0], t, &result->fMap[0][0]);
LinInterp(k1->fMap[0][1], k2->fMap[0][1], t, &result->fMap[0][1]);
LinInterp(k1->fMap[0][2], k2->fMap[0][2], t, &result->fMap[0][2]);
LinInterp(k1->fMap[1][0], k2->fMap[1][0], t, &result->fMap[1][0]);
LinInterp(k1->fMap[1][1], k2->fMap[1][1], t, &result->fMap[1][1]);
LinInterp(k1->fMap[1][2], k2->fMap[1][2], t, &result->fMap[1][2]);
if (!(flags & kIgnoreLastMatRow))
{
LinInterp(k1->fMap[2][0], k2->fMap[2][0], t, &result->fMap[2][0]);
LinInterp(k1->fMap[2][1], k2->fMap[2][1], t, &result->fMap[2][1]);
LinInterp(k1->fMap[2][2], k2->fMap[2][2], t, &result->fMap[2][2]);
}
}
//
//
void hsInterp::LinInterp(const hsMatrix44* mat1, const hsMatrix44* mat2, hsScalar t,
hsMatrix44* out, UInt32 flags)
{
if (flags == 0)
{
if( 0 == t )
{
*out = *mat1;
return;
}
if( hsScalar1 == t )
{
*out = *mat2;
return;
}
}
if( flags & kIgnorePartsScale )
{
if (!(flags & kIgnorePartsRot))
{
// interp rotation with quats
hsQuat q1, q2, qOut;
q1.SetFromMatrix(mat1);
q2.SetFromMatrix(mat2);
LinInterp(&q1, &q2, t, &qOut);
qOut.Normalize();
qOut.MakeMatrix(out);
}
else
out->Reset();
#if 1
hsAssert(mat2->fMap[3][0]==0 && mat2->fMap[3][1]==0 && mat2->fMap[3][2]==0 && mat2->fMap[3][3]==1,
"matrix prob?");
#else
// copy
for(int i=0; i<3; i++)
out->fMap[3][i] = mat2->fMap[3][i];
#endif
if (!(flags & kIgnorePartsPos))
{
// interp translation
hsPoint3 p1,p2,pOut;
mat1->GetTranslate(&p1);
mat2->GetTranslate(&p2);
LinInterp(&p1, &p2, t, &pOut);
out->SetTranslate(&pOut);
out->NotIdentity(); // in case no rot
}
}
else
{
// Complete decomp and parts interp
gemAffineParts gemParts1, gemParts2;
hsAffineParts parts1, parts2, partsOut;
decomp_affine(mat1->fMap, &gemParts1);
AP_SET(parts1, gemParts1);
decomp_affine(mat2->fMap, &gemParts2);
AP_SET(parts2, gemParts2);
LinInterp(&parts1, &parts2, t, &partsOut, flags); // flags will be parsed here
partsOut.ComposeMatrix(out);
}
}
void hsInterp::LinInterp(const hsQuat* k1, const hsQuat* k2, hsScalar t, hsQuat* result)
{
if (t==0.0)
*result = *k1;
else
if (t==1.0)
*result = *k2;
else
{
result->SetFromSlerp(*k1, *k2, t);
}
}
void hsInterp::LinInterp(const hsScaleValue* k1, const hsScaleValue* k2, hsScalar t,
hsScaleValue* result)
{
LinInterp(&k1->fS, &k2->fS, t, &result->fS); // Stretch rotation
LinInterp(&k1->fQ, &k2->fQ, t, &result->fQ); // Stretch factor
}
void hsInterp::LinInterp(const hsAffineParts* k1, const hsAffineParts* k2, hsScalar t,
hsAffineParts* result, UInt32 flags)
{
if (t==0.0)
{
// copy
if (!(flags & kIgnorePartsPos))
result->fT = k1->fT;
if (!(flags & kIgnorePartsRot))
result->fQ = k1->fQ;
if (!(flags & kIgnorePartsScale))
{
// same as preserveScale
result->fU = k1->fU;
result->fK = k1->fK;
}
result->fF = k1->fF;
return;
}
if (flags & kPreservePartsScale)
{
result->fU = k1->fU; // just copy scale from 1st key
result->fK = k1->fK;
}
if (t==1.0)
{
// copy
if (!(flags & kIgnorePartsPos))
result->fT = k2->fT;
if (!(flags & kIgnorePartsRot))
result->fQ = k2->fQ;
if (!(flags & (kIgnorePartsScale | kPreservePartsScale)))
{
result->fU = k2->fU;
result->fK = k2->fK;
}
result->fF = k2->fF;
return;
}
if(k1->fF!=k2->fF)
hsStatusMessageF("WARNING: Inequality in affine parts flip value.");
// hsAssert(k1->fF==k2->fF, "inequality in affine parts flip value");
if (!(flags & kIgnorePartsPos))
LinInterp(&k1->fT, &k2->fT, t, &result->fT); // Translation
if (!(flags & kIgnorePartsRot))
{
LinInterp(&k1->fQ, &k2->fQ, t, &result->fQ); // Essential rotation
}
if (!(flags & (kIgnorePartsScale | kPreservePartsScale)))
{
LinInterp(&k1->fU, &k2->fU, t, &result->fU); // Stretch rotation
LinInterp(&k1->fK, &k2->fK, t, &result->fK); // Stretch factor
}
#if 0
if (!(flags & kIgnorePartsDet))
LinInterp(k1->fF, k2->fF, t, &result->fF); // Flip rot var
#else
result->fF = k1->fF;
#endif
}
//
///////////////////////////////////////////////////////
// Key interpolation
///////////////////////////////////////////////////////
//
void hsInterp::BezScalarEval(const hsScalar value1, const hsScalar outTan,
const hsScalar value2, const hsScalar inTan,
const hsScalar t, const hsScalar tanScale, hsScalar *result)
{
#if 0
// If the tangents were what you'd expect them to be... Hermite splines, than this code
// would make sense. But no, Max likes to store them in a scaled form based on the
// time of each frame. If we ever optimize this further, we could do the scaling on export,
// but I need this to work right now before all the artists hate me too much.
const hsScalar t2 = t * t;
const hsScalar t3 = t2 * t;
const hsScalar term1 = 2 * t3 - 3 * t2;
*result = ((term1 + 1) * value1) +
(-term1 * value2) +
((t3 - 2 * t2 + 1) * outTan) +
((t3 - t2) * inTan);
#else
const hsScalar oneMinusT = (1.0f - t);
const hsScalar tSq = t * t;
const hsScalar oneMinusTSq = oneMinusT * oneMinusT;
*result = (oneMinusT * oneMinusTSq * value1) +
(3.f * t * oneMinusTSq * (value1 + outTan * tanScale)) +
(3.f * tSq * oneMinusT * (value2 + inTan * tanScale)) +
(tSq * t * value2);
#endif
}
void hsInterp::BezInterp(const hsBezPoint3Key* k1, const hsBezPoint3Key* k2, const hsScalar t, hsScalarTriple* result)
{
hsScalar scale = (k2->fFrame - k1->fFrame) * MAX_TICKS_PER_FRAME / 3.f;
BezScalarEval(k1->fValue.fX, k1->fOutTan.fX, k2->fValue.fX, k2->fInTan.fX, t, scale, &result->fX);
BezScalarEval(k1->fValue.fY, k1->fOutTan.fY, k2->fValue.fY, k2->fInTan.fY, t, scale, &result->fY);
BezScalarEval(k1->fValue.fZ, k1->fOutTan.fZ, k2->fValue.fZ, k2->fInTan.fZ, t, scale, &result->fZ);
}
void hsInterp::BezInterp(const hsBezScalarKey* k1, const hsBezScalarKey* k2, const hsScalar t, hsScalar* result)
{
hsScalar scale = (k2->fFrame - k1->fFrame) * MAX_TICKS_PER_FRAME / 3.f;
BezScalarEval(k1->fValue, k1->fOutTan, k2->fValue, k2->fInTan, t, scale, result);
}
void hsInterp::BezInterp(const hsBezScaleKey* k1, const hsBezScaleKey* k2, const hsScalar t, hsScaleValue* result)
{
hsScalar scale = (k2->fFrame - k1->fFrame) * MAX_TICKS_PER_FRAME / 3.f;
BezScalarEval(k1->fValue.fS.fX, k1->fOutTan.fX, k2->fValue.fS.fX, k2->fInTan.fX, t, scale, &result->fS.fX);
BezScalarEval(k1->fValue.fS.fY, k1->fOutTan.fY, k2->fValue.fS.fY, k2->fInTan.fY, t, scale, &result->fS.fY);
BezScalarEval(k1->fValue.fS.fZ, k1->fOutTan.fZ, k2->fValue.fS.fZ, k2->fInTan.fZ, t, scale, &result->fS.fZ);
// Slerp scale axis
LinInterp(&k1->fValue.fQ, &k2->fValue.fQ, t, &result->fQ);
}
//
// Get an element from an array of unknown type
//
static inline hsKeyFrame* GetKey(Int32 i, void *keys, Int32 size)
{
return (hsKeyFrame*) ((char*)keys + size * i);
}
//
// STATIC
// Given a list of keys, and a time, fills in the 2 boundary keys and
// a fraction (p=0-1) indicating where the time falls between them.
// Returns the index of the first key which can be passed in as a hint (lastKeyIdx)
// for the next search.
//
void hsInterp::GetBoundaryKeyFrames(hsScalar time, UInt32 numKeys, void *keys, UInt32 size,
hsKeyFrame **kF1, hsKeyFrame **kF2, UInt32 *lastKeyIdx, hsScalar *p, hsBool forwards)
{
hsAssert(numKeys>1, "Must have more than 1 keyframe");
int k1, k2;
UInt16 frame = (UInt16)(time * MAX_FRAMES_PER_SEC);
// boundary case, past end
if (frame > GetKey(numKeys-1, keys, size)->fFrame)
{
k1=k2=numKeys-1;
(*kF2) = GetKey(k1, keys, size);
(*kF1) = (*kF2);
*p = 0.0;
goto ret;
}
hsKeyFrame *key1, *key2;
// boundary case, before start
if (frame < (key1=GetKey(0, keys, size))->fFrame)
{
k1=k2=0;
(*kF1) = GetKey(k1, keys, size);
(*kF2) = (*kF1);
*p = 0.0;
goto ret;
}
// prime loop
int i;
i = 1;
if (*lastKeyIdx > 0 && *lastKeyIdx < numKeys - 1)
{
// new starting point for search
if (forwards)
key1 = GetKey(*lastKeyIdx, keys, size);
else
key2 = GetKey(*lastKeyIdx + 1, keys, size);
i = *lastKeyIdx + 1;
}
else if (!forwards)
{
key2 = GetKey(1, keys, size);
}
// search pairs of keys
int count;
if (forwards)
{
for (count = 1; count <= numKeys; count++, i++)
{
if (i >= numKeys)
{
key1 = GetKey(0, keys, size);
i = 1;
count++;
}
key2 = GetKey(i, keys, size);
if (frame <= key2->fFrame && frame >= key1->fFrame)
{
k2=i;
k1=i-1;
(*kF2) = key2;
(*kF1) = key1;
*p = (time - (*kF1)->fFrame / MAX_FRAMES_PER_SEC) / (((*kF2)->fFrame - (*kF1)->fFrame) / MAX_FRAMES_PER_SEC);
goto ret;
}
key1=key2;
}
}
else
{
for (count = 1; count <= numKeys; count++, i--)
{
if (i < 1)
{
i = numKeys - 1;
key2 = GetKey(i, keys, size);
count++;
}
key1 = GetKey(i - 1, keys, size);
if (frame <= key2->fFrame && frame >= key1->fFrame)
{
k2 = i;
k1 = i - 1;
(*kF2) = key2;
(*kF1) = key1;
*p = (time - (*kF1)->fFrame / MAX_FRAMES_PER_SEC) / (((*kF2)->fFrame - (*kF1)->fFrame) / MAX_FRAMES_PER_SEC);
goto ret;
}
key2=key1;
}
}
ret:
;
#if 0
char str[128];
sprintf(str, "k1=%d, k2=%d, p=%f\n", k1, k2, *p);
OutputDebugString(str);
#endif
*lastKeyIdx = k1;
}

View File

@ -0,0 +1,82 @@
/*==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==*/
#ifndef HSINTERP_inc
#define HSINTERP_inc
#include "HeadSpin.h"
#include "hsKeys.h"
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
//
// Performs interpolation of keyframes & values.
// t param should be 0-1
//
struct hsColorRGBA;
class hsAffineParts;
class hsInterp
{
public:
enum IgnoreFlags
{
kIgnoreAlpha = 0x1,
kIgnoreLastMatRow = 0x2,
kIgnorePartsPos = 0x4,
kIgnorePartsRot = 0x8,
kIgnorePartsScale = 0x10, // result gets no scale
kIgnorePartsDet = 0x20,
kPreservePartsScale = 0x40 // result gets the scale of key1
};
static void BezScalarEval(const hsScalar value1, const hsScalar outTan,
const hsScalar value2, const hsScalar inTan,
const hsScalar t, const hsScalar scale, hsScalar *result);
static void BezInterp(const hsBezPoint3Key *k1, const hsBezPoint3Key *k2, const hsScalar t, hsScalarTriple *result);
static void BezInterp(const hsBezScalarKey *k1, const hsBezScalarKey *k2, const hsScalar t, hsScalar *result);
static void BezInterp(const hsBezScaleKey *k1, const hsBezScaleKey *k2, const hsScalar t, hsScaleValue *result);
// simple linear interpolation
static void LinInterp(const hsScalar k1, const hsScalar k2, const hsScalar t, hsScalar *result);
static void LinInterp(const hsScalarTriple *k1, const hsScalarTriple *k2, const hsScalar t, hsScalarTriple *result);
static void LinInterp(const hsColorRGBA *k1, const hsColorRGBA *k2, const hsScalar t, hsColorRGBA *result, UInt32 ignoreFlags=0);
static void LinInterp(const hsMatrix33 *k1, const hsMatrix33 *k2, const hsScalar t, hsMatrix33 *result, UInt32 ignoreFlags=0);
static void LinInterp(const hsMatrix44 *mat1, const hsMatrix44 *mat2, const hsScalar t, hsMatrix44 *out, UInt32 ignoreFlags=0);
static void LinInterp(const hsQuat *k1, const hsQuat *k2, const hsScalar t, hsQuat *result);
static void LinInterp(const hsScaleValue *k1, const hsScaleValue *k2, const hsScalar t, hsScaleValue *result);
static void LinInterp(const hsAffineParts *k1, const hsAffineParts *k2, const hsScalar t, hsAffineParts *result, UInt32 ignoreFlags=0);
// Given a time value, find the enclosing keyframes and normalize time (0-1)
static void GetBoundaryKeyFrames(hsScalar time, UInt32 numKeys, void *keys,
UInt32 keySize, hsKeyFrame **kF1, hsKeyFrame **kF2, UInt32 *lastKeyIdx, hsScalar *p, hsBool forwards);
};
#define MAX_FRAMES_PER_SEC 30.0f
#define MAX_TICKS_PER_FRAME 160.0f
#define MAX_TICKS_PER_SEC (MAX_TICKS_PER_FRAME*MAX_FRAMES_PER_SEC)
#endif // #ifndef HSINTERP_inc

View File

@ -0,0 +1,554 @@
/*==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 "hsKeys.h"
#include "hsStream.h"
const int hsKeyFrame::kMaxFrameNumber = 65535;
///////////////////////////////////////////////////////////////
void hsPoint3Key::Read(hsStream *stream)
{
fFrame = stream->ReadSwap16();
fValue.Read(stream);
}
void hsPoint3Key::Write(hsStream *stream)
{
stream->WriteSwap16(fFrame);
fValue.Write(stream);
}
hsBool hsPoint3Key::CompareValue(hsPoint3Key *key)
{
return hsABS(fValue.fX - key->fValue.fX) < .01 &&
hsABS(fValue.fY - key->fValue.fY) < .01 &&
hsABS(fValue.fZ - key->fValue.fZ) < .01;
}
void hsBezPoint3Key::Read(hsStream *stream)
{
fFrame = stream->ReadSwap16();
fInTan.Read(stream);
fOutTan.Read(stream);
fValue.Read(stream);
}
void hsBezPoint3Key::Write(hsStream *stream)
{
stream->WriteSwap16(fFrame);
fInTan.Write(stream);
fOutTan.Write(stream);
fValue.Write(stream);
}
hsBool hsBezPoint3Key::CompareValue(hsBezPoint3Key *key)
{
return hsABS(fValue.fX - key->fValue.fX) < .01 &&
hsABS(fValue.fY - key->fValue.fY) < .01 &&
hsABS(fValue.fZ - key->fValue.fZ) < .01;
}
/////////////////////////////////////////
void hsScalarKey::Read(hsStream *stream)
{
fFrame = stream->ReadSwap16();
fValue = stream->ReadSwapScalar();
}
void hsScalarKey::Write(hsStream *stream)
{
stream->WriteSwap16(fFrame);
stream->WriteSwapScalar(fValue);
}
hsBool hsScalarKey::CompareValue(hsScalarKey *key)
{
return fValue == key->fValue;
}
void hsBezScalarKey::Read(hsStream *stream)
{
fFrame = stream->ReadSwap16();
fInTan = stream->ReadSwapScalar();
fOutTan = stream->ReadSwapScalar();
fValue = stream->ReadSwapScalar();
}
void hsBezScalarKey::Write(hsStream *stream)
{
stream->WriteSwap16(fFrame);
stream->WriteSwapScalar(fInTan);
stream->WriteSwapScalar(fOutTan);
stream->WriteSwapScalar(fValue);
}
hsBool hsBezScalarKey::CompareValue(hsBezScalarKey *key)
{
return fValue == key->fValue;
}
/////////////////////////////////////////
void hsQuatKey::Read(hsStream *stream)
{
fFrame = stream->ReadSwap16();
fValue.Read(stream);
}
void hsQuatKey::Write(hsStream *stream)
{
stream->WriteSwap16(fFrame);
fValue.Write(stream);
}
hsBool hsQuatKey::CompareValue(hsQuatKey *key)
{
return fValue == key->fValue;
}
//////////////////////////////////////////////////////////////////////////////
const hsScalar hsCompressedQuatKey32::kOneOverRootTwo = 0.70710678;
const hsScalar hsCompressedQuatKey32::k10BitScaleRange = 1023 / (2 * kOneOverRootTwo);
void hsCompressedQuatKey32::Read(hsStream *stream)
{
fFrame = stream->ReadSwap16();
fData = stream->ReadSwap32();
}
void hsCompressedQuatKey32::Write(hsStream *stream)
{
stream->WriteSwap16(fFrame);
stream->WriteSwap32(fData);
}
hsBool hsCompressedQuatKey32::CompareValue(hsCompressedQuatKey32 *key)
{
return fData == key->fData;
}
// To store a quat in 32 bits, we find which element is the largest and use 2 bits to
// store which one it is. We now know the other 3 elements fall in the range
// of [-kOneOverRootTwo, kOneOverRootTwo]. We scale that range across 10 bits
// and store each. When extracting, we use the fact that the quat was normalized
// to compute the 4th element.
void hsCompressedQuatKey32::SetQuat(hsQuat &q)
{
q.Normalize();
UInt32 maxElement = kCompQuatNukeX;
hsScalar maxVal = hsABS(q.fX);
if (hsABS(q.fY) > maxVal)
{
maxElement = kCompQuatNukeY;
maxVal = hsABS(q.fY);
}
if (hsABS(q.fZ) > maxVal)
{
maxElement = kCompQuatNukeZ;
maxVal = hsABS(q.fZ);
}
if (hsABS(q.fW) > maxVal)
{
maxElement = kCompQuatNukeW;
maxVal = hsABS(q.fW);
}
switch (maxElement)
{
case kCompQuatNukeX:
{
// Invert the quat so that the largest element is positive.
// We need to do this so that later we know to use the positive root.
if (q.fX < 0)
q = -q;
fData = (maxElement << 30) |
(((UInt32)(k10BitScaleRange * (q.fY + kOneOverRootTwo))) << 20) |
(((UInt32)(k10BitScaleRange * (q.fZ + kOneOverRootTwo))) << 10) |
(((UInt32)(k10BitScaleRange * (q.fW + kOneOverRootTwo))));
break;
}
case kCompQuatNukeY:
{
if (q.fY < 0)
q = -q;
fData = (maxElement << 30) |
(((UInt32)(k10BitScaleRange * (q.fX + kOneOverRootTwo))) << 20) |
(((UInt32)(k10BitScaleRange * (q.fZ + kOneOverRootTwo))) << 10) |
(((UInt32)(k10BitScaleRange * (q.fW + kOneOverRootTwo))));
break;
}
case kCompQuatNukeZ:
{
if (q.fZ < 0)
q = -q;
fData = (maxElement << 30) |
(((UInt32)(k10BitScaleRange * (q.fX + kOneOverRootTwo))) << 20) |
(((UInt32)(k10BitScaleRange * (q.fY + kOneOverRootTwo))) << 10) |
(((UInt32)(k10BitScaleRange * (q.fW + kOneOverRootTwo))));
break;
}
case kCompQuatNukeW:
default:
{
if (q.fW < 0)
q = -q;
fData = (maxElement << 30) |
(((UInt32)(k10BitScaleRange * (q.fX + kOneOverRootTwo))) << 20) |
(((UInt32)(k10BitScaleRange * (q.fY + kOneOverRootTwo))) << 10) |
(((UInt32)(k10BitScaleRange * (q.fZ + kOneOverRootTwo))));
break;
}
}
}
void hsCompressedQuatKey32::GetQuat(hsQuat &q)
{
UInt32 maxElement = fData >> 30;
switch (maxElement)
{
case kCompQuatNukeX:
{
q.fY = (fData >> 20 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
q.fZ = (fData >> 10 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
q.fW = (fData & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
q.fX = hsSquareRoot(1 - q.fY * q.fY - q.fZ * q.fZ - q.fW *q.fW);
break;
}
case kCompQuatNukeY:
{
q.fX = (fData >> 20 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
q.fZ = (fData >> 10 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
q.fW = (fData & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
q.fY = hsSquareRoot(1 - q.fX * q.fX - q.fZ * q.fZ - q.fW *q.fW);
break;
}
case kCompQuatNukeZ:
{
q.fX = (fData >> 20 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
q.fY = (fData >> 10 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
q.fW = (fData & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
q.fZ = hsSquareRoot(1 - q.fX * q.fX - q.fY * q.fY - q.fW *q.fW);
break;
}
case kCompQuatNukeW:
default:
{
q.fX = (fData >> 20 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
q.fY = (fData >> 10 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
q.fZ = (fData & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo;
q.fW = hsSquareRoot(1 - q.fX * q.fX - q.fY * q.fY - q.fZ * q.fZ);
break;
}
}
}
/////////////////////////////////////////////////////////////////////////////
const hsScalar hsCompressedQuatKey64::kOneOverRootTwo = 0.70710678;
const hsScalar hsCompressedQuatKey64::k20BitScaleRange = 1048575 / (2 * kOneOverRootTwo);
const hsScalar hsCompressedQuatKey64::k21BitScaleRange = 2097151 / (2 * kOneOverRootTwo);
void hsCompressedQuatKey64::Read(hsStream *stream)
{
fFrame = stream->ReadSwap16();
fData[0] = stream->ReadSwap32();
fData[1] = stream->ReadSwap32();
}
void hsCompressedQuatKey64::Write(hsStream *stream)
{
stream->WriteSwap16(fFrame);
stream->WriteSwap32(fData[0]);
stream->WriteSwap32(fData[1]);
}
hsBool hsCompressedQuatKey64::CompareValue(hsCompressedQuatKey64 *key)
{
return (fData[0] == key->fData[0]) && (fData[1] == key->fData[1]);
}
// To store a quat in 64 bits, we find which element is the largest and use 2 bits to
// store which one it is. We now know the other 3 elements fall in the range
// of [-kOneOverRootTwo, kOneOverRootTwo]. We scale that range across 20/21/21 bits
// and store each. When extracting, we use the fact that the quat was normalized
// to compute the 4th element.
void hsCompressedQuatKey64::SetQuat(hsQuat &q)
{
q.Normalize();
UInt32 maxElement = kCompQuatNukeX;
hsScalar maxVal = hsABS(q.fX);
if (hsABS(q.fY) > maxVal)
{
maxElement = kCompQuatNukeY;
maxVal = hsABS(q.fY);
}
if (hsABS(q.fZ) > maxVal)
{
maxElement = kCompQuatNukeZ;
maxVal = hsABS(q.fZ);
}
if (hsABS(q.fW) > maxVal)
{
maxElement = kCompQuatNukeW;
maxVal = hsABS(q.fW);
}
switch (maxElement)
{
case kCompQuatNukeX:
{
// Invert the quat so that the largest element is positive.
// We need to do this so that later we know to use the positive root.
if (q.fX < 0)
q = -q;
fData[0] = (maxElement << 30) |
(((UInt32)(k20BitScaleRange * (q.fY + kOneOverRootTwo))) << 10) |
(((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo))) >> 11);
fData[1] =
(((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo))) << 21) |
(((UInt32)(k21BitScaleRange * (q.fW + kOneOverRootTwo))));
break;
}
case kCompQuatNukeY:
{
if (q.fY < 0)
q = -q;
fData[0] = (maxElement << 30) |
(((UInt32)(k20BitScaleRange * (q.fX + kOneOverRootTwo))) << 10) |
(((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo))) >> 11);
fData[1] =
(((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo))) << 21) |
(((UInt32)(k21BitScaleRange * (q.fW + kOneOverRootTwo))));
break;
}
case kCompQuatNukeZ:
{
if (q.fZ < 0)
q = -q;
fData[0] = (maxElement << 30) |
(((UInt32)(k20BitScaleRange * (q.fX + kOneOverRootTwo))) << 10) |
(((UInt32)(k21BitScaleRange * (q.fY + kOneOverRootTwo))) >> 11);
fData[1] =
(((UInt32)(k21BitScaleRange * (q.fY + kOneOverRootTwo))) << 21) |
(((UInt32)(k21BitScaleRange * (q.fW + kOneOverRootTwo))));
break;
}
case kCompQuatNukeW:
default:
{
if (q.fW < 0)
q = -q;
fData[0] = (maxElement << 30) |
(((UInt32)(k20BitScaleRange * (q.fX + kOneOverRootTwo))) << 10) |
(((UInt32)(k21BitScaleRange * (q.fY + kOneOverRootTwo))) >> 11);
fData[1] =
(((UInt32)(k21BitScaleRange * (q.fY + kOneOverRootTwo))) << 21) |
(((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo))));
break;
}
}
}
void hsCompressedQuatKey64::GetQuat(hsQuat &q)
{
UInt32 maxElement = fData[0] >> 30;
switch (maxElement)
{
case kCompQuatNukeX:
{
q.fY = ((fData[0] >> 10) & 0x000fffff) / k20BitScaleRange - kOneOverRootTwo;
q.fZ = (((fData[0] & 0x000003ff) << 11) | (fData[1] >> 21)) / k21BitScaleRange - kOneOverRootTwo;
q.fW = (fData[1] & 0x001fffff) / k21BitScaleRange - kOneOverRootTwo;
q.fX = hsSquareRoot(1 - q.fY * q.fY - q.fZ * q.fZ - q.fW *q.fW);
break;
}
case kCompQuatNukeY:
{
q.fX = ((fData[0] >> 10) & 0x000fffff) / k20BitScaleRange - kOneOverRootTwo;
q.fZ = (((fData[0] & 0x000003ff) << 11) | (fData[1] >> 21)) / k21BitScaleRange - kOneOverRootTwo;
q.fW = (fData[1] & 0x001fffff) / k21BitScaleRange - kOneOverRootTwo;
q.fY = hsSquareRoot(1 - q.fX * q.fX - q.fZ * q.fZ - q.fW *q.fW);
break;
}
case kCompQuatNukeZ:
{
q.fX = ((fData[0] >> 10) & 0x000fffff) / k20BitScaleRange - kOneOverRootTwo;
q.fY = (((fData[0] & 0x000003ff) << 11) | (fData[1] >> 21)) / k21BitScaleRange - kOneOverRootTwo;
q.fW = (fData[1] & 0x001fffff) / k21BitScaleRange - kOneOverRootTwo;
q.fZ = hsSquareRoot(1 - q.fX * q.fX - q.fY * q.fY - q.fW *q.fW);
break;
}
case kCompQuatNukeW:
default:
{
q.fX = ((fData[0] >> 10) & 0x000fffff) / k20BitScaleRange - kOneOverRootTwo;
q.fY = (((fData[0] & 0x000003ff) << 11) | (fData[1] >> 21)) / k21BitScaleRange - kOneOverRootTwo;
q.fZ = (fData[1] & 0x001fffff) / k21BitScaleRange - kOneOverRootTwo;
q.fW = hsSquareRoot(1 - q.fX * q.fX - q.fY * q.fY - q.fZ * q.fZ);
break;
}
}
}
/////////////////////////////////////////
// Not a key
//
void hsScaleValue::Read(hsStream *stream)
{
fS.Read(stream);
fQ.Read(stream);
}
void hsScaleValue::Write(hsStream *stream)
{
fS.Write(stream);
fQ.Write(stream);
}
/////////////////////////////////////////
void hsScaleKey::Read(hsStream *stream)
{
fFrame = stream->ReadSwap16();
fValue.Read(stream);
}
void hsScaleKey::Write(hsStream *stream)
{
stream->WriteSwap16(fFrame);
fValue.Write(stream);
}
hsBool hsScaleKey::CompareValue(hsScaleKey *key)
{
return fValue == key->fValue;
}
void hsBezScaleKey::Read(hsStream *stream)
{
fFrame = stream->ReadSwap16();
fInTan.Read(stream);
fOutTan.Read(stream);
fValue.Read(stream);
}
void hsBezScaleKey::Write(hsStream *stream)
{
stream->WriteSwap16(fFrame);
fInTan.Write(stream);
fOutTan.Write(stream);
fValue.Write(stream);
}
hsBool hsBezScaleKey::CompareValue(hsBezScaleKey *key)
{
return fValue == key->fValue;
}
//////////////////////
void hsG3DSMaxKeyFrame::Set(hsMatrix44 *mat, UInt16 frame)
{
fFrame = frame;
gemAffineParts parts;
decomp_affine(mat->fMap, &parts);
AP_SET(fParts, parts);
}
void hsG3DSMaxKeyFrame::Set(const hsAffineParts &parts, UInt16 frame)
{
fFrame = frame;
fParts = parts;
}
void hsG3DSMaxKeyFrame::Read(hsStream *stream)
{
fFrame = stream->ReadSwap16();
fParts.Read(stream);
}
void hsG3DSMaxKeyFrame::Write(hsStream *stream)
{
stream->WriteSwap16(fFrame);
fParts.Write(stream);
}
hsBool hsG3DSMaxKeyFrame::CompareValue(hsG3DSMaxKeyFrame *key)
{
return fParts == key->fParts;
}
/////////////////////////////////////////
void hsMatrix33Key::Read(hsStream *stream)
{
fFrame = stream->ReadSwap16();
Int32 i,j;
for(i=0;i<3;i++)
for(j=0;j<3;j++)
fValue.fMap[j][i] = stream->ReadSwapScalar();
}
void hsMatrix33Key::Write(hsStream *stream)
{
stream->WriteSwap16(fFrame);
Int32 i,j;
for(i=0;i<3;i++)
for(j=0;j<3;j++)
stream->WriteSwapScalar(fValue.fMap[j][i]);
}
hsBool hsMatrix33Key::CompareValue(hsMatrix33Key *key)
{
return fValue == key->fValue;
}
/////////////////////////////////////////
void hsMatrix44Key::Read(hsStream *stream)
{
fFrame = stream->ReadSwap16();
fValue.Read(stream);
}
void hsMatrix44Key::Write(hsStream *stream)
{
stream->WriteSwap16(fFrame);
fValue.Write(stream);
}
hsBool hsMatrix44Key::CompareValue(hsMatrix44Key *key)
{
return fValue == key->fValue;
}

View File

@ -0,0 +1,239 @@
/*==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==*/
#ifndef HSKEYS_inc
#define HSKEYS_inc
#include "HeadSpin.h"
#include "hsGeometry3.h"
#include "hsQuat.h"
#include "../plTransform/hsAffineParts.h"
#include "hsMatrix33.h"
#include "hsMatrix44.h"
// No virtuals. Keep these nice and lean.
struct hsKeyFrame
{
// Used by plController to specify which keys it has.
enum
{
kUnknownKeyFrame,
kPoint3KeyFrame,
kBezPoint3KeyFrame,
kScalarKeyFrame,
kBezScalarKeyFrame,
kScaleKeyFrame,
kBezScaleKeyFrame,
kQuatKeyFrame,
kCompressedQuatKeyFrame32,
kCompressedQuatKeyFrame64,
k3dsMaxKeyFrame,
kMatrix33KeyFrame,
kMatrix44KeyFrame,
};
UInt16 fFrame;
static const int kMaxFrameNumber;
};
struct hsPoint3Key : public hsKeyFrame
{
hsPoint3 fValue;
void Read(hsStream *stream);
void Write(hsStream *stream);
hsBool CompareValue(hsPoint3Key *key);
};
struct hsBezPoint3Key : public hsKeyFrame
{
hsPoint3 fInTan;
hsPoint3 fOutTan;
hsPoint3 fValue;
void Read(hsStream *stream);
void Write(hsStream *stream);
hsBool CompareValue(hsBezPoint3Key *key);
};
struct hsScalarKey : public hsKeyFrame
{
hsScalar fValue;
void Read(hsStream *stream);
void Write(hsStream *stream);
hsBool CompareValue(hsScalarKey *key);
};
struct hsBezScalarKey : public hsKeyFrame
{
hsScalar fInTan;
hsScalar fOutTan;
hsScalar fValue;
void Read(hsStream *stream);
void Write(hsStream *stream);
hsBool CompareValue(hsBezScalarKey *key);
};
struct hsQuatKey : public hsKeyFrame
{
hsQuat fValue;
void Read(hsStream *stream);
void Write(hsStream *stream);
hsBool CompareValue(hsQuatKey *key);
};
struct hsCompressedQuatKey32 : public hsKeyFrame
{
enum
{
kCompQuatNukeX,
kCompQuatNukeY,
kCompQuatNukeZ,
kCompQuatNukeW,
};
static const hsScalar kOneOverRootTwo;
static const hsScalar k10BitScaleRange;
void SetQuat(hsQuat &q);
void GetQuat(hsQuat &q);
void Read(hsStream *stream);
void Write(hsStream *stream);
hsBool CompareValue(hsCompressedQuatKey32 *key);
protected:
UInt32 fData;
};
struct hsCompressedQuatKey64 : public hsKeyFrame
{
enum
{
kCompQuatNukeX,
kCompQuatNukeY,
kCompQuatNukeZ,
kCompQuatNukeW,
};
static const hsScalar kOneOverRootTwo;
static const hsScalar k20BitScaleRange;
static const hsScalar k21BitScaleRange;
void SetQuat(hsQuat &q);
void GetQuat(hsQuat &q);
void Read(hsStream *stream);
void Write(hsStream *stream);
hsBool CompareValue(hsCompressedQuatKey64 *key);
protected:
UInt32 fData[2];
};
struct hsScaleValue : public hsKeyFrame
{
hsVector3 fS; /* Scale components for x,y,z */
hsQuat fQ; /* The axis along which the scale is applied */
void Read(hsStream *stream);
void Write(hsStream *stream);
int operator==(const hsScaleValue& a) const { return (fS == a.fS && fQ == a.fQ); }
};
//
//
//
struct hsScaleKey : public hsKeyFrame
{
hsScaleValue fValue;
void Read(hsStream *stream);
void Write(hsStream *stream);
hsBool CompareValue(hsScaleKey *key);
};
struct hsBezScaleKey : public hsKeyFrame
{
hsPoint3 fInTan;
hsPoint3 fOutTan;
hsScaleValue fValue;
void Read(hsStream *stream);
void Write(hsStream *stream);
hsBool CompareValue(hsBezScaleKey *key);
};
struct hsG3DSMaxKeyFrame : public hsKeyFrame
{
hsAffineParts fParts;
void Reset() { fParts.Reset(); } // Make parts identity
void Set(hsMatrix44 *mat, UInt16 frame);
void Set(const hsAffineParts &parts, UInt16 frame);
hsMatrix44* GetMatrix44(hsMatrix44 *mat) { fParts.ComposeMatrix(mat); return mat; }
void Read(hsStream *stream);
void Write(hsStream *stream);
hsBool CompareValue(hsG3DSMaxKeyFrame *key);
};
struct hsMatrix33Key : public hsKeyFrame
{
hsMatrix33 fValue;
void Read(hsStream *stream);
void Write(hsStream *stream);
hsBool CompareValue(hsMatrix33Key *key);
};
struct hsMatrix44Key : public hsKeyFrame
{
hsMatrix44 fValue;
void Read(hsStream *stream);
void Write(hsStream *stream);
hsBool CompareValue(hsMatrix44Key *key);
};
#endif

View File

@ -0,0 +1,220 @@
/*==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==*/
#ifndef hsTimedValue_inc
#define hsTimedValue_inc
#include "hsStream.h"
template <class T>
class hsTimedValue {
public:
enum {
kIdle = 0x1,
kInstant = 0x2
};
protected:
UInt32 fFlags;
hsScalar fDuration;
hsScalar fStartTime;
T fValue;
T fGoal;
T fFrom;
public:
hsTimedValue() : fFlags(kIdle|kInstant), fDuration(0) {}
hsTimedValue(const T& v) : fFlags(kIdle|kInstant), fDuration(0) { SetValue(v); }
UInt32 GetFlags() { return fFlags; }
void SetDuration(hsScalar duration);
hsScalar GetDuration() const { return fDuration; }
hsBool32 operator==(const hsTimedValue<T>& v);
hsTimedValue<T>& operator=(const T& v) { SetValue(v); return *this; }
hsTimedValue<T>& operator+=(const T& v) { SetValue(v + fValue); return *this; }
void SetTempValue(const T& v) { fValue = v; }
void SetValue(const T& v) { fFrom = fGoal = fValue = v; fFlags |= kIdle; }
const T& GetValue() const { return fValue; }
void SetGoal(const T& g) { fGoal = g; }
const T& GetGoal() const { return fGoal; }
void Reset() { fFlags |= (kIdle | kInstant); }
void StartClock(hsScalar s);
hsScalar GetStartTime() const { return fStartTime; }
const T& GetFrom() const { return fFrom; }
void Update(hsScalar s);
void WriteScalar(hsStream* s, hsScalar currSecs);
void Write(hsStream* s, hsScalar currSecs);
void ReadScalar(hsStream* s, hsScalar currSecs);
void Read(hsStream* s, hsScalar currSecs);
};
template <class T>
void hsTimedValue<T>::WriteScalar(hsStream* s, hsScalar currSecs)
{
s->WriteSwap32(fFlags);
s->WriteSwapScalar(fValue);
if( !(fFlags & kIdle) )
{
s->WriteSwapScalar(fDuration);
s->WriteSwapScalar(currSecs - fStartTime);
s->WriteSwapScalar(fGoal);
s->WriteSwapScalar(fFrom);
}
}
template <class T>
void hsTimedValue<T>::Write(hsStream* s, hsScalar currSecs)
{
s->WriteSwap32(fFlags);
fValue.Write(s);
if( !(fFlags & kIdle) )
{
s->WriteSwapScalar(fDuration);
s->WriteSwapScalar(currSecs - fStartTime);
fGoal.Write(s);
fFrom.Write(s);
}
}
template <class T>
void hsTimedValue<T>::ReadScalar(hsStream* s, hsScalar currSecs)
{
fFlags = s->ReadSwap32();
fValue = s->ReadSwapScalar();
if( !(fFlags & kIdle) )
{
fDuration = s->ReadSwapScalar();
fStartTime = currSecs - s->ReadSwapScalar();
fGoal = s->ReadSwapScalar();
fFrom = s->ReadSwapScalar();
}
}
template <class T>
void hsTimedValue<T>::Read(hsStream* s, hsScalar currSecs)
{
fFlags = s->ReadSwap32();
fValue.Read(s);
if( !(fFlags & kIdle) )
{
fDuration = s->ReadSwapScalar();
fStartTime = currSecs - s->ReadSwapScalar();
fGoal.Read(s);
fFrom.Read(s);
}
}
template <class T>
void hsTimedValue<T>::SetDuration(hsScalar duration)
{
fDuration = duration;
if( fDuration > 0 )
fFlags &= ~kInstant;
else
fFlags |= kInstant;
}
template <class T>
hsBool32 hsTimedValue<T>::operator==(const hsTimedValue<T>& v)
{
if ((fFlags == v.fFlags) &&
(fDuration == v.fDuration) &&
(fStartTime == v.fStartTime) &&
(fValue == v.fValue) &&
(fGoal == v.fGoal) &&
(fFrom == v.fFrom))
{
return true;
}
return false;
}
template <class T>
void hsTimedValue<T>::StartClock(hsScalar s)
{
fStartTime = s;
if( fFlags & kInstant )
{
fFlags |= kIdle;
fValue = fGoal;
return;
}
fFlags &= ~kIdle;
if( fValue == fGoal )
fFlags |= kIdle;
fFrom = fValue;
}
template <class T>
void hsTimedValue<T>::Update(hsScalar s)
{
if( fFlags & kIdle )
return;
hsAssert(fDuration > 0, "Instant should always be idle");
hsScalar interp = (s - fStartTime) / fDuration;
if( interp >= hsScalar1 )
{
fValue = fGoal;
interp = hsScalar1;
fFlags |= kIdle;
}
else
fValue = fFrom + (fGoal - fFrom) * interp;
}
#endif // hsTimedValue_inc

View File

@ -0,0 +1,347 @@
/*==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 "plAnimEaseTypes.h"
#include "plAnimTimeConvert.h"
///////////////////////////////////////////////////////////////////////////////////////////////
plATCEaseCurve *plATCEaseCurve::CreateEaseCurve(UInt8 type, hsScalar minLength, hsScalar maxLength, hsScalar length,
hsScalar startSpeed, hsScalar goalSpeed)
{
if (type == plAnimEaseTypes::kConstAccel)
return TRACKED_NEW plConstAccelEaseCurve(minLength, maxLength, length, startSpeed, goalSpeed);
if (type == plAnimEaseTypes::kSpline)
return TRACKED_NEW plSplineEaseCurve(minLength, maxLength, length, startSpeed, goalSpeed);
return nil;
}
void plATCEaseCurve::RecalcToSpeed(hsScalar startSpeed, hsScalar goalSpeed, hsBool preserveRate /* = false */)
{
hsScalar rate = 1;
if (fSpeed == goalSpeed && fStartSpeed == startSpeed) // already there, no need to do anything
return;
if (preserveRate)
rate = (fSpeed - fStartSpeed) / fLength;
fStartSpeed = startSpeed;
fSpeed = goalSpeed;
if (preserveRate)
SetLengthOnRate(rate);
}
void plATCEaseCurve::SetLengthOnRate(hsScalar rate)
{
fLength = (fSpeed - fStartSpeed) / rate;
if (fLength < 0)
fLength = -fLength;
}
hsScalar plATCEaseCurve::GetMinDistance()
{
if (fMinLength == 0)
return 0;
hsScalar oldLength = fLength;
fLength = fMinLength;
hsScalar result = PositionGivenTime(fMinLength);
fLength = oldLength;
return result;
}
hsScalar plATCEaseCurve::GetMaxDistance()
{
if (fMaxLength == 0)
return 0;
hsScalar oldLength = fLength;
fLength = fMaxLength;
hsScalar result = PositionGivenTime(fMaxLength);
fLength = oldLength;
return result;
}
hsScalar plATCEaseCurve::GetNormDistance()
{
if (fNormLength == 0)
return 0;
hsScalar oldLength = fLength;
fLength = fNormLength;
hsScalar result = PositionGivenTime(fNormLength);
fLength = oldLength;
return result;
}
void plATCEaseCurve::Read(hsStream *s, hsResMgr *mgr)
{
plCreatable::Read(s, mgr);
fMinLength = s->ReadSwapScalar();
fMaxLength = s->ReadSwapScalar();
fNormLength = fLength = s->ReadSwapScalar();
fStartSpeed = s->ReadSwapScalar();
fSpeed = s->ReadSwapScalar();
fBeginWorldTime = s->ReadSwapDouble();
}
void plATCEaseCurve::Write(hsStream *s, hsResMgr *mgr)
{
plCreatable::Write(s, mgr);
s->WriteSwapScalar(fMinLength);
s->WriteSwapScalar(fMaxLength);
s->WriteSwapScalar(fNormLength);
s->WriteSwapScalar(fStartSpeed);
s->WriteSwapScalar(fSpeed);
s->WriteSwapDouble(fBeginWorldTime);
}
///////////////////////////////////////////////////////////////////////////////////////////////
plConstAccelEaseCurve::plConstAccelEaseCurve()
{
fMinLength = fMaxLength = fNormLength = fLength = 1;
fBeginWorldTime = 0;
RecalcToSpeed(0, 1);
}
plConstAccelEaseCurve::plConstAccelEaseCurve(hsScalar minLength, hsScalar maxLength, hsScalar length,
hsScalar startSpeed, hsScalar goalSpeed)
{
fMinLength = minLength;
fMaxLength = maxLength;
fNormLength = fLength = length;
fBeginWorldTime = 0;
RecalcToSpeed(startSpeed, goalSpeed);
}
plATCEaseCurve *plConstAccelEaseCurve::Clone() const
{
plConstAccelEaseCurve *curve = TRACKED_NEW plConstAccelEaseCurve;
curve->fStartSpeed = fStartSpeed;
curve->fMinLength = fMinLength;
curve->fMaxLength = fMaxLength;
curve->fNormLength = fNormLength;
curve->fBeginWorldTime = fBeginWorldTime;
curve->fLength = fLength;
curve->fSpeed = fSpeed;
return curve;
}
void plConstAccelEaseCurve::SetLengthOnDistance(hsScalar dist)
{
fLength = 2 * dist / (fSpeed + fStartSpeed);
}
hsScalar plConstAccelEaseCurve::PositionGivenTime(hsScalar time) const
{
return (hsScalar)(fStartSpeed * time + (0.5 * (fSpeed - fStartSpeed) / fLength) * time * time);
}
hsScalar plConstAccelEaseCurve::VelocityGivenTime(hsScalar time) const
{
return fStartSpeed + ((fSpeed - fStartSpeed) / fLength) * time;
}
hsScalar plConstAccelEaseCurve::TimeGivenVelocity(hsScalar velocity) const
{
return (velocity - fStartSpeed) / ((fSpeed - fStartSpeed) / fLength);
}
///////////////////////////////////////////////////////////////////////////////////////////////
plSplineEaseCurve::plSplineEaseCurve()
{
fMinLength = fMaxLength = fNormLength = fLength = 1;
fBeginWorldTime = 0;
RecalcToSpeed(0, 1);
}
plSplineEaseCurve::plSplineEaseCurve(hsScalar minLength, hsScalar maxLength, hsScalar length,
hsScalar startSpeed, hsScalar goalSpeed)
{
fMinLength = minLength;
fMaxLength = maxLength;
fNormLength = fLength = length;
fBeginWorldTime = 0;
RecalcToSpeed(startSpeed, goalSpeed);
}
plATCEaseCurve *plSplineEaseCurve::Clone() const
{
plSplineEaseCurve *curve = TRACKED_NEW plSplineEaseCurve;
curve->fStartSpeed = fStartSpeed;
curve->fMinLength = fMinLength;
curve->fMaxLength = fMaxLength;
curve->fNormLength = fNormLength;
curve->fBeginWorldTime = fBeginWorldTime;
curve->fLength = fLength;
curve->fSpeed = fSpeed;
int i;
for (i = 0; i < 4; i++)
curve->fCoef[i] = fCoef[i];
return curve;
}
void plSplineEaseCurve::RecalcToSpeed(hsScalar startSpeed, hsScalar goalSpeed, hsBool preserveRate /* = false */)
{
plATCEaseCurve::RecalcToSpeed(startSpeed, goalSpeed, preserveRate);
// These are greatly simplified because the in/out tangents are always zero
// Note: "b" is always zero for the ease splines we're currently doing (and will remain that way
// so long as the initial acceleration is zero. Can optimize a bit of the eval math to take
// advantage of this.
hsScalar a, b, c, d;
a = fStartSpeed;
b = 0;
c = -3 * fStartSpeed + 3 * fSpeed;
d = 2 * fStartSpeed - 2 * fSpeed;
fCoef[0] = a;
fCoef[1] = b;
fCoef[2] = c;
fCoef[3] = d;
}
void plSplineEaseCurve::SetLengthOnDistance(hsScalar dist)
{
hsScalar curDist = PositionGivenTime(fLength);
fLength = fLength * dist / curDist;
}
hsScalar plSplineEaseCurve::PositionGivenTime(hsScalar time) const
{
hsScalar t1, t2, t3, t4;
t1 = time / fLength;
t2 = t1 * t1;
t3 = t2 * t1;
t4 = t3 * t1;
return fLength * (fCoef[0] * t1 + fCoef[1] * t2 / 2 + fCoef[2] * t3 / 3 + fCoef[3] * t4 / 4);
}
hsScalar plSplineEaseCurve::VelocityGivenTime(hsScalar time) const
{
hsScalar t1, t2, t3;
t1 = time / fLength;
t2 = t1 * t1;
t3 = t2 * t1;
return fCoef[0] + fCoef[1] * t1 + fCoef[2] * t2 + fCoef[3] * t3;
}
hsScalar plSplineEaseCurve::TimeGivenVelocity(hsScalar velocity) const
{
// Code based off of Graphics Gems V, pp 11-12 and
// http://www.worldserver.com/turk/opensource/FindCubicRoots.c.txt
// Solving the equation: fCoef[0] + fCoef[1] * t + fCoef[2] * t^2 + fCoef[3] * t^3 - velocity = 0
hsScalar root;
hsScalar a = (fCoef[0] - velocity) / fCoef[3];
hsScalar b = fCoef[1] / fCoef[3];
hsScalar c = fCoef[2] / fCoef[3];
hsScalar Q = (c * c - 3 * b) / 9;
hsScalar R = (2 * c * c * c - 9 * c * b + 27 * a) / 54;
hsScalar Q3 = Q * Q * Q;
hsScalar D = Q3 - R * R;
if (D >= 0)
{
// 3 roots, find the one in the range [0, 1]
const hsScalar pi = 3.14159;
double theta = acos(R / sqrt(Q3));
double sqrtQ = sqrt(Q);
root = (hsScalar)(-2 * sqrtQ * cos((theta + 4 * pi) / 3) - c / 3); // Middle root, most likely to match
if (root < 0.f || root > 1.f)
{
root = (hsScalar)(-2 * sqrtQ * cos((theta + 2 * pi) / 3) - c / 3); // Lower root
if (root < 0.f || root > 1.f)
{
root = (hsScalar)(-2 * sqrtQ * cos(theta / 3) - c / 3); // Upper root
}
}
}
else // One root to the equation (I don't expect this to happen for ease splines, but JIC)
{
double E = sqrt(-D) + pow(fabs(R), 1.f / 3.f);
root = (hsScalar)((E + Q / E) - c / 3);
if (R > 0)
root = -root;
}
if (root < 0.f || root > 1.f)
{
hsAssert(false, "No valid root found while solving animation spline");
// Either a bug, or a rare case of floating-point inaccuracy. Either way, guess
// the proper root as either the start or end of the curve based on the velocity.
hsScalar dStart = velocity - fStartSpeed;
if (dStart < 0)
dStart = -dStart;
hsScalar dEnd = velocity - fSpeed;
if (dEnd < 0)
dEnd = -dEnd;
root = (dStart < dEnd ? 0.f : 1.f);
}
return root * fLength;
}
void plSplineEaseCurve::Read(hsStream *s, hsResMgr *mgr)
{
plATCEaseCurve::Read(s, mgr);
fCoef[0] = s->ReadSwapScalar();
fCoef[1] = s->ReadSwapScalar();
fCoef[2] = s->ReadSwapScalar();
fCoef[3] = s->ReadSwapScalar();
}
void plSplineEaseCurve::Write(hsStream *s, hsResMgr *mgr)
{
plATCEaseCurve::Write(s, mgr);
s->WriteSwapScalar(fCoef[0]);
s->WriteSwapScalar(fCoef[1]);
s->WriteSwapScalar(fCoef[2]);
s->WriteSwapScalar(fCoef[3]);
}

View File

@ -0,0 +1,43 @@
/*==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==*/
#ifndef PL_ANIM_EASE_TYPES
#define PL_ANIM_EASE_TYPES
// Silly little header so that both export and runtime code can use these
// constants without including tons of unneccessary stuff
namespace plAnimEaseTypes
{
enum {
kNoEase,
kConstAccel,
kSpline,
};
};
#define ENTIRE_ANIMATION_NAME "(Entire Animation)"
#endif

View File

@ -0,0 +1,793 @@
/*==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 "plAnimPath.h"
#include "plController.h"
#include "hsFastMath.h"
#include "hsResMgr.h"
const hsScalar kSmallDelTime = 1.e-2f;
const hsScalar kInvSmallDelTime = 1.f / kSmallDelTime;
const hsScalar kTerminateDelTime = 1.e-3f;
const hsScalar kTerminateDelDistSq = .1f;
plAnimPath::plAnimPath()
: fController(nil), fLength(0), fMinDistSq(0),
fAnimPathFlags(0)
{
fLocalToWorld.Reset();
fWorldToLocal.Reset();
Reset();
}
plAnimPath::~plAnimPath()
{
delete fController;
}
void plAnimPath::Reset()
{
SetCurTime(0);
}
void plAnimPath::SetCurTime(hsScalar t, UInt32 calcFlags)
{
fTime = t;
if( !fController )
{
fPos.Set(0,0,0);
fXform.Reset();
fVel.Set(0,0,0);
fAccel.Set(0,0,0);
return;
}
hsScalar t0, t1, t2;
if( t < kSmallDelTime )
{
t0 = t;
t1 = t + kSmallDelTime;
t2 = t + 2 * kSmallDelTime;
}
else if( t > fLength - kSmallDelTime )
{
t0 = t - 2 * kSmallDelTime;
t1 = t - kSmallDelTime;
t2 = t;
}
else
{
t0 = t - kSmallDelTime;
t1 = t;
t2 = t + kSmallDelTime;
}
if (!(calcFlags & kCalcPosOnly))
{
hsPoint3 pos[3];
fController->Interp(t0, &fParts);
pos[0].Set(fParts.fT.fX, fParts.fT.fY, fParts.fT.fZ);
fController->Interp(t1, &fParts);
pos[1].Set(fParts.fT.fX, fParts.fT.fY, fParts.fT.fZ);
fController->Interp(t2, &fParts);
pos[2].Set(fParts.fT.fX, fParts.fT.fY, fParts.fT.fZ);
fVel.Set(pos+1, pos+0);
fVel *= kInvSmallDelTime;
fVel = fLocalToWorld * fVel;
fAccel.Set(&(pos[2] - pos[1]), &(pos[1] - pos[0]));
fAccel *= kInvSmallDelTime * kInvSmallDelTime;
fAccel = fLocalToWorld * fAccel;
}
fController->Interp(t, &fParts);
fParts.ComposeMatrix(&fXform);
fXform = fLocalToWorld * fXform;
fXform.GetTranslate(&fPos);
}
void plAnimPath::ICalcBounds()
{
if( !fController )
return;
plController* pc = fController->GetPosController();
int i;
hsPoint3 pos;
hsTArray<hsScalar> keyTimes;
pc->GetKeyTimes(keyTimes);
fCenter.Set(0,0,0);
for( i = 0; i < keyTimes.GetCount() ; i++ )
{
pc->Interp(keyTimes[i], &pos);
fCenter += pos;
}
fCenter *= hsScalarInvert((hsScalar)keyTimes.GetCount());
fRadius = 0;
for( i = 0; i < keyTimes.GetCount(); i++ )
{
pc->Interp(keyTimes[i], &pos);
hsScalar rad = (pos - fCenter).Magnitude();
if( rad > fRadius )
fRadius = rad;
}
}
hsScalar plAnimPath::ICalcTotalLength()
{
if( !(fController && fController->GetPosController()) )
return 0;
fLength = fController->GetPosController()->GetLength();
return fLength;
}
void plAnimPath::SetController(plCompoundController* tmc)
{
hsAssert(tmc, "Bad (nil) controller for AnimPath");
hsAssert(tmc->GetPosController(), "Bad controller for AnimPath");
fController = tmc;
ICalcTotalLength();
ICalcBounds();
}
void plAnimPath::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l)
{
fLocalToWorld = l2w;
fWorldToLocal = w2l;
}
void plAnimPath::Read(hsStream* stream, hsResMgr* mgr)
{
fAnimPathFlags=stream->ReadSwap32();
delete fController;
fController = plCompoundController::ConvertNoRef(mgr->ReadCreatable(stream));
ICalcBounds();
fParts.Read(stream);
fLocalToWorld.Read(stream);
fWorldToLocal.Read(stream);
fLength = stream->ReadSwapScalar();
fMinDistSq = stream->ReadSwapScalar();
Reset();
}
void plAnimPath::Write(hsStream* stream, hsResMgr* mgr)
{
stream->WriteSwap32(fAnimPathFlags);
mgr->WriteCreatable(stream, fController);
fParts.Write(stream);
fLocalToWorld.Write(stream);
fWorldToLocal.Write(stream);
stream->WriteSwapScalar(fLength);
stream->WriteSwapScalar(fMinDistSq);
}
hsBool plAnimPath::OutOfRange(hsPoint3 &worldPt, hsScalar range) const
{
hsPoint3 pt = fWorldToLocal * worldPt;
hsScalar radius = (pt - fCenter).Magnitude() - fRadius;
return( radius > range );
}
hsScalar plAnimPath::GetExtremePoint(hsPoint3 &worldPt) const
{
if( !fController )
return 0;
hsPoint3 pt = fWorldToLocal * worldPt;
plController *pc = fController->GetPosController();
hsScalar minDistSq = 1.e33f;
hsScalar minTime = 0, delTime = 0;
// start search by using the time of the closest ctrl point
int i;
hsTArray<hsScalar> keyTimes;
pc->GetKeyTimes(keyTimes);
for( i = 0; i < keyTimes.GetCount(); i++ )
{
hsScalar t = keyTimes[i];
hsPoint3 pos;
pc->Interp(t, &pos); // handles easing
hsScalar distSq = (pt - pos).MagnitudeSquared();
if( distSq < minDistSq )
{
minDistSq = distSq;
minTime = t;
if( 0 == i )
delTime = keyTimes[i+1] - t;
else if( keyTimes.GetCount() - 1 == i )
delTime = t - keyTimes[i - 1];
else
{
hsScalar fore = keyTimes[i + 1] - t;
hsScalar back = t - keyTimes[i - 1];
delTime = hsMaximum(fore, back);
}
}
}
return GetExtremePoint(minTime, delTime, worldPt);
}
hsScalar plAnimPath::GetExtremePoint(hsScalar lastTime, hsScalar delTime, hsPoint3 &worldPt) const
{
if( !fController )
return 0;
hsPoint3 pt = fWorldToLocal * worldPt;
IInitInterval(lastTime, delTime, pt);
return ICheckInterval(pt);
}
hsScalar plAnimPath::ICheckInterval(hsPoint3 &pt) const
{
if( fDelTime <= kTerminateDelTime &&
hsVector3(&fCurPos, &fPrevPos).MagnitudeSquared() < kTerminateDelDistSq)
{
return IBestTime();
}
if( fThisTime < 0 )
return 0;
if( fThisTime > fLength )
return fLength;
if( GetFarthest() )
{
if( (fLastDistSq > fThisDistSq)&&(fLastDistSq >= fNextDistSq) )
return IShiftBack(pt);
if( (fNextDistSq > fThisDistSq)&&(fNextDistSq >= fLastDistSq) )
return IShiftFore(pt);
if( (fThisDistSq >= fLastDistSq)&&(fLastDistSq >= fNextDistSq) )
return ISubDivBack(pt);
if( (fThisDistSq >= fNextDistSq)&&(fNextDistSq >= fLastDistSq) )
return ISubDivFore(pt);
}
else
{
if( (fLastDistSq < fThisDistSq)&&(fLastDistSq <= fNextDistSq) )
return IShiftBack(pt);
if( (fNextDistSq < fThisDistSq)&&(fNextDistSq <= fLastDistSq) )
return IShiftFore(pt);
if( (fThisDistSq <= fLastDistSq)&&(fLastDistSq <= fNextDistSq) )
return ISubDivBack(pt);
if( (fThisDistSq <= fNextDistSq)&&(fNextDistSq <= fLastDistSq) )
return ISubDivFore(pt);
}
hsAssert(false, "Shouldn't have gotten here");
return 0;
}
void plAnimPath::IInitInterval(hsScalar time, hsScalar delTime, hsPoint3 &pt) const
{
plController* pc = fController->GetPosController();
hsPoint3 pos;
fDelTime = delTime;
if( fDelTime <= kTerminateDelTime )
fDelTime = kTerminateDelTime * 2;
else
if( fDelTime > fLength * 0.5f )
fDelTime = fLength * 0.5f;
fThisTime = time;
if( fThisTime < fDelTime )
fThisTime = fDelTime;
else if( fThisTime > fLength - fDelTime )
fThisTime = fLength - fDelTime;
pc->Interp(fThisTime, &pos);
fPrevPos=fCurPos=pos;
fThisDistSq = (pos - pt).MagnitudeSquared();
fNextTime = fThisTime + delTime;
if( fNextTime > fLength )
fNextTime = fLength;
if (!(GetAnimPathFlags() & kFavorBwdSearch))
{
pc->Interp(fNextTime, &pos);
fNextDistSq = (pos - pt).MagnitudeSquared();
}
else
{
fNextDistSq = 1.e33f;
}
fLastTime = fThisTime - delTime;
if( fLastTime < 0 )
fLastTime = 0;
if (!(GetAnimPathFlags() & kFavorFwdSearch))
{
pc->Interp(fLastTime, &pos);
fLastDistSq = (pos - pt).MagnitudeSquared();
}
else
{
fLastDistSq = 1.e33f;
}
if( fMinDistSq != 0 )
{
fThisDistSq -= fMinDistSq;
if( fThisDistSq < 0 )
fThisDistSq = -fThisDistSq;
fNextDistSq -= fMinDistSq;
if( fNextDistSq < 0 )
fNextDistSq = -fNextDistSq;
fLastDistSq -= fMinDistSq;
if( fLastDistSq < 0 )
fLastDistSq = -fLastDistSq;
}
}
hsScalar plAnimPath::ISubDivBack(hsPoint3 &pt) const
{
fNextTime = fThisTime;
fNextDistSq = fThisDistSq;
fDelTime *= 0.5f;
fThisTime -= fDelTime;
if (fThisTime<0)
fThisTime = GetWrap() ? fLength + fThisTime : 0;
plController* pc = fController->GetPosController();
hsPoint3 pos;
pc->Interp(fThisTime, &pos);
fThisDistSq = (pos - pt).MagnitudeSquared() - fMinDistSq;
if( fThisDistSq < 0 )
fThisDistSq = -fThisDistSq;
fPrevPos=fCurPos;
fCurPos=pos;
return ICheckInterval(pt);
}
hsScalar plAnimPath::ISubDivFore(hsPoint3 &pt) const
{
fLastTime = fThisTime;
fLastDistSq = fThisDistSq;
fDelTime *= 0.5f;
fThisTime += fDelTime;
if (fThisTime>fLength)
fThisTime = GetWrap() ? fThisTime-fLength : fLength;
plController* pc = fController->GetPosController();
hsPoint3 pos;
pc->Interp(fThisTime, &pos);
fThisDistSq = (pos - pt).MagnitudeSquared() - fMinDistSq;
if( fThisDistSq < 0 )
fThisDistSq = -fThisDistSq;
fPrevPos=fCurPos;
fCurPos=pos;
return ICheckInterval(pt);
}
hsScalar plAnimPath::IShiftBack(hsPoint3 &pt) const
{
if( !GetWrap() && (fLastTime <= 0) )
return ISubDivBack(pt);
fNextTime = fThisTime;
fNextDistSq = fThisDistSq;
fThisTime = fLastTime;
fThisDistSq = fLastDistSq;
fLastTime -= fDelTime;
if( fLastTime < 0 )
fLastTime = GetWrap() ? fLength + fLastTime : 0;
plController* pc = fController->GetPosController();
hsPoint3 pos;
pc->Interp(fLastTime, &pos);
fLastDistSq = (pos - pt).MagnitudeSquared() - fMinDistSq;
if( fLastDistSq < 0 )
fLastDistSq = -fLastDistSq;
fPrevPos=fCurPos;
fCurPos=pos;
return ICheckInterval(pt);
}
hsScalar plAnimPath::IShiftFore(hsPoint3 &pt) const
{
if( !GetWrap() &&(fNextTime >= fLength) )
return ISubDivFore(pt);
fLastTime = fThisTime;
fLastDistSq = fThisDistSq;
fThisTime = fNextTime;
fThisDistSq = fNextDistSq;
fNextTime += fDelTime;
if( fNextTime > fLength )
fNextTime = GetWrap() ? fNextTime - fLength : fLength;
plController* pc = fController->GetPosController();
hsPoint3 pos;
pc->Interp(fNextTime, &pos);
fNextDistSq = (pos - pt).MagnitudeSquared() - fMinDistSq;
if( fNextDistSq < 0 )
fNextDistSq = -fNextDistSq;
fPrevPos=fCurPos;
fCurPos=pos;
return ICheckInterval(pt);
}
//
// wireframe debug draw method.
// doesn't use any fancy subdivision or curvature measure when drawing.
// Changes current time.
//
void plAnimPath::IMakeSegment(hsTArray<UInt16>& idx, hsTArray<hsPoint3>& pos,
hsPoint3& p1, hsPoint3& p2)
{
hsVector3 del(&p2, &p1);
hsVector3 up;
up.Set(0,0,1.f);
const hsScalar kOutLength = 0.25f;
hsVector3 a = del % up;
hsScalar magSq = a.MagnitudeSquared();
if( magSq < 1.e-3f )
{
a.Set(kOutLength, 0, 0);
}
else
{
a *= hsFastMath::InvSqrtAppr(magSq);
a *= kOutLength;
}
hsVector3 b = a % del;
hsFastMath::Normalize(b);
b *= kOutLength;
hsPoint3 p1out, p2out;
int baseIdx = pos.GetCount();
pos.Append(p1);
pos.Append(p2);
p1out = p1;
p1out += a;
p2out = p2;
p2out += a;
pos.Append(p1out);
pos.Append(p2out);
p1out += -2.f * a;
p2out += -2.f * a;
pos.Append(p1out);
pos.Append(p2out);
p1out = p1;
p1out += b;
p2out = p2;
p2out += b;
pos.Append(p1out);
pos.Append(p2out);
p1out += -2.f * b;
p2out += -2.f * b;
pos.Append(p1out);
pos.Append(p2out);
int i;
for( i = 0; i < 4; i++ )
{
int outIdx = baseIdx + 2 + i * 2;
idx.Append(baseIdx);
idx.Append(baseIdx + 1);
idx.Append(baseIdx + outIdx);
idx.Append(baseIdx + outIdx);
idx.Append(baseIdx + 1);
idx.Append(baseIdx + outIdx + 1);
}
}
void plAnimPath::MakeDrawList(hsTArray<UInt16>& idx, hsTArray<hsPoint3>& pos)
{
hsMatrix44 resetL2W = GetLocalToWorld();
hsMatrix44 resetW2L = GetWorldToLocal();
hsMatrix44 ident;
ident.Reset();
SetTransform(ident, ident);
hsScalar numSegs = fRadius; // crude estimate of arclength
if (numSegs>100)
numSegs=100;
hsScalar animLen = GetLength();
hsScalar timeInc = animLen/numSegs;
hsScalar time=0;
hsPoint3 p1, p2;
SetCurTime(0, kCalcPosOnly);
GetPosition(&p1);
time += timeInc;
hsBool quit=false;
while(! quit && time < animLen+timeInc)
{
if (time > animLen)
{
time = animLen;
quit=true;
}
SetCurTime(time, kCalcPosOnly);
GetPosition(&p2);
IMakeSegment(idx, pos, p1, p2);
time += timeInc;
p1 = p2;
}
SetTransform(resetL2W, resetW2L);
}
//
// Precompute array of arclen deltas for lookahead ability.
// Changes current time!
//
void plAnimPath::ComputeArcLenDeltas(Int32 numSamples)
{
if (fArcLenDeltas.GetCount() >= numSamples)
return; // already computed enough samples
// compute arc len deltas
fArcLenDeltas.Reset();
fArcLenDeltas.SetCount(numSamples);
hsScalar animLen = GetLength();
hsScalar timeInc = animLen/(numSamples-1); // use num-1 since we'll create the zeroth entry by hand
hsScalar time=0;
hsPoint3 p1, p2;
Int32 cnt=0;
// prime initial point
SetCurTime(0, kCalcPosOnly);
GetPosition(&p1);
ArcLenDeltaInfo aldi(time, 0);
fArcLenDeltas[cnt++]=aldi;
time += timeInc;
hsBool quit=false;
while(!quit && time<animLen+timeInc)
{
if (time > animLen || cnt+1 == numSamples)
{
time = animLen;
quit=true;
}
SetCurTime(time, kCalcPosOnly);
GetPosition(&p2);
ArcLenDeltaInfo aldi(time, hsVector3(&p2, &p1).Magnitude());
fArcLenDeltas[cnt++]=aldi;
time += timeInc;
p1 = p2;
}
hsAssert(fArcLenDeltas.GetCount()==numSamples, "arcLenArray size wrong?");
hsAssert(cnt==numSamples, "arcLenArray size wrong?");
}
//
// Returns time of point (at least) arcLength units away from point at startTime.
// Also sets strtSrchIdx for incremental searching.
//
hsScalar plAnimPath::GetLookAheadTime(hsScalar startTime, hsScalar arcLengthIn, hsBool bwd,
Int32* startSrchIdx)
{
if (arcLengthIn==0)
return startTime; // default is no look ahead
if (startTime==GetLength() && !bwd)
return GetLength();
if (startTime==0 && bwd)
return 0;
hsAssert(startSrchIdx, "nil var for startSrchIdx");
hsScalar oldTime=fTime;
ComputeArcLenDeltas(); // precompute first time only
// save and change time if necessary
if (fTime!=startTime)
SetCurTime(startTime, kCalcPosOnly);
// find nearest (forward) arcLen sample point, use starting srch index provided
hsBool found=false;
Int32 i;
for(i=(*startSrchIdx); i<fArcLenDeltas.GetCount()-1; i++)
{
if (fArcLenDeltas[i].fT<=startTime && startTime<fArcLenDeltas[i+1].fT)
{
*startSrchIdx=i;
found=true;
break;
}
}
if (!found)
{
// check if equal to last index
if (startTime==fArcLenDeltas[fArcLenDeltas.GetCount()-1].fT)
{
*startSrchIdx=fArcLenDeltas.GetCount()-1;
found=true;
}
else
{
for(i=0; i<*startSrchIdx; i++)
{
if (fArcLenDeltas[i].fT<=startTime && startTime<fArcLenDeltas[i+1].fT)
{
*startSrchIdx=i;
found=true;
break;
}
}
}
}
// find distance to nearest arcLen sample point
Int32 nearestIdx = bwd ? *startSrchIdx : *startSrchIdx+1;
hsAssert(found, "couldn't find arcLength sample");
hsPoint3 pos;
GetPosition(&pos); // startTime position
hsPoint3 pos2;
hsScalar endTime = fArcLenDeltas[nearestIdx].fT;
SetCurTime(endTime, kCalcPosOnly);
GetPosition(&pos2); // position at nearest sample point
hsScalar curArcLen = hsVector3(&pos2, &pos).Magnitude();
hsScalar curTime=0;
hsBool quit=false;
hsScalar timeOut = 0;
Int32 inc = bwd ? -1 : 1;
// now sum distance deltas until we exceed the desired arcLen
if (curArcLen<arcLengthIn)
{
for(i=bwd ? nearestIdx : nearestIdx+inc; i<fArcLenDeltas.GetCount() && i>=0; i+=inc)
{
if (curArcLen+fArcLenDeltas[i].fArcLenDelta>arcLengthIn)
{
// gone tooFar
endTime = fArcLenDeltas[i].fT;
curTime = fArcLenDeltas[i-1].fT;
break;
}
curArcLen += fArcLenDeltas[i].fArcLenDelta;
}
if ( (i==fArcLenDeltas.GetCount() && !bwd) || (i<0 && bwd) )
{
quit=true;
timeOut = bwd ? 0 : GetLength();
}
}
else
{
curArcLen = 0;
curTime = startTime;
}
if (!quit)
{
// interp remaining interval
// 1. compute necessary distToGoal
hsScalar distToGoal = arcLengthIn-curArcLen;
hsAssert(distToGoal, "0 length distToGoal?");
// 2. compute % of dist interval which gives distToGoal
SetCurTime(curTime, kCalcPosOnly);
GetPosition(&pos);
SetCurTime(endTime, kCalcPosOnly);
GetPosition(&pos2);
hsScalar distInterval = hsVector3(&pos2, &pos).Magnitude();
hsScalar percent = distToGoal/distInterval;
hsAssert(percent>=0 && percent<=1, "illegal percent value");
// 3. compute interpolated time value using percent
if (!bwd)
timeOut = curTime + (endTime-curTime)*percent;
else
timeOut = endTime - (endTime-curTime)*percent;
hsAssert((timeOut>=curTime && timeOut<=endTime), "illegal interpolated time value");
// hsAssert(!bwd || (timeOut<=curTime && timeOut>=endTime), "bwd: illegal interpolated time value");
}
// restore time
if (fTime != oldTime)
SetCurTime(oldTime);
hsAssert(bwd || (timeOut>startTime && timeOut<=GetLength()), "fwd: illegal look ahead time");
hsAssert(!bwd || (timeOut<startTime && timeOut>=0), "bwd: illegal look ahead time");
return timeOut;
}

View File

@ -0,0 +1,172 @@
/*==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==*/
#ifndef plAnimPath_inc
#define plAnimPath_inc
#include "hsTemplates.h"
#include "hsGeometry3.h"
#include "hsMatrix44.h"
#include "../plTransform/hsAffineParts.h"
#include "../pnFactory/plCreatable.h"
class plCompoundController;
class plAnimPath : public plCreatable
{
public:
enum Flags
{
kNone = 0x0,
kFavorFwdSearch = 0x1, // only move fwd on the curve when searching
kFavorBwdSearch = 0x2, // only move bwd on the curve when searching
kCalcPosOnly = 0x4, // only compute pos when calling SetCurTime()
kFarthest = 0x8,
kWrap = 0x10,
kIncrement = 0x20, // find the nearest / farthest point, but increment toward it
};
protected:
// The final product info
hsMatrix44 fXform;
hsPoint3 fPos;
hsVector3 fVel;
hsVector3 fAccel;
hsScalar fTime; // presumably seconds
// The paramters (and options) for this curve.
UInt32 fAnimPathFlags; // currently set at runtime only
hsScalar fMinDistSq;
hsScalar fLength; // presumably seconds
// Controller stuff only works in local space.
hsMatrix44 fLocalToWorld;
hsMatrix44 fWorldToLocal;
// Bounding sphere available for ignoring out of range
hsPoint3 fCenter;
hsScalar fRadius;
plCompoundController* fController;
hsAffineParts fParts;
// These are temps during a search. They're here to avoid recalc.
mutable hsScalar fLastTime;
mutable hsScalar fLastDistSq;
mutable hsScalar fThisTime;
mutable hsScalar fThisDistSq;
mutable hsScalar fNextTime;
mutable hsScalar fNextDistSq;
mutable hsScalar fDelTime;
mutable hsPoint3 fPrevPos, fCurPos;
void ICalcBounds();
hsScalar ICalcTotalLength();
hsScalar IShiftFore(hsPoint3 &pt) const;
hsScalar IShiftBack(hsPoint3 &pt) const;
hsScalar ISubDivFore(hsPoint3 &pt) const;
hsScalar ISubDivBack(hsPoint3 &pt) const;
void IInitInterval(hsScalar time, hsScalar delTime, hsPoint3 &pt) const;
hsScalar ICheckInterval(hsPoint3 &pt) const;
hsScalar IBestTime() const { return fLastDistSq < fThisDistSq
? (fLastDistSq < fNextDistSq
? fLastTime
: fNextTime)
: (fThisDistSq < fNextDistSq
? fThisTime
: fNextTime); }
// Visualization helper
void IMakeSegment(hsTArray<UInt16>& idx, hsTArray<hsPoint3>& pos,
hsPoint3& p1, hsPoint3& p2);
// For computing arclen
struct ArcLenDeltaInfo
{
hsScalar fT;
hsScalar fArcLenDelta; // arc len distance from prev sample point (array entry)
ArcLenDeltaInfo(hsScalar t, hsScalar del) : fT(t),fArcLenDelta(del) {}
ArcLenDeltaInfo() : fT(0),fArcLenDelta(0) {}
};
hsTArray<ArcLenDeltaInfo> fArcLenDeltas;
public:
plAnimPath();
virtual ~plAnimPath();
CLASSNAME_REGISTER( plAnimPath );
GETINTERFACE_ANY( plAnimPath, plCreatable );
void Reset();
void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l);
const hsMatrix44& GetLocalToWorld() const { return fLocalToWorld; }
const hsMatrix44& GetWorldToLocal() const { return fWorldToLocal; }
// Visualization helper
void MakeDrawList(hsTArray<UInt16>& idx, hsTArray<hsPoint3>& pos);
void SetAnimPathFlags(UInt32 f) { fAnimPathFlags=f; }
UInt32 GetAnimPathFlags() const { return fAnimPathFlags; }
void SetWrap(hsBool on) { if(on)fAnimPathFlags |= kWrap; else fAnimPathFlags &= ~kWrap; }
hsBool GetWrap() const { return 0 != (fAnimPathFlags & kWrap); }
void SetFarthest(hsBool on) { if(on)fAnimPathFlags |= kFarthest; else fAnimPathFlags &= ~kFarthest; }
hsBool GetFarthest() const { return 0 != (fAnimPathFlags & kFarthest); }
void SetCurTime(hsScalar t, UInt32 calcFlags=0);
hsScalar GetCurTime() const { return fTime; }
void SetController(plCompoundController* tmc);
plCompoundController* GetController() const { return fController; }
hsScalar GetLength() const { return fLength; } // seconds
void SetMinDistance(hsScalar d) { fMinDistSq = d*d; }
hsScalar GetMinDistance() const { return hsSquareRoot(fMinDistSq); }
hsMatrix44* GetMatrix44(hsMatrix44* xOut) const { *xOut = fXform; return xOut; }
hsPoint3* GetPosition(hsPoint3* pOut) const { *pOut = fPos; return pOut; }
hsVector3* GetVelocity(hsVector3* vOut) const { *vOut = fVel; return vOut; }
hsVector3* GetDirection(hsVector3* dOut) const { dOut->Set(fXform.fMap[0][2], fXform.fMap[1][2], fXform.fMap[2][2]); return dOut; }
hsVector3* GetUp(hsVector3* uOut) const { uOut->Set(fXform.fMap[0][1], fXform.fMap[1][1], fXform.fMap[2][1]); return uOut; }
hsVector3* GetAcceleration(hsVector3* aOut) const { *aOut = fAccel; return aOut; }
hsBool OutOfRange(hsPoint3 &pt, hsScalar range) const;
const hsAffineParts* Parts() const { return &fParts; }
void InitParts(const hsAffineParts& p) { fParts = p; }
hsScalar GetExtremePoint(hsPoint3 &worldPt) const; // Exhaustive search
hsScalar GetExtremePoint(hsScalar lastTime, hsScalar delTime, hsPoint3 &worldPt) const; // Incremental search
// for arclen usage
void ComputeArcLenDeltas(Int32 numSamples=256);
hsScalar GetLookAheadTime(hsScalar startTime, hsScalar arcLength, hsBool bwd, Int32* startSrchIdx);
virtual void Read(hsStream* s, hsResMgr* mgr);
virtual void Write(hsStream* s, hsResMgr* mgr);
};
#endif plAnimPath_inc

View File

@ -0,0 +1,306 @@
/*==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==*/
#ifndef plAnimTimeConvert_inc
#define plAnimTimeConvert_inc
#include "../pnFactory/plCreatable.h"
#include "hsTemplates.h"
#include "../pnNetCommon/plSynchedValue.h"
#pragma warning (disable: 4284)
class plSynchedObject;
class plAnimCmdMsg;
class plEventCallbackMsg;
class plATCEaseCurve;
class plATCState;
class plATCAnim;
class plAGMasterMod;
class plAnimTimeConvert : public plCreatable
{
friend class plAnimTimeConvertSDLModifier;
friend class plAGAnimInstance;
protected:
UInt16 fFlags;
hsScalar fBegin;
hsScalar fEnd;
hsScalar fLoopEnd;
hsScalar fLoopBegin;
hsScalar fSpeed;
hsScalar fCurrentAnimTime;
hsScalar fWrapTime;
double fLastEvalWorldTime;
// Do not change fLastEvalWorldTime anywhere except WorldToAnimTime()
plSynchedObject* fOwner;
double fLastStateChange;
typedef std::list<plATCState *> plATCStateList;
plATCStateList fStates;
hsTArray<hsScalar> fStopPoints;
hsTArray<plEventCallbackMsg*> fCallbackMsgs;
/////////////////////////
// Ease In/Out stuff
plATCEaseCurve *fEaseInCurve;
plATCEaseCurve *fEaseOutCurve;
plATCEaseCurve *fSpeedEaseCurve;
plATCEaseCurve *fCurrentEaseCurve; // One of the above, or nil
//
/////////////////////////
hsScalar fInitialBegin;
hsScalar fInitialEnd;
static hsScalar ICalcEaseTime(const plATCEaseCurve *curve, double start, double end);
void IClearSpeedEase();
void ICheckTimeCallbacks(hsScalar frameStart, hsScalar frameStop);
hsBool ITimeInFrame(hsScalar secs, hsScalar start, hsScalar stop);
void ISendCallback(int i);
plAnimTimeConvert& IStop(double time, hsScalar animTime);
hsBool IIsStoppedAt(const double &wSecs, const UInt32 &flags, const plATCEaseCurve *curve) const;
plAnimTimeConvert& IProcessStateChange(double worldTime, hsScalar animTime = -1);
void IFlushOldStates();
void IClearAllStates();
plATCState *IGetState(double wSecs) const;
plATCState *IGetLatestState() const;
plAnimTimeConvert& SetFlag(UInt8 f, hsBool on) { if(on)fFlags |= f; else fFlags &= ~f; return *this; }
public:
plAnimTimeConvert();
virtual ~plAnimTimeConvert();
void Init(plATCAnim *anim, plAGAnimInstance *instance, plAGMasterMod *master);
CLASSNAME_REGISTER( plAnimTimeConvert );
GETINTERFACE_ANY( plAnimTimeConvert, plCreatable );
void SetOwner(plSynchedObject* o);
const plSynchedObject* GetOwner() const { return fOwner; }
// ALL WorldToAnimTime functions are only valid if called with a time >= fLastEvalWorldTime.
hsBool IsStoppedAt(double wSecs) const;
hsScalar WorldToAnimTime(double wSecs);
hsScalar WorldToAnimTimeNoUpdate(double wSecs) const; // convert time but don't fire triggers or set state
protected:
static hsScalar IWorldToAnimTimeNoUpdate(double wSecs, plATCState *state);
hsScalar IWorldToAnimTimeBeforeState(double wSecs) const;
public:
void SetBegin(hsScalar s) { fBegin = s; }
void SetEnd(hsScalar s) { fEnd = s; }
void SetSpeed(hsScalar goal, hsScalar rate = 0);
void SetLoopPoints(hsScalar begin, hsScalar end) { SetLoopBegin(begin); SetLoopEnd(end); }
void SetLoopBegin(hsScalar s) { fLoopBegin = s; }
void SetLoopEnd(hsScalar s) { fLoopEnd = s; }
void SetEase(hsBool easeIn, UInt8 inType, hsScalar minLength, hsScalar maxLength, hsScalar inLength);
void SetCurrentEaseCurve(int x); // 0=nil, 1=easeIn, 2=easeOut, 3=speed
hsScalar GetBegin() const { return fBegin; }
hsScalar GetEnd() const { return fEnd; }
hsScalar GetLoopBegin() const { return fLoopBegin; }
hsScalar GetLoopEnd() const { return fLoopEnd; }
hsScalar GetSpeed() const { return fSpeed; }
hsTArray<hsScalar> &GetStopPoints() { return fStopPoints; }
hsScalar GetBestStopDist(hsScalar min, hsScalar max, hsScalar norm, hsScalar time) const;
int GetCurrentEaseCurve() const; // returns 0=nil, 1=easeIn, 2=easeOut, 3=speed
void ResizeStates(int cnt);
void ResetWrap();
plAnimTimeConvert& ClearFlags() { fFlags = kNone; return *this; }
hsBool GetFlag(UInt8 f) const { return (fFlags & f) ? true : false; }
plAnimTimeConvert& InitStop(); // Called when initializing an anim that doesn't autostart
plAnimTimeConvert& Stop(hsBool on);
plAnimTimeConvert& Stop(double s = -1.0);
plAnimTimeConvert& Start(double s = -1.0);
plAnimTimeConvert& PlayToTime(hsScalar time);
plAnimTimeConvert& PlayToPercentage(hsScalar percent); // zero to one.
plAnimTimeConvert& Loop(hsBool on);
plAnimTimeConvert& Loop() { return Loop(true); }
plAnimTimeConvert& NoLoop() { return Loop(false); }
plAnimTimeConvert& Backwards(hsBool on);
plAnimTimeConvert& Backwards();
plAnimTimeConvert& Forewards();
hsBool IsStopped() const { return 0 != (fFlags & kStopped); }
hsBool IsLooped() const { return 0 != (fFlags & kLoop); }
hsBool IsBackwards() const { return 0 != (fFlags & kBackwards); }
hsBool IsForewards() const { return !(fFlags & kBackwards); }
double LastEvalWorldTime() const { return fLastEvalWorldTime; }
hsScalar CurrentAnimTime() const { return fCurrentAnimTime; }
void SetCurrentAnimTime(hsScalar s, hsBool jump = false);
virtual void Read(hsStream* s, hsResMgr* mgr);
virtual void Write(hsStream* s, hsResMgr* mgr);
hsBool HandleCmd(plAnimCmdMsg* msg);
void AddCallback(plEventCallbackMsg* pMsg);
void RemoveCallback(plEventCallbackMsg* pMsg);
void ClearCallbacks();
void EnableCallbacks(hsBool val);
enum plAnimTimeFlags {
kNone = 0x0,
kStopped = 0x1,
kLoop = 0x2,
kBackwards = 0x4,
kWrap = 0x8,
kNeedsReset = 0x10,
kEasingIn = 0x20,
kForcedMove = 0x40,
kNoCallbacks = 0x80,
kFlagsMask = 0xff
};
enum plEaseCurveType {
kEaseNone,
kEaseIn,
kEaseOut,
kEaseSpeed,
};
};
// Rules for happy ease curves:
// 1. Any time value between 0 and fLength is kosher.
// 2. Velocity values accepted/returned are in the range [fStartSpeed, fSpeed]
// (some tolerance for values REALLY close to the limit, to account for floating-point inaccuracy)
class plATCEaseCurve : public plCreatable
{
protected:
hsScalar fStartSpeed;
hsScalar fMinLength;
hsScalar fMaxLength;
hsScalar fNormLength;
public:
CLASSNAME_REGISTER( plATCEaseCurve );
GETINTERFACE_ANY( plATCEaseCurve, plCreatable );
double fBeginWorldTime;
hsScalar fLength;
hsScalar fSpeed; // The anim's target ("full") speed.
static plATCEaseCurve *CreateEaseCurve(UInt8 type, hsScalar minLength, hsScalar maxLength, hsScalar normLength,
hsScalar startSpeed, hsScalar goalSpeed);
double GetEndWorldTime() const { return fBeginWorldTime + fLength; }
virtual plATCEaseCurve *Clone() const = 0;
virtual void Read(hsStream *s, hsResMgr *mgr);
virtual void Write(hsStream *s, hsResMgr *mgr);
virtual void RecalcToSpeed(hsScalar startSpeed, hsScalar goalSpeed, hsBool preserveRate = false);
virtual void SetLengthOnRate(hsScalar rate);
virtual void SetLengthOnDistance(hsScalar dist) = 0;
virtual hsScalar PositionGivenTime(hsScalar time) const = 0;
virtual hsScalar VelocityGivenTime(hsScalar time) const = 0;
virtual hsScalar TimeGivenVelocity(hsScalar velocity) const = 0;
virtual hsScalar GetMinDistance();
virtual hsScalar GetMaxDistance();
virtual hsScalar GetNormDistance();
};
class plConstAccelEaseCurve : public plATCEaseCurve
{
public:
plConstAccelEaseCurve();
plConstAccelEaseCurve(hsScalar minLength, hsScalar maxLength, hsScalar length,
hsScalar startSpeed, hsScalar goalSpeed);
CLASSNAME_REGISTER( plConstAccelEaseCurve );
GETINTERFACE_ANY( plConstAccelEaseCurve, plATCEaseCurve );
virtual plATCEaseCurve *Clone() const;
virtual void SetLengthOnDistance(hsScalar dist);
virtual hsScalar PositionGivenTime(hsScalar time) const;
virtual hsScalar VelocityGivenTime(hsScalar time) const;
virtual hsScalar TimeGivenVelocity(hsScalar velocity) const;
};
class plSplineEaseCurve : public plATCEaseCurve
{
public:
plSplineEaseCurve();
plSplineEaseCurve(hsScalar minLength, hsScalar maxLength, hsScalar length,
hsScalar startSpeed, hsScalar goalSpeed);
CLASSNAME_REGISTER( plSplineEaseCurve );
GETINTERFACE_ANY( plSplineEaseCurve, plATCEaseCurve );
virtual void Read(hsStream *s, hsResMgr *mgr);
virtual void Write(hsStream *s, hsResMgr *mgr);
virtual plATCEaseCurve *Clone() const;
virtual void RecalcToSpeed(hsScalar startSpeed, hsScalar goalSpeed, hsBool preserveRate = false);
virtual void SetLengthOnDistance(hsScalar dist);
virtual hsScalar PositionGivenTime(hsScalar time) const;
virtual hsScalar VelocityGivenTime(hsScalar time) const;
virtual hsScalar TimeGivenVelocity(hsScalar velocity) const;
hsScalar fCoef[4];
};
class plATCState
{
public:
plATCState() : fEaseCurve(nil) {}
~plATCState() { delete fEaseCurve; }
void Read(hsStream *s, hsResMgr *mgr);
void Write(hsStream *s, hsResMgr *mgr);
double fStartWorldTime;
hsScalar fStartAnimTime;
UInt8 fFlags;
hsScalar fBegin;
hsScalar fEnd;
hsScalar fLoopBegin;
hsScalar fLoopEnd;
hsScalar fSpeed;
hsScalar fWrapTime;
plATCEaseCurve *fEaseCurve;
};
#endif // plAnimTimeConvert_inc

View File

@ -0,0 +1,900 @@
/*==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 "plController.h"
#include "hsInterp.h"
#include "hsResMgr.h"
#include "../plTransform/hsEuler.h"
#include "plAnimTimeConvert.h"
/////////////////////////////////////////////
// Controller interp caching
/////////////////////////////////////////////
static const char *kInvalidInterpString = "Invalid call to plController::Interp()";
plControllerCacheInfo::plControllerCacheInfo() : fNumSubControllers(0), fSubControllers(nil), fKeyIndex(0), fAtc(nil) {}
plControllerCacheInfo::~plControllerCacheInfo()
{
int i;
for (i = 0; i < fNumSubControllers; i++)
delete fSubControllers[i];
delete [] fSubControllers;
}
void plControllerCacheInfo::SetATC(plAnimTimeConvert *atc)
{
fAtc = atc;
int i;
for (i = 0; i < fNumSubControllers; i++)
if (fSubControllers[i])
fSubControllers[i]->SetATC(atc);
}
//////////////////////////////////////////////////////////////////////////////////////////
plLeafController::~plLeafController()
{
delete [] fKeys;
}
void plLeafController::Interp(hsScalar time, hsScalar* result, plControllerCacheInfo *cache) const
{
hsAssert(fType == hsKeyFrame::kScalarKeyFrame || fType == hsKeyFrame::kBezScalarKeyFrame, kInvalidInterpString);
hsBool tryForward = (cache? cache->fAtc->IsForewards() : true);
if (fType == hsKeyFrame::kScalarKeyFrame)
{
hsScalarKey *k1, *k2;
hsScalar t;
UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx);
hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsScalarKey), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward);
hsInterp::LinInterp(k1->fValue, k2->fValue, t, result);
}
else
{
hsBezScalarKey *k1, *k2;
hsScalar t;
UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx);
hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsBezScalarKey), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward);
hsInterp::BezInterp(k1, k2, t, result);
}
}
void plLeafController::Interp(hsScalar time, hsScalarTriple* result, plControllerCacheInfo *cache) const
{
hsAssert(fType == hsKeyFrame::kPoint3KeyFrame || fType == hsKeyFrame::kBezPoint3KeyFrame, kInvalidInterpString);
hsBool tryForward = (cache? cache->fAtc->IsForewards() : true);
if (fType == hsKeyFrame::kPoint3KeyFrame)
{
hsPoint3Key *k1, *k2;
hsScalar t;
UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx);
hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsPoint3Key), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward);
hsInterp::LinInterp(&k1->fValue, &k2->fValue, t, result);
}
else
{
hsBezPoint3Key *k1, *k2;
hsScalar t;
UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx);
hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsBezPoint3Key), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward);
hsInterp::BezInterp(k1, k2, t, result);
}
}
void plLeafController::Interp(hsScalar time, hsScaleValue* result, plControllerCacheInfo *cache) const
{
hsAssert(fType == hsKeyFrame::kScaleKeyFrame || fType == hsKeyFrame::kBezScaleKeyFrame, kInvalidInterpString);
hsBool tryForward = (cache? cache->fAtc->IsForewards() : true);
if (fType == hsKeyFrame::kScaleKeyFrame)
{
hsScaleKey *k1, *k2;
hsScalar t;
UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx);
hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsScaleKey), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward);
hsInterp::LinInterp(&k1->fValue, &k2->fValue, t, result);
}
else
{
hsBezScaleKey *k1, *k2;
hsScalar t;
UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx);
hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsBezScaleKey), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward);
hsInterp::BezInterp(k1, k2, t, result);
}
}
void plLeafController::Interp(hsScalar time, hsQuat* result, plControllerCacheInfo *cache) const
{
hsAssert(fType == hsKeyFrame::kQuatKeyFrame ||
fType == hsKeyFrame::kCompressedQuatKeyFrame32 ||
fType == hsKeyFrame::kCompressedQuatKeyFrame64, kInvalidInterpString);
hsBool tryForward = (cache? cache->fAtc->IsForewards() : true);
if (fType == hsKeyFrame::kQuatKeyFrame)
{
hsQuatKey *k1, *k2;
hsScalar t;
UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx);
hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsQuatKey), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward);
hsInterp::LinInterp(&k1->fValue, &k2->fValue, t, result);
}
else if (fType == hsKeyFrame::kCompressedQuatKeyFrame32)
{
hsCompressedQuatKey32 *k1, *k2;
hsScalar t;
UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx);
hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsCompressedQuatKey32), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward);
hsQuat q1, q2;
k1->GetQuat(q1);
k2->GetQuat(q2);
hsInterp::LinInterp(&q1, &q2, t, result);
}
else // (fType == hsKeyFrame::kCompressedQuatKeyFrame64)
{
hsCompressedQuatKey64 *k1, *k2;
hsScalar t;
UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx);
hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsCompressedQuatKey64), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward);
hsQuat q1, q2;
k1->GetQuat(q1);
k2->GetQuat(q2);
hsInterp::LinInterp(&q1, &q2, t, result);
}
}
void plLeafController::Interp(hsScalar time, hsMatrix33* result, plControllerCacheInfo *cache) const
{
hsAssert(fType == hsKeyFrame::kMatrix33KeyFrame, kInvalidInterpString);
hsBool tryForward = (cache? cache->fAtc->IsForewards() : true);
hsMatrix33Key *k1, *k2;
hsScalar t;
UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx);
hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsMatrix33Key), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward);
hsInterp::LinInterp(&k1->fValue, &k2->fValue, t, result);
}
void plLeafController::Interp(hsScalar time, hsMatrix44* result, plControllerCacheInfo *cache) const
{
hsAssert(fType == hsKeyFrame::kMatrix44KeyFrame, kInvalidInterpString);
hsBool tryForward = (cache? cache->fAtc->IsForewards() : true);
hsMatrix44Key *k1, *k2;
hsScalar t;
UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx);
hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsMatrix44Key), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward);
hsInterp::LinInterp(&k1->fValue, &k2->fValue, t, result);
}
void plLeafController::Interp(hsScalar time, hsColorRGBA* result, plControllerCacheInfo *cache) const
{
hsPoint3 value;
Interp(time, &value, cache);
result->r = value.fX;
result->g = value.fY;
result->b = value.fZ;
}
plControllerCacheInfo *plLeafController::CreateCache() const
{
plControllerCacheInfo *cache = TRACKED_NEW plControllerCacheInfo;
cache->fNumSubControllers = 0;
return cache;
}
hsScalar plLeafController::GetLength() const
{
UInt32 stride = GetStride();
if (stride == 0 || fNumKeys == 0)
return 0;
UInt8 *ptr = (UInt8 *)fKeys;
return ((hsKeyFrame *)(ptr + (fNumKeys - 1) * stride))->fFrame / MAX_FRAMES_PER_SEC;
}
UInt32 plLeafController::GetStride() const
{
switch (fType)
{
case hsKeyFrame::kPoint3KeyFrame:
return sizeof(hsPoint3Key);
case hsKeyFrame::kBezPoint3KeyFrame:
return sizeof(hsBezPoint3Key);
case hsKeyFrame::kScalarKeyFrame:
return sizeof(hsScalarKey);
case hsKeyFrame::kBezScalarKeyFrame:
return sizeof(hsBezScalarKey);
case hsKeyFrame::kScaleKeyFrame:
return sizeof(hsScaleKey);
case hsKeyFrame::kBezScaleKeyFrame:
return sizeof(hsBezScaleKey);
case hsKeyFrame::kQuatKeyFrame:
return sizeof(hsQuatKey);
case hsKeyFrame::kCompressedQuatKeyFrame32:
return sizeof(hsCompressedQuatKey32);
case hsKeyFrame::kCompressedQuatKeyFrame64:
return sizeof(hsCompressedQuatKey64);
case hsKeyFrame::k3dsMaxKeyFrame:
return sizeof(hsG3DSMaxKeyFrame);
case hsKeyFrame::kMatrix33KeyFrame:
return sizeof(hsMatrix33Key);
case hsKeyFrame::kMatrix44KeyFrame:
return sizeof(hsMatrix44Key);
case hsKeyFrame::kUnknownKeyFrame:
default:
return 0;
}
}
hsPoint3Key *plLeafController::GetPoint3Key(UInt32 i) const
{
if (fType != hsKeyFrame::kPoint3KeyFrame)
return nil;
return (hsPoint3Key *)((UInt8 *)fKeys + i * sizeof(hsPoint3Key));
}
hsBezPoint3Key *plLeafController::GetBezPoint3Key(UInt32 i) const
{
if (fType != hsKeyFrame::kBezPoint3KeyFrame)
return nil;
return (hsBezPoint3Key *)((UInt8 *)fKeys + i * sizeof(hsBezPoint3Key));
}
hsScalarKey *plLeafController::GetScalarKey(UInt32 i) const
{
if (fType != hsKeyFrame::kScalarKeyFrame)
return nil;
return (hsScalarKey *)((UInt8 *)fKeys + i * sizeof(hsScalarKey));
}
hsBezScalarKey *plLeafController::GetBezScalarKey(UInt32 i) const
{
if (fType != hsKeyFrame::kBezScalarKeyFrame)
return nil;
return (hsBezScalarKey *)((UInt8 *)fKeys + i * sizeof(hsBezScalarKey));
}
hsScaleKey *plLeafController::GetScaleKey(UInt32 i) const
{
if (fType != hsKeyFrame::kScaleKeyFrame)
return nil;
return (hsScaleKey *)((UInt8 *)fKeys + i * sizeof(hsScaleKey));
}
hsBezScaleKey *plLeafController::GetBezScaleKey(UInt32 i) const
{
if (fType != hsKeyFrame::kBezScaleKeyFrame)
return nil;
return (hsBezScaleKey *)((UInt8 *)fKeys + i * sizeof(hsBezScaleKey));
}
hsQuatKey *plLeafController::GetQuatKey(UInt32 i) const
{
if (fType != hsKeyFrame::kQuatKeyFrame)
return nil;
return (hsQuatKey *)((UInt8 *)fKeys + i * sizeof(hsQuatKey));
}
hsCompressedQuatKey32 *plLeafController::GetCompressedQuatKey32(UInt32 i) const
{
if (fType != hsKeyFrame::kCompressedQuatKeyFrame32)
return nil;
return (hsCompressedQuatKey32 *)((UInt8 *)fKeys + i * sizeof(hsCompressedQuatKey32));
}
hsCompressedQuatKey64 *plLeafController::GetCompressedQuatKey64(UInt32 i) const
{
if (fType != hsKeyFrame::kCompressedQuatKeyFrame64)
return nil;
return (hsCompressedQuatKey64 *)((UInt8 *)fKeys + i * sizeof(hsCompressedQuatKey64));
}
hsG3DSMaxKeyFrame *plLeafController::Get3DSMaxKey(UInt32 i) const
{
if (fType != hsKeyFrame::k3dsMaxKeyFrame)
return nil;
return (hsG3DSMaxKeyFrame *)((UInt8 *)fKeys + i * sizeof(hsG3DSMaxKeyFrame));
}
hsMatrix33Key *plLeafController::GetMatrix33Key(UInt32 i) const
{
if (fType != hsKeyFrame::kMatrix33KeyFrame)
return nil;
return (hsMatrix33Key *)((UInt8 *)fKeys + i * sizeof(hsMatrix33Key));
}
hsMatrix44Key *plLeafController::GetMatrix44Key(UInt32 i) const
{
if (fType != hsKeyFrame::kMatrix44KeyFrame)
return nil;
return (hsMatrix44Key *)((UInt8 *)fKeys + i * sizeof(hsMatrix44Key));
}
void plLeafController::GetKeyTimes(hsTArray<hsScalar> &keyTimes) const
{
int cIdx;
int kIdx;
UInt32 stride = GetStride();
UInt8 *keyPtr = (UInt8 *)fKeys;
for (cIdx = 0, kIdx = 0; cIdx < fNumKeys, kIdx < keyTimes.GetCount();)
{
hsScalar kTime = keyTimes[kIdx];
hsScalar cTime = ((hsKeyFrame*)(keyPtr + cIdx * stride))->fFrame / MAX_FRAMES_PER_SEC;
if (cTime < kTime)
{
keyTimes.InsertAtIndex(kIdx, cTime);
cIdx++;
kIdx++;
}
else if (cTime > kTime)
{
kIdx++;
}
else
{
kIdx++;
cIdx++;
}
}
// All remaining times in the controller are later than the original keyTimes set
for (; cIdx < fNumKeys; cIdx++)
{
hsScalar cTime = ((hsKeyFrame*)(keyPtr + cIdx * stride))->fFrame / MAX_FRAMES_PER_SEC;
keyTimes.Append(cTime);
}
}
void plLeafController::AllocKeys(UInt32 numKeys, UInt8 type)
{
delete fKeys;
fNumKeys = numKeys;
fType = type;
switch (fType)
{
case hsKeyFrame::kPoint3KeyFrame:
fKeys = TRACKED_NEW hsPoint3Key[fNumKeys];
break;
case hsKeyFrame::kBezPoint3KeyFrame:
fKeys = TRACKED_NEW hsBezPoint3Key[fNumKeys];
break;
case hsKeyFrame::kScalarKeyFrame:
fKeys = TRACKED_NEW hsScalarKey[fNumKeys];
break;
case hsKeyFrame::kBezScalarKeyFrame:
fKeys = TRACKED_NEW hsBezScalarKey[fNumKeys];
break;
case hsKeyFrame::kScaleKeyFrame:
fKeys = TRACKED_NEW hsScaleKey[fNumKeys];
break;
case hsKeyFrame::kBezScaleKeyFrame:
fKeys = TRACKED_NEW hsBezScaleKey[fNumKeys];
break;
case hsKeyFrame::kQuatKeyFrame:
fKeys = TRACKED_NEW hsQuatKey[fNumKeys];
break;
case hsKeyFrame::kCompressedQuatKeyFrame32:
fKeys = TRACKED_NEW hsCompressedQuatKey32[fNumKeys];
break;
case hsKeyFrame::kCompressedQuatKeyFrame64:
fKeys = TRACKED_NEW hsCompressedQuatKey64[fNumKeys];
break;
case hsKeyFrame::k3dsMaxKeyFrame:
fKeys = TRACKED_NEW hsG3DSMaxKeyFrame[fNumKeys];
break;
case hsKeyFrame::kMatrix33KeyFrame:
fKeys = TRACKED_NEW hsMatrix33Key[fNumKeys];
break;
case hsKeyFrame::kMatrix44KeyFrame:
fKeys = TRACKED_NEW hsMatrix44Key[fNumKeys];
break;
case hsKeyFrame::kUnknownKeyFrame:
default:
hsAssert(false, "Trying to allocate unknown keyframe type");
break;
}
}
void plLeafController::QuickScalarController(int numKeys, hsScalar* times, hsScalar* values, UInt32 valueStrides)
{
AllocKeys(numKeys, hsKeyFrame::kScalarKeyFrame);
int i;
for( i = 0; i < numKeys; i++ )
{
((hsScalarKey*)fKeys)[i].fFrame = (UInt16)(*times++ * MAX_FRAMES_PER_SEC);
((hsScalarKey*)fKeys)[i].fValue = *values;
values = (hsScalar *)((UInt8 *)values + valueStrides);
}
}
// If all the keys are the same, this controller is pretty useless.
// This situation actually comes up a lot because of the biped killer
// trying to convert character studio animations.
hsBool plLeafController::AllKeysMatch() const
{
if (fNumKeys <= 1)
return true;
int idx;
for (idx = 1; idx < fNumKeys; idx++)
{
switch (fType)
{
case hsKeyFrame::kPoint3KeyFrame:
{
hsPoint3Key *k1 = GetPoint3Key(idx - 1);
hsPoint3Key *k2 = GetPoint3Key(idx);
if (!k1->CompareValue(k2))
return false;
break;
}
case hsKeyFrame::kBezPoint3KeyFrame:
{
hsBezPoint3Key *k1 = GetBezPoint3Key(idx - 1);
hsBezPoint3Key *k2 = GetBezPoint3Key(idx);
if (!k1->CompareValue(k2))
return false;
break;
}
case hsKeyFrame::kScalarKeyFrame:
{
hsScalarKey *k1 = GetScalarKey(idx - 1);
hsScalarKey *k2 = GetScalarKey(idx);
if (!k1->CompareValue(k2))
return false;
break;
}
case hsKeyFrame::kBezScalarKeyFrame:
{
hsBezScalarKey *k1 = GetBezScalarKey(idx - 1);
hsBezScalarKey *k2 = GetBezScalarKey(idx);
if (!k1->CompareValue(k2))
return false;
break;
}
case hsKeyFrame::kScaleKeyFrame:
{
hsScaleKey *k1 = GetScaleKey(idx - 1);
hsScaleKey *k2 = GetScaleKey(idx);
if (!k1->CompareValue(k2))
return false;
break;
}
case hsKeyFrame::kBezScaleKeyFrame:
{
hsBezScaleKey *k1 = GetBezScaleKey(idx - 1);
hsBezScaleKey *k2 = GetBezScaleKey(idx);
if (!k1->CompareValue(k2))
return false;
break;
}
case hsKeyFrame::kQuatKeyFrame:
{
hsQuatKey *k1 = GetQuatKey(idx - 1);
hsQuatKey *k2 = GetQuatKey(idx);
if (!k1->CompareValue(k2))
return false;
break;
}
case hsKeyFrame::kCompressedQuatKeyFrame32:
{
hsCompressedQuatKey32 *k1 = GetCompressedQuatKey32(idx - 1);
hsCompressedQuatKey32 *k2 = GetCompressedQuatKey32(idx);
if (!k1->CompareValue(k2))
return false;
break;
}
case hsKeyFrame::kCompressedQuatKeyFrame64:
{
hsCompressedQuatKey64 *k1 = GetCompressedQuatKey64(idx - 1);
hsCompressedQuatKey64 *k2 = GetCompressedQuatKey64(idx);
if (!k1->CompareValue(k2))
return false;
break;
}
case hsKeyFrame::k3dsMaxKeyFrame:
{
hsG3DSMaxKeyFrame *k1 = Get3DSMaxKey(idx - 1);
hsG3DSMaxKeyFrame *k2 = Get3DSMaxKey(idx);
if (!k1->CompareValue(k2))
return false;
break;
}
case hsKeyFrame::kMatrix33KeyFrame:
{
hsMatrix33Key *k1 = GetMatrix33Key(idx - 1);
hsMatrix33Key *k2 = GetMatrix33Key(idx);
if (!k1->CompareValue(k2))
return false;
break;
}
case hsKeyFrame::kMatrix44KeyFrame:
{
hsMatrix44Key *k1 = GetMatrix44Key(idx - 1);
hsMatrix44Key *k2 = GetMatrix44Key(idx);
if (!k1->CompareValue(k2))
return false;
break;
}
case hsKeyFrame::kUnknownKeyFrame:
default:
hsAssert(false, "Trying to compare unknown keyframe type");
return false;
}
}
return true;
}
hsBool plLeafController::PurgeRedundantSubcontrollers()
{
return AllKeysMatch();
}
void plLeafController::Read(hsStream* s, hsResMgr *mgr)
{
UInt8 type = s->ReadByte();
UInt32 numKeys = s->ReadSwap32();
AllocKeys(numKeys, type);
int i;
switch (fType)
{
case hsKeyFrame::kPoint3KeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsPoint3Key *)fKeys)[i].Read(s);
break;
case hsKeyFrame::kBezPoint3KeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsBezPoint3Key *)fKeys)[i].Read(s);
break;
case hsKeyFrame::kScalarKeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsScalarKey *)fKeys)[i].Read(s);
break;
case hsKeyFrame::kBezScalarKeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsBezScalarKey *)fKeys)[i].Read(s);
break;
case hsKeyFrame::kScaleKeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsScaleKey *)fKeys)[i].Read(s);
break;
case hsKeyFrame::kBezScaleKeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsBezScaleKey *)fKeys)[i].Read(s);
break;
case hsKeyFrame::kQuatKeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsQuatKey *)fKeys)[i].Read(s);
break;
case hsKeyFrame::kCompressedQuatKeyFrame32:
for (i = 0; i < fNumKeys; i++)
((hsCompressedQuatKey32 *)fKeys)[i].Read(s);
break;
case hsKeyFrame::kCompressedQuatKeyFrame64:
for (i = 0; i < fNumKeys; i++)
((hsCompressedQuatKey64 *)fKeys)[i].Read(s);
break;
case hsKeyFrame::k3dsMaxKeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsG3DSMaxKeyFrame *)fKeys)[i].Read(s);
break;
case hsKeyFrame::kMatrix33KeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsMatrix33Key *)fKeys)[i].Read(s);
break;
case hsKeyFrame::kMatrix44KeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsMatrix44Key *)fKeys)[i].Read(s);
break;
case hsKeyFrame::kUnknownKeyFrame:
default:
hsAssert(false, "Reading in controller with unknown key data");
break;
}
}
void plLeafController::Write(hsStream* s, hsResMgr *mgr)
{
s->WriteByte(fType);
s->WriteSwap32(fNumKeys);
int i;
switch (fType)
{
case hsKeyFrame::kPoint3KeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsPoint3Key *)fKeys)[i].Write(s);
break;
case hsKeyFrame::kBezPoint3KeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsBezPoint3Key *)fKeys)[i].Write(s);
break;
case hsKeyFrame::kScalarKeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsScalarKey *)fKeys)[i].Write(s);
break;
case hsKeyFrame::kBezScalarKeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsBezScalarKey *)fKeys)[i].Write(s);
break;
case hsKeyFrame::kScaleKeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsScaleKey *)fKeys)[i].Write(s);
break;
case hsKeyFrame::kBezScaleKeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsBezScaleKey *)fKeys)[i].Write(s);
break;
case hsKeyFrame::kQuatKeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsQuatKey *)fKeys)[i].Write(s);
break;
case hsKeyFrame::kCompressedQuatKeyFrame32:
for (i = 0; i < fNumKeys; i++)
((hsCompressedQuatKey32 *)fKeys)[i].Write(s);
break;
case hsKeyFrame::kCompressedQuatKeyFrame64:
for (i = 0; i < fNumKeys; i++)
((hsCompressedQuatKey64 *)fKeys)[i].Write(s);
break;
case hsKeyFrame::k3dsMaxKeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsG3DSMaxKeyFrame *)fKeys)[i].Write(s);
break;
case hsKeyFrame::kMatrix33KeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsMatrix33Key *)fKeys)[i].Write(s);
break;
case hsKeyFrame::kMatrix44KeyFrame:
for (i = 0; i < fNumKeys; i++)
((hsMatrix44Key *)fKeys)[i].Write(s);
break;
case hsKeyFrame::kUnknownKeyFrame:
default:
hsAssert(false, "Writing controller with unknown key data");
break;
}
}
/////////////////////////////////////////////////////////////////////////////////
plCompoundController::plCompoundController() : fXController(nil), fYController(nil), fZController(nil) {}
plCompoundController::~plCompoundController()
{
delete fXController;
delete fYController;
delete fZController;
}
void plCompoundController::Interp(hsScalar time, hsScalarTriple* result, plControllerCacheInfo *cache) const
{
if (fXController)
fXController->Interp(time, &result->fX, (cache ? cache->fSubControllers[0] : nil));
if (fYController)
fYController->Interp(time, &result->fY, (cache ? cache->fSubControllers[1] : nil));
if (fZController)
fZController->Interp(time, &result->fZ, (cache ? cache->fSubControllers[2] : nil));
}
void plCompoundController::Interp(hsScalar time, hsQuat* result, plControllerCacheInfo *cache) const
{
hsEuler eul(0,0,0,EulOrdXYZs);
fXController->Interp(time, &eul.fX, (cache ? cache->fSubControllers[0] : nil));
fYController->Interp(time, &eul.fY, (cache ? cache->fSubControllers[1] : nil));
fZController->Interp(time, &eul.fZ, (cache ? cache->fSubControllers[2] : nil));
eul.GetQuat(result);
}
void plCompoundController::Interp(hsScalar time, hsAffineParts* parts, plControllerCacheInfo *cache) const
{
if (fXController)
fXController->Interp(time, &parts->fT, (cache ? cache->fSubControllers[0] : nil));
if (fYController)
fYController->Interp(time, &parts->fQ, (cache ? cache->fSubControllers[1] : nil));
hsScaleValue sv;
if (fZController)
{
fZController->Interp(time, &sv, (cache ? cache->fSubControllers[2] : nil));
parts->fU = sv.fQ;
parts->fK = sv.fS;
}
}
void plCompoundController::Interp(hsScalar time, hsColorRGBA* result, plControllerCacheInfo *cache) const
{
fXController->Interp(time, &result->r, (cache ? cache->fSubControllers[0] : nil));
fYController->Interp(time, &result->g, (cache ? cache->fSubControllers[1] : nil));
fZController->Interp(time, &result->b, (cache ? cache->fSubControllers[2] : nil));
}
hsScalar plCompoundController::GetLength() const
{
hsScalar len=0;
int i;
for(i=0; i<3; i++)
{
if (GetController(i))
len = hsMaximum(len, GetController(i)->GetLength());
}
return len;
}
void plCompoundController::GetKeyTimes(hsTArray<hsScalar> &keyTimes) const
{
if (fXController)
fXController->GetKeyTimes(keyTimes);
if (fYController)
fYController->GetKeyTimes(keyTimes);
if (fZController)
fZController->GetKeyTimes(keyTimes);
}
hsBool plCompoundController::AllKeysMatch() const
{
return (!fXController || fXController->AllKeysMatch()) &&
(!fYController || fYController->AllKeysMatch()) &&
(!fZController || fZController->AllKeysMatch());
}
// Careful here... We might detect that one of our subcontrollers
// has animation keys that all have the same value. That doesn't
// mean they're all zero though. An avatar animation might have
// elbow bend a constant 90 degrees through the entire anim, but
// if we delete the controller and assume zero, we'll have problems.
// Transform controller channels get around this by sampling the source
// first and using that to fill in the missing subcontrollers.
//
// Note: that one of our subcontrollers could itself be a compound
// controller. An example would be a controller for XYZ Euler angles
// that's a sub of the pos/rot/scale transform controller.
// It's possible that some of these sub-sub controllers could be
// removed, but then we'd have to store the default values somewhere.
// At the moment, this doesn't seem likely to save us enough space
// to be worth the effort. (This is why this function doesn't
// recursively call purge on its subcontrollers.)
hsBool plCompoundController::PurgeRedundantSubcontrollers()
{
if (fXController && fXController->AllKeysMatch())
{
delete fXController;
fXController = nil;
}
if (fYController && fYController->AllKeysMatch())
{
delete fYController;
fYController = nil;
}
if (fZController && fZController->AllKeysMatch())
{
delete fZController;
fZController = nil;
}
return (!fXController && !fYController && !fZController);
}
plControllerCacheInfo* plCompoundController::CreateCache() const
{
plControllerCacheInfo* cache = TRACKED_NEW plControllerCacheInfo;
cache->fNumSubControllers = 3;
cache->fSubControllers = TRACKED_NEW plControllerCacheInfo*[cache->fNumSubControllers];
int i;
for (i = 0; i < cache->fNumSubControllers; i++)
cache->fSubControllers[i] = (GetController(i) ? GetController(i)->CreateCache() : nil);
return cache;
}
plController* plCompoundController::GetController(Int32 i) const
{
return (i==0 ? fXController : (i==1 ? fYController : fZController));
}
void plCompoundController::SetController(Int32 i, plController* c)
{
delete GetController(i);
(i==0 ? fXController : (i==1 ? fYController : fZController)) = c;
}
void plCompoundController::Read(hsStream* stream, hsResMgr *mgr)
{
fXController = plController::ConvertNoRef(mgr->ReadCreatable(stream));
fYController = plController::ConvertNoRef(mgr->ReadCreatable(stream));
fZController = plController::ConvertNoRef(mgr->ReadCreatable(stream));
}
void plCompoundController::Write(hsStream* stream, hsResMgr *mgr)
{
mgr->WriteCreatable(stream, fXController);
mgr->WriteCreatable(stream, fYController);
mgr->WriteCreatable(stream, fZController);
}

View File

@ -0,0 +1,214 @@
/*==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==*/
#ifndef HSCONTROLLER_inc
#define HSCONTROLLER_inc
#include "HeadSpin.h"
#include "../pnFactory/plCreatable.h"
#include "hsColorRGBA.h"
#include "hsKeys.h"
#include "hsTemplates.h"
class hsResMgr;
struct hsScaleValue;
struct hsScalarKey;
struct hsPoint3Key;
struct hsScalarTriple;
struct hsMatrix33;
struct hsMatrix44;
class hsQuat;
class hsAffineParts;
class plScalarCurve;
class plAnimTimeConvert;
class plCompoundController;
//
//////////////////////////////////////////////////////////////
// base controller class.
// Controllers correspond to Max controllers.
// Some are leaf controllers which actually have keys, these can also have
// multiple ease and multiplier controllers.
// Some are compound controllers, which just contain other (leaf) controllers.
// Leaf controllers have lists of keys (or plCurves which are just wrappers for
// the lists of keys).
//
class plControllerCacheInfo
{
public:
UInt8 fNumSubControllers;
plControllerCacheInfo **fSubControllers;
UInt32 fKeyIndex;
plAnimTimeConvert *fAtc;
plControllerCacheInfo();
~plControllerCacheInfo();
void SetATC(plAnimTimeConvert *atc);
};
//
//////////////////////////////////////////////////////////////
// defines base methods
//
class plController : public plCreatable
{
public:
CLASSNAME_REGISTER( plController );
GETINTERFACE_ANY( plController, plCreatable );
virtual void Interp(hsScalar time, hsScalar* result, plControllerCacheInfo *cache = nil) const {}
virtual void Interp(hsScalar time, hsScalarTriple* result, plControllerCacheInfo *cache = nil) const {}
virtual void Interp(hsScalar time, hsScaleValue* result, plControllerCacheInfo *cache = nil) const {}
virtual void Interp(hsScalar time, hsQuat* result, plControllerCacheInfo *cache = nil) const {}
virtual void Interp(hsScalar time, hsMatrix33* result, plControllerCacheInfo *cache = nil) const {}
virtual void Interp(hsScalar time, hsMatrix44* result, plControllerCacheInfo *cache = nil) const {}
virtual void Interp(hsScalar time, hsColorRGBA* result, plControllerCacheInfo *cache = nil) const {}
virtual void Interp(hsScalar time, hsAffineParts* parts, plControllerCacheInfo *cache = nil) const {}
virtual plControllerCacheInfo* CreateCache() const { return nil; } // Caller must handle deleting the pointer.
virtual hsScalar GetLength() const = 0;
virtual void GetKeyTimes(hsTArray<hsScalar> &keyTimes) const = 0;
virtual hsBool AllKeysMatch() const = 0;
// Checks each of our subcontrollers (if we have any) and deletes any that
// are nothing but matching keys. Returns true if this controller itself
// is redundant.
virtual hsBool PurgeRedundantSubcontrollers() = 0;
};
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
class plLeafController : public plController
{
friend class plCompoundController;
protected:
UInt8 fType;
void *fKeys; // Need to pay attend to fType to determine what these actually are
UInt32 fNumKeys;
mutable UInt32 fLastKeyIdx;
public:
plLeafController() : fType(hsKeyFrame::kUnknownKeyFrame), fKeys(nil), fNumKeys(0), fLastKeyIdx(0) {}
virtual ~plLeafController();
CLASSNAME_REGISTER( plLeafController );
GETINTERFACE_ANY( plLeafController, plController );
void Interp(hsScalar time, hsScalar* result, plControllerCacheInfo *cache = nil) const;
void Interp(hsScalar time, hsScalarTriple* result, plControllerCacheInfo *cache = nil) const;
void Interp(hsScalar time, hsScaleValue* result, plControllerCacheInfo *cache = nil) const;
void Interp(hsScalar time, hsQuat* result, plControllerCacheInfo *cache = nil) const;
void Interp(hsScalar time, hsMatrix33* result, plControllerCacheInfo *cache = nil) const;
void Interp(hsScalar time, hsMatrix44* result, plControllerCacheInfo *cache = nil) const;
void Interp(hsScalar time, hsColorRGBA* result, plControllerCacheInfo *cache = nil) const;
virtual plControllerCacheInfo* CreateCache() const;
hsScalar GetLength() const;
UInt32 GetStride() const;
hsPoint3Key *GetPoint3Key(UInt32 i) const;
hsBezPoint3Key *GetBezPoint3Key(UInt32 i) const;
hsScalarKey *GetScalarKey(UInt32 i) const;
hsBezScalarKey *GetBezScalarKey(UInt32 i) const;
hsScaleKey *GetScaleKey(UInt32 i) const;
hsBezScaleKey *GetBezScaleKey(UInt32 i) const;
hsQuatKey *GetQuatKey(UInt32 i) const;
hsCompressedQuatKey32 *GetCompressedQuatKey32(UInt32 i) const;
hsCompressedQuatKey64 *GetCompressedQuatKey64(UInt32 i) const;
hsG3DSMaxKeyFrame *Get3DSMaxKey(UInt32 i) const;
hsMatrix33Key *GetMatrix33Key(UInt32 i) const;
hsMatrix44Key *GetMatrix44Key(UInt32 i) const;
UInt8 GetType() const { return fType; }
UInt32 GetNumKeys() const { return fNumKeys; }
void *GetKeyBuffer() const { return fKeys; }
void GetKeyTimes(hsTArray<hsScalar> &keyTimes) const;
void AllocKeys(UInt32 n, UInt8 type);
void QuickScalarController(int numKeys, hsScalar* times, hsScalar* values, UInt32 valueStrides);
hsBool AllKeysMatch() const;
hsBool PurgeRedundantSubcontrollers();
void Read(hsStream* s, hsResMgr* mgr);
void Write(hsStream* s, hsResMgr* mgr);
};
////////////////////////////////////////////////////////////////////////////////
// NON-LEAF (container) CONTROLLERS
////////////////////////////////////////////////////////////////////////////////
//
class plCompoundController : public plController
{
private:
plController* fXController;
plController* fYController;
plController* fZController;
public:
plCompoundController(); // allocs leaf controllers
~plCompoundController();
CLASSNAME_REGISTER( plCompoundController );
GETINTERFACE_ANY( plCompoundController, plController );
void Interp(hsScalar time, hsQuat* result, plControllerCacheInfo *cache = nil) const;
void Interp(hsScalar time, hsScalarTriple* result, plControllerCacheInfo *cache = nil) const;
void Interp(hsScalar time, hsAffineParts* parts, plControllerCacheInfo *cache = nil) const;
void Interp(hsScalar time, hsColorRGBA* result, plControllerCacheInfo *cache = nil) const;
plControllerCacheInfo* CreateCache() const;
plController *GetXController() const { return fXController; }
plController *GetYController() const { return fYController; }
plController *GetZController() const { return fZController; }
plController *GetPosController() const { return fXController; }
plController *GetRotController() const { return fYController; }
plController *GetScaleController() const { return fZController; }
plController *GetController(Int32 i) const;
hsScalar GetLength() const;
void GetKeyTimes(hsTArray<hsScalar> &keyTimes) const;
hsBool AllKeysMatch() const;
hsBool PurgeRedundantSubcontrollers();
void SetXController(plController *c) { delete fXController; fXController = c; }
void SetYController(plController *c) { delete fYController; fYController = c; }
void SetZController(plController *c) { delete fZController; fZController = c; }
void SetPosController(plController *c) { delete fXController; fXController = c; }
void SetRotController(plController *c) { delete fYController; fYController = c; }
void SetScaleController(plController *c) { delete fZController; fZController = c; }
void SetController(Int32 i, plController* c);
void Read(hsStream* s, hsResMgr* mgr);
void Write(hsStream* s, hsResMgr* mgr);
};
#endif

View File

@ -0,0 +1,53 @@
/*==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==*/
#ifndef plInterpCreatable_inc
#define plInterpCreatable_inc
#include "../pnFactory/plCreator.h"
#include "plController.h"
REGISTER_NONCREATABLE( plController );
REGISTER_CREATABLE( plLeafController );
REGISTER_CREATABLE( plCompoundController );
#include "plAnimTimeConvert.h"
REGISTER_CREATABLE( plAnimTimeConvert );
REGISTER_NONCREATABLE( plATCEaseCurve );
REGISTER_CREATABLE( plConstAccelEaseCurve );
REGISTER_CREATABLE( plSplineEaseCurve );
#include "plAnimPath.h"
REGISTER_CREATABLE( plAnimPath );
#include "plModulator.h"
REGISTER_CREATABLE( plModulator );
#endif // plInterpCreatable_inc

View File

@ -0,0 +1,107 @@
/*==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 "plModulator.h"
#include "hsResMgr.h"
#include "hsStream.h"
#include "hsGeometry3.h"
#include "hsBounds.h"
#include "plController.h"
#include "../plIntersect/plVolumeIsect.h"
plModulator::plModulator()
: fVolume(nil),
fSoftDist(0)
{
}
plModulator::~plModulator()
{
delete fVolume;
}
void plModulator::SetVolume(plVolumeIsect* vol)
{
delete fVolume;
fVolume = vol;
}
void plModulator::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l)
{
hsAssert(fVolume, "Modulator with no Volume is pretty useless");
fVolume->SetTransform(l2w, w2l);
}
// Volume - Want to base this on the closest point on the bounds, instead of just the center.
hsScalar plModulator::Modulation(const hsBounds3Ext& bnd) const
{
return Modulation(bnd.GetCenter());
}
hsScalar plModulator::Modulation(const hsPoint3& pos) const
{
hsAssert(fVolume, "Modulator with no Volume is pretty useless");
hsScalar dist = fVolume->Test(pos);
hsScalar retVal;
if( dist > 0 )
{
if( dist < fSoftDist )
{
dist /= fSoftDist;
retVal = 1.f - dist;
}
else
{
retVal = 0;
}
}
else
{
retVal = 1.f;
}
return retVal;
}
void plModulator::Read(hsStream* s, hsResMgr* mgr)
{
fVolume = plVolumeIsect::ConvertNoRef(mgr->ReadCreatable(s));
fSoftDist = s->ReadSwapScalar();
}
void plModulator::Write(hsStream* s, hsResMgr* mgr)
{
mgr->WriteCreatable(s, fVolume);
s->WriteSwapScalar(fSoftDist);
}

View File

@ -0,0 +1,65 @@
/*==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==*/
#ifndef plModulator_inc
#define plModulator_inc
#include "../pnFactory/plCreatable.h"
struct hsMatrix44;
struct hsPoint3;
class plVolumeIsect;
class hsBounds3Ext;
class plModulator : public plCreatable
{
protected:
plVolumeIsect* fVolume;
hsScalar fSoftDist;
public:
plModulator();
virtual ~plModulator();
CLASSNAME_REGISTER( plModulator );
GETINTERFACE_ANY( plModulator, plCreatable );
const plVolumeIsect* GetVolume() const { return fVolume; }
void SetVolume(plVolumeIsect* vol); // Takes ownership, so don't delete after handing it in.
hsScalar Modulation(const hsPoint3& pos) const;
hsScalar Modulation(const hsBounds3Ext& bnd) const;
void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l);
hsScalar GetSoftDist() const { return fSoftDist; }
void SetSoftDist(hsScalar s) { fSoftDist = s; }
virtual void Read(hsStream* s, hsResMgr* mgr);
virtual void Write(hsStream* s, hsResMgr* mgr);
};
#endif // plModulator_inc