This routine is used to give a drawing priority to the faces; our object could be rotated in any way, and it will always be displayed correctly.
Removing hidden surfaces is based upon very specific algos, not very easy to implement and rarely executed with the needed speed. Removing hidden faces is divided into two parts. First one, removes the hidden faces. They are so called because they cannot be seen from observer; to find them you have just to compute the degree formed from the normal to the sourface and by the vector of the view. If this degree is between 90 and -90, the face will be visible; if the result is greater than 90 or minor than -90, the face is hidden.
To compute the normal to the surface, you have just to compute Once deleted invisible faces, you'll have to draw visible ones in a correct way. For this task two algos exist: the z-buffering and the painter algo. Both of them cannot be implemented inside a demo in a standard way, following you'll see how to implement them.
Z-buffering algo is so composed:
- From a face's coords, you have to compute Z value for every X,Y and memorize it inside a buffer - Find the Z value closer to the observer, check this value with the one contained inside the Z buffer (with the same X and Y) and if it is closer to the observer, it will replace the current value. - Now for any X,Y coordinate we have a Z value (the one nearest to the observer) and the pixel color, you just have to draw it.
Painter algo is based upon a series of checks :
1 - Extensions on X axe of two faces overlap ?
If no-> end of checks, drawing priority does not matters.
If yes-> check 2.
2 - Extensions on Y axe of two faces overlap ?
If no-> end of controls.
If yes-> check 3.
3&4 - Extend a polygon widely and you'll have a surface : the other polygon
is completely on the other part ?
If yes -> end of checks.
If no-> check 5.
5 - Now we know faces are in the wrong position : we'll have to swap them.
These algos are very computing-demanding, and our beloved Amigas haven't a PowerPC 604/200Mhz, so I do not suggets to anyone to use one of the above algos, and to give a look to the one I do introduce.
The following is not an algo of mine but just a modification I made to make it as fast as possibkle.
We can divide the algo into two parts. The first one takes all points making a face, sums all Z values and divide the result for the number of points (Z factor).
The second part sorts faces so that much far to the observer are rendered first. So, if an objects obscures another one, we will not have any problem : we'll see our object before all the rest.
Imagine not to use this algo : objects would be rendered always with the same priority... in a wood of trees, the foremost tree must be appear before the rest (so it has to be drawn as last). With a fixed order, everything would work until we do not begin to rotate inside this wood : then priority has to be changed. This is a typical example of when to use a Z sort algo.
Warning : you do not need it always.
There are situations when you can get rid of the Z sort, saving a lot of CPU time : in these cases drawing priorities is irrilevant. We have said that you can avoid sorting with some objects : first of all they have to be CLOSED and then they have to be SIMPLE. For example, a sphere : hidden faces will never be drawn (that's important !) and drawing priority does not mind at all. Objects have to be closed : if a side would be opened, you should have to draw also hidden faces, so you should use Z sort again.
**************************** parte 1 ********************************* CalcPoints: ; Z factor move.l Dlines,a0 ; how to join faces move.l dplane,a1 ; number of punts per face and color lea Intz(pc),a2 ; Z val of all points lea BufferFace(pc),a3 ; empty buffer move.w dface,d6 ; number of faces moveq #0,d0 moveq #0,d1 LpLps: move.w (a1),d0 ; number of punts creating a face add.w #4,a1 move.w d0,d1 ; d1 = d0 subq.w #1,d0 ; d0 = -1 per dbf moveq #0,d3 SomaLp: move.w (a0),d2 ; point number add.w #4,a0 add.w d2,d2 ; * 2 (it is a word) add.w (a2,d2.w),d3 ; Z coord dbf d0,SomaLp ext.l d3 divs.w d1,d3 ; points / Z total move.w d3,(a3)+ ; factor value dbf d6,LpLps **************************** parte 2 ********************************* ; compute Z value of faces (not points) lea BufferFace(pc),a0 ; Z factor of all faces lea FacceSeq(pc),a1 ; empty buffer movea.l a0,a2 move.w dface,d4 ; number of faces move.w d4,d5 ; number of faces move.w d5,d6 ; number of faces subq.w #1,d6 ; number of faces -1 for dbf move.w d6,d7 ; number of faces -1 for dbf add.w d5,d5 ; number of faces *2 addq.w #2,d5 ; +2 because -(a1) before dec ; then we insert val in a1 add.w d5,a1 ; last val move.w #$9999,d5 ; $9999 = -26215 smallest Z value Calcs: move.w d7,d6 ; number of faces -1 per dbf moveq #2,d2 ; first face to insert (1)(2=WORD) moveq #0,d3 , starting face (0) move.w (a0)+,d0 ; Z value Calc: move.w (a0)+,d1 ; Z2 value cmp.w d0,d1 ; if Z2
OBJECT REDRAWING
Abs addresses between $100 and $200 MUST NEVER be used ! ! They are used by me in this old routine because when I have written it (1990) I had an A500 and with A500, A2000, A3000, these addresses were always empty. Now with AGA machines these addresses are in use, so I do not assure the demo will work correctly on these machines.
A lot of time, 6 years, is passed since I have written this code, I'll try to explain it as well as I can.
Display: clr.w $120.w clr.w $122.w clr.w $124.w move.l #0,a1 move.w ScrClrNow,a1 move.w #0,(a1)+ ; erase $150 move.w #$7fff,(a1)+ ; max. value in $152 move.w #0,(a1)+ ; delete $154 move.w #$7fff,(a1)+ ; max. value in $156 move.l Dlines,a6 ** this is the main routine : it is executed once per every face. lea $dff000,a6 move.l Bm2_PL1,d1 move.w dpunti,d6 ; number of points lea UltimateFace(pc),a1 ; empty buffer where to place the value ; corresponding to the faces that will be drawn ; after all the others.e(pc),a0