			      ACK-3D
		 ( Animation Construction Kit 3D )


			    Disclaimer

     The author, Lary Myers, and any other persons referred to
in this documentation or in the computer programs ACK3D and MAPEDIT
accept no responsibility for any loss of time, money or productivity,
or damage to any person(s) or computer hardware or software, as a
result of using the programs ACK3D and MAPEDIT, even if the above
mentioned had knowledge or had been notified of the possibilities
of such events.



		       Distribution and Usage

     Considerable thought has gone into just how the source code for ACK3D
would be released. Some possibilities included providing only the construction
kit and selling the source to interested parties, or providing the source to
be used as freeware for private use but requiring license for commercial ventures,
and so forth.

     What I've decided is to release all the source, header files, notes, etc
into the public domain as "PUBLICWARE". You may freely use the engine source
and associated files for the programs ACK3D and MAPEDIT anyway you see fit.
One of the goals when I started this project was to place the means into other
peoples hands, more talented than I, so they could then create worlds of
adventure and entertainment for the rest of us to share in.

     Here is the one reservation that I must insist on.

     The author, Lary Myers, hereby grants the right to use, modify, and sell
the source code provided in the ACK3D collection of files, with the stipulation
that Lary Myers accepts no responsibilty for changes desired in the code, bug
fixes, or future updates.


     Now, with all that aside, PLEASE let me know of any comments and/or
suggestions you may have regarding possible uses for the engine and any
enhancements you wish to share with me and others. The entire project is
still far from over and together maybe we can create a powerful engine for
use in a wide variety of applications.



------------------------------------------------------------------------------
Programming notes:

Author: Lary Myers


Development system: 486DX 33Mhz
		    8 Meg RAM
		    210 Meg HD
		    Boca VGA
		    Microsoft mouse

		    Borland C++ Version 3.1
		    Microsoft Macro Assembler Version 5.00A

Languages used:	    C and Assembler

Model used:	    Compact


------------------------------------------------------------------------------
Compiling the engine:

    1. Place all of the files from the .ZIP file into one directory. Since I
       use Borland I opted for the following directory structure;

	\BORLANDC\ACK3D\SRC	<- Contains all the source for the engine

	\BORLANDC\ACK3D\KIT	<- Contains executables, images, for the kit

    2. Several years ago I wrote a program called MK.EXE which was used to
       MAKE programs based on dependencies, etc. Even though the compilers
       nowadays have much better makes, I still use this old one of mine, so
       the .MAK files provided with the engine are based on using it with
       MK.EXE. You can either use them as is (if you have BorlandC) or use
       them to see what's needed to create a standard MAKE file. If you use
       MK then type in the following to begin the process;

	    MK A   <- This will build the ACK3D engine

	    MK M   <- This will build the Map Editor



------------------------------------------------------------------------------
Goals:
	1. Create a 3D graphic engine which visually mimics the Wolfenstein
	   3D game as a starting point, then add new features and interface.

	2. Use the engine to create a generic construction kit.

	3. Provide the source and construction kit to the general public.


Techniques:

    The graphic engine is based on a technique called Ray Casting, a term
that has been used to describe the process where a 2D representation of a
maze is rendered in 3D. The details of Ray Casting are provided below;

    1. Using a two dimensional map similiar to graph paper, a maze is built
       consisting of borders (walls) in both the X and Y planes. These walls
       describe the physical boundaries of the map. Figure 1 shows an example
       of a simple map.


	 X
	ͻ
      Y ................................................
	................................................
	͹
	....	          	     	   	    	      ....
	....	          	     	   	    	      ....
	͹
	....	      ....................    	      ....
	....	      ....................    	      ....
	͹
	....	      ....	     	   ....    	      ....
	....	      ....	     	   ....    	      ....
	͹
	....	          	     	   ....    	      ....
	....	          	     	   ....    	      ....
	͹
	....	      ....	     	   ....................
	....	      ....	     	   ....................
	͹
	....	      ....	     	   	    	      ....
	....	      ....	     	   	    	      ....
	͹
	................................................
	................................................
	ͼ


	ͻ
	....	Indicates a square that has walls
	....
	ͼ

	ͻ
	    	Indicates a blank square
	    
	ͼ


			Figure 1




    2. The map is made up of cubes, each having a fixed size. For our
       purposes we'll use 64x64 units for each square. This allows an
       object (or player) to move 64 units in either the X or Y direction
       before moving into another square. The total map is made up of
       a number of squares to form rows and a number of rows to form a
       two dimensional array (again, just like graph paper has some number
       or sqaures by some number of rows of squares).


    3. A player is then defined as a location on the map having three
       properties, an X and Y coordinate as well as an Angle that the
       player is currently facing. Figure 2 shows this relationship.



	
	................
	................	    P - The current location of the player
	
	....	          	    ->	The current angle the player is facing
	.... P->        
	
	....	      ....
	....	      ....
	
	....	      ....
	....	      ....
	


			Figure 2




    4. Once we know where the Player is and the current angle the player is
       facing, we can begin to determine what will be visible at that moment
       in time. To begin with, we need to decide on the field of vision that
       will be used. ACK3D uses a 60 degree field of vision to show the
       walls in a realistic setting. This means that the player will see all
       items 30 degrees to the left of the current viewing angle to 30 degrees
       to the right of the current angle. Figure 3 illustrates this.



			/
		      /	   -30 degrees from viewing angle (VA - 30)
		    /
		  /
		P ------------->  Current viewing angle (VA)
		  \
		    \
		      \	   +30 degrees from viewing angle (VA + 30)
			\


			Figure 3





    5. As you can see, if we superimpose Figure 3 onto figure 2 we begin to
       get a field of vision which encompasses those walls in front of the
       player (at the current viewing angle). This gives us Figure 4.



	  0    1    2	 3
	
      0 .........../....
	........./......	    P - The current location of the player
	/
      1 ....	/         	    ->	The current angle the player is facing
	.... P-------->   
	\
      2 ....	  \    ....
	....	   \  ....
	\
      3 ....	      ....
	....	      ....
	


			Figure 4



    6. What the user (player) should see at this point is shown in Figure 5,
       where the double border represents the screen with the walls displayed
       inside.




	    ͻ
	    				      / 
	    				    /	
	    				  /	
	    Ŀ	
	    				 	
	    				 	
	    				 	
	    				 	
	    				 	
	    				 	
	    				 	
	    				 	
	    	
	    				  \	
	    				    \	
	    				      \ 
	    ͼ


			Figure 5



    7. So, how do we get from a flat 2 dimensional map to the appearance of
       3D walls? By calculating the height of the walls as a function of
       distance. Given the current X,Y coordinates of the player and the
       X,Y coordinates of the wall, we can use trigonometry to figure out
       the distance between the player and the wall. The real trick is to
       perform these calculations with the speed necessary to show realistic
       movement in our 3D environment. We'll get into that in a moment. Right
       now, lets look at the brute force method of determining how far the
       walls are. The first thing to remember is that we will be casting a
       ray for each column of the screen. Using VGA mode 13h this gives us
       320 columns by 200 rows. So, 320 rays will be cast out to fill the
       screen with our field of view.



    8. Lets begin by setting some initial values for the player. We said in
       section 2 that each square was 64 by 64 units in size. Looking at
       Figure 4 we see that the player is in square 1,1 about 3/4 the way
       down from the top of the square and about 1/4 the way over from the
       right side of the square. This puts our player X,Y at roughly 80,112.
       The current viewing angle will be 0 for this part of the discussion.

       Since we are going to use the trig functions provided in the C libraries,
       we can draw a coordinate system where the angle increases in a clockwise
       direction, giving us Figure 6;


			       270
				    330
				  /
				/
		       180 O 0
				\
				  \
				    30
			       90

			Figure 6


    9. Therefore, as mentioned in section 4, we begin our cast 30 degrees to
       the left of the current view angle, or at 330 degrees, and continue
       until we are 30 degrees to the right of the view angle, which will be
       at +30 degrees. As you can see, we can form a right triangle from the
       player position to the point at angle 330.


						       Opp
						Tan =  ---
				/		       Adj
			  Hyp /	 
			    /	 Opp		       Adj
			  / Angle		Cos =  ---
			P		       Hyp
			    Adj

						       Opp
						Sin =  ---
						       Hyp
			Figure 7



   10. What we want to do at this point is draw an imaginary line from the
       player out to some point at angle 330. But how far do we draw the
       line? As far as needed to encounter a wall or until we leave the outside
       boundaries of the map. Lets say our map is 10 squares across by 8
       rows down. That gives us 80 squares total. The maximum width of our
       map is therefore 64 units times 10 or 640 units, by 64 units times 8
       rows or 512 units. The maximum distance we ever have to look is given
       be the equation;



		c2 = a2 + b2	 (c squared = a squared plus b squared)

		    or

		c2 = 640 squared plus 512 squared


		which gives 819.5999 or 820 units


			Equation 1



      So, 820 units is how far we'll cast each ray, checking along the way
      for a wall or a boundary condition. We can use the well established
      Bresenham's line drawing algorithm to plot each point along the line
      to see what it strikes. We begin by determining the endpoints of the
      line. The equations for a point on a circle gives us the X and Y
      points we need;


	    X1 = X + (cos(angle) * distance)

	    Y1 = Y + (sin(angle) * distance)



			Equation 2


      Using the numbers from section 8 and section 10 we get the following;


	    X1 = 80 + (cos(330) * 820)

	    Y1 = 112 + (sin(330) * 820)


	    which gives;

	    X1 = 790

	    Y1 = -298


      Passing the coordinates 80,112,790,-298 into a line routine we begin
      to check for intercepts with walls. At each point along the line we
      calculate our map position with the following equation;



	    MapPosn =  ( Y / 64 ) * 10 + ( X / 64 )


	Recalling that our map was 10 squares across and each square was 64
	by 64 units in dimension.


      Looking back at Figure 4 we see that our first intercept will come when
      the line passes into square 2,0. At this very point we have struck a
      wall and can then determine the distance to that wall. We'll call the
      point Wx and Wy to distinguish them from our line endpoints. Using
      Equation 1 again we can determine the distance to the wall;


		c2 = a2 + b2


		c2 = (Wx - x) squared plus (Wy - y) squared


			Equation 2



      From the drawing scale in Figure 4, it appears a wall is encounterd
      at coordinates 128,128 in square 2,0, so we can use numbers in the
      above to get;


		c2 = (128 - 80) squared plus (128 - 112) squared


		which gives us;


		Distance = 50.596 units or 51 units rounded up


      Using this distance we can look up the height from a precalculated table
      to see how high the wall should appear on the screen.



      We continue this process for each column of the screen until we have
      a 320 columns of graphic walls displayed, each column showing the walls
      at a certain height, which gives the illusion of distance.


      As you may have already surmised, we don't advance our ray casting by
      whole degree increments, instead we move in 60/320 degrees or 0.1875
      increments to give us a 60 degree field of view in 320 columns of
      the video screen.


      This method was initially used by the ACK3D engine and can be summarized
      with the following;


	a. Take the current viewing angle of the player and subtract 30 from
	   it to get the cast angle.

	b. Calculate an X and Y endpoint using the players X,Y coordinates,
	   the cast angle, and a maximum distance to cast the ray.

	c. Using Bresenham's line drawing algorithm, plot each point along the
	   line and look for an intercept with a wall or the point where the
	   line goes outside of the map boundaries. If a wall is found, return
	   the Wx and Wy point of the wall intercept.

	d. Using the players X,Y coordinates, and the Wall Wx,Wy coordinates,
	   calculate the distance to the wall using Equation 2.

	e. Use the distance to look up in a table to get the height of the
	   wall to display.

	f. Add 0.1875 to the cast angle and go back to step b until 320 rays
	   have been cast.


			Method 1




   11. The above discussion was presented in order to give the reader some
       background into the train of thought for ray casting. While Method 1
       did work and did produce a semi-reasonable display, some major problems
       were seen, these can be summarized as;


	a. Speed - Casting a line along every point for 320 rays was just
		   plain SLOW! The screen could actually be seen updating
		   from left to right as the rays were cast! (This was without
		   any optimizations in the ray casting routines).


	b. Accuracy - Floating point was used in order to get the needed
		      increments in angles and distance, but a problem with
		      roundoff still occurred. Some of this was attributable
		      to the code (This was pointed out to me by those who
		      looked at the routines - my thanks to you all!). But,
		      some of it was due to the method itself.


	c. Intercepts - There was no easy way to determine if we had hit a
			wall on the X or Y borders of the square. One method
			that could have been used was to determine if the
			Wx and Wy points fell on an even 64 boundary, but
			this was never tried. Once we found the correct
			intercept, there was still the problem of determining
			which column of the wall should be drawn.



------------------------------------------------------------------------------

Optimized Ray Casting:



    1. Once it was determined that the method was viable, albiet with its
       problems, a more advanced or optimized method could be tried. This
       is how ACK3D evolved.

    2. Many of the givens could be carried forward into the new version, we
       would still use squares on a map, each square being 64 units wide by
       64 units tall. The player properties could still be used, giving us
       an X,Y coordinate pair and a current angle that the player is facing.
       The total viewing angle could remain at 60 degrees with a sweep from
       -30 degrees to the left to +30 degrees to the right of the current
       player angle.

    3. The first order of business was to get rid of the floating point. By
       using fixed point numbers we could attain a speedup in the overall
       process. Fixed point numbers are beyond this discussion but essentially
       a fixed point number is where a given number is shifted a certain
       amount to the left to retain the fractional part of the number. This
       new value is then used in a calculation and the result is shifted back
       to the right to remove the fractional part. ACK3D uses two fixed point
       ranges to perform its calculations, the first being a 16 bit shift and
       the second being a 20 bit shift (which we'll get into soon).

    4. The next thing was to begin using tables for most of the housekeeping
       calculations. Since we knew that our viewing angles were in 0.1875
       degree increments we could precalculate sine and cosine tables (as well
       as tangent) ahead of time. Our full circle would therefore become
       360 / 0.1875 or 1920 entries in the tables. Every angle from 0 to 360
       was calculated, multiplied by 65536 (our 16 bit shift for fixed point)
       and written out to a file. Now if we needed to find the cosine of
       330 degrees, we could use 330/0.1875 or 1760 to lookup in the cosine
       table and get the value 56759 (after rounding). The same holds true
       for sine and tangent tables (and some others) as well.

    5. Now, how to speed up drawing an imaginary line from the player to a
       point off in the distance? Looking at the map it becomes apparent that
       each square is a fixed distance from each other. That is where the
       speedup can be found. Lets break the process down further in that
       we only want to look for walls that fall on the X side and then walls
       that fall on the Y side of the square. This means we have to cast
       two rays for every angle, but it also means we know in advance which
       intercept will occur.



	  0    1    2	 3
	B
      0 .........../....
	........./......	    P - The current location of the player
	A
      1 ....	/         	    ->	The current angle the player is facing
	.... P-------->   
	\
      2 ....	  C    ....
	....	   \  ....	    	Walls on the X side
	\
      3 ....	      D....	    	Walls on the Y side
	....	      ....
	


			Figure 8




    6. Figure 8 shows our section of the map with only the X walls given as
       double lines, the Y walls (which we'll ignore for now) all fall on the
       single lines. From this we can see that a ray looking only for X walls
       will intercept at points A and B at angle 330 (The scale and angles
       are not precise due to text mode limitations, for the sake of discussion
       lets assume that the angle is 330). We also see that points C and D will
       be struck at our rightmost angle of 30 degrees. This gives us a method
       to optimize the ray casting algorithm into the following;

	a. Subtract 30 degrees from the current player angle to get the
	   viewing angle.


	b. Determine the Y intercept of the current square based on the view
	   angle and the X coordinate (note that the X coordinate will be the
	   right side of the current square if our view angle falls between
	   270 degrees and 90 degrees and it will be the left side of the
	   current square if our view angle falls between 90 degrees and
	   270 degrees). This X,Y intercept of the current square will be
	   the first point we look for a wall.

	   By using the tangent or inverse tangent of the view angle we can
	   calculate the Y or X offset depending on which ray we are casting.
	   This gives us a starting point in the current square to begin
	   looking for walls.

	   Knowing the width of each square is 64 we determine the next
	   Y coordinate that our ray will fall on if we increase (or decrease)
	   the X intercept by 64. This can be pre-calculated and placed in a
	   table. We continue stepping both the X and Y coordinates with
	   addition until a wall is hit or we leave the boundaries of the map.
	   By using Equation 1 again we see that a maximum of 13 (rounded)
	   points along any given line is all we will ever need to traverse
	   the maximum distance in the map. Thats 13 points verses 820
	   from the previous method!


	c. Once a wall is struck we use the Y coordinate to determine what
	   column of the wall to display (Y AND 63) and the X coodinate to
	   determine the distance (WITHOUT USING SQUARE ROOT).


	d. We flip the process around and perform a ray cast for walls that
	   fall along the Y side of the square, using the X coordinate to
	   determine the column of the wall and Y coordinate to determine
	   the distance.


	e. Comparing the distance from the Xray and the distance from the
	   yRay for whichever is closer we get the actual wall and wall
	   column to display.


	f. Next, we advance our view angle by one unit in our 1920 entry
	   circle and loop back to step b until 320 ray pairs have been
	   cast.

------------------------------------------------------------------------------
Determining the height of the bitmap:

    1. Once we have the distance to the wall we need to look up the display
       height of the bitmap from our table. This table was pre-calculated
       and is based on dividing the distance into an experimentally derived
       number (that means I tried a variety of values until one looked the
       best!). For ACK3D this turned out to be 18000. All distances from
       1 to our max distance are divided into this number to get a height
       value to draw the bitmap. Heights which are too big or too small are
       set to the maximum or minimum values respectively.


------------------------------------------------------------------------------
Objects within our world:

    1. Dealing with objects turned out to be a completely different problem.
       One that wasn't apparent from the effort put into the wall algorithm.
       The goal was to present an object to the user that did not show the
       same effects with angles as the walls, in other words, I didn't want
       objects to have corners when looking at them diaganally. This meant
       the distance to the object should not be dependent on either the X or
       the Y ray but should be based on a line of sight ray cast from the
       players current position.

    2. After much experimenting and head scratching it was decided to treat
       objects using more traditional 3D methods. But, the speed still had
       to be considered!

    3. Again, the brute force method would be to have all of the objects X
       and Y coordinates in a table. Each object is then translated to base
       0,0 from the player and rotated into the field of vision. If the final
       X,Y location of the object fell within the 60 degree field of vision
       then the object would be visible and handled by the drawing routines.
       But what if there were many objects in this table? The time expended
       to check each object soon becomes a function of the number of objects
       that exist. No, some faster way had to be found.

    4. So, by combining the ray casting with the process in section 3 above
       we could eliminate all objects not within the current field of vision
       thereby reducing the amount of calculations needed.
       This is the process;

	a. During the ray casting process described in section 6 above, also
	   look in the object map to see if an object exist. If so, add the
	   object to a list and set a flag saying the object has been seen.
	   This flag prevents other near-angle rays from adding the same
	   object to the list, only one entry is needed.

	b. Sort the object in the list with nearer objects being pushed down
	   and farther objects being near the top. This way when the objects
	   are later drawn the closer ones will hide objects that may be
	   farther away and at the same angle.

	c. Continue the ray casting past the object just found. This allows
	   us to find additional objects and finally any walls that may be
	   in the line of sight. What gets returned to the caller of the
	   ray casting routine is always the wall information, never any
	   objects (since objects are stored in a global list).

	d. Once all the rays for the entire screen are cast we then draw the
	   walls into the off-screen video buffer. Now its time to check for
	   the objects.

	e. Using the list built during the ray casting we perform the
	   laborious task of translating and rotating the objects coordinates
	   relative to the players position and angle. This gives us final
	   coordinates that we can use to determine where to place the object
	   on the screen and what column to begin drawing the object. As we
	   begin drawing, the distance to the object is checked against the
	   distance to the corresponding wall. If the wall is closer, the
	   object is not drawn in that column. We don't need to check the
	   current object against all the other objects because they were
	   sorted by distance when found, they'll cover up objects farther
	   away by default.

	f. This process continues until all objects have been displayed in
	   the list. Each time the routine is called to draw the screen we
	   clear out the object seen flags and set the object count back
	   to zero. This allows us to start fresh with finding any objects
	   in our field of vision.


------------------------------------------------------------------------------
VGA Mode X:

    1. During initial development it was decided to use the undocumented
       VGA mode X (From Michael Aprash's articles in DDJ) which allow
       off-screen drawing and page flipping. This worked reasonably well
       on fast machines, but slower machines still had problems.

    2. The normal VGA 320x200 mode 13H has also been tried with good results,
       but it is necessary to draw to a memory buffer (the way objects are
       currently handled) and then copy the system memory buffer to the
       screen. While it is a fast and straight forward process to move the
       64,000 bytes from memory to video, it was still not fast enough to
       use on slower machines.

    3. Experimenting has shown that it is possible to setup a screen mode
       that gives 320x200 resolution but still has multiple pages, etc. This
       cuts back on the number of screen writes needed (320 columns by 40
       rows) and has shown a definite increase in performance.

    4. By way of the experimenting, the following files have been developed
       to try various methods.

	ACKASM.ASM - This is the current in-use file which sets multiple plane
		     320x200 mode.

	ACKASM.VGA - This uses normal VGA 320x200 mode 13h

	ACKASM.240 - This uses 320x240 mode X

	These files can be used in place of one another to test the engine
	in the various modes.


------------------------------------------------------------------------------
Final comments:

    ACK3D has evolved over the past few weeks into a reasonable 3D graphics
    engine and I am fairly pleased with the results thus far. There are,
    however some annoying nuances that still plague the engine. These can be
    summarized as follows, and in the words of most books "The rest is left
    as an exercise for the reader" (Man I hate those words!).

    a. Accuracy at certain angles is still an issue, every so often a wall
       sliver appears where it shouldn't, at least at the wrong height
       anyway.

    b. Objects are getting clipped off the screen when thier centerpoint
       is reached. They should be partially visible even if the center is
       not.

    c. Collision detection is far from perfect. A better method needs to
       be devised to handle coming up to walls and objects at any angle.

    d. Secret doors are mariginally working. They should be used with care
       since the side walls of the moving door are invisible until the
       secret door comes to a rest.

    e. Object movement is restricted to always turning right when bumping
       into an obstacle. This needs to have more smarts built in to allow
       moving objects to do things like chase the player, or run away, or
       follow some predetermined path, etc.

    f. Other types of doors, besides secret and sliding doors needs to be
       implemented. Future versions of the engine will handle a variety of
       doors to deal with different uses of the engine.

    g. The goal actions need to be enhanced beyond displaying a screen that
       says something on the order of YOU WON! I mean really....



    I hope this discussion, and the 3D engine itself, prove to be a good
    starting point for your own creations. Many possibilities exist for this
    type of environment ranging from underground dungeons, to space stations,
    to art galleries. All thats needed is the right combination of graphics,
    sound, and storyline.


    Sincerely,
    Lary Myers
    5 Jacob Street
    Ballston Lake, NY 12019

    CIS Account: 76004,1574



