Merge remote-tracking branch 'github/master'
[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
397         int count = splineTime.Num();
398         if ( count == 0 ) {
399                 return &zero;
400         }
401
402 //      Com_Printf("Time: %d\n", t);
403         assert( splineTime.Num() == splinePoints.Num() );
404
405         while ( activeSegment < count ) {
406                 if ( splineTime[activeSegment] >= t ) {
407                         if ( activeSegment > 0 && activeSegment < count - 1 ) {
408                                 double timeHi = splineTime[activeSegment + 1];
409                                 double timeLo = splineTime[activeSegment - 1];
410                                 double percent = ( timeHi - t ) / ( timeHi - timeLo );
411                                 // pick two bounding points
412                                 idVec3 v1 = *splinePoints[activeSegment - 1];
413                                 idVec3 v2 = *splinePoints[activeSegment + 1];
414                                 v2 *= ( 1.0 - percent );
415                                 v1 *= percent;
416                                 v2 += v1;
417                                 interpolatedPos = v2;
418                                 return &interpolatedPos;
419                         }
420                         return splinePoints[activeSegment];
421                 }
422                 else {
423                         activeSegment++;
424                 }
425         }
426         return splinePoints[count - 1];
427 }
428
429 void idSplineList::parse( const char *( *text )  ) {
430         const char *token;
431         //Com_MatchToken( text, "{" );
432         do {
433                 token = Com_Parse( text );
434
435                 if ( !token[0] ) {
436                         break;
437                 }
438                 if ( !Q_stricmp( token, "}" ) ) {
439                         break;
440                 }
441
442                 do {
443                         // if token is not a brace, it is a key for a key/value pair
444                         if ( !token[0] || !Q_stricmp( token, "(" ) || !Q_stricmp( token, "}" ) ) {
445                                 break;
446                         }
447
448                         Com_UngetToken();
449                         idStr key = Com_ParseOnLine( text );
450                         const char *token = Com_Parse( text );
451                         if ( Q_stricmp( key.c_str(), "granularity" ) == 0 ) {
452                                 granularity = atof( token );
453                         }
454                         else if ( Q_stricmp( key.c_str(), "name" ) == 0 ) {
455                                 name = token;
456                         }
457                         token = Com_Parse( text );
458
459                 } while ( 1 );
460
461                 if ( !Q_stricmp( token, "}" ) ) {
462                         break;
463                 }
464
465                 Com_UngetToken();
466                 // read the control point
467                 idVec3 point;
468                 Com_Parse1DMatrix( text, 3, point );
469                 addPoint( point.x, point.y, point.z );
470         } while ( 1 );
471
472         //Com_UngetToken();
473         //Com_MatchToken( text, "}" );
474         dirty = true;
475 }
476
477 void idSplineList::write( fileHandle_t file, const char *p ) {
478         idStr s = va( "\t\t%s {\n", p );
479         FS_Write( s.c_str(), s.length(), file );
480         //s = va("\t\tname %s\n", name.c_str());
481         //FS_Write(s.c_str(), s.length(), file);
482         s = va( "\t\t\tgranularity %f\n", granularity );
483         FS_Write( s.c_str(), s.length(), file );
484         int count = controlPoints.Num();
485         for ( int i = 0; i < count; i++ ) {
486                 s = va( "\t\t\t( %f %f %f )\n", controlPoints[i]->x, controlPoints[i]->y, controlPoints[i]->z );
487                 FS_Write( s.c_str(), s.length(), file );
488         }
489         s = "\t\t}\n";
490         FS_Write( s.c_str(), s.length(), file );
491 }
492
493
494 void idCameraDef::getActiveSegmentInfo( int segment, idVec3 &origin, idVec3 &direction, float *fov ) {
495 #if 0
496         if ( !cameraSpline.validTime() ) {
497                 buildCamera();
498         }
499         double d = (double)segment / numSegments();
500         getCameraInfo( d * totalTime * 1000, origin, direction, fov );
501 #endif
502 /*
503     if (!cameraSpline.validTime()) {
504         buildCamera();
505     }
506     origin = *cameraSpline.getSegmentPoint(segment);
507
508
509     idVec3 temp;
510
511     int numTargets = getTargetSpline()->controlPoints.Num();
512     int count = cameraSpline.splineTime.Num();
513     if (numTargets == 0) {
514         // follow the path
515         if (cameraSpline.getActiveSegment() < count - 1) {
516             temp = *cameraSpline.splinePoints[cameraSpline.getActiveSegment()+1];
517         }
518     } else if (numTargets == 1) {
519         temp = *getTargetSpline()->controlPoints[0];
520     } else {
521         temp = *getTargetSpline()->getSegmentPoint(segment);
522     }
523
524     temp -= origin;
525     temp.Normalize();
526     direction = temp;
527  */
528 }
529
530 bool idCameraDef::getCameraInfo( long time, idVec3 &origin, idVec3 &direction, float *fv ) {
531
532         char buff[1024];
533
534         if ( ( time - startTime ) / 1000 > totalTime ) {
535                 return false;
536         }
537
538
539         for ( int i = 0; i < events.Num(); i++ ) {
540                 if ( time >= startTime + events[i]->getTime() && !events[i]->getTriggered() ) {
541                         events[i]->setTriggered( true );
542                         if ( events[i]->getType() == idCameraEvent::EVENT_TARGET ) {
543                                 setActiveTargetByName( events[i]->getParam() );
544                                 getActiveTarget()->start( startTime + events[i]->getTime() );
545                                 //Com_Printf("Triggered event switch to target: %s\n",events[i]->getParam());
546                         }
547                         else if ( events[i]->getType() == idCameraEvent::EVENT_TRIGGER ) {
548                                 //idEntity *ent = NULL;
549                                 //ent = level.FindTarget( ent, events[i]->getParam());
550                                 //if (ent) {
551                                 //      ent->signal( SIG_TRIGGER );
552                                 //      ent->ProcessEvent( &EV_Activate, world );
553                                 //}
554                         }
555                         else if ( events[i]->getType() == idCameraEvent::EVENT_FOV ) {
556                                 memset( buff, 0, sizeof( buff ) );
557                                 strcpy( buff, events[i]->getParam() );
558                                 const char *param1 = strtok( buff, " \t,\0" );
559                                 const char *param2 = strtok( NULL, " \t,\0" );
560                                 float len = ( param2 ) ? atof( param2 ) : 0;
561                                 float newfov = ( param1 ) ? atof( param1 ) : 90;
562                                 fov.reset( fov.getFOV( time ), newfov, time, len );
563                                 //*fv = fov = atof(events[i]->getParam());
564                         }
565                         else if ( events[i]->getType() == idCameraEvent::EVENT_FADEIN ) {
566                                 float time = atof( events[i]->getParam() );
567                                 Cbuf_AddText( va( "fade 0 0 0 0 %f", time ) );
568                                 Cbuf_Execute();
569                         }
570                         else if ( events[i]->getType() == idCameraEvent::EVENT_FADEOUT ) {
571                                 float time = atof( events[i]->getParam() );
572                                 Cbuf_AddText( va( "fade 0 0 0 255 %f", time ) );
573                                 Cbuf_Execute();
574                         }
575                         else if ( events[i]->getType() == idCameraEvent::EVENT_CAMERA ) {
576                                 memset( buff, 0, sizeof( buff ) );
577                                 strcpy( buff, events[i]->getParam() );
578                                 const char *param1 = strtok( buff, " \t,\0" );
579                                 const char *param2 = strtok( NULL, " \t,\0" );
580
581                                 if ( param2 ) {
582                                         loadCamera( atoi( param1 ), va( "cameras/%s.camera", param2 ) );
583                                         startCamera( time );
584                                 }
585                                 else {
586                                         loadCamera( 0, va( "cameras/%s.camera", events[i]->getParam() ) );
587                                         startCamera( time );
588                                 }
589                                 return true;
590                         }
591                         else if ( events[i]->getType() == idCameraEvent::EVENT_STOP ) {
592                                 return false;
593                         }
594                 }
595         }
596
597         origin = *cameraPosition->getPosition( time );
598
599         *fv = fov.getFOV( time );
600
601         idVec3 temp = origin;
602
603         int numTargets = targetPositions.Num();
604         if ( numTargets == 0 ) {
605 /*
606         // follow the path
607         if (cameraSpline.getActiveSegment() < count - 1) {
608             temp = *cameraSpline.splinePoints[cameraSpline.getActiveSegment()+1];
609             if (temp == origin) {
610                 int index = cameraSpline.getActiveSegment() + 2;
611                 while (temp == origin && index < count - 1) {
612                     temp = *cameraSpline.splinePoints[index++];
613                 }
614             }
615         }
616  */
617         }
618         else {
619                 if ( getActiveTarget()->numPoints() > 0 ) {
620                         temp = *getActiveTarget()->getPosition( time );
621                 }
622         }
623
624         temp -= origin;
625         temp.Normalize();
626         direction = temp;
627
628         return true;
629 }
630
631 bool idCameraDef::waitEvent( int index ) {
632         //for (int i = 0; i < events.Num(); i++) {
633         //      if (events[i]->getSegment() == index && events[i]->getType() == idCameraEvent::EVENT_WAIT) {
634         //              return true;
635         //      }
636         //}
637         return false;
638 }
639
640
641 #define NUM_CCELERATION_SEGS 10
642 #define CELL_AMT 5
643
644 void idCameraDef::buildCamera() {
645         int i;
646         idList<float> waits;
647         idList<int> targets;
648
649         totalTime = baseTime;
650         cameraPosition->setTime( (long)totalTime * 1000 );
651         // we have a base time layout for the path and the target path
652         // now we need to layer on any wait or speed changes
653         for ( i = 0; i < events.Num(); i++ ) {
654                 events[i]->setTriggered( false );
655                 switch ( events[i]->getType() ) {
656                 case idCameraEvent::EVENT_TARGET: {
657                         targets.Append( i );
658                         break;
659                 }
660                 case idCameraEvent::EVENT_FEATHER: {
661                         long startTime = 0;
662                         float speed = 0;
663                         long loopTime = 10;
664                         float stepGoal = cameraPosition->getBaseVelocity() / ( 1000 / loopTime );
665                         while ( startTime <= 1000 ) {
666                                 cameraPosition->addVelocity( startTime, loopTime, speed );
667                                 speed += stepGoal;
668                                 if ( speed > cameraPosition->getBaseVelocity() ) {
669                                         speed = cameraPosition->getBaseVelocity();
670                                 }
671                                 startTime += loopTime;
672                         }
673
674                         startTime = (long)( totalTime * 1000 - 1000 );
675                         long endTime = startTime + 1000;
676                         speed = cameraPosition->getBaseVelocity();
677                         while ( startTime < endTime ) {
678                                 speed -= stepGoal;
679                                 if ( speed < 0 ) {
680                                         speed = 0;
681                                 }
682                                 cameraPosition->addVelocity( startTime, loopTime, speed );
683                                 startTime += loopTime;
684                         }
685                         break;
686
687                 }
688                 case idCameraEvent::EVENT_WAIT: {
689                         waits.Append( atof( events[i]->getParam() ) );
690
691                         //FIXME: this is quite hacky for Wolf E3, accel and decel needs
692                         // do be parameter based etc..
693                         long startTime = events[i]->getTime() - 1000;
694                         if ( startTime < 0 ) {
695                                 startTime = 0;
696                         }
697                         float speed = cameraPosition->getBaseVelocity();
698                         long loopTime = 10;
699                         float steps = speed / ( ( events[i]->getTime() - startTime ) / loopTime );
700                         while ( startTime <= events[i]->getTime() - loopTime ) {
701                                 cameraPosition->addVelocity( startTime, loopTime, speed );
702                                 speed -= steps;
703                                 startTime += loopTime;
704                         }
705                         cameraPosition->addVelocity( events[i]->getTime(), (long)atof( events[i]->getParam() ) * 1000, 0 );
706
707                         startTime = (long)( events[i]->getTime() + atof( events[i]->getParam() ) * 1000 );
708                         long endTime = startTime + 1000;
709                         speed = 0;
710                         while ( startTime <= endTime ) {
711                                 cameraPosition->addVelocity( startTime, loopTime, speed );
712                                 speed += steps;
713                                 startTime += loopTime;
714                         }
715                         break;
716                 }
717                 case idCameraEvent::EVENT_TARGETWAIT: {
718                         //targetWaits.Append(i);
719                         break;
720                 }
721                 case idCameraEvent::EVENT_SPEED: {
722 /*
723                 // take the average delay between up to the next five segments
724                 float adjust = atof(events[i]->getParam());
725                 int index = events[i]->getSegment();
726                 total = 0;
727                 count = 0;
728
729                 // get total amount of time over the remainder of the segment
730                 for (j = index; j < cameraSpline.numSegments() - 1; j++) {
731                     total += cameraSpline.getSegmentTime(j + 1) - cameraSpline.getSegmentTime(j);
732                     count++;
733                 }
734
735                 // multiply that by the adjustment
736                 double newTotal = total * adjust;
737                 // what is the difference..
738                 newTotal -= total;
739                 totalTime += newTotal / 1000;
740
741                 // per segment difference
742                 newTotal /= count;
743                 int additive = newTotal;
744
745                 // now propogate that difference out to each segment
746                 for (j = index; j < cameraSpline.numSegments(); j++) {
747                     cameraSpline.addSegmentTime(j, additive);
748                     additive += newTotal;
749                 }
750                 break;
751  */
752                 }
753                 }
754         }
755
756
757         for ( i = 0; i < waits.Num(); i++ ) {
758                 totalTime += waits[i];
759         }
760
761         // on a new target switch, we need to take time to this point ( since last target switch )
762         // and allocate it across the active target, then reset time to this point
763         long timeSoFar = 0;
764         long total = (long)( totalTime * 1000 );
765         for ( i = 0; i < targets.Num(); i++ ) {
766                 long t;
767                 if ( i < targets.Num() - 1 ) {
768                         t = events[targets[i + 1]]->getTime();
769                 }
770                 else {
771                         t = total - timeSoFar;
772                 }
773                 // t is how much time to use for this target
774                 setActiveTargetByName( events[targets[i]]->getParam() );
775                 getActiveTarget()->setTime( t );
776                 timeSoFar += t;
777         }
778
779
780 }
781
782 void idCameraDef::startCamera( long t ) {
783         cameraPosition->clearVelocities();
784         cameraPosition->start( t );
785         buildCamera();
786         fov.reset( 90, 90, t, 0 );
787         //for (int i = 0; i < targetPositions.Num(); i++) {
788         //      targetPositions[i]->
789         //}
790         startTime = t;
791         cameraRunning = true;
792 }
793
794
795 void idCameraDef::parse( const char *( *text )  ) {
796
797         const char  *token;
798         do {
799                 token = Com_Parse( text );
800
801                 if ( !token[0] ) {
802                         break;
803                 }
804                 if ( !Q_stricmp( token, "}" ) ) {
805                         break;
806                 }
807
808                 if ( Q_stricmp( token, "time" ) == 0 ) {
809                         baseTime = Com_ParseFloat( text );
810                 }
811                 else if ( Q_stricmp( token, "camera_fixed" ) == 0 ) {
812                         cameraPosition = new idFixedPosition();
813                         cameraPosition->parse( text );
814                 }
815                 else if ( Q_stricmp( token, "camera_interpolated" ) == 0 ) {
816                         cameraPosition = new idInterpolatedPosition();
817                         cameraPosition->parse( text );
818                 }
819                 else if ( Q_stricmp( token, "camera_spline" ) == 0 ) {
820                         cameraPosition = new idSplinePosition();
821                         cameraPosition->parse( text );
822                 }
823                 else if ( Q_stricmp( token, "target_fixed" ) == 0 ) {
824                         idFixedPosition *pos = new idFixedPosition();
825                         pos->parse( text );
826                         targetPositions.Append( pos );
827                 }
828                 else if ( Q_stricmp( token, "target_interpolated" ) == 0 ) {
829                         idInterpolatedPosition *pos = new idInterpolatedPosition();
830                         pos->parse( text );
831                         targetPositions.Append( pos );
832                 }
833                 else if ( Q_stricmp( token, "target_spline" ) == 0 ) {
834                         idSplinePosition *pos = new idSplinePosition();
835                         pos->parse( text );
836                         targetPositions.Append( pos );
837                 }
838                 else if ( Q_stricmp( token, "fov" ) == 0 ) {
839                         fov.parse( text );
840                 }
841                 else if ( Q_stricmp( token, "event" ) == 0 ) {
842                         idCameraEvent *event = new idCameraEvent();
843                         event->parse( text );
844                         addEvent( event );
845                 }
846
847
848         } while ( 1 );
849
850         if ( !cameraPosition ) {
851                 Com_Printf( "no camera position specified\n" );
852                 // prevent a crash later on
853                 cameraPosition = new idFixedPosition();
854         }
855
856         Com_UngetToken();
857         Com_MatchToken( text, "}" );
858
859 }
860
861 bool idCameraDef::load( const char *filename ) {
862         char *buf;
863         const char *buf_p;
864
865         FS_ReadFile( filename, (void **)&buf );
866         if ( !buf ) {
867                 return false;
868         }
869
870         clear();
871         Com_BeginParseSession( filename );
872         buf_p = buf;
873         parse( &buf_p );
874         Com_EndParseSession();
875         FS_FreeFile( buf );
876
877         return true;
878 }
879
880 void idCameraDef::save( const char *filename ) {
881         fileHandle_t file = FS_FOpenFileWrite( filename );
882         if ( file ) {
883                 int i;
884                 idStr s = "cameraPathDef { \n";
885                 FS_Write( s.c_str(), s.length(), file );
886                 s = va( "\ttime %f\n", baseTime );
887                 FS_Write( s.c_str(), s.length(), file );
888
889                 cameraPosition->write( file, va( "camera_%s",cameraPosition->typeStr() ) );
890
891                 for ( i = 0; i < numTargets(); i++ ) {
892                         targetPositions[i]->write( file, va( "target_%s", targetPositions[i]->typeStr() ) );
893                 }
894
895                 for ( i = 0; i < events.Num(); i++ ) {
896                         events[i]->write( file, "event" );
897                 }
898
899                 fov.write( file, "fov" );
900
901                 s = "}\n";
902                 FS_Write( s.c_str(), s.length(), file );
903         }
904         FS_FCloseFile( file );
905 }
906
907 int idCameraDef::sortEvents( const void *p1, const void *p2 ) {
908         idCameraEvent *ev1 = (idCameraEvent*)( p1 );
909         idCameraEvent *ev2 = (idCameraEvent*)( p2 );
910
911         if ( ev1->getTime() > ev2->getTime() ) {
912                 return -1;
913         }
914         if ( ev1->getTime() < ev2->getTime() ) {
915                 return 1;
916         }
917         return 0;
918 }
919
920 void idCameraDef::addEvent( idCameraEvent *event ) {
921         events.Append( event );
922         //events.Sort(&sortEvents);
923
924 }
925 void idCameraDef::addEvent( idCameraEvent::eventType t, const char *param, long time ) {
926         addEvent( new idCameraEvent( t, param, time ) );
927         buildCamera();
928 }
929
930 void idCameraDef::removeEvent( int index ) {
931         events.RemoveIndex( index );
932         buildCamera();
933 }
934
935
936 const char *idCameraEvent::eventStr[] = {
937         "NA",
938         "WAIT",
939         "TARGETWAIT",
940         "SPEED",
941         "TARGET",
942         "SNAPTARGET",
943         "FOV",
944         "CMD",
945         "TRIGGER",
946         "STOP",
947         "CAMERA",
948         "FADEOUT",
949         "FADEIN",
950         "FEATHER"
951 };
952
953 void idCameraEvent::parse( const char *( *text )  ) {
954         const char *token;
955         Com_MatchToken( text, "{" );
956         do {
957                 token = Com_Parse( text );
958
959                 if ( !token[0] ) {
960                         break;
961                 }
962                 if ( !strcmp( token, "}" ) ) {
963                         break;
964                 }
965
966                 // here we may have to jump over brush epairs ( only used in editor )
967                 do {
968                         // if token is not a brace, it is a key for a key/value pair
969                         if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) {
970                                 break;
971                         }
972
973                         Com_UngetToken();
974                         idStr key = Com_ParseOnLine( text );
975                         const char *token = Com_Parse( text );
976                         if ( Q_stricmp( key.c_str(), "type" ) == 0 ) {
977                                 type = static_cast<idCameraEvent::eventType>( atoi( token ) );
978                         }
979                         else if ( Q_stricmp( key.c_str(), "param" ) == 0 ) {
980                                 paramStr = token;
981                         }
982                         else if ( Q_stricmp( key.c_str(), "time" ) == 0 ) {
983                                 time = atoi( token );
984                         }
985                         token = Com_Parse( text );
986
987                 } while ( 1 );
988
989                 if ( !strcmp( token, "}" ) ) {
990                         break;
991                 }
992
993         } while ( 1 );
994
995         Com_UngetToken();
996         Com_MatchToken( text, "}" );
997 }
998
999 void idCameraEvent::write( fileHandle_t file, const char *name ) {
1000         idStr s = va( "\t%s {\n", name );
1001         FS_Write( s.c_str(), s.length(), file );
1002         s = va( "\t\ttype %d\n", static_cast<int>( type ) );
1003         FS_Write( s.c_str(), s.length(), file );
1004         s = va( "\t\tparam \"%s\"\n", paramStr.c_str() );
1005         FS_Write( s.c_str(), s.length(), file );
1006         s = va( "\t\ttime %d\n", time );
1007         FS_Write( s.c_str(), s.length(), file );
1008         s = "\t}\n";
1009         FS_Write( s.c_str(), s.length(), file );
1010 }
1011
1012
1013 const char *idCameraPosition::positionStr[] = {
1014         "Fixed",
1015         "Interpolated",
1016         "Spline",
1017 };
1018
1019
1020
1021 const idVec3 *idInterpolatedPosition::getPosition( long t ) {
1022         static idVec3 interpolatedPos;
1023
1024         float velocity = getVelocity( t );
1025         float timePassed = t - lastTime;
1026         lastTime = t;
1027
1028         // convert to seconds
1029         timePassed /= 1000;
1030
1031         float distToTravel = timePassed * velocity;
1032
1033         idVec3 temp = startPos;
1034         temp -= endPos;
1035         float distance = temp.Length();
1036
1037         distSoFar += distToTravel;
1038         float percent = (float)( distSoFar ) / distance;
1039
1040         if ( percent > 1.0 ) {
1041                 percent = 1.0;
1042         }
1043         else if ( percent < 0.0 ) {
1044                 percent = 0.0;
1045         }
1046
1047         // the following line does a straigt calc on percentage of time
1048         // float percent = (float)(startTime + time - t) / time;
1049
1050         idVec3 v1 = startPos;
1051         idVec3 v2 = endPos;
1052         v1 *= ( 1.0 - percent );
1053         v2 *= percent;
1054         v1 += v2;
1055         interpolatedPos = v1;
1056         return &interpolatedPos;
1057 }
1058
1059
1060 void idCameraFOV::parse( const char *( *text )  ) {
1061         const char *token;
1062         Com_MatchToken( text, "{" );
1063         do {
1064                 token = Com_Parse( text );
1065
1066                 if ( !token[0] ) {
1067                         break;
1068                 }
1069                 if ( !strcmp( token, "}" ) ) {
1070                         break;
1071                 }
1072
1073                 // here we may have to jump over brush epairs ( only used in editor )
1074                 do {
1075                         // if token is not a brace, it is a key for a key/value pair
1076                         if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) {
1077                                 break;
1078                         }
1079
1080                         Com_UngetToken();
1081                         idStr key = Com_ParseOnLine( text );
1082                         const char *token = Com_Parse( text );
1083                         if ( Q_stricmp( key.c_str(), "fov" ) == 0 ) {
1084                                 fov = atof( token );
1085                         }
1086                         else if ( Q_stricmp( key.c_str(), "startFOV" ) == 0 ) {
1087                                 startFOV = atof( token );
1088                         }
1089                         else if ( Q_stricmp( key.c_str(), "endFOV" ) == 0 ) {
1090                                 endFOV = atof( token );
1091                         }
1092                         else if ( Q_stricmp( key.c_str(), "time" ) == 0 ) {
1093                                 time = atoi( token );
1094                         }
1095                         token = Com_Parse( text );
1096
1097                 } while ( 1 );
1098
1099                 if ( !strcmp( token, "}" ) ) {
1100                         break;
1101                 }
1102
1103         } while ( 1 );
1104
1105         Com_UngetToken();
1106         Com_MatchToken( text, "}" );
1107 }
1108
1109 bool idCameraPosition::parseToken( const char *key, const char *( *text ) ) {
1110         const char *token = Com_Parse( text );
1111         if ( Q_stricmp( key, "time" ) == 0 ) {
1112                 time = atol( token );
1113                 return true;
1114         }
1115         else if ( Q_stricmp( key, "type" ) == 0 ) {
1116                 type = static_cast<idCameraPosition::positionType>( atoi( token ) );
1117                 return true;
1118         }
1119         else if ( Q_stricmp( key, "velocity" ) == 0 ) {
1120                 long t = atol( token );
1121                 token = Com_Parse( text );
1122                 long d = atol( token );
1123                 token = Com_Parse( text );
1124                 float s = atof( token );
1125                 addVelocity( t, d, s );
1126                 return true;
1127         }
1128         else if ( Q_stricmp( key, "baseVelocity" ) == 0 ) {
1129                 baseVelocity = atof( token );
1130                 return true;
1131         }
1132         else if ( Q_stricmp( key, "name" ) == 0 ) {
1133                 name = token;
1134                 return true;
1135         }
1136         else if ( Q_stricmp( key, "time" ) == 0 ) {
1137                 time = atoi( token );
1138                 return true;
1139         }
1140         Com_UngetToken();
1141         return false;
1142 }
1143
1144
1145
1146 void idFixedPosition::parse( const char *( *text )  ) {
1147         const char *token;
1148         Com_MatchToken( text, "{" );
1149         do {
1150                 token = Com_Parse( text );
1151
1152                 if ( !token[0] ) {
1153                         break;
1154                 }
1155                 if ( !strcmp( token, "}" ) ) {
1156                         break;
1157                 }
1158
1159                 // here we may have to jump over brush epairs ( only used in editor )
1160                 do {
1161                         // if token is not a brace, it is a key for a key/value pair
1162                         if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) {
1163                                 break;
1164                         }
1165
1166                         Com_UngetToken();
1167                         idStr key = Com_ParseOnLine( text );
1168
1169                         const char *token = Com_Parse( text );
1170                         if ( Q_stricmp( key.c_str(), "pos" ) == 0 ) {
1171                                 Com_UngetToken();
1172                                 Com_Parse1DMatrix( text, 3, pos );
1173                         }
1174                         else {
1175                                 Com_UngetToken();
1176                                 idCameraPosition::parseToken( key.c_str(), text );
1177                         }
1178                         token = Com_Parse( text );
1179
1180                 } while ( 1 );
1181
1182                 if ( !strcmp( token, "}" ) ) {
1183                         break;
1184                 }
1185
1186         } while ( 1 );
1187
1188         Com_UngetToken();
1189         Com_MatchToken( text, "}" );
1190 }
1191
1192 void idInterpolatedPosition::parse( const char *( *text )  ) {
1193         const char *token;
1194         Com_MatchToken( text, "{" );
1195         do {
1196                 token = Com_Parse( text );
1197
1198                 if ( !token[0] ) {
1199                         break;
1200                 }
1201                 if ( !strcmp( token, "}" ) ) {
1202                         break;
1203                 }
1204
1205                 // here we may have to jump over brush epairs ( only used in editor )
1206                 do {
1207                         // if token is not a brace, it is a key for a key/value pair
1208                         if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) {
1209                                 break;
1210                         }
1211
1212                         Com_UngetToken();
1213                         idStr key = Com_ParseOnLine( text );
1214
1215                         const char *token = Com_Parse( text );
1216                         if ( Q_stricmp( key.c_str(), "startPos" ) == 0 ) {
1217                                 Com_UngetToken();
1218                                 Com_Parse1DMatrix( text, 3, startPos );
1219                         }
1220                         else if ( Q_stricmp( key.c_str(), "endPos" ) == 0 ) {
1221                                 Com_UngetToken();
1222                                 Com_Parse1DMatrix( text, 3, endPos );
1223                         }
1224                         else {
1225                                 Com_UngetToken();
1226                                 idCameraPosition::parseToken( key.c_str(), text );
1227                         }
1228                         token = Com_Parse( text );
1229
1230                 } while ( 1 );
1231
1232                 if ( !strcmp( token, "}" ) ) {
1233                         break;
1234                 }
1235
1236         } while ( 1 );
1237
1238         Com_UngetToken();
1239         Com_MatchToken( text, "}" );
1240 }
1241
1242
1243 void idSplinePosition::parse( const char *( *text )  ) {
1244         const char *token;
1245         Com_MatchToken( text, "{" );
1246         do {
1247                 token = Com_Parse( text );
1248
1249                 if ( !token[0] ) {
1250                         break;
1251                 }
1252                 if ( !strcmp( token, "}" ) ) {
1253                         break;
1254                 }
1255
1256                 // here we may have to jump over brush epairs ( only used in editor )
1257                 do {
1258                         // if token is not a brace, it is a key for a key/value pair
1259                         if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) {
1260                                 break;
1261                         }
1262
1263                         Com_UngetToken();
1264                         idStr key = Com_ParseOnLine( text );
1265
1266                         const char *token = Com_Parse( text );
1267                         if ( Q_stricmp( key.c_str(), "target" ) == 0 ) {
1268                                 target.parse( text );
1269                         }
1270                         else {
1271                                 Com_UngetToken();
1272                                 idCameraPosition::parseToken( key.c_str(), text );
1273                         }
1274                         token = Com_Parse( text );
1275
1276                 } while ( 1 );
1277
1278                 if ( !strcmp( token, "}" ) ) {
1279                         break;
1280                 }
1281
1282         } while ( 1 );
1283
1284         Com_UngetToken();
1285         Com_MatchToken( text, "}" );
1286 }
1287
1288
1289
1290 void idCameraFOV::write( fileHandle_t file, const char *p ) {
1291         idStr s = va( "\t%s {\n", p );
1292         FS_Write( s.c_str(), s.length(), file );
1293
1294         s = va( "\t\tfov %f\n", fov );
1295         FS_Write( s.c_str(), s.length(), file );
1296
1297         s = va( "\t\tstartFOV %f\n", startFOV );
1298         FS_Write( s.c_str(), s.length(), file );
1299
1300         s = va( "\t\tendFOV %f\n", endFOV );
1301         FS_Write( s.c_str(), s.length(), file );
1302
1303         s = va( "\t\ttime %i\n", time );
1304         FS_Write( s.c_str(), s.length(), file );
1305
1306         s = "\t}\n";
1307         FS_Write( s.c_str(), s.length(), file );
1308 }
1309
1310
1311 void idCameraPosition::write( fileHandle_t file, const char *p ) {
1312
1313         idStr s = va( "\t\ttime %i\n", time );
1314         FS_Write( s.c_str(), s.length(), file );
1315
1316         s = va( "\t\ttype %i\n", static_cast<int>( type ) );
1317         FS_Write( s.c_str(), s.length(), file );
1318
1319         s = va( "\t\tname %s\n", name.c_str() );
1320         FS_Write( s.c_str(), s.length(), file );
1321
1322         s = va( "\t\tbaseVelocity %f\n", baseVelocity );
1323         FS_Write( s.c_str(), s.length(), file );
1324
1325         for ( int i = 0; i < velocities.Num(); i++ ) {
1326                 s = va( "\t\tvelocity %i %i %f\n", velocities[i]->startTime, velocities[i]->time, velocities[i]->speed );
1327                 FS_Write( s.c_str(), s.length(), file );
1328         }
1329
1330 }
1331
1332 void idFixedPosition::write( fileHandle_t file, const char *p ) {
1333         idStr s = va( "\t%s {\n", p );
1334         FS_Write( s.c_str(), s.length(), file );
1335         idCameraPosition::write( file, p );
1336         s = va( "\t\tpos ( %f %f %f )\n", pos.x, pos.y, pos.z );
1337         FS_Write( s.c_str(), s.length(), file );
1338         s = "\t}\n";
1339         FS_Write( s.c_str(), s.length(), file );
1340 }
1341
1342 void idInterpolatedPosition::write( fileHandle_t file, const char *p ) {
1343         idStr s = va( "\t%s {\n", p );
1344         FS_Write( s.c_str(), s.length(), file );
1345         idCameraPosition::write( file, p );
1346         s = va( "\t\tstartPos ( %f %f %f )\n", startPos.x, startPos.y, startPos.z );
1347         FS_Write( s.c_str(), s.length(), file );
1348         s = va( "\t\tendPos ( %f %f %f )\n", endPos.x, endPos.y, endPos.z );
1349         FS_Write( s.c_str(), s.length(), file );
1350         s = "\t}\n";
1351         FS_Write( s.c_str(), s.length(), file );
1352 }
1353
1354 void idSplinePosition::write( fileHandle_t file, const char *p ) {
1355         idStr s = va( "\t%s {\n", p );
1356         FS_Write( s.c_str(), s.length(), file );
1357         idCameraPosition::write( file, p );
1358         target.write( file, "target" );
1359         s = "\t}\n";
1360         FS_Write( s.c_str(), s.length(), file );
1361 }
1362
1363 void idCameraDef::addTarget( const char *name, idCameraPosition::positionType type ) {
1364         idCameraPosition *pos = newFromType( type );
1365         if ( pos ) {
1366                 pos->setName( name );
1367                 targetPositions.Append( pos );
1368                 activeTarget = numTargets() - 1;
1369                 if ( activeTarget == 0 ) {
1370                         // first one
1371                         addEvent( idCameraEvent::EVENT_TARGET, name, 0 );
1372                 }
1373         }
1374 }
1375
1376 const idVec3 *idSplinePosition::getPosition( long t ) {
1377         static idVec3 interpolatedPos;
1378
1379         float velocity = getVelocity( t );
1380         float timePassed = t - lastTime;
1381         lastTime = t;
1382
1383         // convert to seconds
1384         timePassed /= 1000;
1385
1386         float distToTravel = timePassed * velocity;
1387
1388         distSoFar += distToTravel;
1389         double tempDistance = target.totalDistance();
1390
1391         double percent = (double)( distSoFar ) / tempDistance;
1392
1393         double targetDistance = percent * tempDistance;
1394         tempDistance = 0;
1395
1396         double lastDistance1,lastDistance2;
1397         lastDistance1 = lastDistance2 = 0;
1398         idVec3 temp;
1399         int count = target.numSegments();
1400         int i;
1401         for ( i = 1; i < count; i++ ) {
1402                 temp = *target.getSegmentPoint( i - 1 );
1403                 temp -= *target.getSegmentPoint( i );
1404                 tempDistance += temp.Length();
1405                 if ( i & 1 ) {
1406                         lastDistance1 = tempDistance;
1407                 }
1408                 else {
1409                         lastDistance2 = tempDistance;
1410                 }
1411                 if ( tempDistance >= targetDistance ) {
1412                         break;
1413                 }
1414         }
1415
1416         if ( i >= count - 1 ) {
1417                 interpolatedPos = *target.getSegmentPoint( i - 1 );
1418         }
1419         else {
1420 #if 0
1421                 double timeHi = target.getSegmentTime( i + 1 );
1422                 double timeLo = target.getSegmentTime( i - 1 );
1423                 double percent = ( timeHi - t ) / ( timeHi - timeLo );
1424                 idVec3 v1 = *target.getSegmentPoint( i - 1 );
1425                 idVec3 v2 = *target.getSegmentPoint( i + 1 );
1426                 v2 *= ( 1.0 - percent );
1427                 v1 *= percent;
1428                 v2 += v1;
1429                 interpolatedPos = v2;
1430 #else
1431                 if ( lastDistance1 > lastDistance2 ) {
1432                         double d = lastDistance2;
1433                         lastDistance2 = lastDistance1;
1434                         lastDistance1 = d;
1435                 }
1436
1437                 idVec3 v1 = *target.getSegmentPoint( i - 1 );
1438                 idVec3 v2 = *target.getSegmentPoint( i );
1439                 double percent = ( lastDistance2 - targetDistance ) / ( lastDistance2 - lastDistance1 );
1440                 v2 *= ( 1.0 - percent );
1441                 v1 *= percent;
1442                 v2 += v1;
1443                 interpolatedPos = v2;
1444 #endif
1445         }
1446         return &interpolatedPos;
1447
1448 }