/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_shared.h" #include "splines.h" extern "C" { int FS_Write( const void *buffer, int len, fileHandle_t h ); int FS_ReadFile( const char *qpath, void **buffer ); void FS_FreeFile( void *buffer ); fileHandle_t FS_FOpenFileWrite( const char *filename ); void FS_FCloseFile( fileHandle_t f ); void Cbuf_AddText( const char *text ); void Cbuf_Execute( void ); } float Q_fabs( float f ) { int tmp = *( int * ) &f; tmp &= 0x7FFFFFFF; return *( float * ) &tmp; } // (SA) making a list of cameras so I can use // the splines as targets for other things. // Certainly better ways to do this, but this lets // me get underway quickly with ents that need spline // targets. const int MAX_CAMERAS = 64; idCameraDef camera[MAX_CAMERAS]; extern "C" { qboolean loadCamera( int camNum, const char *name ) { if ( camNum < 0 || camNum >= MAX_CAMERAS ) { return qfalse; } camera[camNum].clear(); return (qboolean)camera[camNum].load( name ); } qboolean getCameraInfo( int camNum, int time, float *origin, float *angles, float *fov ) { idVec3 dir, org; if ( camNum < 0 || camNum >= MAX_CAMERAS ) { return qfalse; } org[0] = origin[0]; org[1] = origin[1]; org[2] = origin[2]; if ( camera[camNum].getCameraInfo( time, org, dir, fov ) ) { origin[0] = org[0]; origin[1] = org[1]; origin[2] = org[2]; angles[1] = atan2( dir[1], dir[0] ) * 180 / 3.14159; angles[0] = asin( dir[2] ) * 180 / 3.14159; return qtrue; } return qfalse; } void startCamera( int camNum, int time ) { if ( camNum < 0 || camNum >= MAX_CAMERAS ) { return; } camera[camNum].startCamera( time ); } } //#include "../shared/windings.h" //#include "../qcommon/qcommon.h" //#include "../sys/sys_public.h" //#include "../game/game_entity.h" idCameraDef splineList; idCameraDef *g_splineList = &splineList; idVec3 idSplineList::zero( 0,0,0 ); void glLabeledPoint( idVec3 &color, idVec3 &point, float size, const char *label ) { glColor3fv( color ); glPointSize( size ); glBegin( GL_POINTS ); glVertex3fv( point ); glEnd(); idVec3 v = point; v.x += 1; v.y += 1; v.z += 1; glRasterPos3fv( v ); glCallLists( strlen( label ), GL_UNSIGNED_BYTE, label ); } void glBox( idVec3 &color, idVec3 &point, float size ) { idVec3 mins( point ); idVec3 maxs( point ); mins[0] -= size; mins[1] += size; mins[2] -= size; maxs[0] += size; maxs[1] -= size; maxs[2] += size; glColor3fv( color ); glBegin( GL_LINE_LOOP ); glVertex3f( mins[0],mins[1],mins[2] ); glVertex3f( maxs[0],mins[1],mins[2] ); glVertex3f( maxs[0],maxs[1],mins[2] ); glVertex3f( mins[0],maxs[1],mins[2] ); glEnd(); glBegin( GL_LINE_LOOP ); glVertex3f( mins[0],mins[1],maxs[2] ); glVertex3f( maxs[0],mins[1],maxs[2] ); glVertex3f( maxs[0],maxs[1],maxs[2] ); glVertex3f( mins[0],maxs[1],maxs[2] ); glEnd(); glBegin( GL_LINES ); glVertex3f( mins[0],mins[1],mins[2] ); glVertex3f( mins[0],mins[1],maxs[2] ); glVertex3f( mins[0],maxs[1],maxs[2] ); glVertex3f( mins[0],maxs[1],mins[2] ); glVertex3f( maxs[0],mins[1],mins[2] ); glVertex3f( maxs[0],mins[1],maxs[2] ); glVertex3f( maxs[0],maxs[1],maxs[2] ); glVertex3f( maxs[0],maxs[1],mins[2] ); glEnd(); } void splineTest() { //g_splineList->load("p:/doom/base/maps/test_base1.camera"); } void splineDraw() { //g_splineList->addToRenderer(); } //extern void D_DebugLine( const idVec3 &color, const idVec3 &start, const idVec3 &end ); void debugLine( idVec3 &color, float x, float y, float z, float x2, float y2, float z2 ) { idVec3 from( x, y, z ); idVec3 to( x2, y2, z2 ); //D_DebugLine(color, from, to); } void idSplineList::addToRenderer() { if ( controlPoints.Num() == 0 ) { return; } idVec3 mins, maxs; idVec3 yellow( 1.0, 1.0, 0 ); idVec3 white( 1.0, 1.0, 1.0 ); int i; for ( i = 0; i < controlPoints.Num(); i++ ) { VectorCopy( *controlPoints[i], mins ); VectorCopy( mins, maxs ); mins[0] -= 8; mins[1] += 8; mins[2] -= 8; maxs[0] += 8; maxs[1] -= 8; maxs[2] += 8; debugLine( yellow, mins[0], mins[1], mins[2], maxs[0], mins[1], mins[2] ); debugLine( yellow, maxs[0], mins[1], mins[2], maxs[0], maxs[1], mins[2] ); debugLine( yellow, maxs[0], maxs[1], mins[2], mins[0], maxs[1], mins[2] ); debugLine( yellow, mins[0], maxs[1], mins[2], mins[0], mins[1], mins[2] ); debugLine( yellow, mins[0], mins[1], maxs[2], maxs[0], mins[1], maxs[2] ); debugLine( yellow, maxs[0], mins[1], maxs[2], maxs[0], maxs[1], maxs[2] ); debugLine( yellow, maxs[0], maxs[1], maxs[2], mins[0], maxs[1], maxs[2] ); debugLine( yellow, mins[0], maxs[1], maxs[2], mins[0], mins[1], maxs[2] ); } int step = 0; idVec3 step1; for ( i = 3; i < controlPoints.Num(); i++ ) { for ( float tension = 0.0f; tension < 1.001f; tension += 0.1f ) { float x = 0; float y = 0; float z = 0; for ( int j = 0; j < 4; j++ ) { x += controlPoints[i - ( 3 - j )]->x * calcSpline( j, tension ); y += controlPoints[i - ( 3 - j )]->y * calcSpline( j, tension ); z += controlPoints[i - ( 3 - j )]->z * calcSpline( j, tension ); } if ( step == 0 ) { step1[0] = x; step1[1] = y; step1[2] = z; step = 1; } else { debugLine( white, step1[0], step1[1], step1[2], x, y, z ); step = 0; } } } } void idSplineList::buildSpline() { //int start = Sys_Milliseconds(); clearSpline(); for ( int i = 3; i < controlPoints.Num(); i++ ) { for ( float tension = 0.0f; tension < 1.001f; tension += granularity ) { float x = 0; float y = 0; float z = 0; for ( int j = 0; j < 4; j++ ) { x += controlPoints[i - ( 3 - j )]->x * calcSpline( j, tension ); y += controlPoints[i - ( 3 - j )]->y * calcSpline( j, tension ); z += controlPoints[i - ( 3 - j )]->z * calcSpline( j, tension ); } splinePoints.Append( new idVec3( x, y, z ) ); } } dirty = false; //Com_Printf("Spline build took %f seconds\n", (float)(Sys_Milliseconds() - start) / 1000); } void idSplineList::draw( bool editMode ) { int i; idVec4 yellow( 1, 1, 0, 1 ); if ( controlPoints.Num() == 0 ) { return; } if ( dirty ) { buildSpline(); } glColor3fv( controlColor ); glPointSize( 5 ); glBegin( GL_POINTS ); for ( i = 0; i < controlPoints.Num(); i++ ) { glVertex3fv( *controlPoints[i] ); } glEnd(); if ( editMode ) { for ( i = 0; i < controlPoints.Num(); i++ ) { glBox( activeColor, *controlPoints[i], 4 ); } } //Draw the curve glColor3fv( pathColor ); glBegin( GL_LINE_STRIP ); int count = splinePoints.Num(); for ( i = 0; i < count; i++ ) { glVertex3fv( *splinePoints[i] ); } glEnd(); if ( editMode ) { glColor3fv( segmentColor ); glPointSize( 3 ); glBegin( GL_POINTS ); for ( i = 0; i < count; i++ ) { glVertex3fv( *splinePoints[i] ); } glEnd(); } if ( count > 0 ) { //assert(activeSegment >=0 && activeSegment < count); if ( activeSegment >= 0 && activeSegment < count ) { glBox( activeColor, *splinePoints[activeSegment], 6 ); glBox( yellow, *splinePoints[activeSegment], 8 ); } } } float idSplineList::totalDistance() { // FIXME: save dist and return // if ( controlPoints.Num() == 0 ) { return 0.0; } if ( dirty ) { buildSpline(); } float dist = 0.0; idVec3 temp; int count = splinePoints.Num(); for ( int i = 1; i < count; i++ ) { temp = *splinePoints[i - 1]; temp -= *splinePoints[i]; dist += temp.Length(); } return dist; } void idSplineList::initPosition( long bt, long totalTime ) { if ( dirty ) { buildSpline(); } if ( splinePoints.Num() == 0 ) { return; } baseTime = bt; time = totalTime; // calc distance to travel ( this will soon be broken into time segments ) splineTime.Clear(); splineTime.Append( bt ); double dist = totalDistance(); double distSoFar = 0.0; idVec3 temp; int count = splinePoints.Num(); //for(int i = 2; i < count - 1; i++) { for ( int i = 1; i < count; i++ ) { temp = *splinePoints[i - 1]; temp -= *splinePoints[i]; distSoFar += temp.Length(); double percent = distSoFar / dist; percent *= totalTime; splineTime.Append( percent + bt ); } assert( splineTime.Num() == splinePoints.Num() ); activeSegment = 0; } float idSplineList::calcSpline( int step, float tension ) { switch ( step ) { case 0: return ( pow( 1 - tension, 3 ) ) / 6; case 1: return ( 3 * pow( tension, 3 ) - 6 * pow( tension, 2 ) + 4 ) / 6; case 2: return ( -3 * pow( tension, 3 ) + 3 * pow( tension, 2 ) + 3 * tension + 1 ) / 6; case 3: return pow( tension, 3 ) / 6; } return 0.0; } void idSplineList::updateSelection( const idVec3 &move ) { if ( selected ) { dirty = true; VectorAdd( *selected, move, *selected ); } } void idSplineList::setSelectedPoint( idVec3 *p ) { if ( p ) { p->Snap(); for ( int i = 0; i < controlPoints.Num(); i++ ) { if ( *p == *controlPoints[i] ) { selected = controlPoints[i]; } } } else { selected = NULL; } } const idVec3 *idSplineList::getPosition( long t ) { static idVec3 interpolatedPos; int count = splineTime.Num(); if ( count == 0 ) { return &zero; } // Com_Printf("Time: %d\n", t); assert( splineTime.Num() == splinePoints.Num() ); while ( activeSegment < count ) { if ( splineTime[activeSegment] >= t ) { if ( activeSegment > 0 && activeSegment < count - 1 ) { double timeHi = splineTime[activeSegment + 1]; double timeLo = splineTime[activeSegment - 1]; double percent = ( timeHi - t ) / ( timeHi - timeLo ); // pick two bounding points idVec3 v1 = *splinePoints[activeSegment - 1]; idVec3 v2 = *splinePoints[activeSegment + 1]; v2 *= ( 1.0 - percent ); v1 *= percent; v2 += v1; interpolatedPos = v2; return &interpolatedPos; } return splinePoints[activeSegment]; } else { activeSegment++; } } return splinePoints[count - 1]; } void idSplineList::parse( const char *( *text ) ) { const char *token; //Com_MatchToken( text, "{" ); do { token = Com_Parse( text ); if ( !token[0] ) { break; } if ( !Q_stricmp( token, "}" ) ) { break; } do { // if token is not a brace, it is a key for a key/value pair if ( !token[0] || !Q_stricmp( token, "(" ) || !Q_stricmp( token, "}" ) ) { break; } Com_UngetToken(); idStr key = Com_ParseOnLine( text ); const char *token = Com_Parse( text ); if ( Q_stricmp( key.c_str(), "granularity" ) == 0 ) { granularity = atof( token ); } else if ( Q_stricmp( key.c_str(), "name" ) == 0 ) { name = token; } token = Com_Parse( text ); } while ( 1 ); if ( !Q_stricmp( token, "}" ) ) { break; } Com_UngetToken(); // read the control point idVec3 point; Com_Parse1DMatrix( text, 3, point ); addPoint( point.x, point.y, point.z ); } while ( 1 ); //Com_UngetToken(); //Com_MatchToken( text, "}" ); dirty = true; } void idSplineList::write( fileHandle_t file, const char *p ) { idStr s = va( "\t\t%s {\n", p ); FS_Write( s.c_str(), s.length(), file ); //s = va("\t\tname %s\n", name.c_str()); //FS_Write(s.c_str(), s.length(), file); s = va( "\t\t\tgranularity %f\n", granularity ); FS_Write( s.c_str(), s.length(), file ); int count = controlPoints.Num(); for ( int i = 0; i < count; i++ ) { s = va( "\t\t\t( %f %f %f )\n", controlPoints[i]->x, controlPoints[i]->y, controlPoints[i]->z ); FS_Write( s.c_str(), s.length(), file ); } s = "\t\t}\n"; FS_Write( s.c_str(), s.length(), file ); } void idCameraDef::getActiveSegmentInfo( int segment, idVec3 &origin, idVec3 &direction, float *fov ) { #if 0 if ( !cameraSpline.validTime() ) { buildCamera(); } double d = (double)segment / numSegments(); getCameraInfo( d * totalTime * 1000, origin, direction, fov ); #endif /* if (!cameraSpline.validTime()) { buildCamera(); } origin = *cameraSpline.getSegmentPoint(segment); idVec3 temp; int numTargets = getTargetSpline()->controlPoints.Num(); int count = cameraSpline.splineTime.Num(); if (numTargets == 0) { // follow the path if (cameraSpline.getActiveSegment() < count - 1) { temp = *cameraSpline.splinePoints[cameraSpline.getActiveSegment()+1]; } } else if (numTargets == 1) { temp = *getTargetSpline()->controlPoints[0]; } else { temp = *getTargetSpline()->getSegmentPoint(segment); } temp -= origin; temp.Normalize(); direction = temp; */ } bool idCameraDef::getCameraInfo( long time, idVec3 &origin, idVec3 &direction, float *fv ) { char buff[1024]; if ( ( time - startTime ) / 1000 > totalTime ) { return false; } for ( int i = 0; i < events.Num(); i++ ) { if ( time >= startTime + events[i]->getTime() && !events[i]->getTriggered() ) { events[i]->setTriggered( true ); if ( events[i]->getType() == idCameraEvent::EVENT_TARGET ) { setActiveTargetByName( events[i]->getParam() ); getActiveTarget()->start( startTime + events[i]->getTime() ); //Com_Printf("Triggered event switch to target: %s\n",events[i]->getParam()); } else if ( events[i]->getType() == idCameraEvent::EVENT_TRIGGER ) { //idEntity *ent = NULL; //ent = level.FindTarget( ent, events[i]->getParam()); //if (ent) { // ent->signal( SIG_TRIGGER ); // ent->ProcessEvent( &EV_Activate, world ); //} } else if ( events[i]->getType() == idCameraEvent::EVENT_FOV ) { memset( buff, 0, sizeof( buff ) ); strcpy( buff, events[i]->getParam() ); const char *param1 = strtok( buff, " \t,\0" ); const char *param2 = strtok( NULL, " \t,\0" ); float len = ( param2 ) ? atof( param2 ) : 0; float newfov = ( param1 ) ? atof( param1 ) : 90; fov.reset( fov.getFOV( time ), newfov, time, len ); //*fv = fov = atof(events[i]->getParam()); } else if ( events[i]->getType() == idCameraEvent::EVENT_FADEIN ) { float time = atof( events[i]->getParam() ); Cbuf_AddText( va( "fade 0 0 0 0 %f", time ) ); Cbuf_Execute(); } else if ( events[i]->getType() == idCameraEvent::EVENT_FADEOUT ) { float time = atof( events[i]->getParam() ); Cbuf_AddText( va( "fade 0 0 0 255 %f", time ) ); Cbuf_Execute(); } else if ( events[i]->getType() == idCameraEvent::EVENT_CAMERA ) { memset( buff, 0, sizeof( buff ) ); strcpy( buff, events[i]->getParam() ); const char *param1 = strtok( buff, " \t,\0" ); const char *param2 = strtok( NULL, " \t,\0" ); if ( param2 ) { loadCamera( atoi( param1 ), va( "cameras/%s.camera", param2 ) ); startCamera( time ); } else { loadCamera( 0, va( "cameras/%s.camera", events[i]->getParam() ) ); startCamera( time ); } return true; } else if ( events[i]->getType() == idCameraEvent::EVENT_STOP ) { return false; } } } origin = *cameraPosition->getPosition( time ); *fv = fov.getFOV( time ); idVec3 temp = origin; int numTargets = targetPositions.Num(); if ( numTargets == 0 ) { /* // follow the path if (cameraSpline.getActiveSegment() < count - 1) { temp = *cameraSpline.splinePoints[cameraSpline.getActiveSegment()+1]; if (temp == origin) { int index = cameraSpline.getActiveSegment() + 2; while (temp == origin && index < count - 1) { temp = *cameraSpline.splinePoints[index++]; } } } */ } else { if ( getActiveTarget()->numPoints() > 0 ) { temp = *getActiveTarget()->getPosition( time ); } } temp -= origin; temp.Normalize(); direction = temp; return true; } bool idCameraDef::waitEvent( int index ) { //for (int i = 0; i < events.Num(); i++) { // if (events[i]->getSegment() == index && events[i]->getType() == idCameraEvent::EVENT_WAIT) { // return true; // } //} return false; } const int NUM_CCELERATION_SEGS = 10; const int CELL_AMT = 5; void idCameraDef::buildCamera() { int i; idList waits; idList targets; totalTime = baseTime; cameraPosition->setTime( (long)totalTime * 1000 ); // we have a base time layout for the path and the target path // now we need to layer on any wait or speed changes for ( i = 0; i < events.Num(); i++ ) { events[i]->setTriggered( false ); switch ( events[i]->getType() ) { default: break; case idCameraEvent::EVENT_TARGET: { targets.Append( i ); break; } case idCameraEvent::EVENT_FEATHER: { long startTime = 0; float speed = 0; long loopTime = 10; float stepGoal = cameraPosition->getBaseVelocity() / ( 1000 / loopTime ); while ( startTime <= 1000 ) { cameraPosition->addVelocity( startTime, loopTime, speed ); speed += stepGoal; if ( speed > cameraPosition->getBaseVelocity() ) { speed = cameraPosition->getBaseVelocity(); } startTime += loopTime; } startTime = (long)( totalTime * 1000 - 1000 ); long endTime = startTime + 1000; speed = cameraPosition->getBaseVelocity(); while ( startTime < endTime ) { speed -= stepGoal; if ( speed < 0 ) { speed = 0; } cameraPosition->addVelocity( startTime, loopTime, speed ); startTime += loopTime; } break; } case idCameraEvent::EVENT_WAIT: { waits.Append( atof( events[i]->getParam() ) ); //FIXME: this is quite hacky for Wolf E3, accel and decel needs // do be parameter based etc.. long startTime = events[i]->getTime() - 1000; if ( startTime < 0 ) { startTime = 0; } float speed = cameraPosition->getBaseVelocity(); long loopTime = 10; float steps = speed / ( ( events[i]->getTime() - startTime ) / loopTime ); while ( startTime <= events[i]->getTime() - loopTime ) { cameraPosition->addVelocity( startTime, loopTime, speed ); speed -= steps; startTime += loopTime; } cameraPosition->addVelocity( events[i]->getTime(), (long)atof( events[i]->getParam() ) * 1000, 0 ); startTime = (long)( events[i]->getTime() + atof( events[i]->getParam() ) * 1000 ); long endTime = startTime + 1000; speed = 0; while ( startTime <= endTime ) { cameraPosition->addVelocity( startTime, loopTime, speed ); speed += steps; startTime += loopTime; } break; } case idCameraEvent::EVENT_TARGETWAIT: { //targetWaits.Append(i); break; } case idCameraEvent::EVENT_SPEED: { /* // take the average delay between up to the next five segments float adjust = atof(events[i]->getParam()); int index = events[i]->getSegment(); total = 0; count = 0; // get total amount of time over the remainder of the segment for (j = index; j < cameraSpline.numSegments() - 1; j++) { total += cameraSpline.getSegmentTime(j + 1) - cameraSpline.getSegmentTime(j); count++; } // multiply that by the adjustment double newTotal = total * adjust; // what is the difference.. newTotal -= total; totalTime += newTotal / 1000; // per segment difference newTotal /= count; int additive = newTotal; // now propogate that difference out to each segment for (j = index; j < cameraSpline.numSegments(); j++) { cameraSpline.addSegmentTime(j, additive); additive += newTotal; } break; */ } } } for ( i = 0; i < waits.Num(); i++ ) { totalTime += waits[i]; } // on a new target switch, we need to take time to this point ( since last target switch ) // and allocate it across the active target, then reset time to this point long timeSoFar = 0; long total = (long)( totalTime * 1000 ); for ( i = 0; i < targets.Num(); i++ ) { long t; if ( i < targets.Num() - 1 ) { t = events[targets[i + 1]]->getTime(); } else { t = total - timeSoFar; } // t is how much time to use for this target setActiveTargetByName( events[targets[i]]->getParam() ); getActiveTarget()->setTime( t ); timeSoFar += t; } } void idCameraDef::startCamera( long t ) { cameraPosition->clearVelocities(); cameraPosition->start( t ); buildCamera(); fov.reset( 90, 90, t, 0 ); //for (int i = 0; i < targetPositions.Num(); i++) { // targetPositions[i]-> //} startTime = t; cameraRunning = true; } void idCameraDef::parse( const char *( *text ) ) { const char *token; do { token = Com_Parse( text ); if ( !token[0] ) { break; } if ( !Q_stricmp( token, "}" ) ) { break; } if ( Q_stricmp( token, "time" ) == 0 ) { baseTime = Com_ParseFloat( text ); } else if ( Q_stricmp( token, "camera_fixed" ) == 0 ) { cameraPosition = new idFixedPosition(); cameraPosition->parse( text ); } else if ( Q_stricmp( token, "camera_interpolated" ) == 0 ) { cameraPosition = new idInterpolatedPosition(); cameraPosition->parse( text ); } else if ( Q_stricmp( token, "camera_spline" ) == 0 ) { cameraPosition = new idSplinePosition(); cameraPosition->parse( text ); } else if ( Q_stricmp( token, "target_fixed" ) == 0 ) { idFixedPosition *pos = new idFixedPosition(); pos->parse( text ); targetPositions.Append( pos ); } else if ( Q_stricmp( token, "target_interpolated" ) == 0 ) { idInterpolatedPosition *pos = new idInterpolatedPosition(); pos->parse( text ); targetPositions.Append( pos ); } else if ( Q_stricmp( token, "target_spline" ) == 0 ) { idSplinePosition *pos = new idSplinePosition(); pos->parse( text ); targetPositions.Append( pos ); } else if ( Q_stricmp( token, "fov" ) == 0 ) { fov.parse( text ); } else if ( Q_stricmp( token, "event" ) == 0 ) { idCameraEvent *event = new idCameraEvent(); event->parse( text ); addEvent( event ); } } while ( 1 ); if ( !cameraPosition ) { Com_Printf( "no camera position specified\n" ); // prevent a crash later on cameraPosition = new idFixedPosition(); } Com_UngetToken(); Com_MatchToken( text, "}" ); } bool idCameraDef::load( const char *filename ) { char *buf; const char *buf_p; FS_ReadFile( filename, (void **)&buf ); if ( !buf ) { return false; } clear(); Com_BeginParseSession( filename ); buf_p = buf; parse( &buf_p ); Com_EndParseSession(); FS_FreeFile( buf ); return true; } void idCameraDef::save( const char *filename ) { fileHandle_t file = FS_FOpenFileWrite( filename ); if ( file ) { int i; idStr s = "cameraPathDef { \n"; FS_Write( s.c_str(), s.length(), file ); s = va( "\ttime %f\n", baseTime ); FS_Write( s.c_str(), s.length(), file ); cameraPosition->write( file, va( "camera_%s",cameraPosition->typeStr() ) ); for ( i = 0; i < numTargets(); i++ ) { targetPositions[i]->write( file, va( "target_%s", targetPositions[i]->typeStr() ) ); } for ( i = 0; i < events.Num(); i++ ) { events[i]->write( file, "event" ); } fov.write( file, "fov" ); s = "}\n"; FS_Write( s.c_str(), s.length(), file ); } FS_FCloseFile( file ); } int idCameraDef::sortEvents( const void *p1, const void *p2 ) { idCameraEvent *ev1 = (idCameraEvent*)( p1 ); idCameraEvent *ev2 = (idCameraEvent*)( p2 ); if ( ev1->getTime() > ev2->getTime() ) { return -1; } if ( ev1->getTime() < ev2->getTime() ) { return 1; } return 0; } void idCameraDef::addEvent( idCameraEvent *event ) { events.Append( event ); //events.Sort(&sortEvents); } void idCameraDef::addEvent( idCameraEvent::eventType t, const char *param, long time ) { addEvent( new idCameraEvent( t, param, time ) ); buildCamera(); } void idCameraDef::removeEvent( int index ) { events.RemoveIndex( index ); buildCamera(); } const char *idCameraEvent::eventStr[] = { "NA", "WAIT", "TARGETWAIT", "SPEED", "TARGET", "SNAPTARGET", "FOV", "CMD", "TRIGGER", "STOP", "CAMERA", "FADEOUT", "FADEIN", "FEATHER" }; void idCameraEvent::parse( const char *( *text ) ) { const char *token; Com_MatchToken( text, "{" ); do { token = Com_Parse( text ); if ( !token[0] ) { break; } if ( !strcmp( token, "}" ) ) { break; } // here we may have to jump over brush epairs ( only used in editor ) do { // if token is not a brace, it is a key for a key/value pair if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) { break; } Com_UngetToken(); idStr key = Com_ParseOnLine( text ); const char *token = Com_Parse( text ); if ( Q_stricmp( key.c_str(), "type" ) == 0 ) { type = static_cast( atoi( token ) ); } else if ( Q_stricmp( key.c_str(), "param" ) == 0 ) { paramStr = token; } else if ( Q_stricmp( key.c_str(), "time" ) == 0 ) { time = atoi( token ); } token = Com_Parse( text ); } while ( 1 ); if ( !strcmp( token, "}" ) ) { break; } } while ( 1 ); Com_UngetToken(); Com_MatchToken( text, "}" ); } void idCameraEvent::write( fileHandle_t file, const char *name ) { idStr s = va( "\t%s {\n", name ); FS_Write( s.c_str(), s.length(), file ); s = va( "\t\ttype %d\n", static_cast( type ) ); FS_Write( s.c_str(), s.length(), file ); s = va( "\t\tparam \"%s\"\n", paramStr.c_str() ); FS_Write( s.c_str(), s.length(), file ); s = va( "\t\ttime %d\n", time ); FS_Write( s.c_str(), s.length(), file ); s = "\t}\n"; FS_Write( s.c_str(), s.length(), file ); } const char *idCameraPosition::positionStr[] = { "Fixed", "Interpolated", "Spline", }; const idVec3 *idInterpolatedPosition::getPosition( long t ) { static idVec3 interpolatedPos; float velocity = getVelocity( t ); float timePassed = t - lastTime; lastTime = t; // convert to seconds timePassed /= 1000; float distToTravel = timePassed * velocity; idVec3 temp = startPos; temp -= endPos; float distance = temp.Length(); distSoFar += distToTravel; float percent = (float)( distSoFar ) / distance; if ( percent > 1.0 ) { percent = 1.0; } else if ( percent < 0.0 ) { percent = 0.0; } // the following line does a straigt calc on percentage of time // float percent = (float)(startTime + time - t) / time; idVec3 v1 = startPos; idVec3 v2 = endPos; v1 *= ( 1.0 - percent ); v2 *= percent; v1 += v2; interpolatedPos = v1; return &interpolatedPos; } void idCameraFOV::parse( const char *( *text ) ) { const char *token; Com_MatchToken( text, "{" ); do { token = Com_Parse( text ); if ( !token[0] ) { break; } if ( !strcmp( token, "}" ) ) { break; } // here we may have to jump over brush epairs ( only used in editor ) do { // if token is not a brace, it is a key for a key/value pair if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) { break; } Com_UngetToken(); idStr key = Com_ParseOnLine( text ); const char *token = Com_Parse( text ); if ( Q_stricmp( key.c_str(), "fov" ) == 0 ) { fov = atof( token ); } else if ( Q_stricmp( key.c_str(), "startFOV" ) == 0 ) { startFOV = atof( token ); } else if ( Q_stricmp( key.c_str(), "endFOV" ) == 0 ) { endFOV = atof( token ); } else if ( Q_stricmp( key.c_str(), "time" ) == 0 ) { time = atoi( token ); } token = Com_Parse( text ); } while ( 1 ); if ( !strcmp( token, "}" ) ) { break; } } while ( 1 ); Com_UngetToken(); Com_MatchToken( text, "}" ); } bool idCameraPosition::parseToken( const char *key, const char *( *text ) ) { const char *token = Com_Parse( text ); if ( Q_stricmp( key, "time" ) == 0 ) { time = atol( token ); return true; } else if ( Q_stricmp( key, "type" ) == 0 ) { type = static_cast( atoi( token ) ); return true; } else if ( Q_stricmp( key, "velocity" ) == 0 ) { long t = atol( token ); token = Com_Parse( text ); long d = atol( token ); token = Com_Parse( text ); float s = atof( token ); addVelocity( t, d, s ); return true; } else if ( Q_stricmp( key, "baseVelocity" ) == 0 ) { baseVelocity = atof( token ); return true; } else if ( Q_stricmp( key, "name" ) == 0 ) { name = token; return true; } else if ( Q_stricmp( key, "time" ) == 0 ) { time = atoi( token ); return true; } Com_UngetToken(); return false; } void idFixedPosition::parse( const char *( *text ) ) { const char *token; Com_MatchToken( text, "{" ); do { token = Com_Parse( text ); if ( !token[0] ) { break; } if ( !strcmp( token, "}" ) ) { break; } // here we may have to jump over brush epairs ( only used in editor ) do { // if token is not a brace, it is a key for a key/value pair if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) { break; } Com_UngetToken(); idStr key = Com_ParseOnLine( text ); Com_Parse( text ); if ( Q_stricmp( key.c_str(), "pos" ) == 0 ) { Com_UngetToken(); Com_Parse1DMatrix( text, 3, pos ); } else { Com_UngetToken(); idCameraPosition::parseToken( key.c_str(), text ); } Com_Parse( text ); } while ( 1 ); if ( !strcmp( token, "}" ) ) { break; } } while ( 1 ); Com_UngetToken(); Com_MatchToken( text, "}" ); } void idInterpolatedPosition::parse( const char *( *text ) ) { const char *token; Com_MatchToken( text, "{" ); do { token = Com_Parse( text ); if ( !token[0] ) { break; } if ( !strcmp( token, "}" ) ) { break; } // here we may have to jump over brush epairs ( only used in editor ) do { // if token is not a brace, it is a key for a key/value pair if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) { break; } Com_UngetToken(); idStr key = Com_ParseOnLine( text ); Com_Parse( text ); if ( Q_stricmp( key.c_str(), "startPos" ) == 0 ) { Com_UngetToken(); Com_Parse1DMatrix( text, 3, startPos ); } else if ( Q_stricmp( key.c_str(), "endPos" ) == 0 ) { Com_UngetToken(); Com_Parse1DMatrix( text, 3, endPos ); } else { Com_UngetToken(); idCameraPosition::parseToken( key.c_str(), text ); } Com_Parse( text ); } while ( 1 ); if ( !strcmp( token, "}" ) ) { break; } } while ( 1 ); Com_UngetToken(); Com_MatchToken( text, "}" ); } void idSplinePosition::parse( const char *( *text ) ) { const char *token; Com_MatchToken( text, "{" ); do { token = Com_Parse( text ); if ( !token[0] ) { break; } if ( !strcmp( token, "}" ) ) { break; } // here we may have to jump over brush epairs ( only used in editor ) do { // if token is not a brace, it is a key for a key/value pair if ( !token[0] || !strcmp( token, "(" ) || !strcmp( token, "}" ) ) { break; } Com_UngetToken(); idStr key = Com_ParseOnLine( text ); Com_Parse( text ); if ( Q_stricmp( key.c_str(), "target" ) == 0 ) { target.parse( text ); } else { Com_UngetToken(); idCameraPosition::parseToken( key.c_str(), text ); } Com_Parse( text ); } while ( 1 ); if ( !strcmp( token, "}" ) ) { break; } } while ( 1 ); Com_UngetToken(); Com_MatchToken( text, "}" ); } void idCameraFOV::write( fileHandle_t file, const char *p ) { idStr s = va( "\t%s {\n", p ); FS_Write( s.c_str(), s.length(), file ); s = va( "\t\tfov %f\n", fov ); FS_Write( s.c_str(), s.length(), file ); s = va( "\t\tstartFOV %f\n", startFOV ); FS_Write( s.c_str(), s.length(), file ); s = va( "\t\tendFOV %f\n", endFOV ); FS_Write( s.c_str(), s.length(), file ); s = va( "\t\ttime %i\n", time ); FS_Write( s.c_str(), s.length(), file ); s = "\t}\n"; FS_Write( s.c_str(), s.length(), file ); } void idCameraPosition::write( fileHandle_t file, const char *p ) { idStr s = va( "\t\ttime %i\n", time ); FS_Write( s.c_str(), s.length(), file ); s = va( "\t\ttype %i\n", static_cast( type ) ); FS_Write( s.c_str(), s.length(), file ); s = va( "\t\tname %s\n", name.c_str() ); FS_Write( s.c_str(), s.length(), file ); s = va( "\t\tbaseVelocity %f\n", baseVelocity ); FS_Write( s.c_str(), s.length(), file ); for ( int i = 0; i < velocities.Num(); i++ ) { s = va( "\t\tvelocity %i %i %f\n", velocities[i]->startTime, velocities[i]->time, velocities[i]->speed ); FS_Write( s.c_str(), s.length(), file ); } } void idFixedPosition::write( fileHandle_t file, const char *p ) { idStr s = va( "\t%s {\n", p ); FS_Write( s.c_str(), s.length(), file ); idCameraPosition::write( file, p ); s = va( "\t\tpos ( %f %f %f )\n", pos.x, pos.y, pos.z ); FS_Write( s.c_str(), s.length(), file ); s = "\t}\n"; FS_Write( s.c_str(), s.length(), file ); } void idInterpolatedPosition::write( fileHandle_t file, const char *p ) { idStr s = va( "\t%s {\n", p ); FS_Write( s.c_str(), s.length(), file ); idCameraPosition::write( file, p ); s = va( "\t\tstartPos ( %f %f %f )\n", startPos.x, startPos.y, startPos.z ); FS_Write( s.c_str(), s.length(), file ); s = va( "\t\tendPos ( %f %f %f )\n", endPos.x, endPos.y, endPos.z ); FS_Write( s.c_str(), s.length(), file ); s = "\t}\n"; FS_Write( s.c_str(), s.length(), file ); } void idSplinePosition::write( fileHandle_t file, const char *p ) { idStr s = va( "\t%s {\n", p ); FS_Write( s.c_str(), s.length(), file ); idCameraPosition::write( file, p ); target.write( file, "target" ); s = "\t}\n"; FS_Write( s.c_str(), s.length(), file ); } void idCameraDef::addTarget( const char *name, idCameraPosition::positionType type ) { idCameraPosition *pos = newFromType( type ); if ( pos ) { pos->setName( name ); targetPositions.Append( pos ); activeTarget = numTargets() - 1; if ( activeTarget == 0 ) { // first one addEvent( idCameraEvent::EVENT_TARGET, name, 0 ); } } } const idVec3 *idSplinePosition::getPosition( long t ) { static idVec3 interpolatedPos; float velocity = getVelocity( t ); float timePassed = t - lastTime; lastTime = t; // convert to seconds timePassed /= 1000; float distToTravel = timePassed * velocity; distSoFar += distToTravel; double tempDistance = target.totalDistance(); double percent = (double)( distSoFar ) / tempDistance; double targetDistance = percent * tempDistance; tempDistance = 0; double lastDistance1,lastDistance2; lastDistance1 = lastDistance2 = 0; idVec3 temp; int count = target.numSegments(); int i; for ( i = 1; i < count; i++ ) { temp = *target.getSegmentPoint( i - 1 ); temp -= *target.getSegmentPoint( i ); tempDistance += temp.Length(); if ( i & 1 ) { lastDistance1 = tempDistance; } else { lastDistance2 = tempDistance; } if ( tempDistance >= targetDistance ) { break; } } if ( i >= count - 1 ) { interpolatedPos = *target.getSegmentPoint( i - 1 ); } else { #if 0 double timeHi = target.getSegmentTime( i + 1 ); double timeLo = target.getSegmentTime( i - 1 ); double percent = ( timeHi - t ) / ( timeHi - timeLo ); idVec3 v1 = *target.getSegmentPoint( i - 1 ); idVec3 v2 = *target.getSegmentPoint( i + 1 ); v2 *= ( 1.0 - percent ); v1 *= percent; v2 += v1; interpolatedPos = v2; #else if ( lastDistance1 > lastDistance2 ) { double d = lastDistance2; lastDistance2 = lastDistance1; lastDistance1 = d; } idVec3 v1 = *target.getSegmentPoint( i - 1 ); idVec3 v2 = *target.getSegmentPoint( i ); double percent = ( lastDistance2 - targetDistance ) / ( lastDistance2 - lastDistance1 ); v2 *= ( 1.0 - percent ); v1 *= percent; v2 += v1; interpolatedPos = v2; #endif } return &interpolatedPos; }