========================================================================= JV Enterprises is proud to release to Freeware a modified version of the Towers II graphic engine. Written by: Vince Valenti (c)1993-1996 JV Enterprises Ver. 1.1 You may use this engine in any application/game that you are making, free of charge, as long as you give credit to Vince and JV Enterprises in the code and manual, and you only include the Object file in your program. The credits in the code can be included in the Title screen, Information box, or where credit is given to the programmer(s) for the application/game that is using this engine. You may make any change to the source, but you may not distribute any modified source code as part of this archive, and you may not charge for the source or object code, modified or unmodified. PD houses may charge the nominal fee for the disc and shipping. Okay Now that's out of the way... ENJOY... ------------------------------------------------------------------------- This Archive comes with four files. Falc_eng.obj This is the file you will include in you GFA, C, or Assembly program. Falc_eng.is This is the tokenized version of the source for GFA Assembly Falc_eng.asm This is an ASCII version of the source for GFA assembly. Falc_eng.txt This file. :) The Falcon Engine is a 2D and 3D graphic engine with a modified bubble sort routine, and a Y axis Rotate and X,Z,Y Translate routine. All the routines are designed to be called from GFA Basic. However, you can call them from assembly or C. All calls are passed via the stack. In assembly you must reverse the order in which you place data into the stack. ie... ~C:code_falc_eng%(L:a&,L:a%,W:b&) would be passed to assembly as: move.w word_b,-(sp) move.l long_a,-(sp) move.w word_a,d0 ext.l d0 move.l d0,-(sp) jsr code_falc_eng add.w #10,sp ------------------------------------------------------------------------- This code was never meant to be released as an engine, so there might be some examples that look strange, sorry. :) You can call all the routines via the ~C: command in GFA basic. Please make sure that if you see (L:a&) that you include the 'L:' and that you pass a WORD Value 'xxxx&' through. Several routines do not handle values larger than a word an can produce unpredicable results if the value is larger. Routines that can handle longwords will look like this: (L:a%) With the ~C: command you may not pass ~C:asm%+28(...), so all the examples will include this: stuff%=asm%+28 ~C:stuff%(...) Remember that you only need to define stuff%, for this example, at the beginning of you code, and you don't have to define it every time you call stuff%. ========================================================================= ------------------------------------------------------------------------- Here is the list of routines provided with the Falcon Engine. ; --- General purpose routines --- aseven ;+28 Jump to find out stack value mouse_hand ;+32 Jump for mouse handler ; --- 2D TC block routines --- rput ;+36 jump to replace put rputp ;+40 Jump to replace put part tput ;+44 jump to trans put typut ;+48 jump to trans 2X put w/ y-clip blkget ;+52 jump to block get backput ;+56 Jump for background put ; --- TC full screen routines --- clear ;+60 jump to clear screen fade ;+64 jump to fade (log to phy) routine fade2 ;+68 Jump for fade effects (log to log) ; --- 3D scale and T-map routines --- scaleput ;+72 jump to trans scale put wallput ;+76 jump to wall only texture map mapput ;+80 jump to texture map ; --- 3D rotate and sort routines --- sound_trans ;+84 Jump to sound translate y_rot_trans ;+88 jump to rotate and translate zsort ;+92 jump to z-sort w/offset calc ; --- Misc config variables --- brite: WORD ;+96 FADE variable 0=off (def. 0) y_value1: WORD ;100 max y (def. 200) y_value2: WORD ;102 max y-1 (def. 199) To use the Falcon engine in GFA basic, you can include with the basic code like so: INLINE code_start%,10118 Then Press HELP with the cursor on that line. You will get a menu at the top of the screen. Click on load and find FALC_ENG.OBJ, then click on OK. The file will now be included with every SAVE of your GFA Basic code. Make sure you don't SAVE,A as an ASCII save will not save INLINEs. There will be several example variables that will be used... ret% Return Variable from call addr_block% Starting location of 2D block graphic w/header WORD{addr_block%} = X WORD{addr_block%+2} = Y WORD{addr_block%+4}... = graphic data You can use BLKGET to get a 2D graphic in this format into memory addr_screen% Starting address of either the logical or physical screen. addr_log% Starting address of the logical screen addr_phy% Starting address of the physical screen degrees& Angle that player is facing must be 0-359 ONLY! addr_mem% Any buffer, including block, screen or variable. Variables and Arrays for 3D transformation and sorting functions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - addr_vect% starting address of the vector table array [ie. DIM vectors&(2,M) ] 0=x 1=y 2=z M is the max number of vectors you will have copied at any given time. num_of_vect& Is the current number of vectors you want to rotate in this game loop addr_lookup% starting address of the lookup table array [ie. DIM lookup&(3,M2) ] R/W 0=Graphic Value (<256 4vect , =>256 2vect) R 1=Vect offset in bytes (set by Zsort) R/W 2=Misc value (extra) R 3=Ave Z value (set by Zsort) M2 is the max number of Objects you will have copied at any given time. addr_groff% starting address of the graphic block array. [ie. DIM graph_offset%(M2) ] M2 is the max number of Objects, same as lookup&(). num_of_obj& Is the current number of Objects you want to manipulate in this game loop - - - - - - - - - - - - - - - When making a logical or physical screen please make sure to pad it for clipping errors. (sorry) Leave about 2000 bytes above and below the actual start of the screen address (log or phy) DIM log%(33000) addr_log%=V:log%(0)+2048 This sets up a 132000 byte buffer with appx 2000 byte pads on both sides. ------------------------------------------------------- General Purpose Routines ------------------------------------------------------- A-Seven routine - - - - - - - - code_aseven%=code_start%+28 ret%=C:code_aseven%() This routine returns the stack pointer in the ret% variable. Mouse Handler routine - - - - - - - - - - - code_mouse_hand%=code_start%+32 ret%=C:code_mouse_hand%() This routine returns the starting address for the data and interrupt routine if you ever wish to use this in place of the standard routine. This routine will disable any mouse accelerator, and can slow the mouse down even more if you wish. WORD{ret%} = Mouse Button State (1=rht 2=lft 3=bth) WORD{ret%+2} = X value WORD{ret%+4} = Y value WORD{ret%+6} = Slowdown value (0=norn 1=1/2 2=1/4) ret%+8 = start of the interrupt handler routine This routine will replace the GEM mouse handler. You can install it with: ret%=C:code_mouse_hand%() addr_mouse%=XBIOS(34) ADD addr_mouse%,16 old_mouse%=LPEEK(addr_mouse%) SLPOKE (addr_mouse%),ret%+8 Before you exit your program you must restore the mouse with: SLPOKE (addr_mouse%),old_mouse% To read the mouse just use: a&=WORD{ret%+2} this will give you the X screen coords. for the mouse. This handler does NOT do any clipping, and will count from -30000 to +30000, you will have to make sure the mouse does not go passed 320 or get smaller than 0, by poking directly to it like: IF WORD{ret%+2}<0 THEN WORD{ret%+2}=0 ------------------------------------------------------- 2D True Color Block routines ------------------------------------------------------- Replace Put routine - - - - - - - - - - code_rput%=code_start%+36 ~C:code_rput%(L:x&,L:y&,L:addr_block%,L:addr_screen%) This routine places a 2D graphic on the screen at X,Y. This routine does NOT do any clipping and can write anywhere in memory! Replace Put Part routine - - - - - - - - - - - - - code_rputp%=code_start%+40 ~C:code_rputp%(L:x&,L:y&,L:addr_block%,L:addr_screen%,W:part&) This routine places a 2D graphic on the screen at X,Y. It will draw the graphic from X to X+part&. This routine would be good for a bar graphic where the bar drops slowly as your health drops or something. :) Other than part&, this routine has NO clipping. Transparent Put routine - - - - - - - - - - - - code_tput%=code_start%+44 ~C:code_tput%(L:x&,L:y&,L:addr_block%,L:addr_screen%) This routine places a 2D graphic on the screen at X,Y. Any block pixel equaling '0' will not be drawn and hence transparent. This routine has NO clipping. Transparent 2X Put routine w/Y clipping [effected by: y_value(1&2)] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - code_typut%=code_start%+48 ~C:code_typut%(L:x&,L:y&,L:addr_block%,L:addr_screen%) This routine places a 2D graphic on the screen at X,Y. works like Transparent Put, plus it doubles the width of the graphic on the screen. Also, any value greater than Y_VALUE(1&2) is not drawn. It also clips at X>319. Block Get routine - - - - - - - - - code_blkget%=code_start%+52 ~C:code_blkget%(L:x&,L:y&,W:xl&,W:yl&,L:addr_block%,L:addr_screen%) This routine captures a 2D graphic from X,Y to X+xl,Y+yl, and puts it into the header starting at ADDR_BLOCK%. xl& will be rounded down to the next even number. (eg. 3 to 2, 7 to 6) Background Put routine - - - - - - - - - - - - code_backput%=code_start%+56 ~C:code_backput%(W:degrees&,W:y&,L:addr_block%,L:addr_screen%) This wraps at 2D graphic that is 320x???, and puts it onto the screen at 0,Y. The graphic MUST be 320 X. It will roll the graphic based on what degrees& is. This routine does NOT clip. ------------------------------------------------------- True Color Full Screen Routines ------------------------------------------------------- Clear Memory/Screen Routine - - - - - - - - - - - - - - code_clear%=code_start%+60 ~C:code_clear%(L:addr_mem%,L:num_of_byte%) Puts zero for x number of bytes. Rounds down num_of_byte% to the nearest unit of 64. Make sure you have ENOUGH MEMORY in your buffer, or you may overwrite memory! Fade from Logical to Physical [effected by: y_value(1&2)] - - - - - - - - - - - - - - - code_fade%=code_start%+64 ~C:code_fade%(L:addr_log%,L:addr_phy%,W:mode&) This routine fade graphic data from the logical to the physical address. From black to graphic, mode& is positive (0 to 4). From graphic to black, mode& is negative (0 to -4). Any Y greater than Y_VALUE will not be touched. Effects Fade on Screen [effected by: y_value(1&2)] - - - - - - - - - - - - code_fade2%=code_start%+68 ~C:code_fade2%(L:addr_screen%,W:fade&,W:mode&) This routine washes any graphic currently on the screen to several colors. fade& increases the wash. mode& sets the color. mode&=0 wash to black (fade& 0-4) mode&=1 wash to white (fade& 4-0) mode&=2 wash to red " " mode&=3 wash to green " " mode&=4 wash to blue " " Any screen location lower than the Y value set in Y_VALUE(1&2) will not be touched. ------------------------------------------------------- 3D True Color Scale and Texture Map routines ------------------------------------------------------- Transparent Scaled Put routine [effected by: y_value(1&2),brite] - - - - - - - - - - - - - - - - code_scaleput%=code_start%+72 ~C:code_scaleput%(L:bcx&,L:by&,L:ty&,L:addr_block%,L:addr_screen%) This routine scales a 2D graphic a puts it on the screen at BCX,BY (bottom-center-X, bottom-Y). Unlike the other 2D routines, this routine does not start at the top-left corner of the screen, but on the bottom-center. This makes it easy in 3D environments to find the floor of the graphic when drawn. tcx,ty (1) | | bcx,by (2) It handles ALL clipping from 0-320 X and 0-y_value Y. If BRITE is set to something other than 0, fading to black will occur when the graphic gets smaller. TY, the top-Y position, is used as a scale value, so the routine knows when to shrink or enlarge the graphic. This will only handle graphics that are 16, 32, or 64 pixels wide. Textured Wall Map routine [effected by: y_value(1&2),brite] - - - - - - - - - - - - - code_wallput%=code_start%+76 ~C:wallput%(L:x1&,L:x3&,L:y2&,L:y4&,L:y1&,L:y3&,L:addr_block%, L:addr_screen%,W:door&) This routine takes a 2D graphic and shapes it into the coords. given. This routine is for walls only, that is x1 & x2 are the same and x3 & x4 are the same. 3 1 1 /| |\ /| 1/ | | \3 3/ | | | | |Normal | | Backfaced 2\ | | /4 4| | \|4 2|/ \|2 The routines then fills this area with the texture at ADDR_BLOCK%. It handles all clipping from 0-320 X and 0-y_value Y. If BRITE is anything other than 0, the graphic fades to black as it gets smaller. DOOR& is used to simulate an open door. if DOOR&=0 the full graphic is drawn. As DOOR& grows to 64 it becomes less, and less visible leaving a space open, like a sliding door. This routine will only handle 2D graphics that are 64x64. If in the 2D graphic header you replace the XL (WORD{addr_block%}=0) from 64 to 0, this routine will make that graphic transparent, and any pixel on the graphic that is black(0) will not be drawn leaving a hole. This is great if you want to simulate a window or gate in your texture. If at any time x3 < x1 that is called backfacing, and this routine will not draw it. Texture Map routine [effected by: y_value(1&2)] - - - - - - - - - - code_mapput%=code_start%+80 ~C:code_mapput%(W:x1&,W:y1&,W:x2&,W:y2&,W:x3&,W:y3&,W:x4&, W:y4&,L:addr_block%,L:addr_screen%) This is the slowest of the 3D routines, but it can texture any CONVEX 4 vector polygon. 1 4 /\1 \\ // / \ \\3/ Concave 2\ /3 Convex \/ \/ 2 4 It handles all Clipping from 0-320 X and 0-y_value Y. It only works with 64x64 2D graphics. ------------------------------------------------------- 3D Rotate and Sort routines ------------------------------------------------------- I was not going to include this, but sorting and rotating in GFA or C can be slow. So I inlcuded the sort, transform and rotate commands. NOTE: These routines were designed for Objects with the vectors 256 units away or closer. May produce strange results when ave Z < 0 and the vectors are >256 units apart. (These terms will be explained later) Sound Rotate - - - - - - - code_sound_trans%=code_start%+88 ret%=C:code_sound_trans%(W:x&,W:z&,W:degrees&,W:xs&,W:zs&) This routine modifies xs&,zs& and returns it rotated and translated in ret%. The hiword in ret% is X and the loword in ret% is Z. Great for creating 3D or stereo sound. Y Rotate and XYZ translate routine - - - - - - - - - - - - - - - - - - code_y_rot_trans%=code_start%+92 ~C:code_y_rot_trans%(L:x&,L:z&,W:num_of_vect&,W:degrees&, L:addr_vect%,W:y_off&,W:y&) WOW! Lots of stuff here! This routine, Y-axis rotates, and translates each vector in an array that starts at addr_vect&, and then converts it to U & V screen coord. Don't worry about the names U and V, they just mean a 2D X & Y. The names of the variables change so you don't confuse X in the 3D coord. system with X in the 2D coord. system. This is the only place I will use U&V, all the previous routines use the 2D X and Y. x&,y&, and z& is your absolute position in the 3D world. addr_vect% is the starting address of an array set up in GFA. The array must be a word value array with any number of max vectors DIM vectors&(2,M) ! M=max vectors The array conatains absolute positions (X,Y,Z) of each vector up to num_of_vect&. The routine then converts all the vectors and changes them to (U,V,RZ). The RZ (relative Z) is preserved so that we can Z-sort later. The values U and V can then be taken directly to the 3D drawing routines as X and Y. NOTE: You will need either 2 or 4 sets of U,V (ie. vectors) depending on what 3D routine you use. The Wall Map and Texture Map require 4 vectors, while the Scale Put only requires 2 vectors. Y_OFF& is used to offset the horizon on the screen, Usually the horizon is at y=0, but thats at the top of the screen, increase this number to drop the horizon. (I used 39) Y& in Towers II was set to 80. If is was set to 0, you would be hitting the ceiling and 300 or so and you would be crawling on the ground. Make sure the that array vectors&() is not your master vector table. Copy only the vectors you know can be displayed on the screen from the master vector table. If you use vectors&() as the master vector table, it will start to corrupt after several calculations. That is becuase rounding errors add up and corrupts the data. Also, don't copy the whole table. It takes an exponential amount of time to rotate and sort. If you have a real big 3D world you can't see all of it! Don't copy the vectors you know you can't see. 2/4 Vector Z-average and Z-sort routine - - - - - - - - - - - - - - - - - - - - code_zsort%=code_start%+92 ~C:code_zsort%(L:addr_lookup%,L:addr_vect%,L:addr_groff%, W:num_of_obj&) This one is just a difficult to explain as the Rotate routine. You should always run this routine AFTER you run the Rotate routine above. The routine works with the vectors&() array in the previous example, and first calculates an offset and an ave. z-value, and puts it into the lookup&() array. Then it Z-sorts by the ave. z-value and reorganizes according to the Z-value, lookup&() and graph_offset%() arrays. DIM graph_offset%(M2),lookup&(3,M2) !M2 = max number of objects DIM vectors&(2,M) ! M = max number of vectors (same as ! previous example) addr_vect%=V:vectors&(0,0) addr_lookup%=V:lookup&(0,0) addr_groff%=V:graph_offset%(0) Before you run either this routine or the previous one, you need to copy the vectors and data into these arrays. Depending if you have a 2-Vector Object or a 4-Vector Object, you add the information in. ADDR_LOOKUP% is the starting address of lookup&() array. the lookup&() array is arranged like so: R/W=Read/Write R=Read Only R/W 0=Graphic Value (<256 4vect , =>256 2vect) R 1=Vect offset in bytes (set by Zsort) R/W 2=Misc value (extra) R 3=Ave Z value (set by Zsort) You only have to enter data in (0,x) and (2,x). You can add anything you like in (2,x), but in (0,x) you need to keep to the following guidelines. (0,x) is the Graphic Value, if your object is a Wall or Texture then you need 4 vectors and the Graphic Value (0,x) must be 0-255. If the object is a Scale graphic then the Graphic Value (0,x) must be 256 or greater. NOTE: Do this before you run Zsort or the Rotate routines! 2 Vector Object example 4 Vector Object example - - - - - - - - - - - - - - - - - - - - - - - - vector&(0,count&)=x1 vector&(0,count&)=x1 vector&(1,count&)=y1 vector&(1,count&)=y1 vector&(2,count&)=z1 vector&(2,count&)=z1 vector&(0,count&+1)=x2 vector&(0,count&+1)=x2 vector&(1,count&+1)=y2 vector&(1,count&+1)=y2 vector&(2,count&+1)=z2 vector&(2,count&+1)=z2 lookup&(0,obj_cnt&)=300 vector&(0,count&+2)=x3 lookup&(2,obj_cnt&)=RANDOM(100) vector&(1,count&+2)=y3 graph_offset%(obj_cnt&)=addr_block% vector&(2,count&+2)=z3 ADD count&,2 INC obj_cnt& vector&(0,count&+3)=x4 vector&(1,count&+3)=y4 vector&(2,count&+3)=z4 lookup&(0,obj_cnt&)=5 lookup&(2,obj_cnt&)=RANDOM(100) graph_offset%(obj_cnt&)=addr_block% ADD count&,4 INC obj_cnt& Needless to say, that copying vectors and objects this way will not produce the fastest code, but this is just an example. You could easily BMOVE the data or use {addr_vect%} instead. But you get the idea. I ended up creating an assembly routine just to do all of this, but Towers II was set up kinda strange. :) When Making List make sure the order is correct, a 2 vector list should look like this: 1 | | 2 While a 4 vector list should be ordered like this: 1 +----+ 3 | | 2 +----+ 4 The RANDOM command was used to demonstrate that it does not matter what the number is, and the object number '5' could have been anything under 256. Also, graph_offset%(), as seen in the example, is the starting address of the texture map that you want hooked to this object. Once you have this set up, run the Rotate routine to rotate the world, then run this routine. After you have done so, you will notice that lookup&(1,x) is filled with a number. That number is the start of the vector list for this object. It is represented in bytes. To find the first value, just add it to addr_vect%. x1=WORD{addr_vect%+lookup&(1,x)} or x1=vectors&(0,lookup&(1,x) DIV 6) Also, lookup&(3,x) will also be filled. That is the average Z Value for that object. The larger the number the further away from you the object is. As you run down the list of objects you will notice that the Z-value gets smaller and smaller. If the average Z value is smaller than -49, then DONT draw it since it's behind you. ------------------------------------------------------- Misc. Config Variables ------------------------------------------------------- brite: WORD - - - - - - - - - - addr_brite%=code_start%+96 WORD{addr_brite%}=x This sets the global variable BRITE for any routine effected by this variable. This variable sets the fade routine On or OFF. x=0 OFF (Default) x=-1 to -4 Start fading closer x= 1 to 4 Start fading further y_value1: WORD Default = 200 y_value2: WORD Default = 199 - - - - - - - - - - addr_y_value%=code_start%+100 WORD{addr_y_value%}=x WORD{addr_y_value%+2}=x-1 This sets the global variable Y_VALUE1 and Y_VALUE2 for any routine effected by this variable. This changes the clipping value of Y. Y_VALUE2 MUST be one less than Y_VALUE1 or unpredictable results may occur. Lets say you are making a game in 320x200 TC. And you want the last 40 scan lines for information. You can set Y_value1 to 160 and Y_value2 to 159. From this point all routines effected by these variables will clip at 160-199 and only draw from 0-159.