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