Makefile and conf.h fixes
[btb/d2x.git] / 3d / draw.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13 /*
14  * 
15  * Drawing routines
16  * 
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #include <conf.h>
21 #endif
22
23 #ifdef RCS
24 static char rcsid[] = "$Id: draw.c,v 1.2 2001-01-31 15:17:48 bradleyb Exp $";
25 #endif
26
27 #include "error.h"
28
29 #include "fix.h"
30 #include "vecmat.h"
31 #include "gr.h"
32 #include "3d.h"
33 #include "globvars.h"
34 #include "texmap.h"
35 #include "clipper.h"
36
37 void (*tmap_drawer_ptr)(grs_bitmap *bm,int nv,g3s_point **vertlist) = draw_tmap;
38 void (*flat_drawer_ptr)(int nv,int *vertlist) = gr_upoly_tmap;
39 int (*line_drawer_ptr)(fix x0,fix y0,fix x1,fix y1) = gr_line;
40
41 //specifies 2d drawing routines to use instead of defaults.  Passing
42 //NULL for either or both restores defaults
43 void g3_set_special_render(void (*tmap_drawer)(),void (*flat_drawer)(),int (*line_drawer)(fix, fix, fix, fix))
44 {
45         tmap_drawer_ptr = (tmap_drawer)?tmap_drawer:draw_tmap;
46         flat_drawer_ptr = (flat_drawer)?flat_drawer:gr_upoly_tmap;
47         line_drawer_ptr = (line_drawer)?line_drawer:gr_line;
48 }
49 #ifndef OGL
50 //deal with a clipped line
51 bool must_clip_line(g3s_point *p0,g3s_point *p1,ubyte codes_or)
52 {
53         bool ret;
54
55         if ((p0->p3_flags&PF_TEMP_POINT) || (p1->p3_flags&PF_TEMP_POINT))
56
57                 ret = 0;                //line has already been clipped, so give up
58
59         else {
60
61                 clip_line(&p0,&p1,codes_or);
62
63                 ret = g3_draw_line(p0,p1);
64         }
65
66         //free temp points
67
68         if (p0->p3_flags & PF_TEMP_POINT)
69                 free_temp_point(p0);
70
71         if (p1->p3_flags & PF_TEMP_POINT)
72                 free_temp_point(p1);
73
74         return ret;
75 }
76
77 //draws a line. takes two points.  returns true if drew
78 bool g3_draw_line(g3s_point *p0,g3s_point *p1)
79 {
80         ubyte codes_or;
81
82         if (p0->p3_codes & p1->p3_codes)
83                 return 0;
84
85         codes_or = p0->p3_codes | p1->p3_codes;
86
87         if (codes_or & CC_BEHIND)
88                 return must_clip_line(p0,p1,codes_or);
89
90         if (!(p0->p3_flags&PF_PROJECTED))
91                 g3_project_point(p0);
92
93         if (p0->p3_flags&PF_OVERFLOW)
94                 return must_clip_line(p0,p1,codes_or);
95
96
97         if (!(p1->p3_flags&PF_PROJECTED))
98                 g3_project_point(p1);
99
100         if (p1->p3_flags&PF_OVERFLOW)
101                 return must_clip_line(p0,p1,codes_or);
102
103         return (bool) (*line_drawer_ptr)(p0->p3_sx,p0->p3_sy,p1->p3_sx,p1->p3_sy);
104 }
105 #endif
106
107 //returns true if a plane is facing the viewer. takes the unrotated surface 
108 //normal of the plane, and a point on it.  The normal need not be normalized
109 bool g3_check_normal_facing(vms_vector *v,vms_vector *norm)
110 {
111         vms_vector tempv;
112
113         vm_vec_sub(&tempv,&View_position,v);
114
115         return (vm_vec_dot(&tempv,norm) > 0);
116 }
117
118 bool do_facing_check(vms_vector *norm,g3s_point **vertlist,vms_vector *p)
119 {
120         if (norm) {             //have normal
121
122                 Assert(norm->x || norm->y || norm->z);
123
124                 return g3_check_normal_facing(p,norm);
125         }
126         else {  //normal not specified, so must compute
127
128                 vms_vector tempv;
129
130                 //get three points (rotated) and compute normal
131
132                 vm_vec_perp(&tempv,&vertlist[0]->p3_vec,&vertlist[1]->p3_vec,&vertlist[2]->p3_vec);
133
134                 return (vm_vec_dot(&tempv,&vertlist[1]->p3_vec) < 0);
135         }
136 }
137
138 //like g3_draw_poly(), but checks to see if facing.  If surface normal is
139 //NULL, this routine must compute it, which will be slow.  It is better to 
140 //pre-compute the normal, and pass it to this function.  When the normal
141 //is passed, this function works like g3_check_normal_facing() plus
142 //g3_draw_poly().
143 //returns -1 if not facing, 1 if off screen, 0 if drew
144 bool g3_check_and_draw_poly(int nv,g3s_point **pointlist,vms_vector *norm,vms_vector *pnt)
145 {
146         if (do_facing_check(norm,pointlist,pnt))
147                 return g3_draw_poly(nv,pointlist);
148         else
149                 return 255;
150 }
151
152 bool g3_check_and_draw_tmap(int nv,g3s_point **pointlist,g3s_uvl *uvl_list,grs_bitmap *bm,vms_vector *norm,vms_vector *pnt)
153 {
154         if (do_facing_check(norm,pointlist,pnt))
155                 return g3_draw_tmap(nv,pointlist,uvl_list,bm);
156         else
157                 return 255;
158 }
159
160 //deal with face that must be clipped
161 bool must_clip_flat_face(int nv,g3s_codes cc)
162 {
163         int i;
164         bool ret=0;
165         g3s_point **bufptr;
166
167         bufptr = clip_polygon(Vbuf0,Vbuf1,&nv,&cc);
168
169         if (nv>0 && !(cc.or&CC_BEHIND) && !cc.and) {
170
171                 for (i=0;i<nv;i++) {
172                         g3s_point *p = bufptr[i];
173         
174                         if (!(p->p3_flags&PF_PROJECTED))
175                                 g3_project_point(p);
176         
177                         if (p->p3_flags&PF_OVERFLOW) {
178                                 ret = 1;
179                                 goto free_points;
180                         }
181
182                         Vertex_list[i*2]   = p->p3_sx;
183                         Vertex_list[i*2+1] = p->p3_sy;
184                 }
185         
186                 (*flat_drawer_ptr)(nv,(int *)Vertex_list);
187         }
188         else 
189                 ret=1;
190
191         //free temp points
192 free_points:
193         ;
194
195         for (i=0;i<nv;i++)
196                 if (Vbuf1[i]->p3_flags & PF_TEMP_POINT)
197                         free_temp_point(Vbuf1[i]);
198
199 //      Assert(free_point_num==0);
200
201         return ret;
202 }
203
204 #if (!(defined(D1XD3D) || defined(OGL)))
205 //draw a flat-shaded face.
206 //returns 1 if off screen, 0 if drew
207 bool g3_draw_poly(int nv,g3s_point **pointlist)
208 {
209         int i;
210         g3s_point **bufptr;
211         g3s_codes cc;
212
213         cc.or = 0; cc.and = 0xff;
214
215         bufptr = Vbuf0;
216
217         for (i=0;i<nv;i++) {
218
219                 bufptr[i] = pointlist[i];
220
221                 cc.and &= bufptr[i]->p3_codes;
222                 cc.or  |= bufptr[i]->p3_codes;
223         }
224
225         if (cc.and)
226                 return 1;       //all points off screen
227
228         if (cc.or)
229                 return must_clip_flat_face(nv,cc);
230
231         //now make list of 2d coords (& check for overflow)
232
233         for (i=0;i<nv;i++) {
234                 g3s_point *p = bufptr[i];
235
236                 if (!(p->p3_flags&PF_PROJECTED))
237                         g3_project_point(p);
238
239                 if (p->p3_flags&PF_OVERFLOW)
240                         return must_clip_flat_face(nv,cc);
241
242                 Vertex_list[i*2]   = p->p3_sx;
243                 Vertex_list[i*2+1] = p->p3_sy;
244         }
245
246         (*flat_drawer_ptr)(nv,(int *)Vertex_list);
247
248         return 0;       //say it drew
249 }
250
251 bool must_clip_tmap_face(int nv,g3s_codes cc,grs_bitmap *bm);
252
253 //draw a texture-mapped face.
254 //returns 1 if off screen, 0 if drew
255 bool g3_draw_tmap(int nv,g3s_point **pointlist,g3s_uvl *uvl_list,grs_bitmap *bm)
256 {
257         int i;
258         g3s_point **bufptr;
259         g3s_codes cc;
260
261         cc.or = 0; cc.and = 0xff;
262
263         bufptr = Vbuf0;
264
265         for (i=0;i<nv;i++) {
266                 g3s_point *p;
267
268                 p = bufptr[i] = pointlist[i];
269
270                 cc.and &= p->p3_codes;
271                 cc.or  |= p->p3_codes;
272
273                 p->p3_u = uvl_list[i].u;
274                 p->p3_v = uvl_list[i].v;
275                 p->p3_l = uvl_list[i].l;
276
277                 p->p3_flags |= PF_UVS + PF_LS;
278
279         }
280
281         if (cc.and)
282                 return 1;       //all points off screen
283
284         if (cc.or)
285                 return must_clip_tmap_face(nv,cc,bm);
286
287         //now make list of 2d coords (& check for overflow)
288
289         for (i=0;i<nv;i++) {
290                 g3s_point *p = bufptr[i];
291
292                 if (!(p->p3_flags&PF_PROJECTED))
293                         g3_project_point(p);
294
295                 if (p->p3_flags&PF_OVERFLOW) {
296                         Int3();         //should not overflow after clip
297                         return 255;
298                 }
299         }
300
301         (*tmap_drawer_ptr)(bm,nv,bufptr);
302
303         return 0;       //say it drew
304 }
305 #endif
306
307 bool must_clip_tmap_face(int nv,g3s_codes cc,grs_bitmap *bm)
308 {
309         g3s_point **bufptr;
310         int i;
311
312         bufptr = clip_polygon(Vbuf0,Vbuf1,&nv,&cc);
313
314         if (nv && !(cc.or&CC_BEHIND) && !cc.and) {
315
316                 for (i=0;i<nv;i++) {
317                         g3s_point *p = bufptr[i];
318
319                         if (!(p->p3_flags&PF_PROJECTED))
320                                 g3_project_point(p);
321         
322                         if (p->p3_flags&PF_OVERFLOW) {
323                                 Int3();         //should not overflow after clip
324                                 goto free_points;
325                         }
326                 }
327
328                 (*tmap_drawer_ptr)(bm,nv,bufptr);
329         }
330
331 free_points:
332         ;
333
334         for (i=0;i<nv;i++)
335                 if (bufptr[i]->p3_flags & PF_TEMP_POINT)
336                         free_temp_point(bufptr[i]);
337
338 //      Assert(free_point_num==0);
339         
340         return 0;
341
342 }
343
344 #ifndef __powerc
345 int checkmuldiv(fix *r,fix a,fix b,fix c);
346 #endif
347
348 #ifndef OGL
349 //draw a sortof sphere - i.e., the 2d radius is proportional to the 3d
350 //radius, but not to the distance from the eye
351 int g3_draw_sphere(g3s_point *pnt,fix rad)
352 {
353         if (! (pnt->p3_codes & CC_BEHIND)) {
354
355                 if (! (pnt->p3_flags & PF_PROJECTED))
356                         g3_project_point(pnt);
357
358                 if (! (pnt->p3_codes & PF_OVERFLOW)) {
359                         fix r2,t;
360
361                         r2 = fixmul(rad,Matrix_scale.x);
362 #ifndef __powerc
363                         if (checkmuldiv(&t,r2,Canv_w2,pnt->p3_z))
364                                 return gr_disk(pnt->p3_sx,pnt->p3_sy,t);
365 #else
366                         if (pnt->p3_z == 0)
367                                 return 0;
368                         return gr_disk(pnt->p3_sx, pnt->p3_sy, fl2f(((f2fl(r2) * fCanv_w2) / f2fl(pnt->p3_z))));
369 #endif
370                 }
371         }
372
373         return 0;
374 }
375 #endif
376