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