1 /*
2
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 */
19 //
20 // $Source: r:/prj/lib/src/3d/RCS/matrix.C $
21 // $Revision: 1.17 $
22 // $Author: jaemz $
23 // $Date: 1994/09/20 13:33:41 $
24 //
25 // Matrix setup and multiply routines
26 //
27 // $Log: matrix.asm $
28 // Revision 1.17 1994/09/20 13:33:41 jaemz
29 // *** empty log message ***
30 //
31 // Revision 1.16 1994/08/18 03:46:59 jaemz
32 // Changed stereo glob names to have underscore for c
33 //
34 // Revision 1.15 1994/08/04 16:36:13 jaemz
35 // *** empty log message ***
36 //
37 // Revision 1.14 1994/07/19 13:48:35 jaemz
38 // Added support for stereo
39 //
40 // Revision 1.13 1994/07/15 19:31:23 jaemz
41 // changed view_zoom to _view_zoom for c access
42 //
43 // Revision 1.12 1994/07/15 14:13:37 jaemz
44 // Added _view_position with an underscore to make it c readable
45 //
46 // Revision 1.11 1994/06/02 15:09:36 junochoe
47 // changed matrix_scale to _matrix_scale
48 //
49 // Revision 1.10 1994/02/08 20:46:39 kaboom
50 // Moved back clipping plane to z=1\65536.
51 //
52 // Revision 1.9 1993/12/14 14:04:23 kevin
53 // Swap and negate axis before saving unscaled view matrix.
54 // Also commented out code that breaks citadel under wvideo.
55 //
56 // Revision 1.8 1993/10/02 09:28:38 kaboom
57 // Changed point coder to check for magic minimum z value.
58 //
59 // Revision 1.7 1993/08/10 22:54:16 dc
60 // add _3d.inc to includes
61 //
62 // Revision 1.6 1993/07/13 11:58:05 kaboom
63 // Fixed bugs in saving off of angles.
64 //
65 // Revision 1.5 1993/07/08 23:39:01 kaboom
66 // Now sets pitch, heading, bank, values for scaler/roller.
67 //
68 // Revision 1.4 1993/06/22 18:35:35 kaboom
69 // Changed g3_matrix_x_matrix to g3_matrix_x_matrix_ so it's callable
70 // from watcom C w/register passing.
71 //
72 // Revision 1.3 1993/05/24 15:48:56 matt
73 // process_view_matrix now copies view_matrix to unscaled_matrix before
74 // messing with it.
75 //
76 // Revision 1.2 1993/05/11 15:03:58 matt
77 // Added g3_get_view_pyramid()
78 //
79 // Revision 1.1 1993/05/04 17:39:51 matt
80 // Initial revision
81 //
82 //
83
84 //#include <FixMath.h>
85 #include "3d.h"
86 #include "GlobalV.h"
87 #include "lg.h"
88
89 /*#define f1_0 fixmake(1)
90 #define f0_5 fixmake(0,8000h)
91 #define f0_25 fixmake(0,4000h)
92 */
93
94 fix sinp; // fix ?
95 fix cosp; // fix ?
96 fix sinb; // fix ?
97 fix cosb; // fix ?
98 fix sinh_s; // fix ?
99 fix cosh_s; // fix ?
100
101 // vars for get_view_pyramid
102 fix d13; // fix ?
103 fix d23; // fix ?
104 fix d46; // fix ?
105 fix d56; // fix ?
106 fix d79; // fix ?
107 fix d89; // fix ?
108 fix den; // fix ?
109
110 // prototypes
111 void angles_2_matrix(g3s_angvec *angles, g3s_matrix *view_matrix, int rotation_order);
112 void process_view_matrix(void);
113 void scale_view_matrix(void);
114 void get_pyr_vector(g3s_vector *corners);
115
116 int code_point(g3s_point *pt);
117
118 void compute_XYZ(g3s_matrix *view_matrix);
119 void compute_YXZ(g3s_matrix *view_matrix);
120 void compute_YZX(g3s_matrix *view_matrix);
121 void compute_XZY(g3s_matrix *view_matrix);
122 void compute_ZXY(g3s_matrix *view_matrix);
123 void compute_ZYX(g3s_matrix *view_matrix);
124 void compute_invalid(g3s_matrix *view_matrix);
125
126 // function table
127 void (*rotation_table[])(g3s_matrix *) = {compute_XYZ, compute_YXZ, compute_invalid, compute_YZX,
128 compute_XZY, compute_invalid, compute_ZXY, compute_ZYX};
129
130 // build the view matrix from view angles, etc.
131 // takes esi=pos, ebx=angles, eax=zoom, ecx=rotation order
g3_set_view_angles(g3s_vector * pos,g3s_angvec * angles,int rotation_order,fix zoom)132 void g3_set_view_angles(g3s_vector *pos, g3s_angvec *angles, int rotation_order, fix zoom) {
133 _view_zoom = zoom; // mov _view_zoom,eax ;save zoom
134 _view_position = *pos;
135
136 view_pitch = angles->tx;
137 view_heading = angles->ty;
138 view_bank = angles->tz;
139
140 angles_2_matrix(angles, &view_matrix, rotation_order);
141 process_view_matrix();
142 }
143
144 // build the view matrix from an object matrix
145 // takes esi=pos, ebx=matrix, eax=zoom
g3_set_view_matrix(g3s_vector * pos,g3s_matrix * m,fix zoom)146 void g3_set_view_matrix(g3s_vector *pos, g3s_matrix *m, fix zoom) {
147 _view_zoom = zoom;
148 _view_position = *pos;
149 view_matrix = *m;
150
151 process_view_matrix();
152 }
153
154 // generates a matrix from 3 angles. esi=angles,edi=matrix, ecx=order
155 // trashes esi, ebx, ecx, eax, edx, plus whatever fix_sincos trashes
156 // note that these routines use variables called sinp,sinb,etc., which
157 // are really just rotations around certain axes, and don't necessarily
158 // mean pitch,bank, or heading in each coordinated system
angles_2_matrix(g3s_angvec * angles,g3s_matrix * view_matrix,int rotation_order)159 void angles_2_matrix(g3s_angvec *angles, g3s_matrix *view_matrix, int rotation_order) {
160 fix_sincos(angles->tx, &sinp, &cosp);
161 fix_sincos(angles->ty, &sinh_s, &cosh_s);
162 fix_sincos(angles->tz, &sinb, &cosb);
163
164 rotation_table[rotation_order](view_matrix);
165 }
166
167 // compute a matrix with the given order. takes sin & cos vars set, edi=dest
compute_XYZ(g3s_matrix * view_matrix)168 void compute_XYZ(g3s_matrix *view_matrix) {
169 fix cbsp, cpsb, cpshsb, spsb, cpshcb, spshcb, spshsb, cpcb;
170
171 view_matrix->m1 = fix_mul(cosh_s, cosb); // m1 = chcb
172 view_matrix->m2 = -fix_mul(cosh_s, sinb); // m2 = -chsb
173 view_matrix->m3 = sinh_s; // m3 = sinh
174
175 cbsp = fix_mul(cosb, sinp);
176 spshcb = fix_mul(cbsp, sinh_s);
177 cpsb = fix_mul(cosp, sinb);
178 view_matrix->m4 = cpsb + spshcb; // m4 = cpsb+spshcb
179
180 cpshsb = fix_mul(cpsb, sinh_s);
181 view_matrix->m8 = cbsp + cpshsb; // m8 = spcb+cpshsb
182
183 spsb = fix_mul(sinp, sinb);
184 spshsb = fix_mul(spsb, sinh_s);
185 cpcb = fix_mul(cosp, cosb);
186 view_matrix->m5 = cpcb - spshsb; // m5 = cpcb-spshsb
187
188 cpshcb = fix_mul(cpcb, sinh_s);
189 view_matrix->m7 = spsb - cpshcb; // m7 = spsb-cpshcb
190
191 view_matrix->m6 = -fix_mul(sinp, cosh_s); // m6 = -spch
192 view_matrix->m9 = fix_mul(cosp, cosh_s); // m9 = cpch
193 }
194
compute_YXZ(g3s_matrix * view_matrix)195 void compute_YXZ(g3s_matrix *view_matrix) {
196 fix cbch, sbsh, sbch, cbsh;
197
198 // m1 = cb*ch + sb*sp*sh
199 cbch = fix_mul(cosb, cosh_s);
200 sbsh = fix_mul(sinb, sinh_s);
201 view_matrix->m1 = cbch + fix_mul(sbsh, sinp);
202
203 // m8 = sb*sh + cb*ch*sp
204 view_matrix->m8 = sbsh + fix_mul(cbch, sinp);
205
206 // m2 = -sb*ch + cb*sp*sh
207 sbch = fix_mul(sinb, cosh_s);
208 cbsh = fix_mul(cosb, sinh_s);
209 view_matrix->m2 = fix_mul(cbsh, sinp) - sbch;
210
211 // m7 = -cb*sh + sb*ch*sp
212 view_matrix->m7 = fix_mul(sbch, sinp) - cbsh;
213
214 // m3 = sh*cp
215 view_matrix->m3 = fix_mul(sinh_s, cosp);
216
217 // m4 = sb*cp
218 view_matrix->m4 = fix_mul(sinb, cosp);
219
220 // m5 = cb*cp
221 view_matrix->m5 = fix_mul(cosb, cosp);
222
223 // m6 = - sp
224 view_matrix->m6 = -sinp;
225
226 // m9 = ch*cp
227 view_matrix->m9 = fix_mul(cosh_s, cosp);
228 }
229
compute_YZX(g3s_matrix * view_matrix)230 void compute_YZX(g3s_matrix *view_matrix) { DEBUG("%s: needs to be implemented", __FUNCTION__); }
231
compute_XZY(g3s_matrix * view_matrix)232 void compute_XZY(g3s_matrix *view_matrix) { DEBUG("%s: needs to be implemented", __FUNCTION__); }
233
compute_ZXY(g3s_matrix * view_matrix)234 void compute_ZXY(g3s_matrix *view_matrix) { DEBUG("%s: needs to be implemented", __FUNCTION__); }
235
compute_ZYX(g3s_matrix * view_matrix)236 void compute_ZYX(g3s_matrix *view_matrix) { DEBUG("%s: needs to be implemented", __FUNCTION__); }
237
238 // invalid does nothing (and does it well!)
compute_invalid(g3s_matrix * view_matrix)239 void compute_invalid(g3s_matrix *view_matrix) {}
240
241 // scale, fix, etc, the view matrix
process_view_matrix(void)242 void process_view_matrix(void) {
243
244 // adjust matrix for user's coordinate system
245 if ((axis_swap_flag & 1) != 0) {
246 SwapFix(vm1, vm2);
247 SwapFix(vm4, vm5);
248 SwapFix(vm7, vm8);
249 }
250 if ((axis_swap_flag & 2) != 0) {
251 SwapFix(vm1, vm3);
252 SwapFix(vm4, vm6);
253 SwapFix(vm7, vm9);
254 }
255 if ((axis_swap_flag & 4) != 0) {
256 SwapFix(vm2, vm3);
257 SwapFix(vm5, vm6);
258 SwapFix(vm8, vm9);
259 }
260
261 // get vars for horizon drawer
262 horizon_vector.gX = ((fix *)&view_matrix)[up_axis];
263 horizon_vector.gY = ((fix *)&view_matrix)[up_axis + 1];
264 horizon_vector.gZ = ((fix *)&view_matrix)[up_axis + 2];
265
266 // now fix signs
267 if ((axis_neg_flag & 1) != 0) {
268 vm1 = -vm1;
269 vm4 = -vm4;
270 vm7 = -vm7;
271 }
272
273 if ((axis_neg_flag & 2) != 0) {
274 vm2 = -vm2;
275 vm5 = -vm5;
276 vm8 = -vm8;
277 }
278
279 if ((axis_neg_flag & 4) != 0) {
280 vm3 = -vm3;
281 vm6 = -vm6;
282 vm9 = -vm9;
283 }
284
285 unscaled_matrix = view_matrix;
286 scale_view_matrix();
287 }
288
289 // performs various scaling and other operations on the view matrix
scale_view_matrix(void)290 void scale_view_matrix(void) {
291 int32_t temp_long;
292 fix temp_fix;
293
294 // set matrix scale vector based on zoom
295 _matrix_scale.gX = f1_0; // use 1.0 as defaults
296 _matrix_scale.gY = f1_0;
297 _matrix_scale.gZ = f1_0;
298
299 if (_view_zoom <= f1_0)
300 _matrix_scale.gZ = _view_zoom;
301 else
302 _matrix_scale.gY = _matrix_scale.gX = fix_div(f1_0, _view_zoom);
303
304 // scale set matrix scale vector based on window and pixel ratio
305 temp_long = fix_mul_div(window_height, pixel_ratio, window_width);
306
307 #ifdef stereo_on
308 _g3d_eyesep = fix_mul(-temp_long, _g3d_eyesep_raw); // calculate true eyesep
309 #endif
310
311 if (temp_long <= f1_0)
312 _matrix_scale.gX = fix_mul(_matrix_scale.gX, temp_long);
313 else
314 _matrix_scale.gY = fix_div(_matrix_scale.gY, temp_long);
315
316 // now actually scale the matrix
317 temp_fix = _matrix_scale.gX;
318 vm1 = fix_mul(vm1, temp_fix);
319 vm4 = fix_mul(vm4, temp_fix);
320 vm7 = fix_mul(vm7, temp_fix);
321
322 temp_fix = _matrix_scale.gY;
323 vm2 = fix_mul(vm2, temp_fix);
324 vm5 = fix_mul(vm5, temp_fix);
325 vm8 = fix_mul(vm8, temp_fix);
326
327 temp_fix = _matrix_scale.gZ;
328 vm3 = fix_mul(vm3, temp_fix);
329 vm6 = fix_mul(vm6, temp_fix);
330 vm9 = fix_mul(vm9, temp_fix);
331
332 // scale horizon vector
333 horizon_vector.gX = fix_mul(fix_mul(_matrix_scale.gY, _matrix_scale.gZ), horizon_vector.gX);
334 horizon_vector.gY = fix_mul(fix_mul(_matrix_scale.gX, _matrix_scale.gZ), horizon_vector.gY);
335 horizon_vector.gZ = fix_mul(fix_mul(_matrix_scale.gX, _matrix_scale.gY), horizon_vector.gZ);
336 }
337
338 // takes point in edi, set codes in point, returns codes in bl
339 // trashes eax,bl
340 // note: in an effort to optimize the coder, I tried several variants,
341 // including the C&D coder, and a clever one using the set<cond> instruction
342 // that contained no jumps. On my (Matt's) 486, this dull, straightforward
343 // one was just as fast as any other, and short, too.
code_point(g3s_point * pt)344 int code_point(g3s_point *pt) {
345 int code;
346 int tempX, tempY, tempZ;
347
348 tempX = pt->gX;
349 tempY = pt->gY;
350 tempZ = pt->gZ;
351 code = 0;
352 if (tempX > tempZ)
353 code |= CC_OFF_RIGHT;
354 if (tempY > tempZ)
355 code |= CC_OFF_TOP;
356
357 tempZ = -tempZ;
358 if (tempZ > -1)
359 code |= CC_BEHIND;
360
361 if (tempX < tempZ)
362 code |= CC_OFF_LEFT;
363 if (tempY < tempZ)
364 code |= CC_OFF_BOT;
365
366 pt->codes = code;
367 return (code);
368 }
369
370 // general matrix multiply. takes esi=src vector, edi=matrix, ebx=dest vector
371 // src and dest vectors can be the same
g3_vec_rotate(g3s_vector * dest,g3s_vector * src,g3s_matrix * m)372 void g3_vec_rotate(g3s_vector *dest, g3s_vector *src, g3s_matrix *m) {
373 int32_t srcX, srcY, srcZ; // in locals for PPC speed
374 int64_t r;
375
376 srcX = src->gX;
377 srcY = src->gY;
378 srcZ = src->gZ;
379
380 // first column
381 r = fix64_mul(srcX, m->m1) + fix64_mul(srcY, m->m4) + fix64_mul(srcZ, m->m7);
382 dest->gX = fix64_to_fix(r);
383
384 // second column
385 r = fix64_mul(srcX, m->m2) + fix64_mul(srcY, m->m5) + fix64_mul(srcZ, m->m8);
386 dest->gY = fix64_to_fix(r);
387
388 // third column
389 r = fix64_mul(srcX, m->m3) + fix64_mul(srcY, m->m6) + fix64_mul(srcZ, m->m9);
390 dest->gZ = fix64_to_fix(r);
391 }
392
393 // transpose a matrix at esi in place
394 // trashes eax
g3_transpose(g3s_matrix * m)395 void g3_transpose(g3s_matrix *m) // transpose in place
396 {
397 SwapFix(m->m2, m->m4);
398 SwapFix(m->m3, m->m7);
399 SwapFix(m->m6, m->m8);
400 }
401
402 // transpose the matrix at esi into matrix at edi
403 // trashes eax
g3_copy_transpose(g3s_matrix * dest,g3s_matrix * src)404 void g3_copy_transpose(g3s_matrix *dest, g3s_matrix *src) // copy and transpose
405 {
406 dest->m1 = src->m1;
407 dest->m5 = src->m5;
408 dest->m9 = src->m9;
409
410 dest->m2 = src->m4;
411 dest->m4 = src->m2;
412
413 dest->m3 = src->m7;
414 dest->m7 = src->m3;
415
416 dest->m6 = src->m8;
417 dest->m8 = src->m6;
418 }
419
420 // MLA- oh no I've got LookingGlass disease, I'm making multi-line #defines!
421 // No worries, WH comes to help!
mxm_mul(fix s1_1,fix s1_2,fix s1_3,fix s2_1,fix s2_2,fix s2_3)422 fix mxm_mul(fix s1_1, fix s1_2, fix s1_3, fix s2_1, fix s2_2, fix s2_3) {
423 int64_t r = fix64_mul(s1_1, s2_1) + fix64_mul(s1_2, s2_2) + fix64_mul(s1_3, s2_3);
424 return fix64_to_fix(r);
425 }
426 /*
427 #define mxm_mul(dst, s1_1, s1_2, s1_3, s2_1, s2_2, s2_3) \
428 { \
429 int64_t r = fix64_mul(src1->s1_1, src2->s2_1) + \
430 fix64_mul(src1->s1_2, src2->s2_2) + \
431 fix64_mul(src1->s1_3, src2->s2_3); \
432 dest->dst = fix64_to_fix(r); \
433 }
434 */
435
436 // matrix by matrix multiply: ebx = esi * edi
437 // does ebx = edi * esi
438 // it does the inverse actually, edi*esi, assuming
439 // standard layout:
440 // 147
441 // 258
442 // 369
443 //
444 // dest = bx, src1 = si, src2 = di
g3_matrix_x_matrix(g3s_matrix * dest,g3s_matrix * src1,g3s_matrix * src2)445 void g3_matrix_x_matrix(g3s_matrix *dest, g3s_matrix *src1, g3s_matrix *src2) {
446 dest->m1 = mxm_mul(src1->m1, src1->m2, src1->m3, src2->m1, src2->m4, src2->m7);
447 dest->m2 = mxm_mul(src1->m1, src1->m2, src1->m3, src2->m2, src2->m5, src2->m8);
448 dest->m3 = mxm_mul(src1->m1, src1->m2, src1->m3, src2->m3, src2->m6, src2->m9);
449
450 dest->m4 = mxm_mul(src1->m4, src1->m5, src1->m6, src2->m1, src2->m4, src2->m7);
451 dest->m5 = mxm_mul(src1->m4, src1->m5, src1->m6, src2->m2, src2->m5, src2->m8);
452 dest->m6 = mxm_mul(src1->m4, src1->m5, src1->m6, src2->m3, src2->m6, src2->m9);
453
454 dest->m7 = mxm_mul(src1->m7, src1->m8, src1->m9, src2->m1, src2->m4, src2->m7);
455 dest->m8 = mxm_mul(src1->m7, src1->m8, src1->m9, src2->m2, src2->m5, src2->m8);
456 dest->m9 = mxm_mul(src1->m7, src1->m8, src1->m9, src2->m3, src2->m6, src2->m9);
457 }
458
cross(int v1,int v2,int v3,int v4)459 static int64_t cross(int v1, int v2, int v3, int v4) { return (int64_t)v1 * v2 - (int64_t)v3 * v4; }
460
461 // fills in edi with vector. takes deltas set
get_pyr_vector(g3s_vector * corners)462 void get_pyr_vector(g3s_vector *corners) {
463 int64_t r;
464
465 // calculate denominators, divide each by the longest of the three
466 int64_t den_x = cross(d46, d89, d56, d79);
467 int64_t den_y = cross(d23, d79, d13, d89);
468 int64_t den_z = cross(d13, d56, d23, d46);
469
470 if (llabs(den_x) >= llabs(den_y) && llabs(den_x) >= llabs(den_z)) {
471 corners->gX = f1_0;
472 corners->gY = den_y / (den_x >> 16);
473 corners->gZ = den_z / (den_x >> 16);
474 } else if (llabs(den_y) >= llabs(den_x) && llabs(den_y) >= llabs(den_z)) {
475 corners->gX = den_x / (den_y >> 16);
476 corners->gY = f1_0;
477 corners->gZ = den_z / (den_y >> 16);
478 } else {
479 corners->gX = den_x / (den_z >> 16);
480 corners->gY = den_y / (den_z >> 16);
481 corners->gZ = f1_0;
482 }
483
484 // got_vector
485 g3_vec_normalize(corners);
486
487 // make sure vector points right way
488 r = fix64_mul(corners->gX, vm3) + fix64_mul(corners->gY, vm6) + fix64_mul(corners->gZ, vm9);
489 if (fix64_int(r) < 0) {
490 corners->gX = -corners->gX;
491 corners->gY = -corners->gY;
492 corners->gZ = -corners->gZ;
493 }
494 }
495
496 // fills in four vectors which are the corners of the view pyramid
497 // takes edi=ptr to array of 4 vectors. trashes all but edp
g3_get_view_pyramid(g3s_vector * corners)498 void g3_get_view_pyramid(g3s_vector *corners) {
499 fix save_d23, save_d56, save_d89;
500
501 d13 = vm1 - vm3;
502 d23 = vm2 - vm3;
503 d46 = vm4 - vm6;
504 d56 = vm5 - vm6;
505 d79 = vm7 - vm9;
506 d89 = vm8 - vm9;
507 get_pyr_vector(corners);
508 corners++;
509
510 save_d23 = d23;
511 save_d56 = d56;
512 save_d89 = d89;
513
514 d23 -= vm2 << 1;
515 d56 -= vm5 << 1;
516 d89 -= vm8 << 1;
517 get_pyr_vector(corners);
518 corners++;
519
520 d13 -= vm1 << 1;
521 d46 -= vm4 << 1;
522 d79 -= vm7 << 1;
523 get_pyr_vector(corners);
524 corners++;
525
526 d23 = save_d23;
527 d56 = save_d56;
528 d89 = save_d89;
529 get_pyr_vector(corners);
530 }
531