/******************************************************************************
 *{@C
 *      Copyright:      2010 Paul Obermeier (obermeier@tcl3d.org)
 *
 *                      See the file "Tcl3D_License.txt" for information on
 *                      usage and redistribution of this file, and for a
 *                      DISCLAIMER OF ALL WARRANTIES.
 *
 *      Module:         Tcl3D -> tcl3dOgl
 *      Filename:       tcl3dViewMath.c
 *
 *      Author:         Paul Obermeier
 *
 *      Description:    C functions for handling model and viewing matrices in
 *                      OpenGL compatible style.
 *                      These are needed as a replacement for the deprecated
 *                      funtionality of the fixed-function pipeline introduced
 *                      in OpenGL 3.
 *                      This module is intended to be wrapped with Swig for the
 *                      Tcl3D package. Matrices are represented
 *                      as standard Tcl lists.
 *
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <float.h>

#include "tcl3dVecMath.h"
#include "tcl3dViewMath.h"

/**
  * Replacement function for glOrtho.
  * It has the same signature except the last parameter, where the calculated
  * matrix is stored.
  */
void tcl3dOrtho (double left, double right,
                 double bottom, double top,
                 double nearVal, double farVal,
                 float *res)
{
    res[0] = (float)(2.0/(right-left));
    res[1] = 0.0f;
    res[2] = 0.0f;
    res[3] = 0.0f;
    res[4] = 0.0f;
    res[5] = (float)(2.0/(top-bottom));
    res[6] = 0.0f;
    res[7] = 0.0f;
    res[8] = 0.0f;
    res[9] = 0.0f;
    res[10] = (float)(-2.0/(farVal+nearVal));
    res[11] = 0.0f;
    res[12] = (float)(-(right+left)/(right-left));
    res[13] = (float)(-(top+bottom)/(top-bottom));
    res[14] = (float)(-(farVal+nearVal)/(farVal-nearVal));
    res[15] = 1.0f;
}

/** Replacement function for glFrustum.
  * It has the same signature except the last parameter, where the calculated
  * matrix is stored.
  */
void tcl3dFrustum (double left, double right,
                   double bottom, double top,
                   double nearVal, double farVal,
                   float *res)
{
    res[0] = (float)(2.0*nearVal/(right-left));
    res[1] = 0.0f;
    res[2] = 0.0f;
    res[3] = 0.0f;
    res[4] = 0.0f;
    res[5] = (float)(2.0*nearVal/(top-bottom));
    res[6] = 0.0f;
    res[7] = 0.0f;
    res[8] = (float)((right+left)/(right-left));
    res[9] = (float)((top+bottom)/(top-bottom));
    res[10] = (float)(-(farVal+nearVal)/(farVal-nearVal));
    res[11] = -1.0f;
    res[12] =  0.0f;
    res[13] =  0.0f;
    res[14] = (float)(-(2.0*farVal*nearVal)/(farVal-nearVal));
    res[15] =  0.0f;
}

/** Replacement function for gluPerspective.
  * It has the same signature except the last parameter, where the calculated
  * matrix is stored.
  */
void tcl3dPerspective (double fovy, double aspect,
                       double zNear, double zFar,
                       float *res)
{
    double xmin, xmax, ymin, ymax;

    ymax = zNear * tan(fovy * 3.1415926535897932384626433832795 / 360.0);
    ymin = -ymax;
    xmin = ymin * aspect;
    xmax = ymax * aspect;

    tcl3dFrustum (xmin, xmax, ymin, ymax, zNear, zFar, res);
}

/** Replacement function for gluLookAt.
  * It has the same signature except the last parameter, where the calculated
  * matrix is stored.
  */
void tcl3dLookAt (double eyeX, double eyeY, double eyeZ, 
                  double centerX, double centerY, double centerZ,
                  double upX, double upY, double upZ,
                  float *res)
{
    float forward[3], side[3], up[3];
    float matrix[16];
    float trans[16];

    forward[0] = centerX - eyeX;
    forward[1] = centerY - eyeY;
    forward[2] = centerZ - eyeZ;
    tcl3dVec3fNormalize (forward);

    up[0] = upX;
    up[1] = upY;
    up[2] = upZ;
    
    tcl3dVec3fCrossProduct (forward, up, side);
    tcl3dVec3fNormalize (side);

    tcl3dVec3fCrossProduct (side, forward, up);

    matrix[0] = side[0];
    matrix[1] = up[0];
    matrix[2] = -forward[0];
    matrix[3] = 0.0f;
    matrix[4] = side[1];
    matrix[5] = up[1];
    matrix[6] = -forward[1];
    matrix[7] = 0.0f;
    matrix[8] = side[2];
    matrix[9] = up[2];
    matrix[10] = -forward[2];
    matrix[11] = 0.0f;
    matrix[12] = 0.0f;
    matrix[13] = 0.0f;
    matrix[14] = 0.0f;
    matrix[15] = 1.0f;

    tcl3dMatfTranslate (-eyeX, -eyeY, -eyeZ, trans);

    tcl3dMatfMult (matrix, trans, res);
}
