You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1044 lines
30 KiB
C++
1044 lines
30 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
#ifndef CRYINCLUDE_CRYCOMMON_FINALIZINGSPLINE_H
|
|
#define CRYINCLUDE_CRYCOMMON_FINALIZINGSPLINE_H
|
|
#pragma once
|
|
|
|
|
|
#include <ISplines.h> // <> required for Interfuscator
|
|
#include <AzCore/Casting/numeric_cast.h>
|
|
#include "CryCustomTypes.h"
|
|
|
|
inline bool IsEquivalent(float a, float b, float eps)
|
|
{
|
|
return abs(a - b) <= eps;
|
|
}
|
|
|
|
// change this value to 1 run spline verification code; this was previously enabled in debug and
|
|
// was causing issues because triggering the assert would steal the focus from the editor
|
|
// and cause the user to put a control point in the wrong spot. we don't understand this code
|
|
// enough to triage the asserts yet, but things seem to be functioning properly.
|
|
#define VERIFY_SPLINE_CONVERSION 0
|
|
|
|
namespace spline
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FinalizingSpline
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
template <class Source, class Final>
|
|
class FinalizingSpline
|
|
: public CBaseSplineInterpolator<typename Source::value_type, Source>
|
|
{
|
|
public:
|
|
using_type(Source, value_type);
|
|
using_type(Source, key_type);
|
|
using_type(ISplineInterpolator, ValueType);
|
|
|
|
FinalizingSpline()
|
|
: m_pFinal(0)
|
|
{}
|
|
|
|
void SetFinal(Final* pFinal)
|
|
{
|
|
m_pFinal = pFinal;
|
|
m_pFinal->to_source(*this);
|
|
}
|
|
|
|
// Most spline functions use source spline (base class).
|
|
// interpolate() uses dest, for fidelity.
|
|
virtual void Interpolate(float time, ValueType& value)
|
|
{
|
|
Source::update();
|
|
assert(m_pFinal);
|
|
m_pFinal->interpolate(time, *(value_type*)&value);
|
|
}
|
|
|
|
virtual void EvalInTangent(float time, ValueType& value)
|
|
{
|
|
Source::update();
|
|
assert(m_pFinal);
|
|
m_pFinal->evalInTangent(time, *(value_type*)&value);
|
|
|
|
}
|
|
|
|
virtual void EvalOutTangent(float time, ValueType& value)
|
|
{
|
|
Source::update();
|
|
assert(m_pFinal);
|
|
m_pFinal->evalOutTangent(time, *(value_type*)&value);
|
|
|
|
}
|
|
|
|
virtual void eval(float time, ValueType& value)
|
|
{
|
|
Source::update();
|
|
assert(m_pFinal);
|
|
m_pFinal->interpolate(time, *(value_type*)&value);
|
|
}
|
|
|
|
// Update dest when source modified.
|
|
// Should be called for every update to source spline.
|
|
virtual void SetModified(bool bOn, bool bSort = false)
|
|
{
|
|
Source::SetModified(bOn, bSort);
|
|
assert(m_pFinal);
|
|
m_pFinal->from_source(*this);
|
|
}
|
|
|
|
virtual bool GetKeyTangents(int k, ValueType& tin, ValueType& tout)
|
|
{
|
|
if (k >= 0 && k < this->num_keys())
|
|
{
|
|
this->ToValueType(Source::ds(k), tin);
|
|
this->ToValueType(Source::dd(k), tout);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
virtual int GetKeyFlags(int k)
|
|
{
|
|
return Source::flags(k);
|
|
}
|
|
|
|
///////////////////////////////////////////
|
|
|
|
virtual void SerializeSpline([[maybe_unused]] XmlNodeRef& node, [[maybe_unused]] bool bLoading)
|
|
{}
|
|
|
|
protected:
|
|
|
|
Final* m_pFinal;
|
|
};
|
|
|
|
#if 0
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// LookupTableSpline
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
template <class S, class value_type, const int nMAX_ENTRIES>
|
|
class LookupTableSplineInterpolater
|
|
{
|
|
public:
|
|
ILINE static void fast_interpolate(float t, value_type& val, S* m_table)
|
|
{
|
|
t *= nMAX_ENTRIES - 1.0f;
|
|
float frac = t - floorf(t);
|
|
int idx = int(t);
|
|
val = value_type(m_table[idx]) * (1.f - frac);
|
|
val += value_type(m_table[idx + 1]) * frac;
|
|
}
|
|
};
|
|
|
|
template <class value_type, const int nMAX_ENTRIES>
|
|
class LookupTableSplineInterpolater <UnitFloat8, value_type, nMAX_ENTRIES>
|
|
{
|
|
enum
|
|
{
|
|
SHIFT_AMOUNT = 24
|
|
};
|
|
|
|
public:
|
|
ILINE static void fast_interpolate(float t, value_type& val, UnitFloat8* m_table)
|
|
{
|
|
const float scale = (float)(1 << SHIFT_AMOUNT);
|
|
uint32 ti = uint32(t * scale * (nMAX_ENTRIES - 1.0f));
|
|
uint32 idx = ti >> SHIFT_AMOUNT;
|
|
uint32 frac = ti - (idx << SHIFT_AMOUNT);
|
|
uint32 vali = (uint32)m_table[idx + 1].GetStore() * frac;
|
|
frac = (1 << SHIFT_AMOUNT) - frac;
|
|
vali += (uint32)m_table[idx].GetStore() * frac;
|
|
val = (value_type)vali * (1.0f / (255.0f * scale));
|
|
}
|
|
};
|
|
|
|
|
|
template <class S, class Source>
|
|
class LookupTableSpline
|
|
: public FinalizingSpline<Source>
|
|
{
|
|
typedef FinalizingSpline<Source> super_type;
|
|
using super_type::m_pSource;
|
|
using super_type::is_updated;
|
|
|
|
enum
|
|
{
|
|
nSTORE_SIZE = 128
|
|
};
|
|
enum
|
|
{
|
|
nMAX_ENTRIES = nSTORE_SIZE - 1
|
|
};
|
|
enum
|
|
{
|
|
nMIN_VALUE = nMAX_ENTRIES
|
|
};
|
|
|
|
public:
|
|
|
|
using_type(super_type, value_type);
|
|
|
|
LookupTableSpline()
|
|
{
|
|
init();
|
|
}
|
|
|
|
LookupTableSpline(const LookupTableSpline& other)
|
|
: super_type(other)
|
|
{
|
|
init();
|
|
update();
|
|
}
|
|
void operator=(const LookupTableSpline& other)
|
|
{
|
|
super_type::operator=(other);
|
|
update();
|
|
}
|
|
|
|
~LookupTableSpline()
|
|
{
|
|
delete[] m_table;
|
|
}
|
|
|
|
void interpolate(float t, value_type& val)
|
|
{
|
|
if (!is_updated())
|
|
{
|
|
update();
|
|
}
|
|
fast_interpolate(clamp_tpl(t, 0.0f, 1.0f), val);
|
|
}
|
|
|
|
void fast_interpolate(float t, value_type& val) const
|
|
{
|
|
LookupTableSplineInterpolater<S, value_type, nMAX_ENTRIES>::fast_interpolate(t, val, m_table);
|
|
}
|
|
|
|
ILINE void min_value(value_type& val) const
|
|
{
|
|
val = value_type(m_table[nMIN_VALUE]);
|
|
}
|
|
|
|
void finalize()
|
|
{
|
|
if (!is_updated())
|
|
{
|
|
update();
|
|
}
|
|
super_type::finalize();
|
|
}
|
|
|
|
void GetMemoryUsage(ICrySizer* pSizer, bool bSelf = false) const
|
|
{
|
|
if (bSelf && !pSizer->AddObjectSize(this))
|
|
{
|
|
return;
|
|
}
|
|
super_type::GetMemoryUsage(pSizer);
|
|
if (m_table)
|
|
{
|
|
pSizer->AddObject(m_table, nSTORE_SIZE * sizeof(S));
|
|
}
|
|
}
|
|
|
|
protected:
|
|
|
|
void update()
|
|
{
|
|
value_type minVal(1.0f);
|
|
if (!m_table)
|
|
{
|
|
m_table = new S[nSTORE_SIZE];
|
|
}
|
|
if (!m_pSource || m_pSource->empty())
|
|
{
|
|
for (int i = 0; i < nMAX_ENTRIES; i++)
|
|
{
|
|
m_table[i] = value_type(1.f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_pSource->update();
|
|
for (int i = 0; i < nMAX_ENTRIES; i++)
|
|
{
|
|
value_type val;
|
|
float t = float(i) * (1.0f / (float)(nMAX_ENTRIES - 1));
|
|
m_pSource->interpolate(t, val);
|
|
minVal = min(minVal, val);
|
|
m_table[i] = val;
|
|
}
|
|
}
|
|
m_table[nMIN_VALUE] = minVal;
|
|
}
|
|
|
|
void init()
|
|
{
|
|
m_table = NULL;
|
|
}
|
|
|
|
bool is_updated() const
|
|
{
|
|
return super_type::is_updated() && m_table;
|
|
}
|
|
|
|
S* m_table;
|
|
};
|
|
|
|
#endif // LookupTableSpline
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// OptSpline
|
|
// Minimises memory for key-based storage. Uses 8-bit compressed key values.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
/*
|
|
Choose basis vars t, u = 1-t, ttu, uut.
|
|
This produces exact values at t = 0 and 1, even with compressed coefficients.
|
|
For end points and slopes v0, v1, s0, s1,
|
|
solve for coefficients a, b, c, d:
|
|
|
|
v(t) = a u + b t + c uut + d utt
|
|
s(t) = v'(t) = -a + b + c (1-4t+3t^2) + d (2t-3t^2)
|
|
|
|
v(0) = a
|
|
v(1) = b
|
|
s(0) = -a + b + c
|
|
s(1) = -a + b - d
|
|
|
|
So
|
|
|
|
a = v0
|
|
b = v1
|
|
c = s0 + v0 - v1
|
|
d = -s1 - v0 + v1
|
|
|
|
s0 = c + v1 - v0
|
|
s1 = -d + v1 - v0
|
|
|
|
For compression, all values of v and t are limited to [0..1].
|
|
Find the max possible slope values, such that values never exceed this range.
|
|
|
|
If v0 = v1 = 0, then max slopes would have
|
|
|
|
c = d
|
|
v(1/2) = 1
|
|
c/8 + d/8 = 1
|
|
c = 4
|
|
|
|
If v0 = 0 and v1 = 1, then max slopes would have
|
|
|
|
c = d
|
|
v(1/3) = 1
|
|
1/3 + c 4/9 + d 2/9 = 1
|
|
c = 1
|
|
*/
|
|
|
|
template<class T>
|
|
class OptSpline
|
|
{
|
|
typedef OptSpline<T> self_type;
|
|
|
|
public:
|
|
|
|
typedef T value_type;
|
|
typedef SplineKey<T> key_type;
|
|
typedef TSplineSlopes<T, key_type, true> source_spline;
|
|
|
|
protected:
|
|
|
|
static const int DIM = sizeof(value_type) / sizeof(float);
|
|
|
|
template<class S>
|
|
struct array
|
|
{
|
|
S elems[DIM];
|
|
|
|
ILINE array()
|
|
{
|
|
for (int i = 0; i < DIM; i++)
|
|
{
|
|
elems[i] = 0;
|
|
}
|
|
}
|
|
ILINE array(value_type const& val)
|
|
{
|
|
const float* aVal = reinterpret_cast<const float*>(&val);
|
|
for (int i = 0; i < DIM; i++)
|
|
{
|
|
elems[i] = aVal[i];
|
|
}
|
|
}
|
|
ILINE array& operator=(value_type const& val)
|
|
{
|
|
new(this)array<S>(val);
|
|
return *this;
|
|
}
|
|
|
|
ILINE operator value_type() const
|
|
{
|
|
PREFAST_SUPPRESS_WARNING(6001)
|
|
value_type val;
|
|
float* aVal = reinterpret_cast<float*>(&val);
|
|
for (int i = 0; i < DIM; i++)
|
|
{
|
|
aVal[i] = elems[i];
|
|
}
|
|
return val;
|
|
}
|
|
|
|
ILINE bool operator !() const
|
|
{
|
|
for (int i = 0; i < DIM; i++)
|
|
{
|
|
if (elems[i])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
ILINE bool operator ==(array<S> const& o) const
|
|
{
|
|
for (int i = 0; i < DIM; i++)
|
|
{
|
|
if (!(elems[i] == o.elems[i]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
ILINE S& operator [](int i)
|
|
{
|
|
assert(i >= 0 && i < DIM);
|
|
return elems[i];
|
|
}
|
|
ILINE const S& operator [](int i) const
|
|
{
|
|
assert(i >= 0 && i < DIM);
|
|
return elems[i];
|
|
}
|
|
};
|
|
|
|
typedef UnitFloat8 TStore;
|
|
typedef array< UnitFloat8 > VStore;
|
|
typedef array< float> FStore;
|
|
typedef array< TFixed<int8, 2, 127, true> > SStore;
|
|
|
|
//
|
|
// Element storage
|
|
//
|
|
struct Point
|
|
{
|
|
TStore st; // Time of this point.
|
|
VStore sv; // Value at this point.
|
|
FStore dd; // Out tangent
|
|
FStore ds; // In tangent
|
|
int flags; // key type flags
|
|
|
|
void set_key(float t, value_type v)
|
|
{
|
|
st = t;
|
|
sv = v;
|
|
}
|
|
void set_flags(int f)
|
|
{
|
|
flags = f;
|
|
}
|
|
|
|
void set_tangent(value_type _dd, value_type _ds)
|
|
{
|
|
dd = _dd;
|
|
ds = _ds;
|
|
}
|
|
|
|
};
|
|
|
|
struct Elem
|
|
: Point
|
|
{
|
|
using Point::st; // Total BS required for idiotic gcc.
|
|
using Point::sv;
|
|
|
|
SStore sc, sd; // Coefficients for uut and utt.
|
|
|
|
// Compute coeffs based on 2 endpoints & slopes.
|
|
void set_slopes(value_type s0, value_type s1)
|
|
{
|
|
value_type dv = value_type((this)[1].sv) - value_type(sv);
|
|
sc = s0 - dv;
|
|
sd = dv - s1;
|
|
}
|
|
|
|
ILINE void eval(value_type& val, float t) const
|
|
{
|
|
float u = 1.f - t,
|
|
tu = t * u;
|
|
|
|
float* aF = reinterpret_cast<float*>(&val);
|
|
for (int i = 0; i < DIM; i++)
|
|
{
|
|
float elem = float(sv[i]) * u + float(this[1].sv[i]) * t;
|
|
elem += (float(sc[i]) * u + float(sd[i]) * t) * tu;
|
|
elem = elem < 0.f ? 0.f : elem;
|
|
elem = elem > 1.f ? 1.f : elem;
|
|
aF[i] = elem;
|
|
}
|
|
}
|
|
|
|
// Use the derivative of eval formular to caculate the tangent of t
|
|
ILINE void dev_eval(value_type& val, float t, value_type value) const
|
|
{
|
|
float u = 1.f - t,
|
|
tu = t * u;
|
|
|
|
float* aF = reinterpret_cast<float*>(&val);
|
|
float* currentValue = reinterpret_cast<float*>(&value);
|
|
for (int i = 0; i < DIM; i++)
|
|
{
|
|
float elem = -float(sv[i]) + float(currentValue[i]);
|
|
elem += float(sc[i]) * (1 - 4 * t + 3 * t * t)
|
|
+ float(sd[i]) * (2 * t - 3 * t * t);
|
|
aF[i] = elem;
|
|
}
|
|
}
|
|
|
|
value_type value() const
|
|
{
|
|
return sv;
|
|
}
|
|
|
|
// Slopes
|
|
// v(t) = v0 u + v1 t + (c u + d t) t u
|
|
// v\t(t) = v1 - v0 + (d - c) t u + (d t + c u) (u-t)
|
|
// v\t(0) = v1 - v0 + c
|
|
// v\t(1) = v1 - v0 - d
|
|
value_type start_slope() const
|
|
{
|
|
return value_type((this)[1].sv) - value_type(sv) + value_type(sc);
|
|
}
|
|
value_type end_slope() const
|
|
{
|
|
return value_type((this)[1].sv) - value_type(sv) - value_type(sd);
|
|
}
|
|
};
|
|
|
|
struct Spline
|
|
{
|
|
uint8 nKeys; // Total number of keys.
|
|
Elem aElems[1]; // Points and slopes. Last element is just Point without slopes.
|
|
|
|
Spline()
|
|
: nKeys(0)
|
|
{
|
|
// Empty spline sets dummy values to max, for consistency.
|
|
aElems[0].st = TStore(1);
|
|
aElems[0].sv = value_type(1);
|
|
aElems[0].flags = 0;
|
|
aElems[0].dd = FStore(0);
|
|
aElems[0].ds = FStore(0);
|
|
}
|
|
|
|
Spline(int keys)
|
|
: nKeys(aznumeric_caster(keys))
|
|
{
|
|
#ifdef _DEBUG
|
|
if (nKeys)
|
|
{
|
|
((char*)this)[alloc_size()] = 77;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static size_t alloc_size(int keys)
|
|
{
|
|
assert(keys > 0);
|
|
return sizeof(Spline) + max(keys - 1, 0) * sizeof(Elem);
|
|
}
|
|
size_t alloc_size() const
|
|
{
|
|
return alloc_size(nKeys);
|
|
}
|
|
|
|
key_type key(int n) const
|
|
{
|
|
key_type key;
|
|
if (n < nKeys)
|
|
{
|
|
key.time = aElems[n].st;
|
|
key.value = aElems[n].sv;
|
|
|
|
// Infer slope flags from slopes.
|
|
if (n >= 0) // bezier curve inTangent and outtangent will be asigned by user
|
|
{
|
|
key.flags = aElems[n].flags;
|
|
key.dd = aElems[n].dd;
|
|
key.ds = aElems[n].ds;
|
|
}
|
|
}
|
|
return key;
|
|
}
|
|
|
|
void interpolate(float t, value_type& val) const
|
|
{
|
|
float prev_t = aElems[0].st;
|
|
if (t <= prev_t)
|
|
{
|
|
val = aElems[0].sv;
|
|
}
|
|
else
|
|
{
|
|
// Find spline segment.
|
|
const Elem* pEnd = aElems + nKeys - 1;
|
|
const Elem* pElem = aElems;
|
|
for (; pElem < pEnd; ++pElem)
|
|
{
|
|
float cur_t = pElem[1].st;
|
|
if (t <= cur_t)
|
|
{
|
|
// Eval
|
|
pElem->eval(val, (t - prev_t) / (cur_t - prev_t));
|
|
return;
|
|
}
|
|
prev_t = cur_t;
|
|
}
|
|
|
|
// Last point value.
|
|
val = pElem->sv;
|
|
}
|
|
}
|
|
|
|
|
|
void EvalInTangent(float t, value_type& val) const
|
|
{
|
|
// Compute coeffs dynamically.
|
|
|
|
float prev_t = aElems[0].st;
|
|
value_type prev_v = aElems[0].sv;
|
|
if (t <= prev_t)
|
|
{
|
|
val = 0;
|
|
}
|
|
else
|
|
{
|
|
// Find spline segment.
|
|
const Elem* pEnd = aElems + nKeys - 1;
|
|
const Elem* pElem = aElems;
|
|
for (; pElem < pEnd; ++pElem)
|
|
{
|
|
float cur_t = pElem[1].st;
|
|
value_type cur_v = pElem[1].sv;
|
|
value_type Tvalue = value_type(0);
|
|
interpolate(t, Tvalue);
|
|
|
|
if (t <= cur_t)
|
|
{
|
|
|
|
Elem newElement;
|
|
newElement.set_key(prev_t, prev_v);
|
|
value_type dd = Tvalue - prev_v;
|
|
|
|
value_type ds = aElems[0].ds;
|
|
|
|
newElement.set_tangent(dd, ds);
|
|
|
|
|
|
value_type tds = Tvalue - prev_v;
|
|
if (pElem[0].dd == FStore(0))
|
|
{
|
|
tds = 2 * tds;
|
|
}
|
|
|
|
newElement.sc = dd - dd;
|
|
newElement.sd = dd - value_type(tds);
|
|
|
|
newElement.dev_eval(val, 1, Tvalue);
|
|
return;
|
|
}
|
|
prev_t = cur_t;
|
|
prev_v = cur_v;
|
|
}
|
|
|
|
// Last point value.
|
|
val = 0.0f;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void EvalOutTangent(float t, value_type& val) const
|
|
{
|
|
// Compute coeffs dynamically.
|
|
|
|
float prev_t = aElems[0].st;
|
|
value_type prev_v = aElems[0].sv;
|
|
if (t <= prev_t)
|
|
{
|
|
val = 0;
|
|
}
|
|
else
|
|
{
|
|
// Find spline segment.
|
|
const Elem* pEnd = aElems + nKeys - 1;
|
|
const Elem* pElem = aElems;
|
|
for (; pElem < pEnd; ++pElem)
|
|
{
|
|
float cur_t = pElem[1].st;
|
|
value_type cur_v = pElem[1].sv;
|
|
value_type Tvalue = value_type(0);
|
|
interpolate(t, Tvalue);
|
|
|
|
if (t <= cur_t)
|
|
{
|
|
// Create a temporary Elem to hold the info of the inserted key.
|
|
// Since we will insert a key between pre_key and curr_key, the
|
|
// slop and in/out tangent need to be recaculated.
|
|
Elem newElement;
|
|
// Set value and time for new key
|
|
newElement.set_key(t, Tvalue);
|
|
|
|
//Caculate out tangent of new key.
|
|
value_type dd = cur_v - Tvalue;
|
|
if (pElem[1].ds == FStore(0))
|
|
{
|
|
dd = 2 * dd;
|
|
}
|
|
//Caculate in tangent of new key
|
|
value_type ds = Tvalue - prev_v;
|
|
if (pElem[0].dd == FStore(0))
|
|
{
|
|
ds = 2 * ds;
|
|
}
|
|
newElement.set_tangent(dd,ds);
|
|
|
|
value_type dv = cur_v - Tvalue;
|
|
newElement.sc = dd - dv;
|
|
newElement.sd = dv - value_type(pElem[1].ds);
|
|
|
|
newElement.dev_eval(val, 0, cur_v);
|
|
return;
|
|
}
|
|
prev_t = cur_t;
|
|
prev_v = cur_v;
|
|
}
|
|
|
|
// Last point value.
|
|
val = 0.0f;
|
|
}
|
|
}
|
|
|
|
void min_value(value_type& val) const
|
|
{
|
|
VStore sval = aElems[0].sv;
|
|
for (int n = 1; n < nKeys; n++)
|
|
{
|
|
for (int i = 0; i < DIM; i++)
|
|
{
|
|
sval[i] = min(sval[i], aElems[n].sv[i]);
|
|
}
|
|
}
|
|
val = sval;
|
|
}
|
|
|
|
void max_value(value_type& val) const
|
|
{
|
|
VStore sval = aElems[0].sv;
|
|
for (int n = 1; n < nKeys; n++)
|
|
{
|
|
for (int i = 0; i < DIM; i++)
|
|
{
|
|
sval[i] = max(sval[i], aElems[n].sv[i]);
|
|
}
|
|
}
|
|
val = sval;
|
|
}
|
|
|
|
value_type default_slope(int n) const
|
|
{
|
|
return n > 0 && n < nKeys - 1 ?
|
|
minmag(aElems[n].value() - aElems[n - 1].value(), aElems[n + 1].value() - aElems[n].value())
|
|
: value_type(0.f);
|
|
}
|
|
|
|
void validate() const
|
|
{
|
|
#ifdef _DEBUG
|
|
if (nKeys)
|
|
{
|
|
assert(((char const*)this)[alloc_size()] == 77);
|
|
}
|
|
#endif
|
|
}
|
|
};
|
|
|
|
Spline* m_pSpline;
|
|
|
|
void alloc(int nKeys)
|
|
{
|
|
if (nKeys)
|
|
{
|
|
size_t nAlloc = Spline::alloc_size(nKeys)
|
|
#ifdef _DEBUG
|
|
+ 1
|
|
#endif
|
|
;
|
|
|
|
//set the memory to 0 since the comparison between two splines are comparing memory directly.
|
|
//And set functions won't set the memory because of alignment.
|
|
m_pSpline = new(CryModuleCalloc(1, nAlloc))Spline(nKeys);
|
|
}
|
|
else
|
|
{
|
|
m_pSpline = NULL;
|
|
}
|
|
}
|
|
|
|
void dealloc()
|
|
{
|
|
CryModuleFree(m_pSpline);
|
|
m_pSpline = nullptr;
|
|
}
|
|
|
|
public:
|
|
|
|
~OptSpline()
|
|
{
|
|
dealloc();
|
|
}
|
|
|
|
OptSpline()
|
|
{
|
|
m_pSpline = NULL;
|
|
}
|
|
|
|
OptSpline(const self_type& in)
|
|
{
|
|
if (!in.empty() && in.num_keys() != 0)
|
|
{
|
|
alloc(in.num_keys());
|
|
memcpy(m_pSpline, in.m_pSpline, in.m_pSpline->alloc_size());
|
|
m_pSpline->validate();
|
|
}
|
|
else
|
|
{
|
|
m_pSpline = NULL;
|
|
}
|
|
}
|
|
|
|
self_type& operator=(const self_type& in)
|
|
{
|
|
dealloc();
|
|
new(this)self_type(in);
|
|
return *this;
|
|
}
|
|
|
|
bool operator == (const self_type& o) const
|
|
{
|
|
if (empty() && o.empty())
|
|
{
|
|
return true;
|
|
}
|
|
if (num_keys() != o.num_keys())
|
|
{
|
|
return false;
|
|
}
|
|
return !memcmp(m_pSpline, o.m_pSpline, m_pSpline->alloc_size());
|
|
}
|
|
|
|
//
|
|
// Adaptors for CBaseSplineInterpolator
|
|
//
|
|
bool empty() const
|
|
{
|
|
return !m_pSpline;
|
|
}
|
|
void clear()
|
|
{
|
|
dealloc();
|
|
m_pSpline = NULL;
|
|
}
|
|
ILINE int num_keys() const
|
|
{
|
|
return m_pSpline ? m_pSpline->nKeys : 0;
|
|
}
|
|
|
|
ILINE key_type key(int n) const
|
|
{
|
|
assert(n < num_keys());
|
|
return m_pSpline->key(n);
|
|
}
|
|
|
|
ILINE void interpolate(float t, value_type& val) const
|
|
{
|
|
if (!empty())
|
|
{
|
|
m_pSpline->interpolate(t, val);
|
|
}
|
|
else
|
|
{
|
|
val = value_type(1.f);
|
|
}
|
|
}
|
|
|
|
|
|
ILINE void evalInTangent(float t, value_type& val) const
|
|
{
|
|
if (!empty())
|
|
{
|
|
m_pSpline->EvalInTangent(t, val);
|
|
}
|
|
else
|
|
{
|
|
val = value_type(0.f);
|
|
}
|
|
}
|
|
|
|
ILINE void evalOutTangent(float t, value_type& val) const
|
|
{
|
|
if (!empty())
|
|
{
|
|
m_pSpline->EvalOutTangent(t, val);
|
|
}
|
|
else
|
|
{
|
|
val = value_type(0.f);
|
|
}
|
|
}
|
|
|
|
void GetMemoryUsage(ICrySizer* pSizer) const
|
|
{
|
|
if (!empty())
|
|
{
|
|
pSizer->AddObject(m_pSpline, m_pSpline->alloc_size());
|
|
}
|
|
}
|
|
|
|
//
|
|
// Additional methods.
|
|
//
|
|
void min_value(value_type& val) const
|
|
{
|
|
if (!empty())
|
|
{
|
|
m_pSpline->min_value(val);
|
|
}
|
|
else
|
|
{
|
|
val = value_type(1.f);
|
|
}
|
|
}
|
|
void max_value(value_type& val) const
|
|
{
|
|
if (!empty())
|
|
{
|
|
m_pSpline->max_value(val);
|
|
}
|
|
else
|
|
{
|
|
val = value_type(1.f);
|
|
}
|
|
}
|
|
|
|
void from_source(source_spline& source)
|
|
{
|
|
dealloc();
|
|
source.update();
|
|
int nKeys = source.num_keys();
|
|
|
|
// Check for trivial spline.
|
|
bool is_default = true;
|
|
for (int i = 0; i < nKeys; i++)
|
|
{
|
|
if (source.value(i) != value_type(1))
|
|
{
|
|
is_default = false;
|
|
break;
|
|
}
|
|
}
|
|
if (is_default)
|
|
{
|
|
nKeys = 0;
|
|
}
|
|
|
|
alloc(nKeys);
|
|
if (nKeys)
|
|
{
|
|
// First set key values, then compute slope coefficients.
|
|
for (int i = 0; i < nKeys; i++)
|
|
{
|
|
m_pSpline->aElems[i].set_key(source.time(i), source.value(i));
|
|
m_pSpline->aElems[i].set_flags(source.flags(i));
|
|
m_pSpline->aElems[i].set_tangent(source.dd(i), source.ds(i));
|
|
|
|
}
|
|
for (int i = 0; i < nKeys - 1; i++)
|
|
{
|
|
m_pSpline->aElems[i].set_slopes(source.dd(i), source.ds(i + 1));
|
|
}
|
|
|
|
#if VERIFY_SPLINE_CONVERSION
|
|
// Verify accurate conversion to OptSpline.
|
|
for (int i = 0; i < nKeys; i++)
|
|
{
|
|
key_type ks = source.key(i);
|
|
key_type kf = key(i);
|
|
|
|
assert(TStore(ks.time) == TStore(kf.time));
|
|
assert(VStore(ks.value) == VStore(kf.value));
|
|
|
|
static const float fSlopeEquivalence = 1.f / 60.f;
|
|
assert(IsEquivalent(ks.ds, kf.ds, fSlopeEquivalence));
|
|
assert(IsEquivalent(ks.dd, kf.dd, fSlopeEquivalence));
|
|
|
|
// Verify accurate reconstruction of slope flags.
|
|
ks.flags &= (SPLINE_KEY_TANGENT_IN_MASK | SPLINE_KEY_TANGENT_OUT_MASK);
|
|
|
|
value_type default_slope = i > 0 && i < nKeys - 1 ?
|
|
minmag(ks.value - source.key(i - 1).value, source.key(i + 1).value - ks.value)
|
|
: value_type(0.f);
|
|
if (i == 0 || IsEquivalent(ks.ds, default_slope, fSlopeEquivalence))
|
|
{
|
|
ks.flags &= ~SPLINE_KEY_TANGENT_IN_MASK;
|
|
}
|
|
if (i == nKeys - 1 || IsEquivalent(ks.dd, default_slope, fSlopeEquivalence))
|
|
{
|
|
ks.flags &= ~SPLINE_KEY_TANGENT_OUT_MASK;
|
|
}
|
|
assert(ks.flags == kf.flags);
|
|
}
|
|
#endif //VERIFY_SPLINE_CONVERSION
|
|
}
|
|
}
|
|
|
|
void to_source(source_spline& source) const
|
|
{
|
|
int nKeys = num_keys();
|
|
source.resize(nKeys);
|
|
for (int i = 0; i < nKeys; i++)
|
|
{
|
|
source.key(i) = key(i);
|
|
}
|
|
source.update();
|
|
}
|
|
};
|
|
};
|
|
|
|
#endif // CRYINCLUDE_CRYCOMMON_FINALIZINGSPLINE_H
|