/*======================================================================*/
/* 3DLib								*/
/*									*/
/* Camera-related functions						*/
/*									*/
/* AUTHOR:	Gabor Nagy						*/
/* DATE:	1996-Dec-24 23:41:29					*/
/*									*/
/* 3DLib(TM) Copyright (C) 1995 by Gabor Nagy. All rights reserved.	*/
/*======================================================================*/
#include <float.h>
#include <math.h>
#include <stdarg.h>		// For varargs
#include <stdio.h>

#include <EMalloc.h>
#include <EStrings.h>

#include <E3D/E3D.h>

#include <E3D/Camera.h>

#include <E3D/Math.h>

#include <E3D/Matrix.h>



E3dCameraInfo	E3d_CameraModelInfo;


//========================================================================================
// Calculate ViewDistance and the unit-length vector of the line of sight
//
// Argument
//  E3dCamera* LCamera         Pointer to the Camera structure
//
// Description
//  &nbsp;&nbsp;Calculates the ViewDistance and the unit-length vector of the line of sight for the
//  given Camera and sets the appropriate fields in the Camera structure.
//
// See also
//  E3d_CameraCalcUpVector, E3d_CameraUpdateViewDirection
//========================================================================================
void E3d_CameraUpdateViewDirection(E3dCamera* LCamera)
{
 float		LViewDistance, LDX, LDY, LDZ, LW;


 LDX=LCamera->Position.X-LCamera->InterestPoint.X;
 LDY=LCamera->Position.Y-LCamera->InterestPoint.Y;
 LDZ=LCamera->Position.Z-LCamera->InterestPoint.Z;

 if((LViewDistance=sqrt(LDX*LDX+LDY*LDY+LDZ*LDZ))!=0.0)
 {
  LW=1.0/LViewDistance;
  LCamera->Direction.X=LDX*LW;
  LCamera->Direction.Y=LDY*LW;
  LCamera->Direction.Z=LDZ*LW;
 }
 LCamera->ViewDistance=LViewDistance;
}


//========================================
// Set viewing distance
//========================================
void E3d_CameraSetViewDistance(E3dCamera* LCamera, double LDistance)
{
 LCamera->Position.X=LCamera->Direction.X*LDistance+LCamera->InterestPoint.X;
 LCamera->Position.Y=LCamera->Direction.Y*LDistance+LCamera->InterestPoint.Y;
 LCamera->Position.Z=LCamera->Direction.Z*LDistance+LCamera->InterestPoint.Z;

 LCamera->ViewDistance=LDistance;
}


//========================================
// Set camera position
//========================================
void E3d_CameraSetPosition(E3dCamera* LCamera, E3d3DPosition* LPosition)
{
 LCamera->Position.X=LPosition->X;
 LCamera->Position.Y=LPosition->Y;
 LCamera->Position.Z=LPosition->Z;

 E3d_CameraUpdateViewDirection(LCamera);

 E3d_CameraRefreshMatrices(LCamera);
}


//========================================
// Set camera interest position
//========================================
void E3d_CameraSetInterest(E3dCamera* LCamera, E3d3DPosition* LPosition)
{
 LCamera->Position.X=LPosition->X+LCamera->ViewDistance*LCamera->Direction.X;
 LCamera->Position.Y=LPosition->Y+LCamera->ViewDistance*LCamera->Direction.Y;
 LCamera->Position.Z=LPosition->Z+LCamera->ViewDistance*LCamera->Direction.Z;
 LCamera->InterestPoint.X=LPosition->X;
 LCamera->InterestPoint.Y=LPosition->Y;
 LCamera->InterestPoint.Z=LPosition->Z;

 E3d_CameraUpdateViewDirection(LCamera);

 E3d_CameraRefreshMatrices(LCamera);
}


//========================================================================================
// Calculate camera Longitude & Latitude angles from position
//
// Argument
//  E3dCamera* LCamera         Pointer to the Camera structure
//
// Description
//  Computes the Longitude & Latitude angles from the cameara and interest position.
//
// See also
//  E3d_CameraCalcUpVector, E3d_CameraUpdateViewDirection
//========================================================================================
void E3d_CameraCalcOrbit(E3dCamera* LCamera)
{
 E3dCoordinate	LDCamX, LDCamY, LDCamZ;
 E3dAngle	LBeta;


 LDCamX=LCamera->Position.X-LCamera->InterestPoint.X;
 LDCamY=LCamera->Position.Y-LCamera->InterestPoint.Y;
 LDCamZ=LCamera->Position.Z-LCamera->InterestPoint.Z;

 LCamera->Latitude=-E3d_GetAngle2D(LDCamY, (double)sqrt(LDCamX*LDCamX+LDCamZ*LDCamZ))+90.0;
 LCamera->Longitude=LBeta=E3d_GetAngle2D(LDCamZ, LDCamX);
 if(LBeta<0.0) LCamera->Longitude+=360.0;
}


//================================================================
// Calculate camera position from Longitude & Latitude angles
//================================================================
void E3d_OrbitCamera(E3dCamera* LCamera, double LLongitude, double LLatitude)
{
 E3dMatrix	LMatrix;
 double		LViewDistance, LDX, LDY, LDZ, LW;


 if(LLongitude<0.0) LLongitude+=360.0;
 if(LLongitude>360.0) LLongitude-=360.0;

 LCamera->Latitude=LLatitude;
 LCamera->Longitude=LLongitude;

 E3d_MatrixLoadIdentity(LMatrix);
 E3d_MatrixRotate(LMatrix, 'y', LLongitude);
 E3d_MatrixRotate(LMatrix, 'x', -LLatitude);

 LViewDistance=LCamera->ViewDistance;
 LCamera->Position.X=LViewDistance*LMatrix[M20]+LCamera->InterestPoint.X;
 LCamera->Position.Y=LViewDistance*LMatrix[M21]+LCamera->InterestPoint.Y;
 LCamera->Position.Z=LViewDistance*LMatrix[M22]+LCamera->InterestPoint.Z;

/*------------------------------------------------------------------------------*/
/* Calculate ViewDistance and the unit-length vector of the line-of-sight	*/
/*------------------------------------------------------------------------------*/
 LDX=LCamera->Position.X-LCamera->InterestPoint.X;
 LDY=LCamera->Position.Y-LCamera->InterestPoint.Y;
 LDZ=LCamera->Position.Z-LCamera->InterestPoint.Z;

 if(LViewDistance!=0.0)
 {
  LW=1.0/LViewDistance;
  LCamera->Direction.X=LDX*LW;
  LCamera->Direction.Y=LDY*LW;
  LCamera->Direction.Z=LDZ*LW;
 }
 E3d_CameraCalcUpVector(LCamera);
}


//================================================================
// Compute camera up-vector from the orbital angles
//================================================================
void E3d_CameraCalcUpVector(E3dCamera* LCamera)
{
 double		LVX, LVY, LVZ, LCamDX, LCamDY, LCamDZ, LCamDXZ, LViewDistance, LSinTh, LCosTh, LSinFi, LCosFi;
 E3dMatrix	LMatrix;
 E3dVector	LUpVector;

 LVX=LCamera->Position.X;
 LVY=LCamera->Position.Y;
 LVZ=LCamera->Position.Z;
 LCamDX=LVX-LCamera->InterestPoint.X;
 LCamDY=LVY-LCamera->InterestPoint.Y;
 LCamDZ=LVZ-LCamera->InterestPoint.Z;

 LCamDXZ=sqrt(LCamDX*LCamDX+LCamDZ*LCamDZ);
 LViewDistance=LCamera->ViewDistance;

 LSinTh=-LCamDX/LCamDXZ;
 LCosTh=LCamDZ/LCamDXZ;
 LSinFi=LCamDY/LViewDistance;
 LCosFi=LCamDXZ/LViewDistance;

 E3d_MatrixLoadIdentity(LMatrix);
 E3d_MatrixRotateSinCos(LMatrix, 'y', -LSinTh, LCosTh);
 E3d_MatrixRotateSinCos(LMatrix, 'x', -LSinFi, LCosFi);
 E3d_MatrixRotate(LMatrix, 'z', LCamera->BankAngle);

 E3dM_VectorInit(LUpVector, 0.0, 1.0, 0.0);
 E3d_MatrixTransformVector(LMatrix, &LUpVector);

 E3dM_VectorCopy(LUpVector, LCamera->UpVector);
}


//================================================================================
// Initialize Camera structure
//
// Argument:
//  E3dCamera* LCamera     Pointer to the Camera structure
//
// Description
//  E3d_CameraDefault initializes an E3dCamera structure to reasonable default
//  values.
//  This function must be called for E3dCamera structures not allocated by
//  E3d_CameraAllocate().
//================================================================================
void E3d_CameraDefault(E3dCamera* LCamera)
{
 LCamera->RefCnt=0;
 LCamera->Name=NULL;

 LCamera->Position.X=0.0;
 LCamera->Position.Y=0.0;
 LCamera->Position.Z=0.0;
 LCamera->InterestPoint.X=0.0;
 LCamera->InterestPoint.Y=0.0;
 LCamera->InterestPoint.Z=0.0;


// Lens
//
 LCamera->FieldOfView=36.0;
 LCamera->ShutterTime=0.0;
 LCamera->FocalLength=0.08;	// 80mm
 LCamera->Aperture=0.0;		// No depth of field
 LCamera->LensType=E3dLENS_PERSPECTIVE;

 LCamera->BankAngle=0.0;
 LCamera->XZoom=LCamera->YZoom=1.0;
 LCamera->ZNear=0.1;
 LCamera->ZFar=32767.999;

 LCamera->EyeDistance=0.065;


// Film
//
 LCamera->AspectRatio=1.33333;
 LCamera->GlowThreshold=0.72;
 LCamera->GlowRadius=0.05;	// In fractions of Y resolution
 LCamera->Glow=FALSE;


 LCamera->Model=NULL;

 LCamera->PositionConstraint=NULL;
 LCamera->AlignmentConstraint=NULL;
 LCamera->InterestConstraint=NULL;

 LCamera->NumOfCallbacks=0;
 LCamera->Callbacks=NULL;

 E3d_MatrixLoadIdentity(LCamera->InterestMatrix);
 E3d_CameraUpdateViewDirection(LCamera);
 E3d_CameraCalcOrbit(LCamera);
 E3d_CameraCalcUpVector(LCamera);
 E3d_CameraRefreshMatrices(LCamera);
}


//================================================================================
// Initialize Camera position and field-of-view	
//
// Argument
//  E3dCamera* LCamera     Pointer to the Camera structure
//
// Description
//  E3d_CameraSet initializes an E3dCamera structure to the given values and
//  updates the Camera's auxiliary information.	
//================================================================================
void E3d_CameraSet(E3dCamera* LCamera, double LXPos, double LYPos, double LZPos,
                    double LFieldOfView, double LZoom, double LZNear, double LZFar,
                    double LEyeDistance)
{
 LCamera->Position.X=LXPos;
 LCamera->Position.Y=LYPos;
 LCamera->Position.Z=LZPos;
 LCamera->InterestPoint.X=0.0;
 LCamera->InterestPoint.Y=0.0;
 LCamera->InterestPoint.Z=0.0;

 LCamera->BankAngle=0.0;
 LCamera->FieldOfView=LFieldOfView;
 LCamera->XZoom=LCamera->YZoom=LZoom;
 LCamera->ZNear=LZNear;
 LCamera->ZFar=LZFar;

 LCamera->EyeDistance=LEyeDistance;

 E3d_CameraUpdateViewDirection(LCamera);
 E3d_CameraCalcOrbit(LCamera);
 E3d_CameraCalcUpVector(LCamera);
 E3d_CameraRefreshMatrices(LCamera);

 E3d_CallCallbacks(LCamera, LCamera->Callbacks, LCamera->NumOfCallbacks, NULL);
}


//========================================================================================
// Allocate and initialize a Camera structure
//
// Description
//  E3d_CameraAllocate allocates and initializes an E3dCamera structure.
//
// Return value
//  Pointer to the newly allocated E3dCamera structure or NULL in case of an error.
//========================================================================================
E3dCamera* E3d_CameraAllocate()
{
 E3dCamera*	LCamera=(E3dCamera*)EMalloc(sizeof(E3dCamera));

 if(LCamera)
 {
  E3d_CameraDefault(LCamera);
 }
 return(LCamera);
}


//========================================================================================
// Free a Camera structure
//
// Description
//  This function first decrements the reference count (RefCnt) of the given Camera
//  structure (if it's not already zero).
//  After that, if RefCnt is still greater than zero, it means that something other
//  than the function calling E3d_CameraFree() is still referring to this Camera,
//  so E3d_CameraFree() will simply return.
//  If RefCnt is zero, E3d_CameraFree() will free the Camera structure.
//========================================================================================
void E3d_CameraFree(E3dCamera* LCamera)
{
 if(LCamera->RefCnt>0)
 {
  LCamera->RefCnt-=1;
  if(LCamera->RefCnt>0) return;
 }

 if(LCamera->Callbacks) EFree(LCamera->Callbacks);

 EFree(LCamera);
}


//================================================================
// Copy parameters of a Camera
//================================================================
void E3d_CameraCopy(E3dCamera* LSCamera, E3dCamera* LDCamera)
{
 LDCamera->Position=LSCamera->Position;
 LDCamera->InterestPoint=LSCamera->InterestPoint;
 LDCamera->UpVector=LSCamera->UpVector;
 LDCamera->FieldOfView=LSCamera->FieldOfView;
 LDCamera->ZNear=LSCamera->ZNear;
 LDCamera->ZFar=LSCamera->ZFar;
 LDCamera->EyeDistance=LSCamera->EyeDistance;
 LDCamera->XZoom=LSCamera->XZoom;
 LDCamera->YZoom=LSCamera->YZoom;
 LDCamera->AspectRatio=LSCamera->AspectRatio;

 E3d_CameraRefreshMatrices(LDCamera);
}


//================================================================
// Add callback to a Camera
//================================================================
void E3d_CameraAddCallback(E3dCamera* LCamera, E3dCallbackProc LCallbackProc, EPointer LClientData)
{
 E3dCallbackRec*	LCB;


 if(LCamera->Callbacks==NULL)
 {
  if((LCamera->Callbacks=(E3dCallbackRec*)EMalloc(sizeof(E3dCallbackRec)))!=NULL)
  {
   LCamera->NumOfCallbacks=1;
   LCamera->Callbacks[0].Proc=LCallbackProc;
   LCamera->Callbacks[0].ClientData=LClientData;
  }
 }
 else
 {
  if((LCB=(E3dCallbackRec*)ERealloc(LCamera->Callbacks, sizeof(E3dCallbackRec)*(LCamera->NumOfCallbacks+1)))!=NULL)
  {
   LCamera->Callbacks=LCB;
   LCB[LCamera->NumOfCallbacks].Proc=LCallbackProc;
   LCB[LCamera->NumOfCallbacks].ClientData=LClientData;
   LCamera->NumOfCallbacks+=1;
  }
 }
}


//================================================================
// Add callback to a Camera
//================================================================
void E3d_CameraRemoveCallback(E3dCamera* LCamera, E3dCallbackProc LCallbackProc, EPointer LClientData)
{
 E3dCallbackRec*	LCallbacks;
 int			LC, LN;

 LN=LCamera->NumOfCallbacks;
 if(LN==0) return;

 LCallbacks=LCamera->Callbacks;

 for(LC=0;LC<LN;LC++)
 {
  if((LCallbacks[LC].Proc==LCallbackProc)&&(LCallbacks[LC].ClientData==LClientData))
  {
   if(LC<(LN-1)) memmove(LCallbacks+LC, LCallbacks+LC+1, sizeof(E3dCallbackRec)*(LN-LC-1));
   LCamera->NumOfCallbacks-=1;
   if(LCamera->NumOfCallbacks==0) { EFree(LCamera->Callbacks);LCamera->Callbacks=NULL; }
   else LCamera->Callbacks=(E3dCallbackRec*)ERealloc(LCamera->Callbacks, sizeof(E3dCallbackRec)*LCamera->NumOfCallbacks);
  }
 }
}



//================================================================
// Update Matrices of a Camera
//================================================================
void E3d_CameraRefreshMatrices(E3dCamera* LCamera)
{
 E3dMatrix	LTMatrix;
 E3dCoordinate	LCamDX, LCamDY, LCamDZ, LSX, LSY, LSZ, LUX, LUY, LUZ, LL;
 E3dCoordinate	LCamX, LCamY, LCamZ;
 E3dCoordinate	LF, LFV;

 LCamX=LCamera->Position.X;
 LCamY=LCamera->Position.Y;
 LCamZ=LCamera->Position.Z;
 LCamDX=LCamera->InterestPoint.X-LCamX;
 LCamDY=LCamera->InterestPoint.Y-LCamY;
 LCamDZ=LCamera->InterestPoint.Z-LCamZ;

 LL=sqrt(LCamDX*LCamDX+LCamDY*LCamDY+LCamDZ*LCamDZ);LL=1.0/LL;
 LCamDX*=LL;LCamDY*=LL;LCamDZ*=LL;

 E3dM_VectorCrossProduct(LCamDX, LCamDY, LCamDZ, LCamera->UpVector.X, LCamera->UpVector.Y, LCamera->UpVector.Z, LSX, LSY, LSZ);

/*
printf("UpV: %f,%f,%f -- %f\n", LCamera->UpVector.X, LCamera->UpVector.Y, LCamera->UpVector.Z,
sqrt(LCamera->UpVector.X*LCamera->UpVector.X+LCamera->UpVector.Y*LCamera->UpVector.Y+LCamera->UpVector.Z*LCamera->UpVector.Z));fflush(stdout);
printf("LS: %f, %f, %f -- %f\n", LSX, LSY, LSZ,
sqrt(LSX*LSX+LSY*LSY+LSZ*LSZ));fflush(stdout);
*/

 E3dM_VectorCrossProduct(LSX, LSY, LSZ, LCamDX, LCamDY, LCamDZ, LUX, LUY, LUZ);

 LTMatrix[M00]=LSX;LTMatrix[M01]=LUX;LTMatrix[M02]=-LCamDX;LTMatrix[M03]=0.0;
 LTMatrix[M10]=LSY;LTMatrix[M11]=LUY;LTMatrix[M12]=-LCamDY;LTMatrix[M13]=0.0;
 LTMatrix[M20]=LSZ;LTMatrix[M21]=LUZ;LTMatrix[M22]=-LCamDZ;LTMatrix[M23]=0.0;
 LTMatrix[M30]=0.0;LTMatrix[M31]=0.0;LTMatrix[M32]=0.0;LTMatrix[M33]=1.0;
 E3d_MatrixTranslate(LTMatrix, -LCamX, -LCamY, -LCamZ);

 E3d_MatrixCopy(LTMatrix, LCamera->WorldToViewerMatrix);

 LF=1.0/tan((LCamera->FieldOfView)*0.5*E3dDEGREES_TO_RADIANS);
 LFV=1.0/(LCamera->ZNear-LCamera->ZFar);
 LCamera->ProjectionMatrix[M00]=LF/LCamera->AspectRatio;
 LCamera->ProjectionMatrix[M01]=0.0;
 LCamera->ProjectionMatrix[M02]=0.0;
 LCamera->ProjectionMatrix[M03]=0.0;

 LCamera->ProjectionMatrix[M10]=0.0;
 LCamera->ProjectionMatrix[M11]=LF;
 LCamera->ProjectionMatrix[M12]=0.0;
 LCamera->ProjectionMatrix[M13]=0.0;

 LCamera->ProjectionMatrix[M20]=0.0;
 LCamera->ProjectionMatrix[M21]=0.0;
 LCamera->ProjectionMatrix[M22]=(LCamera->ZNear+LCamera->ZFar)*LFV;
 LCamera->ProjectionMatrix[M23]=-1.0;

 LCamera->ProjectionMatrix[M30]=0.0;
 LCamera->ProjectionMatrix[M31]=0.0;
 LCamera->ProjectionMatrix[M32]=2.0*(LCamera->ZNear*LCamera->ZFar)*LFV;
 LCamera->ProjectionMatrix[M33]=0.0;


 {
  E3dModel*	LModel=LCamera->Model;

  if(LModel)
  {
   E3dModel*		LInterestModel=LModel->Child;
   E3dMatrix		LMatrix;
   E3dCoordinate	LCamDXZ, LViewDistance,
			LSinTh, LCosTh, LSinFi, LCosFi;

   LCamDX=LCamX-LCamera->InterestPoint.X;
   LCamDY=LCamY-LCamera->InterestPoint.Y;
   LCamDZ=LCamZ-LCamera->InterestPoint.Z;

   LCamDXZ=sqrt(LCamDX*LCamDX+LCamDZ*LCamDZ);
   LViewDistance=LCamera->ViewDistance;

   LSinTh=-LCamDX/LCamDXZ;
   LCosTh=LCamDZ/LCamDXZ;
   LSinFi=LCamDY/LViewDistance;
   LCosFi=LCamDXZ/LViewDistance;


// Calculate camera and interest matrices
//
   E3d_MatrixResetTrans(LMatrix, LCamX, LCamY, LCamZ);
   E3d_MatrixRotateSinCos(LMatrix, 'y', -LSinTh, LCosTh);
   E3d_MatrixRotateSinCos(LMatrix, 'x', -LSinFi, LCosFi);
   E3d_MatrixRotate(LMatrix, 'z', LCamera->BankAngle);
   E3d_MatrixCopy(LMatrix, LCamera->CameraMatrix);
   E3d_MatrixCopy(LMatrix, LModel->LocalToWorldMatrix);
   LCamera->InterestMatrix[M30]=LCamera->InterestPoint.X;LCamera->InterestMatrix[M31]=LCamera->InterestPoint.Y;LCamera->InterestMatrix[M32]=LCamera->InterestPoint.Z;

   E3d_MatrixCopy(LCamera->InterestMatrix, LInterestModel->LocalToWorldMatrix);
  }
 }
}



#ifdef USEOpenGL
void E3dGL_CameraLookAtOld(E3dCamera* LCamera)
{
 E3dMatrix	LMatrix;
 E3dCoordinate	LEyeX=LCamera->Position.X, LEyeY=LCamera->Position.Y, LEyeZ=LCamera->Position.Z;
 double		x[3], y[3], z[3];
 double		LMag;

// Make rotation matrix
//
// Z vector
//
 z[0] = LEyeX - LCamera->InterestPoint.X;
 z[1] = LEyeY - LCamera->InterestPoint.Y;
 z[2] = LEyeZ - LCamera->InterestPoint.Z;
 LMag = sqrt(z[0]*z[0] + z[1]*z[1] + z[2]*z[2]);
 if(LMag)
 {
  LMag=1.0/LMag;
  z[0] *= LMag;
  z[1] *= LMag;
  z[2] *= LMag;
 }

// Y vector
//
 y[0] = LCamera->UpVector.X;
 y[1] = LCamera->UpVector.Y;
 y[2] = LCamera->UpVector.Z;

// X vector = Y cross Z
//
 x[0] =  y[1]*z[2] - y[2]*z[1];
 x[1] = -y[0]*z[2] + y[2]*z[0];
 x[2] =  y[0]*z[1] - y[1]*z[0];

// Recompute Y = Z cross X
//
 y[0] =  z[1]*x[2] - z[2]*x[1];
 y[1] = -z[0]*x[2] + z[2]*x[0];
 y[2] =  z[0]*x[1] - z[1]*x[0];

// Must normalize x, y here
//
 LMag = sqrt(x[0]*x[0] + x[1]*x[1] + x[2]*x[2]);
 if(LMag)
 {
  LMag=1.0/LMag;
  x[0] *= LMag;
  x[1] *= LMag;
  x[2] *= LMag;
 }

 LMag = sqrt(y[0]*y[0] + y[1]*y[1] + y[2]*y[2]);
 if(LMag)
 {
  LMag=1.0/LMag;
  y[0] *= LMag;
  y[1] *= LMag;
  y[2] *= LMag;
 }

 LMatrix[M00] = x[0];  LMatrix[M10] = x[1];  LMatrix[M20] = x[2];  LMatrix[M30] = 0.0;
 LMatrix[M01] = y[0];  LMatrix[M11] = y[1];  LMatrix[M21] = y[2];  LMatrix[M31] = 0.0;
 LMatrix[M02] = z[0];  LMatrix[M12] = z[1];  LMatrix[M22] = z[2];  LMatrix[M32] = 0.0;
 LMatrix[M03] = 0.0;   LMatrix[M13] = 0.0;   LMatrix[M23] = 0.0;   LMatrix[M33] = 1.0;

 MglMultMatrix(LMatrix);

// Translate Eye to Origin
//
 glTranslated(-LEyeX, -LEyeY, -LEyeZ);
}



//================================================================
// Perform 'Lookat' Camera transformation for OpenGL
//================================================================
void E3dGL_CameraLookAt(E3dCamera* LCamera)
{
 E3dMatrix	LTMatrix;
 double		LFX, LFY, LFZ, LSX, LSY, LSZ, LUX, LUY, LUZ, LL;
 double		LUpX=LCamera->UpVector.X, LUpY=LCamera->UpVector.Y, LUpZ=LCamera->UpVector.Z,
		LCamX=LCamera->Position.X, LCamY=LCamera->Position.Y, LCamZ=LCamera->Position.Z;

 LFX=LCamera->InterestPoint.X-LCamX;
 LFY=LCamera->InterestPoint.Y-LCamY;
 LFZ=LCamera->InterestPoint.Z-LCamZ;

 LL=sqrt(LFX*LFX+LFY*LFY+LFZ*LFZ);LL=1.0/LL;
 LFX*=LL;LFY*=LL;LFZ*=LL;

 E3dM_VectorCrossProduct(LFX, LFY, LFZ, LUpX, LUpY, LUpZ, LSX, LSY, LSZ);
 E3dM_VectorCrossProduct(LSX, LSY, LSZ, LFX, LFY, LFZ, LUX, LUY, LUZ);

 LTMatrix[M00]=LSX;LTMatrix[M01]=LUX;LTMatrix[M02]=-LFX;LTMatrix[M03]=0.0;
 LTMatrix[M10]=LSY;LTMatrix[M11]=LUY;LTMatrix[M12]=-LFY;LTMatrix[M13]=0.0;
 LTMatrix[M20]=LSZ;LTMatrix[M21]=LUZ;LTMatrix[M22]=-LFZ;LTMatrix[M23]=0.0;
 LTMatrix[M30]=0.0;LTMatrix[M31]=0.0;LTMatrix[M32]=0.0;LTMatrix[M33]=1.0;

 MglMultMatrix(LTMatrix);

// Translate Eye to Origin
//
 glTranslated(-LCamX, -LCamY, -LCamZ);
}



//================================================================
// Set perspective transformation for OpenGL
//================================================================
void E3dGL_CameraPerspective(E3dCamera* LCamera)
{
 double	LXMin, LXMax, LYMin, LYMax;
 double	LAspect=LCamera->AspectRatio, LZNear=LCamera->ZNear, LZFar=LCamera->ZFar;

 LYMax = LZNear * tan(LCamera->FieldOfView * E3dPI / 360.0);
 LYMin = -LYMax;

 LXMin = LYMin * LAspect;
 LXMax = LYMax * LAspect;

 glFrustum(LXMin, LXMax, LYMin, LYMax, LZNear, LZFar);
}



//========================================================
// Set picking Matrix
//========================================================
void E3dGL_CameraPickMatrix(double LX, double LY, double LXSize, double LYSize, const int LViewPort[4])
{
 E3dMatrix	LMatrix;
 float		LSX, LSY;
 float		LTX, LTY;


 LSX = LViewPort[2] / LXSize;
 LSY = LViewPort[3] / LYSize;
 LTX = (LViewPort[2] + 2.0 * (LViewPort[0] - LX)) / LXSize;
 LTY = (LViewPort[3] + 2.0 * (LViewPort[1] - LY)) / LYSize;

 LMatrix[M00] = LSX;  LMatrix[M10] = 0.0;  LMatrix[M20] = 0.0;  LMatrix[M30] = LTX;
 LMatrix[M01] = 0.0;  LMatrix[M11] = LSY;  LMatrix[M21] = 0.0;  LMatrix[M31] = LTY;
 LMatrix[M02] = 0.0;  LMatrix[M12] = 0.0;  LMatrix[M22] = 1.0;  LMatrix[M32] = 0.0;
 LMatrix[M03] = 0.0;  LMatrix[M13] = 0.0;  LMatrix[M23] = 0.0;  LMatrix[M33] = 1.0;

 MglMultMatrix(LMatrix);
}
#endif // USEOpenGL


//========================================================
// Create new CameraInfo structure for a Model
//========================================================
E3dModelInfo* E3d_CameraCreateModelInfo(E3dCamera* LCamera)
{
 E3dCameraInfo*	LCameraInfo;

 LCameraInfo=(E3dCameraInfo*)EMalloc(sizeof(E3dCameraInfo));

 if(LCameraInfo) memcpy(LCameraInfo, &E3d_CameraModelInfo, sizeof(E3dCameraInfo));

 LCameraInfo->Camera=LCamera;
 if(LCamera) LCamera->RefCnt+=1;

 return((E3dModelInfo*)LCameraInfo);
}


//========================================================================
// Find the parameters of a plane that is perpendicular
// to the Camera's view direction and passes through a given point
//========================================================================
void E3d_CameraGetViewPlaneOnPoint(E3dCamera* LCamera, E3dCoordinate LX, E3dCoordinate LY, E3dCoordinate LZ, E3dPlane* LPlane)
{
 E3dCoordinate	LCDX, LCDY, LCDZ;	// Camera Direction vector

// Get D in the plane equation
//
 LCDX=LCamera->Direction.X;
 LCDY=LCamera->Direction.Y;
 LCDZ=LCamera->Direction.Z;

 LPlane->A=-LCDX;
 LPlane->B=-LCDY;
 LPlane->C=-LCDZ;
 LPlane->D=-(LCDX*LX+LCDY*LY+LCDZ*LZ);
}
