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