/**************************************************************************
  dumb3d.cpp - A simple linear algebra library for 3D.

 **************************************************************************/
/**************************************************************************

    (C) Copyright 1995-1997 Microsoft Corp.  All rights reserved.

    You have a royalty-free right to use, modify, reproduce and 
    distribute the Sample Files (and/or any modified version) in 
    any way you find useful, provided that you agree that 
    Microsoft has no warranty obligations or liability for any 
    Sample Application Files which are modified. 

 **************************************************************************/

#include<memory.h>
#include<math.h>
#include"dumb3d.h"

/*--------------------------------------------------------------------------

matrix multiplication.

*/

matrix_4x4 operator*( matrix_4x4 const &Multiplicand,
    matrix_4x4 const &Multiplier )
{
  matrix_4x4 ReturnMatrix;

  for(int i = 0;i < 4;i++)
  {
    for(int j = 0;j < 4;j++)
    {
      real Value = 0;
      
      for(int k = 0;k < 4;k++)
      {
        Value += Multiplicand.GetElement(i,k) *
            Multiplier.GetElement(k,j);
      }

      ReturnMatrix.SetElement(i,j,Value);
    }
  }

  return ReturnMatrix;
}

vector_4 operator*( matrix_4x4 const &Multiplicand,
    vector_4 const &Multiplier )
{
  vector_4 ReturnPoint;

  for(int i = 0;i < 4;i++)
  {
    real Value = 0;
    
    for(int k = 0;k < 4;k++)
    {
      Value += Multiplicand.GetElement(i,k) *
          Multiplier.GetElement(k);
    }

    ReturnPoint.SetElement(i,Value);
  }

  return ReturnPoint;
}

point_4 operator*( matrix_4x4 const &Multiplicand,
    point_4 const &Multiplier )
{
  point_4 ReturnPoint;

  for(int i = 0;i < 4;i++)
  {
    real Value = 0;
    
    for(int k = 0;k < 4;k++)
    {
      Value += Multiplicand.GetElement(i,k) *
          Multiplier.GetElement(k);
    }

    ReturnPoint.SetElement(i,Value);
  }

  return ReturnPoint;
}


/*--------------------------------------------------------------------------

constructor.

*/

matrix_4x4::matrix_4x4( void )
{
  for(int Counter = 0;Counter < 16;Counter++)
  {
    aElements[0][Counter] = 0;
  }

  aElements[0][0] = aElements[1][1] = aElements[2][2] = aElements[3][3] = 1;
}


/*--------------------------------------------------------------------------

Rotations.

*/

matrix_4x4 &matrix_4x4::ConcatenateXRotation( real Degrees )
{
  real Temp01, Temp11, Temp21, Temp31;
  real Temp02, Temp12, Temp22, Temp32;

  real Radians = (Degrees/360) * M_PI * 2;

  real Sin = sin(Radians), Cos = cos(Radians);

  Temp01 = aElements[0][1] * Cos + aElements[0][2] * Sin;
  Temp11 = aElements[1][1] * Cos + aElements[1][2] * Sin;
  Temp21 = aElements[2][1] * Cos + aElements[2][2] * Sin;
  Temp31 = aElements[3][1] * Cos + aElements[3][2] * Sin;

  Temp02 = aElements[0][1] * -Sin + aElements[0][2] * Cos;
  Temp12 = aElements[1][1] * -Sin + aElements[1][2] * Cos;
  Temp22 = aElements[2][1] * -Sin + aElements[2][2] * Cos;
  Temp32 = aElements[3][1] * -Sin + aElements[3][2] * Cos;

  aElements[0][1] = Temp01;
  aElements[1][1] = Temp11;
  aElements[2][1] = Temp21;
  aElements[3][1] = Temp31;
  aElements[0][2] = Temp02;
  aElements[1][2] = Temp12;
  aElements[2][2] = Temp22;
  aElements[3][2] = Temp32;

  return *this;
}

matrix_4x4 &matrix_4x4::ConcatenateYRotation( real Degrees )
{
  real Temp00, Temp10, Temp20, Temp30;
  real Temp02, Temp12, Temp22, Temp32;

  real Radians = (Degrees/360) * M_PI * 2;

  real Sin = sin(Radians), Cos = cos(Radians);

  Temp00 = aElements[0][0] * Cos + aElements[0][2] * -Sin;
  Temp10 = aElements[1][0] * Cos + aElements[1][2] * -Sin;
  Temp20 = aElements[2][0] * Cos + aElements[2][2] * -Sin;
  Temp30 = aElements[3][0] * Cos + aElements[3][2] * -Sin;

  Temp02 = aElements[0][0] * Sin + aElements[0][2] * Cos;
  Temp12 = aElements[1][0] * Sin + aElements[1][2] * Cos;
  Temp22 = aElements[2][0] * Sin + aElements[2][2] * Cos;
  Temp32 = aElements[3][0] * Sin + aElements[3][2] * Cos;

  aElements[0][0] = Temp00;
  aElements[1][0] = Temp10;
  aElements[2][0] = Temp20;
  aElements[3][0] = Temp30;
  aElements[0][2] = Temp02;
  aElements[1][2] = Temp12;
  aElements[2][2] = Temp22;
  aElements[3][2] = Temp32;

  return *this;
}

matrix_4x4 &matrix_4x4::ConcatenateZRotation( real Degrees )
{
  real Temp00, Temp10, Temp20, Temp30;
  real Temp01, Temp11, Temp21, Temp31;

  real Radians = (Degrees/360) * M_PI * 2;

  real Sin = sin(Radians), Cos = cos(Radians);

  Temp00 = aElements[0][0] * Cos + aElements[0][1] * Sin;
  Temp10 = aElements[1][0] * Cos + aElements[1][1] * Sin;
  Temp20 = aElements[2][0] * Cos + aElements[2][1] * Sin;
  Temp30 = aElements[3][0] * Cos + aElements[3][1] * Sin;

  Temp01 = aElements[0][0] * -Sin + aElements[0][1] * Cos;
  Temp11 = aElements[1][0] * -Sin + aElements[1][1] * Cos;
  Temp21 = aElements[2][0] * -Sin + aElements[2][1] * Cos;
  Temp31 = aElements[3][0] * -Sin + aElements[3][1] * Cos;

  aElements[0][0] = Temp00;
  aElements[1][0] = Temp10;
  aElements[2][0] = Temp20;
  aElements[3][0] = Temp30;
  aElements[0][1] = Temp01;
  aElements[1][1] = Temp11;
  aElements[2][1] = Temp21;
  aElements[3][1] = Temp31;

  return *this;
}

/*--------------------------------------------------------------------------

Translations.

*/

matrix_4x4 &matrix_4x4::ConcatenateXTranslation( real Distance )
{
  aElements[0][3] = aElements[0][0] * Distance + aElements[0][3];
  aElements[1][3] = aElements[1][0] * Distance + aElements[1][3];
  aElements[2][3] = aElements[2][0] * Distance + aElements[2][3];
  aElements[3][3] = aElements[3][0] * Distance + aElements[3][3];

  return *this;
}

matrix_4x4 &matrix_4x4::ConcatenateYTranslation( real Distance )
{
  aElements[0][3] = aElements[0][1] * Distance + aElements[0][3];
  aElements[1][3] = aElements[1][1] * Distance + aElements[1][3];
  aElements[2][3] = aElements[2][1] * Distance + aElements[2][3];
  aElements[3][3] = aElements[3][1] * Distance + aElements[3][3];

  return *this;
}

matrix_4x4 &matrix_4x4::ConcatenateZTranslation( real Distance )
{
  aElements[0][3] = aElements[0][2] * Distance + aElements[0][3];
  aElements[1][3] = aElements[1][2] * Distance + aElements[1][3];
  aElements[2][3] = aElements[2][2] * Distance + aElements[2][3];
  aElements[3][3] = aElements[3][2] * Distance + aElements[3][3];

  return *this;
}

/*--------------------------------------------------------------------------

vector normalize.

*/

vector_4 &vector_4::Normalize( void )
{
  real Length = sqrt(GetX()*GetX() + GetY()*GetY() + GetZ()*GetZ());

  SetX(GetX() / Length);
  SetY(GetY() / Length);
  SetZ(GetZ() / Length);

  return *this;
}
  
/*--------------------------------------------------------------------------

view transform ctor.

*/

view_transform::view_transform( point_4 const &Viewpoint,
  vector_4 const &ViewDirection, vector_4 const &Up )
{
  // translate the viewpoint to the origin

  this->ConcatenateXTranslation(-Viewpoint.GetX());
  this->ConcatenateYTranslation(-Viewpoint.GetY());
  this->ConcatenateZTranslation(-Viewpoint.GetZ());

  // get view vectors set up

  vector_4 Right = -CrossProduct(ViewDirection,Up);
  vector_4 ReallyUp = CrossProduct(Right,ViewDirection);
    
  matrix_4x4 LookDownZ;

  for(int Counter = 0;Counter < 3;Counter++)
  {
    LookDownZ.SetElement(0,Counter,Right.GetElement(Counter));
  }

  for(Counter = 0;Counter < 3;Counter++)
  {
    LookDownZ.SetElement(1,Counter,ReallyUp.GetElement(Counter));
  }

  for(Counter = 0;Counter < 3;Counter++)
  {
    LookDownZ.SetElement(2,Counter,ViewDirection.GetElement(Counter));
  }

  this->matrix_4x4::operator=(LookDownZ * *this);
}
