Projects

PHP Data Pivot
PHP Data Subtotals
HTML5 Graph
Java NW3D2
JS Code Formatter
HTML5 Clock
Silverlight Gauge
Java NW3D
Java Fireworks
Java Early 3D
Java Snow
Java Dogfight
Java Water Simulation
Java Bump Mapping
Java Elite Ships

NW3D - Wireframe

Better late than never, I've added a wireframe mode to aid with engine and scene debuggin. The demo below will run at maximum FPS (frames per second) so that you can see the difference in performance between shaded and wireframe modes. Use the cursor keys to move, 'a' to jump and 'w' to toggle wireframe. The FPS readout will reset each time the mode is changed.

No Java Support.

Within the 'scene' class I've added a boolean named 'wireframeMode' and a public method 'toggleWireframe()'. other scene objects but still within the far clipping distance.

  public void toggleWireframe() {
	if (wireframeMode) {
		wireframeMode = false;
	} else {
		wireframeMode = true;
	}
  }

During processing the scene renderer checks whether wireframe mode is enabled and if so, uses the 'Wireframe' method instead of the usual triangle rasteriser.

  public void Wireframe(nw3d_vertex a,
      nw3d_vertex b, nw3d_vertex c, nw3d_material col) {
	    
      v1.copy(a);
      v1.x=(int)(cam.dist*a.x/a.z+0.5);
      v1.y=(int)(cam.dist*a.y/a.z+0.5);
      v1.z=1/a.z;
      v2.copy(b);
      v2.x=(int)(cam.dist*b.x/b.z+0.5);
      v2.y=(int)(cam.dist*b.y/b.z+0.5);
      v2.z=1/b.z;
      v3.copy(c);
      v3.x=(int)(cam.dist*c.x/c.z+0.5);
      v3.y=(int)(cam.dist*c.y/c.z+0.5);
      v3.z=1/c.z;

      drawLine(v1, v2, col);
      drawLine(v1, v3, col);
      drawLine(v2, v3, col);
  }

As you can see, this method performs the 2D screen projection and then calls 'drawLine' three times to join the points of the triangle.

  public void drawLine(nw3d_vertex p1, nw3d_vertex p2, nw3d_material c) {
	int pixind,pf,pt;
	float tempz;
	double tempx,tempy,dz,dy,dx;
	int colRed,colGreen,colBlue,diffx,diffy;

	//Create wireframe colour
	colRed = (int)(c.r*0xff);
	colGreen = (int)(c.g*0xff);
	colBlue = (int)(c.b*0xff);
	int wfcol=0xff<<24|(colRed<0xff?colRed:0xff)<<16|
			(colGreen<0xff?colGreen:0xff)<<8|
			(colBlue<0xff?colBlue:0xff);

	tempz=(float)p1.z;
	diffx=(int)(Math.abs(p2.x-p1.x));
	diffy=(int)(Math.abs(p2.y-p1.y));

	//Decide on which way to move in pixel resolution based
	//on the slope of the line.

	//Move horizontally
	if (diffx >= diffy) {
	    if (diffx>0) {
		//But are we stepping left to right or right to left?
		if (p2.x < p1.x) {
	            pf = (int)p2.x;
		    pt = (int)p1.x;
		    tempy = p2.y;
		    dy=(p1.y-p2.y)/diffx;
		    dz=(p1.z-p2.z)/diffx;
		} else {
		    pf = (int)p1.x;
		    pt = (int)p2.x;
		    tempy = p1.y;
		    dy=(p2.y-p1.y)/diffx;
		    dz=(p2.z-p1.z)/diffx;
		}
  
		for (int i=pf; i<=pt; i++) {
		    pixind = (my-(int)tempy)*width+mx+i;
		    if (pixind > 0 && pixind < size) {
			if (tempz > zBuffer[pixind]) {
			    pBuffer[pixind]=wfcol;
			    zBuffer[pixind]=tempz;
			}
		    }
		    tempy+=dy;
		    tempz+=dz;
		}
	    }

	    //Move vertically
	} else {
	    if (diffy>0) {
		//But are we stepping top to bottom or bottom to top?
		if (p2.y < p1.y) {
		    pf = (int)p2.y;
		    pt = (int)p1.y;
		    tempx = p2.x;
		    dx=(p1.x-p2.x)/diffy;
		    dz=(p1.z-p2.z)/diffy;
		} else {
		    pf = (int)p1.y;
		    pt = (int)p2.y;
		    tempx = p1.x;
		    dx=(p2.x-p1.x)/diffy;
		    dz=(p2.z-p1.z)/diffy;
		}

		for (int i=pf; i<=pt; i++) {
		    pixind = (my-i)*width+mx+(int)tempx;
		    if (pixind > 0 && pixind < size) {
			if (tempz > zBuffer[pixind]) {
			    pBuffer[pixind]=wfcol;
			    zBuffer[pixind]=tempz;
			}
		    }
		    tempx+=dx;
		    tempz+=dz;
		}
	    }
	}    
  }

Based on the slope of the line we either pixel step horizontally or vertically. The opposing axis will use sub-pixel accuracy to ensure that we don't leave any gaps in the line. The z-buffer is still used so that foreground line priority is respected. There is no lighting/shading of the line colour, the basic diffuse colour is used for all pixels.

Back to NW3D index