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