1958 lines
62 KiB
Plaintext
1958 lines
62 KiB
Plaintext
|
||
|
||
Animation Construction Kit 3D
|
||
ACK-3D
|
||
Lary Myers
|
||
|
||
|
||
Contents:
|
||
1 ........... Purpose
|
||
2 ........... Using the ACK engine (DISCLAIMER)
|
||
3 ........... Hardware Requirements
|
||
4 ........... Development Environment
|
||
5 ........... Multi-User
|
||
6 ........... The Interface Structure
|
||
7 ........... Important fields in the Interface Structure
|
||
8 ........... Array Formats in the Interface Structure
|
||
9 ........... Initializing the ACK-3D engine
|
||
10 ........... ACK-3D Bitmaps
|
||
11 ........... Loading Bitmaps
|
||
12 ........... Creating Objects
|
||
13 ........... Using Overlays
|
||
14 ........... Moving around in ACK-3D
|
||
15 ........... Moving objects
|
||
16 ........... Normal and Secret Doors
|
||
17 ........... Sound and ACK-3D
|
||
18 ........... Function summary
|
||
19 ........... Where to Begin (EXAMPLES)
|
||
20 ........... Closing Comments
|
||
|
||
|
||
|
||
|
||
(1) Purpose:
|
||
|
||
ACK-3D began as an experiment to emulate the 3D effects seen in games like
|
||
Wolfenstein-3D and Ken's Labyrinth and to make the technique available to the
|
||
general public. So far things have worked out very well and in some areas have
|
||
even exceeded the before mentioned games. With this latest installment ACK-3D
|
||
gives the developer an easy to use library that can be linked into thier own
|
||
applications without having to delve into the inner workings of the ray-casting
|
||
engine. My hope is that others will use the ACK-3D engine to produce a new
|
||
generation of 3D games that all of us can enjoy.
|
||
|
||
Please forgive any glaring mistakes in this documentation. I find that I can
|
||
write code all day long without getting weary, but as soon as I have to write
|
||
documentation, forget it, too much like work! I hope the text below gives you
|
||
enough information to use the engine.
|
||
|
||
|
||
|
||
(2) Using the ACK-3D engine: (DISCLAIMER)
|
||
|
||
The ACK-3D engine is being released as "PublicWare" and can be freely used
|
||
in any private or commercial programs. The only restriction I would impose is
|
||
that anything developed with the engine have its own disclaimer about liability
|
||
and that under no circumstances will the author (me) be liable for any damages
|
||
of any kind that may result from the use of the engine. "Basically the engine
|
||
is being released "AS-IS" so it's left up to the developer to use it in a
|
||
professional manner".
|
||
|
||
If you develope a wonderful new game using the engine, I'd appreciate a
|
||
mention in the documentation or credits screen, but only if you feel the ACK
|
||
engine helped the game. Thanks.
|
||
|
||
|
||
(3) Hardware Requirements:
|
||
|
||
The current version of ACK-3D requires a minimum of a 386 33Mhz machine and
|
||
will not run on a 286 or below because of 386 code being used. A coprocessor
|
||
is NOT required or needed for the engine itself. Any other hardware requirements
|
||
are left up to the applications that use the engine (ie. mouse, VGA, etc).
|
||
A VGA is only required if the ACK display routines are used (which is up to the
|
||
application). If the application chooses to use another display mode then it
|
||
should issue the appropriate hardware requirements.
|
||
|
||
|
||
|
||
|
||
(4) Development Environment:
|
||
|
||
ACK-3D was developed using BorlandC Version 3.1 in compact model. It is
|
||
setup to also use large model (both may be supplied). The assembly routines
|
||
were compiled using the Microsoft Assembler (MASM) version 5.00A and should
|
||
work with later versions of MASM. The library was created using the Microsoft
|
||
Library Manager (LIB) version 3.02 and should also work with later versions
|
||
of LIB.
|
||
|
||
All of the ACK code is written in either normal C or assembler.
|
||
|
||
Compiler switches used with Borland;
|
||
|
||
-ml = Large model
|
||
|
||
-c = Compile only, don't link
|
||
|
||
-3 = Use 386 instructions
|
||
|
||
-O2 = Compile for size
|
||
|
||
-G = Compile for speed
|
||
|
||
-I = specifies the include path for header files
|
||
|
||
-o = specifies the object path and file name
|
||
|
||
|
||
Assembler switches used with MASM;
|
||
|
||
/Ml = Case sensitive for all symbols and labels
|
||
|
||
/B63 = Size of buffer during assembly (does not affect OBJ file)
|
||
|
||
/D_ML
|
||
/D_MC = Assembly directive for large (_ML) or compact (_MC) model.
|
||
Used by ET.MAC for various macros.
|
||
|
||
|
||
Directory structure;
|
||
|
||
I use the following subdirectories for the ACK engine and demo. This
|
||
is provided for your information. It is what the .MAK files are setup for.
|
||
|
||
Engine source;
|
||
|
||
\borlandc\ack3d\eng <- .C .H .MAK files
|
||
|
||
\borlandc\ack3d\eng\cmobj <- Compact model .OBJ and .LIB files
|
||
|
||
\borlandc\ack3d\eng\lmobj <- Large model .OBJ and .LIB files
|
||
|
||
|
||
Demo source;
|
||
|
||
\borlandc\ack3d\demo <- .C .H .BAT .MAK .EXE .DAT files
|
||
|
||
\borlandc\ack3d\demo\bitmaps <- .BBM and .LBM files
|
||
|
||
\borlandc\ack3d\demo\sound <- .VOC .CMF .PWM files
|
||
|
||
|
||
|
||
|
||
|
||
(5) Multi-User:
|
||
|
||
ACK-3D uses an interface structure to communicate between the application
|
||
and the engine. This structure (which will be explained later) contains the
|
||
pertinent data that the engine needs to draw the current Point of View (POV).
|
||
It is possible to do some fancy things with this approach. Here are some of
|
||
the things I've tried;
|
||
|
||
1. Displaying more than one view at the same time such as directly in
|
||
front (the normal view) and directly behind (essentially eyes in the
|
||
back of your head!).
|
||
|
||
2. Displaying more than one player on the map is also possible but has
|
||
some restrictions that may make it unusable. These restrictions may
|
||
be lifted in the near future.
|
||
|
||
|
||
Some discussion has been going on for a network version of ACK-3D. While
|
||
this is not built into the engine for this version, it does seem to be a
|
||
straightforward progression from this point. Future versions of the engine
|
||
may indeed support multiple players logging into a 3D world and interacting
|
||
with each other in real-time!
|
||
|
||
|
||
(6) The Interface Structure:
|
||
|
||
ACK-3D uses a structure from the application to get the necessary
|
||
information for building a complete POV. It is the applications responsibility
|
||
to correctly create and setup this structure before the ACK engine is called.
|
||
Limited bounds checking is in the engine to prevent degradation of speed during
|
||
the build process. UNPREDICTABLE RESULTS MAY OCCUR WITH INVALID DATA!
|
||
|
||
The structure is defined in the header file ACK3D.H and can be either in
|
||
the data segment of the calling application or allocated in memory whichever
|
||
the application desires. Within the structure are variables for things like the
|
||
current location and angle of the player, the actual map arrays for the walls
|
||
and objects, pointers to the screen buffer, overlay buffer and background
|
||
buffer, as well as dimensions of the viewport to build the walls into. The
|
||
term "build" is being used here because the ACK engine will not display to the
|
||
screen unless told to do so in a separate function. This way the application
|
||
is free to display the current POV anyway it wants to, or it can use the
|
||
supplied function to display if it doesn't. This offers maximum flexibility in
|
||
how the engine is used.
|
||
|
||
|
||
|
||
(7) Important fields in the Interface Structure:
|
||
|
||
Before calling the ACK engine to initialize, some very important items
|
||
need to be filled into the interface structure. These are described below;
|
||
|
||
WinStartX <- The leftmost pixel coordinate of the viewport
|
||
WinEndX <- The rightmost pixel coordinate of the viewport
|
||
WinStartY <- The upper pixel coordinate of the viewport
|
||
WinEndY <- The lower pixel coordinate of the viewport
|
||
|
||
These four fields will allow the ACK engine to fill in some other fields
|
||
within the interface structure, such as WinWidth, WinHeight, etc.
|
||
|
||
Before calling the function to construct the background, the following
|
||
fields must be filled in;
|
||
|
||
TopColor <- Color value of the ceiling
|
||
BottomColor <- Color value of the floor
|
||
LightFlag <- Whether light shading is on or off
|
||
|
||
Before calling the function to actually build the POV, the following fields
|
||
must be filled in;
|
||
|
||
xPlayer <- X coordinate for the POV
|
||
yPlayer <- Y coordinate for the POV
|
||
PlayerAngle <- POV angle in ACK units
|
||
|
||
|
||
The ACK engine relies on the POV fields above to build the current scene.
|
||
By changing these values it's possible to build a variety of scenes one after
|
||
the other. For example, changing the PlayerAngle to be 180 degrees from the
|
||
current angle and then calling the build routine, will create a scene that is
|
||
directly behind the current POV.
|
||
|
||
|
||
|
||
(8) Array Formats in the Interface Structure:
|
||
|
||
Several arrays are used to build the 3D POV scene. ACK-3D uses a map layed
|
||
out in squares like graph paper to determine what the POV sees at any given
|
||
time. This map is a 2 dimensional array of 64 columns by 64 rows. When the
|
||
initialize routine reads in this map, it processes the data and builds the
|
||
arrays in the interface structure. These arrays are xGrid and yGrid which are
|
||
used for drawing the walls. The layout of these arrays is different from the
|
||
map file and is described below;
|
||
|
||
|
||
1,1,1,1,1,1,1,1 Example map of size 8x8, the real map
|
||
1,0,0,0,0,0,0,1 would be the same except 64x64
|
||
1,0,0,0,0,0,0,1
|
||
1,0,1,1,1,0,0,1
|
||
1,0,1,0,1,0,0,1
|
||
1,0,1,1,1,0,0,1
|
||
1,0,0,0,0,0,0,1
|
||
1,1,1,1,1,1,1,1
|
||
|
||
|
||
|
||
This is an example of a map where 0 is a blank square and non-zero values
|
||
represent wall bitmap numbers (from 1 to 255). The ACK engine requires this
|
||
same information broken down into walls that fall on X planes and walls that
|
||
fall on Y planes, so the array above is changed into;
|
||
|
||
1,1,1,1,1,1,1,1,1
|
||
1,0,0,0,0,0,0,1,1 This would be a map of X walls, note the extra
|
||
1,0,0,0,0,0,0,1,1 column on the right side of the map.
|
||
1,0,1,1,1,0,0,1,1
|
||
1,0,1,0,1,0,0,1,1
|
||
1,0,1,1,1,0,0,1,1
|
||
1,0,0,0,0,0,0,1,1
|
||
1,1,1,1,1,1,1,1,1
|
||
|
||
|
||
|
||
1,1,1,1,1,1,1,1
|
||
1,0,0,0,0,0,0,1 This would be a map of Y walls, note the extra
|
||
1,0,0,0,0,0,0,1 row on the bottom of the map.
|
||
1,0,1,1,1,0,0,1
|
||
1,0,1,0,1,0,0,1
|
||
1,0,1,1,1,0,0,1
|
||
1,0,0,0,0,0,0,1
|
||
1,1,1,1,1,1,1,1
|
||
1,1,1,1,1,1,1,1
|
||
|
||
|
||
This is basically what becomes of the original map in order for the engine
|
||
to see walls in either the X or Y planes. What this means is that anytime the
|
||
map needs to be looked at, the following applies;
|
||
|
||
|
||
int MapPosn; /* Location to look at in map */
|
||
|
||
|
||
xGrid[MapPosn]; /* This would be the left X walls */
|
||
xGrid[MapPosn + 1]; /* This would be the right X wall */
|
||
|
||
yGrid[MapPosn]; /* This would be the top Y wall */
|
||
yGrid[MapPosn + GRID_WIDTH]; /* This would be the bottom wall */
|
||
|
||
|
||
If all four of these locations is 0 then there is nothing at this square in
|
||
the map. What does this mean? One thing that would be possible (but not part of
|
||
the ACK engine), is that walls would not have to be cubes, but could be single
|
||
wall panels (might look alittle funny from the side since they wouldn't have
|
||
any width to them). It could also mean that different bitmaps could be used
|
||
for various sides of the cube.
|
||
|
||
|
||
The next two arrays are bMaps and oMaps which, for now, are simple arrays
|
||
that point to the wall and object bitmaps respectively. Each bitmap takes
|
||
4096 bytes of memory for the 64x64 size bitmap. BUT, the important point is
|
||
that ACK reads these bitmaps in normal row order, where there is a row of
|
||
color bytes one after the other, like so;
|
||
|
||
1,2,3,... An arbitrary bitmap of 64x64
|
||
1,1,1,...
|
||
4,2,4,...
|
||
....
|
||
|
||
This bitmap is then "rotated" 90 degrees so it is in column order, which
|
||
makes it easier to use when drawing the bitmap, thus the above becomes;
|
||
|
||
1,1,4,.... Bitmap rotated 90 degrees
|
||
2,1,2,....
|
||
3,1,4,....
|
||
|
||
-----------------------------------------
|
||
Another important array in the interface structure is the PalTable[] array
|
||
which controls how the ACK engine will perform light shading. The array contains
|
||
16 ranges of 256 colors each which are used to substitute for the actual colors
|
||
of the bitmap based on the distance away from the POV.
|
||
|
||
The distance to the wall or object is first divided by 64 to get the zone
|
||
to use for shading (zones greater than 15 are set at 15). This zone of 256 is
|
||
then used as a lookup table indexed by the color of the bitmap. What all this
|
||
allows is a gradual darkening of walls and objects as they become farther away
|
||
from the POV while still allowing some colors to be used as constant lighting
|
||
colors (they will never darken with distance).
|
||
|
||
|
||
-----------------------------------------
|
||
|
||
Now we come to doors. Within the interface structure there is a sub-
|
||
structure labeled Door[MAX_DOORS]. This structure array holds the current
|
||
status of all doors that may be in the process of opening or closing. This
|
||
array is NOT all the doors in the map, only those that are in motion. If the
|
||
ColOffset field of the array is non-zero the door is active and either opening
|
||
or closing. When an application makes a call to AckCheckDoorOpen() the engine
|
||
will determine if the POV is close enough to a door and initialize it in the
|
||
Door[] array. After that, every call to AckBuildView() will cause the door to
|
||
be updated until it goes fully open then fully closed. During this time the
|
||
application is free to alter the fields in the Door[] array (BEWARE that mPos
|
||
and mPos1 are map positions and should NOT be altered during this time). If,
|
||
for example, the application wants a door to remain open for a long period of
|
||
time, it can check the value of ColOffset to see where a door currently is. If
|
||
the value is less than 64 the door is partially open (or partially closed).
|
||
Once the value becomes greater than 64 the door is no longer visible and is
|
||
fully open. By keeping the value greater than 64 (setting the Speed field to
|
||
zero) the door will no longer be moved. Another way might be to set the field
|
||
ColOffset to zero and not allow the door to be seen anymore. This method would
|
||
be the best if the application desires the door to remain permanently open.
|
||
Here is a breakdown of the DOORS structure;
|
||
|
||
|
||
mPos <- Map position for one side of cube
|
||
|
||
mPos1 <- Map position for other side of cube
|
||
|
||
mCode <- Bitmap code that represents the door
|
||
|
||
mCode1 <- Bitmap code for door on other side
|
||
|
||
ColOffset <- Current column offset of door (see above)
|
||
|
||
Speed <- Speed is added to ColOffset during open/close
|
||
|
||
Type <- Type of door (ie DOOR_XCODE or DOOR_YCODE)
|
||
|
||
Flags <- Current door action (DOOR_OPENING or DOOR_CLOSING)
|
||
|
||
The current version of the ACK engine uses the following wall bitmap values
|
||
for doors;
|
||
|
||
60 <- Door lies on the X plane (vertical)
|
||
|
||
61 <- Side panel that appears on both sides of door
|
||
|
||
62 <- Door lies on the Y plane (horizontal)
|
||
|
||
|
||
-----------------------------------------
|
||
|
||
Okay, now let's look at the OBJECT sub-structure. Again, this is an array
|
||
stored within the interface structure and accessable to the application as well
|
||
as the ACK engine. Every object in the map is represented by an entry in this
|
||
array. The AckInitialize() function automatically fills in the initial X and Y
|
||
coordinates of the object when it finds one in the map file. The rest of the
|
||
object data must be filled in by the application before the objects can be
|
||
used. The section below on Creating Objects describes what fields should be
|
||
setup by the application.
|
||
|
||
Some of the other fields in the OBJECT array are described below;
|
||
|
||
Active <- 0 or 1 to indicate the object should be considered during
|
||
the AckCheckObjectMovement() function.
|
||
|
||
bmNum <- Holds from 1 to MAX_VIEW bitmap indices to display for
|
||
the object. If more than 1 bitmap the object will either
|
||
animate in place or display multiple views as the POV
|
||
walks around the object.
|
||
|
||
Sides <- Calculated by AckCreateObject() function dependent on
|
||
the number of bitmaps in bmNum.
|
||
|
||
Dir <- Which direction the object will move.
|
||
|
||
Flags <- This field can contain OF_PASSABLE which allows the POV
|
||
to walk right through the object. (Good for overhead
|
||
lights and such). It can also contain OF_ANIMATE, which
|
||
is used by the function AckCheckObjectMovement() to
|
||
cycle between multiple bitmap images for the object.
|
||
|
||
CurNum <- Current index (base 0) of the bitmap to display out of
|
||
bmNum. If the object only has 1 bitmap then CurNum will
|
||
always be 0.
|
||
|
||
MaxNum <- Total number of bitmaps (base 0) contained in bmNum. If
|
||
the object only has 1 bitmap then MaxNum will be 1.
|
||
|
||
Speed <- The speed at which the object will move. Normal values
|
||
are from 2 to 40, higher than this causes alot of
|
||
jmuping and may cause the object to pass through walls.
|
||
|
||
VidRow <- Reserved.
|
||
|
||
x and y <- Map coordinates of the object, from 0 to 4095.
|
||
|
||
mPos <- The actual map grid location of the object. If x and y
|
||
are changed by the application then mPos should also
|
||
be updated by using the equation;
|
||
|
||
mPos = (y & 0xFFC0) + (x >> 6);
|
||
|
||
|
||
The Dir field is a left-over from the older version of the engine, where
|
||
it was used for crude 8 direction movement, or rotating in place. With this
|
||
latest version, the OF_ANIMATE flag is used to indicate cycling through the
|
||
bitmaps for the object and the Dir field is ignored. The application can
|
||
still use this field for storing an angle of movement for use with the
|
||
function AckMoveObjectPOV().
|
||
|
||
|
||
|
||
|
||
(9) Initializing the ACK-3D engine:
|
||
|
||
Before anything can be done with the ACK engine, it must be initialized.
|
||
This is done by creating the interface structure and passing it into the
|
||
AckInitialize() function. The following example shows one method to accomplish
|
||
this;
|
||
|
||
|
||
ACKENG ae; /* Interface structure in global memory */
|
||
|
||
int main()
|
||
{
|
||
int result;
|
||
|
||
|
||
ae.WinStartY = 0; /* Setup viewport coordinates */
|
||
ae.WinEndY = 104;
|
||
ae.WinStartX = 0;
|
||
ae.WinEndX = 319;
|
||
|
||
result = AckInitialize(&ae); /* Initialize the engine */
|
||
|
||
if (result)
|
||
{
|
||
printf("Error initializing - Code: %d\n",result);
|
||
exit(1);
|
||
}
|
||
|
||
result = AckReadMapFile(&ae,"DEMOMAP.L01"); /* Read map file */
|
||
|
||
if (result)
|
||
{
|
||
printf("Error reading map - Code: %d\n",result);
|
||
exit(1);
|
||
}
|
||
|
||
}
|
||
Example 1
|
||
|
||
|
||
The function AckInitialize() will return an error code if there was a
|
||
problem initializing the engine or a zero if successful. At this point the
|
||
ACKENG interface structure is initialized, the file "TRIG.DAT" has been
|
||
read into the various tables that ACK-3D requires.
|
||
|
||
No bitmaps have been dealt with yet so there is still more to do before
|
||
actually drawing the first POV.
|
||
|
||
No objects have been dealt with yet. The application must handle these
|
||
either on its own or by using one of the supplied ACK-3D functions.
|
||
|
||
|
||
|
||
|
||
(10) ACK-3D Bitmaps:
|
||
|
||
All bitmaps used by ACK-3D are 64 pixels wide by 64 pixels tall and are
|
||
stored in normal line by line fashion (ie. One row of 64 pixels then another
|
||
row, and so forth). Each byte represents one pixel and may have a color from
|
||
0 to 255. The engine itself requires the bitmaps to be in raw image form,
|
||
however the function AckLoadBitmap() will accept either raw IMG format or
|
||
Deluxe Paint II brush files (.BBM extensions).
|
||
|
||
|
||
(11) Loading Bitmaps:
|
||
|
||
ACK-3D provides several functions to load in bitmaps. These functions don't
|
||
have to be used and are only provided for convienence to the application. They
|
||
are as follows;
|
||
|
||
NOTE: If the application chooses to load it's own bitmaps it MUST be sure
|
||
to rotate the bitmaps 90 degrees into the column/row order needed
|
||
by the engine. Also, the function below uses Extended memory (XMS)
|
||
if available which the application may or may not decide to support.
|
||
|
||
|
||
int AckLoadBitmap(ACKENG *ae,int BitmapNumber,int BitmapType,
|
||
char *bmFileName);
|
||
|
||
where:
|
||
ae <- Interface structure
|
||
BitmapNumber <- A value from 1 to 255
|
||
BitmapType <- Either TYPE_WALL or TYPE_OBJECT
|
||
bmFileName <- Name of bitmap file to read
|
||
|
||
This is the general purpose bitmap load routine. It will read either
|
||
raw image files (.IMG) or Deluxe Paint brush files (.BBM) and place the
|
||
bitmap into the appropriate bitmap array (bMaps for walls and oMaps for
|
||
objects).
|
||
|
||
|
||
int AckLoadWall(ACKENG *ae,int WallNumber,char *bmFileName);
|
||
|
||
where:
|
||
ae <- Interface structure
|
||
WallNumber <- Value from 1 to 255
|
||
bmFileName <- Name of bitmap file to read
|
||
|
||
This routine simply calls AckLoadBitmap() with TYPE_WALL set.
|
||
|
||
|
||
int AckLoadObject(ACKENG *ae,int BmpNumber,char *bmFileName);
|
||
|
||
where:
|
||
ae <- Interface structure
|
||
BmpNumber <- Value from 1 to 255
|
||
bmFileName <- Name of bitmap file to read
|
||
|
||
This routine simply calls AckLoadBitmap() with TYPE_OBJECT set.
|
||
|
||
|
||
|
||
|
||
(12) Creating Objects:
|
||
|
||
Objects in ACK-3D can be either stationary or movable depending on the needs
|
||
of the application. Both types of object are handled exactly the same, the
|
||
stationary ones just never change location. Objects can share bitmaps if needed
|
||
and can have more than one bitmap to accomplish different tasks. If the object
|
||
has multiple bitmaps it can either stay in one spot and display the bitmaps
|
||
in sequence (this animates the object) or can use the mulitple bitmaps to show
|
||
different views of the object when the POV is walking around it.
|
||
|
||
The sequence for creating an object is as follows;
|
||
|
||
1) Load any bitmaps associated with the object.
|
||
|
||
2) Specify the object speed and any flags such as OF_PASSABLE or OF_ANIMATE.
|
||
|
||
3) Call the routine AckCreateObject() to fill-in other information in the
|
||
object structure.
|
||
|
||
Example;
|
||
|
||
int main()
|
||
{
|
||
|
||
/* Initialization done per Example 1 above */
|
||
|
||
AckLoadObject(&ae,1,"object1.bbm"); /* Load in a bitmap for object */
|
||
|
||
ae.ObjList[1].Flags |= OF_ANIMATE; /* Stay in place and animate */
|
||
ae.ObjList[1].Speed = 1; /* Speed is non-zero to activate */
|
||
AckCreateObject(&ae,1,1,nums);
|
||
|
||
|
||
}
|
||
|
||
Example 2
|
||
|
||
|
||
The example above uses an array of unsigned chars called nums[] to specify
|
||
the different views of the object. In this case there is only one so the number
|
||
of views is also passed as 1. There can be as many as MAX_VIEWS (ack3d.h)
|
||
different bitmaps assigned to one object for use as animation, etc.
|
||
|
||
|
||
|
||
(13) Using Overlays:
|
||
|
||
If the application so desires, an overlay screen can be used with ACK-3D.
|
||
This overlay will only be effective if the AckDisplayScreen() function is used,
|
||
unless the application manipulates the overlay on its own.
|
||
|
||
What an overlay does is allow a full screen picture to be used which may
|
||
overlay some of the area where walls are displayed. There could be ancient
|
||
pillars, or merely a sign that says "Demo in progress" or whatever, the whole
|
||
point to the overlay is to provide a means of displaying graphics over the top
|
||
of the viewport when displayed on the screen.
|
||
|
||
The overlay is read in like a normal bitmap, except it is full-screen in
|
||
size (320x200). It is then compiled into drawing commands that are placed in
|
||
the interface structure pointed to by OverlayBuffer. Everytime the function
|
||
AckDisplayScreen() is called, this pointer is used to draw the overlay on top
|
||
of the last POV that was built.
|
||
|
||
Overlays are optional and do not need to be used unless desired by the
|
||
application. For those who wish to process the compiled overlay themselves,
|
||
here is the format for the OverlayBuffer;
|
||
|
||
|
||
Length 2 bytes <- Length of data below (does not include offset)
|
||
Offset 2 bytes <- Offset into screen to show data
|
||
Data n bytes <- Actual data of size Length
|
||
|
||
Length,Offset,Data combinations will continue until a Length of zero
|
||
is reached, meaning no more data.
|
||
|
||
The function AckDrawOverlay(ScreenBuffer,OverlayBuffer) can be used to
|
||
place the overlay on top of any walls that were drawn. This function processes
|
||
the overlay commands described above and draws into the screen buffer. The
|
||
application can then do any additional drawing it desires before actually
|
||
displaying to the video.
|
||
|
||
|
||
(14) Moving around in ACK-3D:
|
||
|
||
Once things are initialized and the current POV is built and displayed, it
|
||
becomes time to move around in the map. This is accomplished by using the
|
||
function:
|
||
|
||
int AckMovePOV(ACKENG *ae,int Angle,int Amount);
|
||
|
||
where:
|
||
ae <- Interface structure
|
||
Angle <- Direction to move
|
||
Amount <- Amount to move
|
||
|
||
|
||
In its simplest form the function can just be called with the current angle
|
||
the POV is facing and some amount to move, such as;
|
||
|
||
AckMovePOV(&ae,ae.PlayerAngle,16);
|
||
|
||
But it can also be used to backup, with the following;
|
||
|
||
NewAngle = ae.PlayerAngle + INT_ANGLE_180;
|
||
|
||
if (NewAngle >= INT_ANGLE_360)
|
||
NewAngle -= INT_ANGLE_360;
|
||
|
||
AckMovePOV(&ae,NewAngle,16);
|
||
|
||
This function does the necessary collision detection and returns 0 if the
|
||
POV actually moved, in which case the values ae.xPlayer and ae.yPlayer have
|
||
been updated with the new coordinates of the POV.
|
||
|
||
If the application wishes to do the moving itself, it can make a call to
|
||
the function AckCheckHit() which returns 0 if no walls are in the way. Any
|
||
collisions with objects will not be returned from AckCheckHit.
|
||
|
||
|
||
|
||
(15) Moving Objects:
|
||
|
||
ACK-3D contains two functions for handling object animation and movement.
|
||
Animation is performed by switching the displayed bitmap for the object
|
||
whenever the function is called. The application is responsible for setting
|
||
up the object structure to provide the engine with the necessary information
|
||
to animate or move the object.
|
||
|
||
void AckCheckObjectMovement(ACKENG *ae);
|
||
|
||
This function will check all active objects and determine if any need
|
||
to have thier bitmaps changed for animation. The application can perform this
|
||
check itself if so desired. This function is provided for convienence.
|
||
|
||
|
||
int AckMoveObjectPOV(ACKENG *ae,int ObjIndex,int Angle,int Amount);
|
||
|
||
This function actually moves the object at the specified Angle for the
|
||
specified Amount. The return value can be processed by the application to
|
||
determine if the object has struck a wall, another object, or the player.
|
||
|
||
|
||
|
||
(16) Normal and Secret Doors:
|
||
|
||
Once ACK-3D reads in the map file, it is processed for a variety of things,
|
||
one of these being where doors will appear in the map. The application should
|
||
establish where doors will appear within the map file BEFORE calling the
|
||
function AckInitialize(). Doors are treated as special walls in the ACK engine
|
||
and will not automatically be checked to see if they should open. This is up
|
||
to the application and can be accomplished with the function;
|
||
|
||
|
||
void AckCheckDoorOpen(int xPlayer,int yPlayer,int PlayerAngle,ACKENG *ae);
|
||
|
||
where:
|
||
xPlayer <- Current x coordinate of the POV
|
||
yPlayer <- Current y coordinate of the POV
|
||
PlayerAngle <- Current angle the POV is facing
|
||
ae <- Pointer to the interface structure
|
||
|
||
What this function does is determine if the POV is close enough to either
|
||
a normal or secret door and set the appropriate information to begin opening
|
||
the door. This information is kept in the Door[] array of the interface
|
||
structure and is then used by the ACK engine during subsequent builds. After
|
||
the door has been triggered to open, the process is automatic (unless the
|
||
application manipulates the Door array, which it can if need be) until the
|
||
door goes back to fully closed.
|
||
|
||
|
||
(17) Sound and ACK-3D:
|
||
|
||
The ACK engine is being released with a public domain program from Mystic
|
||
software that allows playing of SoundBlaster .CMF and .VOC files. In addition
|
||
the files with .PWM extensions are capable of playing through the PC speaker.
|
||
|
||
Since the sound features are public domain there are some limitations that
|
||
are imposed by Mystic Software. The first is that a Terminate and Stay Resident
|
||
program (TSR) must be loaded before using the sound routines. The program is
|
||
included with the ACK demo and is called WORXLITE.EXE. This TSR need be loaded
|
||
only once when first running the demo program.
|
||
|
||
The second limitation is how the sound is used. Sound files consume alot
|
||
of memory so don't try to load several of them or large ones, they won't fit.
|
||
There also appears to be some problems with the sound routines that need to
|
||
be watched out for. On some machines it appears that playing background music
|
||
with the .CMF files will occasionally lock up the machine and require a hard
|
||
reboot to clear it. Playing the sound effect (.VOC files) alone seems to work
|
||
fine. The other problem is that .VOC files cannot be loaded after background
|
||
music is started playing, this forces all VOC files to be loaded up front which
|
||
contributes to the memory overhead mentioned earlier.
|
||
|
||
But, the sound routines are provided to allow you to experiment with them
|
||
in the 3D engine without having to write your own. The full fledged WORX
|
||
library can be purchased from Mystic Software if you wish to use it in a full
|
||
fledged shareware or commercial application.
|
||
|
||
|
||
|
||
(18) Function Summary:
|
||
|
||
Here is a list of the available ACK-3D functions and what they do.
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckInitialize(ACKENG *ae);
|
||
where:
|
||
ae <- Pointer to interface structure
|
||
|
||
Purpose:
|
||
Initializes the various arrays used by the ACK engine.
|
||
Reads and processes the file "TRIG.DAT".
|
||
Reads and processes the Map file supplied in the call.
|
||
Allocates 64000 bytes for the ScreenBuffer.
|
||
Calculates viewport items based on initial dimensions setup by the
|
||
application.
|
||
|
||
Returns:
|
||
0 if successful
|
||
One of the error codes listed in ACK3D.H
|
||
|
||
Notes:
|
||
This function must be called before any other ACK function.
|
||
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckReadMapFile(ACKENG *ae,char *MapFileName);
|
||
where:
|
||
ae <- Pointer to interface structure
|
||
MapFileName <- Name of map/object file to read
|
||
|
||
Purpose:
|
||
Reads and processes the ACK binary map file.
|
||
|
||
Returns:
|
||
0 if successful
|
||
One of the error codes listed in ACK3D.H
|
||
|
||
Notes:
|
||
Upon return the xGrid and yGrid arrays will be filled in with the
|
||
wall bitmap numbers. The application is then free to change these if it
|
||
wishes some walls to be different.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckLoadBitmap(ACKENG *ae,int BitmapNumber,int BitmapType,char *bmFileName);
|
||
where:
|
||
ae <- Pointer to interface structure
|
||
BitmapNumber <- Index number into bitmap array
|
||
BitmapType <- Either TYPE_WALL or TYPE_OBJECT
|
||
bmFileName <- Name of bitmap file to read
|
||
|
||
Purpose:
|
||
Reads in either raw (IMG) or DPII (BBM) bitmap file and processes it
|
||
into the form that ACK-3D requires.
|
||
Allocates the 4K of memory for the bitmap and stores the pointer in
|
||
either bMaps[] or oMaps[] array based on BitmapType.
|
||
|
||
Returns:
|
||
0 if successful
|
||
One of the error codes listed in ACK3D.H
|
||
|
||
Notes:
|
||
This function now uses XMS (Extended Memory) when possible to load
|
||
bitmaps.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckLoadWall(ACKENG *ae,int WallNumber,char *bmFileName);
|
||
where:
|
||
ae <- Pointer to interface structure
|
||
WallNumber <- Index number into bitmap array
|
||
bmFileName <- Name of bitmap file to read
|
||
|
||
Purpose:
|
||
Calls AckLoadBitmap() function with TYPE_WALL BitmapType set.
|
||
|
||
Returns:
|
||
0 if successful
|
||
One of the error codes listed in ACK3D.H
|
||
|
||
Notes:
|
||
This function now uses XMS (Extended Memory) when possible to load
|
||
bitmaps.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckLoadObject(ACKENG *ae,int BmpNumber,char *bmFileName);
|
||
where:
|
||
ae <- Pointer to interface structure
|
||
BmpNumber <- Index number into bitmap array
|
||
bmFileName <- Name of bitmap file to read
|
||
|
||
Purpose:
|
||
Calls AckLoadBitmap() function with TYPE_OBJECT BitmapType set.
|
||
|
||
Returns:
|
||
0 if successful
|
||
One of the error codes listed in ACK3D.H
|
||
|
||
Notes:
|
||
This function now uses XMS (Extended Memory) when possible to load
|
||
bitmaps.
|
||
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckCreateObject(ACKENG *ae,int ObjNumber,int NumBitmaps,UCHAR *bmNums);
|
||
where:
|
||
ae <- Pointer to interface structure
|
||
ObjNumber <- Index number into ObjList array (Different than bitmap
|
||
number!).
|
||
NumBitmaps <- Number of bitmap indexes contained in bmNums.
|
||
bmNums <- List of bitmap numbers associated with this object.
|
||
|
||
Purpose:
|
||
Sets up ObjList structure with information regarding the object.
|
||
Calculates the number of sides to the object if multiple bitmaps are
|
||
specified.
|
||
|
||
Returns:
|
||
0 if successful
|
||
One of the error codes listed in ACK3D.H
|
||
|
||
Notes:
|
||
None
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckCreateOverlay(ACKENG *ae, UCHAR far *OverlayScreen);
|
||
where:
|
||
ae <- Pointer to interface structure
|
||
OverlayScreen <- Pointer to 64k screen image to use.
|
||
|
||
Purpose:
|
||
Determines which part of the screen is within the viewport and compiles
|
||
this area for use by the AckDisplayScreen() function. The compiled
|
||
commands are placed in the pointer OverlayBuffer in the interface
|
||
structure.
|
||
|
||
Returns:
|
||
0 if successful
|
||
One of the error codes listed in ACK3D.H
|
||
|
||
Notes:
|
||
This function process the overlay screen passed and builds a compiled
|
||
overlay buffer (which is allocated) and returned in ae->OverlayBuffer.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckBuildBackground(ACKENG *ae);
|
||
where:
|
||
ae <- Pointer to interface structure
|
||
|
||
Purpose:
|
||
Builds a static floor and ceiling background image based on the values
|
||
of TopColor, BottomColor and LightFlag in the interface structure. The
|
||
resulting image is pointed to by BkgdBuffer in the interface structure.
|
||
|
||
Returns:
|
||
0 always
|
||
|
||
Notes:
|
||
This function builds a psuedo-shaded ceiling and floor picture that is
|
||
used for the background of the screen. The application can override this
|
||
and have its own background if desired.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckBuildView(ACKENG *ae);
|
||
where:
|
||
ae <- Pointer to interface structure
|
||
|
||
Purpose:
|
||
Constructs the current POV and places the result into ScreenBuffer
|
||
pointer in the interface structure. No displaying is done to the
|
||
screen at this time, nor has the optional overlay buffer been processed
|
||
by this function.
|
||
|
||
Returns:
|
||
0 always
|
||
|
||
Notes:
|
||
The best place to put this function is in the applications main loop
|
||
so it is repeatedly called whenever the POV moves or objects move or
|
||
animate.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
void AckDrawOverlay(UCHAR far *Screen,UCHAR far *Overlay);
|
||
where:
|
||
Screen <- Buffer to draw overlay buffer into
|
||
Overlay <- Pointer to compiled overlay image
|
||
|
||
Purpose:
|
||
Processes the compiled overlay image and draws into the screen buffer.
|
||
|
||
Notes:
|
||
This function should be called before AckDisplayScreen (or the
|
||
applications own display function) if an overlay is used.
|
||
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckDisplayScreen(ACKENG *ae);
|
||
where:
|
||
ae <- Pointer to interface structure
|
||
|
||
Purpose:
|
||
Display the last built POV onto the screen in normal VGA mode 13h.
|
||
|
||
|
||
Returns:
|
||
0 always
|
||
|
||
Notes:
|
||
If the application wishes to use an overlay it must call the function,
|
||
AckDrawOverlay() before calling AckDisplayScreen().
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
void AckCheckObjectMovement(ACKENG *ae);
|
||
where:
|
||
ae <- Pointer to interface structure
|
||
|
||
Purpose:
|
||
Scans the ObjList sub-structure of the interface structure to determine
|
||
if any objects need to be updated. This function should be called
|
||
before the AckBuildView() function.
|
||
|
||
Returns:
|
||
Nothing
|
||
|
||
Notes:
|
||
This routine is mainly good for animating objects that have multiple
|
||
bitmaps. The application should devise its own movement algorithms and
|
||
call AckMoveObjectPOV() to carry them out.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckMovePOV(ACKENG *ae,int Angle,int Amount);
|
||
where:
|
||
ae <- Pointer to interface structure
|
||
Angle <- Angle to move POV
|
||
Amount <- Amount to move POV
|
||
|
||
Purpose:
|
||
Determines if the move is valid and then sets the new coordinates into
|
||
xPlayer and yPlayer of the interface structure.
|
||
|
||
Returns:
|
||
0 if successful
|
||
1 if X wall was hit
|
||
2 if Y wall was hit
|
||
3 if an object was hit
|
||
|
||
Notes:
|
||
None
|
||
|
||
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckCheckDoorOpen(int xPlayer,int yPlayer,int PlayerAngle,ACKENG *ae);
|
||
where:
|
||
xPlayer <- Current x coordinate of the POV
|
||
yPlayer <- Current y coordinate of the POV
|
||
PlayerAngle <- Current angle POV is facing
|
||
ae <- Pointer to interface structure
|
||
|
||
Purpose:
|
||
Determines if the POV is close enough to trigger a door open. If so,
|
||
the door is placed in the Door sub-structure of the interface structure
|
||
and the opening process is begun. Subsequent calls to AckBuildView()
|
||
will automatically continue the open and close process.
|
||
|
||
Returns:
|
||
0 if no door was opened
|
||
1 if X door was opened
|
||
2 if Y door was opened
|
||
3 if X secret door was opened
|
||
4 if Y secret door was opened
|
||
|
||
Notes:
|
||
None
|
||
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckCheckHit(int xPlayer,int yPlayer,int ViewAngle,ACKENG *ae);
|
||
where:
|
||
xPlayer <- Current x coordinate of the POV
|
||
yPlayer <- Current y coordinate of the POV
|
||
ViewAngle <- Current angle POV is facing
|
||
ae <- Pointer to interface structure
|
||
|
||
Purpose:
|
||
Allows the application to determine if an obstacle is close to the POV.
|
||
|
||
Returns:
|
||
0 if nothing nearby
|
||
1 if X wall is close
|
||
2 if Y wall is close
|
||
|
||
Notes:
|
||
This function is called by AckMovePOV() and checks for collisions with
|
||
walls (not objects).
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckGetObjectHit(void);
|
||
|
||
Purpose:
|
||
Allows the application to determine the object the POV last came in
|
||
contact with.
|
||
|
||
Returns:
|
||
Returns the object index of the last object the POV hit.
|
||
|
||
Notes:
|
||
None
|
||
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckDeleteObject(ACKENG *ae,int ObjectIndex);
|
||
where:
|
||
ae <- Pointer to interface structure
|
||
ObjectIndex <- Index number of object to delete
|
||
|
||
Purpose:
|
||
Essentially sets the objects Active flag to 0 so the object is no longer
|
||
checked by the engine. Good idea to call this function for future
|
||
versions which may need to do more processing.
|
||
|
||
Returns:
|
||
-1 if object already inactive
|
||
0 if object was deleted from map
|
||
|
||
Notes:
|
||
None
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
void AckSetVGAmode(void);
|
||
|
||
Purpose:
|
||
Places the screen into standard 320x200 VGA mode 13h.
|
||
|
||
Returns:
|
||
Nothing
|
||
|
||
Notes:
|
||
This is a utility function. The application can set the screen to
|
||
graphics using its own routines if it so desires, the ACK engine does not
|
||
depend on mode 13h unless the AckDisplayScreen() function is called.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckLoadAndSetPalette(char *FileName);
|
||
where:
|
||
FileName <- Name of palette file to load
|
||
|
||
Purpose:
|
||
Reads the specified palette file then sets the palette of the VGA.
|
||
|
||
Returns:
|
||
0 if successful
|
||
One of the error codes listed in ACK3D.H
|
||
|
||
Notes:
|
||
This is a utility function to read a palette file (768 bytes) and
|
||
set the video palette to the contents of the file. The application can
|
||
use it's own function if desired or can also use AckSetPalette() with
|
||
a buffer if needed.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
void AckSetPalette(UCHAR far *PalBuffer);
|
||
where:
|
||
PalBuffer <- 768 byte buffer containing palette information
|
||
|
||
Purpose:
|
||
Sets the contents of the PalBuffer into the video palette.
|
||
|
||
Returns:
|
||
Nothing
|
||
|
||
Notes:
|
||
Use this function to set a palette that has already been read into
|
||
a buffer. Use AckLoadAndSetPalette() to read AND set a palette from a
|
||
file. This is a utility function.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
void AckFadeIn(int Begin,int Count,UCHAR far *Palette);
|
||
where:
|
||
Begin <- Starting color index to fade in
|
||
Count <- Number of sequential color indexes to fade
|
||
Palette <- Buffer containing palette information to use
|
||
|
||
Purpose:
|
||
Slowly fades the screen from black to the contents of the Palette
|
||
buffer passed.
|
||
|
||
Notes:
|
||
This is a utility function.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
void AckFadeOut(int Begin,int Count);
|
||
where:
|
||
Begin <- Starting color index to fade in
|
||
Count <- Number of sequential color indexes to fade
|
||
|
||
Purpose:
|
||
This function slowly fades the screen from the current palette to black.
|
||
|
||
Notes:
|
||
This is a utility function.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
void AckSetTextmode(void);
|
||
|
||
Purpose:
|
||
Places the screen into normal 80x25 text color mode 3.
|
||
|
||
Returns:
|
||
Nothing
|
||
|
||
Notes:
|
||
This is a utility function.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
UCHAR far *AckReadiff(char *FileName);
|
||
where:
|
||
FileName <- Name of .LBM or .BBM file to read
|
||
|
||
Purpose:
|
||
Reads in a Deluxe Paint picture (.LBM) or brush (.BBM) file, allocates
|
||
a buffer for the images and returns the buffer pointer to the caller.
|
||
The image in the buffer will contain 4 bytes at the beginning with the
|
||
width and height of the image in integer format.
|
||
This function is provided if the application wishes to read its own
|
||
images and is also needed if the overlay image is Deluxe Paint format.
|
||
|
||
Returns:
|
||
Pointer to buffer if successful
|
||
NULL if an error reading the image
|
||
|
||
Notes:
|
||
None
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckWrapUp(ACKENG *ae);
|
||
where:
|
||
ae <- Pointer to interface structure
|
||
|
||
Purpose:
|
||
Frees up memory buffers used by the ACK engine.
|
||
|
||
Returns:
|
||
0 always
|
||
|
||
Notes:
|
||
This function MUST be called before exiting the application so that
|
||
XMS memory is returned to the system.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckSoundInitialize(int DefaultSoundDevice);
|
||
where:
|
||
DefaultSoundDevice <- One of the DEV_ values in ACKSND.H
|
||
|
||
Purpose:
|
||
Starts up WORX routines and determines hardware
|
||
|
||
Returns:
|
||
0 if okay
|
||
-1 if error initializing
|
||
|
||
Notes:
|
||
This function MUST be called before the other sound routines are used.
|
||
Calling with DEV_NOSOUND will force all sound off.
|
||
Calling with DEV_PCSPEAKER will force sound through the speaker even
|
||
if a sound card is present.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckPlayBackground(char *MusicFileName);
|
||
where:
|
||
MusicFileName <- Name of .CMF file to begin playing in background.
|
||
|
||
Purpose:
|
||
Continuously plays the music file in the background.
|
||
|
||
Returns:
|
||
0 if okay
|
||
-1 if error loading file
|
||
|
||
Notes:
|
||
See text in section Sound and ACK-3D for limitations with music.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
int AckLoadSound(int VocIndex,char *VocFileName);
|
||
where:
|
||
VocIndex <- One of the SOUND_ defines in ACKSND.H
|
||
VocFileName <- Name of .VOC file to load.
|
||
|
||
Purpose:
|
||
Loads up a sound file for later playing.
|
||
|
||
Returns:
|
||
0 if okay
|
||
-1 if error loading sound
|
||
|
||
Notes:
|
||
This function will load the .VOC file if a SoundBlaster or Adlib card
|
||
is present. If the PC speaker is specified then the routine will look for
|
||
.PWM files (created with the VOC2PWM.EXE program). The application should
|
||
always call with the .VOC extent.
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
void AckPlaySound(int SoundIndex);
|
||
where:
|
||
SoundIndex <- One of SOUND_ indexes from ACKSND.H
|
||
|
||
Purpose:
|
||
Plays the specified sound effect (.VOC file)
|
||
|
||
Returns:
|
||
Nothing
|
||
|
||
Notes:
|
||
Sound file must have been previously loaded with AckLoadSound().
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
void AckStopBackground(void);
|
||
where:
|
||
Nothing
|
||
|
||
Purpose:
|
||
Stops the current background music file (.CMF)
|
||
|
||
Returns:
|
||
Nothing
|
||
|
||
Notes:
|
||
None
|
||
|
||
|
||
------------------------------------------------------------------------------
|
||
void AckSoundShutdown(void);
|
||
where:
|
||
Nothing
|
||
|
||
Purpose:
|
||
Closes down the WORX routines, freeing memory, etc.
|
||
|
||
Returns:
|
||
Nothing
|
||
|
||
Notes:
|
||
This routine MUST be called before exiting the application if sound is
|
||
being used in the ACK engine.
|
||
|
||
|
||
|
||
|
||
|
||
(19) Where to Begin: (EXAMPLES)
|
||
|
||
Okay, now that we've covered some of the details, let's look at how an
|
||
application begins to use the ACK engine. The first thing to do is to make
|
||
some decisions, such as (and these may not be related to each other);
|
||
|
||
1. Will the interface structure be in global memory, or allocated memory?
|
||
|
||
2. What size of viewport will be used?
|
||
|
||
3. Will light shading be used? Will the ceiling and floor be shaded?
|
||
|
||
4. Are objects going to be manipulated by the ACK engine, or the app?
|
||
|
||
5. Will there be an overlay screen?
|
||
|
||
|
||
Why is question 1 important? The interface structure is not small, just to
|
||
begin with there are four 8k arrays within it to hold the map data, as well as
|
||
object and door arrays, so if you plan on having alot of global data in the
|
||
application then the interface structure should be allocated from the far heap.
|
||
It doesn't really matter to the engine since a pointer to the structure is
|
||
passed in the library calls as far data.
|
||
|
||
Let's say for our example that we'll allocate the structure and hold it in
|
||
a global data pointer within the application. We could begin with something
|
||
like;
|
||
|
||
#include "ack3d.h"
|
||
|
||
ACKENG *ae; /* This is our global pointer */
|
||
|
||
/* This could be a routine that the application uses to initialize things */
|
||
int AppInitialize(void)
|
||
{
|
||
int result = 0;
|
||
|
||
ae = malloc(sizeof(ACKENG)); /* We first get memory for the structure */
|
||
|
||
if (ae == NULL) /* Whoops, we didn't get the memory */
|
||
return(-1); /* So return an error */
|
||
|
||
memset(ae,0,sizeof(ACKENG)); /* Now we clear out the entire structure */
|
||
|
||
/* Perform other initialization here */
|
||
|
||
return(result);
|
||
}
|
||
|
||
Snippet 1
|
||
|
||
|
||
This code snippet does nothing more than allocate and clear the interface
|
||
structure that will be used by the application and the ACK engine. Now we need
|
||
to setup the size of the viewport and initialize the engine;
|
||
|
||
#include "ack3d.h"
|
||
|
||
#define VIEW_X 80 /* Size of the 3D viewport */
|
||
#define VIEW_X1 240
|
||
#define VIEW_Y 40
|
||
#define VIEW_Y1 160
|
||
|
||
|
||
char *MapFileName = "LEVEL1.MAP";
|
||
|
||
|
||
int AppSetupEngine(void)
|
||
{
|
||
int result;
|
||
|
||
ae->WinStartX = VIEW_X;
|
||
ae->WinStartY = VIEW_Y; /* Plug in the size we want */
|
||
ae->WinEndX = VIEW_X1; /* for our viewport */
|
||
ae->WinEndY = VIEW_Y1;
|
||
|
||
result = AckInitialize(ae); /* Then initialize the engine! */
|
||
|
||
return(result); /* 0 if no error, else errorcode */
|
||
}
|
||
|
||
Snippet 2
|
||
|
||
|
||
Code snippet 2 sets up an arbitrary viewport size (I usually load a picture
|
||
into Deluxe Paint and write down the coordinates of where I want the 3D walls
|
||
to be), and then calls the engine to intialize. Upon return the value of result
|
||
will either be zero, meaning no problems, or one of the ERR_ codes listed in
|
||
the ACK3D.H header file. If an error occurs the application should NOT continue,
|
||
since unpredictable results WILL occur.
|
||
|
||
At this point the engine has filled in quite a bit of the ACKENG structure
|
||
with information about the viewport and the map file that was read in and
|
||
processed. A buffer is automatically allocated for the ScreenBuffer pointer in
|
||
the structure and the map arrays now contain the necessary wall and object
|
||
information to build the POV. But, we can't do that just yet. First we have
|
||
to decide on a couple more things. Lets take code snippet 2 and expand it;
|
||
|
||
|
||
#include "ack3d.h"
|
||
|
||
#define VIEW_X 80 /* Size of the 3D viewport */
|
||
#define VIEW_X1 240
|
||
#define VIEW_Y 40
|
||
#define VIEW_Y1 160
|
||
|
||
#define CEILING_COLOR 23
|
||
#define FLOOR_COLOR 27
|
||
|
||
#define DOORSPEED 4 /* Doors need a speed to open/close */
|
||
|
||
#define PLAYER_X 390 /* Initial X coordinate */
|
||
#define PLAYER_Y 260 /* Initial Y coordinate */
|
||
#define PLAYER_ANGLE 480 /* Initial POV angle */
|
||
|
||
|
||
char *MapFileName = "LEVEL1.MAP";
|
||
|
||
|
||
int AppSetupEngine(void)
|
||
{
|
||
int result;
|
||
|
||
ae->WinStartX = VIEW_X;
|
||
ae->WinStartY = VIEW_Y; /* Plug in the size we want */
|
||
ae->WinEndX = VIEW_X1; /* for our viewport */
|
||
ae->WinEndY = VIEW_Y1;
|
||
|
||
|
||
ae->xPlayer = PLAYER_X; /* Setup intial coordinates */
|
||
ae->yPlayer = PLAYER_Y; /* for the POV */
|
||
ae->PlayerAngle = PLAYER_ANGLE;
|
||
ae->DoorSpeed = DOORSPEED; /* Set a default door speed */
|
||
|
||
result = AckInitialize(ae); /* Then initialize the engine! */
|
||
|
||
if (result)
|
||
return(result); /* Error, so get out now */
|
||
|
||
result = AckReadMapFile(ae,MapFileName);
|
||
if (result)
|
||
return(result);
|
||
|
||
ae->TopColor = CEILING_COLOR; /* Setup our colors for the */
|
||
ae->BottomColor = FLOOR_COLOR; /* background.... */
|
||
|
||
ae->LightFlag = SHADING_ON; /* Yes, we want light shading */
|
||
result = AckBuildBackground(ae); /* Build the ceiling, floor */
|
||
|
||
if (result)
|
||
return(result); /* Error, so get out now */
|
||
|
||
return(result); /* 0 if no error, else errorcode */
|
||
}
|
||
|
||
Snippet 3
|
||
|
||
|
||
|
||
Code snippet 3 adds to snippet 2 by setting up the background ceiling and
|
||
floor that the ACK engine requires. In the example above we decided to shade
|
||
the background. By using SHADING_OFF (both found in ACK3D.H) we could also
|
||
decide to have a solid background. Note the two defines for ceiling and floor
|
||
color, these can be any color from 0 to 255, whatever is appropriate for the
|
||
applications.
|
||
|
||
Also note that the initial coordinates and angle of the POV are setup here
|
||
but do not need to be. These can be deferred until the call to AckBuildView()
|
||
is made. They are done here merely for convienence.
|
||
|
||
|
||
Okay, things are coming along nicely. Now we need to decide if an overlay
|
||
is needed or not. Remember an overlay is only need if there will be some part
|
||
of the full screen that will always display over the 3D walls. An example
|
||
would be gothic pillars, or perhaps magic staffs that border the full screen
|
||
display. Whatever the application needs to give the visual effect.
|
||
|
||
An overlay is not mandatory and does induce a slight speed degradation when
|
||
displaying the engine. It also means the function AckDisplayScreen() must be
|
||
called to actually use the overlay (unless the application has it's own
|
||
routine to handle it).
|
||
|
||
Let's say we want an overlay, this is what we would do;
|
||
|
||
|
||
#include "ack3d.h"
|
||
|
||
|
||
char *PictureFile = "MYPIC.LBM";
|
||
|
||
|
||
int AppSetupOverlay(void)
|
||
{
|
||
int result = 0;
|
||
UCHAR far *OverlayPic;
|
||
|
||
OverlayPic = AckReadiff(PictureFile); /* Load a Deluxe Paint picture */
|
||
|
||
if (OverlayPic == NULL) /* Whoops, got a problem */
|
||
return(-1); /* So return with an error */
|
||
|
||
|
||
result = AckCreateOverlay(ae,&OverlayPic[4]); /* Compile the overlay */
|
||
|
||
free(OverlayPic); /* Free up the picture unless */
|
||
/* we want to use it later */
|
||
|
||
return(result);
|
||
}
|
||
|
||
Snippet 4
|
||
|
||
|
||
This example reads in a Deluxe Paint LBM file and calls the ACK engine to
|
||
compile it. The resulting overlay sequence will be in the interface structure
|
||
pointed to by OverlayBuffer (unless an error occurs).
|
||
|
||
One change to this code snippet would be to keep the picture buffer around
|
||
so it can be displayed on the screen. Note, only the portion which will cover
|
||
the 3D viewport will be compiled, NOT the entire picture.
|
||
|
||
The application can use the supplied LBM read routine if it wishes, or read
|
||
in the picture on its own, as long as the picture buffer passed to the routine
|
||
is a flat 320 by 200 (64000 byte) image that the overlay section can be snipped
|
||
out of.
|
||
|
||
The AckReadiff() routine places the width and height of the image in the
|
||
first four bytes of the buffer, which is why &OverlayPic[4] was passed to
|
||
the engine.
|
||
|
||
Again remember, the overlay is optional and only needs to be used if part
|
||
of the screen is going to appear over the walls.
|
||
|
||
|
||
By now we've setup quite a bit of the information we'll need to actually
|
||
draw a 3D view of our map, but things will look pretty bad if we try to draw
|
||
at this point, we don't have any bitmaps yet! Let's proceed with what is needed
|
||
to get some walls into our application;
|
||
|
||
|
||
#include "ack3d.h"
|
||
|
||
|
||
typedef struct {
|
||
int Number;
|
||
int Type;
|
||
char *Name;
|
||
} BMTABLE;
|
||
|
||
|
||
BMTABLE bmTable[] = {
|
||
1 ,TYPE_WALL ,"swall1.bbm",
|
||
2 ,TYPE_WALL ,"swall2.bbm",
|
||
3 ,TYPE_WALL ,"swall3.bbm",
|
||
4 ,TYPE_WALL ,"swall4.bbm",
|
||
5 ,TYPE_WALL ,"swall5.bbm",
|
||
58 ,TYPE_WALL ,"secret.bbm",
|
||
59 ,TYPE_WALL ,"secret.bbm",
|
||
60 ,TYPE_WALL ,"sdoor.bbm",
|
||
61 ,TYPE_WALL ,"sside.bbm",
|
||
62 ,TYPE_WALL ,"sdoor.bbm",
|
||
1 ,TYPE_OBJECT ,"eyeball.bbm",
|
||
2 ,TYPE_OBJECT ,"treasure.bbm",
|
||
-1 ,-1 ,"" /* End of table */
|
||
};
|
||
|
||
|
||
int AppLoadBitmaps(void)
|
||
{
|
||
int result;
|
||
int i = 0;
|
||
|
||
while (bmTable[i].Number != -1)
|
||
{
|
||
result = AckLoadBitmap(ae,
|
||
bmTable[i].Number,
|
||
bmTable[i].Type,
|
||
bmTable[i].Name);
|
||
|
||
if (result) /* Error during load */
|
||
break; /* so get out now */
|
||
|
||
i++; /* Next index in table */
|
||
}
|
||
|
||
|
||
return(result);
|
||
}
|
||
|
||
Snippet 5
|
||
|
||
|
||
What code snippet 5 does is loop through a table and load all the bitmaps
|
||
for this example application (the bitmap names are arbitrary). Should an error
|
||
occur the routine exits immediately and returns the error to the caller. Note
|
||
also that this same routine can be used to load objects as well as walls, just
|
||
use TYPE_OBJECT instead of TYPE_WALL (defined in ACK3D.H) for the objects.
|
||
|
||
Upon return from code snippet 5 we'll have all the bitmaps loaded that are
|
||
needed to begin. If no objects are going to be used then we can proceed, but
|
||
for example purposes let's say we have the two objects loaded in snippet 5 to
|
||
setup. We do this by calling the function AckCreateObject() as in the following
|
||
example;
|
||
|
||
|
||
#include "ack3d.h"
|
||
|
||
|
||
int AppSetupObjects(void)
|
||
{
|
||
int result;
|
||
UCHAR BitmapNumbers[2];
|
||
|
||
ae->ObjList[1].Dir = 0; /* Direction doesn't matter */
|
||
ae->ObjList[1].Speed = 0; /* is a stationary object */
|
||
|
||
BitmapNumbers[0] = 1; /* Bitmap to use with object (eyeball) */
|
||
|
||
result = AckCreateObject(ae,1,1,BitmapNumbers);
|
||
|
||
if (result) /* An error occurred */
|
||
return(result); /* so get out now */
|
||
|
||
ae->ObjList[2].Dir = 0; /* Again a direction is irrelavent */
|
||
ae->ObjList[2].Speed = 0; /* Because speed 0 is stationary */
|
||
|
||
BitmapNumbers[0] = 2; /* Bitmap to use (treasure) */
|
||
|
||
result = AckCreateObject(ae,2,1,BitmapNumbers);
|
||
|
||
return(result);
|
||
}
|
||
|
||
Snippet 6
|
||
|
||
|
||
Snippet 6 is a brute force method of creating the objects. A more elegant
|
||
method would be to setup a table to create the objects, similiar to what we
|
||
did in snippet 5 with the bitmaps.
|
||
|
||
|
||
|
||
At this point we've got the engine initialized, a background buffer built,
|
||
bitmaps loaded, and objects created. We're not even in graphics mode yet! The
|
||
ACK engine provides some support functions that may be used (unless the
|
||
application has its own), to setup graphics mode and later go back to text
|
||
mode. These functions are;
|
||
|
||
|
||
AckSetVGAmode(); <- Sets video into mode 13h (320x200 w/ 256 colors)
|
||
|
||
AckSetTextmode(); <- Sets video into mode 3 (80x25 16 color text)
|
||
|
||
|
||
Use these if desired, they are just thrown in as support routines. Another
|
||
routine can also be used, this one reads in a palette file and sets up the
|
||
VGA palette registers;
|
||
|
||
|
||
AckLoadAndSetPalette( FileName ); <- Reads a palette file and sets regs.
|
||
|
||
|
||
This routine may be used AFTER the video is placed in graphics mode. Pass
|
||
the name of a 768 byte palette file to use. Upon return the new palette will
|
||
be set. Let's put these routines into a code snippet;
|
||
|
||
|
||
#include "ack3d.h"
|
||
|
||
|
||
char *PalFile = "DEMO.PAL";
|
||
|
||
|
||
|
||
int AppSetGraphics(void)
|
||
{
|
||
int result;
|
||
|
||
|
||
AckSetVGAmode(); /* Go into graphics */
|
||
|
||
result = AckLoadAndSetPalette(PalFile);
|
||
|
||
return(result);
|
||
}
|
||
|
||
|
||
Snippet 7
|
||
|
||
|
||
|
||
|
||
Okay, we've got the beginnings of a 3D game! As part of this introduction
|
||
we need to be concerned with only a couple more things. Beyond that it becomes
|
||
the applications responsibility to handle user interaction with the engine.
|
||
|
||
Once we're ready to begin displaying the POV on the screen we need to make
|
||
one more mandatory call to the engine to tell it to build the current scene. The
|
||
code snippet below shows this process (for now we'll assume the application is
|
||
going to let the ACK engine perform the actual display);
|
||
|
||
|
||
|
||
#include "ack3d.h"
|
||
|
||
|
||
void AppShow3D(void)
|
||
{
|
||
|
||
/* Any preprocessing the application wishes to do can go here */
|
||
|
||
AckBuildView(ae); /* Tell the ACK engine to construct the POV */
|
||
|
||
AckDisplayScreen(ae); /* Display the POV on the video screen */
|
||
|
||
|
||
}
|
||
|
||
Snippet 8
|
||
|
||
|
||
|
||
Remember that the initial coordinates of the POV were setup in code snippet
|
||
number 3 above. They can easily be setup anytime before calling AckBuildView()
|
||
if the application so decides.
|
||
|
||
|
||
|
||
|
||
It's high time we put all these code snippets together into a running
|
||
example program. This is shown below;
|
||
|
||
|
||
|
||
#include "ack3d.h"
|
||
|
||
#define VIEW_X 80 /* Size of the 3D viewport */
|
||
#define VIEW_X1 240
|
||
#define VIEW_Y 40
|
||
#define VIEW_Y1 160
|
||
|
||
#define CEILING_COLOR 23
|
||
#define FLOOR_COLOR 27
|
||
|
||
|
||
#define PLAYER_X 390 /* Initial X coordinate */
|
||
#define PLAYER_Y 260 /* Initial Y coordinate */
|
||
#define PLAYER_ANGLE 480 /* Initial POV angle */
|
||
|
||
|
||
char *MapFileName = "LEVEL1.MAP";
|
||
char *PictureFile = "MYPIC.LBM";
|
||
char *PalFile = "DEMO.PAL";
|
||
|
||
typedef struct {
|
||
int Number;
|
||
int Type;
|
||
char *Name;
|
||
} BMTABLE;
|
||
|
||
|
||
BMTABLE bmTable[] = {
|
||
1 ,TYPE_WALL ,"swall1.bbm",
|
||
2 ,TYPE_WALL ,"swall2.bbm",
|
||
3 ,TYPE_WALL ,"swall3.bbm",
|
||
4 ,TYPE_WALL ,"swall4.bbm",
|
||
5 ,TYPE_WALL ,"swall5.bbm",
|
||
58 ,TYPE_WALL ,"secret.bbm",
|
||
59 ,TYPE_WALL ,"secret.bbm",
|
||
60 ,TYPE_WALL ,"sdoor.bbm",
|
||
61 ,TYPE_WALL ,"sside.bbm",
|
||
62 ,TYPE_WALL ,"sdoor.bbm",
|
||
1 ,TYPE_OBJECT ,"eyeball.bbm",
|
||
2 ,TYPE_OBJECT ,"treasure.bbm",
|
||
-1 ,-1 ,"" /* End of table */
|
||
};
|
||
|
||
|
||
/* Prototypes */
|
||
int AppInitialize(void);
|
||
int AppSetupEngine(void);
|
||
int AppSetupOverlay(void);
|
||
int AppLoadBitmaps(void);
|
||
int AppSetupObjects(void);
|
||
int AppSetGraphics(void);
|
||
void AppShow3D(void);
|
||
|
||
|
||
/* Entry point for application */
|
||
|
||
int main(void)
|
||
{
|
||
int result,done = 0;
|
||
|
||
result = AppInitialize();
|
||
|
||
if (result)
|
||
{
|
||
printf("Error initializing: ErrorCode = %d\n",result);
|
||
return(1);
|
||
}
|
||
|
||
|
||
result = AppSetupEngine();
|
||
|
||
if (result)
|
||
{
|
||
printf("Error setting up ACK engine: ErrorCode = %d\n",result);
|
||
return(1);
|
||
}
|
||
|
||
|
||
result = AppSetupOverlay();
|
||
|
||
if (result)
|
||
{
|
||
printf("Error loading overlay: ErrorCode = %d\n",result);
|
||
return(1);
|
||
}
|
||
|
||
|
||
result = AppLoadBitmaps();
|
||
|
||
if (result)
|
||
{
|
||
printf("Error loading bitmaps: ErrorCode = %d\n",result);
|
||
return(1);
|
||
}
|
||
|
||
|
||
result = AppSetupObjects();
|
||
|
||
if (result)
|
||
{
|
||
printf("Error creating objects: ErrorCode = %d\n",result);
|
||
return(1);
|
||
}
|
||
|
||
|
||
|
||
result = AppSetGraphics();
|
||
|
||
if (result)
|
||
{
|
||
AckSetTextmode();
|
||
printf("Error loading palette: ErrorCode = %d\n",result);
|
||
return(1);
|
||
}
|
||
|
||
|
||
|
||
while (!done)
|
||
{
|
||
AppShow3D();
|
||
|
||
|
||
if (getch() == 27) /* Check for the escape key */
|
||
break;
|
||
|
||
}
|
||
|
||
|
||
|
||
AckSetTextmode();
|
||
|
||
return(0);
|
||
}
|
||
|
||
|
||
Example 3
|
||
|
||
|
||
|
||
|
||
(20) Closing comments:
|
||
|
||
And here I said at the start of this document that writing docs is too
|
||
much like work! Anyway, I hope the information provided above, as well as the
|
||
actual source to the ACK engine, is enough to get you started on your own
|
||
3D adventure. It's been a very exciting project and I've met alot of nice folks
|
||
who have pitched in and helped in many ways. I wish to express thanks to all
|
||
of you who made this possible and advanced thanks to all of you who may use the
|
||
engine to produce more games for all of us to enjoy.
|
||
|
||
Those who have been of GREAT help are;
|
||
|
||
Jaimi McEntire who helped a great deal with programming and graphics.
|
||
Jaimi also did the neat space station picture in the title!
|
||
|
||
Ken Lemieux who provided the space dudes and shuttle!
|
||
|
||
Steve Salter who did ALOT of the wall and object graphics!
|
||
|
||
Frank Sachse who provided the sound routine interface and music!
|
||
|
||
Ron Sachse who provided some of the wall bitmaps!
|
||
|
||
Mark Betz who allowed me to use his fading routines!
|
||
|
||
Bart Stewart who built an image editor for this thing!
|
||
|
||
Michael Wilson who's XMS code was readily available and is working
|
||
great!
|
||
|
||
Thanks guys! Without you the ACK engine and demo would not have been
|
||
possible!
|
||
|
||
Now some bad news: There is currently a problem with displaying objects that
|
||
sometimes causes "ghosts" images to be displayed. While this does not cause any
|
||
damage to anything it is very annoying and I haven't been able to track down the
|
||
cause. If anyone has any suggestions or ideas I'd appreciate hearing them and
|
||
building an update for others. Thanks!
|
||
|
||
One last note: This will be my final installment of ACK3D for awhile. I'm
|
||
not going to drop out of the picture totally but I've got some new things that
|
||
I'm eager to try out! Maybe some more goodies will come out of it, one can
|
||
never tell.
|
||
|
||
If you wish to reach me on CompuServe I'm usually hanging around the Game
|
||
Design library of The Gamers forum, or you can email me direct. My CompuServe
|
||
account is 72355,655.
|
||
|
||
Sincerely,
|
||
Lary Myers
|
||
|
||
|
||
|