1 /* Catacomb Apocalypse Source Code
\r
2 * Copyright (C) 1993-2014 Flat Rock Software
\r
4 * This program is free software; you can redistribute it and/or modify
\r
5 * it under the terms of the GNU General Public License as published by
\r
6 * the Free Software Foundation; either version 2 of the License, or
\r
7 * (at your option) any later version.
\r
9 * This program is distributed in the hope that it will be useful,
\r
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
12 * GNU General Public License for more details.
\r
14 * You should have received a copy of the GNU General Public License along
\r
15 * with this program; if not, write to the Free Software Foundation, Inc.,
\r
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
24 //#define DRAWEACH // draw walls one at a time for debugging
\r
27 unsigned mostwalls,numwalls;
\r
30 =============================================================================
\r
34 =============================================================================
\r
37 #define PI 3.141592657
\r
38 #define ANGLEQUAD (ANGLES/4)
\r
42 #define FINEANGLES 3600
\r
47 const unsigned MAXSCALEHEIGHT = (VIEWWIDTH/2);
\r
48 const unsigned MAXVISHEIGHT = (VIEWHEIGHT/2);
\r
49 const unsigned BASESCALE = 32;
\r
52 =============================================================================
\r
56 =============================================================================
\r
60 // calculate location of screens in video memory so they have the
\r
61 // maximum possible distance seperating them (for scaling overflow)
\r
64 unsigned screenloc[3]= {PAGE1START,PAGE2START,PAGE3START};
\r
65 unsigned freelatch = FREESTART;
\r
69 long scaleshapecalll;
\r
70 long scaletablecall;
\r
73 =============================================================================
\r
77 =============================================================================
\r
80 long bytecount,endcount; // for profiling
\r
82 int pixelangle[VIEWWIDTH];
\r
83 int far finetangent[FINEANGLES+1];
\r
85 unsigned viewxpix,viewypix;
\r
88 ============================================================================
\r
92 ============================================================================
\r
95 fixed tileglobal = TILEGLOBAL;
\r
96 fixed focallength = FOCALLENGTH;
\r
97 fixed mindist = MINDIST;
\r
98 int viewheight = VIEWHEIGHT;
\r
102 tilept tile,lasttile, // tile of wall being followed
\r
103 focal, // focal point in tiles
\r
104 left,mid,right; // rightmost tile in view
\r
108 int segstart[VIEWHEIGHT], // addline tracks line segment and draws
\r
109 segend[VIEWHEIGHT],
\r
110 segcolor[VIEWHEIGHT]; // only when the color changes
\r
113 walltype walls[MAXWALLS],*leftwall,*rightwall;
\r
116 //==========================================================================
\r
124 long lasttimecount;
\r
130 int firstangle,lastangle;
\r
134 fixed sintable[ANGLES+ANGLES/4],*costable = sintable+(ANGLES/4);
\r
136 fixed viewx,viewy; // the focal point
\r
138 fixed viewsin,viewcos;
\r
140 int zbuffer[VIEWXH+1]; // holds the height of the wall at that point
\r
142 //==========================================================================
\r
144 void DrawLine (int xl, int xh, int y,int color);
\r
145 void DrawWall (walltype *wallptr);
\r
146 void TraceRay (unsigned angle);
\r
147 fixed FixedByFrac (fixed a, fixed b);
\r
148 fixed FixedAdd (void);
\r
149 fixed TransformX (fixed gx, fixed gy);
\r
150 int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max);
\r
151 int BackTrace (int finish);
\r
152 void ForwardTrace (void);
\r
153 int TurnClockwise (void);
\r
154 int TurnCounterClockwise (void);
\r
155 void FollowWall (void);
\r
157 void NewScene (void);
\r
158 void BuildTables (void);
\r
160 //==========================================================================
\r
169 = Must be in write mode 2 with all planes enabled
\r
170 = The bit mask is left set to the end value, so clear it after all lines are
\r
173 = draws a black dot at the left edge of the line
\r
178 unsigned static char dotmask[8] = {0x80,0x40,0x20,0x10,8,4,2,1};
\r
179 unsigned static char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1};
\r
180 unsigned static char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff};
\r
182 void DrawLine (int xl, int xh, int y,int color)
\r
184 unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;
\r
190 Quit("DrawLine: xh<xl");
\r
192 Quit("DrawLine: y<VIEWY");
\r
194 Quit("DrawLine: y>VIEWYH");
\r
197 maskleft = leftmask[xlp];
\r
198 maskright = rightmask[xh&7];
\r
201 dest = bufferofs+ylookup[y]+xlb;
\r
204 // set the GC index register to point to the bit mask register
\r
206 asm mov al,GC_BITMASK
\r
207 asm mov dx,GC_INDEX
\r
213 // entire line is in one byte
\r
216 maskleft&=maskright;
\r
218 asm mov es,[screenseg]
\r
220 asm mov dx,GC_INDEX+1
\r
222 asm mov al,[BYTE PTR maskleft]
\r
223 asm out dx,al // mask off pixels
\r
225 asm mov al,[BYTE PTR color]
\r
226 asm xchg al,[es:di] // load latches and write pixels
\r
231 asm mov es,[screenseg]
\r
233 asm mov dx,GC_INDEX+1
\r
234 asm mov bh,[BYTE PTR color]
\r
239 asm mov al,[BYTE PTR maskleft]
\r
240 asm out dx,al // mask off pixels
\r
243 asm xchg al,[es:di] // load latches and write pixels
\r
250 asm out dx,al // no masking
\r
259 asm mov al,[BYTE PTR maskright]
\r
260 asm out dx,al // mask off pixels
\r
261 asm xchg bh,[es:di] // load latches and write pixels
\r
265 //==========================================================================
\r
267 void DrawLineDot (int xl, int xh, int y,int color)
\r
269 unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;
\r
275 Quit("DrawLine: xh<xl");
\r
277 Quit("DrawLine: y<VIEWY");
\r
279 Quit("DrawLine: y>VIEWYH");
\r
282 maskdot = dotmask[xlp];
\r
283 maskleft = leftmask[xlp];
\r
284 maskright = rightmask[xh&7];
\r
287 dest = bufferofs+ylookup[y]+xlb;
\r
290 // set the GC index register to point to the bit mask register
\r
292 asm mov al,GC_BITMASK
\r
293 asm mov dx,GC_INDEX
\r
299 // entire line is in one byte
\r
302 maskleft&=maskright;
\r
304 asm mov es,[screenseg]
\r
306 asm mov dx,GC_INDEX+1
\r
308 asm mov al,[BYTE PTR maskleft]
\r
309 asm out dx,al // mask off pixels
\r
311 asm mov al,[BYTE PTR color]
\r
312 asm xchg al,[es:di] // load latches and write pixels
\r
316 // write the black dot at the start
\r
318 asm mov al,[BYTE PTR maskdot]
\r
319 asm out dx,al // mask off pixels
\r
322 asm xchg al,[es:di] // load latches and write pixels
\r
328 asm mov es,[screenseg]
\r
330 asm mov dx,GC_INDEX+1
\r
331 asm mov bh,[BYTE PTR color]
\r
336 asm mov al,[BYTE PTR maskleft]
\r
337 asm out dx,al // mask off pixels
\r
340 asm xchg al,[es:di] // load latches and write pixels
\r
343 // write the black dot at the start
\r
345 asm mov al,[BYTE PTR maskdot]
\r
346 asm out dx,al // mask off pixels
\r
348 asm xchg al,[es:di] // load latches and write pixels
\r
355 asm out dx,al // no masking
\r
364 asm mov al,[BYTE PTR maskright]
\r
365 asm out dx,al // mask off pixels
\r
366 asm xchg bh,[es:di] // load latches and write pixels
\r
372 //==========================================================================
\r
375 long wallscalesource;
\r
379 ====================
\r
383 ====================
\r
386 void near ScaleOneWall (int xl, int xh)
\r
388 int x,pixwidth,height;
\r
390 *(((unsigned *)&wallscalesource)+1) = wallseg[xl];
\r
392 for (x=xl;x<=xh;x+=pixwidth)
\r
394 height = wallheight[x];
\r
395 pixwidth = wallwidth[x];
\r
396 (unsigned)wallscalesource = wallofs[x];
\r
398 *(((unsigned *)&scaletablecall)+1) = (unsigned)scaledirectory[height];
\r
399 (unsigned)scaletablecall = scaledirectory[height]->codeofs[0];
\r
402 // scale a byte wide strip of wall
\r
408 asm shr di,1 // X in bytes
\r
409 asm add di,[bufferofs]
\r
414 asm add bx,[pixwidth] // bx = pixel*8+pixwidth-1
\r
416 asm mov al,BYTE PTR [bitmasks1+bx]
\r
417 asm mov dx,GC_INDEX+1
\r
418 asm out dx,al // set bit mask register
\r
419 asm mov es,[screenseg]
\r
420 asm lds si,[wallscalesource]
\r
421 asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels
\r
423 asm mov al,BYTE PTR [ss:bitmasks2+bx]
\r
428 // draw a second byte for vertical strips that cross two bytes
\r
431 asm out dx,al // set bit mask register
\r
432 asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels
\r
441 char wall_anim_pos[NUMFLOORS];
\r
443 // EAST / WEST WALLS
\r
445 int far walllight1[NUMFLOORS] = {0,
\r
447 0,//CRYSTAL1LIGHTPIC,
\r
448 0,//EGYPT1LIGHTPIC,
\r
463 TEMPLEWALLLIGHTPIC,
\r
474 BRNFLGWINDOWLIGHTPIC,
\r
475 BRNFLGVINELIGHTPIC,
\r
491 0,//SPACE9LIGHTPIC,
\r
492 0,//SPACEDMG9LIGHTPIC,
\r
516 DEMONSTATUELIGHTPIC,
\r
518 0,//CRYSTALBWALL1LIGHTPIC,
\r
522 TROLLSTATUELIGHTPIC,
\r
524 BRNDMGVINELIGHTPIC,
\r
532 WATER_EXP_WALL_1PIC,
\r
533 WATER_EXP_WALL_2PIC,
\r
534 WATER_EXP_WALL_3PIC,
\r
539 TROLLBLOODYLIGHTPIC,
\r
542 0, // INVISIBLE WALL
\r
556 0, // STAIRDWNLIGHTPIC,
\r
564 MAS_VINE1_LIGHTPIC,
\r
565 MAS_VINE2_LIGHTPIC,
\r
567 // Start of non-solid walls
\r
579 // NORTH / SOUTH WALLS
\r
581 int far walldark1[NUMFLOORS] = {0,
\r
583 0,//CRYSTAL1DARKPIC,
\r
609 BRNFLGWINDOWDARKPIC,
\r
627 0,//SPACEDMG9DARKPIC,
\r
651 DEMONSTATUEDARKPIC,
\r
653 0,//CRYSTALBWALL1DARKPIC,
\r
657 TROLLSTATUEDARKPIC,
\r
668 WATER_EXP_WALL_1PIC,
\r
669 WATER_EXP_WALL_2PIC,
\r
670 WATER_EXP_WALL_3PIC,
\r
675 TROLLBLOODYDARKPIC,
\r
679 0, // INVISIBLE WALL
\r
704 // Start of non-solid walls
\r
718 =====================
\r
722 = Draws a wall by vertical segments, for texture mapping!
\r
724 = wallptr->side is true for east/west walls (constant x)
\r
726 = fracheight and fracstep are 16.16 bit fractions
\r
728 =====================
\r
731 void DrawVWall (walltype *wallptr)
\r
735 unsigned width,sourceint;
\r
736 unsigned wallpic,wallpicseg;
\r
738 long fracheight,fracstep,longheightchange;
\r
741 unsigned slope,distance;
\r
742 int traceangle,angle;
\r
744 unsigned lastpix,lastsource,lastwidth;
\r
746 if (wallptr->rightclip < wallptr->leftclip)
\r
747 Quit ("DrawVWall: Right < Left");
\r
750 // setup for height calculation
\r
752 wallptr->height1 >>= 1;
\r
753 wallptr->height2 >>= 1;
\r
754 wallptr->planecoord>>=10; // remove non significant bits
\r
756 width = wallptr->x2 - wallptr->x1;
\r
759 heightchange = wallptr->height2 - wallptr->height1;
\r
760 asm mov ax,[heightchange]
\r
761 asm mov WORD PTR [longheightchange+2],ax
\r
762 asm mov WORD PTR [longheightchange],0 // avoid long shift by 16
\r
763 fracstep = longheightchange/width;
\r
766 fracheight = ((long)wallptr->height1<<16)+0x8000;
\r
767 skip = wallptr->leftclip - wallptr->x1;
\r
769 fracheight += fracstep*skip;
\r
772 // setup for texture mapping
\r
774 // mapadd is 64*64 (to keep source positive) + the origin wall intercept
\r
775 // distance has 6 unit bits, and 6 frac bits
\r
776 // traceangle is the center view angle in FINEANGLES, moved to be in
\r
777 // the +-90 degree range (to thew right of origin)
\r
779 traceangle = fineviewangle;
\r
781 // find wall picture to map from
\r
784 { // east or west wall
\r
786 wallpic = walllight1[wallptr->color+wall_anim_pos[wallptr->color]];
\r
787 if (wallptr->planecoord < viewxpix)
\r
789 distance = viewxpix-wallptr->planecoord;
\r
790 traceangle -= FINEANGLES/2;
\r
791 mapadd = (64-viewypix&63); // the pixel spot of the origin
\r
795 distance = wallptr->planecoord-viewxpix;
\r
796 // traceangle is correct
\r
797 mapadd = viewypix&63; // the pixel spot of the origin
\r
801 { // north or south wall
\r
803 wallpic = walldark1[wallptr->color+wall_anim_pos[wallptr->color]];
\r
804 if (wallptr->planecoord < viewypix)
\r
806 distance = viewypix-wallptr->planecoord;
\r
807 traceangle -= FINEANGLES/4;
\r
808 mapadd = viewxpix&63; // the pixel spot of the origin
\r
812 distance = wallptr->planecoord-viewypix;
\r
813 traceangle -= FINEANGLES*3/4;
\r
814 mapadd = (64-viewxpix&63); // the pixel spot of the origin
\r
818 mapadd = 64*64-mapadd; // make sure it stays positive
\r
820 wallpicseg = (unsigned)walldirectory[wallpic-FIRSTWALLPIC];
\r
821 if (traceangle > FINEANGLES/2)
\r
822 traceangle -= FINEANGLES;
\r
825 // calculate everything
\r
827 // IMPORTANT! This loop is executed around 5000 times / second!
\r
829 lastpix = lastsource = (unsigned)-1;
\r
831 for (x = wallptr->leftclip ; x <= wallptr->rightclip ; x++)
\r
836 asm mov ax,WORD PTR [fracheight]
\r
837 asm mov dx,WORD PTR [fracheight+2]
\r
839 asm add ax,WORD PTR [fracstep]
\r
840 asm adc dx,WORD PTR [fracstep+2]
\r
841 asm mov WORD PTR [fracheight],ax
\r
842 asm mov WORD PTR [fracheight+2],dx
\r
845 asm cmp cx,MAXSCALEHEIGHT
\r
846 asm jbe storeheight
\r
847 asm mov cx,MAXSCALEHEIGHT
\r
849 asm mov WORD PTR [wallheight+bx],cx
\r
850 asm mov WORD PTR [zbuffer+bx],cx
\r
852 // height = fracheight>>16;
\r
853 // fracheight += fracstep;
\r
854 // if (height > MAXSCALEHEIGHT)
\r
855 // height = MAXSCALEHEIGHT;
\r
856 // wallheight[x] = zbuffer[x] = height;
\r
861 angle = pixelangle[x]+traceangle;
\r
865 slope = finetangent[angle];
\r
868 // distance is an unsigned 6.6 bit number (12 pixel bits)
\r
869 // slope is a signed 5.10 bit number
\r
870 // result is a signed 11.16 bit number
\r
874 source = distance*slope;
\r
878 source &= 63; // mask off the unused units
\r
879 source = 63-source;
\r
880 source <<= 6; // multiply by 64 for offset into pic
\r
882 asm mov ax,[distance]
\r
883 asm imul [slope] // ax is the source pixel
\r
886 asm shr al,1 // low 6 bits is now pixel number
\r
887 asm add ax,[mapadd]
\r
890 asm sub dx,ax // otherwise it is backwards
\r
896 asm shl dx,1 // *64 to index into shape
\r
897 asm mov [source],dx
\r
899 if (source != lastsource)
\r
901 if (lastpix != (unsigned)-1)
\r
903 wallofs[lastpix] = lastsource;
\r
904 wallseg[lastpix] = wallpicseg;
\r
905 wallwidth[lastpix] = lastwidth;
\r
908 lastsource = source;
\r
912 lastwidth++; // optimized draw, same map as last one
\r
914 wallofs[lastpix] = lastsource;
\r
915 wallseg[lastpix] = wallpicseg;
\r
916 wallwidth[lastpix] = lastwidth;
\r
920 //==========================================================================
\r
928 = Used to find the left and rightmost tile in the view area to be traced from
\r
929 = Follows a ray of the given angle from viewx,viewy in the global map until
\r
930 = it hits a solid tile
\r
932 = tile.x,tile.y : tile coordinates of contacted tile
\r
933 = tilecolor : solid tile's color
\r
940 void TraceRay (unsigned angle)
\r
942 long tracex,tracey,tracexstep,traceystep,searchx,searchy;
\r
944 int otx,oty,searchsteps;
\r
946 tracexstep = costable[angle];
\r
947 traceystep = sintable[angle];
\r
950 // advance point so it is even with the view plane before we start checking
\r
952 fixtemp = FixedByFrac(prestep,tracexstep);
\r
953 tracex = viewx+fixtemp;
\r
954 fixtemp = FixedByFrac(prestep,traceystep);
\r
955 tracey = viewy-fixtemp;
\r
957 tile.x = tracex>>TILESHIFT; // starting point in tiles
\r
958 tile.y = tracey>>TILESHIFT;
\r
961 if (tracexstep<0) // use 2's complement, not signed magnitude
\r
962 tracexstep = -(tracexstep&0x7fffffff);
\r
964 if (traceystep<0) // use 2's complement, not signed magnitude
\r
965 traceystep = -(traceystep&0x7fffffff);
\r
968 // we assume viewx,viewy is not inside a solid tile, so go ahead one step
\r
971 do // until a solid tile is hit
\r
975 spotvis[otx][oty] = true;
\r
976 tracex += tracexstep;
\r
977 tracey -= traceystep;
\r
978 tile.x = tracex>>TILESHIFT;
\r
979 tile.y = tracey>>TILESHIFT;
\r
981 if (tile.x!=otx && tile.y!=oty && (tilemap[otx][tile.y] || tilemap[tile.x][oty]) )
\r
984 // trace crossed two solid tiles, so do a binary search along the line
\r
985 // to find a spot where only one tile edge is crossed
\r
988 searchx = tracexstep;
\r
989 searchy = traceystep;
\r
994 if (tile.x!=otx && tile.y!=oty)
\r
1002 // not far enough, no tiles crossed
\r
1003 tracex += searchx;
\r
1004 tracey -= searchy;
\r
1008 // if it is REAL close, go for the most clockwise intersection
\r
1010 if (++searchsteps == 16)
\r
1012 tracex = (long)otx<<TILESHIFT;
\r
1013 tracey = (long)oty<<TILESHIFT;
\r
1018 tracex += TILEGLOBAL-1;
\r
1019 tracey += TILEGLOBAL;
\r
1023 tracex += TILEGLOBAL;
\r
1031 tracey += TILEGLOBAL-1;
\r
1040 tile.x = tracex>>TILESHIFT;
\r
1041 tile.y = tracey>>TILESHIFT;
\r
1043 } while (( tile.x!=otx && tile.y!=oty) || (tile.x==otx && tile.y==oty) );
\r
1045 } while (!(tilecolor = tilemap[tile.x][tile.y]) );
\r
1049 //==========================================================================
\r
1053 ========================
\r
1057 = multiply a 16/16 bit, 2's complement fixed point number by a 16 bit
\r
1058 = fraction, passed as a signed magnitude 32 bit number
\r
1060 ========================
\r
1063 #pragma warn -rvl // I stick the return value in with ASMs
\r
1065 fixed FixedByFrac (fixed a, fixed b)
\r
1072 asm mov si,[WORD PTR b+2] // sign of result = sign of fraction
\r
1074 asm mov ax,[WORD PTR a]
\r
1075 asm mov cx,[WORD PTR a+2]
\r
1078 asm jns aok: // negative?
\r
1083 asm xor si,0x8000 // toggle sign of result
\r
1087 // multiply cx:ax by bx
\r
1089 asm mov bx,[WORD PTR b]
\r
1090 asm mul bx // fraction*fraction
\r
1091 asm mov di,dx // di is low word of result
\r
1093 asm mul bx // units*fraction
\r
1098 // put result dx:ax in 2's complement
\r
1100 asm test si,0x8000 // is the result negative?
\r
1115 =========================
\r
1119 = add two 16 bit fixed point numbers
\r
1120 = to subtract, invert the sign of B before invoking
\r
1122 =========================
\r
1125 fixed FixedAdd (fixed a, fixed b)
\r
1129 asm mov ax,[WORD PTR a]
\r
1130 asm mov dx,[WORD PTR a+2]
\r
1132 asm mov bx,[WORD PTR b]
\r
1133 asm mov cx,[WORD PTR b+2]
\r
1136 asm jns aok: // negative?
\r
1138 asm not ax // convert a from signed magnitude to 2's compl
\r
1145 asm jns bok: // negative?
\r
1147 asm not bx // convert b from signed magnitude to 2's compl
\r
1153 asm add ax,bx // perform the addition
\r
1157 asm and dx,0x7fff // value was negative
\r
1158 asm not ax // back to signed magnitude
\r
1165 asm mov [WORD PTR value],ax
\r
1166 asm mov [WORD PTR value+2],dx
\r
1172 //==========================================================================
\r
1176 ========================
\r
1180 = Takes paramaters:
\r
1181 = gx,gy : globalx/globaly of point
\r
1184 = viewx,viewy : point of view
\r
1185 = viewcos,viewsin : sin/cos of viewangle
\r
1189 = CENTERX : pixel location of center of view window
\r
1190 = TILEGLOBAL : size of one
\r
1191 = FOCALLENGTH : distance behind viewx/y for center of projection
\r
1192 = scale : conversion from global value to screen value
\r
1195 = screenx,screenheight: projected edge location and size
\r
1197 ========================
\r
1200 void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight)
\r
1203 fixed gxt,gyt,nx,ny;
\r
1206 // translate point to view centered coordinates
\r
1214 gxt = FixedByFrac(gx,viewcos);
\r
1215 gyt = FixedByFrac(gy,viewsin);
\r
1221 gxt = FixedByFrac(gx,viewsin);
\r
1222 gyt = FixedByFrac(gy,viewcos);
\r
1226 // calculate perspective ratio
\r
1231 ratio = nx*scale/FOCALLENGTH;
\r
1233 if (ratio<=MINRATIO)
\r
1236 *screenx = CENTERX + ny/ratio;
\r
1238 *screenheight = TILEGLOBAL/ratio;
\r
1244 // transform actor
\r
1246 void TransformActor (objtype *ob)
\r
1249 fixed gx,gy,gxt,gyt,nx,ny;
\r
1252 // translate point to view centered coordinates
\r
1260 gxt = FixedByFrac(gx,viewcos);
\r
1261 gyt = FixedByFrac(gy,viewsin);
\r
1262 nx = gxt-gyt-ob->size;
\r
1267 gxt = FixedByFrac(gx,viewsin);
\r
1268 gyt = FixedByFrac(gy,viewcos);
\r
1272 // calculate perspective ratio
\r
1277 ratio = nx*scale/FOCALLENGTH;
\r
1279 if (ratio<=MINRATIO)
\r
1282 ob->viewx = CENTERX + ny/ratio;
\r
1284 ob->viewheight = TILEGLOBAL/ratio;
\r
1287 //==========================================================================
\r
1289 fixed TransformX (fixed gx, fixed gy)
\r
1292 fixed gxt,gyt,nx,ny;
\r
1295 // translate point to view centered coordinates
\r
1303 gxt = FixedByFrac(gx,viewcos);
\r
1304 gyt = FixedByFrac(gy,viewsin);
\r
1309 //==========================================================================
\r
1312 ==================
\r
1318 = scale projection constant
\r
1319 = sintable/costable overlapping fractional tables
\r
1320 = firstangle/lastangle angles from focalpoint to left/right view edges
\r
1321 = prestep distance from focal point before checking for tiles
\r
1323 ==================
\r
1326 void BuildTables (void)
\r
1331 float angle,anglestep,radtoint;
\r
1336 // calculate the angle offset from view angle of each pixel's ray
\r
1338 radtoint = (float)FINEANGLES/2/PI;
\r
1339 for (i=0;i<VIEWWIDTH/2;i++)
\r
1341 // start 1/2 pixel over, so viewangle bisects two middle pixels
\r
1342 x = (TILEGLOBAL*i+TILEGLOBAL/2)/VIEWWIDTH;
\r
1343 tang = (float)x/(FOCALLENGTH+MINDIST);
\r
1344 angle = atan(tang);
\r
1345 intang = angle*radtoint;
\r
1346 pixelangle[VIEWWIDTH/2-1-i] = intang;
\r
1347 pixelangle[VIEWWIDTH/2+i] = -intang;
\r
1351 // calculate fine tangents
\r
1352 // 1 sign bit, 5 units (clipped to), 10 fracs
\r
1354 #define MININT (-MAXINT)
\r
1356 for (i=0;i<FINEANGLES/4;i++)
\r
1358 intang = tan(i/radtoint)*(1l<<10);
\r
1361 // if the tangent is not reprentable in this many bits, bound the
\r
1362 // units part ONLY
\r
1364 if (intang>MAXINT)
\r
1365 intang = 0x8f00 | (intang & 0xff);
\r
1366 else if (intang<MININT)
\r
1367 intang = 0xff00 | (intang & 0xff);
\r
1369 finetangent[i] = intang;
\r
1370 // finetangent[FINEANGLES/2+i] = intang;
\r
1371 // finetangent[FINEANGLES/2-i-1] = -intang;
\r
1372 finetangent[FINEANGLES-i-1] = -intang;
\r
1376 // calculate scale value so one tile at mindist allmost fills the view horizontally
\r
1378 scale = GLOBAL1/VIEWWIDTH;
\r
1379 scale *= focallength;
\r
1380 scale /= (focallength+mindist);
\r
1383 // costable overlays sintable with a quarter phase shift
\r
1384 // ANGLES is assumed to be divisable by four
\r
1386 // The low word of the value is the fraction, the high bit is the sign bit,
\r
1387 // bits 16-30 should be 0
\r
1391 anglestep = PI/2/ANGLEQUAD;
\r
1392 for (i=0;i<=ANGLEQUAD;i++)
\r
1394 value=GLOBAL1*sin(angle);
\r
1396 sintable[i+ANGLES]=
\r
1397 sintable[ANGLES/2-i] = value;
\r
1398 sintable[ANGLES-i]=
\r
1399 sintable[ANGLES/2+i] = value | 0x80000000l;
\r
1400 angle += anglestep;
\r
1404 // figure trace angles for first and last pixel on screen
\r
1406 angle = atan((float)VIEWWIDTH/2*scale/FOCALLENGTH);
\r
1407 angle *= ANGLES/(PI*2);
\r
1409 intang = (int)angle+1;
\r
1410 firstangle = intang;
\r
1411 lastangle = -intang;
\r
1413 prestep = GLOBAL1*((float)FOCALLENGTH/costable[firstangle]);
\r
1418 walls[0].x2 = VIEWX-1;
\r
1419 walls[0].height2 = 32000;
\r
1423 //==========================================================================
\r
1426 =====================
\r
1430 =====================
\r
1433 void ClearScreen (void)
\r
1435 unsigned topcolor=*skycolor, bottomcolor=*groundcolor;
\r
1436 unsigned topimage=topcolor&0xf0,bottomimage=bottomcolor&0xf0;
\r
1437 unsigned pfoffset=0;
\r
1441 if (topimage == 0x20) // special code for lightning
\r
1442 topimage = topcolor = 0;
\r
1444 // Manually wipe screen with solid color.
\r
1445 // If BOTH sky and ground are 'images' don't manually clear it!
\r
1447 if ((!topimage) || (!bottomimage))
\r
1452 // clear the screen
\r
1454 asm mov dx,GC_INDEX
\r
1455 asm mov ax,GC_MODE + 256*2 // read mode 0, write mode 2
\r
1457 asm mov ax,GC_BITMASK + 255*256
\r
1460 //asm mov dx,40-VIEWWIDTH/8 // dx = modulo
\r
1461 asm mov bl,VIEWWIDTH/16
\r
1462 asm mov bh,CENTERY+1
\r
1464 asm mov ax,topcolor
\r
1465 asm mov es,[screenseg]
\r
1466 asm mov di,[bufferofs]
\r
1467 asm add di,((SCREENWIDTH*VIEWY)+(VIEWX/8))
\r
1473 //asm add di,dx // no need to add "0" modulo
\r
1477 asm mov bh,CENTERY+1
\r
1478 asm mov ax,bottomcolor
\r
1484 //asm add di,dx // no need to add "0" modulo
\r
1486 asm jnz bottomloop
\r
1493 // code to test parallax turning
\r
1499 pfoffset = LONG_PERCENTAGE(3200,359,(359-player->angle),12);
\r
1500 while (pfoffset >= 640)
\r
1502 LatchDrawPicStrip(0,0,SKY1PIC+topimage,pfoffset+8);
\r
1507 //// pfoffset = LONG_PERCENTAGE(3200,359,(359-player->angle),12)+320;
\r
1508 // pfoffset += 320;
\r
1509 // while (pfoffset >= 640)
\r
1510 // pfoffset -= 640;
\r
1511 // LatchDrawPicStrip(0,64,SKY1PIC+topimage,pfoffset+8);
\r
1512 bottomimage -= 16;
\r
1513 LatchDrawPic(0,64,GND1PIC+bottomimage);
\r
1518 asm mov dx,GC_INDEX
\r
1519 asm mov ax,GC_MODE + 256*10 // read mode 1, write mode 2
\r
1521 asm mov al,GC_BITMASK
\r
1526 //==========================================================================
\r
1529 =====================
\r
1533 = Clips and draws all the walls traced this refresh
\r
1535 =====================
\r
1538 void DrawWallList (void)
\r
1540 int i,leftx,newleft,rightclip;
\r
1541 walltype *wall, *check;
\r
1545 asm mov di,OFFSET wallwidth
\r
1547 asm mov cx,VIEWWIDTH/2
\r
1552 rightwall->x1 = VIEWXH+1;
\r
1553 rightwall->height1 = 32000;
\r
1554 (rightwall+1)->x1 = 32000;
\r
1558 for (wall=&walls[1];wall<rightwall && leftx<=VIEWXH ;wall++)
\r
1560 if (leftx >= wall->x2)
\r
1563 rightclip = wall->x2;
\r
1566 while (check->x1 <= rightclip && check->height1 >= wall->height2)
\r
1568 rightclip = check->x1-1;
\r
1572 if (rightclip>VIEWXH)
\r
1575 if (leftx < wall->x1 - 1)
\r
1576 newleft = wall->x1-1; // there was black space between walls
\r
1580 if (rightclip > newleft)
\r
1582 wall->leftclip = newleft+1;
\r
1583 wall->rightclip = rightclip;
\r
1585 leftx = rightclip;
\r
1590 ScaleWalls (); // draw all the walls
\r
1594 //==========================================================================
\r
1597 =====================
\r
1601 = Draws all objects that are visable
\r
1603 =====================
\r
1606 objtype *depthsort[MAXACTORS];
\r
1608 void DrawScaleds (void)
\r
1610 #if USE_INERT_LIST
\r
1611 extern inertobjtype inertobjlist[], *inert;
\r
1613 boolean inertlist=false;
\r
1615 int i,j,least,numvisable,height;
\r
1616 objtype *obj,**vislist,*farthest;
\r
1618 byte *tilespot,*visspot;
\r
1623 // calculate base positions of all objects
\r
1625 vislist = &depthsort[0];
\r
1627 obj = player->next;
\r
1630 tilespot = &tilemap[0][0]+(obj->tilex<<6)+obj->tiley;
\r
1631 visspot = &spotvis[0][0]+(obj->tilex<<6)+obj->tiley;
\r
1633 // could be in any of the nine surrounding tiles
\r
1636 || ( *(visspot-1) && !*(tilespot-1) )
\r
1637 || ( *(visspot+1) && !*(tilespot+1) )
\r
1638 || ( *(visspot-65) && !*(tilespot-65) )
\r
1639 || ( *(visspot-64) && !*(tilespot-64) )
\r
1640 || ( *(visspot-63) && !*(tilespot-63) )
\r
1641 || ( *(visspot+65) && !*(tilespot+65) )
\r
1642 || ( *(visspot+64) && !*(tilespot+64) )
\r
1643 || ( *(visspot+63) && !*(tilespot+63) ) )
\r
1645 #if USE_INERT_LIST
\r
1648 if ((obj->active == noalways) || (obj->active == always))
\r
1649 obj->active = always;
\r
1651 obj->active = yes;
\r
1652 TransformActor (obj);
\r
1653 if (!obj->viewheight || obj->viewheight > VIEWWIDTH)
\r
1654 goto cont; // too close or far away
\r
1656 if (!obj->state->shapenum)
\r
1663 #if USE_INERT_LIST
\r
1666 if ((obj->active != always) && (obj->active != noalways))
\r
1671 #if USE_INERT_LIST
\r
1672 if ((!obj) && (!inertlist))
\r
1674 if (inert != inertobjlist)
\r
1675 obj = (objtype *)inertobjlist;
\r
1681 if (vislist == &depthsort[0])
\r
1682 return; // no visable objects
\r
1685 // draw from back to front
\r
1687 for (i = 0; i<numvisable; i++)
\r
1690 for (j=0;j<numvisable;j++)
\r
1692 height = depthsort[j]->viewheight;
\r
1693 if (height < least)
\r
1696 farthest = depthsort[j];
\r
1702 shape = shapedirectory[farthest->state->shapenum-FIRSTSCALEPIC];
\r
1703 ScaleShape(farthest->viewx,shape,farthest->viewheight);
\r
1704 farthest->viewheight = 32000;
\r
1708 //==========================================================================
\r
1712 =====================
\r
1716 =====================
\r
1719 void CalcTics (void)
\r
1721 long newtime,oldtimecount;
\r
1730 // calculate tics since last refresh for adaptive timing
\r
1732 if (lasttimecount > TimeCount)
\r
1733 TimeCount = lasttimecount; // if the game was paused a LONG time
\r
1736 if (DemoMode) // demo recording and playback needs
\r
1737 { // to be constant
\r
1739 // take DEMOTICS or more tics, and modify Timecount to reflect time taken
\r
1741 oldtimecount = lasttimecount;
\r
1742 while (TimeCount<oldtimecount+DEMOTICS*2)
\r
1744 lasttimecount = oldtimecount + DEMOTICS;
\r
1745 TimeCount = lasttimecount + DEMOTICS;
\r
1746 realtics = tics = DEMOTICS;
\r
1752 // non demo, so report actual time
\r
1754 newtime = TimeCount;
\r
1755 realtics = tics = newtime-lasttimecount;
\r
1756 lasttimecount = newtime;
\r
1758 #ifdef FILEPROFILE
\r
1759 strcpy (scratch,"\tTics:");
\r
1760 itoa (tics,str,10);
\r
1761 strcat (scratch,str);
\r
1762 strcat (scratch,"\n");
\r
1763 write (profilehandle,scratch,strlen(scratch));
\r
1768 TimeCount -= (tics-MAXTICS);
\r
1772 if (realtics>MAXREALTICS)
\r
1773 realtics = MAXREALTICS;
\r
1778 //==========================================================================
\r
1782 ========================
\r
1786 ========================
\r
1789 void DrawHand (void)
\r
1791 #define HAND_X_POS ((VIEWWIDTH/16)-(10/2)) // "10" = hand width in bytes
\r
1793 #define picnum HAND1PICM
\r
1796 unsigned dest,width,height;
\r
1798 // if (gamestate.shotpower || boltsleft)
\r
1799 // picnum += (((unsigned)TimeCount>>3)&1);
\r
1801 source = grsegs[picnum];
\r
1802 dest = ylookup[VIEWHEIGHT-handheight]+HAND_X_POS+bufferofs; // 12
\r
1803 width = picmtable[picnum-STARTPICM].width;
\r
1804 height = picmtable[picnum-STARTPICM].height;
\r
1806 VW_MaskBlock(source,0,dest,width,handheight,width*height);
\r
1810 //==========================================================================
\r
1814 ========================
\r
1818 ========================
\r
1821 void ThreeDRefresh (void)
\r
1826 aborttrace = false;
\r
1829 // clear out the traced array
\r
1833 asm mov di,OFFSET spotvis
\r
1835 asm mov cx,[mapwidth] // mapheight*32 words
\r
1845 // set up variables for this view
\r
1848 viewangle = player->angle;
\r
1849 fineviewangle = viewangle*(FINEANGLES/ANGLES);
\r
1850 viewsin = sintable[viewangle];
\r
1851 viewcos = costable[viewangle];
\r
1852 viewx = player->x - FixedByFrac(FOCALLENGTH,viewcos);
\r
1853 viewy = player->y + FixedByFrac(FOCALLENGTH,viewsin);
\r
1854 viewx &= 0xfffffc00; // stop on a pixel boundary
\r
1855 viewy &= 0xfffffc00;
\r
1858 viewxpix = viewx>>10;
\r
1859 viewypix = viewy>>10;
\r
1861 focal.x = viewx>>TILESHIFT;
\r
1862 focal.y = viewy>>TILESHIFT;
\r
1865 // find the rightmost visable tile in view
\r
1867 tracedir = viewangle + lastangle;
\r
1870 else if (tracedir>=ANGLES)
\r
1872 TraceRay( tracedir );
\r
1877 // find the leftmost visable tile in view
\r
1879 tracedir = viewangle + firstangle;
\r
1882 else if (tracedir>=ANGLES)
\r
1884 TraceRay( tracedir );
\r
1887 // follow the walls from there to the right
\r
1889 rightwall = &walls[1];
\r
1896 // actually draw stuff
\r
1898 if (++screenpage == 3)
\r
1901 bufferofs = screenloc[screenpage];
\r
1907 // draw the wall list saved be FollowWalls ()
\r
1909 // animframe = (TimeCount&8)>>3;
\r
1912 // draw all the scaled images
\r
1914 asm mov dx,GC_INDEX
\r
1916 asm mov ax,GC_COLORDONTCARE
\r
1917 asm out dx,ax // don't look at any of the planes
\r
1919 asm mov ax,GC_MODE + 256*(10) // read mode 1, write mode 2
\r
1922 asm mov al,GC_BITMASK
\r
1925 AnimateWallList();
\r
1939 // show screen and time last cycle
\r
1944 FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,true);
\r
1945 lasttimecount = TimeCount;
\r
1946 if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement
\r
1950 asm mov cx,[bufferofs]
\r
1951 asm mov dx,3d4h // CRTC address register
\r
1952 asm mov al,0ch // start address high register
\r
1956 asm out dx,al // set the high byte
\r
1958 asm mov al,0dh // start address low register
\r
1962 asm out dx,al // set the low byte
\r
1965 displayofs = bufferofs;
\r