2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
32 #include "Game_local.h"
35 ===============================================================================
39 Base class for cameras
41 ===============================================================================
44 ABSTRACT_DECLARATION( idEntity, idCamera )
52 void idCamera::Spawn( void ) {
57 idCamera::GetRenderView
60 renderView_t *idCamera::GetRenderView() {
61 renderView_t *rv = idEntity::GetRenderView();
66 /***********************************************************************
70 ***********************************************************************/
71 const idEventDef EV_Camera_SetAttachments( "<getattachments>", NULL );
73 CLASS_DECLARATION( idCamera, idCameraView )
74 EVENT( EV_Activate, idCameraView::Event_Activate )
75 EVENT( EV_Camera_SetAttachments, idCameraView::Event_SetAttachments )
81 idCameraView::idCameraView
84 idCameraView::idCameraView() {
95 void idCameraView::Save( idSaveGame *savefile ) const {
96 savefile->WriteFloat( fov );
97 savefile->WriteObject( attachedTo );
98 savefile->WriteObject( attachedView );
103 idCameraView::Restore
106 void idCameraView::Restore( idRestoreGame *savefile ) {
107 savefile->ReadFloat( fov );
108 savefile->ReadObject( reinterpret_cast<idClass *&>( attachedTo ) );
109 savefile->ReadObject( reinterpret_cast<idClass *&>( attachedView ) );
114 idCameraView::Event_SetAttachments
117 void idCameraView::Event_SetAttachments( ) {
118 SetAttachment( &attachedTo, "attachedTo" );
119 SetAttachment( &attachedView, "attachedView" );
124 idCameraView::Event_Activate
127 void idCameraView::Event_Activate( idEntity *activator ) {
128 if (spawnArgs.GetBool("trigger")) {
129 if (gameLocal.GetCamera() != this) {
130 if ( g_debugCinematic.GetBool() ) {
131 gameLocal.Printf( "%d: '%s' start\n", gameLocal.framenum, GetName() );
134 gameLocal.SetCamera(this);
136 if ( g_debugCinematic.GetBool() ) {
137 gameLocal.Printf( "%d: '%s' stop\n", gameLocal.framenum, GetName() );
139 gameLocal.SetCamera(NULL);
145 =====================
147 =====================
149 void idCameraView::Stop( void ) {
150 if ( g_debugCinematic.GetBool() ) {
151 gameLocal.Printf( "%d: '%s' stop\n", gameLocal.framenum, GetName() );
153 gameLocal.SetCamera(NULL);
154 ActivateTargets( gameLocal.GetLocalPlayer() );
159 =====================
161 =====================
163 void idCameraView::SetAttachment( idEntity **e, const char *p ) {
164 const char *cam = spawnArgs.GetString( p );
165 if ( strlen ( cam ) ) {
166 *e = gameLocal.FindEntity( cam );
172 =====================
174 =====================
176 void idCameraView::Spawn( void ) {
177 // if no target specified use ourself
178 const char *cam = spawnArgs.GetString("cameraTarget");
179 if ( strlen ( cam ) == 0) {
180 spawnArgs.Set("cameraTarget", spawnArgs.GetString("name"));
182 fov = spawnArgs.GetFloat("fov", "90");
184 PostEventMS( &EV_Camera_SetAttachments, 0 );
186 UpdateChangeableSpawnArgs(NULL);
190 =====================
191 idCameraView::GetViewParms
192 =====================
194 void idCameraView::GetViewParms( renderView_t *view ) {
210 view->vieworg = ent->GetPhysics()->GetOrigin();
211 if ( attachedView ) {
212 dir = attachedView->GetPhysics()->GetOrigin() - view->vieworg;
214 view->viewaxis = dir.ToMat3();
216 view->viewaxis = ent->GetPhysics()->GetAxis();
219 gameLocal.CalcFov( fov, view->fov_x, view->fov_y );
223 ===============================================================================
227 ===============================================================================
230 const idEventDef EV_Camera_Start( "start", NULL );
231 const idEventDef EV_Camera_Stop( "stop", NULL );
233 CLASS_DECLARATION( idCamera, idCameraAnim )
234 EVENT( EV_Thread_SetCallback, idCameraAnim::Event_SetCallback )
235 EVENT( EV_Camera_Stop, idCameraAnim::Event_Stop )
236 EVENT( EV_Camera_Start, idCameraAnim::Event_Start )
237 EVENT( EV_Activate, idCameraAnim::Event_Activate )
242 =====================
243 idCameraAnim::idCameraAnim
244 =====================
246 idCameraAnim::idCameraAnim() {
257 =====================
258 idCameraAnim::~idCameraAnim
259 =====================
261 idCameraAnim::~idCameraAnim() {
262 if ( gameLocal.GetCamera() == this ) {
263 gameLocal.SetCamera( NULL );
272 void idCameraAnim::Save( idSaveGame *savefile ) const {
273 savefile->WriteInt( threadNum );
274 savefile->WriteVec3( offset );
275 savefile->WriteInt( frameRate );
276 savefile->WriteInt( starttime );
277 savefile->WriteInt( cycle );
278 activator.Save( savefile );
283 idCameraAnim::Restore
286 void idCameraAnim::Restore( idRestoreGame *savefile ) {
287 savefile->ReadInt( threadNum );
288 savefile->ReadVec3( offset );
289 savefile->ReadInt( frameRate );
290 savefile->ReadInt( starttime );
291 savefile->ReadInt( cycle );
292 activator.Restore( savefile );
298 =====================
300 =====================
302 void idCameraAnim::Spawn( void ) {
303 if ( spawnArgs.GetVector( "old_origin", "0 0 0", offset ) ) {
304 offset = GetPhysics()->GetOrigin() - offset;
309 // always think during cinematics
320 void idCameraAnim::LoadAnim( void ) {
322 idLexer parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS | LEXFL_NOSTRINGCONCAT );
330 key = spawnArgs.GetString( "anim" );
332 gameLocal.Error( "Missing 'anim' key on '%s'", name.c_str() );
335 filename = spawnArgs.GetString( va( "anim %s", key ) );
336 if ( !filename.Length() ) {
337 gameLocal.Error( "Missing 'anim %s' key on '%s'", key, name.c_str() );
340 filename.SetFileExtension( MD5_CAMERA_EXT );
341 if ( !parser.LoadFile( filename ) ) {
342 gameLocal.Error( "Unable to load '%s' on '%s'", filename.c_str(), name.c_str() );
346 cameraCuts.SetGranularity( 1 );
348 camera.SetGranularity( 1 );
350 parser.ExpectTokenString( MD5_VERSION_STRING );
351 version = parser.ParseInt();
352 if ( version != MD5_VERSION ) {
353 parser.Error( "Invalid version %d. Should be version %d\n", version, MD5_VERSION );
356 // skip the commandline
357 parser.ExpectTokenString( "commandline" );
358 parser.ReadToken( &token );
361 parser.ExpectTokenString( "numFrames" );
362 numFrames = parser.ParseInt();
363 if ( numFrames <= 0 ) {
364 parser.Error( "Invalid number of frames: %d", numFrames );
368 parser.ExpectTokenString( "frameRate" );
369 frameRate = parser.ParseInt();
370 if ( frameRate <= 0 ) {
371 parser.Error( "Invalid framerate: %d", frameRate );
375 parser.ExpectTokenString( "numCuts" );
376 numCuts = parser.ParseInt();
377 if ( ( numCuts < 0 ) || ( numCuts > numFrames ) ) {
378 parser.Error( "Invalid number of camera cuts: %d", numCuts );
381 // parse the camera cuts
382 parser.ExpectTokenString( "cuts" );
383 parser.ExpectTokenString( "{" );
384 cameraCuts.SetNum( numCuts );
385 for( i = 0; i < numCuts; i++ ) {
386 cameraCuts[ i ] = parser.ParseInt();
387 if ( ( cameraCuts[ i ] < 1 ) || ( cameraCuts[ i ] >= numFrames ) ) {
388 parser.Error( "Invalid camera cut" );
391 parser.ExpectTokenString( "}" );
393 // parse the camera frames
394 parser.ExpectTokenString( "camera" );
395 parser.ExpectTokenString( "{" );
396 camera.SetNum( numFrames );
397 for( i = 0; i < numFrames; i++ ) {
398 parser.Parse1DMatrix( 3, camera[ i ].t.ToFloatPtr() );
399 parser.Parse1DMatrix( 3, camera[ i ].q.ToFloatPtr() );
400 camera[ i ].fov = parser.ParseFloat();
402 parser.ExpectTokenString( "}" );
405 if ( !gameLocal.GetLocalPlayer() ) {
412 idDebugGraph dtGraph;
413 idDebugGraph dqGraph;
414 gGraph.SetNumSamples( numFrames );
415 tGraph.SetNumSamples( numFrames );
416 qGraph.SetNumSamples( numFrames );
417 dtGraph.SetNumSamples( numFrames );
418 dqGraph.SetNumSamples( numFrames );
420 gameLocal.Printf( "\n\ndelta vec:\n" );
421 float diff_t, last_t, t;
422 float diff_q, last_q, q;
423 diff_t = last_t = 0.0f;
424 diff_q = last_q = 0.0f;
425 for( i = 1; i < numFrames; i++ ) {
426 t = ( camera[ i ].t - camera[ i - 1 ].t ).Length();
427 q = ( camera[ i ].q.ToQuat() - camera[ i - 1 ].q.ToQuat() ).Length();
430 gGraph.AddValue( ( i % 10 ) == 0 );
431 tGraph.AddValue( t );
432 qGraph.AddValue( q );
433 dtGraph.AddValue( diff_t );
434 dqGraph.AddValue( diff_q );
436 gameLocal.Printf( "%d: %.8f : %.8f, %.8f : %.8f\n", i, t, diff_t, q, diff_q );
441 gGraph.Draw( colorBlue, 300.0f );
442 tGraph.Draw( colorOrange, 60.0f );
443 dtGraph.Draw( colorYellow, 6000.0f );
444 qGraph.Draw( colorGreen, 60.0f );
445 dqGraph.Draw( colorCyan, 6000.0f );
454 void idCameraAnim::Start( void ) {
455 cycle = spawnArgs.GetInt( "cycle" );
460 if ( g_debugCinematic.GetBool() ) {
461 gameLocal.Printf( "%d: '%s' start\n", gameLocal.framenum, GetName() );
464 starttime = gameLocal.time;
465 gameLocal.SetCamera( this );
466 BecomeActive( TH_THINK );
468 // if the player has already created the renderview for this frame, have him update it again so that the camera starts this frame
469 if ( gameLocal.GetLocalPlayer()->GetRenderView()->time == gameLocal.time ) {
470 gameLocal.GetLocalPlayer()->CalculateRenderView();
475 =====================
477 =====================
479 void idCameraAnim::Stop( void ) {
480 if ( gameLocal.GetCamera() == this ) {
481 if ( g_debugCinematic.GetBool() ) {
482 gameLocal.Printf( "%d: '%s' stop\n", gameLocal.framenum, GetName() );
485 BecomeInactive( TH_THINK );
486 gameLocal.SetCamera( NULL );
488 idThread::ObjectMoveDone( threadNum, this );
491 ActivateTargets( activator.GetEntity() );
496 =====================
498 =====================
500 void idCameraAnim::Think( void ) {
504 if ( thinkFlags & TH_THINK ) {
505 // check if we're done in the Think function when the cinematic is being skipped (idCameraAnim::GetViewParms isn't called when skipping cinematics).
506 if ( !gameLocal.skipCinematic ) {
510 if ( camera.Num() < 2 ) {
511 // 1 frame anims never end
515 if ( frameRate == USERCMD_HZ ) {
516 frameTime = gameLocal.time - starttime;
517 frame = frameTime / gameLocal.msec;
519 frameTime = ( gameLocal.time - starttime ) * frameRate;
520 frame = frameTime / 1000;
523 if ( frame > camera.Num() + cameraCuts.Num() - 2 ) {
529 // advance start time so that we loop
530 starttime += ( ( camera.Num() - cameraCuts.Num() ) * 1000 ) / frameRate;
539 =====================
540 idCameraAnim::GetViewParms
541 =====================
543 void idCameraAnim::GetViewParms( renderView_t *view ) {
549 cameraFrame_t *camFrame;
559 if ( camera.Num() == 0 ) {
560 // we most likely are in the middle of a restore
561 // FIXME: it would be better to fix it so this doesn't get called during a restore
566 SetTimeState ts( timeGroup );
569 if ( frameRate == USERCMD_HZ ) {
570 frameTime = gameLocal.time - starttime;
571 frame = frameTime / gameLocal.msec;
574 frameTime = ( gameLocal.time - starttime ) * frameRate;
575 frame = frameTime / 1000;
576 lerp = ( frameTime % 1000 ) * 0.001f;
579 // skip any frames where camera cuts occur
582 for( i = 0; i < cameraCuts.Num(); i++ ) {
583 if ( frame < cameraCuts[ i ] ) {
590 if ( g_debugCinematic.GetBool() ) {
591 int prevFrameTime = ( gameLocal.time - starttime - gameLocal.msec ) * frameRate;
592 int prevFrame = prevFrameTime / 1000;
596 for( i = 0; i < cameraCuts.Num(); i++ ) {
597 if ( prevFrame < cameraCuts[ i ] ) {
604 if ( prevCut != cut ) {
605 gameLocal.Printf( "%d: '%s' cut %d\n", gameLocal.framenum, GetName(), cut );
609 // clamp to the first frame. also check if this is a one frame anim. one frame anims would end immediately,
610 // but since they're mainly used for static cams anyway, just stay on it infinitely.
611 if ( ( frame < 0 ) || ( camera.Num() < 2 ) ) {
612 view->viewaxis = camera[ 0 ].q.ToQuat().ToMat3();
613 view->vieworg = camera[ 0 ].t + offset;
614 view->fov_x = camera[ 0 ].fov;
615 } else if ( frame > camera.Num() - 2 ) {
621 // advance start time so that we loop
622 starttime += ( ( camera.Num() - cameraCuts.Num() ) * 1000 ) / frameRate;
623 GetViewParms( view );
628 if ( gameLocal.GetCamera() != NULL ) {
629 // we activated another camera when we stopped, so get it's viewparms instead
630 gameLocal.GetCamera()->GetViewParms( view );
633 // just use our last frame
634 camFrame = &camera[ camera.Num() - 1 ];
635 view->viewaxis = camFrame->q.ToQuat().ToMat3();
636 view->vieworg = camFrame->t + offset;
637 view->fov_x = camFrame->fov;
639 } else if ( lerp == 0.0f ) {
640 camFrame = &camera[ frame ];
641 view->viewaxis = camFrame[ 0 ].q.ToMat3();
642 view->vieworg = camFrame[ 0 ].t + offset;
643 view->fov_x = camFrame[ 0 ].fov;
645 camFrame = &camera[ frame ];
646 invlerp = 1.0f - lerp;
647 q1 = camFrame[ 0 ].q.ToQuat();
648 q2 = camFrame[ 1 ].q.ToQuat();
649 q3.Slerp( q1, q2, lerp );
650 view->viewaxis = q3.ToMat3();
651 view->vieworg = camFrame[ 0 ].t * invlerp + camFrame[ 1 ].t * lerp + offset;
652 view->fov_x = camFrame[ 0 ].fov * invlerp + camFrame[ 1 ].fov * lerp;
655 gameLocal.CalcFov( view->fov_x, view->fov_x, view->fov_y );
657 // setup the pvs for this frame
658 UpdatePVSAreas( view->vieworg );
661 static int lastFrame = 0;
662 static idVec3 lastFrameVec( 0.0f, 0.0f, 0.0f );
663 if ( gameLocal.time != lastFrame ) {
664 gameRenderWorld->DebugBounds( colorCyan, idBounds( view->vieworg ).Expand( 16.0f ), vec3_origin, gameLocal.msec );
665 gameRenderWorld->DebugLine( colorRed, view->vieworg, view->vieworg + idVec3( 0.0f, 0.0f, 2.0f ), 10000, false );
666 gameRenderWorld->DebugLine( colorCyan, lastFrameVec, view->vieworg, 10000, false );
667 gameRenderWorld->DebugLine( colorYellow, view->vieworg + view->viewaxis[ 0 ] * 64.0f, view->vieworg + view->viewaxis[ 0 ] * 66.0f, 10000, false );
668 gameRenderWorld->DebugLine( colorOrange, view->vieworg + view->viewaxis[ 0 ] * 64.0f, view->vieworg + view->viewaxis[ 0 ] * 64.0f + idVec3( 0.0f, 0.0f, 2.0f ), 10000, false );
669 lastFrameVec = view->vieworg;
670 lastFrame = gameLocal.time;
674 if ( g_showcamerainfo.GetBool() ) {
675 gameLocal.Printf( "^5Frame: ^7%d/%d\n\n\n", realFrame + 1, camera.Num() - cameraCuts.Num() );
681 idCameraAnim::Event_Activate
684 void idCameraAnim::Event_Activate( idEntity *_activator ) {
685 activator = _activator;
686 if ( thinkFlags & TH_THINK ) {
695 idCameraAnim::Event_Start
698 void idCameraAnim::Event_Start( void ) {
704 idCameraAnim::Event_Stop
707 void idCameraAnim::Event_Stop( void ) {
713 idCameraAnim::Event_SetCallback
716 void idCameraAnim::Event_SetCallback( void ) {
717 if ( ( gameLocal.GetCamera() == this ) && !threadNum ) {
718 threadNum = idThread::CurrentThreadNum();
719 idThread::ReturnInt( true );
721 idThread::ReturnInt( false );