import java.lang.Math;
// the CPoint3D make all var to public
// CWindow is a window in View Plane. Since most windows systems are up-side-down,
// i.e., the origin is at the upper-left corner, so this class is also upside down.
class CWindow
{ float top, bottom, left, right;
public CWindow(float t, float b, float l, float r)
{ set(t, b, l, r);
}
public CWindow()
{ reset();
}
public void set(float t, float b, float l, float r)
{ if (t > b)
{ top = t; bottom = b; }
else
{ top = b; bottom = t; }
if (l < r)
{ left = l; right = r; }
else
{ left = r; right = l; }
}
public void reset()
{ top = (float)1.0; bottom = (float)-1.0;
left = (float)-1.0; right = (float)1.0;
}
}
public class CCamera
{ float COP; // center of perjection (eye position)
// It is on -VPN dir in view coord w/ a distance behind VRP
byte projMode; // perspective/parallel
int screenW, screenH;
float FOV; // field of view. Like that in zoom lens
CPoint3D VRP; // view reference point = origin of VC
CVector VPN; // view plane normal. also an axis in VC
CVector VPU, VPV; // u- & v- axies in VC
CVector VUP; // the Up dir in VC. Not required perp. to VPN
CPoint3D eyeInWC; // used for back face removal & shading
CWindow window; // visible window in view plane
CMatrix3D Mw_v; // mapping from WC to VC
CMatrix3D Mv_w; // mapping from VC to WC. Used to calculate
// eyeInWC and dir from eye to pt in WC
CMatrix3D Mwin_vp; // mapping windows in view plane to viewport in NDC.
// This is a matter of 2D pts transformation.
CMatrix3D projM; // projection matrix
CMatrix3D totalM; // final matrix that converts pts from WC to VC.
// totalM = Mwin_vp * projM * MW_v.
final byte PARALLEL = 0;
final byte PERSPECTIVE = 1;
public CCamera()
{
eyeInWC = new CPoint3D();
VRP = new CPoint3D();
VPN = new CVector();
VPU = new CVector();
VPV = new CVector();
VUP = new CVector();
window = new CWindow();
Mw_v = new CMatrix3D(4,4);
Mv_w = new CMatrix3D(4,4);
Mwin_vp = new CMatrix3D(4,4);
projM = new CMatrix3D(4,4);
reset();
}
// ToDo: eyeInWC should be corres. to COP
public void reset()
{ COP = 5;
eyeInWC.x = (float)0.0; eyeInWC.y=(float)0.0;
eyeInWC.z=(float)6.0;
VRP.x = (float)0.0; VRP.y = (float)0.0; VRP.z = (float)1.0;
VUP.dx = (float)0.0; VUP.dy = (float)1.0; VUP.dz = (float)0.0;
VPN.dx = (float)0.0; VPN.dy = (float)0.0; VPN.dz = -(float)1.0;
projMode = PERSPECTIVE;
setViewCoord();
setProjMatrix();
window.reset();
CModel.reset(); // static method call
}
/* after change the eyeInWC, you have to call
setViewCoord();
setWCToVCMatrix();
then calculate the COP (the n-component of eye's (u,v,n) coord in VC by
(u,v,n) = Mw_v * eyeInWC;
*/
public void prepareDisplay()
{
setViewCoord();
setWCToVCMatrix();
setWindowToViewportMatrix();
totalM = CMatrix3D.mult(projM, Mw_v);
totalM = CMatrix3D.mult(Mwin_vp, totalM);
}
public void setViewCoord()
{
VPN.normalize();
VPU = CVector.crossProduct(VPN, VUP);
if ( (VPU.dx == 0) && (VPU.dy == 0) && (VPU.dz == 0) ) /* so VUP // VPN */
// set VPU to a vector that is perp. to VPN
if ( VPN.dx != (float)0.0 || VPN.dy != (float)0.0 )
{ VPU.dx = -VPN.dy; VPU.dy = VPN.dx;
VPU.dz = (float)0.0;
}
else
{ VPU.dx = -VPN.dz; VPU.dy = (float)0.0;
VPU.dz = -VPN.dx;
}
else
VPU.normalize();
VPV = CVector.crossProduct(VPU, VPN);
}
public void setProjMatrix()
{ projM.unit();
if (projMode == PERSPECTIVE)
projM.set(3, 2, 1/COP); // foreshortening ratio
}
public void setCOP(float cop)
{ if ( Math.abs(cop) < (float)0.00000000000001 )
{ System.out.println("COP cannot be zero!!!\n");
System.out.println("Ignore the change.\n");
return;
}
COP = cop;
setProjMatrix();
}
// field of view affects the visible window size.
public void setFOV(double angle) // FOV affect the window
{ double rad = 0.017453293 * angle / 2;
float w = COP * (float)Math.tan(rad);
float h = window.top * w / window.right;
window.set(h, -h, -w, w);
}
public void setVPN(float dx, float dy, float dz)
{ if ( Math.abs(dx) < (float)0.00000000000001 &&
Math.abs(dy) < (float)0.00000000000001 &&
Math.abs(dz) < (float)0.00000000000001 )
{ System.out.println("VPN can't be (0,0,0)!\n");
return;
}
VPN.dx = dx; VPN.dy = dy;
VPN.dz = dz;
}
public void setVUP(float dx, float dy, float dz)
{ if ( Math.abs(dx) < (float)0.00000000000001 &&
Math.abs(dy) < (float)0.00000000000001 &&
Math.abs(dz) < (float)0.00000000000001 )
{ System.out.println("VUP can't be (0,0,0)!\n");
return;
}
VUP.dx = dx; VUP.dy = dy;
VUP.dz = dz;
}
public void setVRP(float x, float y, float z)
{ VRP.x = x; VRP.y = y;
VRP.z = z;
}
public void setScreen(int w, int h)
{ screenW = (w < h) ? w : h;
screenH = screenW;
/** ToDo:
set the frameBuf and depthBuf and init. **/
}
public void setWindowToViewportMatrix()
{ float tmp1, tmp2;
Mwin_vp.unit();
tmp1 = (float)1.0/(window.right - window.left);
Mwin_vp.set(0, 0, tmp1);
tmp2 = -(float)1.0/(window.top - window.bottom);
Mwin_vp.set(1, 1, tmp2);
// Here may have bugs!!!
Mwin_vp.set(0, 3, - window.left * tmp1);
Mwin_vp.set(1, 3, - window.bottom * tmp2 + (float)1.0);
}
public void setWCToVCMatrix()
{ CVector r = new CVector();
r.dx = VRP.x; r.dy = VRP.y;
r.dz = VRP.z;
Mw_v.set(0,0,VPU.dx); Mw_v.set(0,1,VPU.dy); Mw_v.set(0,2,VPU.dz);
Mw_v.set(1,0,VPV.dx); Mw_v.set(1,1,VPV.dy); Mw_v.set(1,2,VPV.dz);
Mw_v.set(2,0,VPN.dx); Mw_v.set(2,1,VPN.dy); Mw_v.set(2,2,VPN.dz);
Mw_v.set(0, 3, -CVector.dotProduct(r,VPU));
Mw_v.set(1, 3, -CVector.dotProduct(r,VPV));
Mw_v.set(2, 3, -CVector.dotProduct(r,VPN));
Mw_v.set(3, 0, (float)0.0); Mw_v.set(3, 1, (float)0.0);
Mw_v.set(3, 2, (float)0.0); Mw_v.set(3, 3, (float)1.0);
// Mv_w is the inverse of Mw_v. Because this is a pure rotation, so the
// first 3x3 submatrix in Mv_w is simply the transpose of that in Mw_v.
// (x, y, z) = inv(M) * (u, v, n) + VRP
Mv_w.set(0,0,VPU.dx); Mv_w.set(0,1,VPV.dx); Mv_w.set(0,2,VPN.dx);
Mv_w.set(1,0,VPU.dy); Mv_w.set(1,1,VPV.dy); Mv_w.set(1,2,VPN.dy);
Mv_w.set(2,0,VPU.dz); Mv_w.set(2,1,VPV.dz); Mv_w.set(2,2,VPN.dz);
Mv_w.set(0,3,VRP.x); Mv_w.set(1,3,VRP.y); Mv_w.set(2,3,VRP.z);
Mv_w.set(3, 0, (float)0.0); Mv_w.set(3, 1, (float)0.0);
Mv_w.set(3, 2, (float)0.0); Mv_w.set(3, 3, (float)1.0);
}
}
// since array in Java is extremely slow -- every reference need check type,
// so instead of 2 dimension, this class is implement as a 1D array.
public class CMatrix3D
{ int row, col;
float[] data;
public CMatrix3D()
{ row = col = 0;
data = null;
}
public CMatrix3D(int row, int col)
{ this.row = row; this.col = col;
data = new float[row*col];
if ( col == row )
unit();
}
public void unit()
{ if (row != col)
return;
int i;
for (i=0; i<row*row; i++)
this.data[i] = (float)0.0;
for (i=0; i<row; i++)
this.data[i*col+i] = (float)1.0;
}
public void set(int r, int c, float val)
{ this.data[col * r + c] = val;
}
public void set(int r, int c, double val)
{ this.data[col * r + c] = (float)val;
}
public float get(int r, int c)
{ return this.data[col * r + c];
}
public static CMatrix3D mult(CMatrix3D m1, CMatrix3D m2)
{ CMatrix3D out;
if (m1.col != m2.row)
{ System.out.println("Two matrices are not compatible!");
return null;
}
out = new CMatrix3D(m1.row, m2.col);
int i, j, k ;
for(i=0; i<m1.row; i++)
for(j=0; j<m2.col; j++) {
float sum = (float)0.0 ;
for(k=0; k<m1.col; k++)
sum += m1.get(i, k) * m2.get(k, j);
out.set(i, j, sum);
}
return out ;
}
}
import java.lang.Math;