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