shithub: duke3d

Download patch

ref: 09a21797abe4a341f26b9d9db21b736fcf8965fc
parent: beae8dac32f4778a34615ca2a6974c31931386e5
author: Fabien Sanglard <[email protected]>
date: Fri Dec 14 06:34:17 EST 2012

Added full explanation for scansector and inside.

--- a/Engine/src/engine.c
+++ b/Engine/src/engine.c
@@ -127,8 +127,14 @@
 uint8_t  britable[16][64];
 uint8_t  textfont[1024], smalltextfont[1024];
 
+//xb1 and xb2 seems to be storing the column of the wall endpoint
+//yb1 and yb2 store the Y distance from the camera.
 static int32_t xb1[MAXWALLSB], yb1[MAXWALLSB], xb2[MAXWALLSB], yb2[MAXWALLSB];
+
+//rx1,rx2,ry1,ry2 stores the cameraspace wall endpoints coordinates.
 static int32_t rx1[MAXWALLSB], ry1[MAXWALLSB], rx2[MAXWALLSB], ry2[MAXWALLSB];
+
+//
 static short p2[MAXWALLSB], thesector[MAXWALLSB], thewall[MAXWALLSB];
 
 static short bunchfirst[MAXWALLSB], bunchlast[MAXWALLSB];
@@ -353,7 +359,8 @@
             wal2 = &wall[wal->point2];
 
             // In camera space the center is the player.
-            // Make the camera the center of the world for the 2 Wall endpoints (x,y)
+            // Tranform the 2 Wall endpoints (x,y) from worldspace to camera space.
+            // After that we have two vectors starting from the camera and going to the endpoints (x1,y1) and (x2,y2).
             x1 = wal->x-globalposx;
             y1 = wal->y-globalposy;
 
@@ -371,15 +378,16 @@
                     // Using cross product, determine if the portal is facing us or not.
                     // If it is: Add it to the stack and bump the stack counter.
                     // This line is equivalent to tempint < 0x40000
-                    if (((unsigned)tempint+262144) < 524288)
+                    if (((unsigned)tempint+262144) < 524288) // ??? What is this test ?? How acute the angle is ?
                     {
                         //(x2-x1)*(x2-x1)+(y2-y1)*(y2-y1) is the squared length of the wall
+                        // ??? What is this test ?? How acute the angle is ?
                         if (mulscale5(tempint,tempint) <= (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
                             sectorborder[sectorbordercnt++] = nextsectnum;
                     }
                 }
 
-            // Rotate the wall endpoint position according to the player orientation.
+            // Rotate the wall endpoints vectors according to the player orientation.
             // This is a regular rotation matrix using [29.3] fixed point.
             if ((z == startwall) || (wall[z-1].point2 != z))
             {
@@ -400,7 +408,7 @@
 
 
 
-            // No idea what this is.
+            // Equivalent of a near plane clipping ?
             if ((yp1 < 256) && (yp2 < 256)) goto skipitaddwall;
 
             /* If wall's NOT facing you */
@@ -407,12 +415,12 @@
             if (dmulscale32(xp1,yp2,-xp2,yp1) >= 0) goto skipitaddwall;
 
             // The wall is still not eligible for rendition: Let's do some more Frustrum culling !!
-
             if (xp1 >= -yp1)
             {
                 if ((xp1 > yp1) || (yp1 == 0))
                     goto skipitaddwall;
 
+                //Project the point onto screen and see in which column it belongs.
                 xb1[numscans] = halfxdimen + scale(xp1,halfxdimen,yp1);
                 if (xp1 >= 0)
                     xb1[numscans]++;   /* Fix for SIGNED divide */
@@ -454,7 +462,8 @@
             }
             if ((yb2[numscans] < 256) || (xb1[numscans] > xb2[numscans])) goto skipitaddwall;
 
-            /* Made it all the way! */
+            // Made it all the way!
+            // Time to add this wall information to the stack of wall potentially visible.
             thesector[numscans] = sectnum;
             thewall[numscans] = z;
 
@@ -476,16 +485,22 @@
         }
 
         //FCS: TODO rename this p2[] to bunchList[] or something like that. This name is an abomination
+        
+        //Break down the list of walls for this sector into bunchs. Since a bunch is a
+        // continuously visible list of wall: A sector can generate many bunches.
         for(z=numscansbefore; z<numscans; z++)
         {
             if ((wall[thewall[z]].point2 != thewall[p2[z]]) || (xb2[z] >= xb1[p2[z]]))
             {
+                // Create an entry in the bunch list
                 bunchfirst[numbunches++] = p2[z];
+                
+                //Mark the end of the bunch wall list.
                 p2[z] = -1;
             }
         }
 
-        //For each bunch, cache the last wall of the bunch in bunchlast.
+        //For each bunch, find the last wall and cache it in bunchlast.
         for(z=bunchfrst; z<numbunches; z++)
         {
             for(zz=bunchfirst[z]; p2[zz]>=0; zz=p2[zz]);
@@ -493,6 +508,7 @@
         }
 
     } while (sectorbordercnt > 0);
+    // do this until the stack of sectors to visit if empty.
 }
 
 
@@ -2420,7 +2436,7 @@
     }
 }
 
-
+//FCS: Geez one more horrible algorithm to decipher :/ :( cry smiley.....
 int wallfront(int32_t l1, int32_t l2)
 {
     walltype *wal;
@@ -2472,6 +2488,7 @@
 }
 
 
+//Return 1 if bunch b1 is in from of bunch b2.
 static int bunchfront(int32_t b1, int32_t b2)
 {
     int32_t x1b1, x2b1, x1b2, x2b2, b1f, b2f, i;
@@ -2479,7 +2496,10 @@
     b1f = bunchfirst[b1];
     x1b1 = xb1[b1f];
     x2b2 = xb2[bunchlast[b2]]+1;
-    if (x1b1 >= x2b2) return(-1);
+    
+    if (x1b1 >= x2b2)
+        return(-1);
+    
     b2f = bunchfirst[b2];
     x1b2 = xb1[b2f];
     x2b1 = xb2[bunchlast[b1]]+1;
@@ -2490,6 +2510,7 @@
         for(i=b2f; xb2[i]<x1b1; i=p2[i]);
         return(wallfront(b1f,i));
     }
+    
     for(i=b1f; xb2[i]<x1b2; i=p2[i]);
     return(wallfront(i,b2f));
 }
@@ -2670,8 +2691,14 @@
         mirrorsy2 = max(dmost[mirrorsx1],dmost[mirrorsx2]);
     }
 
+    // Scan sector has generated bunches, it is not time to see which one to render.
+    // numhits is the number of column to draw (if the screen is 320x200) then numhits starts at 200.
+    // Due to rounding error, not all column may be drawn so an additional stop condition is here:
+    // When every bunches have been tested for rendition.
     while ((numbunches > 0) && (numhits > 0))
     {
+        // tempbuf is used to mark which bunches have been elected as "closest".
+        // if tempbug[x] == 1 then it should be discarded.
         clearbuf(&tempbuf[0],(long)((numbunches+3)>>2),0L);
         tempbuf[0] = 1;
 
@@ -2682,6 +2709,7 @@
             tempbuf[i] = 1;
             if (j == 0) tempbuf[closest] = 1, closest = i;
         }
+        
         for(i=0; i<numbunches; i++) /* Double-check */
         {
             if (tempbuf[i]) continue;
@@ -2690,7 +2718,7 @@
             if (j == 0) tempbuf[closest] = 1, closest = i, i = 0;
         }
 
-        //Draw every solid walls in the bunch "closest"
+        //Draw every solid walls with ceiling/floor in the bunch "closest"
         drawalls(closest);
 
         if (automapping)
@@ -2699,7 +2727,9 @@
                 show2dwall[thewall[z]>>3] |= pow2char[thewall[z]&7];
         }
 
+        //Since we just rendered a bunch, lower the current stack element
         numbunches--;
+        //...and move the bunch at the top of the stack so we won't iterate on it again...
         bunchfirst[closest] = bunchfirst[numbunches];
         bunchlast[closest] = bunchlast[numbunches];
     }
@@ -4291,20 +4321,26 @@
 
 /*
  FCS: Return true if the point (x,Y) is inside the sector sectnum.
- Note that a sector is closed to the answer is always 0 or 1.
+ Note that a sector is closed (but can be concave) so the answer is always 0 or 1.
 
- Algorithm: For each wall one after an other....???
+ Algorithm: This is an optimized raycasting inside polygon test:
+ http://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm
+ The goal is to follow an horizontal ray passing by (x,y) and count how many
+ wall are being crossed.
+ If it is an odd number of time: (x,y) is inside the sector.
+ If it is an even nymber of time:(x,y) is outside the sector.
  */
+
 int inside(int32_t x, int32_t y, short sectnum)
 {
     walltype *wal;
     int32_t i, x1, y1, x2, y2;
-    uint32_t  cnt;
+    uint32_t  wallCrossed;
 
     //Quick check if the sector ID is valid.
     if ((sectnum < 0) || (sectnum >= numsectors)) return(-1);
 
-    cnt = 0;
+    wallCrossed = 0;
     wal = &wall[sector[sectnum].wallptr];
     i = sector[sectnum].wallnum;
     do
@@ -4322,13 +4358,17 @@
             //If (x1^x2) >= 0 x1 and x2 have identic sign bit: x is on the left or the right of both wal->x and wall[wal->point2].x.
             if ((x1^x2) >= 0)
             {
-                // Euh...je ne sais pas.....
-                cnt ^= x1;
+                // If (x,y) is on totally on the left or on the right, just count x1 (which indicate if we are on
+                // (x,y) is on the left or on the right.
+                wallCrossed ^= x1;
             }
             else
             {
-                //Cross-Product WTF ?
-                cnt ^= (x1*y2-x2*y1)^y2;
+                // This is the most complicated case: X is between x1 and x2 and y is between y1 and y2.
+                // We need to know exactly if it is on the left or on the right in order to know if the ray
+                // is crossing the wall or not,
+                // The sign of the Cross-Product can answer this case :) !
+                wallCrossed ^= (x1*y2-x2*y1)^y2;
             }
         }
 
@@ -4337,8 +4377,9 @@
 
     } while (i);
 
-    //Just return the sign.
-    return(cnt>>31);
+    //Just return the sign. If the position vector cut the sector walls an odd number of time
+    //it is inside. Otherwise (even) it is outside.
+    return(wallCrossed>>31);
 }