1 /* Catacomb 3-D 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]= {0x900,0x2000,0x3700};
\r
65 unsigned freelatch = 0x4e00;
\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
168 = Must be in write mode 2 with all planes enabled
\r
169 = The bit mask is left set to the end value, so clear it after all lines are
\r
172 = draws a black dot at the left edge of the line
\r
177 unsigned static char dotmask[8] = {0x80,0x40,0x20,0x10,8,4,2,1};
\r
178 unsigned static char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1};
\r
179 unsigned static char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff};
\r
181 void DrawLine (int xl, int xh, int y,int color)
\r
183 unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;
\r
189 Quit("DrawLine: xh<xl");
\r
191 Quit("DrawLine: y<VIEWY");
\r
193 Quit("DrawLine: y>VIEWYH");
\r
196 maskleft = leftmask[xlp];
\r
197 maskright = rightmask[xh&7];
\r
200 dest = bufferofs+ylookup[y]+xlb;
\r
203 // set the GC index register to point to the bit mask register
\r
205 asm mov al,GC_BITMASK
\r
206 asm mov dx,GC_INDEX
\r
212 // entire line is in one byte
\r
215 maskleft&=maskright;
\r
217 asm mov es,[screenseg]
\r
219 asm mov dx,GC_INDEX+1
\r
221 asm mov al,[BYTE PTR maskleft]
\r
222 asm out dx,al // mask off pixels
\r
224 asm mov al,[BYTE PTR color]
\r
225 asm xchg al,[es:di] // load latches and write pixels
\r
230 asm mov es,[screenseg]
\r
232 asm mov dx,GC_INDEX+1
\r
233 asm mov bh,[BYTE PTR color]
\r
238 asm mov al,[BYTE PTR maskleft]
\r
239 asm out dx,al // mask off pixels
\r
242 asm xchg al,[es:di] // load latches and write pixels
\r
249 asm out dx,al // no masking
\r
258 asm mov al,[BYTE PTR maskright]
\r
259 asm out dx,al // mask off pixels
\r
260 asm xchg bh,[es:di] // load latches and write pixels
\r
264 //==========================================================================
\r
266 void DrawLineDot (int xl, int xh, int y,int color)
\r
268 unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;
\r
274 Quit("DrawLine: xh<xl");
\r
276 Quit("DrawLine: y<VIEWY");
\r
278 Quit("DrawLine: y>VIEWYH");
\r
281 maskdot = dotmask[xlp];
\r
282 maskleft = leftmask[xlp];
\r
283 maskright = rightmask[xh&7];
\r
286 dest = bufferofs+ylookup[y]+xlb;
\r
289 // set the GC index register to point to the bit mask register
\r
291 asm mov al,GC_BITMASK
\r
292 asm mov dx,GC_INDEX
\r
298 // entire line is in one byte
\r
301 maskleft&=maskright;
\r
303 asm mov es,[screenseg]
\r
305 asm mov dx,GC_INDEX+1
\r
307 asm mov al,[BYTE PTR maskleft]
\r
308 asm out dx,al // mask off pixels
\r
310 asm mov al,[BYTE PTR color]
\r
311 asm xchg al,[es:di] // load latches and write pixels
\r
315 // write the black dot at the start
\r
317 asm mov al,[BYTE PTR maskdot]
\r
318 asm out dx,al // mask off pixels
\r
321 asm xchg al,[es:di] // load latches and write pixels
\r
327 asm mov es,[screenseg]
\r
329 asm mov dx,GC_INDEX+1
\r
330 asm mov bh,[BYTE PTR color]
\r
335 asm mov al,[BYTE PTR maskleft]
\r
336 asm out dx,al // mask off pixels
\r
339 asm xchg al,[es:di] // load latches and write pixels
\r
342 // write the black dot at the start
\r
344 asm mov al,[BYTE PTR maskdot]
\r
345 asm out dx,al // mask off pixels
\r
347 asm xchg al,[es:di] // load latches and write pixels
\r
354 asm out dx,al // no masking
\r
363 asm mov al,[BYTE PTR maskright]
\r
364 asm out dx,al // mask off pixels
\r
365 asm xchg bh,[es:di] // load latches and write pixels
\r
369 //==========================================================================
\r
372 long wallscalesource;
\r
376 ====================
\r
380 ====================
\r
383 void near ScaleOneWall (int xl, int xh)
\r
385 int x,pixwidth,height;
\r
387 *(((unsigned *)&wallscalesource)+1) = wallseg[xl];
\r
389 for (x=xl;x<=xh;x+=pixwidth)
\r
391 height = wallheight[x];
\r
392 pixwidth = wallwidth[x];
\r
393 (unsigned)wallscalesource = wallofs[x];
\r
395 *(((unsigned *)&scaletablecall)+1) = (unsigned)scaledirectory[height];
\r
396 (unsigned)scaletablecall = scaledirectory[height]->codeofs[0];
\r
399 // scale a byte wide strip of wall
\r
405 asm shr di,1 // X in bytes
\r
406 asm add di,[bufferofs]
\r
411 asm add bx,[pixwidth] // bx = pixel*8+pixwidth-1
\r
413 asm mov al,BYTE PTR [bitmasks1+bx]
\r
414 asm mov dx,GC_INDEX+1
\r
415 asm out dx,al // set bit mask register
\r
416 asm mov es,[screenseg]
\r
417 asm lds si,[wallscalesource]
\r
418 asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels
\r
420 asm mov al,BYTE PTR [ss:bitmasks2+bx]
\r
425 // draw a second byte for vertical strips that cross two bytes
\r
428 asm out dx,al // set bit mask register
\r
429 asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels
\r
438 int walllight1[NUMFLOORS] = {0,
\r
439 WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC,
\r
440 WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC,
\r
441 EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC,
\r
442 RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,
\r
443 YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,
\r
444 GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,
\r
445 BDOOR1PIC,BDOOR2PIC,BDOOR1PIC,BDOOR2PIC};
\r
447 int walldark1[NUMFLOORS] = {0,
\r
448 WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC,
\r
449 WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC,
\r
450 EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC,
\r
451 RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,
\r
452 YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,
\r
453 GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,
\r
454 BDOOR1PIC,BDOOR2PIC,BDOOR1PIC,BDOOR2PIC};
\r
456 int walllight2[NUMFLOORS] = {0,
\r
457 WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC,
\r
458 WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC,
\r
459 EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC,
\r
460 RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,
\r
461 YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,
\r
462 GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,
\r
463 BDOOR2PIC,BDOOR1PIC,BDOOR2PIC,BDOOR1PIC};
\r
465 int walldark2[NUMFLOORS] = {0,
\r
466 WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC,
\r
467 WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC,
\r
468 EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC,
\r
469 RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,
\r
470 YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,
\r
471 GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,
\r
472 BDOOR2PIC,BDOOR1PIC,BDOOR2PIC,BDOOR1PIC};
\r
475 =====================
\r
479 = Draws a wall by vertical segments, for texture mapping!
\r
481 = wallptr->side is true for east/west walls (constant x)
\r
483 = fracheight and fracstep are 16.16 bit fractions
\r
485 =====================
\r
488 void DrawVWall (walltype *wallptr)
\r
492 unsigned width,sourceint;
\r
493 unsigned wallpic,wallpicseg;
\r
495 long fracheight,fracstep,longheightchange;
\r
498 unsigned slope,distance;
\r
499 int traceangle,angle;
\r
501 unsigned lastpix,lastsource,lastwidth;
\r
504 if (wallptr->rightclip < wallptr->leftclip)
\r
505 Quit ("DrawVWall: Right < Left");
\r
508 // setup for height calculation
\r
510 wallptr->height1 >>= 1;
\r
511 wallptr->height2 >>= 1;
\r
512 wallptr->planecoord>>=10; // remove non significant bits
\r
514 width = wallptr->x2 - wallptr->x1;
\r
517 heightchange = wallptr->height2 - wallptr->height1;
\r
518 asm mov ax,[heightchange]
\r
519 asm mov WORD PTR [longheightchange+2],ax
\r
520 asm mov WORD PTR [longheightchange],0 // avoid long shift by 16
\r
521 fracstep = longheightchange/width;
\r
524 fracheight = ((long)wallptr->height1<<16)+0x8000;
\r
525 skip = wallptr->leftclip - wallptr->x1;
\r
527 fracheight += fracstep*skip;
\r
530 // setup for texture mapping
\r
532 // mapadd is 64*64 (to keep source positive) + the origin wall intercept
\r
533 // distance has 6 unit bits, and 6 frac bits
\r
534 // traceangle is the center view angle in FINEANGLES, moved to be in
\r
535 // the +-90 degree range (to thew right of origin)
\r
537 traceangle = fineviewangle;
\r
539 // find wall picture to map from
\r
542 { // east or west wall
\r
544 wallpic = walllight2[wallptr->color];
\r
546 wallpic = walllight1[wallptr->color];
\r
548 if (wallptr->planecoord < viewxpix)
\r
550 distance = viewxpix-wallptr->planecoord;
\r
551 traceangle -= FINEANGLES/2;
\r
552 mapadd = (64-viewypix&63); // the pixel spot of the origin
\r
556 distance = wallptr->planecoord-viewxpix;
\r
557 // traceangle is correct
\r
558 mapadd = viewypix&63; // the pixel spot of the origin
\r
562 { // north or south wall
\r
564 wallpic = walldark2[wallptr->color];
\r
566 wallpic = walldark1[wallptr->color];
\r
568 if (wallptr->planecoord < viewypix)
\r
570 distance = viewypix-wallptr->planecoord;
\r
571 traceangle -= FINEANGLES/4;
\r
572 mapadd = viewxpix&63; // the pixel spot of the origin
\r
576 distance = wallptr->planecoord-viewypix;
\r
577 traceangle -= FINEANGLES*3/4;
\r
578 mapadd = (64-viewxpix&63); // the pixel spot of the origin
\r
582 mapadd = 64*64-mapadd; // make sure it stays positive
\r
584 wallpicseg = (unsigned)walldirectory[wallpic-FIRSTWALLPIC];
\r
585 if (traceangle > FINEANGLES/2)
\r
586 traceangle -= FINEANGLES;
\r
589 // calculate everything
\r
591 // IMPORTANT! This loop is executed around 5000 times / second!
\r
593 lastpix = lastsource = (unsigned)-1;
\r
595 for (x = wallptr->leftclip ; x <= wallptr->rightclip ; x++)
\r
600 asm mov ax,WORD PTR [fracheight]
\r
601 asm mov dx,WORD PTR [fracheight+2]
\r
603 asm add ax,WORD PTR [fracstep]
\r
604 asm adc dx,WORD PTR [fracstep+2]
\r
605 asm mov WORD PTR [fracheight],ax
\r
606 asm mov WORD PTR [fracheight+2],dx
\r
609 asm cmp cx,MAXSCALEHEIGHT
\r
610 asm jbe storeheight
\r
611 asm mov cx,MAXSCALEHEIGHT
\r
613 asm mov WORD PTR [wallheight+bx],cx
\r
614 asm mov WORD PTR [zbuffer+bx],cx
\r
616 // height = fracheight>>16;
\r
617 // fracheight += fracstep;
\r
618 // if (height > MAXSCALEHEIGHT)
\r
619 // height = MAXSCALEHEIGHT;
\r
620 // wallheight[x] = zbuffer[x] = height;
\r
625 angle = pixelangle[x]+traceangle;
\r
629 slope = finetangent[angle];
\r
632 // distance is an unsigned 6.6 bit number (12 pixel bits)
\r
633 // slope is a signed 5.10 bit number
\r
634 // result is a signed 11.16 bit number
\r
638 source = distance*slope;
\r
642 source &= 63; // mask off the unused units
\r
643 source = 63-source;
\r
644 source <<= 6; // multiply by 64 for offset into pic
\r
646 asm mov ax,[distance]
\r
647 asm imul [slope] // ax is the source pixel
\r
650 asm shr al,1 // low 6 bits is now pixel number
\r
651 asm add ax,[mapadd]
\r
654 asm sub dx,ax // otherwise it is backwards
\r
660 asm shl dx,1 // *64 to index into shape
\r
661 asm mov [source],dx
\r
663 if (source != lastsource)
\r
665 if (lastpix != (unsigned)-1)
\r
667 wallofs[lastpix] = lastsource;
\r
668 wallseg[lastpix] = wallpicseg;
\r
669 wallwidth[lastpix] = lastwidth;
\r
672 lastsource = source;
\r
676 lastwidth++; // optimized draw, same map as last one
\r
678 wallofs[lastpix] = lastsource;
\r
679 wallseg[lastpix] = wallpicseg;
\r
680 wallwidth[lastpix] = lastwidth;
\r
684 //==========================================================================
\r
692 = Used to find the left and rightmost tile in the view area to be traced from
\r
693 = Follows a ray of the given angle from viewx,viewy in the global map until
\r
694 = it hits a solid tile
\r
696 = tile.x,tile.y : tile coordinates of contacted tile
\r
697 = tilecolor : solid tile's color
\r
704 void TraceRay (unsigned angle)
\r
706 long tracex,tracey,tracexstep,traceystep,searchx,searchy;
\r
708 int otx,oty,searchsteps;
\r
710 tracexstep = costable[angle];
\r
711 traceystep = sintable[angle];
\r
714 // advance point so it is even with the view plane before we start checking
\r
716 fixtemp = FixedByFrac(prestep,tracexstep);
\r
717 tracex = viewx+fixtemp;
\r
718 fixtemp = FixedByFrac(prestep,traceystep);
\r
719 tracey = viewy-fixtemp;
\r
721 tile.x = tracex>>TILESHIFT; // starting point in tiles
\r
722 tile.y = tracey>>TILESHIFT;
\r
725 if (tracexstep<0) // use 2's complement, not signed magnitude
\r
726 tracexstep = -(tracexstep&0x7fffffff);
\r
728 if (traceystep<0) // use 2's complement, not signed magnitude
\r
729 traceystep = -(traceystep&0x7fffffff);
\r
732 // we assume viewx,viewy is not inside a solid tile, so go ahead one step
\r
735 do // until a solid tile is hit
\r
739 spotvis[otx][oty] = true;
\r
740 tracex += tracexstep;
\r
741 tracey -= traceystep;
\r
742 tile.x = tracex>>TILESHIFT;
\r
743 tile.y = tracey>>TILESHIFT;
\r
745 if (tile.x!=otx && tile.y!=oty && (tilemap[otx][tile.y] || tilemap[tile.x][oty]) )
\r
748 // trace crossed two solid tiles, so do a binary search along the line
\r
749 // to find a spot where only one tile edge is crossed
\r
752 searchx = tracexstep;
\r
753 searchy = traceystep;
\r
758 if (tile.x!=otx && tile.y!=oty)
\r
766 // not far enough, no tiles crossed
\r
772 // if it is REAL close, go for the most clockwise intersection
\r
774 if (++searchsteps == 16)
\r
776 tracex = (long)otx<<TILESHIFT;
\r
777 tracey = (long)oty<<TILESHIFT;
\r
782 tracex += TILEGLOBAL-1;
\r
783 tracey += TILEGLOBAL;
\r
787 tracex += TILEGLOBAL;
\r
795 tracey += TILEGLOBAL-1;
\r
804 tile.x = tracex>>TILESHIFT;
\r
805 tile.y = tracey>>TILESHIFT;
\r
807 } while (( tile.x!=otx && tile.y!=oty) || (tile.x==otx && tile.y==oty) );
\r
809 } while (!(tilecolor = tilemap[tile.x][tile.y]) );
\r
813 //==========================================================================
\r
817 ========================
\r
821 = multiply a 16/16 bit, 2's complement fixed point number by a 16 bit
\r
822 = fraction, passed as a signed magnitude 32 bit number
\r
824 ========================
\r
827 #pragma warn -rvl // I stick the return value in with ASMs
\r
829 fixed FixedByFrac (fixed a, fixed b)
\r
836 asm mov si,[WORD PTR b+2] // sign of result = sign of fraction
\r
838 asm mov ax,[WORD PTR a]
\r
839 asm mov cx,[WORD PTR a+2]
\r
842 asm jns aok: // negative?
\r
847 asm xor si,0x8000 // toggle sign of result
\r
851 // multiply cx:ax by bx
\r
853 asm mov bx,[WORD PTR b]
\r
854 asm mul bx // fraction*fraction
\r
855 asm mov di,dx // di is low word of result
\r
857 asm mul bx // units*fraction
\r
862 // put result dx:ax in 2's complement
\r
864 asm test si,0x8000 // is the result negative?
\r
877 //==========================================================================
\r
881 ========================
\r
885 = Takes paramaters:
\r
886 = gx,gy : globalx/globaly of point
\r
889 = viewx,viewy : point of view
\r
890 = viewcos,viewsin : sin/cos of viewangle
\r
894 = CENTERX : pixel location of center of view window
\r
895 = TILEGLOBAL : size of one
\r
896 = FOCALLENGTH : distance behind viewx/y for center of projection
\r
897 = scale : conversion from global value to screen value
\r
900 = screenx,screenheight: projected edge location and size
\r
902 ========================
\r
905 void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight)
\r
908 fixed gxt,gyt,nx,ny;
\r
911 // translate point to view centered coordinates
\r
919 gxt = FixedByFrac(gx,viewcos);
\r
920 gyt = FixedByFrac(gy,viewsin);
\r
926 gxt = FixedByFrac(gx,viewsin);
\r
927 gyt = FixedByFrac(gy,viewcos);
\r
931 // calculate perspective ratio
\r
936 ratio = nx*scale/FOCALLENGTH;
\r
938 if (ratio<=MINRATIO)
\r
941 *screenx = CENTERX + ny/ratio;
\r
943 *screenheight = TILEGLOBAL/ratio;
\r
951 void TransformActor (objtype *ob)
\r
954 fixed gx,gy,gxt,gyt,nx,ny;
\r
957 // translate point to view centered coordinates
\r
965 gxt = FixedByFrac(gx,viewcos);
\r
966 gyt = FixedByFrac(gy,viewsin);
\r
967 nx = gxt-gyt-ob->size;
\r
972 gxt = FixedByFrac(gx,viewsin);
\r
973 gyt = FixedByFrac(gy,viewcos);
\r
977 // calculate perspective ratio
\r
982 ratio = nx*scale/FOCALLENGTH;
\r
984 if (ratio<=MINRATIO)
\r
987 ob->viewx = CENTERX + ny/ratio;
\r
989 ob->viewheight = TILEGLOBAL/ratio;
\r
992 //==========================================================================
\r
994 fixed TransformX (fixed gx, fixed gy)
\r
997 fixed gxt,gyt,nx,ny;
\r
1000 // translate point to view centered coordinates
\r
1008 gxt = FixedByFrac(gx,viewcos);
\r
1009 gyt = FixedByFrac(gy,viewsin);
\r
1014 //==========================================================================
\r
1017 ==================
\r
1023 = scale projection constant
\r
1024 = sintable/costable overlapping fractional tables
\r
1025 = firstangle/lastangle angles from focalpoint to left/right view edges
\r
1026 = prestep distance from focal point before checking for tiles
\r
1028 ==================
\r
1031 void BuildTables (void)
\r
1036 float angle,anglestep,radtoint;
\r
1041 // calculate the angle offset from view angle of each pixel's ray
\r
1043 radtoint = (float)FINEANGLES/2/PI;
\r
1044 for (i=0;i<VIEWWIDTH/2;i++)
\r
1046 // start 1/2 pixel over, so viewangle bisects two middle pixels
\r
1047 x = (TILEGLOBAL*i+TILEGLOBAL/2)/VIEWWIDTH;
\r
1048 tang = (float)x/(FOCALLENGTH+MINDIST);
\r
1049 angle = atan(tang);
\r
1050 intang = angle*radtoint;
\r
1051 pixelangle[VIEWWIDTH/2-1-i] = intang;
\r
1052 pixelangle[VIEWWIDTH/2+i] = -intang;
\r
1056 // calculate fine tangents
\r
1057 // 1 sign bit, 5 units (clipped to), 10 fracs
\r
1059 #define MININT (-MAXINT)
\r
1061 for (i=0;i<FINEANGLES/4;i++)
\r
1063 intang = tan(i/radtoint)*(1l<<10);
\r
1066 // if the tangent is not reprentable in this many bits, bound the
\r
1067 // units part ONLY
\r
1069 if (intang>MAXINT)
\r
1070 intang = 0x8f00 | (intang & 0xff);
\r
1071 else if (intang<MININT)
\r
1072 intang = 0xff00 | (intang & 0xff);
\r
1074 finetangent[i] = intang;
\r
1075 // finetangent[FINEANGLES/2+i] = intang;
\r
1076 // finetangent[FINEANGLES/2-i-1] = -intang;
\r
1077 finetangent[FINEANGLES-i-1] = -intang;
\r
1081 // calculate scale value so one tile at mindist allmost fills the view horizontally
\r
1083 scale = GLOBAL1/VIEWWIDTH;
\r
1084 scale *= focallength;
\r
1085 scale /= (focallength+mindist);
\r
1088 // costable overlays sintable with a quarter phase shift
\r
1089 // ANGLES is assumed to be divisable by four
\r
1091 // The low word of the value is the fraction, the high bit is the sign bit,
\r
1092 // bits 16-30 should be 0
\r
1096 anglestep = PI/2/ANGLEQUAD;
\r
1097 for (i=0;i<=ANGLEQUAD;i++)
\r
1099 value=GLOBAL1*sin(angle);
\r
1101 sintable[i+ANGLES]=
\r
1102 sintable[ANGLES/2-i] = value;
\r
1103 sintable[ANGLES-i]=
\r
1104 sintable[ANGLES/2+i] = value | 0x80000000l;
\r
1105 angle += anglestep;
\r
1109 // figure trace angles for first and last pixel on screen
\r
1111 angle = atan((float)VIEWWIDTH/2*scale/FOCALLENGTH);
\r
1112 angle *= ANGLES/(PI*2);
\r
1114 intang = (int)angle+1;
\r
1115 firstangle = intang;
\r
1116 lastangle = -intang;
\r
1118 prestep = GLOBAL1*((float)FOCALLENGTH/costable[firstangle]);
\r
1123 walls[0].x2 = VIEWX-1;
\r
1124 walls[0].height2 = 32000;
\r
1128 //==========================================================================
\r
1131 =====================
\r
1135 =====================
\r
1138 void ClearScreen (void)
\r
1141 // clear the screen
\r
1143 asm mov dx,GC_INDEX
\r
1144 asm mov ax,GC_MODE + 256*2 // read mode 0, write mode 2
\r
1146 asm mov ax,GC_BITMASK + 255*256
\r
1149 asm mov dx,40-VIEWWIDTH/8
\r
1150 asm mov bl,VIEWWIDTH/16
\r
1151 asm mov bh,CENTERY+1
\r
1154 asm mov es,[screenseg]
\r
1155 asm mov di,[bufferofs]
\r
1165 asm mov bh,CENTERY+1
\r
1174 asm jnz bottomloop
\r
1177 asm mov dx,GC_INDEX
\r
1178 asm mov ax,GC_MODE + 256*10 // read mode 1, write mode 2
\r
1180 asm mov al,GC_BITMASK
\r
1185 //==========================================================================
\r
1188 =====================
\r
1192 = Clips and draws all the walls traced this refresh
\r
1194 =====================
\r
1197 void DrawWallList (void)
\r
1199 int i,leftx,newleft,rightclip;
\r
1200 walltype *wall, *check;
\r
1204 asm mov di,OFFSET wallwidth
\r
1206 asm mov cx,VIEWWIDTH/2
\r
1211 rightwall->x1 = VIEWXH+1;
\r
1212 rightwall->height1 = 32000;
\r
1213 (rightwall+1)->x1 = 32000;
\r
1217 for (wall=&walls[1];wall<rightwall && leftx<=VIEWXH ;wall++)
\r
1219 if (leftx >= wall->x2)
\r
1222 rightclip = wall->x2;
\r
1225 while (check->x1 <= rightclip && check->height1 >= wall->height2)
\r
1227 rightclip = check->x1-1;
\r
1231 if (rightclip>VIEWXH)
\r
1234 if (leftx < wall->x1 - 1)
\r
1235 newleft = wall->x1-1; // there was black space between walls
\r
1239 if (rightclip > newleft)
\r
1241 wall->leftclip = newleft+1;
\r
1242 wall->rightclip = rightclip;
\r
1244 leftx = rightclip;
\r
1249 ScaleWalls (); // draw all the walls
\r
1253 //==========================================================================
\r
1256 =====================
\r
1260 = Draws all objects that are visable
\r
1262 =====================
\r
1265 objtype *depthsort[MAXACTORS];
\r
1267 void DrawScaleds (void)
\r
1269 int i,j,least,numvisable,height;
\r
1270 objtype *obj,**vislist,*farthest;
\r
1272 byte *tilespot,*visspot;
\r
1277 // calculate base positions of all objects
\r
1279 vislist = &depthsort[0];
\r
1281 for (obj = player->next;obj;obj=obj->next)
\r
1283 if (!obj->state->shapenum)
\r
1286 tilespot = &tilemap[0][0]+(obj->tilex<<6)+obj->tiley;
\r
1287 visspot = &spotvis[0][0]+(obj->tilex<<6)+obj->tiley;
\r
1289 // could be in any of the nine surrounding tiles
\r
1292 || ( *(visspot-1) && !*(tilespot-1) )
\r
1293 || ( *(visspot+1) && !*(tilespot+1) )
\r
1294 || ( *(visspot-65) && !*(tilespot-65) )
\r
1295 || ( *(visspot-64) && !*(tilespot-64) )
\r
1296 || ( *(visspot-63) && !*(tilespot-63) )
\r
1297 || ( *(visspot+65) && !*(tilespot+65) )
\r
1298 || ( *(visspot+64) && !*(tilespot+64) )
\r
1299 || ( *(visspot+63) && !*(tilespot+63) ) )
\r
1301 obj->active = true;
\r
1302 TransformActor (obj);
\r
1303 if (!obj->viewheight || obj->viewheight > VIEWWIDTH)
\r
1304 continue; // too close or far away
\r
1311 if (vislist == &depthsort[0])
\r
1312 return; // no visable objects
\r
1315 // draw from back to front
\r
1317 for (i = 0; i<numvisable; i++)
\r
1320 for (j=0;j<numvisable;j++)
\r
1322 height = depthsort[j]->viewheight;
\r
1323 if (height < least)
\r
1326 farthest = depthsort[j];
\r
1332 shape = shapedirectory[farthest->state->shapenum-FIRSTSCALEPIC];
\r
1333 ScaleShape(farthest->viewx,shape,farthest->viewheight);
\r
1334 farthest->viewheight = 32000;
\r
1338 //==========================================================================
\r
1342 =====================
\r
1346 =====================
\r
1349 void CalcTics (void)
\r
1351 long newtime,oldtimecount;
\r
1360 // calculate tics since last refresh for adaptive timing
\r
1362 if (lasttimecount > TimeCount)
\r
1363 TimeCount = lasttimecount; // if the game was paused a LONG time
\r
1365 if (DemoMode) // demo recording and playback needs
\r
1366 { // to be constant
\r
1368 // take DEMOTICS or more tics, and modify Timecount to reflect time taken
\r
1370 oldtimecount = lasttimecount;
\r
1371 while (TimeCount<oldtimecount+DEMOTICS*2)
\r
1373 lasttimecount = oldtimecount + DEMOTICS;
\r
1374 TimeCount = lasttimecount + DEMOTICS;
\r
1380 // non demo, so report actual time
\r
1382 newtime = TimeCount;
\r
1383 tics = newtime-lasttimecount;
\r
1384 lasttimecount = newtime;
\r
1386 #ifdef FILEPROFILE
\r
1387 strcpy (scratch,"\tTics:");
\r
1388 itoa (tics,str,10);
\r
1389 strcat (scratch,str);
\r
1390 strcat (scratch,"\n");
\r
1391 write (profilehandle,scratch,strlen(scratch));
\r
1396 TimeCount -= (tics-MAXTICS);
\r
1403 //==========================================================================
\r
1407 ========================
\r
1411 ========================
\r
1414 void DrawHand (void)
\r
1418 unsigned dest,width,height;
\r
1420 picnum = HAND1PICM;
\r
1421 if (gamestate.shotpower || boltsleft)
\r
1422 picnum += (((unsigned)TimeCount>>3)&1);
\r
1424 source = grsegs[picnum];
\r
1425 dest = ylookup[VIEWHEIGHT-handheight]+12+bufferofs;
\r
1426 width = picmtable[picnum-STARTPICM].width;
\r
1427 height = picmtable[picnum-STARTPICM].height;
\r
1429 VW_MaskBlock(source,0,dest,width,handheight,width*height);
\r
1433 //==========================================================================
\r
1437 ========================
\r
1441 ========================
\r
1444 void ThreeDRefresh (void)
\r
1449 aborttrace = false;
\r
1452 // clear out the traced array
\r
1456 asm mov di,OFFSET spotvis
\r
1458 asm mov cx,[mapwidth] // mapheight*32 words
\r
1468 // set up variables for this view
\r
1471 viewangle = player->angle;
\r
1472 fineviewangle = viewangle*(FINEANGLES/ANGLES);
\r
1473 viewsin = sintable[viewangle];
\r
1474 viewcos = costable[viewangle];
\r
1475 viewx = player->x - FixedByFrac(FOCALLENGTH,viewcos);
\r
1476 viewy = player->y + FixedByFrac(FOCALLENGTH,viewsin);
\r
1477 viewx &= 0xfffffc00; // stop on a pixel boundary
\r
1478 viewy &= 0xfffffc00;
\r
1481 viewxpix = viewx>>10;
\r
1482 viewypix = viewy>>10;
\r
1484 focal.x = viewx>>TILESHIFT;
\r
1485 focal.y = viewy>>TILESHIFT;
\r
1488 // find the rightmost visable tile in view
\r
1490 tracedir = viewangle + lastangle;
\r
1493 else if (tracedir>=ANGLES)
\r
1495 TraceRay( tracedir );
\r
1500 // find the leftmost visable tile in view
\r
1502 tracedir = viewangle + firstangle;
\r
1505 else if (tracedir>=ANGLES)
\r
1507 TraceRay( tracedir );
\r
1510 // follow the walls from there to the right
\r
1512 rightwall = &walls[1];
\r
1519 // actually draw stuff
\r
1521 if (++screenpage == 3)
\r
1524 bufferofs = screenloc[screenpage];
\r
1530 // draw the wall list saved be FollowWalls ()
\r
1532 animframe = (TimeCount&8)>>3;
\r
1535 // draw all the scaled images
\r
1537 asm mov dx,GC_INDEX
\r
1539 asm mov ax,GC_COLORDONTCARE
\r
1540 asm out dx,ax // don't look at any of the planes
\r
1542 asm mov ax,GC_MODE + 256*(10) // read mode 1, write mode 2
\r
1545 asm mov al,GC_BITMASK
\r
1561 // show screen and time last cycle
\r
1566 FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,true);
\r
1567 lasttimecount = TimeCount;
\r
1568 if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement
\r
1572 asm mov cx,[bufferofs]
\r
1573 asm mov dx,3d4h // CRTC address register
\r
1574 asm mov al,0ch // start address high register
\r
1578 asm out dx,al // set the high byte
\r
1580 asm mov al,0dh // start address low register
\r
1584 asm out dx,al // set the low byte
\r
1587 displayofs = bufferofs;
\r