Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | /* |
2 | * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>. |
||
3 | * It is copyright by its individual contributors, as recorded in the |
||
4 | * project's Git history. See COPYING.txt at the top level for license |
||
5 | * terms and a link to the Git history. |
||
6 | */ |
||
7 | /* |
||
8 | * |
||
9 | * Drawing routines |
||
10 | * |
||
11 | */ |
||
12 | |||
13 | |||
14 | #include "dxxerror.h" |
||
15 | |||
16 | #include "3d.h" |
||
17 | #include "globvars.h" |
||
18 | #include "texmap.h" |
||
19 | #include "clipper.h" |
||
20 | #if !DXX_USE_OGL |
||
21 | #include "gr.h" |
||
22 | #endif |
||
23 | |||
24 | namespace dcx { |
||
25 | |||
26 | tmap_drawer_type tmap_drawer_ptr = draw_tmap; |
||
27 | |||
28 | //specifies 2d drawing routines to use instead of defaults. Passing |
||
29 | //NULL for either or both restores defaults |
||
30 | void g3_set_special_render(tmap_drawer_type tmap_drawer) |
||
31 | { |
||
32 | tmap_drawer_ptr = tmap_drawer; |
||
33 | } |
||
34 | #if !DXX_USE_OGL |
||
35 | //deal with a clipped line |
||
36 | static void must_clip_line(grs_canvas &canvas, g3s_point *p0, g3s_point *p1, const uint8_t codes_or, const uint8_t color, temporary_points_t &tp) |
||
37 | { |
||
38 | if ((p0->p3_flags&PF_TEMP_POINT) || (p1->p3_flags&PF_TEMP_POINT)) |
||
39 | ; //line has already been clipped, so give up |
||
40 | else { |
||
41 | clip_line(p0,p1,codes_or,tp); |
||
42 | g3_draw_line(canvas, *p0, *p1, color, tp); |
||
43 | } |
||
44 | |||
45 | //free temp points |
||
46 | |||
47 | if (p0->p3_flags & PF_TEMP_POINT) |
||
48 | tp.free_temp_point(p0); |
||
49 | |||
50 | if (p1->p3_flags & PF_TEMP_POINT) |
||
51 | tp.free_temp_point(p1); |
||
52 | } |
||
53 | |||
54 | //draws a line. takes two points. returns true if drew |
||
55 | void g3_draw_line(grs_canvas &canvas, g3s_point &p0, g3s_point &p1, const uint8_t color) |
||
56 | { |
||
57 | temporary_points_t tp; |
||
58 | g3_draw_line(canvas, p0, p1, color, tp); |
||
59 | } |
||
60 | |||
61 | void g3_draw_line(grs_canvas &canvas, g3s_point &p0, g3s_point &p1, const uint8_t color, temporary_points_t &tp) |
||
62 | { |
||
63 | ubyte codes_or; |
||
64 | |||
65 | if (p0.p3_codes & p1.p3_codes) |
||
66 | return; |
||
67 | |||
68 | codes_or = p0.p3_codes | p1.p3_codes; |
||
69 | |||
70 | if ( |
||
71 | (codes_or & CC_BEHIND) || |
||
72 | (static_cast<void>((p0.p3_flags & PF_PROJECTED) || (g3_project_point(p0), 0)), p0.p3_flags & PF_OVERFLOW) || |
||
73 | (static_cast<void>((p1.p3_flags & PF_PROJECTED) || (g3_project_point(p1), 0)), p1.p3_flags & PF_OVERFLOW) |
||
74 | ) |
||
75 | return must_clip_line(canvas, &p0,&p1, codes_or, color, tp); |
||
76 | gr_line(canvas, p0.p3_sx, p0.p3_sy, p1.p3_sx, p1.p3_sy, color); |
||
77 | } |
||
78 | #endif |
||
79 | |||
80 | //returns true if a plane is facing the viewer. takes the unrotated surface |
||
81 | //normal of the plane, and a point on it. The normal need not be normalized |
||
82 | bool g3_check_normal_facing(const vms_vector &v,const vms_vector &norm) |
||
83 | { |
||
84 | return (vm_vec_dot(vm_vec_sub(View_position,v),norm) > 0); |
||
85 | } |
||
86 | |||
87 | bool do_facing_check(const std::array<cg3s_point *, 3> &vertlist) |
||
88 | { |
||
89 | //normal not specified, so must compute |
||
90 | //get three points (rotated) and compute normal |
||
91 | const auto tempv = vm_vec_perp(vertlist[0]->p3_vec,vertlist[1]->p3_vec,vertlist[2]->p3_vec); |
||
92 | return (vm_vec_dot(tempv,vertlist[1]->p3_vec) < 0); |
||
93 | } |
||
94 | |||
95 | #if !DXX_USE_OGL |
||
96 | //deal with face that must be clipped |
||
97 | static void must_clip_flat_face(grs_canvas &canvas, int nv, g3s_codes cc, polygon_clip_points &Vbuf0, polygon_clip_points &Vbuf1, const uint8_t color) |
||
98 | { |
||
99 | temporary_points_t tp; |
||
100 | auto &bufptr = clip_polygon(Vbuf0,Vbuf1,&nv,&cc,tp); |
||
101 | |||
102 | if (nv>0 && !(cc.uor&CC_BEHIND) && !cc.uand) { |
||
103 | std::array<fix, MAX_POINTS_IN_POLY*2> Vertex_list; |
||
104 | for (int i=0;i<nv;i++) { |
||
105 | g3s_point *p = bufptr[i]; |
||
106 | |||
107 | if (!(p->p3_flags&PF_PROJECTED)) |
||
108 | g3_project_point(*p); |
||
109 | |||
110 | if (p->p3_flags&PF_OVERFLOW) { |
||
111 | goto free_points; |
||
112 | } |
||
113 | |||
114 | Vertex_list[i*2] = p->p3_sx; |
||
115 | Vertex_list[i*2+1] = p->p3_sy; |
||
116 | } |
||
117 | gr_upoly_tmap(canvas, nv, Vertex_list, color); |
||
118 | } |
||
119 | //free temp points |
||
120 | free_points: |
||
121 | ; |
||
122 | } |
||
123 | |||
124 | //draw a flat-shaded face. |
||
125 | //returns 1 if off screen, 0 if drew |
||
126 | void _g3_draw_poly(grs_canvas &canvas, const uint_fast32_t nv, cg3s_point *const *const pointlist, const uint8_t color) |
||
127 | { |
||
128 | g3s_codes cc; |
||
129 | |||
130 | polygon_clip_points Vbuf0, Vbuf1; |
||
131 | auto bufptr = &Vbuf0[0]; |
||
132 | |||
133 | for (int i=0;i<nv;i++) { |
||
134 | |||
135 | bufptr[i] = pointlist[i]; |
||
136 | |||
137 | cc.uand &= bufptr[i]->p3_codes; |
||
138 | cc.uor |= bufptr[i]->p3_codes; |
||
139 | } |
||
140 | |||
141 | if (cc.uand) |
||
142 | return; //all points off screen |
||
143 | |||
144 | if (cc.uor) |
||
145 | { |
||
146 | must_clip_flat_face(canvas, nv, cc, Vbuf0, Vbuf1, color); |
||
147 | return; |
||
148 | } |
||
149 | |||
150 | //now make list of 2d coords (& check for overflow) |
||
151 | std::array<fix, MAX_POINTS_IN_POLY*2> Vertex_list; |
||
152 | for (int i=0;i<nv;i++) { |
||
153 | g3s_point *p = bufptr[i]; |
||
154 | |||
155 | if (!(p->p3_flags&PF_PROJECTED)) |
||
156 | g3_project_point(*p); |
||
157 | |||
158 | if (p->p3_flags&PF_OVERFLOW) |
||
159 | { |
||
160 | must_clip_flat_face(canvas, nv, cc, Vbuf0, Vbuf1, color); |
||
161 | return; |
||
162 | } |
||
163 | |||
164 | Vertex_list[i*2] = p->p3_sx; |
||
165 | Vertex_list[i*2+1] = p->p3_sy; |
||
166 | } |
||
167 | gr_upoly_tmap(canvas, nv, Vertex_list, color); |
||
168 | //say it drew |
||
169 | } |
||
170 | |||
171 | static void must_clip_tmap_face(grs_canvas &, int nv, g3s_codes cc, grs_bitmap &bm, polygon_clip_points &Vbuf0, polygon_clip_points &Vbuf1); |
||
172 | |||
173 | //draw a texture-mapped face. |
||
174 | //returns 1 if off screen, 0 if drew |
||
175 | void _g3_draw_tmap(grs_canvas &canvas, const unsigned nv, cg3s_point *const *const pointlist, const g3s_uvl *const uvl_list, const g3s_lrgb *const light_rgb, grs_bitmap &bm) |
||
176 | { |
||
177 | g3s_codes cc; |
||
178 | |||
179 | polygon_clip_points Vbuf0, Vbuf1; |
||
180 | auto bufptr = &Vbuf0[0]; |
||
181 | |||
182 | for (int i=0;i<nv;i++) { |
||
183 | g3s_point *p; |
||
184 | |||
185 | p = bufptr[i] = pointlist[i]; |
||
186 | |||
187 | cc.uand &= p->p3_codes; |
||
188 | cc.uor |= p->p3_codes; |
||
189 | |||
190 | #if !DXX_USE_OGL |
||
191 | p->p3_u = uvl_list[i].u; |
||
192 | p->p3_v = uvl_list[i].v; |
||
193 | p->p3_l = (light_rgb[i].r+light_rgb[i].g+light_rgb[i].b)/3; |
||
194 | #endif |
||
195 | |||
196 | p->p3_flags |= PF_UVS + PF_LS; |
||
197 | |||
198 | } |
||
199 | |||
200 | if (cc.uand) |
||
201 | return; //all points off screen |
||
202 | |||
203 | if (cc.uor) |
||
204 | { |
||
205 | must_clip_tmap_face(canvas, nv, cc, bm, Vbuf0, Vbuf1); |
||
206 | return; |
||
207 | } |
||
208 | |||
209 | //now make list of 2d coords (& check for overflow) |
||
210 | |||
211 | for (int i=0;i<nv;i++) { |
||
212 | g3s_point *p = bufptr[i]; |
||
213 | |||
214 | if (!(p->p3_flags&PF_PROJECTED)) |
||
215 | g3_project_point(*p); |
||
216 | |||
217 | if (p->p3_flags&PF_OVERFLOW) { |
||
218 | Int3(); //should not overflow after clip |
||
219 | return; |
||
220 | } |
||
221 | } |
||
222 | |||
223 | (*tmap_drawer_ptr)(canvas, bm, nv, bufptr); |
||
224 | } |
||
225 | |||
226 | static void must_clip_tmap_face(grs_canvas &canvas, int nv, g3s_codes cc, grs_bitmap &bm, polygon_clip_points &Vbuf0, polygon_clip_points &Vbuf1) |
||
227 | { |
||
228 | temporary_points_t tp; |
||
229 | auto &bufptr = clip_polygon(Vbuf0,Vbuf1,&nv,&cc,tp); |
||
230 | if (nv && !(cc.uor&CC_BEHIND) && !cc.uand) { |
||
231 | |||
232 | for (int i=0;i<nv;i++) { |
||
233 | g3s_point *p = bufptr[i]; |
||
234 | |||
235 | if (!(p->p3_flags&PF_PROJECTED)) |
||
236 | g3_project_point(*p); |
||
237 | |||
238 | if (p->p3_flags&PF_OVERFLOW) { |
||
239 | Int3(); //should not overflow after clip |
||
240 | goto free_points; |
||
241 | } |
||
242 | } |
||
243 | |||
244 | (*tmap_drawer_ptr)(canvas, bm, nv, &bufptr[0]); |
||
245 | } |
||
246 | |||
247 | free_points: |
||
248 | ; |
||
249 | |||
250 | // Assert(free_point_num==0); |
||
251 | } |
||
252 | |||
253 | //draw a sortof sphere - i.e., the 2d radius is proportional to the 3d |
||
254 | //radius, but not to the distance from the eye |
||
255 | void g3_draw_sphere(grs_canvas &canvas, cg3s_point &pnt, const fix rad, const uint8_t color) |
||
256 | { |
||
257 | if (! (pnt.p3_codes & CC_BEHIND)) { |
||
258 | |||
259 | if (! (pnt.p3_flags & PF_PROJECTED)) |
||
260 | g3_project_point(pnt); |
||
261 | |||
262 | if (! (pnt.p3_codes & PF_OVERFLOW)) { |
||
263 | const auto r2 = fixmul(rad, Matrix_scale.x); |
||
264 | #ifndef __powerc |
||
265 | fix t; |
||
266 | if (!checkmuldiv(&t, r2, Canv_w2, pnt.p3_z)) |
||
267 | return; |
||
268 | #else |
||
269 | if (pnt.p3_z == 0) |
||
270 | return; |
||
271 | const fix t = fl2f(((f2fl(r2) * fCanv_w2) / f2fl(pnt.p3_z))); |
||
272 | #endif |
||
273 | gr_disk(canvas, pnt.p3_sx, pnt.p3_sy, t, color); |
||
274 | } |
||
275 | } |
||
276 | } |
||
277 | #endif |
||
278 | |||
279 | } |