📄 fcurve.cpp
字号:
Stuff::Scalar v1,
Stuff::Scalar s1
)
{
Check_Object(this);
m_a = (s1+s0) + 2.0f*(v0-v1);
m_b = (s1 - s0 - 3.0f*m_a) * 0.5f;
m_slope = s0;
m_value = v0;
return (
Stuff::Close_Enough(m_a + m_b + m_slope + m_value, v1) &&
Stuff::Close_Enough(3.0f*m_a + 2.0f*m_b + m_slope, s1)
);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::SplineCurve::ComputeRange(
Stuff::Scalar *low,
Stuff::Scalar *hi
)
{
Check_Object(this);
Check_Pointer(low);
Check_Pointer(hi);
//
//------------------------------------------------------------------------
// We know that we will have to test the function at the beginning and end
// of the segment, so go ahead and do that now
//------------------------------------------------------------------------
//
*hi = *low = m_value;
Stuff::Scalar t = ComputeValue(1.0f, 0.0f);
if (t>*hi)
*hi = t;
else if (t<*low)
*low = t;
//
//----------------------------------------------------------------------
// If the curve is not cubic, we just have to look for the local min/max
// at the solution to 2*m_b*t + m_slope == 0. If the curve is linear, we just
// return
//----------------------------------------------------------------------
//
if (!m_a)
{
if (m_b)
{
t = -0.5f * m_slope / m_b;
if (t > 0.0f && t < 1.0f)
{
t = ComputeValue(t, 0.0f);
if (t < *low)
*low = t;
else if (t > *hi)
*hi = t;
}
}
return;
}
//
//----------------------------------------------------------------------
// Now we need to deal with the cubic. Its min/max will be at either of
// the two roots of the equation 3*m_a*t*t + 2*m_b*t + m_slope == 0
//----------------------------------------------------------------------
//
Stuff::Scalar da = 3.0f*m_a;
Stuff::Scalar db = 2.0f*m_b;
Stuff::Scalar range = db*db - 4.0f*da*m_slope;
if (range < 0.0f)
return;
da = 0.5f / da;
db = -db * da;
range = Stuff::Sqrt(range) * da;
//
//------------------------------------------------------------------------
// db now holds the midpoint between the two solutions, which will be at +
// or - range from that point
//------------------------------------------------------------------------
//
t = db - range;
if (t > 0.0f && t < 1.0f)
{
t = ComputeValue(t, 0.0f);
if (t < *low)
*low = t;
else if (t > *hi)
*hi = t;
}
t = db + range;
if (t > 0.0f && t < 1.0f)
{
t = ComputeValue(t, 0.0f);
if (t < *low)
*low = t;
else if (t > *hi)
*hi = t;
}
}
//##########################################################################
//########################## CurveKey ###############################
//##########################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// return: true=math good, false=math unstable
bool
gosFX::CurveKey::SetConstantKey(
Stuff::Scalar key_time,
Stuff::Scalar v
)
{
Check_Object(this);
m_time = key_time;
m_slope = 0.0f;
m_value = v;
return true;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// return: true=math good, false=math unstable
bool
gosFX::CurveKey::SetLinearKey(
Stuff::Scalar key_time,
Stuff::Scalar v0,
Stuff::Scalar v1,
Stuff::Scalar dt
)
{
Check_Object(this);
Verify(dt > Stuff::SMALL);
m_time = key_time;
m_slope = (v1 - v0) / dt;
m_value = v0;
return Stuff::Close_Enough(m_slope*dt + v0, v1);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::CurveKey::ComputeRange(
Stuff::Scalar *low,
Stuff::Scalar *hi,
Stuff::Scalar dt
)
{
Check_Object(this);
Check_Pointer(low);
Check_Pointer(hi);
//
//------------------------------------------------------------------------
// We know that we will have to test the function at the beginning and end
// of the segment, so go ahead and do that now
//------------------------------------------------------------------------
//
*hi = *low = m_value;
if (dt < Stuff::SMALL)
return;
Stuff::Scalar t = ComputeValue(dt);
if (t>*hi)
*hi = t;
else if (t<*low)
*low = t;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
gosFX::ComplexCurve::ComplexCurve():
Curve(e_ComplexType)
{
Check_Pointer(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
gosFX::ComplexCurve::ComplexCurve(const ComplexCurve &fcurve):
Curve(e_ComplexType)
{
Check_Pointer(this);
m_keys = fcurve.m_keys;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
gosFX::ComplexCurve::ComplexCurve(
Stuff::MemoryStream *stream,
int gfx_version
):
Curve(e_ComplexType)
{
Check_Pointer(this);
Check_Object(stream);
Load(stream, gfx_version);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
gosFX::ComplexCurve&
gosFX::ComplexCurve::operator=(const ComplexCurve &fcurve)
{
Check_Pointer(this);
m_keys = fcurve.m_keys;
return *this;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::ComplexCurve::Save(Stuff::MemoryStream *stream)
{
Check_Object(this);
Check_Object(stream);
Stuff::MemoryStreamIO_Write(stream, &m_keys);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::ComplexCurve::Load(
Stuff::MemoryStream *stream,
int gfx_version
)
{
Check_Pointer(this);
Check_Object(stream);
if (gfx_version < 15)
{
Stuff::Scalar duration;
*stream >> duration;
}
Stuff::MemoryStreamIO_Read(stream, &m_keys);
//
//--------------------------------------------------------------------------
// If we are reading a previous version, make sure the tail doesn't go below
// zero
//--------------------------------------------------------------------------
//
CurveKey *key = &m_keys[m_keys.GetLength()-1];
Stuff::Scalar dt = 1.0f - key->m_time;
Min_Clamp(dt, 0.0f);
Stuff::Scalar low, hi;
key->ComputeRange(&low, &hi, dt);
if (low < 0.0f)
{
key->SetLinearKey(
key->m_time,
key->m_value,
0.0f,
dt
);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
gosFX::ComplexCurve::InsertKey(Stuff::Scalar m_time)
{
Check_Object(this);
gos_PushCurrentHeap(Heap);
//
//-----------------------------------------------------------------------
// Figure out where to insert the next key, then increase the size of the
// key array and shift the m_keys after the insert point up one slot
//-----------------------------------------------------------------------
//
int before = GetKeyIndex(m_time);
int key_count = m_keys.GetLength();
m_keys.SetLength(key_count+1);
for (int i=key_count-1; i>=before; --i)
m_keys[i+1] = m_keys[i];
CurveKey* key;
if (key_count > 0)
key = &(*this)[++before];
else
key = &(*this)[before];
Check_Object(key);
//
//-----------------------------------------------------------------------
// If this is an insert as opposed to an append, we need to set the key
// values of the new segment so they smoothly complete the original curve
// being inserted into
//-----------------------------------------------------------------------
//
if (before < key_count)
{
Stuff::Scalar t = m_time - key->m_time;
Stuff::Scalar v0 = key->ComputeValue(t);
t = key[1].m_time - key->m_time;
Stuff::Scalar v1 = key->ComputeValue(t);
key->SetLinearKey(m_time, v0, v1, key[1].m_time - m_time);
}
//
//-----------------------------------------------------------------------
// Otherwise, we are appending, so all we can do is establish m_a key equal
// to the the previous key at this m_time
//-----------------------------------------------------------------------
//
else if (key_count > 0)
{
Verify(before == key_count);
Stuff::Scalar t = m_time - key->m_time;
Stuff::Scalar v0 = key->ComputeValue(t);
t += 1.0f;
Stuff::Scalar v1 = key->ComputeValue(t);
key->SetLinearKey(m_time, v0, v1, 1.0f);
}
//
//-----------------------------------------------------------------
// In we are inserting the first key, make m_a constant m_value of zero
//-----------------------------------------------------------------
//
else
key->SetConstantKey(m_time, 0.0f);
//
//-------------------------
// Return the new key index
//-------------------------
//
gos_PopCurrentHeap();
return before;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::ComplexCurve::DeleteKey(int index)
{
Check_Object(this);
Verify(index>0 && index<m_keys.GetLength());
gos_PushCurrentHeap(Heap);
//
//---------------------------------------------------
// If this is the last key, we just resize and return
//---------------------------------------------------
//
int key_count = m_keys.GetLength();
if (index == key_count-1)
{
m_keys.SetLength(index);
gos_PopCurrentHeap();
return;
}
//
//-----------------------------------------------------------
// Compute the ending values of this key, then shift the m_keys
//-----------------------------------------------------------
//
CurveKey* key = &(*this)[index];
Check_Object(key);
Stuff::Scalar t = key[1].m_time - key->m_time;
Stuff::Scalar v1 = key->ComputeValue(t);
for (int i=index+1; i<key_count; ++i)
m_keys[i-1] = m_keys[i];
//
//-------------------------------------------------
// Now connect the previous key to the new next key
//-------------------------------------------------
//
--key;
key->SetLinearKey(
key->m_time,
key->m_value,
v1,
key[1].m_time - key->m_time
);
m_keys.SetLength(key_count-1);
gos_PopCurrentHeap();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::ComplexCurve::SetCurve(Stuff::Scalar m_value)
{
Check_Object(this);
gos_PushCurrentHeap(Heap);
m_keys.SetLength(1);
m_keys[0].SetConstantKey(0.0f, m_value);
gos_PopCurrentHeap();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::ComplexCurve::SetCurve(
Stuff::Scalar starting_value,
Stuff::Scalar ending_value
)
{
Check_Object(this);
//
//-----------------------------------------------------------------------
// Build the linear step, then the constant m_value at the end of the curve
//-----------------------------------------------------------------------
//
gos_PushCurrentHeap(Heap);
m_keys.SetLength(1);
m_keys[0].SetLinearKey(0.0f, starting_value, ending_value, 1.0f);
gos_PopCurrentHeap();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
gosFX::ComplexCurve::ComputeRange(
Stuff::Scalar *low,
Stuff::Scalar *hi
)
{
Check_Object(this);
Check_Pointer(low);
Check_Pointer(hi);
//
//--------------------------------------------
// If the key is empty, set everything to zero
//--------------------------------------------
//
int key_count = m_keys.GetLength();
if (!key_count)
{
*low = *hi = 0.0f;
return;
}
//
//----------------------------
// Deal with the last keyframe
//----------------------------
//
CurveKey *key = &m_keys[--key_count];
Stuff::Scalar dt = 1.0f - key->m_time;
Min_Clamp(dt, 0.0f);
key->ComputeRange(low, hi, dt);
//
//------------------------------------
// Now deal with the preceeding frames
//------------------------------------
//
while (--key_count >= 0)
{
--key;
Stuff::Scalar h,l;
key->ComputeRange(&l, &h, key[1].m_time - key->m_time);
if (h > *hi)
*hi = h;
if (l < *low)
*low = l;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -