/*----------------------------------------------------------------------------
 *      vt.c
 *
 *    This file contains the viewing transformation and perspective
 * transformation functions.
 *----------------------------------------------------------------------------
*/

#include    <exec/types.h>
#include    <intuition/intuition.h>
#include    <lattice/stdio.h>
#include    "defs.h"
#include    "ds.h"

extern struct Window  *window ;
extern VIEW_TRANS     view_trans ;
extern ROTATION_INFO  rot_info ;
extern REF_AXES       ref_axes [NUM_LINKS] ;


initialize_viewing_transformation ()
{
 /*---------------------------------------------------------------------------
  *    Pre-calculate the viewing transformations to be used based on an
  * eye coordinate in the arms world coordinate system.
  *    Display the arm after the 1st view transformation is calculated to 
  * amuse the user while the rest are calculated.
  *---------------------------------------------------------------------------
 */
 static VIEWPOINT  viewpoint  =
                        { {   0.0,  200.0,    0.0},       /* front view */
                          {   0.0,    0.0,  350.0},       /* top view */
                          { 200.0,    0.0,    0.0},       /* side view */ 
                          { 200.0,  200.0,  200.0},       /* angle 1 view */  
                          {-100.0,  150.0,  350.0},       /* angle 2 view */
                          { 255.0,  180.0, -250.0}        /* angle 3 view */
                        } ; 

 viewing_transformation (viewpoint.angle1, view_trans.angle1) ;
 rot_info.curr_view_trans  = (int *) &view_trans.angle1 ;

 transform_links () ;
 set_screen_coords () ;
 draw_image (window->RPort) ;

 viewing_transformation (viewpoint.front, view_trans.front) ;
 viewing_transformation (viewpoint.top, view_trans.top) ;
 viewing_transformation (viewpoint.side, view_trans.side) ;
 viewing_transformation (viewpoint.angle2, view_trans.angle2) ;
 viewing_transformation (viewpoint.angle3, view_trans.angle3) ;
}


viewing_transformation (vpoint, view_trans) 

float     vpoint [3] ;
int       view_trans [4][4] ;
{
 FFP      fvpoint [3] ;                   
 int      abs_vpt [3],  fhypot1,  fhypot2 ;
 int      pos_theta,  neg_theta ;
 int      trans [NUM_MATRICES] [4][4] ;
 int      i,  k ;

 /*----------------------------------------------------------------------------
  *   Set the world coordinate arm vertices w.r.t the viewpoint.  The viewpoint 
  * is specified in world coordinates.  The algorithm is based on the example
  * in Newman & Sproul "Principles of Interactive Computer Graphics" 2nd ed.
  * p.348.  An accurate view from any octant in the world coordinate space 
  * can be displayed, given an eye viewpoint.
  *----------------------------------------------------------------------------
 */  

 /* set up conversion to Motorola FFP to speed up floating point operations */

 for (i  = X;  i <= Z;  ++i)  {         
     fvpoint [i].f  = vpoint [i] ;  
     fvpoint [i].i  = SPFieee (fvpoint [i].i) ;
     abs_vpt [i]    = SPAbs (fvpoint [i].i) ;
 }

 /* special cases such as front, side and top views can be treated */
 /* separately for quicker handling */
 
 /* top view */
 if (SPCmp (fvpoint [X].i, ffp.zero.i) == 0  &&
     SPCmp (fvpoint [Y].i, ffp.zero.i) == 0)     {

     set_to_identity_matrix (view_trans) ;   

     view_trans [0][0]  = ffp.zero.i ;  
     view_trans [1][0]  = ffp.one.i ;  
     view_trans [0][1]  = ffp.one.i ;  
     view_trans [1][1]  = ffp.zero.i ;
     view_trans [2][2]  = ffp.neg_one.i ;  
     view_trans [3][2]  = fvpoint [Z].i ;

     return (NULL) ;
 }
 /* front view */
 if (SPCmp (fvpoint [X].i, ffp.zero.i) == 0  &&
     SPCmp (fvpoint [Z].i, ffp.zero.i) == 0)     {

     set_to_identity_matrix (view_trans) ;   

     view_trans [0][0]  = ffp.neg_one.i ;
     view_trans [1][1]  = ffp.zero.i ;
     view_trans [2][1]  = ffp.neg_one.i ;  
     view_trans [2][2]  = ffp.zero.i ;
     view_trans [1][2]  = ffp.neg_one.i ;
     view_trans [3][2]  = fvpoint [Y].i ;

     return (NULL) ;
 }
 /* side view */
 if (SPCmp (fvpoint [Y].i, ffp.zero.i) == 0  &&
     SPCmp (fvpoint [Z].i, ffp.zero.i) == 0)     {

     set_to_identity_matrix (view_trans) ;   

     view_trans [0][0]  = ffp.zero.i ;
     view_trans [1][0]  = ffp.one.i ;  
     view_trans [1][1]  = ffp.zero.i ;
     view_trans [2][1]  = ffp.neg_one.i ;
     view_trans [0][2]  = ffp.neg_one.i ;
     view_trans [2][2]  = ffp.zero.i ;
     view_trans [3][2]  = fvpoint [X].i ;

     return (NULL) ;
 }


 /* the messier viewing transformations follow */

 /* initialize matrices used to the identity matrix */
 for (k = 0;  k < NUM_MATRICES;  ++k)  
     set_to_identity_matrix (trans [k]) ; 
     
 
 /* trans [0] */
 /* translate original world coordinate axes to eye coordinate axes */

 trans [0] [3][0]  = SPNeg (fvpoint [X].i) ;
 trans [0] [3][1]  = SPNeg (fvpoint [Y].i) ;
 trans [0] [3][2]  = SPNeg (fvpoint [Z].i) ;


 /* trans [1] */
 /* rotate about the new X axis by 90 degrees so that the */
 /* new Z axis cuts through the old ZX plane */

 trans [1] [1][1]  = trans [1] [2][2]  = ffp.zero.i ;    /* cos(90) is 0.0 */

 if (SPCmp (fvpoint [Y].i, ffp.zero.i) == 1)  {     /* sin(-90) = -sin(90) */
    trans [1] [1][2]  = ffp.neg_one.i ;             /* =  -1 or +1 */
    trans [1] [2][1]  = ffp.one.i ;
 }
 else   {
    trans [1] [1][2]  = ffp.one.i ;
    trans [1] [2][1]  = ffp.neg_one.i ;
 }


 /* trans [2] */
 /* rotate about the new Y axis so that the new Z axis */
 /* cuts the old Z axis */ 

 fhypot1  = SPSqrt (SPAdd (SPMul (abs_vpt [X], abs_vpt [X]),
                           SPMul (abs_vpt [Y], abs_vpt [Y]))) ;

 trans [2] [0][0]  = trans [2] [2][2]  = SPDiv (fhypot1, abs_vpt [X]) ;

 pos_theta  = SPDiv (fhypot1, abs_vpt [Y]) ;
 neg_theta  = SPNeg (pos_theta) ;
 
 if (SPCmp (fvpoint [X].i, ffp.zero.i) == 1)   {
    trans [2] [2][0]  = pos_theta ;
    trans [2] [0][2]  = neg_theta ;
    }
 else   {
    trans [2] [2][0]  = neg_theta ;
    trans [2] [0][2]  = pos_theta ;
 }


 /* trans [3] */
 /* rotate about the new X axis so that the new Z axis */
 /* passes through the old coordinate system origin */

 fhypot2  = SPSqrt (SPAdd (SPMul (fhypot1, fhypot1),
                           SPMul (abs_vpt [Z], abs_vpt [Z]))) ;
      
 trans [3] [1][1]  = trans [3] [2][2]  = SPDiv (fhypot2, fhypot1) ;
                                                         
 pos_theta  = SPDiv (fhypot1, abs_vpt [Z]) ;
 neg_theta  = SPNeg (pos_theta) ;

 if (SPCmp (SPMul (fvpoint [Y].i, fvpoint [Z].i), ffp.zero.i) == 1)   {
    trans [3] [2][1]  = pos_theta ;
    trans [3] [1][2]  = neg_theta ;
 }    
 else   {
    trans [3] [2][1]  = neg_theta ;
    trans [3] [1][2]  = pos_theta ;
 }


 /* trans [4] */
 /* invert the X and Y axis depending on the viewpoint */

 if (SPCmp (fvpoint [Y].i, ffp.zero.i) == 1)  {
    trans [4] [1][1]  = ffp.neg_one.i ;
    trans [4] [0][0]  = ffp.neg_one.i ; 
 }


 matrix_mult (trans [0], trans [1], trans [5]) ;
 matrix_mult (trans [5], trans [2], trans [0]) ;
 matrix_mult (trans [0], trans [3], trans [1]) ;
 matrix_mult (trans [1], trans [4], view_trans) ;
}


perspective_transformation (aperture)

int   aperture ;
{
 int  persp_trans [4][4] ;
 int  zmax,  zmin ;
 int  temp ;

 /*---------------------------------------------------------------------------
  *     Create a perspective transformation based on the current post viewing
  * transformation coordinated.   The algorithm is taken from  Newman & Sproul
  * "Principles of Interactive Computer Graphics" 2nd ed. p.356.
  *     The Z coordinate information is retained in a format suitable for
  * hidden surface removal.  Face plane equations are still valid.
  *     The aperture setting varies the ratio of the screen size to the
  * distance from the viewpoint. 
  *     The Z coordinates map into a range from 0 to 1, and the X, Y
  * coordinates map into the range -1 to +1.  The closest object in the
  * current view will be at Z position 0, and the farthest at 1. 
  *     This transformation occasionally does strange things to the view.
  *---------------------------------------------------------------------------
 */  
 find_max_min_coords (Z) ;

 zmax  = arm.amax [Z] ;
 zmin  = arm.amin [Z] ;

 set_to_identity_matrix (persp_trans) ;  
 
 aperture  = SPMul (aperture, zmin) ;
 
 temp  = SPSub (SPDiv (zmax, zmin), ffp.one.i) ;

 persp_trans [2][2]  = SPDiv (SPMul (temp, zmin), aperture) ; 
 persp_trans [3][2]  = SPDiv (temp, SPNeg (aperture)) ; 
 persp_trans [2][3]  = SPDiv (zmin, aperture) ;
 persp_trans [3][3]  = ffp.zero.i ;  

 transform_vertices_to_perspective (persp_trans) ;
}


transform_vertices_to_perspective (persp_trans)

int  persp_trans [4][4] ;
{
 LINK_PTR        l ;
 FACE_PTR        f ;
 FFP_POINT_PTR   p_curr ;
 int             i,  j,  k ;

 /*---------------------------------------------------------------------------
  *    Run through all the arm vertices and transform them with the 
  * perspective transformation.
  *---------------------------------------------------------------------------
 */
 l  = &arm.link [UPPER] ;

 for (k = 0;  k < NUM_LINKS;  ++k)   {
     f  = &l->face[0] ;

     for (i = 0;  i < l->num_faces;  ++i)  {
         p_curr  = &f->ffp_curr_vertex[0] ;

         for (j = 0;  j < f->num_vertices;  ++j) 
             transform_persp_pt (p_curr++, persp_trans) ;
         ++f ;
     }
     transform_ref_axes_persp (k, persp_trans) ;
     ++l ;
 }
}


transform_ref_axes_persp (joint, trans)

int  joint ;
int  trans [4][4] ;
{
 FFP_POINT_PTR  p_curr ;
 int            i ;

 /*---------------------------------------------------------------------------
  *    Run through all the reference axes coordinates and transform them
  * with the perspective transformation.
  *---------------------------------------------------------------------------
 */
 p_curr  = &ref_axes [joint].curr_coord [0] ;

 for (i = X;  i <= ORIGIN;  ++i)
    transform_persp_pt (p_curr++, trans) ;
}




transform_persp_pt (p_curr, persp_trans)

int      p_curr [] ;
int      persp_trans [4][4] ;
{
 int     temp [4] ;
 int     i,  j ;

 /*---------------------------------------------------------------------------
  *   Transform one point (vertex) with the perspective transformation.
  *---------------------------------------------------------------------------
 */
 for (i = X;  i <= W;  ++i) 
     temp [i]    = ffp.zero.i ;

 for (i = X;  i <= W;  ++i)
     for (j = X;  j <= W;  ++j)
         temp [i]  = SPAdd (temp [i], SPMul (p_curr [j], persp_trans [j][i])) ;

 for (i = X;  i <= W;  ++i)  
     p_curr [i]  = SPDiv (temp [W], temp [i]) ;
}


set_to_identity_matrix (trans)

int   trans [4][4] ;
{
 int    i,  j ;

 /*---------------------------------------------------------------------------
  *   Set "trans" to the identity matrix.  Use Motorola FFP floats stored
  * in Lattice int variables.
  *---------------------------------------------------------------------------
 */
 for (i = 0;  i < 4;  ++i)   {
     for (j = 0;  j < 4;  ++j)   {
         if (i == j)  trans [i][j]  = ffp.one.i ;
         else         trans [i][j]  = ffp.zero.i ;
     }
 }
}


matrix_mult (t1, t2, r)

int  t1 [4][4],  t2 [4][4],  r [4][4] ;
{
 /*---------------------------------------------------------------------------
  *   Optimized matrix multiplier using Motorola FFP routines.  The float
  * values in the matrices are stored in "ints".
  *   The result of multiplying 4x4 "t1" by 4x4 "t2" is returned in matrix "r".
  *---------------------------------------------------------------------------
 */
 int  i, j, k ;

 for (i = 0;  i < 4;  ++i)
    for (j = 0;  j < 4;  ++j)
       r [i][j]  = ffp.zero.i ;

 for (i = 0;  i < 4;  ++i)
    for (j = 0;  j < 4;  ++j)
       for (k = 0;  k < 4;  ++k)
           r [i][j]  = SPAdd (r [i][j], SPMul (t1 [i][k], t2 [k][j])) ;
}  
