#include <math.h>
#include "wrend.h"

/* 3D object translation, rotation, scaling, and animation
   Copyright 1995 Egerter Software */



void set_object_pivot (object_3d *obj, float x, float y, float z)
{
 obj->pivotx = x;
 obj->pivoty = y;
 obj->pivotz = z;

 obj->rotatex = 0;
 obj->rotatey = 0;
 obj->rotatez = 0;

 obj->scalex = 1;
 obj->scaley = 1;
 obj->scalez = 1;
}


void translate_object (object_3d *obj, vertex *pts, float x, float y, float z)
{
int start, finish;
int poly, point;
triangle_3d *ptr;

  /* Translate the center of each polygon */
  /*
  start = obj->start_poly;
  finish = obj->end_poly;
  for (poly = start; poly <= finish; poly++)
  {
   ptr = &polylist[poly];
   ptr->center.x += x;
   ptr->center.y += y;
   ptr->center.z += z;
  }
 */
  /* Translate each vertex in this object */
  start = obj->startpoints;
  finish = start + obj->points - 1;

  for (point = start; point <= finish; point++)
  {
   pts[point].local.x += x;
   pts[point].local.y += y;
   pts[point].local.z += z;
  }

 obj->pivotx += x;
 obj->pivoty += y;
 obj->pivotz += z;
}


void scale_object (object_3d *obj, vertex *pts, float x, float y, float z,
		   float pivotx, float pivoty, float pivotz)
{
int start, finish;
int point;
vertex *pt;

 /* Translate each vertex in this object */
 start = obj->startpoints;
 finish = start + obj->points - 1;

 for (point = start; point <= finish; point++)
  {
   pt = &pts[point];

   pt->local.x = (pt->local.x - pivotx) * x + pivotx;
   pt->local.y = (pt->local.y - pivoty) * y + pivoty;
   pt->local.z = (pt->local.z - pivotz) * z + pivotz;
  }

}
   

void rotate_point (float *ptx,    float *pty,    float *ptz, 
		   float x,      float y,      float z,
		   float pivotx, float pivoty, float pivotz)
{
float i,j,k;
float rotx, roty, rotz;
float xo, yo, zo;
float xcos, xsin, ycos, ysin, zcos, zsin;

 xcos = cos (x * M_PI / 180.0);
 xsin = sin (x * M_PI / 180.0);
 ycos = cos (y * M_PI / 180.0);
 ysin = sin (y * M_PI / 180.0);
 zcos = cos (z * M_PI / 180.0);
 zsin = sin (z * M_PI / 180.0);
 
 xo = (*ptx) - pivotx;
 yo = (*pty) - pivoty;
 zo = (*ptz) - pivotz;

 /* Z axis */
 i = (xo * zcos - yo * zsin);
 j = (xo * zsin + yo * zcos);
 k = zo;

 /* X axis */
 roty = (j * xcos - k * xsin);
 rotz = (j * xsin + k * xcos);
 k = rotz;
 
 /* Y axis */
 rotx = (k * ysin + i * ycos);
 rotz = (k * ycos - i * ysin);

 *ptx = rotx + pivotx;
 *pty = roty + pivoty;
 *ptz = rotz + pivotz;
  
   
}




void rotate_object_with_children (object_3d *obj, vertex *pts, float x, float y, float z,
				  float pivotx, float pivoty, float pivotz)
{
float i,j,k;
float rotx, roty, rotz;
int start, finish;
int point, poly;
float xo, yo, zo;
float xcos, xsin, ycos, ysin, zcos, zsin;
vertex *pt;
triangle_3d *ptr;
int child;
object_3d *childptr;

 xcos = cos (x * M_PI / 180.0);
 xsin = sin (x * M_PI / 180.0);
 ycos = cos (y * M_PI / 180.0);
 ysin = sin (y * M_PI / 180.0);
 zcos = cos (z * M_PI / 180.0);
 zsin = sin (z * M_PI / 180.0);

 /* Rotate each vertex in this object */
 start = obj->startpoints;
 finish = start + obj->points - 1;

 for (point = start; point <= finish; point++)
  {
   /* Rotate the vertex */
   pt = &pts[point];

   xo = pt->local.x - pivotx;
   yo = pt->local.y - pivoty;
   zo = pt->local.z - pivotz;

   /* Z axis */
   i = (xo * zcos - yo * zsin);
   j = (xo * zsin + yo * zcos);
   k = zo;

   /* X axis */
   roty = (j * xcos - k * xsin);
   rotz = (j * xsin + k * xcos);
   k = rotz;

   /* Y axis */
   rotx = (k * ysin + i * ycos);
   rotz = (k * ycos - i * ysin);

   pt->local.x = rotx + pivotx;
   pt->local.y = roty + pivoty;
   pt->local.z = rotz + pivotz;
  
   
   /* Rotate the normal */
   xo = pt->normal.x;
   yo = pt->normal.y;
   zo = pt->normal.z;

   /* Z axis */
   i = (xo * zcos - yo * zsin);
   j = (xo * zsin + yo * zcos);
   k = zo;

   /* X axis */
   roty = (j * xcos - k * xsin);
   rotz = (j * xsin + k * xcos);
   k = rotz;

   /* Y axis */
   rotx = (k * ysin + i * ycos);
   rotz = (k * ycos - i * ysin);

   pt->normal.x = rotx;
   pt->normal.y = roty;
   pt->normal.z = rotz;
  
  }

 
 start = obj->start_poly;
 finish = obj->end_poly;

 for (poly = start; poly <= finish; poly++)
  {
   ptr = &polylist[poly];

   /* Rotate the normal of each polygon */
   xo = ptr->normal.x;
   yo = ptr->normal.y;
   zo = ptr->normal.z;

   /* Z axis */
   i = (xo * zcos - yo * zsin);
   j = (xo * zsin + yo * zcos);
   k = zo;

   /* X axis */
   roty = (j * xcos - k * xsin);
   rotz = (j * xsin + k * xcos);
   k = rotz;

   /* Y axis */
   rotx = (k * ysin + i * ycos);
   rotz = (k * ycos - i * ysin);

   ptr->normal.x = rotx;
   ptr->normal.y = roty;
   ptr->normal.z = rotz;
  }

   
 if (obj->maxchild > -1)  
  {
   for (child = 0; child <= obj->maxchild; child++)
    {
     childptr = &objectlist[obj->children[child]];
     rotate_point (&childptr->pivotx, &childptr->pivoty, &childptr->pivotz, 
		    x, y, z, pivotx, pivoty, pivotz);
     rotate_object_with_children (childptr, pts, x, y, z, pivotx, pivoty, 
	pivotz);
    }
  }
}


void rotate_object (object_3d *obj, vertex *pts, float x, float y, float z,
		    float pivotx, float pivoty, float pivotz)
{
float i,j,k;
float rotx, roty, rotz;
int start, finish;
int point, poly;
float xo, yo, zo;
float xcos, xsin, ycos, ysin, zcos, zsin;
vertex *pt;
triangle_3d *ptr;

 xcos = cos (x * M_PI / 180.0);
 xsin = sin (x * M_PI / 180.0);
 ycos = cos (y * M_PI / 180.0);
 ysin = sin (y * M_PI / 180.0);
 zcos = cos (z * M_PI / 180.0);
 zsin = sin (z * M_PI / 180.0);


 //obj->rotatex += x;
 //obj->rotatey += y;
 //obj->rotatez += z;

 /* Rotate each vertex in this object */
 start = obj->startpoints;
 finish = start + obj->points - 1;

 for (point = start; point <= finish; point++)
  {
   /* Rotate the vertex */
   pt = &pts[point];

   xo = pt->local.x - pivotx;
   yo = pt->local.y - pivoty;
   zo = pt->local.z - pivotz;

   /* Z axis */
   i = (xo * zcos - yo * zsin);
   j = (xo * zsin + yo * zcos);
   k = zo;

   /* X axis */
   roty = (j * xcos - k * xsin);
   rotz = (j * xsin + k * xcos);
   k = rotz;

   /* Y axis */
   rotx = (k * ysin + i * ycos);
   rotz = (k * ycos - i * ysin);

   pt->local.x = rotx + pivotx;
   pt->local.y = roty + pivoty;
   pt->local.z = rotz + pivotz;
  
   
   /* Rotate the normal */
   xo = pt->normal.x;
   yo = pt->normal.y;
   zo = pt->normal.z;

   /* Z axis */
   i = (xo * zcos - yo * zsin);
   j = (xo * zsin + yo * zcos);
   k = zo;

   /* X axis */
   roty = (j * xcos - k * xsin);
   rotz = (j * xsin + k * xcos);
   k = rotz;

   /* Y axis */
   rotx = (k * ysin + i * ycos);
   rotz = (k * ycos - i * ysin);

   pt->normal.x = rotx;
   pt->normal.y = roty;
   pt->normal.z = rotz;
  
  }

 
 start = obj->start_poly;
 finish = obj->end_poly;

 for (poly = start; poly <= finish; poly++)
  {
   ptr = &polylist[poly];

   /* Rotate the normal of each polygon */
   xo = ptr->normal.x;
   yo = ptr->normal.y;
   zo = ptr->normal.z;

   /* Z axis */
   i = (xo * zcos - yo * zsin);
   j = (xo * zsin + yo * zcos);
   k = zo;

   /* X axis */
   roty = (j * xcos - k * xsin);
   rotz = (j * xsin + k * xcos);
   k = rotz;

   /* Y axis */
   rotx = (k * ysin + i * ycos);
   rotz = (k * ycos - i * ysin);

   ptr->normal.x = rotx;
   ptr->normal.y = roty;
   ptr->normal.z = rotz;
  }
}



/* Considerations:
  - multiple keys per frame, for different objects
  - additive elements due to hierarchy
  - does scale affect children objects?

  - idea:
    - store additive amounts in object structure
    - for each keyframe, calculate new values and store
     into obj struct

    - for each frame of animation
    - set elements for each objects to 0
    - for each object,
	add elements from obj struct
	for each child of object
	  add elements from parent
    - for each object
      transform if needed



*/




void setup_nextframe (object_3d *obj)
{
int numframes;
int searchframe;
int frame;
float tx, ty, tz,   rx, ry, rz,  sx, sy, sz;
float cx, cy, cz;
keydata *keyprev;
keydata *key;
int advance;

 advance = 0;
 frame = obj->currentframe;
 
 cx = obj->pivotx;
 cy = obj->pivoty;
 cz = obj->pivotz;

 key = &obj->keylist[frame];
 keyprev = key; 

 if ((key->update_translation) && (obj->tstepsleft == -1))
  {
   searchframe = frame+1;
   while ((obj->keylist[searchframe].update_translation == 0) 
      && (searchframe < obj->maxframe))
     searchframe++;
 
   if (obj->keylist[searchframe].update_translation == 1)
   {
    numframes = obj->keylist[searchframe].framenumber -
		obj->keylist[frame].framenumber;
    obj->tstepsleft = numframes;
   
    key = &obj->keylist[searchframe];

    tx = (key->tx - keyprev->tx) / numframes;
    ty = (key->ty - keyprev->ty) / numframes;
    tz = (key->tz - keyprev->tz) / numframes;
    obj->translatex = tx;
    obj->translatey = ty;
    obj->translatez = tz;
    advance = 1;
   }
   else
   if (obj->tstepsleft == 0)
     obj->tstepsleft = -1;

  }
 else
  {
   if (obj->tstepsleft == 0)
     obj->tstepsleft = -1;
  }

 /*if (key->update_scale)
  {
   sx = 1 + (key->sx / numframes);
   sy = 1 + (key->sy / numframes);
   sz = 1 + (key->sz / numframes);
   obj->scalex = sx;
   obj->scaley = sy;
   obj->scalez = sz;
  }*/


 if ((key->update_rotation) && (obj->rstepsleft == -1))
  {
   searchframe = frame+1;
   while ((obj->keylist[searchframe].update_rotation == 0) 
      && (searchframe < obj->maxframe))
     searchframe++;
   
   if (obj->keylist[searchframe].update_rotation == 1)
   {
    numframes = obj->keylist[searchframe].framenumber -
		obj->keylist[frame].framenumber;
    obj->rstepsleft = numframes;

    key = &obj->keylist[searchframe];
   
    rx = (-key->rx) / numframes;
    ry = (-key->ry) / numframes;
    rz = (-key->rz) / numframes;
    obj->rotatex = rx;
    obj->rotatey = ry;
    obj->rotatez = rz;
    advance = 1;
   }
   else
   if (obj->rstepsleft == 0)
     obj->rstepsleft = -1;

  }
 else
  {
   if (obj->rstepsleft == 0)
     obj->rstepsleft = -1;
  }
 
 if (advance) 
  {
   obj->currentframe++; 
   if (obj->currentframe == obj->maxframe)
     obj->currentframe = 0;
  }
}


void animate_object (object_3d *obj, vertex *pts)
{
object_3d *obj2;
object_3d *ch;
int child;
float px, py, pz,  tx, ty, tz,  rx, ry, rz,  sx, sy, sz;

 px = obj->pivotx;
 py = obj->pivoty;
 pz = obj->pivotz;
 sx = obj->scalex;
 sy = obj->scaley;
 sz = obj->scalez;
 rx = obj->rotatex;
 ry = obj->rotatey;
 rz = obj->rotatez;
 tx = obj->translatex;
 ty = obj->translatey;
 tz = obj->translatez;

/* if ((tx != 0) || (ty != 0) || (tz != 0))
  {
   translate_object (obj, pts, tx, ty, tz);
   
   obj2 = obj;
   while (obj2->numchildren > 0)
    {
     ch = &objectlist[obj2->children[0]];
     translate_object (ch, pts, tx, ty, tz);
     obj2 = ch;
    }
  }
  */
/* if ((sx != 1) || (sy != 1) || (sz != 1))
  {
   scale_object (obj, pts, sx, sy, sz, px, py, pz);
   if (obj->numchildren > 0)
    for (child = 0; child < obj->numchildren; child++)
     scale_object (&objectlist[obj->children[child]], pts, sx, sy, sz, px, py, pz);
  }*/

 if ((rx != 0) || (ry != 0) || (rz != 0))
  {
   rotate_object_with_children (obj, pts, rx, ry, rz, px, py, pz);
  }
}



void animate_objects (vertex *pts)
{
int objnum; /* Object number */
object_3d *obj; /* ptr to the object */

 for (objnum = 0; objnum < totalobjects; objnum++)
  {
   obj = &objectlist[objnum];
   if (obj->maxframe > -1)
     {   
      if ((obj->tstepsleft < 0) || (obj->rstepsleft < 0))
	setup_nextframe (obj);
      if ((obj->tstepsleft > -1) || (obj->rstepsleft > -1))
	{
	 animate_object (obj, pts);
	 if (obj->tstepsleft >= 0)
	   obj->tstepsleft--;
	 if (obj->rstepsleft >= 0)
	   obj->rstepsleft--;
	}
       
     }
  }
}
