#include "3dc.h"

#include "inline.h"


/*
 externs for commonly used global variables and arrays
*/

	extern MORPHDISPLAY MorphDisplay;
	extern int NormalFrameTime;


/*

 Global Variables

*/





/*

 Update Morphing Animation Control Block

*/

void UpdateMorphing(MORPHCTRL *mcptr)

{

	MORPHHEADER *mhdr = mcptr->ObMorphHeader;
	int UpdateRate;


	/*textprint("UpdateMorphing\n");*/


	if(mcptr->ObMorphFlags & mph_flag_play) {

		/* How fast? */

		if(mcptr->ObMorphSpeed == ONE_FIXED) {

			UpdateRate = NormalFrameTime;

		}

		else {

			UpdateRate = MUL_FIXED(NormalFrameTime, mcptr->ObMorphSpeed);

		}


		/* Update the current frame */

		if(mcptr->ObMorphFlags & mph_flag_reverse) {

			mcptr->ObMorphCurrFrame -= UpdateRate;

			if(mcptr->ObMorphCurrFrame < 0) {

				if(mcptr->ObMorphFlags & mph_flag_noloop) {

					mcptr->ObMorphCurrFrame = 0;

					/* The sequence has finished and we are at the start */

					mcptr->ObMorphFlags |= (mph_flag_finished | mph_flag_start);

				}

				else {

					mcptr->ObMorphCurrFrame += mhdr->mph_maxframes;

					/* The sequence has looped and we are back at the end */

					mcptr->ObMorphFlags |= (mph_flag_looped | mph_flag_end);

				}

			}

		}

		else {

			mcptr->ObMorphCurrFrame += UpdateRate;

			if(mcptr->ObMorphCurrFrame >= mhdr->mph_maxframes) {

				if(mcptr->ObMorphFlags & mph_flag_noloop) {

					/* The sequence has finished and we are at the end */

					mcptr->ObMorphFlags |= (mph_flag_finished | mph_flag_end);

					mcptr->ObMorphCurrFrame = mhdr->mph_maxframes - 1;

				}

				else {

					mcptr->ObMorphCurrFrame -= mhdr->mph_maxframes;

					/* The sequence has looped and we are back at the start */

					mcptr->ObMorphFlags |= (mph_flag_looped | mph_flag_start);

				}

			}

		}

	}

}



/*

 Update the Morphing Animation for this object

*/

void UpdateMorphingDptr(DISPLAYBLOCK *dptr)

{

	SHAPEHEADER *sptr1;
	SHAPEHEADER *sptr2;


	/* Update object radius and extents */

	GetMorphDisplay(&MorphDisplay, dptr);

	sptr1 = MorphDisplay.md_sptr1;
	sptr2 = MorphDisplay.md_sptr2;

	#if 0
	textprint("sptr1->shaperadius = %d\n", sptr1->shaperadius);
	textprint("sptr2->shaperadius = %d\n", sptr2->shaperadius);
	#endif


	/* Radius */

	if(sptr1->shaperadius == sptr2->shaperadius) {

		dptr->ObRadius = sptr1->shaperadius;

	}

	else {

		dptr->ObRadius = WideMul2NarrowDiv(sptr1->shaperadius,
													MorphDisplay.md_one_minus_lerp,
													sptr2->shaperadius,
													MorphDisplay.md_lerp, ONE_FIXED);

	}


	/* X Extent */

	if(sptr1->shapemaxx == sptr2->shapemaxx) {

		dptr->ObMaxX = sptr1->shapemaxx;

	}

	else {

		dptr->ObMaxX = WideMul2NarrowDiv(sptr1->shapemaxx,
													MorphDisplay.md_one_minus_lerp,
													sptr2->shapemaxx,
													MorphDisplay.md_lerp, ONE_FIXED);

	}

	if(sptr1->shapeminx == sptr2->shapeminx) {

		dptr->ObMinX = sptr1->shapeminx;

	}

	else {

		dptr->ObMinX = WideMul2NarrowDiv(sptr1->shapeminx,
													MorphDisplay.md_one_minus_lerp,
													sptr2->shapeminx,
													MorphDisplay.md_lerp, ONE_FIXED);

	}


	/* Y Extent */

	if(sptr1->shapemaxy == sptr2->shapemaxy) {

		dptr->ObMaxY = sptr1->shapemaxy;

	}

	else {

		dptr->ObMaxY = WideMul2NarrowDiv(sptr1->shapemaxy,
													MorphDisplay.md_one_minus_lerp,
													sptr2->shapemaxy,
													MorphDisplay.md_lerp, ONE_FIXED);

	}

	if(sptr1->shapeminy == sptr2->shapeminy) {

		dptr->ObMinY = sptr1->shapeminy;

	}

	else {

		dptr->ObMinY = WideMul2NarrowDiv(sptr1->shapeminy,
													MorphDisplay.md_one_minus_lerp,
													sptr2->shapeminy,
													MorphDisplay.md_lerp, ONE_FIXED);

	}


	/* Z Extent */

 	if(sptr1->shapemaxz == sptr2->shapemaxz) {

		dptr->ObMaxZ = sptr1->shapemaxz;

	}

	else {

		dptr->ObMaxZ = WideMul2NarrowDiv(sptr1->shapemaxz,
													MorphDisplay.md_one_minus_lerp,
													sptr2->shapemaxz,
													MorphDisplay.md_lerp, ONE_FIXED);

	}

	if(sptr1->shapeminz == sptr2->shapeminz) {

		dptr->ObMinZ = sptr1->shapeminz;

	}

	else {

		dptr->ObMinZ = WideMul2NarrowDiv(sptr1->shapeminz,
													MorphDisplay.md_one_minus_lerp,
													sptr2->shapeminz,
													MorphDisplay.md_lerp, ONE_FIXED);

	}

	#if 0
	textprint("dptr->ObRadius = %d\n", dptr->ObRadius);
	#endif

}



/*

 Using the current frame, calculate the lerp values and find out which two
 shapes to interpolate between.

 Write this information back to a MORPHDISPLAY structure.

*/

void GetMorphDisplay(MORPHDISPLAY *md, DISPLAYBLOCK *dptr)

{

	MORPHFRAME *mdata;
	MORPHCTRL *mc = dptr->ObMorphCtrl;
	MORPHHEADER *mhdr = mc->ObMorphHeader;


	md->md_lerp = mc->ObMorphCurrFrame & 0xffff;
	md->md_one_minus_lerp = ONE_FIXED - md->md_lerp;

	mdata = mhdr->mph_frames;
	mdata = &mdata[mc->ObMorphCurrFrame >> 16];

	md->md_shape1 = mdata->mf_shape1;
	md->md_shape2 = mdata->mf_shape2;

	md->md_sptr1 = GetShapeData(md->md_shape1);
	md->md_sptr2 = GetShapeData(md->md_shape2);

}


void CopyMorphCtrl(MORPHCTRL *src, MORPHCTRL *dst)

{

	dst->ObMorphCurrFrame = src->ObMorphCurrFrame;
	dst->ObMorphFlags     = src->ObMorphFlags;
	dst->ObMorphSpeed     = src->ObMorphSpeed;
	dst->ObMorphHeader    = src->ObMorphHeader;

}


 