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

		COMCamera.cpp

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

		Author:	Jerome Bignon
		Date:	06/21/99

		Revision History

		when		who			what & why
		==========	==========	======================================================
		08/20/99	cph			Put in Extension3DInit() and Extension3DCleanup() methods
		7/27/99		jdt			removed unused code
		7/27/99		jdt			fixed CreateRay() and changed Zoom based on conical camera
		7/13/99		cph			added GetStandardcameraInfo() based on conical camera

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

#include "Camera.h"


const MCGUID CLSID_Camera(R_CLSID_Camera);


void Extension3DInit(IMCUnknown* utilities)
{
	// Perform your dll initialisation there
}

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

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

	if (classId == CLSID_Camera) res = new Camera;

	return res;
}

// Constructor
Camera::Camera()
{
	fData.fZoomCoef=20.0;
	ExtensionDataChanged();
}

void* Camera::GetExtensionDataBuffer()
{
	return &fData; // The Shell uses this pointer to set the values of the camera's parameters
}
  
MCCOMErr Camera::ExtensionDataChanged()
{
	// 35mm film has 36x24mm exposure with about a 21.6667 radius
	// half width in x at 1 unit in y is ( 1 unit in y * (x / y))

	fHalfWidth 		= 21.677 / fData.fZoomCoef; 
	fInvHalfWidth 	= 1.0 / fHalfWidth;

	SetTransform(fTransform);

	return MC_S_OK;
}

int16 Camera::GetResID()
{
	return 131; // This is the view ID in the resource file
}
  
// I3DExCamera methods :
// set the coordinates transformation values
MCCOMErr Camera::SetTransform( const TTransform3D& transform )
{
	fTransform=(transform); // Copy the data of transform and not the pointer.

	fCameraOrigin = TVector3::kZero;
	fXCameraDirection.SetValues( fHalfWidth, 0.0, 0.0 );											
	fYCameraDirection.SetValues( 0.0, fHalfWidth, 0.0 ); 
	fZCameraDirection = TVector3::kNegativeZ;

	fCameraOrigin=fTransform.TransformPoint(fCameraOrigin);
	fXCameraDirection=fTransform.TransformVector(fXCameraDirection);
	fYCameraDirection=fTransform.TransformVector(fYCameraDirection);
	fZCameraDirection=fTransform.TransformVector(fZCameraDirection);

	return MC_S_OK;
}
                         
// Ray Creation :
boolean Camera::CreateRay( const TVector2&	screenPosition, 
					  		     TVector3& 	resultOrigin, 
								 TVector3& 	resultDirection) const
{
	resultOrigin = fCameraOrigin; // origin is always at the centerpoint
				
	// direction changes since rays converge to the camera center
	resultDirection[0] = fZCameraDirection.x + ( screenPosition[0]*fXCameraDirection[0]+screenPosition[1]*fYCameraDirection[0] );  
	resultDirection[1] = fZCameraDirection.y + ( screenPosition[0]*fXCameraDirection[1]+screenPosition[1]*fYCameraDirection[1] );  
	resultDirection[2] = fZCameraDirection.z + ( screenPosition[0]*fXCameraDirection[2]+screenPosition[1]*fYCameraDirection[2] );
		
	resultDirection.Normalize();

	return true;
}


boolean Camera::CreateRay(const TVector2& screenPosition,const TVector2& screenDerivate, Ray3D &ray)  const
{
	TVector3 &direction=ray.fDirection;

	direction = fZCameraDirection + screenPosition[0]*fXCameraDirection + screenPosition[1]*fYCameraDirection;

	real invNorm = 1.0f / direction.GetMagnitude();

	direction *= invNorm;

	ray.fOrigin = fCameraOrigin;

	ray.fDx = ( fXCameraDirection - direction*(direction*fXCameraDirection) ) *(screenDerivate.x*invNorm);
	ray.fDy = ( fYCameraDirection - direction*(direction*fYCameraDirection) ) *(screenDerivate.y*invNorm);

	ray.fOx=TVector3::kZero;
	ray.fOy=TVector3::kZero;

	return true;
}


 
// 3D Projection :
boolean Camera::PreProject3DTo2D( const TVector3& cameraPosition, 
									    TVector4& projectedPosition ) const
{
	const real distToCOP =  - cameraPosition.z;
		
	// projection for a perspective camera
	projectedPosition.x 	= fInvHalfWidth * cameraPosition.x;
	projectedPosition.y 	= fInvHalfWidth * cameraPosition.y;
	projectedPosition.z 	= - cameraPosition.z;
	projectedPosition.w 	= distToCOP;	
	
	return ( projectedPosition.w > 0 );

}

boolean Camera::Project3DTo2D(const TVector3& cameraPosition, TVector3& screenPosition ) const
{
	const real distToCOP = - cameraPosition.z;
	
	if ( distToCOP > 0 )
	{
		const real invW = 1.0/distToCOP;
		
		screenPosition.x 	= fInvHalfWidth * cameraPosition.x * invW;
		screenPosition.y 	= fInvHalfWidth * cameraPosition.y * invW;
		screenPosition.z 	= distToCOP;	

		return true;
	}
	
	return false;

}
  
uint32 Camera::GetPrimitiveID() const
{
	return IDTYPE('c','a','m','e');
}

boolean Camera::IsProjectionAffine( void ) const
{
	return false;
}

//GetStandardCameraInfo is used by the Interactive Renderer
void Camera::GetStandardCameraInfo(TStandardCameraInfo& cameraInfo ) const
{
	// EProjectionType: can only be ortho/isometric or persp./conical
	cameraInfo.fProjectionType 		= kProjectionType_kPerspective;
	// real	: fCameraOriginr ortho/perspective cameras
	cameraInfo.fHalfWidthAtOneUnit	= fHalfWidth;
	// boolean	: whether standard camera matches true camera
	cameraInfo.fIsMatching = true;	
}
