cameraSpline.cpp
Engine/source/T3D/cameraSpline.cpp
Public Variables
bool
Detailed Description
Public Variables
bool gBuilding
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24 25#include "T3D/cameraSpline.h" 26 27#include "console/console.h" 28#include "gfx/gfxDevice.h" 29 30 31//----------------------------------------------------------------------------- 32 33CameraSpline::Knot::Knot() 34{ 35 mPosition = Point3F::Zero; 36 mRotation = QuatF::Identity; 37 mSpeed = 0.0f; 38 mType = NORMAL; 39 mPath = SPLINE; 40 mDistance = 0.0f; 41 prev = NULL; next = NULL; 42}; 43 44CameraSpline::Knot::Knot(const Knot &k) 45{ 46 mPosition = k.mPosition; 47 mRotation = k.mRotation; 48 mSpeed = k.mSpeed; 49 mType = k.mType; 50 mPath = k.mPath; 51 mDistance = k.mDistance; 52 prev = NULL; next = NULL; 53} 54 55CameraSpline::Knot::Knot(const Point3F &p, const QuatF &r, F32 s, Knot::Type type, Knot::Path path) 56{ 57 mPosition = p; 58 mRotation = r; 59 mSpeed = s; 60 mType = type; 61 mPath = path; 62 mDistance = 0.0f; 63 prev = NULL; next = NULL; 64} 65 66 67//----------------------------------------------------------------------------- 68 69CameraSpline::CameraSpline() 70{ 71 mFront = NULL; 72 mSize = 0; 73 mIsMapDirty = true; 74 VECTOR_SET_ASSOCIATION(mTimeMap); 75} 76 77 78CameraSpline::~CameraSpline() 79{ 80 removeAll(); 81} 82 83 84void CameraSpline::push_back(Knot *w) 85{ 86 if (!mFront) 87 { 88 mFront = w; 89 w->next = w; 90 w->prev = w; 91 } 92 else 93 { 94 Knot *before = back(); 95 Knot *after = before->next; 96 97 w->next = before->next; 98 w->prev = before; 99 after->prev = w; 100 before->next = w; 101 } 102 ++mSize; 103 mIsMapDirty = true; 104} 105 106CameraSpline::Knot* CameraSpline::getKnot(S32 i) 107{ 108 Knot *k = mFront; 109 while(i--) 110 k = k->next; 111 return k; 112} 113 114CameraSpline::Knot* CameraSpline::remove(Knot *w) 115{ 116 if (w->next == mFront && w->prev == mFront) 117 mFront = NULL; 118 else 119 { 120 w->prev->next = w->next; 121 w->next->prev = w->prev; 122 if (mFront == w) 123 mFront = w->next; 124 } 125 --mSize; 126 mIsMapDirty = true; 127 return w; 128} 129 130 131void CameraSpline::removeAll() 132{ 133 while(front()) 134 delete remove(front()); 135 mSize = 0; 136} 137 138 139//----------------------------------------------------------------------------- 140 141static bool gBuilding = false; 142 143void CameraSpline::buildTimeMap() 144{ 145 if (!mIsMapDirty) 146 return; 147 148 gBuilding = true; 149 150 mTimeMap.clear(); 151 mTimeMap.reserve(size()*3); // preallocate 152 153 // Initial node and knot value.. 154 TimeMap map; 155 map.mTime = 0; 156 map.mDistance = 0; 157 mTimeMap.push_back(map); 158 159 Knot ka,kj,ki; 160 value(0, &kj, true); 161 F32 length = 0.0f; 162 ka = kj; 163 164 // Loop through the knots and add nodes. Nodes are added for every knot and 165 // whenever the spline length and segment length deviate by epsilon. 166 F32 epsilon = Con::getFloatVariable("CameraSpline::epsilon", 0.90f); 167 const F32 Step = 0.05f; 168 F32 lt = 0,time = 0; 169 do 170 { 171 if ((time += Step) > F32(mSize - 1)) 172 time = (F32)mSize - 1.0f; 173 174 value(time, &ki, true); 175 length += (ki.mPosition - kj.mPosition).len(); 176 F32 segment = (ki.mPosition - ka.mPosition).len(); 177 178 if ((segment / length) < epsilon || time == (mSize - 1) || mFloor(lt) != mFloor(time)) 179 { 180 map.mTime = time; 181 map.mDistance = length; 182 mTimeMap.push_back(map); 183 ka = ki; 184 } 185 kj = ki; 186 lt = time; 187 } 188 while (time < mSize - 1); 189 190 mIsMapDirty = false; 191 192 gBuilding = false; 193} 194 195 196//----------------------------------------------------------------------------- 197 198void CameraSpline::renderTimeMap() 199{ 200 buildTimeMap(); 201 202 gBuilding = true; 203 204 // Build vertex buffer 205 GFXVertexBufferHandle<GFXVertexPCT> vb; 206 vb.set(GFX, mTimeMap.size(), GFXBufferTypeVolatile); 207 void *ptr = vb.lock(); 208 if(!ptr) return; 209 210 MRandomLCG random(1376312589 * (uintptr_t)this); 211 S32 index = 0; 212 for(Vector<TimeMap>::iterator itr=mTimeMap.begin(); itr != mTimeMap.end(); itr++) 213 { 214 Knot a; 215 value(itr->mTime, &a, true); 216 217 S32 cr = random.randI(0,255); 218 S32 cg = random.randI(0,255); 219 S32 cb = random.randI(0,255); 220 vb[index].color.set(cr, cg, cb); 221 vb[index].point.set(a.mPosition.x, a.mPosition.y, a.mPosition.z); 222 index++; 223 } 224 225 gBuilding = false; 226 227 vb.unlock(); 228 229 // Render the buffer 230 GFX->pushWorldMatrix(); 231 GFX->setupGenericShaders(); 232 GFX->setVertexBuffer(vb); 233 GFX->drawPrimitive(GFXLineStrip,0,index); 234 GFX->popWorldMatrix(); 235} 236 237 238//----------------------------------------------------------------------------- 239 240F32 CameraSpline::advanceTime(F32 t, S32 delta_ms) 241{ 242 buildTimeMap(); 243 Knot k; 244 value(t, &k, false); 245 F32 dist = getDistance(t) + k.mSpeed * (F32(delta_ms) / 1000.0f); 246 return getTime(dist); 247} 248 249 250F32 CameraSpline::advanceDist(F32 t, F32 meters) 251{ 252 buildTimeMap(); 253 F32 dist = getDistance(t) + meters; 254 return getTime(dist); 255} 256 257 258F32 CameraSpline::getDistance(F32 t) 259{ 260 if (mSize <= 1) 261 return 0; 262 263 // Find the nodes spanning the time 264 Vector<TimeMap>::iterator end = mTimeMap.begin() + 1, start; 265 for (; end < (mTimeMap.end() - 1) && end->mTime < t; end++) { } 266 start = end - 1; 267 268 // Interpolate between the two nodes 269 F32 i = (t - start->mTime) / (end->mTime - start->mTime); 270 return start->mDistance + (end->mDistance - start->mDistance) * i; 271} 272 273 274F32 CameraSpline::getTime(F32 d) 275{ 276 if (mSize <= 1) 277 return 0; 278 279 // Find nodes spanning the distance 280 Vector<TimeMap>::iterator end = mTimeMap.begin() + 1, start; 281 for (; end < (mTimeMap.end() - 1) && end->mDistance < d; end++) { } 282 start = end - 1; 283 284 // Check for duplicate points.. 285 F32 seg = end->mDistance - start->mDistance; 286 if (!seg) 287 return end->mTime; 288 289 // Interpolate between the two nodes 290 F32 i = (d - start->mDistance) / (end->mDistance - start->mDistance); 291 return start->mTime + (end->mTime - start->mTime) * i; 292} 293 294 295//----------------------------------------------------------------------------- 296void CameraSpline::value(F32 t, CameraSpline::Knot *result, bool skip_rotation) 297{ 298 // Do some easing in and out for t. 299 if(!gBuilding) 300 { 301 F32 oldT = t; 302 if(oldT < 0.5f) 303 { 304 t = 0.5f - (mSin( (0.5 - oldT) * M_PI ) / 2.f); 305 } 306 307 if((F32(size()) - 1.5f) > 0.f && oldT - (F32(size()) - 1.5f) > 0.f) 308 { 309 oldT -= (F32(size()) - 1.5f); 310 t = (F32(size()) - 1.5f) + (mCos( (0.5f - oldT) * F32(M_PI) ) / 2.f); 311 } 312 } 313 314 // Verify that t is in range [0 >= t > size] 315// AssertFatal(t >= 0.0f && t < (F32)size(), "t out of range"); 316 Knot *p1 = getKnot((S32)mFloor(t)); 317 Knot *p2 = next(p1); 318 319 F32 i = t - mFloor(t); // adjust t to 0 to 1 on p1-p2 interval 320 321 if (p1->mPath == Knot::SPLINE) 322 { 323 Knot *p0 = (p1->mType == Knot::KINK) ? p1 : prev(p1); 324 Knot *p3 = (p2->mType == Knot::KINK) ? p2 : next(p2); 325 result->mPosition.x = mCatmullrom(i, p0->mPosition.x, p1->mPosition.x, p2->mPosition.x, p3->mPosition.x); 326 result->mPosition.y = mCatmullrom(i, p0->mPosition.y, p1->mPosition.y, p2->mPosition.y, p3->mPosition.y); 327 result->mPosition.z = mCatmullrom(i, p0->mPosition.z, p1->mPosition.z, p2->mPosition.z, p3->mPosition.z); 328 } 329 else 330 { // Linear 331 result->mPosition.interpolate(p1->mPosition, p2->mPosition, i); 332 } 333 334 if (skip_rotation) 335 return; 336 337 buildTimeMap(); 338 339 // find the two knots to interpolate rotation and velocity through since some 340 // knots are only positional 341 S32 start = (S32)mFloor(t); 342 S32 end = (p2 == p1) ? start : (start + 1); 343 while (p1->mType == Knot::POSITION_ONLY && p1 != front()) 344 { 345 p1 = prev(p1); 346 start--; 347 } 348 349 while (p2->mType == Knot::POSITION_ONLY && p2 != back()) 350 { 351 p2 = next(p2); 352 end++; 353 } 354 355 if (start == end) 356 { 357 result->mRotation = p1->mRotation; 358 result->mSpeed = p1->mSpeed; 359 } 360 else 361 { 362 363 F32 c = getDistance(t); 364 F32 d1 = getDistance((F32)start); 365 F32 d2 = getDistance((F32)end); 366 367 if (d1 == d2) 368 { 369 result->mRotation = p2->mRotation; 370 result->mSpeed = p2->mSpeed; 371 } 372 else 373 { 374 i = (c-d1)/(d2-d1); 375 376 if(p1->mPath == Knot::SPLINE) 377 { 378 Knot *p0 = (p1->mType == Knot::KINK) ? p1 : prev(p1); 379 Knot *p3 = (p2->mType == Knot::KINK) ? p2 : next(p2); 380 381 F32 q,w,e; 382 q = mCatmullrom(i, 0, 1, 1, 1); 383 w = mCatmullrom(i, 0, 0, 0, 1); 384 e = mCatmullrom(i, 0, 0, 1, 1); 385 386 QuatF a; a.interpolate(p0->mRotation, p1->mRotation, q); 387 QuatF b; b.interpolate(p2->mRotation, p3->mRotation, w); 388 389 result->mRotation.interpolate(a, b, e); 390 result->mSpeed = mCatmullrom(i, p0->mSpeed, p1->mSpeed, p2->mSpeed, p3->mSpeed); 391 } 392 else 393 { 394 result->mRotation.interpolate(p1->mRotation, p2->mRotation, i); 395 result->mSpeed = (p1->mSpeed * (1.0f-i)) + (p2->mSpeed * i); 396 } 397 } 398 } 399} 400 401 402 403