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 | } |