-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathACKOBJ.C
473 lines (432 loc) · 18.9 KB
/
ACKOBJ.C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
// This source file contains the internal functions needed to add objects
// to the slice structures as a view is being built.
// (c) 1995 ACK Software (Lary Myers)
//#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
//#include <conio.h>
//#include <dos.h>
//#include <mem.h>
//#include <io.h>
//#include <fcntl.h>
#include <time.h>
#include <string.h>
//#include <sys\stat.h>
#include <limits.h>
#include "ACK3D.H" // Main ACK-3D internal and interface data structures
#include "ACKENG.H" // Internal data structures and constants
#include "ACKEXT.H" // Defines external (global) variables
extern short gWinStartX; // Global variables to define the left and
extern short gWinEndX; // right edge of the viewport
// A function pointer to refernce the actual routine used to build a wall slice
extern void (*WallMaskRtn)(void);
//±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
// Internal function called by FindObject(). Your programs may call this
// function if they need to calculate the angle between two points. dx and
// dy represent the deltas between the two points. (i.e. dx = x1 - x and
// dy = y1 - y)
//
// Quadrants
// 2 | 3 If the object is in quadrants 0 or 2, we need
// ---+--- to add the resulting angle to the quad value less
// 1 | 0 than the resulting angle. If the object is in
// quadrants 1 or 3, we need to subtract the
// resulting angle from the next higher quadrant
// value. This is because quads 1 and 3 are negative
// values returned from arctan, while 0 and 2 are
// positive.
//
// The angle between the two points is determined by using the formula:
// tan (angle) = dy/dx. The look-up table LongTanTable[] is used to
// access tangent values of angles.
//±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
short AckGetObjectAngle(int32_t dx,int32_t dy)
{
short i,quadrant,objAngle;
short Beg;
int32_t avalue;
if (dx == 0 || dy == 0) // Test to see if angle is 0, 90, 180, or 270
{
if (dx == 0) // Distance is directly up or down
{
if (dy < 0) // Distance is straight up
return(INT_ANGLE_270);
return(INT_ANGLE_90);
}
if (dx < 0) // dy = 0; distance is directly left or right
return(INT_ANGLE_180);
return(0);
}
// Need to determine which quadrant is involved
quadrant = 0; // Set to quad 0 as default
if (dx < 0 && dy > 0) // We're in quad 1
quadrant = INT_ANGLE_180;
else
{
if (dx < 0 && dy < 0) // We're in quad 2
quadrant = INT_ANGLE_270;
else
{
if (dx > 0 && dy < 0) // We're in quad 3
quadrant = INT_ANGLE_360;
}
}
// Get the absolute values to use for our ratio
if (dy < 0)
dy = -dy;
if (dx < 0)
dx = -dx;
//=======================================================================
// Next we need to convert dy into the same fixed point representation
// as used in our tangent table. Then, we divide dy by dx (rise/run)
// to get the ratio so we can determine the tangent of the angle between
// the two pints. We use the ratio to search the tangent table
// and the index that is returned tells us what the actual angle is.
// We only need to check angles from 0 to 90 degrees. Later, the angle
// will be adjusted to take into account which quadrant we are in.
//=======================================================================
dy = dy << FP_SHIFT; // Make the dividend the same fixed point as the table
avalue = dy / dx; // Get our ratio to search for
// This ratio tells us the tangent of the angle
if (LongTanTable[INT_ANGLE_90-1] <= avalue) // Angle is 89 degrees
return(INT_ANGLE_90-1);
objAngle = 0; // Initialize angle to 0
//=============================================================================
// Now we use a binary lookup trick to speed up the search. This invloves
// a test to see if the angle is between o and 45 degrees or between 45 and
// 90 degrees. Then, we search the list sequentially to find the first value
// higher than our ratio.
//=============================================================================
Beg = 0; // Assume midpoint between 0 and 45 degrees
if (LongTanTable[INT_ANGLE_45] < avalue)
{
if (LongTanTable[360] < avalue)
Beg = 360; // Use angle of 360
else
Beg = INT_ANGLE_45; // Midpoint between 45 and 90 degrees
}
// Loop to check the tan table and find the correct angle
for (i = Beg; i < INT_ANGLE_90; i++)
{
if (LongTanTable[i] > avalue) // We've passed by the angle
{
objAngle = i - 1; // Get the correct angle
break;
}
}
if (objAngle < 0) // Adjust for angle=0
objAngle = 0;
//============================================================================
// Now we adjust the resulting angle based on the quadrant. If we are in
// quad 0 we do nothing. If we're in quads 1 and 3 we subtract the angle from
// the next higher quad angle. If we're in quad 2 we add the angle to the next
// lower quad angle to get the actual angle (0-1800) between the points.
//============================================================================
if (quadrant)
{
if (quadrant != INT_ANGLE_270)
objAngle = quadrant - objAngle;
else
objAngle += INT_ANGLE_180;
}
// Returns the angle between the two points. This value is mainly used for
// determining the angle between the POV and an object, but it could
// be used for generic purposes as well.
return(objAngle);
}
//±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
// Internal function that returns the square root of a int32_t value.
// This function is called by FindObject().
//±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
short long_sqrt(int32_t v)
{
short i;
unsigned short result,tmp;
uint32_t low,high;
if (v <= 1L) return((unsigned)v); // Value is less than 1; return value
low = v; // Initialize starting variables
high = 0L;
result = 0;
for (i = 0; i < 16; i++)
{
result += result;
high = (high << 2) | ((low >>30) & 0x3);
low <<= 2; // Shift left by 2
tmp = result + result + 1;
if (high >= tmp)
{
result++;
high -= tmp;
}
}
return(result);
}
//±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
// Internal function called by AckBuildView() which checks the list of
// objects found during the ray cast process and places the object slices
// into the wall slices.
//±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
void FindObject(void)
{
short i,j,StartX,EndX;
short oCount;
short minAngle,maxAngle,objAngle;
short objColumn;
USHORT distance;
int32_t dx,dy;
short count;
short ObjNum,oQuad,pQuad,numsides,afact;
short NewX;
short Column,ColEnd;
short wt,ObjIndex;
// short vidwt,vidht,hoff;
// short MaxObjs;
short SliceLen;
USHORT BmpColumn;
int32_t xp,yp;
// short wht;
UCHAR *wall;
// UCHAR *pTable;
UCHAR **omaps;
SLICE *sa,*sa2,*saNext;
UCHAR *bmpFlags;
NEWOBJECT **oList;
NEWOBJECT *oPtr;
if (FoundObjectCount) // Make sure objects were found during ray casting
{
oList = &aeGlobal->ObjList[0]; // Get pointer to the array of objects
StartX = gWinStartX; // Starting column of view
EndX = gWinEndX; // Ending column of view
minAngle = PlayerAngle - (INT_ANGLE_32 + 10); // Starting angle of view
if (minAngle < 0) // Check for wrap-around at angle 0
minAngle += INT_ANGLE_360;
maxAngle = PlayerAngle + (INT_ANGLE_32 + 10); // Ending angle of view
if (maxAngle >= INT_ANGLE_360) // Check for wrap-around at angle 360
maxAngle -= INT_ANGLE_360;
TotalObjects = 0; // Stores nmber of objects in view
SliceLen = sizeof(SLICE) - 9; // Amount of slice we'll move later
// Loop and process each object in the view. This invloves setting up
// a few arrays to store the object number, the distance from the player
// to the object, the viewing angle, and the view column where the object
// will be displayed.
for (oCount = 0; oCount < FoundObjectCount; oCount++)
{
i = ObjectsSeen[oCount]; // Get index to possible object
oPtr = oList[i]; // Pointer to object structure
if (oPtr == NULL) // Make sure it's a valid object
continue;
if (!oPtr->Active) // Make sure it's visible
continue;
dx = oPtr->x - xPglobal; // Get the delta x,y between the
dy = oPtr->y - yPglobal; // object and the player
// Calculate the angle the object is relative to the player
if ((objAngle = AckGetObjectAngle(dx,dy)) < 0)
continue; // Negative angle means it can't be seen
// Here we determine if the POV is looking toward the right or
// the left and the actual column of the view (from 0 to view width)
// where the object would be seen.
if (minAngle > maxAngle) // If looking towards the right
{
if (objAngle >= minAngle) // Cal. view column of object
objColumn = objAngle - minAngle;
else
objColumn = (objAngle+INT_ANGLE_360) - minAngle;
}
else
{
objColumn = objAngle - minAngle; // Calc. view column of object
}
// Get the distance to the object
distance = long_sqrt((dx * dx) + (dy * dy));
// No need to check further if it's too far away
if (distance >= MaxDistance)
continue;
// Place the objects in the correct order so further ones are behind
j = TotalObjects; // Current number of objects we've found
if (j)
{
// Sort the objects found by distance so that further ones
// are drawn BEFORE closer ones.
for (count = 0; count < TotalObjects; count++)
{
if (distance <= ObjRelDist[count])
{
for (j = TotalObjects; j > count; j--)
{
ObjRelDist[j] = ObjRelDist[j-1];
ObjNumber[j] = ObjNumber[j-1];
ObjColumn[j] = ObjColumn[j-1];
ObjAngle[j] = ObjAngle[j-1];
}
j = count;
count = TotalObjects;
}
}
}
// Hold onto relavant data for the object found
ObjNumber[j] = i; // Store the object number
ObjRelDist[j] = distance; // Store the distance to the object
ObjColumn[j] = objColumn; // Store view column where object resides
ObjAngle[j] = objAngle; // Store the viewing angle
TotalObjects++; // Bump the count of objects in view
ObjRelDist[TotalObjects] = 0L; // Set to relative dist. in next object to 0
}
// Didn't find any objects on the above pass, so we're done
if (!TotalObjects)
return;
omaps = &aeGlobal->oMaps[0]; // Bitmaps used for objects
pQuad = PlayerAngle / INT_ANGLE_45; // Quadrant POV is facing
// Check each object in the list to be displayed and get the object's
// bitmap. Also, calulate the width and height of the object.
// This loop also checks to see if an object has multiple sides
// and it determines which bitmap should be used to display the object.
for (i = 0; i < TotalObjects; i++)
{
ObjIndex = ObjNumber[i]; // Actual object found
oPtr = oList[ObjIndex]; // Pointer to object structure
if (oPtr == NULL) // Again check for a null object
continue;
// Current bitmap for the object (this number can change if the
// object is animated)
ObjNum = oPtr->CurrentBitmaps[oPtr->CurrentBm];
distance = ObjRelDist[i]; // Get relative distance to object
// Make sure distance is within a reasonable entry in our
// pre-calculated table
if (distance >= (MAX_DISTANCE - 10))
distance = MAX_DISTANCE-11;
// Get the width of the object
wt = DistanceTable[distance]; // Adjust the width using the distance
// Keep the width of the object reasonable
if (wt > 300) // The object is too wide
continue; // Skip over
if (wt < 6) wt = 6; // Adjust if too small
// Get the scale factor which was pre-calculated based on
// distance in AckInitialize() function
yp = AdjustTable[distance];
xp = 0; // First col of the object to display
NewX = ObjColumn[i]; // View column where object resides
// Check if object has multiple sides. If so we need to determine
// the correct bitmap to display based on the angle between the
// POV and the object. We'll perform a trick here by breaking down
// the problem into quadrants and then use the quadrant to determine
// which side we're facing. The object itself is facing a certain
// angle (stored in the Dir field of the object structure) so this
// needs to be taken into account as well.
if (oPtr->Flags & OF_MULTIVIEW)
{
afact = oPtr->aFactor; // Get the angles per side of object
numsides = oPtr->Sides; // Get total sides for this object
pQuad = ObjAngle[i] / afact; // Get the quadrant from POV to object
oQuad = oPtr->Dir / afact; // Get the quadrant it wants to face
// The difference between the POV-Object angle and the angle the
// object is facing determines the actual side of the object that
// can currently be seen
j = (pQuad - oQuad) + (numsides >> 1);
// Check for wrap-around and keep within range
if (j >= numsides)
j -= numsides;
// Check wrap-around in both directions
if (j < 0)
j += numsides;
// Calculate which bitmap set we should use (each side could
// have multiple bitmaps for animation)
j *= oPtr->BitmapsPerView;
j += oPtr->CurrentBm;
// Get the actual bitmap for this side and animation
ObjNum = oPtr->CurrentBitmaps[j];
}
// Done processing multiple sides. Next, find the
// ending column based on the starting column plus the scaled
// width of the object.
ColEnd = NewX + wt;
// Finally get the pointer to the actual bitmap
//printf("bitmap #: %d\n",ObjNum);
wall = omaps[ObjNum];
if(wall == NULL)
continue; //skip this object if NULL bitmap
// Pick up the transparent flags at end of bitmap
bmpFlags = &wall[BITMAP_SIZE];
j = distance;
// Loop from starting column to ending column and fold in the
// object into the appropriate slice structure.
for (Column = NewX - wt; Column < ColEnd; Column++)
{
// Make sure column is within view width
if (Column >= StartX && Column <= EndX)
{
// Scale bitmap column down from fixed point
BmpColumn = xp >> FP_SHIFT;
if (bmpFlags[BmpColumn]) // If transparent column
goto keepgoing; // Ouch! But it works
j = distance;
// Account for fisheye effect
dy = ViewCosTable[Column] >> 2;
dx = distance * dy;
// Now we strip off somemore decimal points and check round-up
dy = dx >> 12;
if (dx - (dy << 12) >= 4096)
dy++;
if (dy > 32L)
j = dy;
// Now we pick up the slice for this column and insert sort
// the object slice based on the distance. This allows objects
// to appear between walls at various distances, behind
// transparent walls, and so on.
sa = &Slice[Column]; // Access the corresponding slice
if (sa->Active) // Multiple slices for this column?
{
while (sa != NULL)
{
if (j <= sa->Distance)
{
sa2 = sa;
while (sa2->Next != NULL) // Go to end of slices
sa2 = sa2->Next;
saNext = sa2->Prev;
while (sa2 != sa) // Move slice down to create
{ // a space for new slice
memcpy(sa2,saNext,sizeof(SLICE)-(sizeof(SLICE*) * 2));
sa2->Active = saNext->Active;
sa2 = sa2->Prev;
saNext = saNext->Prev;
}
// Fill in the slice structure with the
// info about the object
sa->Distance = distance;
sa->bNumber = ObjNum;
sa->bColumn = BmpColumn;
sa->bMap = omaps;
sa->Active = 1;
sa->Type = ST_OBJECT;
sa->Fnc = WallMaskRtn;
break;
}
if (!sa->Active)
break;
sa = sa->Next;
}
}
else // Only one slice is used for this column (typical)
{
if (j <= sa->Distance) // Only put it in if object is
{ // closer than current slice
sa->Active = 1;
saNext = sa->Next;
memcpy(saNext,sa,sizeof(SLICE)-(sizeof(SLICE*) * 2));
sa->Distance = distance;
sa->bColumn = BmpColumn;
sa->bNumber = ObjNum;
sa->bMap = omaps;
sa->Type = ST_OBJECT;
sa->Fnc = WallMaskRtn;
saNext->Active = 0;
}
}
}
keepgoing:
xp += yp; // Advance the next column to display (scaling)
}
}
}
}
// **** End of Source ****