/*****************************************************************************
 * $Id: eng_dyna.hh,v 1.150 2006/11/24 21:56:57 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Dyna (dynamic object) class.
 * @remarks	FYI: This line is faster but wrong.  It'll cause typesig checks to fail:
 *               return reinterpret_cast<const WorldVertex*>( &mCm[Ox] );
 *
 *          Control response (rate of rotation):
 *          Dyna's control response is determined by a float multiplier.
 *          The Module class adjusts control response in terms of Percent
 *          in range {10%,...,100%}.  0% is illegal since float multipler
 *          would be 0.0 which would disable rotation.
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#ifndef ENG_DYNA_HH
#define ENG_DYNA_HH 1

#include "eng_object.hh"
#include "eng_pilot.hh"  // Pilot, Animiation, ChasePlane class are associated with Dyna

////////////////////////////////////////////////////////////////////////////////
/// @brief Base class for dynamic objects that have their own local coordinate systems and matrix.
///
/// About the set of matrixes:
/// The purpose of the extended-precision matrix xm[] is to use maximum accuracy
/// for rotating a matrix.  The faster single-precision matrix cm[] is used
/// far more frequently for transforming vertexes.  cm[] is a const reference
/// to the protected sm[].  sm[] should never be modified except by SyncMatrix().
/// This dual matrix scheme is a trade-off between speed vs. code complexity.
/// Any modification to xm[] requires synchronizing sm[].  But this requirement
/// is limited to the code in this class as xm[]/sm[] are protected.
///
class Dyna : public virtual Object
{
PREVENT_COPYING(Dyna)
// Trade-off matrix precision for speed.
#if SPEED
#define mXm mSm
#endif
private:	typedef Object Parent;
public:							Dyna( void );
public:		virtual				~Dyna();
// abstract methods:
public:		virtual void		Draw( void ) = 0;
public:		virtual void		Rotate( uint axis, Degree deg ) = 0;
public:		virtual void		Translate( uint axis, fp inc ) = 0;
// virtual methods:
public:		virtual void		Init( void );
public:		virtual void		Reset( void );
public:		virtual int			Animate( void );
// Dyna::*Animation() are just for convenience/brevity (not for overridding).
// Rather, Pilot class's *Animation() is for overriding.
public:     void				SetAnimation( eAnimation animation ) { mPilot->SetAnimation( animation ); }
public:     void				AddAnimation( eAnimation animation ) { mPilot->AddAnimation( animation ); }
public:		void				ToggleAnimation( eAnimation animation ) { mPilot->ToggleAnimation( animation ); }
public:		void				RemoveAnimation( eAnimation animation ) { mPilot->RemoveAnimation( animation ); }
public:     bool				QueryAnimation( eAnimation animation ) { return mPilot->QueryAnimation( animation ); }
public:		virtual void		Reload( const MatrixExt& xm ) { CopyMatrix( mXm/*dest*/, xm/*src*/ ); SyncMatrix(); }
public:		virtual void		SetOrigin( uint Oxyz, fp coord );
public:		virtual void		RotateInc( uint axis, int dir );
public:		virtual void		TranslateInc( uint axis, int dir, fp stride = 0.0 );
public:		virtual void		Step( void );
public:		virtual void		Fence( void );
public:		virtual void		Revive( void ) { }
public:		virtual bool		IfDestroyed( void ) { return mPilot->IfDestroyed(); }  // if crashed, shotdown, etc
// methods:
public:		const Matrix&		GetWorldEyeMatrix( void );
public:		bool				Reproducible( void ) { return false; }
// Get/set position methods.  Extended-precision float type is necessary
// to maintain extended-precision matrix (mXm).
// hi-freq (non-virtual would be faster, but it must be virtual,
// and anyway Object::GetPosition() declares it virtual).
public:		virtual Position GetPosition( void )
			{
				return mPos;
			}
// Position arg should have positive values.
public:		virtual void SetPosition( const Position& pos )
			{
				ASSERT( pos.x >= 0 && pos.y >= 0 && pos.z >= 0 );
				mXm[Ox] = pos.x;
				mXm[Oy] = pos.y;
				mXm[Oz] = pos.z;
				SyncMatrix();		// to update mCm[]/mSm[]
			}
// Maneuverability vs. control response.
// Maneuverability represents a craft's performance characteristics (roll/yaw/pitch rate).
// The user can adjust the control response which scales the base maneuverability values.
// Craft performance characteristics:
public:		void				SetManeuverability( uint axis, Degree degreeMilsec ) { ASSERT(CHECK_AXIS(axis)); mManeuverability[axis] = degreeMilsec; }
public:		fp					GetManeuverability( uint axis )                      { ASSERT(CHECK_AXIS(axis)); return mManeuverability[axis]; }
public:		void				SetEnginePower( fp power ) { }
// Translation rate:
public:		virtual void		SetThrottle( Percent percent );
public:		Percent				GetThrottle( void );
public:		fp					GetStride( uint axis = AXIS_Z );
// Input device methods, user preferences:
public:		void				ClaimInput( void );
public:		bool				HaveInput( void );
// Dyna defines control response as a float multiplier, Module as a Percent.
#define DYNA_CONTROL_RESPONSE_FLOAT_DEFAULT  (1.0)
public:		void				SetResponse( fp response ) { mResponse = Max(0.1,response); }  // min 0.1 to prevent 0.0
public:		fp					GetResponse( void )        { return mResponse; }
private:	int					GetMilsecForDyna( void );
// Update single-precision matrix (and mPos) after extended-precision one is modified.
protected:	virtual void		SyncMatrix( void )
			{
			#if ! SPEED
				mSm[Xx] = mXm[Xx];
				mSm[Xy] = mXm[Xy];
				mSm[Xz] = mXm[Xz];
				mSm[Yx] = mXm[Yx];
				mSm[Yy] = mXm[Yy];
				mSm[Yz] = mXm[Yz];
				mSm[Zx] = mXm[Zx];
				mSm[Zy] = mXm[Zy];
				mSm[Zz] = mXm[Zz];
				mSm[Ox] = mPos.x = mXm[Ox];	// update Position also
				mSm[Oy] = mPos.y = mXm[Oy];
				mSm[Oz] = mPos.z = mXm[Oz];
			#else
				mPos.x = mXm[Ox];	// update Position also
				mPos.y = mXm[Oy];
				mPos.z = mXm[Oz];
			#endif // SPEED
			}
// Data:
public:		const Matrix&		mCm;					///< cm[] is a const reference to sm[]
public:		ChasePlane*			mChasePlane;            /// derived class may reassign pointer
public:		Pilot*				mPilot;					/// derived class may reassign pointer
protected:
#if ! SPEED
protected:	MatrixExt			mXm;					///< extended-precision matrix for accuracy
#endif
protected:	Matrix				mSm;					///< single-precision copy of xm[] for speed
protected:	static Matrix		mWEm;					///< temp Dyna matrix multiplied by Eye matrix (World/Eye)
protected:	Position			mPos;					///< tied with the matrix
protected:	Normal*				mNormals;
protected:	fp					mAxisDir[3];			///< axis orientation (+-1)
protected:	fp					mYmin;					///< must be positive (0 if N/A)
protected:	Percent				mThrottle;
protected:	fp					mSpeed;					///< coord/millisecond
// Maneuverability is defined as by degree/millisecond rate of rotation.
// This rate is independent of the joystick/keyboard response.
protected:	fp					mManeuverability[3];	///< degree/millisecond
protected:	fp					mResponse;				///< scales base maneuverability to suit the user
private:	uint				mFrameWorldEyeMatrix;	///< used to avoid recalculating
private:	static SafePtr<Dyna> mgInputOwner;			///< there can be only one
public:		DECL_TYPESIG( TYPESIG_DYNA )
friend class Tracer;
friend class Pilot;
friend void ConfigCopyMatrix( MatrixExt& dest, const Dyna& dyna );
// Let other modules define friends.
#ifdef DYNA_FRIENDS
DYNA_FRIENDS
#endif
};

#if DEBUG
typedef       SafePtr<Dyna> DynaPtr;
typedef const SafePtr<Dyna> DynaPtrConst;
#else
typedef Dyna*       DynaPtr;
typedef Dyna* const DynaPtrConst;
#endif

////////////////////////////////////////////////////////////////////////////////
/// @brief Dyna object with a geometric mesh.
///
class DynaMesh : public Dyna, public ObjectMesh
{
PREVENT_COPYING(DynaMesh)
public:								DynaMesh( QuadrantPtrConst q );
public:								~DynaMesh();
private:	void					Init( void );
public:		void					Reset( void ) { Dyna::Reset(); ObjectMesh::Reset(); }
public:		virtual Position		GetPosition( void ) { return Dyna::GetPosition(); }
public:		virtual void			SetPosition( const Position& pos ) { Dyna::SetPosition(pos); }
public:		bool					CollisionDetection( const Dyna* gunner = NULL );
public:		virtual LocalVertex*		GetLocalVertexs( void ) = 0;
public:		virtual const WorldVertex*	GetWorldVertexs( void );
public:		virtual const EyeVertex*	GetEyeVertexs( void );
public:		virtual Normal*				GetWorldNormals( void );
/// Avoids recalculating vertexs for the same object in the same frame.
/// These macros should be used in any GetWorldVertexs() that
/// dynamically calculates world vertexs every frame.
/// Unnecessary for objects that pre-calculate world vertexs
/// or objects that cache world vertexs.
/// Be sure that world vertex array will live long enough.
#define RETURN_IF_SAVED_DYNA_WORLD_VERTEXS \
	const WorldVertex* wvSaved = GetSavedWorldVertexs(); \
	if ( wvSaved ) return wvSaved;
#define SAVE_DYNA_WORLD_VERTEXS( WV ) \
	SaveWorldVertexs( (WV) );
private:	static DynaPtr				mWorldVertexsSavedOwner;
private:	static const WorldVertex*	mWorldVertexsSaved;
private:	static fp					mWorldVertexsSaved0x;	// wv[0].x
protected:	const WorldVertex* GetSavedWorldVertexs( void )
			{
				// Did this Dyna save its world vertexs on this frame?
				if ( EX(mWorldVertexsSavedOwner != this) )
					return NULL;  // need to calculate world vertexs
				else			
				{
					// Catch if array contents changed.
					CHECK_TYPESIG( &mWorldVertexsSaved[0], TYPESIG_LIB_VERTEX_WORLD );
					ASSERT( mWorldVertexsSaved[0].x == mWorldVertexsSaved0x );
					return mWorldVertexsSaved;
				}
			}
protected:	void SaveWorldVertexs( const WorldVertex* wv )
			{
				mWorldVertexsSavedOwner = this;  // remember to avoid recalculating
				mWorldVertexsSaved = wv;
				DEBUG_CODE( mWorldVertexsSaved0x = wv[0].x; )
			}
/// Reset state prior to rendering a frame.
public:		static void StartFrame( void )
			{
				// Forget.  Necessary in case one Dyna exists.
				mWorldVertexsSavedOwner = NULL;
				mWorldVertexsSaved = NULL;
			}
};

#endif // ENG_DYNA_HH
