Merge commit '830125fad042fad35dc029b6eb57c8156ad7e176'
[xonotic/netradiant.git] / libs / splines / splines.cpp
1 /*
2    Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "q_shared.h"
23 #include "splines.h"
24
25 extern "C" {
26 int FS_Write( const void *buffer, int len, fileHandle_t h );
27 int FS_ReadFile( const char *qpath, void **buffer );
28 void FS_FreeFile( void *buffer );
29 fileHandle_t FS_FOpenFileWrite( const char *filename );
30 void FS_FCloseFile( fileHandle_t f );
31 void Cbuf_AddText( const char *text );
32 void Cbuf_Execute( void );
33 }
34
35 float Q_fabs( float f ) {
36         int tmp = *( int * ) &f;
37         tmp &= 0x7FFFFFFF;
38         return *( float * ) &tmp;
39 }
40
41 // (SA) making a list of cameras so I can use
42 //              the splines as targets for other things.
43 //              Certainly better ways to do this, but this lets
44 //              me get underway quickly with ents that need spline
45 //              targets.
46 #define MAX_CAMERAS 64
47
48 idCameraDef camera[MAX_CAMERAS];
49
50 extern "C" {
51 qboolean loadCamera( int camNum, const char *name ) {
52         if ( camNum < 0 || camNum >= MAX_CAMERAS ) {
53                 return qfalse;
54         }
55         camera[camNum].clear();
56         return (qboolean)camera[camNum].load( name );
57 }
58
59 qboolean getCameraInfo( int camNum, int time, float *origin, float *angles, float *fov ) {
60         idVec3 dir, org;
61         if ( camNum < 0 || camNum >= MAX_CAMERAS ) {
62                 return qfalse;
63         }
64         org[0] = origin[0];
65         org[1] = origin[1];
66         org[2] = origin[2];
67         if ( camera[camNum].getCameraInfo( time, org, dir, fov ) ) {
68                 origin[0] = org[0];
69                 origin[1] = org[1];
70                 origin[2] = org[2];
71                 angles[1] = atan2( dir[1], dir[0] ) * 180 / 3.14159;
72                 angles[0] = asin( dir[2] ) * 180 / 3.14159;
73                 return qtrue;
74         }
75         return qfalse;
76 }
77
78 void startCamera( int camNum, int time ) {
79         if ( camNum < 0 || camNum >= MAX_CAMERAS ) {
80                 return;
81         }
82         camera[camNum].startCamera( time );
83 }
84
85 }
86
87
88 //#include "../shared/windings.h"
89 //#include "../qcommon/qcommon.h"
90 //#include "../sys/sys_public.h"
91 //#include "../game/game_entity.h"
92
93 idCameraDef splineList;
94 idCameraDef *g_splineList = &splineList;
95
96 idVec3 idSplineList::zero( 0,0,0 );
97
98 void glLabeledPoint( idVec3 &color, idVec3 &point, float size, const char *label ) {
99         glColor3fv( color );
100         glPointSize( size );
101         glBegin( GL_POINTS );
102         glVertex3fv( point );
103         glEnd();
104         idVec3 v = point;
105         v.x += 1;
106         v.y += 1;
107         v.z += 1;
108         glRasterPos3fv( v );
109         glCallLists( strlen( label ), GL_UNSIGNED_BYTE, label );
110 }
111
112
113 void glBox( idVec3 &color, idVec3 &point, float size ) {
114         idVec3 mins( point );
115         idVec3 maxs( point );
116         mins[0] -= size;
117         mins[1] += size;
118         mins[2] -= size;
119         maxs[0] += size;
120         maxs[1] -= size;
121         maxs[2] += size;
122         glColor3fv( color );
123         glBegin( GL_LINE_LOOP );
124         glVertex3f( mins[0],mins[1],mins[2] );
125         glVertex3f( maxs[0],mins[1],mins[2] );
126         glVertex3f( maxs[0],maxs[1],mins[2] );
127         glVertex3f( mins[0],maxs[1],mins[2] );
128         glEnd();
129         glBegin( GL_LINE_LOOP );
130         glVertex3f( mins[0],mins[1],maxs[2] );
131         glVertex3f( maxs[0],mins[1],maxs[2] );
132         glVertex3f( maxs[0],maxs[1],maxs[2] );
133         glVertex3f( mins[0],maxs[1],maxs[2] );
134         glEnd();
135
136         glBegin( GL_LINES );
137         glVertex3f( mins[0],mins[1],mins[2] );
138         glVertex3f( mins[0],mins[1],maxs[2] );
139         glVertex3f( mins[0],maxs[1],maxs[2] );
140         glVertex3f( mins[0],maxs[1],mins[2] );
141         glVertex3f( maxs[0],mins[1],mins[2] );
142         glVertex3f( maxs[0],mins[1],maxs[2] );
143         glVertex3f( maxs[0],maxs[1],maxs[2] );
144         glVertex3f( maxs[0],maxs[1],mins[2] );
145         glEnd();
146
147 }
148
149 void splineTest() {
150         //g_splineList->load("p:/doom/base/maps/test_base1.camera");
151 }
152
153 void splineDraw() {
154         //g_splineList->addToRenderer();
155 }
156
157
158 //extern void D_DebugLine( const idVec3 &color, const idVec3 &start, const idVec3 &end );
159
160 void debugLine( idVec3 &color, float x, float y, float z, float x2, float y2, float z2 ) {
161         idVec3 from( x, y, z );
162         idVec3 to( x2, y2, z2 );
163         //D_DebugLine(color, from, to);
164 }
165
166 void idSplineList::addToRenderer() {
167
168         if ( controlPoints.Num() == 0 ) {
169                 return;
170         }
171
172         idVec3 mins, maxs;
173         idVec3 yellow( 1.0, 1.0, 0 );
174         idVec3 white( 1.0, 1.0, 1.0 );
175         int i;
176
177         for ( i = 0; i < controlPoints.Num(); i++ ) {
178                 VectorCopy( *controlPoints[i], mins );
179                 VectorCopy( mins, maxs );
180                 mins[0] -= 8;
181                 mins[1] += 8;
182                 mins[2] -= 8;
183                 maxs[0] += 8;
184                 maxs[1] -= 8;
185                 maxs[2] += 8;
186                 debugLine( yellow, mins[0], mins[1], mins[2], maxs[0], mins[1], mins[2] );
187                 debugLine( yellow, maxs[0], mins[1], mins[2], maxs[0], maxs[1], mins[2] );
188                 debugLine( yellow, maxs[0], maxs[1], mins[2], mins[0], maxs[1], mins[2] );
189                 debugLine( yellow, mins[0], maxs[1], mins[2], mins[0], mins[1], mins[2] );
190
191                 debugLine( yellow, mins[0], mins[1], maxs[2], maxs[0], mins[1], maxs[2] );
192                 debugLine( yellow, maxs[0], mins[1], maxs[2], maxs[0], maxs[1], maxs[2] );
193                 debugLine( yellow, maxs[0], maxs[1], maxs[2], mins[0], maxs[1], maxs[2] );
194                 debugLine( yellow, mins[0], maxs[1], maxs[2], mins[0], mins[1], maxs[2] );
195
196         }
197
198         int step = 0;
199         idVec3 step1;
200         for ( i = 3; i < controlPoints.Num(); i++ ) {
201                 for ( float tension = 0.0f; tension < 1.001f; tension += 0.1f ) {
202                         float x = 0;
203                         float y = 0;
204                         float z = 0;
205                         for ( int j = 0; j < 4; j++ ) {
206                                 x += controlPoints[i - ( 3 - j )]->x * calcSpline( j, tension );
207                                 y += controlPoints[i - ( 3 - j )]->y * calcSpline( j, tension );
208                                 z += controlPoints[i - ( 3 - j )]->z * calcSpline( j, tension );
209                         }
210                         if ( step == 0 ) {
211                                 step1[0] = x;
212                                 step1[1] = y;
213                                 step1[2] = z;
214                                 step = 1;
215                         }
216                         else {
217                                 debugLine( white, step1[0], step1[1], step1[2], x, y, z );
218                                 step = 0;
219                         }
220
221                 }
222         }
223 }
224
225 void idSplineList::buildSpline() {
226         //int start = Sys_Milliseconds();
227         clearSpline();
228         for ( int i = 3; i < controlPoints.Num(); i++ ) {
229                 for ( float tension = 0.0f; tension < 1.001f; tension += granularity ) {
230                         float x = 0;
231                         float y = 0;
232                         float z = 0;
233                         for ( int j = 0; j < 4; j++ ) {
234                                 x += controlPoints[i - ( 3 - j )]->x * calcSpline( j, tension );
235                                 y += controlPoints[i - ( 3 - j )]->y * calcSpline( j, tension );
236                                 z += controlPoints[i - ( 3 - j )]->z * calcSpline( j, tension );
237                         }
238                         splinePoints.Append( new idVec3( x, y, z ) );
239                 }
240         }
241         dirty = false;
242         //Com_Printf("Spline build took %f seconds\n", (float)(Sys_Milliseconds() - start) / 1000);
243 }
244
245
246 void idSplineList::draw( bool editMode ) {
247         int i;
248         idVec4 yellow( 1, 1, 0, 1 );
249
250         if ( controlPoints.Num() == 0 ) {
251                 return;
252         }
253
254         if ( dirty ) {
255                 buildSpline();
256         }
257
258
259         glColor3fv( controlColor );
260         glPointSize( 5 );
261
262         glBegin( GL_POINTS );
263         for ( i = 0; i < controlPoints.Num(); i++ ) {
264                 glVertex3fv( *controlPoints[i] );
265         }
266         glEnd();
267
268         if ( editMode ) {
269                 for ( i = 0; i < controlPoints.Num(); i++ ) {
270                         glBox( activeColor, *controlPoints[i], 4 );
271                 }
272         }
273
274         //Draw the curve
275         glColor3fv( pathColor );
276         glBegin( GL_LINE_STRIP );
277         int count = splinePoints.Num();
278         for ( i = 0; i < count; i++ ) {
279                 glVertex3fv( *splinePoints[i] );
280         }
281         glEnd();
282
283         if ( editMode ) {
284                 glColor3fv( segmentColor );
285                 glPointSize( 3 );
286                 glBegin( GL_POINTS );
287                 for ( i = 0; i < count; i++ ) {
288                         glVertex3fv( *splinePoints[i] );
289                 }
290                 glEnd();
291         }
292         if ( count > 0 ) {
293                 //assert(activeSegment >=0 && activeSegment < count);
294                 if ( activeSegment >= 0 && activeSegment < count ) {
295                         glBox( activeColor, *splinePoints[activeSegment], 6 );
296                         glBox( yellow, *splinePoints[activeSegment], 8 );
297                 }
298         }
299
300 }
301
302 float idSplineList::totalDistance() {
303
304         // FIXME: save dist and return
305         //
306         if ( controlPoints.Num() == 0 ) {
307                 return 0.0;
308         }
309
310         if ( dirty ) {
311                 buildSpline();
312         }
313
314         float dist = 0.0;
315         idVec3 temp;
316         int count = splinePoints.Num();
317         for ( int i = 1; i < count; i++ ) {
318                 temp = *splinePoints[i - 1];
319                 temp -= *splinePoints[i];
320                 dist += temp.Length();
321         }
322         return dist;
323 }
324
325 void idSplineList::initPosition( long bt, long totalTime ) {
326
327         if ( dirty ) {
328                 buildSpline();
329         }
330
331         if ( splinePoints.Num() == 0 ) {
332                 return;
333         }
334
335         baseTime = bt;
336         time = totalTime;
337
338         // calc distance to travel ( this will soon be broken into time segments )
339         splineTime.Clear();
340         splineTime.Append( bt );
341         double dist = totalDistance();
342         double distSoFar = 0.0;
343         idVec3 temp;
344         int count = splinePoints.Num();
345         //for(int i = 2; i < count - 1; i++) {
346         for ( int i = 1; i < count; i++ ) {
347                 temp = *splinePoints[i - 1];
348                 temp -= *splinePoints[i];
349                 distSoFar += temp.Length();
350                 double percent = distSoFar / dist;
351                 percent *= totalTime;
352                 splineTime.Append( percent + bt );
353         }
354         assert( splineTime.Num() == splinePoints.Num() );
355         activeSegment = 0;
356 }
357
358
359
360 float idSplineList::calcSpline( int step, float tension ) {
361         switch ( step ) {
362         case 0: return ( pow( 1 - tension, 3 ) ) / 6;
363         case 1: return ( 3 * pow( tension, 3 ) - 6 * pow( tension, 2 ) + 4 ) / 6;
364         case 2: return ( -3 * pow( tension, 3 ) + 3 * pow( tension, 2 ) + 3 * tension + 1 ) / 6;
365         case 3: return pow( tension, 3 ) / 6;
366         }
367         return 0.0;
368 }
369
370
371
372 void idSplineList::updateSelection( const idVec3 &move ) {
373         if ( selected ) {
374                 dirty = true;
375                 VectorAdd( *selected, move, *selected );
376         }
377 }
378
379
380 void idSplineList::setSelectedPoint( idVec3 *p ) {
381         if ( p ) {
382                 p->Snap();
383                 for ( int i = 0; i < controlPoints.Num(); i++ ) {
384                         if ( *p == *controlPoints[i] ) {
385                                 selected = controlPoints[i];
386                         }
387                 }
388         }
389         else {
390                 selected = NULL;
391         }
392 }
393
394 const idVec3 *idSplineList::getPosition( long t ) {
395         static idVec3 interpolatedPos;
396         static long lastTime = -1;
397
398         int count = splineTime.Num();
399         if ( count == 0 ) {
400                 return &zero;
401         }
402
403 //      Com_Printf("Time: %d\n", t);
404         assert( splineTime.Num() == splinePoints.Num() );
405
406         while ( activeSegment < count ) {
407                 if ( splineTime[activeSegment] >= t ) {
408                         if ( activeSegment > 0 && activeSegment < count - 1 ) {
409                                 double timeHi = splineTime[activeSegment + 1];
410                                 double timeLo = splineTime[activeSegment - 1];
411                                 double percent = ( timeHi - t ) / ( timeHi - timeLo );
412                                 // pick two bounding points
413                                 idVec3 v1 = *splinePoints[activeSegment - 1];
414                                 idVec3 v2 = *splinePoints[activeSegment + 1];
415                                 v2 *= ( 1.0 - percent );
416                                 v1 *= percent;
417                                 v2 += v1;
418                                 interpolatedPos = v2;
419                                 return &interpolatedPos;
420                         }
421                         return splinePoints[activeSegment];
422                 }
423                 else {
424                         activeSegment++;
425                 }
426         }
427         return splinePoints[count - 1];
428 }
429
430 void idSplineList::parse( const char *( *text )  ) {
431         const char *token;
432         //Com_MatchToken( text, "{" );
433         do {
434                 token = Com_Parse( text );
435
436                 if ( !token[0] ) {
437                         break;
438                 }
439                 if ( !Q_stricmp( token, "}" ) ) {
440                         break;
441                 }
442
443                 do {
444                         // if token is not a brace, it is a key for a key/value pair
445                         if ( !token[0] || !Q_stricmp( token, "(" ) || !Q_stricmp( token, "}" ) ) {
446                                 break;
447                         }
448
449                         Com_UngetToken();
450                         idStr key = Com_ParseOnLine( text );
451                         const char *token = Com_Parse( text );
452                         if ( Q_stricmp( key.c_str(), "granularity" ) == 0 ) {
453                                 granularity = atof( token );
454                         }
455                         else if ( Q_stricmp( key.c_str(), "name" ) == 0 ) {
456                                 name = token;
457                         }
458                         token = Com_Parse( text );
459
460                 } while ( 1 );
461
462                 if ( !Q_stricmp( token, "}" ) ) {
463                         break;
464                 }
465
466                 Com_UngetToken();
467                 // read the control point
468                 idVec3 point;
469                 Com_Parse1DMatrix( text, 3, point );
470                 addPoint( point.x, point.y, point.z );
471         } while ( 1 );
472
473         //Com_UngetToken();
474         //Com_MatchToken( text, "}" );
475         dirty = true;
476 }
477
478 void idSplineList::write( fileHandle_t file, const char *p ) {
479         idStr s = va( "\t\t%s {\n", p );
480         FS_Write( s.c_str(), s.length(), file );
481         //s = va("\t\tname %s\n", name.c_str());
482         //FS_Write(s.c_str(), s.length(), file);
483         s = va( "\t\t\tgranularity %f\n", granularity );
484         FS_Write( s.c_str(), s.length(), file );
485         int count = controlPoints.Num();
486         for ( int i = 0; i < count; i++ ) {
487                 s = va( "\t\t\t( %f %f %f )\n", controlPoints[i]->x, controlPoints[i]->y, controlPoints[i]->z );
488                 FS_Write( s.c_str(), s.length(), file );
489         }
490         s = "\t\t}\n";
491         FS_Write( s.c_str(), s.length(), file );
492 }
493
494
495 void idCameraDef::getActiveSegmentInfo( int segment, idVec3 &origin, idVec3 &direction, float *fov ) {
496 #if 0
497         if ( !cameraSpline.validTime() ) {
498                 buildCamera();
499         }
500         double d = (double)segment / numSegments();
501         getCameraInfo( d * totalTime * 1000, origin, direction, fov );
502 #endif
503 /*
504     if (!cameraSpline.validTime()) {
505         buildCamera();
506     }
507     origin = *cameraSpline.getSegmentPoint(segment);
508
509
510     idVec3 temp;
511
512     int numTargets = getTargetSpline()->controlPoints.Num();
513     int count = cameraSpline.splineTime.Num();
514     if (numTargets == 0) {
515         // follow the path
516         if (cameraSpline.getActiveSegment() < count - 1) {
517             temp = *cameraSpline.splinePoints[cameraSpline.getActiveSegment()+1];
518         }
519     } else if (numTargets == 1) {
520         temp = *getTargetSpline()->controlPoints[0];
521     } else {
522         temp = *getTargetSpline()->getSegmentPoint(segment);
523     }
524
525     temp -= origin;
526     temp.Normalize();
527     direction = temp;
528  */
529 }
530
531 bool idCameraDef::getCameraInfo( long time, idVec3 &origin, idVec3 &direction, float *fv ) {
532
533         char buff[1024];
534
535         if ( ( time - startTime ) / 1000 > totalTime ) {
536                 return false;
537         }
538
539
540         for ( int i = 0; i < events.Num(); i++ ) {
541                 if ( time >= startTime + events[i]->getTime() && !events[i]->getTriggered() ) {
542                         events[i]->setTriggered( true );
543                         if ( events[i]->getType() == idCameraEvent::EVENT_TARGET ) {
544                                 setActiveTargetByName( events[i]->getParam() );
545                                 getActiveTarget()->start( startTime + events[i]->getTime() );
546                                 //Com_Printf("Triggered event switch to target: %s\n",events[i]->getParam());
547                         }
548                         else if ( events[i]->getType() == idCameraEvent::EVENT_TRIGGER ) {
549                                 //idEntity *ent = NULL;
550                                 //ent = level.FindTarget( ent, events[i]->getParam());
551                                 //if (ent) {
552                                 //      ent->signal( SIG_TRIGGER );
553                                 //      ent->ProcessEvent( &EV_Activate, world );
554                                 //}
555                         }
556                         else if ( events[i]->getType() == idCameraEvent::EVENT_FOV ) {
557                                 memset( buff, 0, sizeof( buff ) );
558                                 strcpy( buff, events[i]->getParam() );
559                                 const char *param1 = strtok( buff, " \t,\0" );
560                                 const char *param2 = strtok( NULL, " \t,\0" );
561                                 float len = ( param2 ) ? atof( param2 ) : 0;
562                                 float newfov = ( param1 ) ? atof( param1 ) : 90;
563                                 fov.reset( fov.getFOV( time ), newfov, time, len );
564                                 //*fv = fov = atof(events[i]->getParam());
565                         }
566                         else if ( events[i]->getType() == idCameraEvent::EVENT_FADEIN ) {
567                                 float time = atof( events[i]->getParam() );
568                                 Cbuf_AddText( va( "fade 0 0 0 0 %f", time ) );
569                                 Cbuf_Execute();
570                         }
571                         else if ( events[i]->getType() == idCameraEvent::EVENT_FADEOUT ) {
572                                 float time = atof( events[i]->getParam() );
573                                 Cbuf_AddText( va( "fade 0 0 0 255 %f", time ) );
574                                 Cbuf_Execute();
575                         }
576                         else if ( events[i]->getType() == idCameraEvent::EVENT_CAMERA ) {
577                                 memset( buff, 0, sizeof( buff ) );
578                                 strcpy( buff, events[i]->getParam() );
579                                 const char *param1 = strtok( buff, " \t,\0" );
580                                 const char *param2 = strtok( NULL, " \t,\0" );
581
582                                 if ( param2 ) {
583                                         loadCamera( atoi( param1 ), va( "cameras/%s.camera", param2 ) );
584                                         startCamera( time );
585                                 }
586                                 else {
587                                         loadCamera( 0, va( "cameras/%s.camera", events[i]->getParam() ) );
588                                         startCamera( time );
589                                 }
590                                 return true;
591                         }
592                         else if ( events[i]->getType() == idCameraEvent::EVENT_STOP ) {
593                                 return false;
594                         }
595                 }
596         }
597
598         origin = *cameraPosition->getPosition( time );
599
600         *fv = fov.getFOV( time );
601
602         idVec3 temp = origin;
603
604         int numTargets = targetPositions.Num();
605         if ( numTargets == 0 ) {
606 /*
607         // follow the path
608         if (cameraSpline.getActiveSegment() < count - 1) {
609             temp = *cameraSpline.splinePoints[cameraSpline.getActiveSegment()+1];
610             if (temp == origin) {
611                 int index = cameraSpline.getActiveSegment() + 2;
612                 while (temp == origin && index < count - 1) {
613                     temp = *cameraSpline.splinePoints[index++];
614                 }
615             }
616         }
617  */
618         }
619         else {
620                 if ( getActiveTarget()->numPoints() > 0 ) {
621                         temp = *getActiveTarget()->getPosition( time );
622                 }
623         }
624
625         temp -= origin;
626         temp.Normalize();
627         direction = temp;
628
629         return true;
630 }
631
632 bool idCameraDef::waitEvent( int index ) {
633         //for (int i = 0; i < events.Num(); i++) {
634         //      if (events[i]->getSegment() == index && events[i]->getType() == idCameraEvent::EVENT_WAIT) {
635         //              return true;
636         //      }
637         //}
638         return false;
639 }
640
641
642 #define NUM_CCELERATION_SEGS 10
643 #define CELL_AMT 5
644
645 void idCameraDef::buildCamera() {
646         int i;
647         int lastSwitch = 0;
648         idList<float> waits;
649         idList<int> targets;
650
651         totalTime = baseTime;
652         cameraPosition->setTime( (long)totalTime * 1000 );
653         // we have a base time layout for the path and the target path
654         // now we need to layer on any wait or speed changes
655         for ( i = 0; i < events.Num(); i++ ) {
656                 idCameraEvent *ev = events[i];
657                 events[i]->setTriggered( false );
658                 switch ( events[i]->getType() ) {
659                 case idCameraEvent::EVENT_TARGET: {
660                         targets.Append( i );
661                         break;
662                 }
663                 case idCameraEvent::EVENT_FEATHER: {
664                         long startTime = 0;
665                         float speed = 0;
666                         long loopTime = 10;
667                         float stepGoal = cameraPosition->getBaseVelocity() / ( 1000 / loopTime );
668                         while ( startTime <= 1000 ) {
669                                 cameraPosition->addVelocity( startTime, loopTime, speed );
670                                 speed += stepGoal;
671                                 if ( speed > cameraPosition->getBaseVelocity() ) {
672                                         speed = cameraPosition->getBaseVelocity();
673                                 }
674                                 startTime += loopTime;
675                         }
676
677                         startTime = (long)( totalTime * 1000 - 1000 );
678                         long endTime = startTime + 1000;
679                         speed = cameraPosition->getBaseVelocity();
680                         while ( startTime < endTime ) {
681                                 speed -= stepGoal;
682                                 if ( speed < 0 ) {
683                                         speed = 0;
684                                 }
685                                 cameraPosition->addVelocity( startTime, loopTime, speed );
686                                 startTime += loopTime;
687                         }
688                         break;
689
690                 }
691                 case idCameraEvent::EVENT_WAIT: {
692                         waits.Append( atof( events[i]->getParam() ) );
693
694                         //FIXME: this is quite hacky for Wolf E3, accel and decel needs
695                         // do be parameter based etc..
696                         long startTime = events[i]->getTime() - 1000;
697                         if ( startTime < 0 ) {
698                                 startTime = 0;
699                         }
700                         float speed = cameraPosition->getBaseVelocity();
701                         long loopTime = 10;
702                         float steps = speed / ( ( events[i]->getTime() - startTime ) / loopTime );
703                         while ( startTime <= events[i]->getTime() - loopTime ) {
704                                 cameraPosition->addVelocity( startTime, loopTime, speed );
705                                 speed -= steps;
706                                 startTime += loopTime;
707                         }
708                         cameraPosition->addVelocity( events[i]->getTime(), (long)atof( events[i]->getParam() ) * 1000, 0 );
709
710                         startTime = (long)( events[i]->getTime() + atof( events[i]->getParam() ) * 1000 );
711                         long endTime = startTime + 1000;
712                         speed = 0;
713                         while ( startTime <= endTime ) {
714                                 cameraPosition->addVelocity( startTime, loopTime, speed );
715                                 speed += steps;
716                                 startTime += loopTime;
717                         }
718                         break;
719                 }
720                 case idCameraEvent::EVENT_TARGETWAIT: {
721                         //targetWaits.Append(i);
722                         break;
723                 }
724                 case idCameraEvent::EVENT_SPEED: {
725 /*
726                 // take the average delay between up to the next five segments
727                 float adjust = atof(events[i]->getParam());
728                 int index = events[i]->getSegment();
729                 total = 0;
730                 count = 0;
731
732                 // get total amount of time over the remainder of the segment
733                 for (j = index; j < cameraSpline.numSegments() - 1; j++) {
734                     total += cameraSpline.getSegmentTime(j + 1) - cameraSpline.getSegmentTime(j);
735                     count++;
736                 }
737
738                 // multiply that by the adjustment
739                 double newTotal = total * adjust;
740                 // what is the difference..
741                 newTotal -= total;
742                 totalTime += newTotal / 1000;
743
744                 // per segment difference
745                 newTotal /= count;
746                 int additive = newTotal;
747
748                 // now propogate that difference out to each segment
749                 for (j = index; j < cameraSpline.numSegments(); j++) {
750                     cameraSpline.addSegmentTime(j, additive);
751                     additive += newTotal;
752                 }
753                 break;
754  */
755                 }
756                 }
757         }
758
759
760         for ( i = 0; i < waits.Num(); i++ ) {
761                 totalTime += waits[i];
762         }
763
764         // on a new target switch, we need to take time to this point ( since last target switch )
765         // and allocate it across the active target, then reset time to this point
766         long timeSoFar = 0;
767         long total = (long)( totalTime * 1000 );
768         for ( i = 0; i < targets.Num(); i++ ) {
769                 long t;
770                 if ( i < targets.Num() - 1 ) {
771                         t = events[targets[i + 1]]->getTime();
772                 }
773                 else {
774                         t = total - timeSoFar;
775                 }
776                 // t is how much time to use for this target
777                 setActiveTargetByName( events[targets[i]]->getParam() );
778                 getActiveTarget()->setTime( t );
779                 timeSoFar += t;
780         }
781
782
783 }
784
785 void idCameraDef::startCamera( long t ) {
786         cameraPosition->clearVelocities();
787         cameraPosition->start( t );
788         buildCamera();
789         fov.reset( 90, 90, t, 0 );
790         //for (int i = 0; i < targetPositions.Num(); i++) {
791         //      targetPositions[i]->
792         //}
793         startTime = t;
794         cameraRunning = true;
795 }
796
797
798 void idCameraDef::parse( const char *( *text )  ) {
799
800         const char  *token;
801         do {
802                 token = Com_Parse( text );
803
804                 if ( !token[0] ) {
805                         break;
806                 }
807                 if ( !Q_stricmp( token, "}" ) ) {
808                         break;
809                 }
810
811                 if ( Q_stricmp( token, "time" ) == 0 ) {
812                         baseTime = Com_ParseFloat( text );
813                 }
814                 else if ( Q_stricmp( token, "camera_fixed" ) == 0 ) {
815                         cameraPosition = new idFixedPosition();
816                         cameraPosition->parse( text );
817                 }
818                 else if ( Q_stricmp( token, "camera_interpolated" ) == 0 ) {
819                         cameraPosition = new idInterpolatedPosition();
820                         cameraPosition->parse( text );
821                 }
822                 else if ( Q_stricmp( token, "camera_spline" ) == 0 ) {
823                         cameraPosition = new idSplinePosition();
824                         cameraPosition->parse( text );
825                 }
826                 else if ( Q_stricmp( token, "target_fixed" ) == 0 ) {
827                         idFixedPosition *pos = new idFixedPosition();
828                         pos->parse( text );
829                         targetPositions.Append( pos );
830                 }
831                 else if ( Q_stricmp( token, "target_interpolated" ) == 0 ) {
832                         idInterpolatedPosition *pos = new idInterpolatedPosition();
833                         pos->parse( text );
834                         targetPositions.Append( pos );
835                 }
836                 else if ( Q_stricmp( token, "target_spline" ) == 0 ) {
837                         idSplinePosition *pos = new idSplinePosition();
838                         pos->parse( text );
839                         targetPositions.Append( pos );
840                 }
841                 else if ( Q_stricmp( token, "fov" ) == 0 ) {
842                         fov.parse( text );
843                 }
844                 else if ( Q_stricmp( token, "event" ) == 0 ) {
845                         idCameraEvent *event = new idCameraEvent();
846                         event->parse( text );
847                         addEvent( event );
848                 }
849
850
851         } while ( 1 );
852
853         if ( !cameraPosition ) {
854                 Com_Printf( "no camera position specified\n" );
855                 // prevent a crash later on
856                 cameraPosition = new idFixedPosition();
857         }
858
859         Com_UngetToken();
860         Com_MatchToken( text, "}" );
861
862 }
863
864 bool idCameraDef::load( const char *filename ) {
865         char *buf;
866         const char *buf_p;
867         int length = FS_ReadFile( filename, (void **)&buf );
868         if ( !buf ) {
869                 return false;
870         }
871
872         clear();
873         Com_BeginParseSession( filename );
874         buf_p = buf;
875         parse( &buf_p );
876         Com_EndParseSession();
877         FS_FreeFile( buf );
878
879         return true;
880 }
881
882 void idCameraDef::save( const char *filename ) {
883         fileHandle_t file = FS_FOpenFileWrite( filename );
884         if ( file ) {
885                 int i;
886                 idStr s = "cameraPathDef { \n";
887                 FS_Write( s.c_str(), s.length(), file );
888                 s = va( "\ttime %f\n", baseTime );
889                 FS_Write( s.c_str(), s.length(), file );
890
891                 cameraPosition->write( file, va( "camera_%s",cameraPosition->typeStr() ) );
892
893                 for ( i = 0; i < numTargets(); i++ ) {
894                         targetPositions[i]->write( file, va( "target_%s", targetPositions[i]->typeStr() ) );
895                 }
896
897                 for ( i = 0; i < events.Num(); i++ ) {
898                         events[i]->write( file, "event" );
899                 }
900
901                 fov.write( file, "fov" );
902
903                 s = "}\n";
904                 FS_Write( s.c_str(), s.length(), file );
905         }
906         FS_FCloseFile( file );
907 }
908
909 int idCameraDef::sortEvents( const void *p1, const void *p2 ) {
910         idCameraEvent *ev1 = (idCameraEvent*)( p1 );
911         idCameraEvent *ev2 = (idCameraEvent*)( p2 );
912
913         if ( ev1->getTime() > ev2->getTime() ) {
914                 return -1;
915         }
916         if ( ev1->getTime() < ev2->getTime() ) {
917                 return 1;
918         }
919         return 0;
920 }
921
922 void idCameraDef::addEvent( idCameraEvent *event ) {
923         events.Append( event );
924         //events.Sort(&sortEvents);
925
926 }
927 void idCameraDef::addEvent( idCameraEvent::eventType t, const char *param, long time ) {
928         addEvent( new idCameraEvent( t, param, time ) );
929         buildCamera();
930 }
931
932 void idCameraDef::removeEvent( int index ) {
933         events.RemoveIndex( index );
934         buildCamera();
935 }
936
937
938 const char *idCameraEvent::eventStr[] = {
939         "NA",
940         "WAIT",
941         "TARGETWAIT",
942         "SPEED",
943         "TARGET",
944         "SNAPTARGET",
945         "FOV",
946         "CMD",
947         "TRIGGER",
948         "STOP",
949         "CAMERA",
950         "FADEOUT",
951         "FADEIN",
952         "FEATHER"
953 };
954
955 void idCameraEvent::parse( const char *( *text )  ) {
956         const char *token;
957         Com_MatchToken( text, "{" );
958         do {
959                 token = Com_Parse( text );
960
961                 if ( !token[0] ) {
962                         break;
963                 }
964                 if ( !strcmp( token, "}" ) ) {
965                         break;
966                 }
967
968                 // here we may have to jump over brush epairs ( only used in editor )
969                 do {
970                         // if token is not a brace, it is a key for a key/value pair
971                         if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) {
972                                 break;
973                         }
974
975                         Com_UngetToken();
976                         idStr key = Com_ParseOnLine( text );
977                         const char *token = Com_Parse( text );
978                         if ( Q_stricmp( key.c_str(), "type" ) == 0 ) {
979                                 type = static_cast<idCameraEvent::eventType>( atoi( token ) );
980                         }
981                         else if ( Q_stricmp( key.c_str(), "param" ) == 0 ) {
982                                 paramStr = token;
983                         }
984                         else if ( Q_stricmp( key.c_str(), "time" ) == 0 ) {
985                                 time = atoi( token );
986                         }
987                         token = Com_Parse( text );
988
989                 } while ( 1 );
990
991                 if ( !strcmp( token, "}" ) ) {
992                         break;
993                 }
994
995         } while ( 1 );
996
997         Com_UngetToken();
998         Com_MatchToken( text, "}" );
999 }
1000
1001 void idCameraEvent::write( fileHandle_t file, const char *name ) {
1002         idStr s = va( "\t%s {\n", name );
1003         FS_Write( s.c_str(), s.length(), file );
1004         s = va( "\t\ttype %d\n", static_cast<int>( type ) );
1005         FS_Write( s.c_str(), s.length(), file );
1006         s = va( "\t\tparam \"%s\"\n", paramStr.c_str() );
1007         FS_Write( s.c_str(), s.length(), file );
1008         s = va( "\t\ttime %d\n", time );
1009         FS_Write( s.c_str(), s.length(), file );
1010         s = "\t}\n";
1011         FS_Write( s.c_str(), s.length(), file );
1012 }
1013
1014
1015 const char *idCameraPosition::positionStr[] = {
1016         "Fixed",
1017         "Interpolated",
1018         "Spline",
1019 };
1020
1021
1022
1023 const idVec3 *idInterpolatedPosition::getPosition( long t ) {
1024         static idVec3 interpolatedPos;
1025
1026         float velocity = getVelocity( t );
1027         float timePassed = t - lastTime;
1028         lastTime = t;
1029
1030         // convert to seconds
1031         timePassed /= 1000;
1032
1033         float distToTravel = timePassed * velocity;
1034
1035         idVec3 temp = startPos;
1036         temp -= endPos;
1037         float distance = temp.Length();
1038
1039         distSoFar += distToTravel;
1040         float percent = (float)( distSoFar ) / distance;
1041
1042         if ( percent > 1.0 ) {
1043                 percent = 1.0;
1044         }
1045         else if ( percent < 0.0 ) {
1046                 percent = 0.0;
1047         }
1048
1049         // the following line does a straigt calc on percentage of time
1050         // float percent = (float)(startTime + time - t) / time;
1051
1052         idVec3 v1 = startPos;
1053         idVec3 v2 = endPos;
1054         v1 *= ( 1.0 - percent );
1055         v2 *= percent;
1056         v1 += v2;
1057         interpolatedPos = v1;
1058         return &interpolatedPos;
1059 }
1060
1061
1062 void idCameraFOV::parse( const char *( *text )  ) {
1063         const char *token;
1064         Com_MatchToken( text, "{" );
1065         do {
1066                 token = Com_Parse( text );
1067
1068                 if ( !token[0] ) {
1069                         break;
1070                 }
1071                 if ( !strcmp( token, "}" ) ) {
1072                         break;
1073                 }
1074
1075                 // here we may have to jump over brush epairs ( only used in editor )
1076                 do {
1077                         // if token is not a brace, it is a key for a key/value pair
1078                         if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) {
1079                                 break;
1080                         }
1081
1082                         Com_UngetToken();
1083                         idStr key = Com_ParseOnLine( text );
1084                         const char *token = Com_Parse( text );
1085                         if ( Q_stricmp( key.c_str(), "fov" ) == 0 ) {
1086                                 fov = atof( token );
1087                         }
1088                         else if ( Q_stricmp( key.c_str(), "startFOV" ) == 0 ) {
1089                                 startFOV = atof( token );
1090                         }
1091                         else if ( Q_stricmp( key.c_str(), "endFOV" ) == 0 ) {
1092                                 endFOV = atof( token );
1093                         }
1094                         else if ( Q_stricmp( key.c_str(), "time" ) == 0 ) {
1095                                 time = atoi( token );
1096                         }
1097                         token = Com_Parse( text );
1098
1099                 } while ( 1 );
1100
1101                 if ( !strcmp( token, "}" ) ) {
1102                         break;
1103                 }
1104
1105         } while ( 1 );
1106
1107         Com_UngetToken();
1108         Com_MatchToken( text, "}" );
1109 }
1110
1111 bool idCameraPosition::parseToken( const char *key, const char *( *text ) ) {
1112         const char *token = Com_Parse( text );
1113         if ( Q_stricmp( key, "time" ) == 0 ) {
1114                 time = atol( token );
1115                 return true;
1116         }
1117         else if ( Q_stricmp( key, "type" ) == 0 ) {
1118                 type = static_cast<idCameraPosition::positionType>( atoi( token ) );
1119                 return true;
1120         }
1121         else if ( Q_stricmp( key, "velocity" ) == 0 ) {
1122                 long t = atol( token );
1123                 token = Com_Parse( text );
1124                 long d = atol( token );
1125                 token = Com_Parse( text );
1126                 float s = atof( token );
1127                 addVelocity( t, d, s );
1128                 return true;
1129         }
1130         else if ( Q_stricmp( key, "baseVelocity" ) == 0 ) {
1131                 baseVelocity = atof( token );
1132                 return true;
1133         }
1134         else if ( Q_stricmp( key, "name" ) == 0 ) {
1135                 name = token;
1136                 return true;
1137         }
1138         else if ( Q_stricmp( key, "time" ) == 0 ) {
1139                 time = atoi( token );
1140                 return true;
1141         }
1142         Com_UngetToken();
1143         return false;
1144 }
1145
1146
1147
1148 void idFixedPosition::parse( const char *( *text )  ) {
1149         const char *token;
1150         Com_MatchToken( text, "{" );
1151         do {
1152                 token = Com_Parse( text );
1153
1154                 if ( !token[0] ) {
1155                         break;
1156                 }
1157                 if ( !strcmp( token, "}" ) ) {
1158                         break;
1159                 }
1160
1161                 // here we may have to jump over brush epairs ( only used in editor )
1162                 do {
1163                         // if token is not a brace, it is a key for a key/value pair
1164                         if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) {
1165                                 break;
1166                         }
1167
1168                         Com_UngetToken();
1169                         idStr key = Com_ParseOnLine( text );
1170
1171                         const char *token = Com_Parse( text );
1172                         if ( Q_stricmp( key.c_str(), "pos" ) == 0 ) {
1173                                 Com_UngetToken();
1174                                 Com_Parse1DMatrix( text, 3, pos );
1175                         }
1176                         else {
1177                                 Com_UngetToken();
1178                                 idCameraPosition::parseToken( key.c_str(), text );
1179                         }
1180                         token = Com_Parse( text );
1181
1182                 } while ( 1 );
1183
1184                 if ( !strcmp( token, "}" ) ) {
1185                         break;
1186                 }
1187
1188         } while ( 1 );
1189
1190         Com_UngetToken();
1191         Com_MatchToken( text, "}" );
1192 }
1193
1194 void idInterpolatedPosition::parse( const char *( *text )  ) {
1195         const char *token;
1196         Com_MatchToken( text, "{" );
1197         do {
1198                 token = Com_Parse( text );
1199
1200                 if ( !token[0] ) {
1201                         break;
1202                 }
1203                 if ( !strcmp( token, "}" ) ) {
1204                         break;
1205                 }
1206
1207                 // here we may have to jump over brush epairs ( only used in editor )
1208                 do {
1209                         // if token is not a brace, it is a key for a key/value pair
1210                         if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) {
1211                                 break;
1212                         }
1213
1214                         Com_UngetToken();
1215                         idStr key = Com_ParseOnLine( text );
1216
1217                         const char *token = Com_Parse( text );
1218                         if ( Q_stricmp( key.c_str(), "startPos" ) == 0 ) {
1219                                 Com_UngetToken();
1220                                 Com_Parse1DMatrix( text, 3, startPos );
1221                         }
1222                         else if ( Q_stricmp( key.c_str(), "endPos" ) == 0 ) {
1223                                 Com_UngetToken();
1224                                 Com_Parse1DMatrix( text, 3, endPos );
1225                         }
1226                         else {
1227                                 Com_UngetToken();
1228                                 idCameraPosition::parseToken( key.c_str(), text );
1229                         }
1230                         token = Com_Parse( text );
1231
1232                 } while ( 1 );
1233
1234                 if ( !strcmp( token, "}" ) ) {
1235                         break;
1236                 }
1237
1238         } while ( 1 );
1239
1240         Com_UngetToken();
1241         Com_MatchToken( text, "}" );
1242 }
1243
1244
1245 void idSplinePosition::parse( const char *( *text )  ) {
1246         const char *token;
1247         Com_MatchToken( text, "{" );
1248         do {
1249                 token = Com_Parse( text );
1250
1251                 if ( !token[0] ) {
1252                         break;
1253                 }
1254                 if ( !strcmp( token, "}" ) ) {
1255                         break;
1256                 }
1257
1258                 // here we may have to jump over brush epairs ( only used in editor )
1259                 do {
1260                         // if token is not a brace, it is a key for a key/value pair
1261                         if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) {
1262                                 break;
1263                         }
1264
1265                         Com_UngetToken();
1266                         idStr key = Com_ParseOnLine( text );
1267
1268                         const char *token = Com_Parse( text );
1269                         if ( Q_stricmp( key.c_str(), "target" ) == 0 ) {
1270                                 target.parse( text );
1271                         }
1272                         else {
1273                                 Com_UngetToken();
1274                                 idCameraPosition::parseToken( key.c_str(), text );
1275                         }
1276                         token = Com_Parse( text );
1277
1278                 } while ( 1 );
1279
1280                 if ( !strcmp( token, "}" ) ) {
1281                         break;
1282                 }
1283
1284         } while ( 1 );
1285
1286         Com_UngetToken();
1287         Com_MatchToken( text, "}" );
1288 }
1289
1290
1291
1292 void idCameraFOV::write( fileHandle_t file, const char *p ) {
1293         idStr s = va( "\t%s {\n", p );
1294         FS_Write( s.c_str(), s.length(), file );
1295
1296         s = va( "\t\tfov %f\n", fov );
1297         FS_Write( s.c_str(), s.length(), file );
1298
1299         s = va( "\t\tstartFOV %f\n", startFOV );
1300         FS_Write( s.c_str(), s.length(), file );
1301
1302         s = va( "\t\tendFOV %f\n", endFOV );
1303         FS_Write( s.c_str(), s.length(), file );
1304
1305         s = va( "\t\ttime %i\n", time );
1306         FS_Write( s.c_str(), s.length(), file );
1307
1308         s = "\t}\n";
1309         FS_Write( s.c_str(), s.length(), file );
1310 }
1311
1312
1313 void idCameraPosition::write( fileHandle_t file, const char *p ) {
1314
1315         idStr s = va( "\t\ttime %i\n", time );
1316         FS_Write( s.c_str(), s.length(), file );
1317
1318         s = va( "\t\ttype %i\n", static_cast<int>( type ) );
1319         FS_Write( s.c_str(), s.length(), file );
1320
1321         s = va( "\t\tname %s\n", name.c_str() );
1322         FS_Write( s.c_str(), s.length(), file );
1323
1324         s = va( "\t\tbaseVelocity %f\n", baseVelocity );
1325         FS_Write( s.c_str(), s.length(), file );
1326
1327         for ( int i = 0; i < velocities.Num(); i++ ) {
1328                 s = va( "\t\tvelocity %i %i %f\n", velocities[i]->startTime, velocities[i]->time, velocities[i]->speed );
1329                 FS_Write( s.c_str(), s.length(), file );
1330         }
1331
1332 }
1333
1334 void idFixedPosition::write( fileHandle_t file, const char *p ) {
1335         idStr s = va( "\t%s {\n", p );
1336         FS_Write( s.c_str(), s.length(), file );
1337         idCameraPosition::write( file, p );
1338         s = va( "\t\tpos ( %f %f %f )\n", pos.x, pos.y, pos.z );
1339         FS_Write( s.c_str(), s.length(), file );
1340         s = "\t}\n";
1341         FS_Write( s.c_str(), s.length(), file );
1342 }
1343
1344 void idInterpolatedPosition::write( fileHandle_t file, const char *p ) {
1345         idStr s = va( "\t%s {\n", p );
1346         FS_Write( s.c_str(), s.length(), file );
1347         idCameraPosition::write( file, p );
1348         s = va( "\t\tstartPos ( %f %f %f )\n", startPos.x, startPos.y, startPos.z );
1349         FS_Write( s.c_str(), s.length(), file );
1350         s = va( "\t\tendPos ( %f %f %f )\n", endPos.x, endPos.y, endPos.z );
1351         FS_Write( s.c_str(), s.length(), file );
1352         s = "\t}\n";
1353         FS_Write( s.c_str(), s.length(), file );
1354 }
1355
1356 void idSplinePosition::write( fileHandle_t file, const char *p ) {
1357         idStr s = va( "\t%s {\n", p );
1358         FS_Write( s.c_str(), s.length(), file );
1359         idCameraPosition::write( file, p );
1360         target.write( file, "target" );
1361         s = "\t}\n";
1362         FS_Write( s.c_str(), s.length(), file );
1363 }
1364
1365 void idCameraDef::addTarget( const char *name, idCameraPosition::positionType type ) {
1366         const char *text = ( name == NULL ) ? va( "target0%d", numTargets() + 1 ) : name;
1367         idCameraPosition *pos = newFromType( type );
1368         if ( pos ) {
1369                 pos->setName( name );
1370                 targetPositions.Append( pos );
1371                 activeTarget = numTargets() - 1;
1372                 if ( activeTarget == 0 ) {
1373                         // first one
1374                         addEvent( idCameraEvent::EVENT_TARGET, name, 0 );
1375                 }
1376         }
1377 }
1378
1379 const idVec3 *idSplinePosition::getPosition( long t ) {
1380         static idVec3 interpolatedPos;
1381
1382         float velocity = getVelocity( t );
1383         float timePassed = t - lastTime;
1384         lastTime = t;
1385
1386         // convert to seconds
1387         timePassed /= 1000;
1388
1389         float distToTravel = timePassed * velocity;
1390
1391         distSoFar += distToTravel;
1392         double tempDistance = target.totalDistance();
1393
1394         double percent = (double)( distSoFar ) / tempDistance;
1395
1396         double targetDistance = percent * tempDistance;
1397         tempDistance = 0;
1398
1399         double lastDistance1,lastDistance2;
1400         lastDistance1 = lastDistance2 = 0;
1401         idVec3 temp;
1402         int count = target.numSegments();
1403         int i;
1404         for ( i = 1; i < count; i++ ) {
1405                 temp = *target.getSegmentPoint( i - 1 );
1406                 temp -= *target.getSegmentPoint( i );
1407                 tempDistance += temp.Length();
1408                 if ( i & 1 ) {
1409                         lastDistance1 = tempDistance;
1410                 }
1411                 else {
1412                         lastDistance2 = tempDistance;
1413                 }
1414                 if ( tempDistance >= targetDistance ) {
1415                         break;
1416                 }
1417         }
1418
1419         if ( i >= count - 1 ) {
1420                 interpolatedPos = *target.getSegmentPoint( i - 1 );
1421         }
1422         else {
1423 #if 0
1424                 double timeHi = target.getSegmentTime( i + 1 );
1425                 double timeLo = target.getSegmentTime( i - 1 );
1426                 double percent = ( timeHi - t ) / ( timeHi - timeLo );
1427                 idVec3 v1 = *target.getSegmentPoint( i - 1 );
1428                 idVec3 v2 = *target.getSegmentPoint( i + 1 );
1429                 v2 *= ( 1.0 - percent );
1430                 v1 *= percent;
1431                 v2 += v1;
1432                 interpolatedPos = v2;
1433 #else
1434                 if ( lastDistance1 > lastDistance2 ) {
1435                         double d = lastDistance2;
1436                         lastDistance2 = lastDistance1;
1437                         lastDistance1 = d;
1438                 }
1439
1440                 idVec3 v1 = *target.getSegmentPoint( i - 1 );
1441                 idVec3 v2 = *target.getSegmentPoint( i );
1442                 double percent = ( lastDistance2 - targetDistance ) / ( lastDistance2 - lastDistance1 );
1443                 v2 *= ( 1.0 - percent );
1444                 v1 *= percent;
1445                 v2 += v1;
1446                 interpolatedPos = v2;
1447 #endif
1448         }
1449         return &interpolatedPos;
1450
1451 }