View Javadoc

1   package com.insanityengine.ghia.renderer;
2   
3   import com.insanityengine.ghia.m3.*;
4   import com.insanityengine.ghia.libograf.*;
5   import com.insanityengine.ghia.pixels.*;
6   
7   /*** 
8    * 
9    * <P>
10   * This is a straight up port of Michael Abrash's DDA from chapter 56
11   * of the _Graphics Programming Black Book (Special Edition)_
12   * </P>
13   * 
14   * $Header: /usr/local/cvsroot/ghia/src/java/com/insanityengine/ghia/renderer/Abrashed.java,v 1.3 2005/03/25 02:59:21 brian Exp $
15   * 
16   * @author BrianHammond
17   * 
18   */
19  public class Abrashed extends BufferBase implements RendererInterface {
20  
21  
22  	/*** 
23  	 * 
24  	 * render
25  	 * 
26  	 * @param context
27  	 * 
28  	 */
29  	public void render( LiboGraf context ) {
30  		invokeDrawers( context );
31  		copyTo( context.getGraphics() );
32  	}
33  
34  	//////////////////////////////////////////////////
35  	// DrawerRegistryInterface
36  	
37  	/***
38  	 *
39  	 * Some renderers are big babys about when it is ok to mess with their goodies.
40  	 * Hence this cheesy add/remove Drawer mess
41  	 *
42  	 * @param drawer to call per frame
43  	 *
44  	 */
45  	public void addDrawer( DrawingInterface drawer ) {
46  		registry.addDrawer( drawer );
47  	}
48  
49  	/***
50  	 *
51  	 * Some renderers are big babys about when it is ok to mess with their goodies.
52  	 * Hence this cheesy add/remove Drawer mess
53  	 *
54  	 * @param drawer to call per frame
55  	 *
56  	 */
57  	public void removeDrawer( DrawingInterface drawer ) {
58  		registry.removeDrawer( drawer );
59  	}
60  
61  	/***
62  	 *
63  	 * Invoke any registered DrawingInterfaces
64  	 *
65  	 */
66  	public void invokeDrawers( LiboGraf context ) {
67  		registry.invokeDrawers( context );
68  	}
69  
70  	/*** 
71  	 * 
72  	 * drawPolygon
73  	 * 
74  	 * @param number
75  	 * @param ptz
76  	 * 
77  	 */
78  	public void drawPolygon( int number, Pt3 ptz[] ) {
79  
80  		if ( normal.z < 0 ) return;
81  		frustrum.toScreen( number, ptz );
82  		if ( frustrum.offScreen( number, ptz ) ) return;
83  
84  		float MinY, MaxY;
85  		
86  		int i;
87  		int MinVert = 0;
88  		
89  		NumVerts = number;
90  		VertexPtr = ptz;
91  		
92  		/* Nothing to draw if less than 3 vertices */
93  		if ( number < 3 ) {
94  			return;
95  		}
96  		
97  		/* 
98  			Scan through the destination polygon vertices and find the top of the
99  			left and right edges, taking advantage of our knowledge that vertices
100 			run in a clockwise direction (else this polygon wouldn't be visible
101 			due to backface removal) 
102 		*/
103 		MinVert = MaxVert = 0;
104 		MaxY = MinY = VertexPtr[ 0 ].getY();
105 		for ( i = 1 ; i < NumVerts ; i++ ) {
106 			if ( VertexPtr[ i ].getY() < MinY ) {
107 				MinY = VertexPtr[ i ].getY();
108 				MinVert = i;
109 			}
110 			if ( VertexPtr[ i ].getY() > MaxY ) {
111 				MaxY = VertexPtr[ i ].getY();
112 				MaxVert = i;
113 			}
114 		}
115 		
116 		/* Reject flat (0-pixel-high) polygons */
117 		if ( MinY >= MaxY ) {
118 			return;
119 		}
120 		
121 		/* 
122 			The destination Y coordinate is not edge specific; it applies to
123 			both edges, since we always step Y by 1 
124 		*/
125 		DestY = MinY;
126 		
127 		/* 
128 			Set up to scan the initial left and right edges of the source and
129 			destination polygons. We always step the destination polygon edges
130 			by one in Y, so calculate the corresponding destination X step for
131 			each edge, and then the corresponding source image X and Y steps 
132 		*/
133 
134 		// set up left edge first
135 		LeftEdge.Direction = -1;
136 		LeftEdge.setUpEdge( MinVert );
137 		
138 		// set up right edge
139 		RightEdge.Direction = 1;
140 		RightEdge.setUpEdge( MinVert );
141 		
142 		/* 
143 			Step down destination edges one scan line at a time. At each scan
144 			line, find the corresponding edge points in the source image. Scan
145 			between the edge points in the source, drawing the corresponding
146 			pixels across the current scan line in the destination polygon. (We
147 			know which way the left and right edges run through the vertex list
148 			because visible (non-backface-culled) polygons always have the
149 			vertices in clockwise order as seen from the viewpoint) 
150 		*/
151 		for ( DestY = MinY; DestY < h ; DestY++ ) {
152 			
153 			// Draw only if inside Y bounds of clip rectangle 
154 			if ( DestY >= 0 ) {
155 				ScanOutLine( LeftEdge, RightEdge );
156 			}
157 			
158 			/* 
159 				Advance the source and destination polygon edges, ending if we've
160 			   	scanned all the way to the bottom of the polygon 
161 			*/
162 			if ( !stepEdge( LeftEdge ) || !stepEdge( RightEdge ) ) break;
163 		}
164 	}
165 
166 	////////////////////////////////////////////////////////////////////////////////
167 
168 	private class EdgeScan {
169 	   	/* 
170 			through edge list:
171 			+1 for a right edge (forward through vertex list)
172 			-1 for a left  edge (backwardthrough vertex list)
173 		*/
174 		int Direction;
175 		
176 		float RemainingScans;	// height left to scan out in dest
177 		int CurrentEnd;			// vertex # of end of current edge
178 		
179 		float SourceX;			// current X location in source for this edge
180 		float SourceY;			// current Y location in source for this edge
181 		float SourceZ;			// current Z location in source for this edge
182 		float SourceStepX;		// X step in source for Y step in dest of 1
183 		float SourceStepY;		// Y step in source for Y step in dest of 1
184 		float SourceStepZ;		// Z step in source for Y step in dest of 1
185 		
186 		/* 
187 		   variables used for all-integer Bresenham's-type X stepping through the dest, 
188 		   needed for precise pixel placement to avoid gaps 
189 		*/
190 		int DestX;				// current X location in dest for this edge
191 		int DestXIntStep;		// whole part of dest X step per scan-line Y step
192 		int DestXDirection;		// -1 or 1 to indicate way X steps (left/right)
193 		int DestXErrTerm;		// current error term for dest X stepping
194 		int DestXAdjUp;			// amount to add to error term per scan line move
195 		int DestXAdjDown;		// amount to subtract from error term when the error term turns over
196 
197 		/*** 
198 		 * 
199 		 * Sets up an edge to be scanned; the edge starts at StartVert and proceeds
200 		 * in direction Edge.Direction through the vertex list. Edge.Direction
201 		 * must be set prior to call; -1 to scan a left edge (backward through
202 		 * the vertex list), 1 to scan a right edge (forward through the
203 		 * vertex list). Automatically skips over 0-height edges. Returns
204 		 * 1 for success, or 0 if there are no more edges to scan. 
205 		 * 
206 		 * @param StartVert
207 		 * 
208 		 * @return a boolean
209 		 * 
210 		 */
211 		boolean setUpEdge( int StartVert ) {
212 			
213 			int NextVert;
214 			
215 			for ( ; StartVert != MaxVert  ; ) {
216 				NextVert = StartVert + Direction;
217 				if ( NextVert >= NumVerts ) {
218 					NextVert = 0;
219 				} else 
220 				if ( NextVert < 0 ) {
221 					NextVert = NumVerts - 1;
222 				}
223 				
224 				/* 
225 				  	Calculate the variables for this edge and done if this is not a
226 				   	zero-height edge 
227 				*/
228 				RemainingScans = VertexPtr[ NextVert ].getY() - VertexPtr[ StartVert ].getY();
229 				if ( RemainingScans != 0 ) {
230 					return calcStepz( ( int ) RemainingScans, StartVert, NextVert );
231 				}
232 				StartVert = NextVert;   /* keep looking for a non-0-height edge */
233 			}
234 			
235 			return false;
236 		}
237 
238 		/*** 
239 		 * 
240 		 * calcStepz
241 		 * 
242 		 * @param RemainingScans
243 		 * @param StartVert
244 		 * @param NextVert
245 		 * 
246 		 * @return true
247 		 * 
248 		 */
249 		private final boolean calcStepz( int RemainingScans, int StartVert, int NextVert ) {
250 			if ( 0 == RemainingScans ) RemainingScans = 1;
251 			
252 			float DestYHeight = RemainingScans;
253 			CurrentEnd = NextVert;
254 			
255 			SourceX = VertexPtr[ StartVert ].getS();
256 			SourceY = VertexPtr[ StartVert ].getT();
257 			SourceZ = VertexPtr[ StartVert ].getZ();
258 			
259 			SourceStepX = ( VertexPtr[ NextVert ].getS() - SourceX );
260 			SourceStepY = ( VertexPtr[ NextVert ].getT() - SourceY );
261 			SourceStepZ = ( VertexPtr[ NextVert ].getZ() - SourceZ );
262 
263 			SourceStepX /= DestYHeight;
264 			SourceStepY /= DestYHeight;
265 			SourceStepZ /= DestYHeight;
266 
267 			/* Set up Bresenham-style variables for dest X stepping */
268 			DestX = ( int ) VertexPtr[ StartVert ].getX();
269 			int DestXWidth = ( int ) ( VertexPtr[ NextVert ].getX() - VertexPtr[ StartVert ].getX() );
270 			if ( DestXWidth < 0 ) {
271 				/* Set up for drawing right to left */
272 				DestXDirection = -1;
273 				DestXWidth = -DestXWidth;
274 				DestXErrTerm = 1 - RemainingScans;
275 				DestXIntStep = ( int ) -( DestXWidth / RemainingScans );
276 			} else {
277 				/* Set up for drawing left to right */
278 				DestXDirection = 1;
279 				DestXErrTerm = 0;
280 				DestXIntStep = ( int ) +( DestXWidth / RemainingScans );
281 			}
282 			DestXAdjUp = DestXWidth % RemainingScans;
283 			DestXAdjDown = RemainingScans;
284 
285 			return true;
286 		}
287 			
288 		
289 	};
290 
291 	/*** 
292 	 * 
293 	 * ScanOutLine:  Texture-map-draw the scan line between two edges. 
294 	 * 
295 	 * @param LeftEdge
296 	 * @param RightEdge
297 	 * 
298 	 */
299 	private void ScanOutLine( EdgeScan LeftEdge, EdgeScan RightEdge ) {
300 		float SourceX = LeftEdge.SourceX;
301 		float SourceY = LeftEdge.SourceY;
302 		float SourceZ = LeftEdge.SourceZ;
303 		
304 		float DestX = LeftEdge.DestX;
305 		float DestXMax = RightEdge.DestX;
306 		
307 		float DestWidth;
308 		float SourceXStep, SourceYStep, SourceZStep;
309 		
310 		/* Nothing to do if fully X clipped */
311 		if ( ( DestXMax <= 0 ) || ( DestX  >= w ) ) {
312 			return;
313 		}
314 		if ( ( DestXMax - DestX ) <= 0 ) {
315 			return;  /* nothing to draw */
316 		}
317 		
318 		/* 
319 		   Width of destination scan line, for scaling. Note:
320 		   because this is an integer-based scaling, it can have
321 		   a total error of as much as nearly one pixel. For more
322 		   precise scaling, also maintain a fixed-point DestX in
323 		   each edge, and use it for scaling. If this is done,
324 		   it will also be necessary to nudge the source start
325 		   coordinates to the right by an amount corresponding
326 		   to the distance from the the real (fixed-point) DestX
327 		   and the first pixel (at an integer X) to be drawn)
328 		 */
329 		DestWidth = DestXMax - DestX;
330 		
331 		// Calculate source steps that correspond to each dest X step (across the scan line)
332 		SourceXStep = ( RightEdge.SourceX - SourceX ) / DestWidth; 
333 		SourceYStep = ( RightEdge.SourceY - SourceY ) / DestWidth;
334 		SourceZStep = ( RightEdge.SourceZ - SourceZ ) / DestWidth;
335 	   
336 		// Clip right edge if necessary 
337 		if ( DestXMax >= w ) {
338 			DestXMax = w - 1;
339 		} 
340 		
341 		// Clip left edge if necssary
342 		if ( DestX  < 0 ) {
343 			SourceX += SourceXStep * ( 0 - DestX );
344 			SourceY += SourceYStep * ( 0 - DestX );
345 			SourceZ += SourceZStep * ( 0 - DestX );
346 			DestX = 0;
347 		} 
348 
349 		int pix = 0x00FF0000;
350 		int idx = ( int ) DestX + ( ( ( int ) DestY ) * w );
351 
352 		/* 
353 		   Scan across the destination scan line, updating the
354 		   source image position accordingly
355 		 */
356 		for (; DestX < DestXMax; DestX++, idx++ ) {
357 			/* 
358 				Get currently mapped pixel out of image and
359 				draw it to screen 
360 			 */
361 			if ( zbuffer.set( idx, SourceZ ) ) {
362 		  		if ( null != skin ) {
363 			  		pix = ( int ) skin.getPixelAt( ( int ) SourceX, ( int ) SourceY );
364 				}
365 				pixels[ idx ] = pix;
366 			}
367 							
368 			// Point to the next source pixel
369 			SourceX += SourceXStep; 
370 			SourceY += SourceYStep;
371 			SourceZ += SourceZStep;
372 		}
373 	}
374 			
375 
376 	/* 
377 	   Steps an edge one scan line in the destination, and the
378 	   corresponding distance in the source. If an edge runs out,
379 	   starts a new edge if there is one. Returns 1 for success,
380 	   or 0 if there are no more edges to scan.
381 	 */
382 	private boolean stepEdge( EdgeScan Edge ) { 
383 		/* 
384 		   
385 			Count off the scan line we stepped last time; if this
386 			edge is finished, try to start another one
387 
388 		*/ 
389 		Edge.RemainingScans--;
390 		if ( Edge.RemainingScans == 0 ) { 
391 			/* Set up the next edge; done if there is no next edge */ 
392 			if ( !Edge.setUpEdge( Edge.CurrentEnd ) ) { 
393 				return false;	/* no more edges; done drawing polygon */ 
394 			} 
395 			return true;     /* all set to draw the new edge */
396 		} 
397 	
398 		/* Step the current source edge */ 
399 		Edge.SourceX += Edge.SourceStepX; 
400 		Edge.SourceY += Edge.SourceStepY; 
401 		
402 		/* 
403 		   Step dest X with Bresenham-style variables, to get
404 		   precise dest pixel placement and avoid gaps
405 		*/
406 		Edge.DestX += Edge.DestXIntStep;
407 		
408 		/* whole pixel step */ 
409 		/* Do error term stuff for fractional pixel X step handling */ 
410 		Edge.DestXErrTerm += Edge.DestXAdjUp;
411 		if ( Edge.DestXErrTerm > 0 ) {
412 			 Edge.DestX += Edge.DestXDirection;
413 			 Edge.DestXErrTerm -= Edge.DestXAdjDown; 
414 		} 
415 		
416 		return true;
417 	}
418 
419 	private int MaxVert, NumVerts;
420 	private float DestY;
421 	private Pt3 VertexPtr[];
422 	private int TexMapWidth;
423 
424 	private DrawerRegistry registry = new DrawerRegistry();
425 
426 	private EdgeScan LeftEdge = new EdgeScan();
427 	private EdgeScan RightEdge  = new EdgeScan();
428 	
429 };
430 
431 /***
432  *
433  * $Log: Abrashed.java,v $
434  * Revision 1.3  2005/03/25 02:59:21  brian
435  * still doesn;t work...
436  *
437  * Revision 1.2  2005/03/25 01:44:06  brian
438  * added ScanOutLine
439  *
440  * Revision 1.1  2005/03/24 22:48:32  brian
441  * dda ala abrash
442  *
443  *
444  */