//
//  Cam.java
//  raytracing
//
//  Created by Martin RobinSon on 12/07/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//
import java.awt.Color;

public class Cam {
		//---------------------------------------------------------------------
		//init
		//---------------------------------------------------------------------
		double x, y, z, vx, vy, vz, arcx, arcy;
		TroisDee list3d[];	int num3d;
		Poly2d zlist[];		int zpivot;
		int width, height;
		public Cam(int num3d,int numz,int width,int height){
			this.x = 0.0; this.y = 0.0; this.z = 0.0; this.vx = 0.0; this.vy = 0.0; this.vz = 1.0;
			this.arcx = 0.30*Math.PI; this.arcy = 0.30*Math.PI;
			this.list3d = new TroisDee[num3d]; this.num3d = 0;
			this.zlist = new Poly2d[numz];	this.zpivot = 0;
			this.width = width; this.height = height;
		}
		//---------------------------------------------------------------------
		//operation
		//---------------------------------------------------------------------
		public void move_ip(double x, double y, double z){
			this.x += x;	this.y += y;	this.z += z;
			for (int i = 0; i<this.num3d; i++){
				this.list3d[i].x -= x;	this.list3d[i].y -= y;	this.list3d[i].z -= z;
			}
		}
		public void rotate_x(double r){
			double y, z, radius, radian; // = Math.atan2( this.vy, this.vz );
			//this.vy = Math.sin( radian + r ); this.vz = Math.cos( radian + r );
			for (int i = 0; i<this.num3d; i++){
				y = this.list3d[i].y;	z = this.list3d[i].z;
				radian = Math.atan2( y, z );	radius = Math.sqrt((y*y)+(z*z));
				this.list3d[i].y = radius * Math.sin(radian-r);
				this.list3d[i].z = radius * Math.cos(radian-r);
				this.list3d[i].rotate_x(-r);
			}
		}
		public void rotate_y(double r){
			double x, z, radius, radian ;//= Math.atan2( this.vx, this.vz );
			//this.vx = Math.sin( radian + r ); this.vz = Math.cos( radian + r );
			for (int i = 0; i<this.num3d; i++){
				x = this.list3d[i].x;	z = this.list3d[i].z;
				radian = Math.atan2( x, z );	radius = Math.sqrt((x*x)+(z*z));
				this.list3d[i].x = radius * Math.sin(radian-r);
				this.list3d[i].z = radius * Math.cos(radian-r);
				this.list3d[i].rotate_y(-r);
			}
		}
		public void rotate_z(double r){
			double x, y, radius, radian ;//= Math.atan2( this.vx, this.vy );
			//this.vx = Math.sin( radian + r );	this.vy = Math.cos( radian + r );
			for (int i = 0; i<this.num3d; i++){
				x = this.list3d[i].x;	y = this.list3d[i].y;
				radian = Math.atan2( x, y );	radius = Math.sqrt((x*x)+(y*y));
				this.list3d[i].x = radius * Math.sin(radian-r);
				this.list3d[i].y = radius * Math.cos(radian-r);
				this.list3d[i].rotate_z(-r);
			}
		}
		//---------------------------------------------------------------------
		//obj
		//---------------------------------------------------------------------
		public class Poly2d {
			int x[], y[], num;
			double z;
			Color fill, border;
			public Poly2d (){
				this.z = 0.0;
			}	
		}
		public Poly2d poly3dto2d(TroisDee.Poly poly3d){
			Poly2d poly2d = new Poly2d();
			poly2d.num = poly3d.pivot;
			poly2d.x = new int[poly3d.pivot];
			poly2d.y = new int[poly3d.pivot];
			double tmpx = 0,tmpy = 0,tmpz = 0;
			for (int h = 0; h < poly3d.pivot; h++) {
				if (poly3d.liste[h].z <= 0){
					return null;	//this was the only way i found to division per zero , also , we dont have to draw behind
				}
				poly2d.x[h] = (int)(( (poly3d.liste[h].x/poly3d.liste[h].z) + 1.0)*(width/2));
				poly2d.y[h] = (int)(( (poly3d.liste[h].y/poly3d.liste[h].z) + 1.0)*(width/2));
				tmpx += poly3d.liste[h].x;	
				tmpy += poly3d.liste[h].y;	
				tmpz += poly3d.liste[h].z;
			}
			tmpx /= poly3d.pivot;
			tmpy /= poly3d.pivot;
			tmpz /= poly3d.pivot;
			//poly2d.z = Math.sqrt(tmpx*tmpx + tmpy*tmpy + tmpz*tmpz);
			poly2d.z = (tmpx*tmpx + tmpy*tmpy + tmpz*tmpz);
			poly2d.fill = new Color(poly3d.r,poly3d.g,poly3d.b);
			poly2d.border = new Color(poly3d.dr, poly3d.dg, poly3d.db);
			return poly2d;
		}
		//---------------------------------------------------------------------
		//occlusion
		//---------------------------------------------------------------------
		public boolean backface_culling(TroisDee.Poly poly3d, TroisDee obj3d) {
			double tmpx=0,tmpy=0,tmpz=0;
			for (int i=0;i<poly3d.pivot;i++) {
				tmpx += poly3d.liste[i].x;
				tmpy += poly3d.liste[i].y;
				tmpz += poly3d.liste[i].z;
			}
			tmpx /= poly3d.pivot;
			tmpy /= poly3d.pivot;
			tmpz /= poly3d.pivot;
			if ( (obj3d.x*obj3d.x+obj3d.y*obj3d.y+obj3d.z*obj3d.z) >= (tmpx*tmpx+tmpy*tmpy+tmpz*tmpz) ) {
				return false;
			}
			return true;
		}
		//---------------------------------------------------------------------
		//render
		//---------------------------------------------------------------------
		public void scan(){
			this.zpivot = 0;	//important to reset zlist;
			//=======================polygon scan===============================
			for (int j = 0; j<this.num3d; j++) {	
				this.list3d[j].getReal();		//better find another name
				for ( int i = 0; i < this.list3d[j].polypivot; i++ ) {
					TroisDee.Poly poly3d = this.list3d[j].polys[i];		//all polygons
					//if (backface_culling(poly3d, this.list3d[j]) == false) {
						Poly2d poly2d =  poly3dto2d(poly3d); //the great transformation from a 3d polygon to a 2d one
						if ((poly2d != null) && ( this.zpivot < this.zlist.length )) {
								this.zlist[this.zpivot] = poly2d;
								this.zpivot += 1;
						}
					//}
				}
				this.list3d[j].getUnreal();		//better find another name
			}
			//=======================polygon scan===============================
			//=================Zsort=========================
			//sort all 2d polygon from the fartest
			//gnome sorting of all polygon from fartest to nearest
			int pivot = 0;
			while (pivot < this.zpivot) {
				if ( pivot == 0 || this.zlist[pivot-1].z >= this.zlist[pivot].z ) {
					pivot += 1;
				}
				else {
					Poly2d tmp = this.zlist[pivot];
					this.zlist[pivot] = this.zlist[pivot-1];
					this.zlist[pivot-1] = tmp;
					pivot -= 1;
				}
			}
			//=================Zsort==========================
		}
}
