123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "T3D/cameraSpline.h"
- #include "console/console.h"
- #include "gfx/gfxDevice.h"
- //-----------------------------------------------------------------------------
- CameraSpline::Knot::Knot()
- {
- mPosition = Point3F::Zero;
- mRotation = QuatF::Identity;
- mSpeed = 0.0f;
- mType = NORMAL;
- mPath = SPLINE;
- mDistance = 0.0f;
- prev = NULL; next = NULL;
- };
- CameraSpline::Knot::Knot(const Knot &k)
- {
- mPosition = k.mPosition;
- mRotation = k.mRotation;
- mSpeed = k.mSpeed;
- mType = k.mType;
- mPath = k.mPath;
- mDistance = k.mDistance;
- prev = NULL; next = NULL;
- }
- CameraSpline::Knot::Knot(const Point3F &p, const QuatF &r, F32 s, Knot::Type type, Knot::Path path, String hitCommand)
- {
- mPosition = p;
- mRotation = r;
- mSpeed = s;
- mType = type;
- mPath = path;
- mDistance = 0.0f;
- mHitCommand = hitCommand;
- prev = NULL; next = NULL;
- }
- //-----------------------------------------------------------------------------
- CameraSpline::CameraSpline()
- {
- mFront = NULL;
- mSize = 0;
- mIsMapDirty = true;
- VECTOR_SET_ASSOCIATION(mTimeMap);
- }
- CameraSpline::~CameraSpline()
- {
- removeAll();
- }
- void CameraSpline::push_back(Knot *w)
- {
- if (!mFront)
- {
- mFront = w;
- w->next = w;
- w->prev = w;
- }
- else
- {
- Knot *before = back();
- Knot *after = before->next;
- w->next = before->next;
- w->prev = before;
- after->prev = w;
- before->next = w;
- }
- ++mSize;
- mIsMapDirty = true;
- }
- CameraSpline::Knot* CameraSpline::getKnot(S32 i)
- {
- Knot *k = mFront;
- while(i--)
- k = k->next;
- return k;
- }
- CameraSpline::Knot* CameraSpline::remove(Knot *w)
- {
- if (w->next == mFront && w->prev == mFront)
- mFront = NULL;
- else
- {
- w->prev->next = w->next;
- w->next->prev = w->prev;
- if (mFront == w)
- mFront = w->next;
- }
- --mSize;
- mIsMapDirty = true;
- return w;
- }
- void CameraSpline::removeAll()
- {
- while(front())
- delete remove(front());
- mSize = 0;
- }
- //-----------------------------------------------------------------------------
- static bool gBuilding = false;
- void CameraSpline::buildTimeMap()
- {
- if (!mIsMapDirty)
- return;
- gBuilding = true;
- mTimeMap.clear();
- mTimeMap.reserve(size()*3); // preallocate
- // Initial node and knot value..
- TimeMap map;
- map.mTime = 0;
- map.mDistance = 0;
- mTimeMap.push_back(map);
- Knot ka,kj,ki;
- value(0, &kj, true);
- F32 length = 0.0f;
- ka = kj;
- // Loop through the knots and add nodes. Nodes are added for every knot and
- // whenever the spline length and segment length deviate by epsilon.
- F32 epsilon = Con::getFloatVariable("CameraSpline::epsilon", 0.90f);
- const F32 Step = 0.05f;
- F32 lt = 0,time = 0;
- do
- {
- if ((time += Step) > F32(mSize - 1))
- time = (F32)mSize - 1.0f;
- value(time, &ki, true);
- length += (ki.mPosition - kj.mPosition).len();
- F32 segment = (ki.mPosition - ka.mPosition).len();
- if ((segment / length) < epsilon || time == (mSize - 1) || mFloor(lt) != mFloor(time))
- {
- map.mTime = time;
- map.mDistance = length;
- mTimeMap.push_back(map);
- ka = ki;
- }
- kj = ki;
- lt = time;
- }
- while (time < mSize - 1);
- mIsMapDirty = false;
- gBuilding = false;
- }
- //-----------------------------------------------------------------------------
- void CameraSpline::renderTimeMap()
- {
- buildTimeMap();
- gBuilding = true;
- // Build vertex buffer
- GFXVertexBufferHandle<GFXVertexPCT> vb;
- vb.set(GFX, mTimeMap.size(), GFXBufferTypeVolatile);
- void *ptr = vb.lock();
- if(!ptr) return;
- MRandomLCG random(1376312589 * (uintptr_t)this);
- S32 index = 0;
- for(Vector<TimeMap>::iterator itr=mTimeMap.begin(); itr != mTimeMap.end(); itr++)
- {
- Knot a;
- value(itr->mTime, &a, true);
- S32 cr = random.randI(0,255);
- S32 cg = random.randI(0,255);
- S32 cb = random.randI(0,255);
- vb[index].color.set(cr, cg, cb);
- vb[index].point.set(a.mPosition.x, a.mPosition.y, a.mPosition.z);
- index++;
- }
- gBuilding = false;
- vb.unlock();
- // Render the buffer
- GFX->pushWorldMatrix();
- GFX->setupGenericShaders();
- GFX->setVertexBuffer(vb);
- GFX->drawPrimitive(GFXLineStrip,0,index);
- GFX->popWorldMatrix();
- }
- //-----------------------------------------------------------------------------
- F32 CameraSpline::advanceTime(F32 t, S32 delta_ms)
- {
- buildTimeMap();
- Knot k;
- value(t, &k, false);
- F32 dist = getDistance(t) + k.mSpeed * (F32(delta_ms) / 1000.0f);
- return getTime(dist);
- }
- F32 CameraSpline::advanceDist(F32 t, F32 meters)
- {
- buildTimeMap();
- F32 dist = getDistance(t) + meters;
- return getTime(dist);
- }
- F32 CameraSpline::getDistance(F32 t)
- {
- if (mSize <= 1)
- return 0;
- // Find the nodes spanning the time
- Vector<TimeMap>::iterator end = mTimeMap.begin() + 1, start;
- for (; end < (mTimeMap.end() - 1) && end->mTime < t; end++) { }
- start = end - 1;
- // Interpolate between the two nodes
- F32 i = (t - start->mTime) / (end->mTime - start->mTime);
- return start->mDistance + (end->mDistance - start->mDistance) * i;
- }
- F32 CameraSpline::getTime(F32 d)
- {
- if (mSize <= 1)
- return 0;
- // Find nodes spanning the distance
- Vector<TimeMap>::iterator end = mTimeMap.begin() + 1, start;
- for (; end < (mTimeMap.end() - 1) && end->mDistance < d; end++) { }
- start = end - 1;
- // Check for duplicate points..
- F32 seg = end->mDistance - start->mDistance;
- if (!seg)
- return end->mTime;
- // Interpolate between the two nodes
- F32 i = (d - start->mDistance) / (end->mDistance - start->mDistance);
- return start->mTime + (end->mTime - start->mTime) * i;
- }
- //-----------------------------------------------------------------------------
- void CameraSpline::value(F32 t, CameraSpline::Knot *result, bool skip_rotation)
- {
- // Do some easing in and out for t.
- if(!gBuilding)
- {
- F32 oldT = t;
- if(oldT < 0.5f)
- {
- t = 0.5f - (mSin( (0.5 - oldT) * M_PI ) / 2.f);
- }
- if((F32(size()) - 1.5f) > 0.f && oldT - (F32(size()) - 1.5f) > 0.f)
- {
- oldT -= (F32(size()) - 1.5f);
- t = (F32(size()) - 1.5f) + (mCos( (0.5f - oldT) * F32(M_PI) ) / 2.f);
- }
- }
- // Verify that t is in range [0 >= t > size]
- // AssertFatal(t >= 0.0f && t < (F32)size(), "t out of range");
- Knot *p1 = getKnot((S32)mFloor(t));
- Knot *p2 = next(p1);
- F32 i = t - mFloor(t); // adjust t to 0 to 1 on p1-p2 interval
- if (p1->mPath == Knot::SPLINE)
- {
- Knot *p0 = (p1->mType == Knot::KINK) ? p1 : prev(p1);
- Knot *p3 = (p2->mType == Knot::KINK) ? p2 : next(p2);
- result->mPosition.x = mCatmullrom(i, p0->mPosition.x, p1->mPosition.x, p2->mPosition.x, p3->mPosition.x);
- result->mPosition.y = mCatmullrom(i, p0->mPosition.y, p1->mPosition.y, p2->mPosition.y, p3->mPosition.y);
- result->mPosition.z = mCatmullrom(i, p0->mPosition.z, p1->mPosition.z, p2->mPosition.z, p3->mPosition.z);
- }
- else
- { // Linear
- result->mPosition.interpolate(p1->mPosition, p2->mPosition, i);
- }
- if (skip_rotation)
- return;
- buildTimeMap();
- // find the two knots to interpolate rotation and velocity through since some
- // knots are only positional
- S32 start = (S32)mFloor(t);
- S32 end = (p2 == p1) ? start : (start + 1);
- while (p1->mType == Knot::POSITION_ONLY && p1 != front())
- {
- p1 = prev(p1);
- start--;
- }
- while (p2->mType == Knot::POSITION_ONLY && p2 != back())
- {
- p2 = next(p2);
- end++;
- }
- if (start == end)
- {
- result->mRotation = p1->mRotation;
- result->mSpeed = p1->mSpeed;
- }
- else
- {
-
- F32 c = getDistance(t);
- F32 d1 = getDistance((F32)start);
- F32 d2 = getDistance((F32)end);
-
- if (d1 == d2)
- {
- result->mRotation = p2->mRotation;
- result->mSpeed = p2->mSpeed;
- }
- else
- {
- i = (c-d1)/(d2-d1);
- if(p1->mPath == Knot::SPLINE)
- {
- Knot *p0 = (p1->mType == Knot::KINK) ? p1 : prev(p1);
- Knot *p3 = (p2->mType == Knot::KINK) ? p2 : next(p2);
- F32 q,w,e;
- q = mCatmullrom(i, 0, 1, 1, 1);
- w = mCatmullrom(i, 0, 0, 0, 1);
- e = mCatmullrom(i, 0, 0, 1, 1);
- QuatF a; a.interpolate(p0->mRotation, p1->mRotation, q);
- QuatF b; b.interpolate(p2->mRotation, p3->mRotation, w);
-
- result->mRotation.interpolate(a, b, e);
- result->mSpeed = mCatmullrom(i, p0->mSpeed, p1->mSpeed, p2->mSpeed, p3->mSpeed);
- }
- else
- {
- result->mRotation.interpolate(p1->mRotation, p2->mRotation, i);
- result->mSpeed = (p1->mSpeed * (1.0f-i)) + (p2->mSpeed * i);
- }
- }
- }
- }
|