/*****************************************************************************
 * $Id: eng.cc,v 1.403 2006/11/11 03:19:09 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Palomino 3D engine.
 * @verbatim
 * ------------------------------------------------------------------------------
 * Render loop:
 *
 * The render loop is driven by a timer or a redisplay event.
 * Animation is driven by the same timer, and animation drives rendering.
 * Functions called after timer tick:
 * - OpenGL timer event
 * - Engine::Tick()
 * - Engine::Animate()       // usually redisplay=true, if false, rendering is skipped
 * - GFX_POST_REDISPLAY()    // force redisplay
 * - OpenGL redisplay event  // also is starting point if window needs repaint
 * - Engine::Render()
 *
 * The render loop sorts only visible objects and excludes invisible objects.
 * Therefore, usually, Draw() methods no longer should call Visible().
 * An exception is Objects that are part of an ObjectGroup as the render loop
 * only tests the visibility of ObjectGroup, not individual Objects.
 *
 * The render loop guarantees:
 * - Object::Draw() is only called if Object is visible.
 * - Omni::Draw() is called if Omni is visible and only once per frame.
 *   Omnis are linked in a special Quadrant separate from other Objects [2006/11].
 * - Does NOT guarantee the visibility of individual Objects contained by an ObjectGroup.
 *   Grouped Objects are excluded by (hidden from) the render loop, so their Draw()
 *   methods still must call Visible() themselves.
 * ------------------------------------------------------------------------------
 *//*
 * LEGAL:   COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#define ENG_CC 1
#include "eng.hh"
#include "eng_debug.hh"
#include "eng_sort.hh"

/*****************************************************************************
 * Main rendering routine.
 * Called by graphics system to render a frame.
 *****************************************************************************/
void
Engine::Render( void )
{
	if ( ! mBusy )
	{
		++mBusy;
		Object::StartFrame();
		Dyna::StartFrame();
		mView.DrawViews();
		--mBusy;
	}
}

/*****************************************************************************
 * Draw objects in locus of quadrants.
 *
 * In order to properly draw objects with translucent blended polygons
 * (eg cockpit glass), all other opaque objects must be drawn first.
 * Rather than having two passes, for speed, the Craft class is specially
 * iterated thru after the first pass as currently only Craft objects
 * use blending for translucency.  However, the variable View::renderPass
 * does have two values as if two passes were done in case later two passes
 * becomes really necessary.
 *****************************************************************************/
void
Engine::DrawObjects( void )
{
	mView.mCur->mRenderPass = 1;
	gWorld.mLocus.Update();

	mSorter.Draw();

	mView.mCur->mRenderPass = 2;
	gEngineEvent.mDrawObjects.Broadcast();
	mView.mCur->mRenderPass = 0;
	KillZombies();
}

/*****************************************************************************
 * Main animation routine.
 * Called periodically by a timer.
 *****************************************************************************/
void
Engine::Animate( void )
{
	if ( ! GFXSYS_IF_READY() || mView.IfFreeze() ) return;

	mView.UpdateFPS();

	// Skip rendering if the scene hasn't changed.
	mRedisplay = 0;
	gEngineEvent.mAnimate.Broadcast(); // may affect Engine::mRedisplay
	if ( mRedisplay ) GFX_POST_REDISPLAY();
}

/*****************************************************************************
 * Every timer tick.
 * @param	milsecFrame
 *			Elapsed time relative to the previous timer-tick.
 * @param	milsecTotal
 *          Elapsed time relative to the start of the program.
 *****************************************************************************/
void
Engine::Tick( int milsecFrame, int milsecTotal )
{
	mMilsecFrame = milsecFrame;
	mMilsecTotal = milsecTotal;
	DEBUG_CODE( if ( mTestFrameRate ) { TickTestFrameRate(); return; } )
	gEngineEvent.mTick.Broadcast();
	Animate();
}

/*****************************************************************************
 * Reset scene.
 *****************************************************************************/
static void
ResetSceneObject( Object* obj )
{
	obj->Reset();
}
void
Engine::ResetScene( void )
{
	mEye.Reset();
	mView.Reset();
	SetCurrent( &mEye );
	gWorld.ForEachObject( ResetSceneObject );
	gEngineEvent.mResetScene.Broadcast();
	GFX_POST_REDISPLAY();
}
