convert line ends
[canaan.git] / prj / cam / src / portal / portdraw.c
blob55a75360c26b4bb7d277db4ff0c5a45369483a74
1 /*
2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
4 */
6 // $Header: r:/t2repos/thief2/src/portal/portdraw.c,v 1.119 2000/02/19 13:18:57 toml Exp $
7 //
8 // PORTAL
9 //
10 // dynamic portal/cell-based renderer
12 #include <math.h>
13 #include <string.h>
14 #include <stdlib.h>
16 #include <lg.h>
17 #include <r3d.h>
18 #include <g2pt.h>
19 #include <mprintf.h>
20 #include <lgd3d.h>
21 #include <tmpalloc.h>
23 #include <portwatr.h>
24 #include <portal_.h>
25 #include <portclip.h>
26 #include <porthw.h>
27 #include <pt_clut.h>
28 #include <portsky.h>
29 #include <wrdbrend.h>
31 #include <profile.h>
33 #ifdef DBG_ON
34 #define STATIC
35 #else
36 #define STATIC static
37 #endif
39 extern mxs_vector portal_camera_loc;
40 extern bool show_lightmap;
42 #define STATS_ON
44 #ifdef DBG_ON
45 #ifndef STATS_ON
46 #define STATS_ON
47 #endif
48 #endif
50 #ifdef STATS_ON
51 #define STAT(x) x
52 #else
53 #define STAT(x)
54 #endif
56 // Here's the current placement of textures applied to medium
57 // boundaries:
58 PortalCellMotion portal_cell_motion[MAX_CELL_MOTION];
60 // We can fiddle this up and down to vary the level of detail.
61 // Higher means more texels. Right now it only affects MIP mapping.
62 // The expected range is 0.8-1.5; the default setting is just what looks
63 // good to me without clobbering the frame rate.
64 float portal_detail_level = 1.90;
66 float dot_clamp=0.6;
67 #define VISOBJ_NULL (-1)
70 // If this is on, we blow off the usual rendering and use these flags.
71 // Each face is a solid, flat polygons, with a white wireframe outline.
72 #ifndef SHIP
73 #include <uigame.h>
74 #include <guistyle.h>
75 #include <memall.h>
76 #include <dbmem.h> // must be last header!
77 bool draw_solid_by_MIP_level = FALSE;
78 bool draw_solid_by_cell = FALSE;
79 bool draw_solid_wireframe = FALSE;
80 bool draw_solid_by_poly_flags = FALSE;
81 bool draw_solid_by_cell_flags = FALSE;
82 bool draw_wireframe_around_tmap = FALSE;
83 bool draw_wireframe_around_poly = FALSE;
84 uint polygon_cell_color;
85 uint _polygon_cell_flags_color;
87 #define COLOR_WHITE 0x1ffffff
88 #endif // ~SHIP
90 r3s_point *cur_ph; // list of pointers to r3s_points for current pool
91 Vector *cur_pool; // list of untransformed vectors
92 PortalCell *cur_cell;
93 ushort *cur_anim_light_index_list;
94 extern int cur_cell_num;
96 extern ulong fog_r3_color; // from portal.c
98 bool portal_clip_poly = TRUE;
99 bool portal_render_from_texture = FALSE;
101 #ifdef STATS_ON
102 extern int stat_num_poly_drawn;
103 extern int stat_num_poly_raw;
104 extern int stat_num_poly_considered;
105 extern int stat_num_backface_tests;
106 #endif
108 ///// determine if a surface is visible /////
109 // This rejects if it is backfacing
110 // right now we use the r3d, which is slow; we should compute
111 // the polygon normal once and use that, and eventually use
112 // the normal cache and make it superfast
114 #define MAX_VERT 32
116 // is there a reason this isn't a bool?
117 int check_surface_visible(PortalCell *cell, PortalPolygonCore *poly, int voff)
119 // evaluate the plane equation for this surface
121 extern mxs_vector portal_camera_loc;
122 int plane = poly->planeid;
123 PortalPlane *p = &cell->plane_list[plane];
124 float dist = mx_dot_vec(&portal_camera_loc, &p->normal)
125 + p->plane_constant;
127 STAT(++stat_num_backface_tests;)
128 return dist > 0;
132 static double portal_detail_2, dot_clamp_2;
133 static double pixel_scale, pixel_scale_2;
134 static double premul, premul2; // premul2 != premul*premul, not named premul_2
135 void portal_mip_setup(float zoom)
137 portal_detail_2 = portal_detail_level * portal_detail_level;
138 pixel_scale = zoom * grd_bm.w * (1.0 / 128.0);
139 pixel_scale_2 = pixel_scale * pixel_scale;
141 dot_clamp_2 = dot_clamp * dot_clamp;
143 premul = pixel_scale * portal_detail_level;
144 premul2 = pixel_scale_2 * portal_detail_2 * dot_clamp_2;
148 //////////////////////////////////////////////////////////////////
150 ///// render a single region /////
152 bool show_region, show_portal, linear_map=FALSE;
153 extern void draw_objects_in_node(int n);
155 uchar *r_vertex_list;//, *r_vertex_lighting;
156 void *r_clip;
158 // get a transformed vector. since we haven't implemented the cache
159 // yet, we always do it. since the r3d doesn't implement o2c, we just
160 // have to transform two points and subtract them. This will change.
162 mxs_vector *get_cached_vector(mxs_vector *where, mxs_vector *v)
163 { PROF
164 r3_rotate_o2c(where, v);
165 END_PROF;
166 return where; // should copy it into cache
169 // our 3x3 texture perspective correction matrix
171 // tables mapping lighting values from 0..255 (which is how
172 // we store them in polygons) into floating point 'i' values
173 // appropriate for clipping.
174 #if 0
175 float light_mapping[256];
176 float light_mapping_dithered[256];
177 #endif
178 extern int dither;
179 int max_draw_polys = 1024;
181 extern grs_bitmap *get_cached_surface(PortalPolygonRenderInfo *render,
182 PortalLightMap *lt, grs_bitmap *texture,
183 int MIP_level);
185 #ifndef SHIP
187 /* ----- /-/-/-/-/-/-/-/-/ <<< (((((( /\ )))))) >>> \-\-\-\-\-\-\-\-\ ----- *\
189 This draws a wireframe around one polygon (it doesn't have to be
190 one that's normally rendered).
192 \* ----- \-\-\-\-\-\-\-\-\ <<< (((((( \/ )))))) >>> /-/-/-/-/-/-/-/-/ ----- */
193 void draw_polygon_wireframe(r3s_phandle *points, int num_points, uint color)
195 int p1, p2;
197 r3_set_color(guiScreenColor(color));
198 r3_set_line_context(R3_LN_FLAT);
200 p2 = num_points - 1;
201 for (p1 = 0; p1 < num_points; p1++) {
202 r3_draw_line(points[p1], points[p2]);
203 p2 = p1;
207 #define VERT_DIST 7
209 void draw_polygon_vertices(r3s_phandle *points, int num_points, uint color)
211 int p1;
212 double cx,cy;
213 r3_set_color(guiScreenColor(color));
215 num_points = r3_clip_polygon(num_points, points, &points);
217 if (num_points) {
219 // find center point of polygon
221 cx = cy = 0;
222 for (p1 = 0; p1 < num_points; p1++) {
223 cx += points[p1]->grp.sx;
224 cy += points[p1]->grp.sy;
227 cx /= num_points;
228 cy /= num_points;
230 // now draw all the vertices, displaced towards the center
232 for (p1 = 0; p1 < num_points; p1++) {
233 r3s_point temp = *points[p1];
235 double dx = (cx - temp.grp.sx);
236 double dy = (cy - temp.grp.sy);
237 double len = sqrt(dx*dx + dy*dy);
238 // displace by at most 3, and at least len/2
240 if (len/2 < fix_make(VERT_DIST,0)) {
241 temp.grp.sx = temp.grp.sx + dx/2;
242 temp.grp.sy = temp.grp.sy + dy/2;
243 } else {
244 temp.grp.sx = temp.grp.sx + (dx/len) * fix_make(VERT_DIST,0);
245 temp.grp.sy = temp.grp.sy + (dy/len) * fix_make(VERT_DIST,0);
248 r3_draw_point(&temp);
254 /* ----- /-/-/-/-/-/-/-/-/ <<< (((((( /\ )))))) >>> \-\-\-\-\-\-\-\-\ ----- *\
256 When we're showing the polygons in the visualization tools, each
257 visible face is a flat, unshaded polygon with a wireframe border.
259 The context constants come from x:\prj\tech\libsrc\r3d\prim.h.
261 \* ----- \-\-\-\-\-\-\-\-\ <<< (((((( \/ )))))) >>> /-/-/-/-/-/-/-/-/ ----- */
262 void draw_polygon_outline(PortalPolygonCore *poly, r3s_phandle *points,
263 uint polygon_color, uint outline_color)
265 int num_points = poly->num_vertices;
267 // Here's our polygon.
268 r3_set_clipmode(R3_CLIP);
269 r3_set_polygon_context(R3_PL_POLYGON | R3_PL_UNLIT | R3_PL_SOLID);
270 r3_set_color(guiScreenColor(polygon_color));
272 r3_draw_poly(poly->num_vertices, points);
274 // And here go the lines.
275 draw_polygon_wireframe(points, num_points, outline_color);
279 /* ----- /-/-/-/-/-/-/-/-/ <<< (((((( /\ )))))) >>> \-\-\-\-\-\-\-\-\ ----- *\
281 The only difference between one visualization tool and the next is
282 the color of the polygons. We return TRUE if we draw a flat poly.
284 \* ----- \-\-\-\-\-\-\-\-\ <<< (((((( \/ )))))) >>> /-/-/-/-/-/-/-/-/ ----- */
285 bool poly_outline_by_flags(PortalPolygonCore *poly, r3s_phandle *points,
286 int MIP_level)
288 if (draw_solid_by_MIP_level) {
289 int i;
290 int light_level = 32;
291 for (i = MIP_level; i < 4; i++)
292 light_level += 40;
293 draw_polygon_outline(poly, points,
294 uiRGB(light_level, light_level, light_level),
295 uiRGB(light_level + 48, light_level + 48,
296 light_level + 48));
297 return TRUE;
300 if (draw_solid_by_cell) {
301 //draw_polygon_outline(poly, points, cur_cell->refs ? COLOR_WHITE : polygon_cell_color, COLOR_WHITE);
302 draw_polygon_outline(poly, points, polygon_cell_color, COLOR_WHITE);
303 return TRUE;
306 if (draw_solid_wireframe) {
307 draw_polygon_outline(poly, points, 0, COLOR_WHITE);
308 return TRUE;
311 if (draw_solid_by_poly_flags) {
312 draw_polygon_outline(poly, points, poly->flags, COLOR_WHITE);
313 return TRUE;
316 if (draw_solid_by_cell_flags) {
317 draw_polygon_outline(poly, points, _polygon_cell_flags_color,
318 COLOR_WHITE);
319 return TRUE;
322 return FALSE;
325 #endif // ~SHIP
328 extern int portal_clip_num;
330 extern BOOL g_lgd3d;
331 extern BOOL g_zbuffer;
333 extern int portal_hack_blend;
335 #ifndef SHIP
337 #define TEST_TEXTURE 1
339 extern ushort portal_color_convert(ushort);
341 static r3s_point bt_verts[4];
342 static r3s_phandle bt_vlist[4] = { bt_verts+0, bt_verts+1, bt_verts+2, bt_verts+3 };
344 static grs_bitmap blend_texture;
345 void portal_do_blendtest(void)
347 #ifdef RGB_LIGHTING
348 #ifdef RGB_888
349 ushort blend_tex_out[16];
351 r3s_texture texture = portal_get_texture(TEST_TEXTURE);
353 if (!g_lgd3d) return;
355 r3_start_block();
357 r3_set_texture(texture);
359 bt_verts[0].p.z = bt_verts[1].p.z =
360 bt_verts[2].p.z = bt_verts[3].p.z = 1.0;
361 bt_verts[0].grp.w = bt_verts[1].grp.w =
362 bt_verts[2].grp.w = bt_verts[3].grp.w = 1.0;
364 bt_verts[0].grp.sx = bt_verts[3].grp.sx = fix_make(10,0);
365 bt_verts[1].grp.sx = bt_verts[2].grp.sx = fix_make(60,0);
366 bt_verts[0].grp.sy = bt_verts[1].grp.sy = fix_make(10,0);
367 bt_verts[2].grp.sy = bt_verts[3].grp.sy = fix_make(60,0);
369 r3_set_polygon_context(R3_PL_POLYGON | R3_PL_TEXTURE | R3_PL_GOURAUD);
370 bt_verts[0].grp.i = bt_verts[1].grp.i = 0;
371 bt_verts[2].grp.i = bt_verts[3].grp.i = 1.0;
373 bt_verts[0].grp.u = bt_verts[3].grp.u = 0;
374 bt_verts[1].grp.u = bt_verts[2].grp.u = 1;
375 bt_verts[0].grp.v = bt_verts[1].grp.v = 0;
376 bt_verts[2].grp.v = bt_verts[3].grp.v = 1;
378 r3_draw_poly(4, bt_vlist);
380 bt_verts[0].grp.sx = bt_verts[3].grp.sx = fix_make(10+80,0);
381 bt_verts[1].grp.sx = bt_verts[2].grp.sx = fix_make(60+80,0);
383 r3_set_polygon_context(R3_PL_POLYGON | R3_PL_TEXTURE | R3_PL_UNLIT);
384 r3_draw_poly(4, bt_vlist);
386 bt_verts[0].grp.u = bt_verts[3].grp.u = 0.25 * 0.5;
387 bt_verts[1].grp.u = bt_verts[2].grp.u = 0.25 * 0.5;
388 bt_verts[0].grp.v = bt_verts[1].grp.v = 0.25 * 0.5;
389 bt_verts[2].grp.v = bt_verts[3].grp.v = 1 - 0.25*1.5;
391 // build lightmap texture in device space
392 gr_init_bitmap(&blend_texture, (char *) blend_tex_out, BMT_FLAT32, 0, 4, 4);
394 /* @TODO: figger out what todo.
395 blend_tex_out[0] = portal_color_convert(0);
396 blend_tex_out[4] = portal_color_convert(15+15*32+15*32*32);
397 blend_tex_out[8] = portal_color_convert(31+31*32+31*32*32);
399 blend_tex_out[2] = blend_tex_out[1] = blend_tex_out[0];
400 blend_tex_out[6] = blend_tex_out[5] = blend_tex_out[4];
401 blend_tex_out[10] = blend_tex_out[9] = blend_tex_out[8];
402 blend_tex_out[12] = blend_tex_out[8];
404 lgd3d_blend_multiply(portal_hack_blend);
405 lgd3d_set_blend(TRUE);
406 lgd3d_set_alpha(0.5);
407 r3_set_texture(&blend_texture);
408 r3_draw_poly(4, bt_vlist);
409 lgd3d_blend_normal();
410 lgd3d_set_blend(FALSE);
411 lgd3d_set_alpha(1.0);
413 r3_end_block();
415 lgd3d_unload_texture(&blend_texture);
416 #else // RGB_888
417 ushort blend_tex_out[16];
419 r3s_texture texture = portal_get_texture(TEST_TEXTURE);
421 if (!g_lgd3d) return;
423 r3_start_block();
425 r3_set_texture(texture);
427 bt_verts[0].p.z = bt_verts[1].p.z =
428 bt_verts[2].p.z = bt_verts[3].p.z = 1.0;
429 bt_verts[0].grp.w = bt_verts[1].grp.w =
430 bt_verts[2].grp.w = bt_verts[3].grp.w = 1.0;
432 bt_verts[0].grp.sx = bt_verts[3].grp.sx = fix_make(10,0);
433 bt_verts[1].grp.sx = bt_verts[2].grp.sx = fix_make(60,0);
434 bt_verts[0].grp.sy = bt_verts[1].grp.sy = fix_make(10,0);
435 bt_verts[2].grp.sy = bt_verts[3].grp.sy = fix_make(60,0);
437 r3_set_polygon_context(R3_PL_POLYGON | R3_PL_TEXTURE | R3_PL_GOURAUD);
438 bt_verts[0].grp.i = bt_verts[1].grp.i = 0;
439 bt_verts[2].grp.i = bt_verts[3].grp.i = 1.0;
441 bt_verts[0].grp.u = bt_verts[3].grp.u = 0;
442 bt_verts[1].grp.u = bt_verts[2].grp.u = 1;
443 bt_verts[0].grp.v = bt_verts[1].grp.v = 0;
444 bt_verts[2].grp.v = bt_verts[3].grp.v = 1;
446 r3_draw_poly(4, bt_vlist);
448 bt_verts[0].grp.sx = bt_verts[3].grp.sx = fix_make(10+80,0);
449 bt_verts[1].grp.sx = bt_verts[2].grp.sx = fix_make(60+80,0);
451 r3_set_polygon_context(R3_PL_POLYGON | R3_PL_TEXTURE | R3_PL_UNLIT);
452 r3_draw_poly(4, bt_vlist);
454 bt_verts[0].grp.u = bt_verts[3].grp.u = 0.25 * 0.5;
455 bt_verts[1].grp.u = bt_verts[2].grp.u = 0.25 * 0.5;
456 bt_verts[0].grp.v = bt_verts[1].grp.v = 0.25 * 0.5;
457 bt_verts[2].grp.v = bt_verts[3].grp.v = 1 - 0.25*1.5;
459 // build lightmap texture in device space
460 gr_init_bitmap(&blend_texture, (char *) blend_tex_out, BMT_FLAT16, 0, 4, 4);
462 blend_tex_out[0] = portal_color_convert(0);
463 blend_tex_out[4] = portal_color_convert(15+15*32+15*32*32);
464 blend_tex_out[8] = portal_color_convert(31+31*32+31*32*32);
465 blend_tex_out[2] = blend_tex_out[1] = blend_tex_out[0];
466 blend_tex_out[6] = blend_tex_out[5] = blend_tex_out[4];
467 blend_tex_out[10] = blend_tex_out[9] = blend_tex_out[8];
468 blend_tex_out[12] = blend_tex_out[8];
470 lgd3d_blend_multiply(portal_hack_blend);
471 lgd3d_set_blend(TRUE);
472 lgd3d_set_alpha(0.5);
473 r3_set_texture(&blend_texture);
474 r3_draw_poly(4, bt_vlist);
475 lgd3d_blend_normal();
476 lgd3d_set_blend(FALSE);
477 lgd3d_set_alpha(1.0);
479 r3_end_block();
481 lgd3d_unload_texture(&blend_texture);
482 #endif // RGB_888
483 #endif
485 #endif
488 extern void do_poly_linear(r3s_texture tex, int n, r3s_phandle *vpl, fix u_offset, fix v_offset);
490 // compute the P,M,N vectors and hand them to portal-tmappers
491 STATIC
492 void compute_tmapping(PortalPolygonRenderInfo *render, uchar not_light,
493 PortalLightMap *lt, r3s_point *anchor_point)
495 // compute texture mapping data by getting our u,v vectors,
496 // the anchor point, and the translation values, and translating
497 // the anchor point by the translation lengths
498 mxs_vector u_vec, v_vec, pt;
499 mxs_real usc, vsc;
501 usc = ((float) render->u_base) * 1.0 / (16.0*256.0); // u translation
502 vsc = ((float) render->v_base) * 1.0 / (16.0*256.0); // v translation
504 if (!not_light) {
505 usc -= ((float) lt->base_u) * 0.25;
506 vsc -= ((float) lt->base_v) * 0.25;
509 get_cached_vector(&u_vec, &render->tex_u);
510 get_cached_vector(&v_vec, &render->tex_v);
511 mx_scale_add_vec(&pt, &anchor_point->p, &u_vec, -usc);
512 mx_scale_add_vec(&pt, &pt, &v_vec, -vsc);
514 // This gives us our 3x3 texture perspective correction matrix.
515 g2pt_calc_uvw_deltas(&pt, &u_vec, &v_vec);
519 STATIC
520 int compute_mip3(PortalPolygonRenderInfo *render, PortalPlane *p)
522 // this is the fast way, which doesn't need to take a sqrt
523 // the slow way, which makes some sense, appears at end of file
524 double a,b,d;
525 mxs_vector eye;
526 mx_sub_vec(&eye, &portal_camera_loc, &render->center);
528 a = mx_mag2_vec(&eye);
529 b = mx_dot_vec(&eye, &p->normal);
531 if (b * b >= a*dot_clamp_2) {
532 d = premul * b * render->texture_mag;
533 // do first test because it doesn't require multiplies
534 if (d >= a) return 0;
535 // now binary search the remaining ones
536 if (d < a*0.25)
537 return d >= a*0.125 ? 3 : 4;
538 else
539 return d >= a*0.5 ? 1 : 2;
540 } else {
541 d = premul2 * render->texture_mag * render->texture_mag;
542 if (d >= a) return 0;
543 if (d < a*0.25*0.25)
544 return d >= a*0.125*0.125 ? 3 : 4;
545 else
546 return d >= a*0.5*0.5 ? 1 : 2;
550 int compute_mip(PortalPolygonRenderInfo *render, PortalPlane *p)
552 double sz,dist,k;
553 mxs_vector eye;
555 mx_sub_vec(&eye, &portal_camera_loc, &render->center);
556 dist = mx_mag_vec(&eye);
558 k = mx_dot_vec(&eye, &p->normal); // k/dist == foreshortening amount
560 // estimate distance to nearest point
561 dist = dist - render->texture_mag*2*(1-k/dist);
562 if (dist <= 0) return 0;
564 // compute foreshortening amount, note this uses post-modified
565 // dist, which is geometrically correct if the post-modified dist
566 // weren't an approximation
567 k /= dist;
568 if (k > 1.0) k = 1.0;
570 sz = premul * render->texture_mag / dist;
572 if (k < dot_clamp) k = dot_clamp;
573 sz *= k;
575 if (sz >= 1.0) return 0;
576 if (sz >= 0.5) return 1;
577 if (sz >= 0.25) return 2;
578 if (sz >= 0.125) return 3;
579 return 4;
582 extern void render_background_hack_clipped(int n, r3s_phandle *vlist);
583 extern int portbg_clip_sky(int, r3s_phandle *, r3s_phandle **);
585 // from portal.c. True if we are drawing new-style sky
586 extern BOOL bRenderNewSky;
588 void draw_background_hack(int n, r3s_phandle *vlist)
590 r3s_point *ph = cur_ph;
591 Vector *pool = cur_pool;
592 PortalCell *cell = cur_cell;
593 ushort *anim_light_index_list = cur_anim_light_index_list;
594 void *clip = r_clip;
595 uchar *vertex_list = r_vertex_list;
597 render_background_hack_clipped(n,vlist);
599 cur_ph = ph;
600 cur_pool = pool;
601 cur_cell = cell;
602 cur_anim_light_index_list = anim_light_index_list;
603 r_clip = clip;
604 r_vertex_list = vertex_list;
607 extern int portal_sky_id;
608 extern int portal_sky_spans;
610 // returns FALSE if it was totally transparent; return TRUE if it was
611 // non-transparent, even if not visible (e.g. clipped away)
612 STATIC
613 bool draw_surface(PortalPolygonCore *poly, PortalPolygonRenderInfo *render,
614 PortalLightMap *lt, int voff, void *clip)
616 int i, n,n2,n3, sc;
617 int desired_mip, mip_level;
618 r3s_texture texture;
619 grs_bitmap *tex=0;
620 fix corner_u_offset, corner_v_offset;
621 uchar not_light;
622 bool position_from_motion = FALSE;
623 r3s_phandle vlist[MAX_VERT], *valid3d, *final;
625 // get the raw, unlit texture
626 texture = portal_get_texture(render->texture_id);
627 // It is our impression (wsf) that this only happens for the sky hack:
628 if (!texture && !bRenderNewSky) {
629 n = poly->num_vertices;
630 for (i=0; i < n; ++i)
631 vlist[i] = &cur_ph[r_vertex_list[voff + i]];
633 // clip against the view cone
634 n2 = r3_clip_polygon(n, vlist, &valid3d);
635 if (n2 <= 2) { END_PROF; return TRUE; }
636 STAT(++stat_num_poly_considered;)
638 if (portal_clip_poly) {
639 // clip against the portal
640 n3 = portclip_clip_polygon(n2, valid3d, &final, clip);
641 if (n3 <= 2) { END_PROF; return TRUE; }
642 } else {
643 n3 = n2;
644 final = valid3d;
647 if (n3)
648 draw_background_hack(n3, final);
649 return FALSE; // an invisible portal, or background hack
652 not_light = (lt == NULL) || (poly->flags & RENDER_DOESNT_LIGHT);
654 // prepare the vertex list
655 n = poly->num_vertices;
656 if (n > MAX_VERT) Error(1, "draw_surface: too many vertices.\n");
658 for (i=0; i < n; ++i)
659 vlist[i] = &cur_ph[r_vertex_list[voff + i]];
661 // clip against the view cone
662 n2 = r3_clip_polygon(n, vlist, &valid3d);
663 if (n2 <= 2) { END_PROF; return TRUE; }
664 STAT(++stat_num_poly_considered;)
666 if (portal_clip_poly) {
667 // clip against the portal
668 n3 = portclip_clip_polygon(n2, valid3d, &final, clip);
669 if (n3 <= 2) { END_PROF; return TRUE; }
670 } else {
671 n3 = n2;
672 final = valid3d;
675 if (portal_clip_num) { // only true if it's the sky
676 n3 = portbg_clip_sky(n3, final, &final);
677 sc = texture->w;
678 } else {
679 desired_mip = compute_mip(render, &cur_cell->plane_list[poly->planeid]);
680 mip_level = 0;
681 while (desired_mip > 0) {
682 if (texture[1].w == 0) break; // not enough mip levels
683 ++mip_level;
684 --desired_mip;
685 ++texture;
687 sc = 64 >> mip_level; // texture->w;
690 #ifdef STATS_ON
691 ++stat_num_poly_drawn;
692 if (stat_num_poly_drawn > max_draw_polys) return TRUE;
693 #endif
695 #ifndef SHIP
696 if (poly_outline_by_flags(poly, vlist, mip_level)) {
697 END_PROF;
698 return TRUE;
700 #endif // ~SHIP
702 #ifdef DBG_ON
703 if (texture->w & (texture->w - 1))
704 Error(1, "Texture non-power-of-two in w!\n");
705 if (texture->h & (texture->h - 1))
706 Error(1, "Texture non-power-of-two in h!\n");
707 #endif
709 if ((poly->motion_index)
710 && (portal_cell_motion[poly->motion_index].in_motion)) {
711 mxs_vector u_vec, v_vec, pt;
713 portal_position_portal_texture(&u_vec, &v_vec, &pt,
714 &(cur_pool[r_vertex_list[voff + render->texture_anchor]]),
715 render, &cur_cell->plane_list[poly->planeid],
716 &portal_cell_motion[poly->motion_index]);
718 g2pt_calc_uvw_deltas(&pt, &u_vec, &v_vec);
719 position_from_motion = TRUE;
720 } else
721 compute_tmapping(render, not_light, lt, vlist[render->texture_anchor]);
723 // rescale based on mipmap scaling
724 for(i=0; i < 3; ++i) {
725 g2pt_tmap_data[3*i ] *= sc;
726 g2pt_tmap_data[3*i+1] *= sc;
729 // bug: if not lighting, don't deref 'lt'!!!
730 if (not_light) {
731 corner_u_offset = corner_v_offset = 0;
732 tex = texture;
733 } else {
734 corner_u_offset = corner_v_offset = fix_make(2, 0);
735 // go get it from surface cache or light it or whatever
736 tex = get_cached_surface(render, lt, texture, mip_level);
738 #ifdef EDITOR
739 // This should only happen if the lightmap is too big, which
740 // should only happen when someone's fiddling with the texture
741 // scale in the editor.
742 if (!tex) {
743 corner_u_offset = corner_v_offset = 0;
744 tex = texture;
746 #endif // EDITOR
750 if ((render->texture_id == portal_sky_id) && !bRenderNewSky)
752 switch (ptsky_type) {
753 case PTSKY_SPAN:
754 portal_sky_spans += ptsky_calc_spans(n3, final);
755 break;
756 case PTSKY_NONE:
757 goto poly_done;
760 else if (portal_sky_spans > 0)
762 ptsky_render_stars();
763 portal_sky_spans = 0;
766 if (linear_map)
767 do_poly_linear(tex, n3, final, corner_u_offset, corner_v_offset);
768 else
769 g2pt_poly_perspective_uv(tex, n3, final,
770 corner_u_offset, corner_v_offset, FALSE);
771 poly_done:
772 #ifndef SHIP
773 #if 0
774 if (draw_wireframe_around_tmap)
775 draw_polygon_wireframe(vlist, poly->num_vertices, COLOR_WHITE);
776 #else
777 if (draw_wireframe_around_tmap) {
778 draw_polygon_wireframe(final, n3, COLOR_WHITE+2);
779 draw_polygon_vertices(final, n3, COLOR_WHITE);
781 #endif
782 #endif // ~SHIP
784 END_PROF;
785 return TRUE;
788 #ifndef SHIP
789 void draw_wireframe(PortalPolygonCore *p, int voff, uint color)
791 int i,j,n;
792 n = p->num_vertices;
794 r3_set_color(guiScreenColor(color));
795 j = n-1;
796 for (i=0; i < n; ++i) {
797 r3_draw_line(&cur_ph[r_vertex_list[voff+i]],
798 &cur_ph[r_vertex_list[voff+j]]);
799 j = i;
804 void draw_cell_wireframe(PortalCell *cell, uint color)
806 int voff, i;
808 r3_start_block();
809 r3_set_clipmode(NEED_CLIP(cell) ? R3_CLIP : R3_NO_CLIP);
810 voff = 0;
812 for (i=0; i < cell->num_polys; ++i) {
813 draw_wireframe(&cell->poly_list[i], voff, color);
814 voff += cell->poly_list[i].num_vertices;
816 r3_end_block();
818 #endif
820 //// A hacked system for mapping distance in water to a clut id
822 static water_clut[32] =
824 0,0,1,1, 2,2,3,3, 4,4,5,5, 5,6,6,6,
825 7,7,7,8, 8,8,9,9, 9,10,10,10, 10,11,11,11
828 int compute_water_clut(mxs_real water_start, mxs_real water_end)
830 #if 0
831 int len;
833 // compute the clut to use after passing through water
834 // at distance water_start to water_end
836 len = (water_end - water_start)*10.0;
838 if (len > 255) len = 255;
839 if (len < 0) len = 0;
841 return water_clut[len >> 3];
842 #else
843 // explicitly force the clut to an amount at a middling
844 // distance under the old system
845 return water_clut[64 >> 3];
846 #endif
849 static void draw_many_objects(void);
851 extern bool background_needs_clut;
852 extern uchar background_clut[];
853 extern bool g2pt_span_clip;
855 // preload lightmaps for a single region
856 void portal_preload_lightmaps(int cell)
857 { PROF
859 PortalCell *r;
860 int n, voff;
861 uint light_bitmask;
862 PortalPolygonCore *poly;
863 PortalPolygonRenderInfo *render;
864 PortalLightMap *light;
866 r = WR_CELL(cell);
867 n = r->num_render_polys;
868 light = r->light_list;
870 if (r->num_full_bright || portal_render_from_texture || (n==0) || (light==NULL))
871 return;
873 voff=0;
874 poly = r->poly_list;
875 render = r->render_list;
877 cur_ph = POINTS(r);
878 cur_pool = r->vpool;
879 cur_cell = r;
880 cur_anim_light_index_list = r->anim_light_index_list;
881 r_vertex_list = r->vertex_list;
882 light_bitmask = r->changed_anim_light_bitmask;
884 while (n--) {
885 if (light->anim_light_bitmask & light_bitmask)
886 porthw_uncache_lightmap(render);
888 if (!(poly->flags & RENDER_DOESNT_LIGHT))
889 if (check_surface_visible(r, poly, voff))
890 porthw_preload_lightmap(render, light);
892 voff += poly->num_vertices;
893 ++poly;
894 ++render;
895 ++light;
897 r->changed_anim_light_bitmask = 0;
898 END_PROF;
901 extern void draw_surface_lgd3d(
902 PortalPolygonCore *poly,
903 PortalPolygonRenderInfo *render,
904 PortalLightMap *lt,
905 int voff,
906 void *clip);
908 BOOL portal_draw_lgd3d = TRUE;
909 BOOL portal_punt_draw_surface = FALSE;
911 static void check_and_draw_surface(
912 PortalPolygonCore *poly,
913 PortalPolygonRenderInfo *render,
914 PortalLightMap *light,
915 int voff,
916 PortalCell *r)
918 if ((!portal_punt_draw_surface) && check_surface_visible(r, poly, voff))
919 if (g_lgd3d)
920 draw_surface_lgd3d(poly, render, light, voff, CLIP_DATA(r));
921 else
922 draw_surface(poly, render, light, voff, CLIP_DATA(r));
926 /* ----- /-/-/-/-/-/-/-/-/ <<< (((((( /\ )))))) >>> \-\-\-\-\-\-\-\-\ ----- *\
928 draw a single cell
930 \* ----- \-\-\-\-\-\-\-\-\ <<< (((((( \/ )))))) >>> /-/-/-/-/-/-/-/-/ ----- */
931 extern void uncache_surface(PortalPolygonRenderInfo *);
932 void draw_region(int cell)
933 { PROF
935 PortalCell *r = WR_CELL(cell);
936 int n = r->num_render_polys;
937 int voff=0;
938 PortalPolygonCore *poly = r->poly_list;
939 PortalPolygonRenderInfo *render = r->render_list;
940 PortalLightMap *light = r->light_list;
941 ClutChain temp;
942 uchar clut;
944 if (r->num_full_bright || portal_render_from_texture)
945 light = NULL; // disable lighting if any light_brights shining on
947 // maybe i fixed this now, but it's a waste of time probably
948 // if (!n && OBJECTS(r) == 0) { END_PROF; return; }
950 // copy common data into globals for efficient communicating
951 // someday we should inline the function "draw_surface" and
952 // then get rid of these globals
954 cur_ph = POINTS(r);
955 cur_pool = r->vpool;
956 cur_cell = r;
957 cur_anim_light_index_list = r->anim_light_index_list;
958 r_vertex_list = r->vertex_list;
960 r_clip = CLIP_DATA(r);
962 // if we end in water, then we have to do a clut for
963 // this water which we haven't already added to our clut list
964 // note that you probably need to read the clut tree document
965 // to understand what's going on here.
967 if (r->medium == 255)
968 g2pt_clut = pt_clut_list[255]; // background?
969 else {
971 int mot = r->motion_index;
972 clut = 0;
974 if (mot > 0)
975 clut = pt_motion_haze_clut[mot];
977 if (!clut)
978 clut = pt_medium_haze_clut[r->medium];
980 if (clut) {
981 temp.clut_id = clut + compute_water_clut(ZWATER(r), DIST(r));
982 temp.clut_id2 = 0;
983 // this is a bit bogus, we shoud really do per-poly not per-cell
984 temp.next = CLUT(r).clut_id ? &CLUT(r) : 0;
985 g2pt_clut = pt_get_clut(&temp);
986 } else if (CLUT(r).clut_id) {
987 g2pt_clut = pt_get_clut(&CLUT(r));
988 } else {
989 g2pt_clut = 0;
993 // if span clipping, draw the objects first
994 if (g2pt_span_clip && OBJECTS(r) >= 0)
995 // at least one object
996 draw_many_objects();
998 if (!n && !(r->flags & CELL_OBSCURED)) goto skip_poly_draw;
1000 #ifdef STATS_ON
1001 stat_num_poly_raw += n;
1002 #endif
1004 // setup our default clip parameters (since we don't use
1005 // primitives, just the clipper, this isn't set automagically).
1006 // we could set it once elsewhere, and if we have self-lit polys
1007 // we might want to set it every poly. setting it here makes
1008 // us interact safely with object rendering.
1009 r3_set_clip_flags(0);
1011 #ifndef SHIP
1013 // The other polygon outline tools are handled in draw_surface().
1014 if (draw_solid_by_cell || draw_wireframe_around_poly) {
1015 polygon_cell_color = (((uint) (r->sphere_center.x * 85.12737321727
1016 - r->sphere_center.y * 123.33228937433)
1017 ^ (uint)(r->sphere_center.z * 311.22029342383)
1018 ^ r->num_portal_polys
1019 ^ (r->num_render_polys << 8)
1020 ^ (r->num_polys << 12)
1021 ^ r->num_vertices) & 0xffffff) | 0x1000000;
1024 if (draw_solid_by_cell_flags)
1025 _polygon_cell_flags_color = (r->flags) << 8;
1027 #endif // ~SHIP
1029 // now draw all the polygons
1031 r3_start_block();
1032 r3_set_clipmode(NEED_CLIP(r) ? R3_CLIP : R3_NO_CLIP);
1034 if (g_lgd3d) {
1035 r3_set_polygon_context(R3_PL_POLYGON | R3_PL_TEXTURE | R3_PL_UNLIT);
1036 if (portal_fog_on)
1037 lgd3d_set_fog_enable(!!(CELL_FLAGS(r) & CELL_FOG));
1040 if (light) {
1041 uint light_bitmask = r->changed_anim_light_bitmask;
1043 while (n--) {
1044 if (light->anim_light_bitmask & light_bitmask)
1045 uncache_surface(render);
1047 check_and_draw_surface(poly, render, light, voff, r);
1048 voff += poly->num_vertices;
1049 ++poly;
1050 ++render;
1051 ++light;
1053 r->changed_anim_light_bitmask = 0;
1054 } else {
1055 while (n--) {
1056 check_and_draw_surface(poly, render, NULL, voff, r);
1057 voff += poly->num_vertices;
1058 ++poly;
1059 ++render;
1063 // In hardware, if we're not going to draw terrain here then we
1064 // need to set the z for this cell the old-fashioned way.
1065 if (g_lgd3d && (r->flags & CELL_OBSCURED)) {
1066 r3s_phandle vlist[MAX_VERT];
1067 int i, num_vertices;
1069 ulong color = 0;
1070 /*//zb
1071 if (r->flags & CELL_FOGGED_OUT) {
1072 color = fog_r3_color;
1073 r->flags &= ~CELL_FOGGED_OUT;
1076 r3_set_color(guiScreenColor(color));
1078 r3_set_polygon_context(R3_PL_POLYGON | R3_PL_UNLIT | R3_PL_SOLID);
1080 voff = r->portal_vertex_list;
1081 poly = r->portal_poly_list;
1082 n = r->num_portal_polys;
1084 lgd3d_disable_palette();
1086 while (n--) {
1087 num_vertices = poly->num_vertices;
1089 if (check_surface_visible(r, poly, voff)) {
1090 for (i = 0; i < num_vertices; ++i)
1091 vlist[i] = &cur_ph[r_vertex_list[voff + i]];
1093 r3_draw_poly(num_vertices, vlist);
1096 voff += num_vertices;
1097 ++poly;
1100 lgd3d_enable_palette();
1103 r3_end_block();
1105 skip_poly_draw:
1107 #ifndef SHIP
1108 if (r->flags & (CELL_RENDER_WIREFRAME | CELL_RENDER_WIREFRAME_ONCE)
1109 || draw_wireframe_around_poly) {
1110 draw_cell_wireframe(r, COLOR_WHITE);
1111 r->flags &= ~CELL_RENDER_WIREFRAME_ONCE;
1113 #endif // ~SHIP
1115 if (!g2pt_span_clip && OBJECTS(r) >= 0)
1116 draw_many_objects();
1118 if (r->flags & 128)
1119 portal_sfx_callback(cell);
1121 END_PROF;
1124 extern void draw_surface_multitexture(
1125 PortalPolygonCore *poly,
1126 PortalPolygonRenderInfo *render,
1127 PortalLightMap *lt,
1128 int voff,
1129 void *clip);
1131 extern void draw_surface_lightmap_only(
1132 PortalPolygonCore *poly,
1133 PortalPolygonRenderInfo *render,
1134 PortalLightMap *lt,
1135 int voff,
1136 void *clip);
1138 extern void draw_surface_texture_only(
1139 PortalPolygonCore *poly,
1140 PortalPolygonRenderInfo *render,
1141 int voff,
1142 void *clip);
1144 BOOL portal_multitexture = FALSE;
1145 extern bool punt_hardware_lighting;
1147 void draw_region_lgd3d(int cell)
1148 { PROF
1150 PortalCell *r = WR_CELL(cell);
1151 int n = r->num_render_polys;
1152 int voff=0;
1153 PortalPolygonCore *poly = r->poly_list;
1154 PortalPolygonRenderInfo *render = r->render_list;
1155 PortalLightMap *light = r->light_list;
1157 // copy common data into globals for efficient communicating
1158 // someday we should inline the function "draw_surface" and
1159 // then get rid of these globals
1161 cur_ph = POINTS(r);
1162 cur_pool = r->vpool;
1163 cur_cell = r;
1164 r_vertex_list = r->vertex_list;
1166 r_clip = CLIP_DATA(r);
1168 if (!n && !(r->flags & CELL_OBSCURED)) goto skip_poly_draw1;
1170 #ifdef STATS_ON
1171 stat_num_poly_raw += n;
1172 #endif
1174 // setup our default clip parameters (since we don't use
1175 // primitives, just the clipper, this isn't set automagically).
1176 // we could set it once elsewhere, and if we have self-lit polys
1177 // we might want to set it every poly. setting it here makes
1178 // us interact safely with object rendering.
1179 r3_set_clip_flags(0);
1181 #ifndef SHIP
1183 // The other polygon outline tools are handled in draw_surface().
1184 if (draw_solid_by_cell || draw_wireframe_around_poly) {
1185 polygon_cell_color = (((uint) (r->sphere_center.x * 85.12737321727
1186 - r->sphere_center.y * 123.33228937433)
1187 ^ (uint)(r->sphere_center.z * 311.22029342383)
1188 ^ r->num_portal_polys
1189 ^ (r->num_render_polys << 8)
1190 ^ (r->num_polys << 12)
1191 ^ r->num_vertices) & 0xffffff) | 0x1000000;
1194 if (draw_solid_by_cell_flags)
1195 _polygon_cell_flags_color = (r->flags) << 8;
1197 #endif // ~SHIP
1199 // now draw all the polygons
1201 r3_start_block();
1202 r3_set_clipmode(NEED_CLIP(r) ? R3_CLIP : R3_NO_CLIP);
1204 if (n)
1206 r3_set_polygon_context(R3_PL_POLYGON | R3_PL_TEXTURE | R3_PL_UNLIT);
1207 if (portal_fog_on)
1208 lgd3d_set_fog_enable(!!(CELL_FLAGS(r) & CELL_FOG));
1209 if (portal_multitexture)
1210 { // we're doing single pass, multi-texturing!
1211 while (n--)
1213 if (check_surface_visible(r, poly, voff))
1214 draw_surface_multitexture(poly, render, light, voff, CLIP_DATA(r));
1216 voff += poly->num_vertices;
1217 ++poly;
1218 ++render;
1219 ++light;
1221 } else
1222 { // two pass, texture first
1223 while (n--) {
1224 if ((!portal_punt_draw_surface) && check_surface_visible(r, poly, voff))
1225 draw_surface_texture_only(poly, render, voff, CLIP_DATA(r));
1226 voff += poly->num_vertices;
1227 ++poly;
1228 ++render;
1231 // if we're not zbuffering, we need to do lightmaps immediately;
1232 // otherwise they get done in a completely seperate pass
1233 // (see draw_region_lightmap_only())
1234 if ((!g_zbuffer)&&(!punt_hardware_lighting))
1236 n = r->num_render_polys;
1237 poly = r->poly_list;
1238 render = r->render_list;
1239 voff = 0;
1241 lgd3d_blend_multiply(portal_hack_blend);
1242 lgd3d_set_blend(TRUE);
1243 #ifdef RGB_LIGHTING
1244 lgd3d_set_alpha(0.5);
1245 #endif
1246 while (n--) {
1247 if (!(poly->flags & RENDER_DOESNT_LIGHT))
1248 if (check_surface_visible(r, poly, voff))
1249 draw_surface_lightmap_only(poly, render, light, voff, CLIP_DATA(r));
1251 voff += poly->num_vertices;
1252 ++poly;
1253 ++render;
1254 ++light;
1256 lgd3d_blend_normal();
1257 lgd3d_set_blend(FALSE);
1258 #ifdef RGB_LIGHTING
1259 lgd3d_set_alpha(1.0);
1260 #endif
1265 // In hardware, if we're not going to draw terrain here then we
1266 // need to set the z for this portal the old-fashioned way.
1267 if (g_lgd3d && (r->flags & CELL_OBSCURED)) {
1268 r3s_phandle vlist[MAX_VERT];
1269 int i, num_vertices;
1271 ulong color = 0;
1272 /*//zb
1273 if (r->flags & CELL_FOGGED_OUT) {
1274 color = fog_r3_color;
1275 r->flags &= ~CELL_FOGGED_OUT;
1278 // r3_set_color(guiScreenColor(color));
1279 r3_set_color( color );
1281 r3_set_polygon_context(R3_PL_POLYGON | R3_PL_UNLIT | R3_PL_SOLID);
1283 voff = r->portal_vertex_list;
1284 poly = r->portal_poly_list;
1285 n = r->num_portal_polys;
1287 lgd3d_disable_palette();
1289 while (n--) {
1290 num_vertices = poly->num_vertices;
1292 if (check_surface_visible(r, poly, voff)) {
1293 for (i = 0; i < num_vertices; ++i)
1294 vlist[i] = &cur_ph[r_vertex_list[voff + i]];
1296 r3_draw_poly(num_vertices, vlist);
1299 voff += num_vertices;
1300 ++poly;
1303 lgd3d_enable_palette();
1306 r3_end_block();
1308 skip_poly_draw1:
1310 #ifndef SHIP
1311 if (r->flags & (CELL_RENDER_WIREFRAME | CELL_RENDER_WIREFRAME_ONCE)
1312 || draw_wireframe_around_poly) {
1313 draw_cell_wireframe(r, COLOR_WHITE);
1314 r->flags &= ~CELL_RENDER_WIREFRAME_ONCE;
1316 #endif // ~SHIP
1318 if (OBJECTS(r) >= 0)
1319 draw_many_objects();
1321 if (r->flags & 128)
1322 portal_sfx_callback(cell);
1324 END_PROF;
1327 void draw_region_lightmap_only(int cell)
1328 { PROF
1330 PortalCell *r = WR_CELL(cell);
1331 int n = r->num_render_polys;
1332 int voff=0;
1333 PortalPolygonCore *poly = r->poly_list;
1334 PortalPolygonRenderInfo *render = r->render_list;
1335 PortalLightMap *light = r->light_list;
1337 if (r->num_full_bright || portal_render_from_texture || (light==NULL))
1338 {END_PROF; return;}
1340 // copy common data into globals for efficient communicating
1341 // someday we should inline the function "draw_surface" and
1342 // then get rid of these globals
1344 cur_ph = POINTS(r);
1345 cur_pool = r->vpool;
1346 cur_cell = r;
1347 cur_anim_light_index_list = r->anim_light_index_list;
1348 r_vertex_list = r->vertex_list;
1350 r_clip = CLIP_DATA(r);
1352 #ifdef STATS_ON
1353 stat_num_poly_raw += n;
1354 #endif
1356 r3_set_clip_flags(0);
1358 r3_start_block();
1359 r3_set_clipmode(NEED_CLIP(r) ? R3_CLIP : R3_NO_CLIP);
1361 r3_set_polygon_context(R3_PL_POLYGON | R3_PL_TEXTURE | R3_PL_UNLIT);
1362 if (portal_fog_on)
1363 lgd3d_set_fog_enable(!!(CELL_FLAGS(r) & CELL_FOG));
1365 while (n--) {
1366 if (!(poly->flags & RENDER_DOESNT_LIGHT))
1367 if (check_surface_visible(r, poly, voff))
1368 draw_surface_lightmap_only(poly, render, light, voff, CLIP_DATA(r));
1370 voff += poly->num_vertices;
1371 ++poly;
1372 ++render;
1373 ++light;
1376 r3_end_block();
1379 END_PROF;
1383 // can't be bigger than 64 due to sorting limitation!
1384 // see sBlockedBits below!
1385 #define MAX_SORTED_OBJS 64
1387 Position* portal_object_pos_default(ObjID obj)
1389 static Position pos;
1390 return &pos;
1393 Position* (*portal_object_pos)(ObjID obj) = portal_object_pos_default;
1395 static int obj_compare(ObjVisibleID p, ObjVisibleID q)
1397 extern mxs_vector portal_camera_loc;
1398 ObjID x = vis_objs[p].obj;
1399 ObjID y = vis_objs[q].obj;
1401 // compute distance from camera
1403 float dist1, dist2;
1405 dist1 = mx_dist2_vec(&portal_camera_loc, &portal_object_pos(x)->loc.vec);
1406 dist2 = mx_dist2_vec(&portal_camera_loc, &portal_object_pos(y)->loc.vec);
1408 if (dist1 < dist2)
1409 return -1;
1410 else
1411 return dist1 > dist2;
1414 typedef struct sBlockedBits
1416 ulong Bits[2];
1417 } sBlockedBits;
1419 // nByte !! yields 0 or 1:
1420 // nSetBit is nBit shifted by 32 if nByte is 1, else not shifted
1421 #define SetBlockedBit(bits, nBit) \
1422 nByte = !!(nBit>>32); \
1423 nSetBit = nBit>>(nByte<<5); \
1424 bits.Bits[nByte] |= nSetBit
1426 // Assumes bit is set:
1427 #define ResetBlockedBit(bits, nBit) \
1428 nByte = !!(nBit>>32); \
1429 nSetBit = nBit>>(nByte<<5); \
1430 bits.Bits[nByte] ^= nSetBit
1432 // Just stuff a 0 there.
1433 #define ZeroBlockedBit(bits, nBit) \
1434 nByte = !!(nBit>>32); \
1435 nSetBit = nBit>>(nByte<<5); \
1436 bits.Bits[nByte] &= ~nSetBit
1438 // Returns last rvalue in sequence, and performs operations first to last:
1439 #define IsBitSet(bits, nBit) \
1440 (nByte = !!(nBit>>32), \
1441 nSetBit = nBit>>(nByte<<5), \
1442 bits.Bits[nByte] & nSetBit)
1444 void topological_sort(ObjVisibleID *obj_list, int n)
1446 // int x,y, i,j, b;
1447 int x,y, i,j;
1449 ulong nByte; // Helper for macros
1450 ulong nSetBit; // Helper for macros
1451 ulong nVal;
1452 sBlockedBits blocked[MAX_SORTED_OBJS];
1453 sBlockedBits b;
1455 // used to be this, which gave us at most 32 objects we could sort:
1456 // ulong blocked[MAX_SORTED_OBJS];
1458 ObjVisibleID my_list[MAX_SORTED_OBJS];
1460 // we should special case 2 (and maybe 3) objects!
1462 if (n > MAX_SORTED_OBJS) n = MAX_SORTED_OBJS;
1464 memcpy(my_list, obj_list, sizeof(my_list[0])*n);
1466 memset(blocked, 0, sizeof(sBlockedBits)*MAX_SORTED_OBJS);
1468 // Well, it turns out player is always in way of camera. Because we're hardware, we only
1469 // really need to sort objects that are alpha, yet this sorter sorts ALL objects, which is
1470 // unnecessary. We should move sorting into rendobj level, and only sort alpha objects.
1473 // collect all n^2 comparison data
1474 for (x=0; x < n; ++x) {
1475 b.Bits[0] = b.Bits[1] = 0;
1476 for (y=0; y < n; ++y) {
1477 if (x != y && portal_object_blocks(vis_objs[obj_list[y]].obj,
1478 vis_objs[obj_list[x]].obj)) {
1479 // check if they form a cycle
1480 // if (y < x && (blocked[y] & (1 << x))) {
1481 if (y < x && IsBitSet(blocked[y], (1<<x))) {
1482 // they do, so they're too close to each other...
1483 // compare their centers: dist-x > dist-y ???
1484 if (obj_compare(obj_list[x],obj_list[y]) > 0) {
1485 // y is closer, so no x blocks y
1486 ResetBlockedBit(blocked[y],(1<<x));
1487 SetBlockedBit(b, (1<<y));
1488 // blocked[y] ^= 1 << x;
1489 // b |= 1 << y; // yes y blocks x
1491 // else say x blocks y (already coded), and no y blocks x
1492 } else
1493 // no cycle, so y blocks x
1494 SetBlockedBit(b, (1<<y));
1495 // b |= 1 << y;
1498 blocked[x] = b;
1501 // ok, now we know everything. search for somebody who is
1502 // unblocked
1503 for (i=0; i < n; ++i) {
1504 // find guy #n
1505 for (j=0; j < n; ++j)
1506 if (my_list[j] != VISOBJ_NULL && !blocked[j].Bits[0] && !blocked[j].Bits[1])
1507 // if (my_list[j] != VISOBJ_NULL && !blocked[j])
1508 goto use_j;
1509 // nobody is unblocked... oops... must break cycle
1510 // we should use farthest guy, but let's just hack it
1511 #ifndef SHIP
1512 // mprintf("Breaking object-sorting cycle.\n");
1513 #endif
1514 #ifndef SHIP
1515 for (j=0; j < n; ++j)
1516 if (my_list[j] != VISOBJ_NULL)
1517 goto use_j;
1518 Error(1, "Ran out of objects inside object sorter.");
1519 #endif
1520 use_j:
1521 obj_list[i] = my_list[j];
1522 my_list[j] = VISOBJ_NULL;
1523 blocked[j].Bits[0] = blocked[j].Bits[1] = 0;
1524 // blocked[j] = 0;
1525 // unblock anybody this guy blocked
1526 nVal = 1 << j;
1527 // b = 1 << j;
1528 for (j=0; j < n; ++j)
1529 ZeroBlockedBit(blocked[j], nVal);
1530 // if (blocked[j] & b)
1531 // blocked[j] ^= b;
1535 extern bool obj_dealt[]; // HACK: need real object dealt flags
1536 extern bool obj_hide[]; // HACK
1537 void core_render_object(ObjVisibleID id, uchar *clut)
1539 if (!obj_hide[vis_objs[id].obj])
1540 portal_render_object(vis_objs[id].obj, clut, vis_objs[id].fragment);
1541 obj_dealt[vis_objs[id].obj] = 0;
1544 extern long (*portal_get_time)(void);
1545 extern int stat_num_object_ms;
1546 bool disable_topsort;
1547 static void draw_many_objects(void)
1549 PortalCell *r = cur_cell;
1550 ObjVisibleID id = OBJECTS(r);
1551 uchar *clut = g2pt_clut;
1552 ObjVisibleID obj_list[MAX_SORTED_OBJS];
1553 int num=0, i;
1555 if (portal_sky_spans > 0)
1556 ptsky_render_stars();
1558 #ifdef STATS_ON
1559 stat_num_object_ms -= portal_get_time();
1560 #endif
1562 // reset the polygon context so the fact we
1563 // stuffed the r3_clip_flags won't screw us up...
1564 // there must be a better way to do this...
1565 if (!g_lgd3d) {
1566 r3_set_polygon_context(0);
1568 // because portal goes behind r3d's back to access g2 directly,
1569 // we need to set this flag as well
1570 r3d_do_setup = TRUE;
1573 // put the first MAX_SORTED_OBJS in an array
1575 while (id >= 0 && num < MAX_SORTED_OBJS) {
1576 if (!obj_hide[vis_objs[id].obj])
1577 obj_list[num++] = id;
1578 else
1579 obj_dealt[vis_objs[id].obj] = 0;
1580 id = vis_objs[id].next_visobj;
1583 // if there are still more objects, just render 'em
1585 while (id >= 0) {
1586 Warning(("draw_many_objects: Too many objects to sort.\n"));
1587 core_render_object(id, clut);
1588 id = vis_objs[id].next_visobj;
1591 // now sort and draw the remaining objects
1593 if (num == 1)
1594 core_render_object(obj_list[0], clut);
1595 else if (num) {
1597 // wsf: if we're in hardware, don't need to sort all objects. We're moving sort
1598 // into rendobj level, and only sorting alpha objects, that don't write into z-buffer.
1599 if (!disable_topsort && !g_lgd3d)
1600 topological_sort(obj_list, num);
1602 // the order of drawing should depend on render_back_to_front-ness
1603 for (i=num-1; i >= 0; --i)
1604 core_render_object(obj_list[i], clut);
1607 #ifdef STATS_ON
1608 stat_num_object_ms += portal_get_time();
1609 #endif
1611 // restore g2pt_clut in case object rendering trashed it
1612 g2pt_clut = clut;
1615 BOOL sphere_intersects_plane(mxs_vector *center, float radius, PortalPlane *p)
1617 // compute distance from sphere center to plane
1618 float dist = mx_dot_vec(center, &p->normal) + p->plane_constant;
1620 // if sphere is at least radius away, don't bother
1621 if (dist >= radius)
1622 return FALSE;
1624 // if sphere is at least radius _behind_ the plane, we don't need
1625 // to draw anything, but (a) that should never happen, and (b) we
1626 // don't have a distinct return value to indicate it, anyway
1628 return TRUE;
1631 #define FLOAT_PTR_NEG(x) (* (int *) (x) < -0.005)
1633 BOOL bbox_intersects_plane(mxs_vector *bbox_min, mxs_vector *bbox_max,
1634 PortalPlane *p)
1636 mxs_vector temp;
1637 float dist;
1639 // find the point as far _behind_ the plane as possible
1641 if (FLOAT_PTR_NEG(&p->normal.x))
1642 temp.x = bbox_max->x;
1643 else
1644 temp.x = bbox_min->x;
1646 if (FLOAT_PTR_NEG(&p->normal.y))
1647 temp.y = bbox_max->y;
1648 else
1649 temp.y = bbox_min->y;
1651 if (FLOAT_PTR_NEG(&p->normal.z))
1652 temp.z = bbox_max->z;
1653 else
1654 temp.z = bbox_min->z;
1656 dist = mx_dot_vec(&temp, &p->normal) + p->plane_constant;
1657 if (dist >= 0)
1658 return FALSE;
1660 return TRUE;
1663 static int num_pushed;
1665 void portal_push_clip_planes(
1666 mxs_vector *bbox_min, mxs_vector *bbox_max,
1667 mxs_vector *sphere_center, float radius)
1669 int i,plane_count = cur_cell->num_planes;
1670 PortalPlane *pl = cur_cell->plane_list;
1672 num_pushed = 0;
1673 for (i=0; i < plane_count; ++i, ++pl) {
1674 mxs_plane p;
1675 if (sphere_center && !sphere_intersects_plane(sphere_center, radius, pl))
1676 continue;
1677 if (bbox_min && !bbox_intersects_plane(bbox_min, bbox_max, pl))
1678 continue;
1679 ++num_pushed;
1680 p.x = pl->normal.x;
1681 p.y = pl->normal.y;
1682 p.z = pl->normal.z;
1683 p.d = pl->plane_constant+0.002;
1684 r3_push_clip_plane(&p);
1688 void portal_pop_clip_planes(void)
1690 int i;
1691 for (i=0; i < num_pushed; ++i)
1692 r3_pop_clip_plane();
1696 ////////// code that could probably be deleted /////////
1698 // (old palette-based lighting stuff)
1700 static float rescale(float val, float *map)
1702 // find where 'val' is from 0..32
1703 int i;
1704 float where;
1706 if (val == 1.0) return 1.0;
1708 for (i=0; val >= map[i]; ++i);
1709 // ok, now val < map[i]
1711 --i;
1712 // map[i] <= val < map[i+1]
1714 // now determine where val occurs if it were linear interpolated
1715 // map[i] + where * (map[j] - map[i]) == val
1717 where = (val - map[i]) / (map[i+1] - map[i]);
1718 return (where + i) / 32.0;
1722 // is any of this used in the new regime?
1723 uchar length_mapping[1024];
1724 void init_portal_shading(int dark, int light)
1726 #if 0
1727 int i, j;
1728 float last, step, cur, val;
1729 float map[33];
1731 // map 0 -> dark + 0.5
1732 // map 128 -> light + 0.5
1734 // i/128 * (light - dark) + dark + 0.5
1736 // except we want to remap them to account for
1737 // nonlinearity; i/128 -> 0..1, but now we want
1738 // to deal with the fact that the output device
1739 // really takes x (0..1) and computes
1740 // a decaying series, 0.1 ^ (1-x), which outputs
1741 // from 0.1...1
1743 // Closed form this seems messy, so I'll use a lookup
1744 // table!
1746 // first compute the table which decays the way the
1747 // real thing decays
1749 cur = 1.0;
1750 // cur * step^32 == 0.1
1751 // step^32 == 0.1
1752 // step = (0.1)^1/32
1754 step = pow(0.1, 1.0/32);
1756 for (i=32; i >= 0; --i) {
1757 // rescale it from 0.1--1.0 into 0.0--1.0
1758 val = (cur-0.1)/0.9;
1759 map[i] = val;
1760 cur *= step;
1762 map[32] = 1.0;
1763 map[0] = 0;
1765 for (i=0; i < 128; ++i)
1766 light_mapping[i] =
1767 (rescale((float) i / 128.0, map) * (light - dark)
1768 + dark + 0.5) * 65536;
1770 last = light_mapping[i-1];
1771 for ( ; i < 256; ++i)
1772 light_mapping[i] = last;
1774 for (i=0; i < 256; ++i)
1775 light_mapping_dithered[i] = light_mapping[i]*2;
1777 for (i=1; i <= 16; ++i)
1778 length_mapping[i] = i-1;
1780 j = 3;
1781 for (; i < 1024; ++i) {
1782 if (i >= (4 << j)) ++j;
1783 if (i >= (3 << j)) length_mapping[i] = (j+5)*2+1;
1784 else length_mapping[i] = (j+5)*2;
1786 #endif
1789 ///// find the clipping data for a portal /////
1790 // returns TRUE if poly non empty
1791 // we save away info about the final polygon shape
1792 // in case we end up needing more info; e.g. the
1793 // water comes back and checks out the average z depth
1795 static r3s_phandle port_vr[MAX_VERT], *port_p;
1796 static int port_n;
1798 ClipData *PortalGetClipInfo(PortalCell *cell, PortalPolygonCore *poly,
1799 int voff, void *clip)
1801 int i, n = poly->num_vertices;
1802 uchar *vlist = cell->vertex_list + voff;
1804 if (n > MAX_VERT)
1805 Error(1, "PortalGetClipInfo: Portal has too many vertices.\n");
1807 for (i=0; i < n; ++i)
1808 port_vr[i] = &cur_ph[*vlist++];
1810 n = r3_clip_polygon(n, port_vr, &port_p);
1811 if (n <= 2)
1812 return NULL;
1814 port_n = n;
1816 return PortalClipFromPolygon(n, port_p, clip);
1819 mxs_real compute_portal_z(void)
1821 mxs_real z=0;
1822 int i;
1824 for (i=0; i < port_n; ++i)
1825 z += port_p[i]->p.z;
1827 // compute the average
1828 z /= port_n;
1830 return (z > 0.1 ? z : 0.1);