2
3
mirror of https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git synced 2025-07-14 02:27:40 -04:00

CWE Directory Reorganization

Rearrange directory structure of CWE to be loosely equivalent to
the H'uru Plasma repository.

Part 1: Movement of directories and files.
This commit is contained in:
rarified
2021-05-15 12:49:46 -06:00
parent c3f4a640a3
commit 96903e8dca
4002 changed files with 159 additions and 644 deletions

View File

@ -0,0 +1,510 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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,98 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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,570 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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,255 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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,236 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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,363 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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,59 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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,809 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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,188 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,322 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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,916 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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,230 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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,69 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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,123 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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,81 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#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