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