//3d.js

var WCMatrix = {
    x1: 1.0, x2: 0.0, x3: 0.0,
    y1: 0.0, y2: 1.0, y3: 0.0,
    zx: 0.0, zy: 0.0, zz: 1.0
}

var ScaleMatrix = {
    x1: 1.0, x2: 0.0, x3: 0.0,
    y1: 0.0, y2: 1.0, y3: 0.0,
    zx: 0.0, zy: 0.0, zz: 1.0
}

Object.prototype.clone = function(){
    var b = new Object();
    for(i in this)
        b[i] = this[i];
    return b;
}

function Clone(obj){
    for(i in obj)
        this[i] = obj[i];
    
    return this;
}//end Clone()


var Vector = {
}//end Vector

Vector.multiply = function(v1, v2){
    return {
        x: (v1.x*v2.x),
        y: (v1.y*v2.y),
        z: (v1.z*v2.z)
    }
}//end multiply()

Vector.divide = function(v1, v2){
    return {
        x: (v1.x/v2.x),
        y: (v1.y/v2.y),
        z: (v1.z/v2.z)
    }
}//end divide()

Vector.subtract = function(v1, v2){
    return{
        x: (v1.x-v2.x),
        y: (v1.y-v2.y),
        z: (v1.z-v2.z)
    }
}//end subtract()

Vector.add = function(v1, v2){
    return{
        x: (v1.x+v2.x),
        y: (v1.y+v2.y),
        z: (v1.z+v2.z)
    }
}//end add()

Vector.transformWCS = function(v1){
    return{
        x: (v1.x * WCMatrix.x1 + v1.y * WCMatrix.x2 + v1.z * WCMatrix.x3),
        y: (v1.x * WCMatrix.y1 + v1.y * WCMatrix.y2 + v1.z * WCMatrix.y3),
        z: (v1.x * WCMatrix.z1 + v1.y * WCMatrix.z2 + v1.z * WCMatrix.z3)
    }
}//end transformWCS()

Vector.transformPerspective = function(vec, cam){
    //if(!Vertex->Aligned.z)
    //Vertex->Aligned.z=1;
    //Vertex->Screen.x = FOCAL_DISTANCE * Vertex->Aligned.x / Vertex->Aligned.z + XOrigin;
    //Vertex->Screen.y = FOCAL_DISTANCE * Vertex->Aligned.y / Vertex->Aligned.z + YOrigin;

    var z = cam.z;
    if(!z)
        z = 1;
    
    return {
        x: (cam.focalDistance * vec.x / z),
        y: (cam.focalDistance * vec.y / z),
        z: z
    }
}


//Polygon class
function Polygon(vectors){
    this.vectors = new Array();
    for(var i=0; i<vectors.length; i++)
        this.vectors[i] = vectors[i].clone();
}//end Polygon()

//isVisible
Polygon.isVisible = function(xpoints, ypoints){
    
    var p1x = xpoints[1];
    var p1y = ypoints[1];
    
    var v1x = xpoints[2]-p1x;
    var v1y = ypoints[2]-p1y;
    
    var v2x = xpoints[0]-p1x;
    var v2y = ypoints[0]-p1y;
    
    a = v1x*v2y-v2x*v1y;
    
    return a<0;
}//end isVisible()

//Face class
function Face(color){
    this.points = new Array();
    this.color = color;
    if(!this.color)
        this.color = "#AFAFAF";
    
    for(var i=1; i<Face.arguments.length; i++)
        this.points[i-1] = Face.arguments[i];
}//end Face()

//Model class
function Model(polygon, faces, vector){
    this.polygon = polygon.clone();
    this.faces = faces;
    this.vector = vector;
    
    //rotate
    this.rotate = function(vec){
        
        for(var i=0; i<this.polygon.vectors.length; i++){
            var tempVector = this.polygon.vectors[i];
            
            var cos = Math.cos(vec.z);
            var sin = Math.sin(vec.z);
        
            var zx = (tempVector.x*cos) - (tempVector.y*sin) - tempVector.x;
            var zy = (tempVector.x*sin) + (tempVector.y*cos) - tempVector.y;
            
            var dist = tempVector.x + zx;
            cos = Math.cos(vec.y);
            sin = Math.sin(vec.y);
            
            var yx = (dist*cos) - (tempVector.z*sin) - dist;
            var yz = (dist*sin) + (tempVector.z*cos) - tempVector.z;
            
            dist = tempVector.y + zy;
            var dist2 = tempVector.z + yz;
            cos = Math.cos(vec.x);
            sin = Math.sin(vec.x);

            var xy = (dist*cos) - (dist2*sin) - dist;
            var xz = (dist*sin) + (dist2*cos) - (tempVector.z + yz);
            
            this.polygon.vectors[i].x += (yx+zx);
            this.polygon.vectors[i].y += (zy+xy);
            this.polygon.vectors[i].z += (xz+yz);
        }//end for
        
    }//end rotate()

}//end Model()

//Camera class
function Camera(vector, angle, focalDistance){
    this.vector = vector;
    this.angle = angle;
    this.focalDistance = focalDistance;
    if(!this.focalDistance)
        this.focalDistance = 1;
}//end Camera()



function Engine(){
    
    var models = new Array();
    var g = null;
    var _camera = new Camera( {x:0,y:0,z:300}, {x:0,y:0,z:0} );
    
    this.wireframe = true;
    this.filled = true;
    var fps = 0;
    var trackFPS = false;
    
    this.addModel = function(model){
        models.push(model);
    }//end addModel()
    
    this.getFPS = function(){
        return fps;
    }//end getFPS()
    
    this.enableFPS = function(b){
        trackFPS = b;
    }//end enableFPS()
    
    this.setGraphics = function(graphics){
        g = graphics;
    }//end setGraphics()
    
    this.setCamera = function(cam){
        _camera = camera;
    }//end setCamera()
    
    
    var screenXA = new Array();
    var screenYA = new Array();
    
    var st = 0;
    var frame = 0;
    
    var compareModels = function(m1, m2){
        return m2.vector.z-m1.vector.z;
    }//end compareModels()
    
    this.render = function(){
        if(trackFPS && frame == 0)
            st = new Date().getTime();
        
        var wfX = new Array();
        var wfY = new Array();
        var wfI = 0;
        
        var scale = 0.003;
        
        //sort models
        models.sort(compareModels);
        
        for(var j=0; j<models.length; j++){
            var model = models[j];
    
            for(var i=0; i<model.polygon.vectors.length; i++){
            
                //transform SCS
                //var tempVector = Vector.add(model.polygon.vectors[i], camera.vector);
                //tempVector = Vector.add(tempVector, model.vector);
                
                /*
                var cx = camera.vector.x;
                var cy = camera.vector.y;
                var cz = camera.vector.z;
                var tx, ty, tz; // temporary x, y and z variables
	
                // rotation around y axis
                var angle = camera.angle.x;
                tx = Math.cos(angle)*cx - Math.sin(angle)*cz;
                tz = Math.sin(angle)*cx + Math.cos(angle)*cz;
                cx = tx;
                cz = tz;
	
                // rotation around x axis
                angle = camera.angle.y;
                ty = Math.cos(angle)*cy - Math.sin(angle)*cz;
                tz = Math.sin(angle)*cy + Math.cos(angle)*cz;
                cy = ty;
                cz = tz;
	
                tempVector = Vector.subtract(tempVector, {x:cx, y:cy, z:cz});
                */
                
                /*
                tempVector = Vector.divide(tempVector, {x:scale, y:scale, z:1});
                
                tempVector = Vector.add(tempVector, camera.angle);
                
                var z = tempVector.z;
                var x = tempVector.x /z;// /scale;
                var y = tempVector.y /z;// /scale;
                */
                
                
                //transform to World Coordinates
                var vec = Vector.transformWCS(model.polygon.vectors[i]);
                
                //vec = Vector.add(camera.vector, vec);
                
                //move vector to model's position vector
                vec = Vector.add(model.vector, vec);
                
                vec = Vector.transformPerspective(vec, _camera);
                
                var x = vec.x;
                var y = vec.y;
                
                
                if(g.toString() == "SVG"){
                    x += 100;
                    y += 100;
                }//end if
                
                screenXA[i] = x;
                screenYA[i] = y;
            }//end for
            
            
            for(var i=0; i<model.faces.length; i++){
                var face = model.faces[i];
                
                var paX = new Array();
                var paY = new Array();
                
                for(var n=0; n<face.points.length; n++){
                    paX[n] = screenXA[face.points[n]];
                    paY[n] = screenYA[face.points[n]];
                }//end for
                
                if(Polygon.isVisible(paX, paY) && this.filled){
                    var color = face.color;
                    
                    g.setColor(color);
                    g.fillPolygon(paX, paY);
                }//end if
                
                if(this.wireframe && this.filled){
                    wfX[wfI] = paX;
                    wfY[wfI++] = paY;
                }//end if
                
                if(this.wireframe && !this.filled){
                    g.setColor("#000000");
                    g.drawPolygon(paX, paY);
                }//end if
                
            }//end for
            
        }//end for
        
        if(this.wireframe && this.filled){
            g.setColor("#000000");
            for(var i=0; i<wfI; i++){
                g.drawPolygon(wfX[i], wfY[i]);
            }//end for
        }//end if
        
        g.paint();

        //fps tracking
        if(trackFPS){
            frame++;
            var et = new Date().getTime();
            var timediff = (et-st)/1000;
            if(timediff >= 1){
                fps = frame;
                frame = 0;
            }//end if
        }//end if
        
    }//end render()
    
}//end Engine()





