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

	Light.cpp

	Copyright (c)1990 - 1999 MetaCreations. All rights reserved.

	Author:	Jerome Bignon

	Date:	06/22/99

	Revision History

	when		who			what & why
	=========== ===========	=============================================================
	09/17/99	cph			fix bug in GlobalToLocal() function
	08/20/99	cph			Put in Extension3DInit() and Extension3DCleanup() methods

****************************************************************************************/
#include "math.h"
#include "Light.h"


const MCGUID CLSID_Light(R_CLSID_Light);


// Tool to get the Global Coordinates from the Local Coordinates 
void LocalToGlobal(TTransform3D* transform,TVector3* LocalPos,TVector3* GlobalPos)
{
	// Axes rotations
	(*GlobalPos)[0] = transform->fRotationAndScale[0][0]*(*LocalPos)[0]
					+transform->fRotationAndScale[0][1]*(*LocalPos)[1]
					+transform->fRotationAndScale[0][2]*(*LocalPos)[2];

	(*GlobalPos)[1] = transform->fRotationAndScale[1][0]*(*LocalPos)[0]
					+transform->fRotationAndScale[1][1]*(*LocalPos)[1]
					+transform->fRotationAndScale[1][2]*(*LocalPos)[2];

	(*GlobalPos)[2] = transform->fRotationAndScale[2][0]*(*LocalPos)[0]
					+transform->fRotationAndScale[2][1]*(*LocalPos)[1]
					+transform->fRotationAndScale[2][2]*(*LocalPos)[2];

	// Unit conversion : Points Units to 3D Units 
	(*GlobalPos)[0] /= 288.0f;  
	(*GlobalPos)[1] /= 288.0f;  
	(*GlobalPos)[2] /= 288.0f;
	
	// Origin translation
	(*GlobalPos)[0] += transform->fTranslation[0];
	(*GlobalPos)[1] += transform->fTranslation[1];
	(*GlobalPos)[2] += transform->fTranslation[2];
}


// Tool to get the Global Coordinates from the Local Coordinates for a Vector
void LocalToGlobalVector(TTransform3D* transform,TVector3* LocalPos,TVector3* GlobalPos)
{
	// Axes rotations
	(*GlobalPos)[0] = transform->fRotationAndScale[0][0]*(*LocalPos)[0]
					+transform->fRotationAndScale[0][1]*(*LocalPos)[1]
					+transform->fRotationAndScale[0][2]*(*LocalPos)[2];

	(*GlobalPos)[1] = transform->fRotationAndScale[1][0]*(*LocalPos)[0]
					+transform->fRotationAndScale[1][1]*(*LocalPos)[1]
					+transform->fRotationAndScale[1][2]*(*LocalPos)[2];

	(*GlobalPos)[2] = transform->fRotationAndScale[2][0]*(*LocalPos)[0]
					+transform->fRotationAndScale[2][1]*(*LocalPos)[1]
					+transform->fRotationAndScale[2][2]*(*LocalPos)[2];
}

// Tool to get the Local Coordinates from the Global Coordinates             

void GlobalToLocal(TTransform3D* transform,TVector3* GlobalPos,TVector3* LocalPos)
{
	  // Origin translation
	(*GlobalPos)[0] -= transform->fTranslation[0];
	(*GlobalPos)[1] -= transform->fTranslation[1];
	(*GlobalPos)[2] -= transform->fTranslation[2];

   // Unit conversion : 3D Units to Points Units
	(*GlobalPos)[0] *= 288.0f;
	(*GlobalPos)[1] *= 288.0f;
	(*GlobalPos)[2] *= 288.0f;
 
  // Axes rotations
	(*LocalPos)[0] = transform->fRotationAndScale[0][0]*(*GlobalPos)[0]
					+transform->fRotationAndScale[1][0]*(*GlobalPos)[1]
					+transform->fRotationAndScale[2][0]*(*GlobalPos)[2];

	(*LocalPos)[1] = transform->fRotationAndScale[1][1]*(*GlobalPos)[0]
					+transform->fRotationAndScale[1][1]*(*GlobalPos)[1]
					+transform->fRotationAndScale[2][1]*(*GlobalPos)[2];

	(*LocalPos)[2] = transform->fRotationAndScale[0][2]*(*GlobalPos)[0]
					+transform->fRotationAndScale[1][2]*(*GlobalPos)[1]
					+transform->fRotationAndScale[2][2]*(*GlobalPos)[2];
}


// Tool to get the Local Coordinates from the Global Coordinates for a Vector
void GlobalToLocalVector(TTransform3D* transform, const TVector3& GlobalPos,TVector3* LocalPos)
{
	// Axes rotations

	(*LocalPos)[0] = transform->fRotationAndScale[0][0]*(GlobalPos)[0]
					+transform->fRotationAndScale[1][0]*(GlobalPos)[1]
					+transform->fRotationAndScale[2][0]*(GlobalPos)[2];

	(*LocalPos)[1] = transform->fRotationAndScale[0][1]*(GlobalPos)[0]
					+transform->fRotationAndScale[1][1]*(GlobalPos)[1]
					+transform->fRotationAndScale[2][1]*(GlobalPos)[2];

	(*LocalPos)[2] = transform->fRotationAndScale[0][2]*(GlobalPos)[0]
					+transform->fRotationAndScale[1][2]*(GlobalPos)[1]
					+transform->fRotationAndScale[2][2]*(GlobalPos)[2];
}
//--- End of the tools implementation ----------------------------------------------------- 

void Extension3DInit(IMCUnknown* utilities)
{
	// Perform your dll initialization here
}

void Extension3DCleanup()
{
	// Perform any nec clean-up here
}


TBasicUnknown* MakeCOMObject(const MCCLSID& classId)	// This method instanciate
{														// the object COMBack
	TBasicUnknown* res = NULL;

	if (classId == CLSID_Light) res = new Light;

	return res;
}

// Constructor of the C++ Object :
Light::Light()
{
	// Rotation Matrix initialized to identity :
	fTransform.fRotationAndScale[0][0]=fTransform.fRotationAndScale[1][1]=fTransform.fRotationAndScale[2][2]=1.0;
	fTransform.fRotationAndScale[0][1]=fTransform.fRotationAndScale[0][2]=0.0;
	fTransform.fRotationAndScale[1][0]=fTransform.fRotationAndScale[1][2]=0.0;
	fTransform.fRotationAndScale[2][0]=fTransform.fRotationAndScale[2][1]=0.0;

	// Translation Vector initialized to zero  :
	fTransform.fTranslation[0]=fTransform.fTranslation[1]=fTransform.fTranslation[2]=0.0;
	
	// Data initialization :
	// -- Default color : White
	fData.fLightColor.R=1.0;
	fData.fLightColor.G=1.0;
	fData.fLightColor.B=1.0;
	
	// -- Aperture Angles
	fData.fHorApertureAngle=90;     // 90° (-45° to 45°)
	fData.fVerApertureAngle=90;     // 
	fData.fBeamAperture=5;          // 5° for each beam
	
	// -- Intensity of the Beams
	fData.fIntensity=1.0f;			// 100% of intensity
	
	// -- Numbers of Beams
	fData.fNbBeamsHorizontally=10;
	fData.fNbBeamsVertically=10;
	ExtensionDataChanged();			// Preprocessed values

	fData.fScalingFactor = 1;
}
  
void* Light::GetExtensionDataBuffer()
{
	return &fData; // The Shell uses this pointer to set the values of the camera's parameters
}
  
MCCOMErr Light::ExtensionDataChanged()
{
	fHorAng=((real)fData.fHorApertureAngle)/2.0f;			// Recalculate Aperture Angles
	fVerAng=((real)fData.fVerApertureAngle)/2.0f;
	fBeamAngle=((real)fData.fBeamAperture)/2.0f;
	fBeamLimit=cos(fBeamAngle/180.0f*3.14159265358979323846f);//QuickSinCos(fBeamAngle,sinus,cosinus);
	return MC_S_OK;
}

int16 Light::GetResID()
{
	return 132; // This is the view ID in the resource file
}
  
// I3DExLightsource methods :
// set the coordinates Transformation values
MCCOMErr Light::SetTransform(TTransform3D* transform)
{
	fTransform=*transform;
	return MC_S_OK;
}

MCCOMErr Light::GetDirection(const TVector3 &point,TVector3 &resultDirection,real &resultDistance)
{
	resultDirection[0]=fTransform.fTranslation[0]-point[0];
	resultDirection[1]=fTransform.fTranslation[1]-point[1];
	resultDirection[2]=fTransform.fTranslation[2]-point[2]; // Vector from surface point to the Light

	resultDistance=	sqrt(resultDirection[0]*resultDirection[0] + resultDirection[1]*resultDirection[1]
					+resultDirection[2]*resultDirection[2]);

	resultDirection[0] /= resultDistance;
	resultDirection[1] /= resultDistance;
	resultDirection[2] /= resultDistance;

	return MC_S_OK;
}
  
  
boolean Light::GetColor(const TVector3& point,
						const TVector3& direction,
						const real distance,
						TMCColorRGB& result,
						real& shadowCoef)
{
	TVector3 localVector,refDir;
	real    angle,nearest_dir,hor,ver,theta,phi,r;
	real    costheta,sintheta,cosphi,sinphi;
	real    cosdifferentialAngle; 
	real    angleLimit=360.0f;

	shadowCoef = 1.0f; // We always want shadows for this lightsource

	GlobalToLocalVector(&fTransform,direction,&localVector);

	// Nearest direction vector determination  

	if (fData.fNbBeamsHorizontally!=1)
	{
		// direction is calculated in the Spherical Coordinates (see Spheric Camera)
		// -- Horizontal Determination
		angle=atan2(localVector[0],localVector[2])*180.0f/3.14159265358979323846f; //QuickArcSinCos(localVector[0],localVector[2],angle);
		if (angle>angleLimit/2.0f)
		{
			angle-=angleLimit; 
		}
		if ((angle>fHorAng+fBeamAngle)||(angle<-fHorAng-fBeamAngle))
			return false; // the point is outside the maximum horizontal aperture

		nearest_dir=(angle/(fHorAng*2.0f)+0.5f)*((real)fData.fNbBeamsHorizontally-1.0f);
		hor=floor(nearest_dir);
		if (nearest_dir-hor>0.5f)
			hor+=1.0f;
		if (hor>((real)fData.fNbBeamsHorizontally-1.0f))
			hor=((real)fData.fNbBeamsHorizontally-1.0f);
		if (hor<0.0f)
			hor=0.0f;
		// hor must be between 0 and fNbBeamsHorizontally-1
		theta=fHorAng*2.0f*(hor/((real)fData.fNbBeamsHorizontally-1)-0.5f);
	}
	else
	{
		theta=0.0f;
	}
	if (fData.fNbBeamsVertically!=1)
	{
		// -- Vertical Determination
		r=localVector[0]*localVector[0]+localVector[2]*localVector[2];
		r=sqrt(r);
		angle=atan2(localVector[1],r)*180.0f/3.14159265358979323846f; //QuickArcSinCos(localVector[1],r,angle);
		if (angle>(angleLimit/2.0f))
		{
			angle-=angleLimit;
		}
		if ((angle>fVerAng+fBeamAngle)||(angle<-fVerAng-fBeamAngle))
			return false; // the point is outside the maximum vertical aperture

		nearest_dir=(angle/(fVerAng*2.0f)+0.5f)*((real)fData.fNbBeamsVertically-1.0f);
		ver=floor(nearest_dir);
		if (nearest_dir-ver>0.5f)
			ver+=1.0f;
		if (ver>((real)fData.fNbBeamsVertically-1.0f))
			ver=((real)fData.fNbBeamsVertically-1.0f);
		if (ver<0.0f)
			ver=0.0f;
		// ver must be between 0 and fNbBeamsVertically-1
		phi=(fVerAng*2.0f)*(ver/((real)fData.fNbBeamsVertically-1.0f)-0.5f);
	}
	else
	{
		phi=0.0f;
	}
	// nearest direction vector

	// Spherical Coordinates of the direction vector
	sintheta=sin(theta/180.0f*3.14159265358979323846f);
	costheta=cos(theta/180.0f*3.14159265358979323846f); //QuickSinCos(theta,sintheta,costheta);
	sinphi=sin(phi/180.0f*3.14159265358979323846f);
	cosphi=cos(phi/180.0f*3.14159265358979323846f); //QuickSinCos(phi,sinphi,cosphi);

	refDir[0]=sintheta*cosphi;
	refDir[1]=sinphi;
	refDir[2]=costheta*cosphi;

	// Direction comparaison
	cosdifferentialAngle = refDir * localVector;     // dot product to get the cosinus of the angle

	if (cosdifferentialAngle<fBeamLimit)
		return false; // the point is outside the Beam

	result=fData.fLightColor;
	result.R*=fData.fIntensity;
	result.G*=fData.fIntensity;
 	result.B*=fData.fIntensity;             

	return true;  
}
                        
// can the light source be visible in the 3D perspective display
boolean Light::IsVisibleInPerspective()
{
  return true; // the source is not a distant light (like the sun)
               // so it can be in the 3D perspective display.
}

LightTraceElement* Light::GetNewTrace(short* nb)
{
	return NULL;
}

uint32 Light::GetPrimitiveID()
{
	//Arnaud: there is no CObl primitive, so use the same geometry than for the distant light (hidden primitive)
	return 'dist';	
	//return 'CObl';
}

MCCOMErr Light::GetLightInfo(boolean &hasLightCone,boolean &hasLightSphere,real &halfAngle,TTransform3D &transform)
{
	hasLightCone	= true;
	hasLightSphere	= true;
	halfAngle		= (fHorAng+fVerAng)*kRealOneHalf;
	transform		= fTransform;

	return MC_S_OK;
}

