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/polygon.asm $
21 // $Revision: 1.31 $
22 // $Author: jaemz $
23 // $Date: 1994/11/06 13:59:23 $
24 //
25 // Polygon drawers
26 //
27
28 #include "3d.h"
29 #include "GlobalV.h"
30 #include "lg.h"
31 #include "OpenGL.h"
32
33 // prototypes
34 int check_and_draw_common(long c, int n_verts, g3s_phandle *p);
35 int draw_poly_common(long c, int n_verts, g3s_phandle *p);
36 int draw_line_common(g3s_phandle p0, g3s_phandle p1);
37
38 #define GR_WIRE_POLY_LINE 6
39 #define GR_WIRE_POLY_SLINE 7
40 #define GR_WIRE_POLY_CLINE 8
41
42 #define MAX_VERTS 100 // max for one poly
43
44 // array of 2d points
45 grs_vertex p_vlist[MAX_VERTS];
46 grs_vertex *p_vpl[MAX_VERTS];
47 long _n_verts;
48 long poly_color;
49
50 // arrays of point handles, used in clipping
51 g3s_phandle vbuf[MAX_VERTS];
52 g3s_phandle _vbuf2[MAX_VERTS];
53
54 // for surface normal check
55 g3s_vector temp_vector;
56
57 // used by line clipper
58 /*long temp_points[4];
59 long n_temp_used;*/
60
61 long draw_color;
62 long poly_index[] = {FIX_UPOLY, FIX_TLUC8_UPOLY, FIX_USPOLY, FIX_TLUC8_SPOLY, FIX_UCPOLY};
63
64 char gour_flag; // 0=normal,1=tluc_poly,2=spoly,3=tluc_spoly,4=cpoly
65
66 // check if a list of point (as in a polygon) are on screen. returns codes
67 // takes esi=list of points, ecx=codes, returns bx=codes.
68 // trashes ebx,ecx,edx,esi
g3_check_codes(int n_verts,g3s_phandle * p)69 g3s_codes g3_check_codes(int n_verts, g3s_phandle *p) {
70 int i;
71 g3s_codes retcode;
72 char andcode, orcode;
73
74 andcode = 0xff;
75 orcode = 0;
76
77 for (i = n_verts; i > 0; i--) {
78 andcode &= (*p)->codes;
79 orcode |= (*p)->codes;
80 p++;
81 }
82
83 retcode.or_ = orcode;
84 retcode.and_ = andcode;
85 return (retcode);
86 }
87
88 extern void g3_compute_normal_quick(g3s_vector *v, g3s_vector *v0, g3s_vector *v1, g3s_vector *v2);
89
90 // takes 3 rotated points: eax,edx,ebx.
91 // returns al=true (& s flag set) if facing. trashes all but ebp
g3_check_poly_facing(g3s_phandle p0,g3s_phandle p1,g3s_phandle p2)92 bool g3_check_poly_facing(g3s_phandle p0, g3s_phandle p1, g3s_phandle p2) {
93 g3_compute_normal_quick(&temp_vector, (g3s_vector *)p0, (g3s_vector *)p1, (g3s_vector *)p2);
94
95 int64_t result =
96 fix64_mul(p0->gX, temp_vector.gX) + fix64_mul(p0->gY, temp_vector.gY) + fix64_mul(p0->gZ, temp_vector.gZ);
97
98 return (fix64_int(result) < 0);
99 }
100
101 // takes same input as draw_poly, but first checks if facing
g3_check_and_draw_cpoly(int n_verts,g3s_phandle * p)102 int g3_check_and_draw_cpoly(int n_verts, g3s_phandle *p) {
103 gour_flag = 4;
104 return (check_and_draw_common(0, n_verts, p));
105 }
106
g3_check_and_draw_tluc_spoly(int n_verts,g3s_phandle * p)107 int g3_check_and_draw_tluc_spoly(int n_verts, g3s_phandle *p) {
108 gour_flag = 3;
109 return (check_and_draw_common(0, n_verts, p));
110 }
111
g3_check_and_draw_spoly(int n_verts,g3s_phandle * p)112 int g3_check_and_draw_spoly(int n_verts, g3s_phandle *p) {
113 gour_flag = 2;
114 return (check_and_draw_common(0, n_verts, p));
115 }
116
g3_check_and_draw_tluc_poly(long c,int n_verts,g3s_phandle * p)117 int g3_check_and_draw_tluc_poly(long c, int n_verts, g3s_phandle *p) {
118 gour_flag = 1;
119 return (check_and_draw_common(c, n_verts, p));
120 }
121
g3_check_and_draw_poly(long c,int n_verts,g3s_phandle * p)122 int g3_check_and_draw_poly(long c, int n_verts, g3s_phandle *p) {
123 gour_flag = 0;
124 return (check_and_draw_common(c, n_verts, p));
125 }
126
check_and_draw_common(long c,int n_verts,g3s_phandle * p)127 int check_and_draw_common(long c, int n_verts, g3s_phandle *p) {
128 // clang-format off
129 #ifdef stereo_on
130 test _g3d_stereo,1
131 jz check_and_draw_common_raw
132 pushm eax,ecx,esi
133
134 call check_and_draw_common_raw
135 set_rt_canv
136
137 popm eax,ecx,esi
138 pushm eax,ecx
139
140 // moves list at esi to temp and repoints esi
141 test gour_flag,6
142 jnz do_uvi_copy1
143 move_to_stereo
144 jmp raw_poly_continue1
145 do_uvi_copy1:
146 mov edx,esi
147 mov eax,ecx
148 move_to_stereo_and_uvi
149 mov esi,edx
150 raw_poly_continue1:
151
152 popm eax,ecx
153 call check_and_draw_common_raw
154
155 set_lt_canv
156 ret
157 check_and_draw_common_raw:
158 #endif
159 // clang-format on
160
161 if (g3_check_poly_facing(p[0], p[1], p[2])) {
162 #ifdef stereo_on
163 js draw_poly_common_raw
164 #else
165 return draw_poly_common(c, n_verts, p);
166 #endif
167 }
168 else return 0; // no draw
169 }
170
171 // takes ecx=# verts, esi=ptr to list of point handles
172 // modify all but ebp
173
174 // RBG-space smooth poly
g3_draw_cpoly(int n_verts,g3s_phandle * p)175 int g3_draw_cpoly(int n_verts, g3s_phandle *p) {
176 gour_flag = 4;
177 return draw_poly_common(0, n_verts, p);
178 }
179
180 // smooth poly
g3_draw_tluc_spoly(int n_verts,g3s_phandle * p)181 int g3_draw_tluc_spoly(int n_verts, g3s_phandle *p) {
182 gour_flag = 3;
183 return draw_poly_common(0, n_verts, p);
184 }
185
186 // smooth poly
g3_draw_spoly(int n_verts,g3s_phandle * p)187 int g3_draw_spoly(int n_verts, g3s_phandle *p) {
188 gour_flag = 2;
189 return draw_poly_common(0, n_verts, p);
190 }
191
g3_draw_tluc_poly(long c,int n_verts,g3s_phandle * p)192 int g3_draw_tluc_poly(long c, int n_verts, g3s_phandle *p) {
193 gour_flag = 1;
194 return draw_poly_common(c, n_verts, p);
195 }
196
g3_draw_poly(long c,int n_verts,g3s_phandle * p)197 int g3_draw_poly(long c, int n_verts, g3s_phandle *p) {
198 gour_flag = 0;
199 return draw_poly_common(c, n_verts, p);
200 }
201
draw_poly_common(long c,int n_verts,g3s_phandle * p)202 int draw_poly_common(long c, int n_verts, g3s_phandle *p) {
203 if (use_opengl()) {
204 extern int opengl_draw_poly(long, int, g3s_phandle *, char);
205 return opengl_draw_poly(c, n_verts, p, gour_flag);
206 }
207
208 char andcode, orcode;
209 g3s_phandle *old_p;
210 int i;
211 g3s_phandle *src;
212 g3s_phandle src_pt;
213 grs_vertex *dest;
214 long rgb;
215
216 // clang-format off
217 #ifdef stereo_on
218 test _g3d_stereo, 1
219 jz draw_poly_common_raw
220
221 pushm eax,ecx,esi
222
223 call draw_poly_common_raw
224 set_rt_canv
225
226 popm eax,ecx,esi
227 pushm eax,ecx
228
229 // moves list at esi to temp and repoints esi
230 test gour_flag,6
231 jnz do_uvi_copy2
232 move_to_stereo
233 jmp raw_poly_continue2
234 do_uvi_copy2:
235 mov edx,esi
236 mov eax,ecx
237 move_to_stereo_and_uvi
238 mov esi,edx
239 raw_poly_continue2:
240
241 popm eax,ecx
242 call draw_poly_common_raw
243
244 set_lt_canv
245 ret
246
247 draw_poly_common_raw:
248 #endif
249
250 // clang-format on
251
252 poly_color = c;
253
254 // first, go through points and get codes
255 andcode = 0xff;
256 orcode = 0;
257 old_p = p;
258
259 for (i = n_verts; i > 0; i--) {
260 andcode &= (*p)->codes;
261 orcode |= (*p)->codes;
262 p++;
263 }
264
265 if (andcode)
266 return CLIP_ALL; // punt!
267
268 p = old_p;
269
270 // copy to temp buffer for clipping
271 // BlockMove(p,vbuf,n_verts<<2);
272 memmove(vbuf, p, n_verts * sizeof *p);
273
274 n_verts = g3_clip_polygon(n_verts, vbuf, _vbuf2);
275 if (!n_verts)
276 return CLIP_ALL;
277
278 // now, copy 2d points to buffer for polygon draw, projecting if neccesary
279 src = _vbuf2;
280 dest = p_vlist;
281
282 for (i = 0; i < n_verts; i++) {
283 src_pt = *(src++);
284
285 // check if this point has been projected
286 if ((src_pt->p3_flags & PF_PROJECTED) == 0) // projected yet?
287 g3_project_point(src_pt);
288
289 dest->x = src_pt->sx; // store 2D X & Y
290 dest->y = src_pt->sy;
291 p_vpl[i] = dest; // store ptr
292 dest++;
293 }
294
295 if (gour_flag >= 2) // some kind of shading
296 {
297 if (gour_flag >= 4) // cpoly
298 {
299 src = _vbuf2;
300 dest = p_vlist;
301 for (i = 0; i < n_verts; i++) {
302 src_pt = *(src++);
303 rgb = src_pt->rgb;
304 dest->u = (rgb & 0x000003ff) << 14; // r
305 dest->v = (rgb & 0x001ffc00) << 3; // g
306 dest->w = (rgb & 0xffe00000) >> 8; // b
307
308 dest++;
309 }
310 } else // spoly
311 {
312 src = _vbuf2;
313 dest = p_vlist;
314 for (i = 0; i < n_verts; i++) {
315 src_pt = *(src++);
316 dest->i = (((ulong)src_pt->i) + gouraud_base) << 8;
317
318 dest++;
319 }
320 }
321 }
322
323 // draw it
324 ((void (*)(long c, int n, grs_vertex **vpl))grd_canvas_table[poly_index[gour_flag]])(poly_color, n_verts, p_vpl);
325
326 return CLIP_NONE;
327 }
328
329 // draw a point in 3-space. takes esi=point. returns al=drew.
330 // trashes eax,edx,esi and if must project, ecx
g3_draw_point(g3s_phandle p)331 int g3_draw_point(g3s_phandle p) {
332 int sx, sy;
333
334 if (p->codes)
335 return CLIP_ALL;
336
337 if ((p->p3_flags & PF_PROJECTED) == 0) // check if projected
338 g3_project_point(p);
339
340 sx = (p->sx + 0x08000) >> 16; // round & get int part
341 sy = (p->sy + 0x08000) >> 16; // round & get int part
342 return (((int (*)(short x, short y))grd_canvas_table[DRAW_POINT])(sx, sy));
343 }
344
345 // draws a line in 3-space. takes esi,edi=points
346
347 // fixed 7/24 dc to have a common and have draw_line set gour_flag, not ignore
348 // it
g3_draw_cline(g3s_phandle p0,g3s_phandle p1)349 int g3_draw_cline(g3s_phandle p0, g3s_phandle p1) // rgb-space gouraud line
350 {
351 if (p0->rgb != p1->rgb) {
352 gour_flag = 1;
353 return (draw_line_common(p0, p1));
354 } else {
355 gour_flag = 0;
356 draw_color = grd_ipal[gr_index_brgb(p0->rgb)];
357 return (draw_line_common(p0, p1));
358 }
359 }
360
g3_draw_sline(g3s_phandle p0,g3s_phandle p1)361 int g3_draw_sline(g3s_phandle p0, g3s_phandle p1) // 2d-intensity gouraud line
362 {
363 gour_flag = -1;
364 return (draw_line_common(p0, p1));
365 }
366
g3_draw_line(g3s_phandle p0,g3s_phandle p1)367 int g3_draw_line(g3s_phandle p0, g3s_phandle p1) {
368 draw_color = gr_get_fcolor();
369 gour_flag = 0;
370 return (draw_line_common(p0, p1));
371 }
372
draw_line_common(g3s_phandle p0,g3s_phandle p1)373 int draw_line_common(g3s_phandle p0, g3s_phandle p1) {
374 byte code0, code1;
375 int result;
376 grs_vertex v0, v1;
377
378 vbuf[0] = p0;
379 vbuf[1] = p1;
380 if (g3_clip_line(vbuf, _vbuf2) == 16)
381 return CLIP_ALL;
382
383 p0 = _vbuf2[0];
384 p1 = _vbuf2[1];
385 code0 = p0->codes;
386 code1 = p1->codes;
387
388 // ok, draw now with points = esi,edi. bl=codes_or
389 // note that in stereo mode, you're doing this twice. We should
390 // just always project all points, or have the code clipper update stuff
391
392 if ((p0->p3_flags & PF_PROJECTED) == 0)
393 g3_project_point(p0);
394 if ((p1->p3_flags & PF_PROJECTED) == 0)
395 g3_project_point(p1);
396
397 if (draw_color == 255)
398 draw_color = 0;
399
400 if (gour_flag == 0) // normal line
401 {
402 // use wire poly lines. Always clip.
403 // set up args -- vertex contents on stack, pass sp
404 // for line only need 1st 2 elements of grs_vertex, only push them
405 v0.x = p0->sx;
406 v0.y = p0->sy;
407 v1.x = p1->sx;
408 v1.y = p1->sy;
409 ((int (*)(long c, long parm, grs_vertex *v0, grs_vertex *v1))grd_line_clip_fill_vector[GR_WIRE_POLY_LINE])(
410 draw_color, gr_get_fill_parm(), &v0, &v1);
411
412 result = CLIP_NONE;
413 } else if (gour_flag > 0) // cline
414 {
415 uchar a, b, c;
416
417 v0.x = p0->sx;
418 v0.y = p0->sy;
419 gr_split_rgb(p0->rgb, &a, &b, &c);
420 v0.u = a;
421 v0.v = b;
422 v0.w = c;
423
424 v1.x = p1->sx;
425 v1.y = p1->sy;
426 gr_split_rgb(p1->rgb, &a, &b, &c);
427 v1.u = a;
428 v1.v = b;
429 v1.w = c;
430 ((int (*)(long c, long parm, grs_vertex *v0, grs_vertex *v1))grd_line_clip_fill_vector[GR_WIRE_POLY_CLINE])(
431 gr_get_fcolor(), gr_get_fill_parm(), &v0, &v1);
432
433 result = CLIP_NONE;
434 // DebugString("implement me?");
435 /*
436 // mov edx,ebx // dl=clip codes
437
438 // set up args -- vertex contents on stack, pass sp
439 // for cline only need 1st 5 elements of grs_vertex, only push them
440 gr_splitrgb [esi].rgb,eax // eax scratch
441 pushm [esi].sy,[esi].sx
442 mov ebx,esp // v0 on stack, addr is arg
443 gr_splitrgb [edi].rgb,eax // eax scratch
444 pushm [edi].sy,[edi].sx
445 mov ecx,esp // v1 on stack, addr is arg
446
447 gr_getcol eax
448 gr_getfp edx
449 mov edi,grd_line_clip_fill_vector
450 call d [edi + 4*GR_WIRE_POLY_CLINE]
451 add esp,40 // 2 vertex's each 5 fix's
452
453 mov eax,CLIP_NONE
454 jmp leave_draw_line*/
455 } else // sline
456 {
457 DEBUG("%s: implement me?", __FUNCTION__);
458 // we have to do this annoyingly because i is an sfix,
459 // and 2d takes a fix, so we dump things in eax and munge
460 /*
461 // new smaller converter
462 xor eax,eax
463 mov ax,[edi].i
464 shl eax,8
465 push eax
466 push [edi].sy
467 xor eax,eax
468 mov ax,[esi].i
469 shl eax,8
470
471 or bl,bl // check triv acc
472
473 mov ebx,eax // we had to do all the ugliness in eax b/c
474 needed
475 // the codes in bl to check for triv acc,
476 and we
477 // couldnt do the check earlier because all
478 our
479 // and's reset the zero flag needed for the
480 jz below mov eax,[esi].sx mov edx,[esi].sy mov ecx,[edi].sx
481
482 jz unclipped_sline
483 call gen_fix_sline_
484 jmp leave_draw_line
485
486 ret
487
488 unclipped_sline:
489 // gr_call FIX_USLINE
490 // set up args -- vertex contents on stack, pass sp
491 // pushd contents of vertex -- don't care about u,v,w
492 // i needs to have sfix to fix
493 xor eax,eax // eax scratch
494 mov ax,[esi].i
495 shl eax,8
496 push eax
497 pushm 0,0,0 // uvw are don't cares
498 pushm [esi].sy,[esi].sx
499 mov ebx,esp // v0 on stack, addr is arg
500 xor eax,eax // eax scratch
501 mov ax,[edi].i
502 shl eax,8
503 push eax
504 pushm 0,0,0 // uvw are don't cares
505 pushm [edi].sy,[edi].sx
506 mov ecx,esp // v1 on stack, addr is arg
507 // now its OK to trash esi
508 gr_getcol eax
509 gr_getfp edx
510 mov esi,grd_uline_fill_vector
511 call d [esi + 4*SLINE]
512 add esp,48 // 2 vertex's each 6 fix's
513
514 // junk from old stuff -- kill these
515 pop esi
516 pop esi
517
518 mov eax,CLIP_NONE
519 jmp leave_draw_line
520 */
521 }
522
523 return result;
524 }
525
526 // check if a surface is facing the viewer
527 // takes esi=point on surface, edi=surface normal (can be unnormalized)
528 // trashes eax,ebx,ecx,edx. returns al=true & sign set, if facing
g3_check_normal_facing(g3s_vector * v,g3s_vector * normal)529 bool g3_check_normal_facing(g3s_vector *v, g3s_vector *normal) {
530 int64_t result = fix64_mul(v->gX - _view_position.gX, normal->gX) +
531 fix64_mul(v->gY - _view_position.gY, normal->gY) +
532 fix64_mul(v->gZ - _view_position.gZ, normal->gZ);
533
534 return (fix64_int(result) < 0);
535 }
536