DLLEXPORT miBoolean latlong_lens
(
	miColor * out_pResult,
	miState * state,
	latlong_lens_params * in_pParams
)
{

	miBoolean vmirror;

	miScalar uval, vval, rayx, rayy, rayz;
	miVector raydir, raydir_internal; 



	vmirror = *mi_eval_boolean(&(in_pParams->m_vmirror));

	if (vmirror==miTRUE)
	{
	uval = (state->camera->x_resolution - state->raster_x) / state->camera->x_resolution;
	}
	else
	{
	uval = state->raster_x / state->camera->x_resolution;	
	}

	vval = (state->camera->y_resolution - state->raster_y) / state->camera->y_resolution;

	rayx = (float)(sin(vval*M_PI)*cos(M_PI*(2*uval+0.5))); 
	rayy = (float)(cos(vval*M_PI));
	rayz = (float)(sin(vval*M_PI)*sin(M_PI*(2*uval+0.5)));


	raydir.x = rayx; raydir.y = rayy; raydir.z = rayz;

	mi_vector_from_camera(state, &raydir_internal, &raydir);

  return (mi_trace_eye(out_pResult, state, &state->org, &raydir_internal));


	
}
DLLEXPORT miBoolean domeAFL_FOV_Stereo(
	miColor	*result,
	miState	*state,
	struct dsDomeAFL_FOV_Stereo *params)
{
	miScalar	cameras_separation_multiplier = *mi_eval_scalar(&params->Cameras_Separation_Map);
	miScalar	head_turn_multiplier = *mi_eval_scalar(&params->Head_Turn_Map);
	miScalar	head_tilt = *mi_eval_scalar(&params->Head_Tilt_Map);

	miVector	org, ray, target, htarget;
	miMatrix	tilt;
	double		x, y, r, phi, theta, rot, tmp, tmpY, tmpZ;
	double		sinP, cosP, sinT, cosT, sinR, cosR;

   	// normalize image coordinates btwn [-1,1]...
	// [rz] swap X-Y to match camera view and apply a vertical symmetry
	// [rz] basically, we rotate the cartesian axis 90deg CW
	x = -2.0*state->raster_y/state->camera->y_resolution+1.0;
	y = 2.0*state->raster_x/state->camera->x_resolution-1.0;

	// Calculate the radius value
	r = MI_SQRT((x*x)+(y*y));

	if (r < 1.0) {

		// Calculate phi...
		if ((r > -EPSILON) && (r < EPSILON) ) {
			phi = 0.0;
		} else {
			phi = atan2(y,x);
		}
		// Calculate theta...
		theta = r*(fov_angle/2.0);

		// start by matching camera (center camera)
		// mi_point_to_camera(state, &org, &state->org);
		org.x = org.y = org.z = 0.0;

		// saves common used values for performance reasons
		sinP = sin(phi); cosP = cos(phi);
		sinT = sin(theta); cosT = cos(theta);

		// center camera target vector (normalized)
		target.x = (miScalar)(sinP*sinT);
		target.y = (miScalar)(-cosP*sinT);
		target.z = (miScalar)(-cosT);

		if (camera != CENTERCAM) {
			// camera selection and initial position
			// @@@ use switch?
			if (camera == LEFTCAM) {
				org.x = (miScalar)(-cameras_separation*cameras_separation_multiplier/2);
			}
			if (camera == RIGHTCAM) {
				org.x = (miScalar)(cameras_separation*cameras_separation_multiplier/2);
			}

			if (dome_tilt_compensation) {		// tilted dome mode

				// head rotation
				// @@@ need to check atan2 params for 0 values?
				// @@@ save values of sin/cos
				tmpY = target.y*cos(-dome_tilt)-target.z*sin(-dome_tilt);
				tmpZ = target.z*cos(-dome_tilt)+target.y*sin(-dome_tilt);
				rot = atan2(target.x,-tmpY)*head_turn_multiplier;
				if (vertical_mode)
					rot *= fabs(sinP);
				sinR = sin(rot); cosR = cos(rot);

				// rotate camera
				tmp = org.x*cosR-org.y*sinR;
				org.y = (miScalar)(org.y*cosR+org.x*sinR);
				org.x = (miScalar)tmp;

				// compensate for dome tilt
				// @@@ save values of sin/cos
				tmp = org.y*cos(dome_tilt)-org.z*sin(dome_tilt);
				org.z = (miScalar)(org.z*cos(dome_tilt)+org.y*sin(dome_tilt));
				org.y = (miScalar)tmp;

				// calculate head target
				tmp = sqrt(target.x*target.x+tmpY*tmpY);
				htarget.x = (miScalar)(sin(rot)*tmp);
				htarget.y = (miScalar)(-cos(rot)*tmp);
				htarget.z = (miScalar)tmpZ;

				// dome rotation again on head target
				tmp = htarget.y*cos(dome_tilt)-htarget.z*sin(dome_tilt);
				htarget.z = (miScalar)(htarget.z*cos(dome_tilt)+htarget.y*sin(dome_tilt));
				htarget.y = (miScalar)tmp;

			} else {

				if (vertical_mode) {			// vertical mode

					// head rotation
					// @@@ need to check atan2 params for 0 values?
					rot = atan2(target.x,-target.z)*head_turn_multiplier*fabs(sinP);
					sinR = sin(rot); cosR = cos(rot);

					// rotate camera
					tmp = org.x*cosR-org.z*sinR;
					org.z = (miScalar)(org.z*cosR+org.x*sinR);
					org.x = (miScalar)tmp;

					// calculate head target
					tmp = sqrt(target.x*target.x+target.z*target.z);
					htarget.x = (miScalar)(sin(rot)*tmp);
					htarget.y = (miScalar)target.y;
					htarget.z = (miScalar)(-cos(rot)*tmp);

				} else {						// horizontal mode

					// head rotation
					rot = phi*head_turn_multiplier;
					sinR = sin(rot); cosR = cos(rot);

					// rotate camera
					tmp = org.x*cosR-org.y*sinR;
					org.y = (miScalar)(org.y*cosR+org.x*sinR);
					org.x = (miScalar)tmp;

					// calculate head target
					htarget.x = (miScalar)(sin(rot)*sinT);
					htarget.y = (miScalar)(-cos(rot)*sinT);
					htarget.z = (miScalar)target.z;

				}
			}

			// head tilt
			head_tilt = (miScalar)((head_tilt-0.5)*M_PI);
			mi_matrix_ident(tilt);
			mi_matrix_rotate_axis(tilt, &htarget, head_tilt);
			mi_vector_transform(&org, &org, tilt);

			// calculate ray from camera to target
			target.x *= dome_radius;
			target.y *= dome_radius;
			target.z *= dome_radius;
			ray.x = target.x-org.x;
			ray.y = target.y-org.y;
			ray.z = target.z-org.z;
			mi_vector_normalize(&ray);

		} else {		// center camera

			ray = target;

		}


		// Account for view offset...
   		// Offset is added to y & z components because they are negative values...
		// @@@ ray.x = ray.x - viewport_offset.x;
		// @@@ ray.y = ray.y + viewport_offset.y;
		// @@@ ray.z = ray.z + viewport_offset.z;

//mi_debug("II->,Phi=%f,Theta=%f,rot=%f,camx=%f,camy=%f", (miScalar)phi, (miScalar)theta, (miScalar)rot, (miScalar)org.x, (miScalar)org.y);

		// Flip the ray direction about the y-axis
		// @@@ if(*mi_eval_boolean(&params->Flip_Ray_X)) { 
		// @@@ 	ray.x = (-ray.x);
		// @@@ }
       	// Flip the ray direction about the x-axis
		// @@@ if(*mi_eval_boolean(&params->Flip_Ray_Y)) {
		// @@@ 	ray.y = (-ray.y);		
		// @@@ }


		// Convert ray from camera space
		mi_vector_from_camera(state, &ray, &ray);
		mi_point_from_camera(state, &org, &org);

		// Trace new ray...
		return(mi_trace_eye(result, state, &org, &ray));

	} else {

		// Set the return colors to Black
		result->r = result->g = result->b = result->a = 0;
		return(miFALSE);
	}

}
DLLEXPORT miBoolean domeAFL_FOV(
	miColor	*result,
	miState	*state,
	register struct dsDomeAFL_FOV *params)
{
	miScalar	fov_angle_deg = *mi_eval_scalar(&params->FOV_Angle);
	miGeoScalar	fov_angle_rad;

	miVector	viewpt_offset = *mi_eval_vector(&params->View_Offset);
	miVector	ray;

	miGeoScalar x, y, r, phi, theta;

   	/* normalize image coordinates btwn [-1,1]... */
	/* [ah] Rotate the cartesian axis 90 deg CW   */
	x = -2.0*state->raster_y/state->camera->y_resolution+1.0;
	y = 2.0*state->raster_x/state->camera->x_resolution-1.0;
	
	/* Calcaulate the radius value */
	r = MI_SQRT( ( x * x ) + ( y * y ) );

	if ( r < 1.0 ) {

		/* Calculate phi... */
		if ( (r > -EPSILON) && (r < EPSILON) ) {
			phi = 0.0;
		} else {
			phi = atan2(x,y);	// [rz] using atan2 instead of original if-then formula
		}

		/* Convert FOV angle of fisheye from degrees to radians... */
		fov_angle_rad = fov_angle_deg * M_PI / 180.0;

		/* Calculate theta... */
		
		theta = r * ( fov_angle_rad / 2.0 );

		/* Calculate Ray direction vector... */
		ray.x = (float)(sin(theta) * cos(phi));
		ray.y = (float)(-sin(theta) * sin(phi));
			/* -Z is Look At Direction*/
		ray.z = (float)(-cos(theta));

		/* Account for view offset... */
   		/* Offset is added to y & z components because 
   		   they are negative values...*/
		ray.x = ray.x - viewpt_offset.x;
		ray.y = ray.y + viewpt_offset.y;
			/* Add because MR uses -Z as Look At */
		ray.z = ray.z + viewpt_offset.z;
	
 		/* Flip the ray direction about the y-axis */
		if(*mi_eval_boolean(&params->Flip_Ray_X)) { 
			ray.x = (-ray.x);
		}

        	/* Flip the ray direction about the x-axis */
		if(*mi_eval_boolean(&params->Flip_Ray_Y)) {
			ray.y = (-ray.y);		
		}

		/* Convert ray from camera space */
		mi_vector_from_camera(state, &ray, &ray);

		/* Trace new ray... */
	        return(mi_trace_eye(result, state, &state->org, &ray));          
	
	} else {

		/* Set the return colors to Black */
		result->r = result->g =
		result->b = result->a = 0;
		return(miFALSE);
	}

} /* end of dome_FOV_AFL() */
DLLEXPORT miBoolean LatLong_Stereo(
	miColor	*result,
	miState	*state,
	struct dsLatLong_Stereo *params)
{
	miScalar	cameras_separation_multiplier = *mi_eval_scalar(&params->Cameras_Separation_Map);
	miScalar	head_tilt = *mi_eval_scalar(&params->Head_Tilt_Map);

	miVector	org, ray, target, htarget;
	miMatrix	tilt;
	double		x, y, phi, theta, tmp;
	double		sinP, cosP, sinT, cosT;

	miBoolean	zenithMode = *mi_eval_boolean(&params->Zenith_Mode);

	// Normalize image coordinates btwn [-1,-1] and [1,1]...
	x = 2.0*state->raster_x/state->camera->x_resolution-1.0;
	y = 2.0*state->raster_y/state->camera->y_resolution-1.0;

	// Calculate phi and theta...
	phi = x*(fov_horiz_angle/2.0);
	if (zenithMode)
		theta = M_PI_2-y*(fov_vert_angle/2.0);
	else
		theta = y*(fov_vert_angle/2.0);

	// Start by matching camera (center camera)
	// mi_point_to_camera(state, &org, &state->org);
	org.x = org.y = org.z = 0.0;

	// Saves common used values for performance reasons
	sinP = sin(phi); cosP = cos(phi);
	sinT = sin(theta); cosT = cos(theta);

	// Center camera target vector (normalized)
	if (zenithMode) {
		target.x = (miScalar)(sinP*sinT);
		target.y = (miScalar)(-cosP*sinT);
		target.z = (miScalar)(-cosT);
	} else {
		target.x = (miScalar)(sinP*cosT);
		target.y = (miScalar)(sinT);
		target.z = (miScalar)(-cosP*cosT);
	}

	if (camera != CENTERCAM) {
		// Camera selection and initial position
		if (camera == LEFTCAM) {
			org.x = (miScalar)(-cameras_separation*cameras_separation_multiplier/2);
		} else if (camera == RIGHTCAM) {
			org.x = (miScalar)(cameras_separation*cameras_separation_multiplier/2);
		}

		// Head rotation = phi
		// Rotate camera
		if (zenithMode) {
			tmp = org.x*cosP-org.y*sinP;
			org.y = (miScalar)(org.y*cosP+org.x*sinP);
			org.x = (miScalar)tmp;
		} else {
			tmp = org.x*cosP-org.z*sinP;
			org.z = (miScalar)(org.z*cosP+org.x*sinP);
			org.x = (miScalar)tmp;
		}

		// Calculate head target
		htarget.x = (miScalar)(sinP*sinT);
		htarget.y = (miScalar)(-cosP*sinT);
		htarget.z = (miScalar)target.z;

		// Head tilt
		head_tilt = (miScalar)((head_tilt-0.5)*M_PI);
		mi_matrix_ident(tilt);
		mi_matrix_rotate_axis(tilt, &htarget, head_tilt);
		mi_vector_transform(&org, &org, tilt);

		// Calculate ray from camera to target
		target.x *= parallax_distance;
		target.y *= parallax_distance;
		target.z *= parallax_distance;
		ray.x = target.x-org.x;
		ray.y = target.y-org.y;
		ray.z = target.z-org.z;
		mi_vector_normalize(&ray);
	} else{		
    // Center camera
    ray = target;
	}

//mi_debug("II->,Phi=%f,Theta=%f,rot=%f,camx=%f,camy=%f", (miScalar)phi, (miScalar)theta, (miScalar)rot, (miScalar)org.x, (miScalar)org.y);

	// Flip the X ray direction about the Y-axis
	if(*mi_eval_boolean(&params->Flip_Ray_X)) { 
		org.x = (-org.x);
		ray.x = (-ray.x);
	}
	// Flip the Y ray direction about the X-axis
	if(*mi_eval_boolean(&params->Flip_Ray_Y)) {
		if(zenithMode) {
			org.z = (-org.z);
			ray.z = (-ray.z);
		} else {
			org.y = (-org.y);
			ray.y = (-ray.y);
		}
	}

  #if 1
    /* Adjust the ray differentials */
    rotate_ray_differentials(state, ray);
  #endif
  
	// Convert ray from camera space
	mi_vector_from_camera(state, &ray, &ray);
	mi_point_from_camera(state, &org, &org);

	// Trace new ray...
	return(mi_trace_eye(result, state, &org, &ray));

}
DLLEXPORT miBoolean domeAFL_WxH(
	miColor	*result,
	miState	*state,
	register struct dsDomeAFL_WxH *params)
{
	miScalar	diameter = *mi_eval_scalar(&params->Diameter);
	miScalar	height   = *mi_eval_scalar(&params->Height);
	
	miGeoScalar	fov;	    /* Field-of-View of specified dome */
	miGeoScalar	radius;	    /* Radius of dome being subtended */
	
	/* Does this need to be a pointer? */
	miVector	viewpt_offset = *mi_eval_vector(&params->View_Offset);
	miVector	ray;

	miGeoScalar	x, y, r, phi, theta;

	/* normalize image coordinates btwn [-1,1]... */
	x = (2.0 * state->raster_x) / state->camera->x_resolution - 1.0;
	y = (2.0 * state->raster_y) / state->camera->y_resolution - 1.0;

	/* Calculate FOV for given Diameter & height of dome...          */
	/* Equations obtained from:                                      */
	/*    http://mathforum.org/dr.math/faq/faq.circle.segment.html#8 */
	radius =  ((diameter * diameter) + (4 * height * height)) / (8 * height);
	fov = 2 * asin(diameter / (2 * radius));
	
	/* Calcaulate the radius value */
	r = MI_SQRT( ( x * x ) + ( y * y ) );

	if ( r < 1.0 ) {

		/* Calculate phi... */
		if ( (r > -EPSILON) && (r < EPSILON) ) {
			phi = 0.0;
		} else {
			phi = atan2(x,y);	// [rz] using atan2 instead of original if-then formula
		}

		/* Calculate theta... */
		theta = r * ( fov / 2.0 );

		/* Calculate Ray direction vector... */
		ray.x = (float)(sin(theta) * cos(phi));
		ray.y = (float)(-sin(theta) * sin(phi));
			/* -Z is Look At Direction*/
		ray.z = (float)(-cos(theta));

		/* Account for view offset... */
		/* Offset is added to y & z components because 
		   they are negative values...*/
		ray.x = ray.x - viewpt_offset.x;
		ray.y = ray.y + viewpt_offset.y; 
			/* Add because MR uses -Z as Look At */
		ray.z = ray.z + viewpt_offset.z;

		// Flip the ray direction about the y-axis
		if(*mi_eval_boolean(&params->Flip_Ray_X)) { 
 			ray.x = (-ray.x);
		}

        	/* Flip the ray direction about the x-axis */
		if(*mi_eval_boolean(&params->Flip_Ray_Y)) {
			ray.y = (-ray.y);		
		}

		/* Convert ray from camera space */
		mi_vector_from_camera(state, &ray, &ray);

		/* Trace new ray... */
		return(mi_trace_eye(result, state, &state->org, &ray));

	} else {

		/* Set return color to Black */
		result->r = result->g =
		result->b = result->a = 0;
		return(miFALSE);
	}

} /* end of domeAFL_WxH() */