import java.awt.*; import Geometry.*; import java.util.*; public class ThreeDimensionalApplet extends PixApplet { int x = 100, y = 100; int coords[][] = new int[4][]; double normals[][] = new double[4][]; int colors[][] = new int[4][]; int currentColor[] = new int[4]; int texCoords[][] = new int[4][]; ThreeDimensionalApplet() { for(int i=0; i RENDER_LAST ) { throw new RuntimeException("Attempted to draw an invalid primitive"); } if( wireframeMode && shapeType == RENDER_TRIANGLE ) renderState = RENDER_OPEN_TRIANGLE; else if( wireframeMode && shapeType == RENDER_QUAD ) renderState = RENDER_OPEN_QUAD; else renderState = shapeType; sentVertices = 0; polygon = new Polygon(); } //function end //Concludes a primitive-drawing operation void end() { if( renderState == RENDER_NONE ) { throw new RuntimeException("Cannot call end() without begin()"); } else if( renderState == RENDER_POLYGON ) { //if(!cullPolygon || counterClockwise(polygon)) // graphics.fillPolygon(polygon); } else if( renderState == RENDER_OPEN_POLYGON ) { //graphics.drawPolygon(polygon); } renderState = RENDER_NONE; } //functions color3d, color3i, color //Sets the current color. //Drawing functions will use the current color when a primitive is drawn //color3d accepts values in the range [0.0, 1.0] //color3i accepts values in the range [0 255] //color accepts a java.awt.Color object //They are given seperate names to avoid accidental type promotion void color3d(double r, double g, double b) { //graphics.setColor(new Color((int)(r*255.0), (int)(g*255.0), (int)(b*255.0))); } void color3i(int r, int g, int b) { //graphics.setColor(new Color(r,g,b)); currentColor[0] = r; currentColor[1] = g; currentColor[2] = b; colors[sentVertices][0] = r; colors[sentVertices][1] = g; colors[sentVertices][2] = b; } void color(Color color) { //graphics.setColor(color); } //function vertex //Submits vertices for drawing primitives, 2d integer form void vertex(int x, int y) { vertex((double)x, (double)y, 0); } //function vertex //Submits vertices for drawing primitives, double form void vertex(double x, double y, double z) { vertex(new Vector3(x,y,z)); } //function vertex(Vector3) //Submits vertices for drawing primitives, vector/double form void vertex(Vector3 worldCoord) { //Three different coordinates //worldCoord is the input coordinate //transCoord is the post-modelview-transformation coordinate //screenCoord is the post-projection coordinate boolean fastMul = false; int screenX, screenY; int screenZ; //This following section is probably the key part of the assignment // Here is where we take the vertex input and transform it, // according to the current transformation matrix // We assume we have orthogonal projection, so applying the projection matrix // would be (more or less) equivalent to dropping the z coordinate //To save time, we use a very optimized partial matrix multiplier, fastMul() // see the notes in Geometry/Matrix4x4.java on our hacks if( fastMul ) { double[] transCoord; transCoord = transformMatrix.fastMul(worldCoord); screenX = (int)transCoord[0]; screenY = (int)transCoord[1]; screenZ = (int)transCoord[2]; } else { //This way is a more proper, but slower, version above the above //It uses a general (complete) matrix multipler, as opposed to the other // which makes assumptions of the Matrix input Vector3 transCoord, screenCoord; transCoord = transformMatrix.mul(worldCoord); screenCoord = transCoord; screenX = (int)screenCoord.x(); screenY = (int)screenCoord.y(); double doubleZ = screenCoord.z(); screenZ = (int)( Integer.MAX_VALUE*((doubleZ - near)/(far - near)) ); //screenZ = (int)screenCoord.z(); } //lighting = true; if( !lighting ) { colors[sentVertices][0] = currentColor[0]; colors[sentVertices][1] = currentColor[1]; colors[sentVertices][2] = currentColor[2]; } else { Color col = computeLightedColor(); colors[sentVertices][0] = col.getRed(); colors[sentVertices][1] = col.getGreen(); colors[sentVertices][2] = col.getBlue(); } //Take different actions based on the render state //For most primitives, we test if we need to light before drawing if( renderState == RENDER_NONE ) { throw new RuntimeException("Must call begin() before sending a vertex"); } else if( renderState == RENDER_POINTS ) { //For points, jsut draw an appropriatly sized box on every vertex() //if(lighting) graphics.setColor(computeLightedColor()); //graphics.fillRect(screenX-pointSize/2, screenY-pointSize/2, pointSize, pointSize); for(int i=screenX-thePointSize/2; i 0 ) { //if(lighting) graphics.setColor(computeLightedColor()); //graphics.drawLine(lineX, lineY, screenX, screenY); } //lineX = screenX; //lineY = screenY; sentVertices++; } else if( renderState == RENDER_POLYGON || renderState == RENDER_OPEN_POLYGON || renderState == RENDER_TRIANGLE || renderState == RENDER_OPEN_TRIANGLE || renderState == RENDER_QUAD || renderState == RENDER_OPEN_QUAD ) { //For all of the polygon types, we will need to store the additional vertex //With triangles, we will draw on the third vtx, below //With polygons, we draw on the end() call //polygon.addPoint(screenX, screenY); //coords[sentVertices] = new Vector3(screenX, screenY, screenZ); coords[sentVertices][0] = screenX; coords[sentVertices][1] = screenY; coords[sentVertices][2] = screenZ; sentVertices++; } //Draw a triangle on every third vertex if( renderState == RENDER_TRIANGLE && sentVertices == 3 ) { //Perform back face culling if(!cullPolygon || counterClockwise(coords)) { //if(lighting) graphics.setColor(computeLightedColor()); //graphics.fillPolygon(polygon); /* drawTriangle(true, polygon.xpoints[0], polygon.ypoints[0], 0, 0, 0, 0, 0, 0, polygon.xpoints[1], polygon.ypoints[1], 0, 0, 0,0, 0, 0, polygon.xpoints[2], polygon.ypoints[2], 0, 0, 0, 0, 0, 0); */ drawTriangle(true, true, coords[0][0], coords[0][1], coords[0][2], colors[0][0], colors[0][1], colors[0][2], texCoords[0][0], texCoords[0][1], coords[1][0], coords[1][1], coords[1][2], colors[1][0], colors[1][1], colors[1][2], texCoords[1][0], texCoords[1][1], coords[2][0], coords[2][1], coords[2][2], colors[2][0], colors[2][1], colors[2][2], texCoords[2][0], texCoords[2][1] ); } polygon = new Polygon(); sentVertices = 0; } else if( renderState == RENDER_OPEN_TRIANGLE && sentVertices == 3 ) { //perform back face culling if(!cullPolygon || counterClockwise(coords)) { //if(lighting) graphics.setColor(computeLightedColor()); //graphics.drawPolygon(polygon); drawTriangle(false, false, coords[0][0], coords[0][1], coords[0][2], colors[0][0], colors[0][1], colors[0][2], 0, 0, coords[1][0], coords[1][1], coords[1][2], colors[1][0], colors[1][1], colors[1][2], 0, 0, coords[2][0], coords[2][1], coords[2][2], colors[2][0], colors[2][1], colors[2][2], 0, 0 ); } sentVertices = 0; } //Draw a quad on every fourth vertex if( renderState == RENDER_OPEN_QUAD && sentVertices == 4 ) { //perform back face culling if(!cullPolygon || counterClockwise(coords)) { //if(lighting) graphics.setColor(computeLightedColor()); //graphics.drawPolygon(polygon); drawTriangle(false, false, coords[0][0], coords[0][1], coords[0][2], colors[0][0], colors[0][1], colors[0][2], 0, 0, coords[1][0], coords[1][1], coords[1][2], colors[1][0], colors[1][1], colors[1][2], 0, 0, coords[2][0], coords[2][1], coords[2][2], colors[2][0], colors[2][1], colors[2][2], 0, 0 ); drawTriangle(false, false, coords[0][0], coords[0][1], coords[0][2], colors[0][0], colors[0][1], colors[0][2], 0, 0, coords[2][0], coords[2][1], coords[2][2], colors[2][0], colors[2][1], colors[2][2], 0, 0, coords[3][0], coords[3][1], coords[3][2], colors[3][0], colors[3][1], colors[3][2], 0, 0 ); } sentVertices = 0; } //Draw a quad on every fourth vertex else if( renderState == RENDER_QUAD && sentVertices == 4 ) { //perform back face culling if(!cullPolygon || counterClockwise(coords)) { //if(lighting) graphics.setColor(computeLightedColor()); //graphics.drawPolygon(polygon); drawTriangle(true, false, coords[0][0], coords[0][1], coords[0][2], colors[0][0], colors[0][1], colors[0][2], 0, 0, coords[1][0], coords[1][1], coords[1][2], colors[1][0], colors[1][1], colors[1][2], 0, 0, coords[2][0], coords[2][1], coords[2][2], colors[2][0], colors[2][1], colors[2][2], 0, 0 ); drawTriangle(true, false, coords[0][0], coords[0][1], coords[0][2], colors[0][0], colors[0][1], colors[0][2], 0, 0, coords[2][0], coords[2][1], coords[2][2], colors[2][0], colors[2][1], colors[2][2], 0, 0, coords[3][0], coords[3][1], coords[3][2], colors[3][0], colors[3][1], colors[3][2], 0, 0 ); } sentVertices = 0; } } //function identity void identity() { transformMatrix = new Matrix4x4(); } //function transform(Matrix) //Sets the current modelview transform matrix //Essentially equivalent to glLoadMatrix() void transform(Matrix4x4 mat) { //transformMatrix = mat.mul(transformMatrix); transformMatrix = transformMatrix.mul(mat); //System.out.println(mat); //We want to append this matrix onto the end of the chain //Therefore out = in * mat } //function getTransform() //Retrieves the current transform matrix Matrix4x4 getTransform() { return transformMatrix; } //function pushMatrix //Pushes the current transform matrix onto the transform stack //Essentially equivalent to glPushMatrix() void pushMatrix() { transformStack.push( transformMatrix ); } //function popMatrix //Pushes the current transform matrix onto the transform stack //Essentially equivalent to glPopMatrix() void popMatrix() { transformMatrix = (Matrix4x4)transformStack.peek(); transformStack.pop(); } //function ortho //Sets up an orthoganl projection //Right now, only the near and far args have effect void ortho(double _near, double _far) { near = _near; far = _far; zInterval = (far - near) / (Integer.MAX_VALUE / 2); } double near=-1000; double far=1000; double zInterval = (far - near) / (Integer.MAX_VALUE / 2); //function project //Given the current modelview and projection matrices, //Projects the input in world coordinates to screen coordinates //Similar to gluProject() Vector3 project(Vector3 world) { return transformMatrix.mul(world); } //function unProject //Given the current modelview and projection matrices, //Performs the inverse of the projection operation, which switches from //screen coordinates to world coordinates; Similar to gluUnProject() Vector3 unProject(Vector3 screen) { return transformMatrix.inverse().mul(screen); } //function setDamage //Informs the system that the window has been damaged/invalidated //and needs to be redrawn void setDamage() { damage = true; } boolean wireframeMode = false; void wireframe(boolean doWire) { wireframeMode = doWire; } //Render modes - passed to begin() static final int RENDER_NONE=0; static final int RENDER_LINES=1; static final int RENDER_LINE_STRIP=2; static final int RENDER_POLYGON=3; static final int RENDER_OPEN_POLYGON=4; static final int RENDER_TRIANGLE=5; static final int RENDER_OPEN_TRIANGLE=6; static final int RENDER_QUAD=7; static final int RENDER_OPEN_QUAD=8; static final int RENDER_POINTS=9; static final int RENDER_LAST=RENDER_POINTS; //Option settings - passed to enable() / disable() static final int RENDER_CULL_FACE=256; static final int RENDER_LIGHTING=257; static final int RENDER_TEXTURE=258; void enable(int param) { switch(param) { case RENDER_CULL_FACE: cullPolygon = true; break; case RENDER_LIGHTING: lighting = true; break; case RENDER_TEXTURE: texturing = true; break; default: throw new RuntimeException("Invalid parameter passed to enable"); } } void disable(int param) { switch(param) { case RENDER_CULL_FACE: cullPolygon = false; break; case RENDER_LIGHTING: lighting = false; break; case RENDER_TEXTURE: texturing = false; break; default: throw new RuntimeException("Invalid parameter passed to enable"); } } //function pointSize //Sets the size of the boxes used to draw the point primitives void pointSize(int size) { thePointSize = size; } //function normal //Sets the current normal, used in dynamic lighting calculations void normal(Vector3 vec) { currentNormal = vec; } //function texture //Sets the currently used texture - texturing must be enabled seperatly void texture(Bitmap bmp) { currentTexture = bmp; } void texCoord(double s, double t) { texCoords[sentVertices][0] = (int)(s*currentTexture.width); texCoords[sentVertices][1] = (int)(t*currentTexture.height); } //function lightDirection //Sets the direction of the incoming light (we have only one) //Note that we assume light is at infinite distance void lightDirection(Vector3 pos) { lightPos = pos; } //Various functions to set lighting properties //These include ambient/diffuse color/material/weight-coefficient //As well as functions to set ambient & diffuse at same time void ambientColor(int r, int g, int b) { ambientColor = new Color(r, b, g); } void ambientMaterial(int r, int g, int b) { ambientMaterial = new Color(r, b, g); } void ambientAndDiffuseMaterial(int r, int g, int b) { ambientMaterial = new Color(r, b, g); diffuseMaterial = new Color(r, g, b); } void ambientAndDiffuseColor(int r, int g, int b) { ambientColor = new Color(r, b, g); diffuseColor = new Color(r, g, b); } void diffuseColor(int r, int g, int b) { diffuseColor = new Color(r, g, b); } void diffuseMaterial(int r, int g, int b) { diffuseMaterial = new Color(r, g, b); } void ambientCoeff(double coeff) { ambientCoeff = coeff; } void diffuseCoeff(double coeff) { diffuseCoeff = coeff; } //function computeLightedColor //Given the current light settings and normal, //computes the Phong difuse lighting equation and returns the color public Color computeLightedColor() { double rgb[] = new double[3]; for(int i=0; i<3; i++) { rgb[i] = ambientCoeff*getRGB(ambientColor,i)*getRGB(ambientMaterial,i) + diffuseCoeff*getRGB(diffuseColor,i)*getRGB(diffuseMaterial,i) * max(0,currentNormal.dot(lightPos)); } return new Color(d2i(rgb[0]), d2i(rgb[1]), d2i(rgb[2])); } //function isectLineSphere //Determines the intersection of a line (not segment) from p1->p2 // with a sphere centered at p3 w/ radius r. //Returns the closest intersection to the start of the ray Vector3 isectLineSphere(Vector3 p1, Vector3 p2, Vector3 p3, double r) { //Extract fields to make them easier to deal with double x1, x2, x3; double y1, y2, y3; double z1, z2, z3; x1 = p1.x(); x2 = p2.x(); x3 = p3.x(); y1 = p1.y(); y2 = p2.y(); y3 = p3.y(); z1 = p1.z(); z2 = p2.z(); z3 = p3.z(); //Substitue the equations of the line "p1 + u*(p2-p1)" //Into the equation of the circle "(x-p3.x)^2 + (y-p3.y)^2 + (z-p3.z)^2 = r" //And we get a quadratic form a*u^2 + b*u + c, where a,b,c are the following double a, b, c; a = sq(x2-x1) + sq(y2-y1) + sq(z2-z1); b = 2*( (x2-x1)*(x1-x3) + (y2-y1)*(y1-y3) + (z2-z1)*(z1-z3) ); c = sq(x3) + sq(y3) + sq(z3) + sq(x1) + sq(y1) + sq(z1) - 2*(x3*x1 + y3*y1 + z3*z1) - sq(r); //We can solve with the quadratic equation (there may be 2 real solutions) //The discriminant (part under the radical in the quad.eq.) will tell us how many. double discrim; discrim = sq(b) - 4*a*c; double sqrt_discrim = Math.sqrt(discrim); //If the discriminant is less than zero, there was no collision if( discrim < 0 ) return null; //If the discriminant is zero, there is one soln. (w/ multiplicity 2) else if( discrim == 0 ) { double u = -b/(2*a); return p1.add( (p2.sub(p1)).mul(u) ); } //Otherwise, there are two solutions, pick the one w/ parameter closest to p1 else { double u1 = ( -b - sqrt_discrim) / (2*a); double u2 = ( -b + sqrt_discrim) / (2*a); //since sqrt_disc is > 0, I think this will always be u1? double u = min(u1, u2); return p1.add( (p2.sub(p1)).mul(u) ); } } //function counterClockwise //Determines if the given polygon (in screen space) has counterclockwise winding boolean counterClockwise(int poly[][]) { //We grab three points, and use the relation of the third to the // line formed by the first two to determine winding //Specifically, is the determinant of the matrix consisting of the // second and third vector translated to originate from the first // positive or negative //More specifically, is (p1_x-p0_x) * (p2_y-p0_y) > (p2_x-p0_x) * (p1_y-p0_y) // if so, it is ccw /* int x0 = poly.xpoints[0]; int y0 = poly.ypoints[0]; int x1 = poly.xpoints[1]; int y1 = poly.ypoints[1]; int x2 = poly.xpoints[2]; int y2 = poly.ypoints[2]; */ int x0 = (int)poly[0][0]; int y0 = (int)poly[0][1]; int x1 = (int)poly[1][0]; int y1 = (int)poly[1][1]; int x2 = (int)poly[2][0]; int y2 = (int)poly[2][1]; //This way seems to work, though it is the opposite of what we had before return (x1-x0)*(y2-y0) < (x2-x0)*(y1-y0); } int rgb[] = new int[]{255,255,255}; void drawTriangle( boolean filled, boolean textured, int x1, int y1, int z1, int r1, int g1, int b1, int s1, int t1, int x2, int y2, int z2, int r2, int g2, int b2, int s2, int t2, int x3, int y3, int z3, int r3, int g3, int b3, int s3, int t3) { int pix[] = this.pix; int zbuffer[] = this.zbuffer; int xlow, xmid, xhigh; int ylow, ymid, yhigh; int zlow, zmid, zhigh; int rlow, rmid, rhigh; int glow, gmid, ghigh; int blow, bmid, bhigh; int slow, smid, shigh; int tlow, tmid, thigh; if( x1 < 0 && x2 < 0 && x3 < 0 ) return; if( y1 < 0 && y2 < 0 && y3 < 0 ) return; if( x1 < x2 ) { if( x1 < x3 ) { if( x2 < x3 ) //1,2, 3 { xlow = x1; ylow = y1; zlow = z1; rlow = r1; glow = g1; blow = b1; slow = s1; tlow = t1; xmid = x2; ymid = y2; zmid = z2; rmid = r2; gmid = g2; bmid = b2; smid = s2; tmid = t2; xhigh = x3; yhigh = y3; zhigh = z3; rhigh = r3; ghigh = g3; bhigh = b3; shigh = s3; thigh = t3; } else //1, 3, 2 { xlow = x1; ylow = y1; zlow = z1; rlow = r1; glow = g1; blow = b1; slow = s1; tlow = t1; xmid = x3; ymid = y3; zmid = z3; rmid = r3; gmid = g3; bmid = b3; smid = s3; tmid = t3; xhigh = x2; yhigh = y2; zhigh = z2; rhigh = r2; ghigh = g2; bhigh = b2; shigh = s2; thigh = t2; } } else //order is 3, 1, 2 { xlow = x3; ylow = y3; zlow = z3; rlow = r3; glow = g3; blow = b3; slow = s3; tlow = t3; xmid = x1; ymid = y1; zmid = z1; rmid = r1; gmid = g1; bmid = b1; smid = s1; tmid = t1; xhigh = x2; yhigh = y2; zhigh = z2; rhigh = r2; ghigh = g2; bhigh = b2; shigh = s2; thigh = t2; } } else // y2 < y1 { if( x1 < x3 ) //order is 2, 1, 3 { xlow = x2; ylow = y2; zlow = z2; rlow = r2; glow = g2; blow = b2; slow = s2; tlow = t2; xmid = x1; ymid = y1; zmid = z1; rmid = r1; gmid = g1; bmid = b1; smid = s1; tmid = t1; xhigh = x3; yhigh = y3; zhigh = z3; rhigh = r3; ghigh = g3; bhigh = b3; shigh = s3; thigh = t3; } else { if( x2 < x3 ) //2, 3, 1 { xlow = x2; ylow = y2; zlow = z2; rlow = r2; glow = g2; blow = b2; slow = s2; tlow = t2; xmid = x3; ymid = y3; zmid = z3; rmid = r3; gmid = g3; bmid = b3; smid = s3; tmid = t3; xhigh = x1; yhigh = y1; zhigh = z1; rhigh = r1; ghigh = g1; bhigh = b1; shigh = s1; thigh = t1; } else //3, 2, 1 { xlow = x3; ylow = y3; zlow = z3; rlow = r3; glow = g3; blow = b3; slow = s3; tlow = t3; xmid = x2; ymid = y2; zmid = z2; rmid = r2; gmid = g2; bmid = b2; smid = s2; tmid = t2; xhigh = x1; yhigh = y1; zhigh = z1; rhigh = r1; ghigh = g1; bhigh = b1; shigh = s1; thigh = t1; } } } //====================================================================== // We have sorted, now draw the first triangle //====================================================================== double runAB = xmid - xlow; double runBC = xhigh - xmid; double runAC = xhigh - xlow; //Compute the slope of the upper line segment //Also compute the slopes of r,g,b on this segment //The key is that we want all dX1 to be on the upper line, // and dX2 on the lower line double dy1, dz1, dr1, dg1, db1, ds1, dt1; double dy2, dz2, dr2, dg2, db2, ds2, dt2; dy1 = (ymid - ylow) / runAB; dy2 = (yhigh - ylow) / runAC; //If the above holds, then it will be the dX1 that will hit the other vtx, // and that will have to change int changes = 1; if( dy1 > dy2 ) { //if dy1 > dy2, all is good, assign the others //This means that the line from the low to mid is above line from low to high //dy1 = (ymid - ylow) / runAB; dz1 = (zmid - zlow) / runAB; dr1 = (rmid - rlow) / runAB; dg1 = (gmid - glow) / runAB; db1 = (bmid - blow) / runAB; ds1 = (smid - slow) / runAB; dt1 = (tmid - tlow) / runAB; //then compute the slopes for the lower segment //dy2 = (yhigh - ylow) / runAC; dz2 = (zhigh - zlow) / runAC; dr2 = (rhigh - rlow) / runAC; dg2 = (ghigh - glow) / runAC; db2 = (bhigh - blow) / runAC; ds2 = (shigh - slow) / runAC; dt2 = (thigh - tlow) / runAC; } else { //If dy1 is not > dy2, this is bad. Swap and then assign stuff dy2 = (ymid - ylow) / runAB; dz2 = (zmid - zlow) / runAB; dr2 = (rmid - rlow) / runAB; dg2 = (gmid - glow) / runAB; db2 = (bmid - blow) / runAB; ds2 = (smid - slow) / runAB; dt2 = (tmid - tlow) / runAB; dy1 = (yhigh - ylow) / runAC; dz1 = (zhigh - zlow) / runAC; dr1 = (rhigh - rlow) / runAC; dg1 = (ghigh - glow) / runAC; db1 = (bhigh - blow) / runAC; ds1 = (shigh - slow) / runAC; dt1 = (thigh - tlow) / runAC; //Now, the other path will hit the new vtx changes = 2; } //Draw the left triangle double yUp=ylow, yDown=ylow; double zUp=zlow, zDown=zlow; double rUp=rlow, rDown=rlow; double gUp=glow, gDown=glow; double bUp=blow, bDown=blow; double sUp=slow, sDown=slow; double tUp=tlow, tDown=tlow; /* drawHalfTriangle(filled, xlow, xmid, yUp, yDown, dy1, dy2, zUp, zDown, dz1, dz2, rUp, rDown, dr1, dr2, gUp, gDown, dg1, dg2, bUp, bDown, db1, db2 ); */ int texHeight=-1, texWidth=-1, colors[]=null; if( texturing ) { texHeight = currentTexture.height; texWidth = currentTexture.width; colors = currentTexture.colors; } //try //{ for(int i=(int)xlow; i<(int)xmid; i++) { double ddz, ddr, ddg, ddb, dds, ddt; if( yUp - yDown != 0 ) { ddz = (zUp - zDown) / (yUp - yDown); ddr = (rUp - rDown) / (yUp - yDown); ddg = (gUp - gDown) / (yUp - yDown); ddb = (bUp - bDown) / (yUp - yDown); dds = (sUp - sDown) / (yUp - yDown); ddt = (tUp - tDown) / (yUp - yDown); double zNow=zDown, rNow=rDown, gNow=gDown, bNow=bDown; double sNow=sDown, tNow=tDown; if( filled ) { for(int j=(int)yDown; j<(int)yUp+1; j++) { //Draw the stuff here //try //{ int idx = xy2i(i,j); int texIdx = (int)tNow*texWidth + (int)sNow; if( texIdx < 0 ) texIdx = 0; if( texIdx >= texWidth*texHeight ) texIdx = 0; //int zInt = (int)Math.round(zNow); int zInt = (int)(zNow); if( zbuffer[idx] > zInt ) { //pix[idx] = pack((int)rNow, (int)gNow, (int)bNow); //pix[idx] = (colors[texIdx]*rNow)>>1; //unpack(rgb, colors[texIdx]); if( texturing ) { unpack(rgb, colors[texIdx]); pix[idx] = pack((int)(rNow * rgb[0]/255.0), (int)(gNow * rgb[1]/255.0), (int)(bNow * rgb[2]/255.0)); } else { pix[idx] = pack((int)(rNow), (int)(gNow), (int)(bNow)); } zbuffer[idx] = zInt; } //} //catch( ArrayIndexOutOfBoundsException e ) { return; } //End draw zNow += ddz; rNow += ddr; gNow += ddg; bNow += ddb; sNow += dds; tNow += ddt; } } else //wire framed { //pix[xy2i(i,(int)yUp)] = pack((int)rUp, (int)gUp, (int)bUp); //pix[xy2i(i,(int)yDown)] = pack((int)rDown, (int)gDown, (int)bDown); //int zInt = (int)(zNow); int idx; idx = xy2i(i,(int)yUp); if( zbuffer[idx] > (int)zUp ) pix[idx] = pack((int)rUp, (int)gUp, (int)bUp); idx = xy2i(i,(int)yDown); if( zbuffer[idx] > (int)zDown ) pix[idx] = pack((int)rDown, (int)gDown, (int)bDown); } } yUp += dy1; yDown += dy2; zUp += dz1; zDown += dz2; rUp += dr1; rDown += dr2; gUp += dg1; gDown += dg2; bUp += db1; bDown += db2; sUp += ds1; sDown += ds2; tUp += dt1; tDown += dt2; } if( xlow == xmid ) { if( ylow > ymid ) { yUp = ylow; rUp = rlow; gUp = glow; bUp = blow; sUp = slow; tUp = tlow; yDown = ymid; rDown = rmid; gDown = gmid; bDown = bmid; sDown = smid; tDown = tmid; } else { yDown = ylow; rDown = rlow; gDown = glow; bDown = blow; sDown = slow; tDown = tlow; yUp = ymid; rUp = rmid; gUp = gmid; bUp = bmid; sUp = smid; tUp = tmid; } } //======================================================= // We have now drawn the first triangle, draw second //======================================================= //If the runBC is zero, we dont have a second triangle to draw //We know that the line from (yUp->end) is above line (yDown->end) //This eliminates some of the extra tests //For one of the two lines, we just continue as before //For the other, we have the new vertex to deal with if( changes == 1 ) { dy1 = (yhigh - ymid) / runBC; dz1 = (zhigh - zmid) / runBC; dr1 = (rhigh - rmid) / runBC; dg1 = (ghigh - gmid) / runBC; db1 = (bhigh - bmid) / runBC; ds1 = (shigh - smid) / runBC; dt1 = (thigh - tmid) / runBC; } else { //then compute the slopes for the lower segment dy2 = (yhigh - ymid) / runBC; dz2 = (zhigh - zmid) / runBC; dr2 = (rhigh - rmid) / runBC; dg2 = (ghigh - gmid) / runBC; db2 = (bhigh - bmid) / runBC; ds2 = (shigh - smid) / runBC; dt2 = (thigh - tmid) / runBC; } //Draw the right triangle //yUp, yDown, etc. stay as they were - we have changed the slope /* drawHalfTriangle(filled, xmid, xhigh, yUp, yDown, dy1, dy2, zUp, zDown, dz1, dz2, rUp, rDown, dr1, dr2, gUp, gDown, dg1, dg2, bUp, bDown, db1, db2 ); */ for(int i=(int)xmid; i<(int)xhigh+1; i++) { double ddz, ddr, ddg, ddb, dds, ddt; if( yUp - yDown != 0 ) { ddz = (zUp - zDown) / (yUp - yDown); ddr = (rUp - rDown) / (yUp - yDown); ddg = (gUp - gDown) / (yUp - yDown); ddb = (bUp - bDown) / (yUp - yDown); dds = (sUp - sDown) / (yUp - yDown); ddt = (tUp - tDown) / (yUp - yDown); double zNow=zDown, rNow=rDown, gNow=gDown, bNow=bDown, sNow=sDown, tNow=tDown; if( filled ) { for(int j=(int)yDown; j<(int)yUp+1; j++) { //Draw the stuff here //try //{ int idx = xy2i(i,j); int zInt = (int)Math.round(zNow); int texIdx = (int)tNow*texWidth + (int)sNow; if( texIdx < 0 ) texIdx = 0; if( texIdx >= texWidth*texHeight ) texIdx = 0; if( zbuffer[idx] > zInt ) { //pix[idx] = pack((int)rNow, (int)gNow, (int)bNow); //pix[idx] = colors[texIdx]; if( texturing ) { unpack(rgb, colors[texIdx]); pix[idx] = pack((int)(rNow * rgb[0]/255.0), (int)(gNow * rgb[1]/255.0), (int)(bNow * rgb[2]/255.0)); } else { pix[idx] = pack((int)(rNow), (int)(gNow), (int)(bNow)); } zbuffer[idx] = zInt; } //} //catch( ArrayIndexOutOfBoundsException e ) { return; } //End draw zNow += ddz; rNow += ddr; gNow += ddg; bNow += ddb; sNow += dds; tNow += ddt; } } else //wire framed { //try //{ //pix[xy2i(i,(int)yUp)] = pack((int)rUp, (int)gUp, (int)bUp); //pix[xy2i(i,(int)yDown)] = pack((int)rDown, (int)gDown, (int)bDown); //} //catch( ArrayIndexOutOfBoundsException e ) { return; } int idx; idx = xy2i(i,(int)yUp); if( zbuffer[idx] > (int)zUp ) pix[idx] = pack((int)rUp, (int)gUp, (int)bUp); idx = xy2i(i,(int)yDown); if( zbuffer[idx] > (int)zDown ) pix[idx] = pack((int)rDown, (int)gDown, (int)bDown); } } yUp += dy1; yDown += dy2; zUp += dz1; zDown += dz2; rUp += dr1; rDown += dr2; gUp += dg1; gDown += dg2; bUp += db1; bDown += db2; sUp += ds1; sDown += ds2; tUp += dt1; tDown += dt2; } //} //catch( ArrayIndexOutOfBoundsException e ) //{ // return; //}; } void clear() { int[] pix = this.pix; int value = pack(255,255,255); int zvalue = Integer.MAX_VALUE; for(int i=0; i zNow ) //{ pix[idx] = pack((int)rNow, (int)gNow, (int)bNow); //zbuffer[idx] = zNow; //} } catch( ArrayIndexOutOfBoundsException e ) { return; } //End draw zNow += ddz; rNow += ddr; gNow += ddg; bNow += ddb; } } else //wire framed { try { pix[xy2i(i,(int)yUp)] = pack((int)rUp, (int)gUp, (int)bUp); pix[xy2i(i,(int)yDown)] = pack((int)rDown, (int)gDown, (int)bDown); } catch( ArrayIndexOutOfBoundsException e ) { return; } } } yUp += dy1; yDown += dy2; zUp += dz1; zDown += dz2; rUp += dr1; rDown += dr2; gUp += dg1; gDown += dg2; bUp += db1; bDown += db2; } } void drawTriangle(int x1, int y1, int x2, int y2, int x3, int y3) { drawTriangle(true, false, x1, y1, 0, 255, 0, 0, 0, 0, x2, y2, 0, 0, 255, 0, 0, 0, x3, y3, 0, 0, 0, 255, 0, 0 ); } public void render() { clear(); drawTriangle(10, 10, 60, 10, 30, 30); //No side aligned drawTriangle(30, 60, 10, 100, 50, 50); drawTriangle(300, 300, 500, 400, 560, 50); //Left side y aligned drawTriangle(10, 100, 10, 150, 50, 120); } //Square final double sq(double x) { return x*x; } //Maximum final double max(double a, double b) { return (a > b ? a : b); } //Minimum final double min(double a, double b) { return (a < b ? a : b); } //double color value [0.0,1.0] --> integer color value [0,255] final int d2i(double c) { return (int)max(0.0, min(255, 255*c)); } //extracts an rgb color component, in double [0.0, 1.0] final double getRGB(Color color, int i) { switch(i) { case 0: return color.getRed()/255.0; case 1: return color.getGreen()/255.0; case 2: return color.getBlue()/255.0; default: throw new RuntimeException("Passed a valid color component"); } } //Current transform matrix, which takes world coord to screen coord private Matrix4x4 transformMatrix = new Matrix4x4(); //Current transform-matrix stack, the previous var points to the head of this private Stack transformStack = new Stack(); //Current Render state; which primitive, if any, is being rendered private int renderState = RENDER_NONE; //Storage for any polygon-based primitive being submitted Polygon polygon = new Polygon(); //Storage for the last coordinate for a line-based primitive private int lineX= 0; private int lineY= 0; //Counts the number of vertices sent for the current primitive private int sentVertices = 0; //Current rendering context private Graphics graphics = null; //Do we cull faces? boolean cullPolygon=true; //Do we have lighting? boolean lighting=false; //Do we have texturing? boolean texturing=false; //The currently assigned texture Bitmap currentTexture=null; //Size for rendering points int thePointSize=1; //Currently set normal (we only have face normals for now) Vector3 currentNormal=new Vector3(0,1,0); //Information used for lighting (we have a diffuse lighting model) Vector3 lightPos=new Vector3(0,1,0); Color ambientColor=new Color(0,64,127); Color diffuseColor=new Color(0,64,127); Color ambientMaterial=new Color(255,255,255); Color diffuseMaterial=new Color(255,255,255); double ambientCoeff=0.2; double diffuseCoeff=0.6; }