]> icculus.org git repositories - btb/d2x.git/blob - main/render.c
more header cleanup
[btb/d2x.git] / main / render.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-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 /*
15  *
16  * Rendering Stuff
17  *
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <conf.h>
22 #endif
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "inferno.h"
29 #include "error.h"
30 #include "texmap.h"
31 #include "mono.h"
32 #include "3d.h"
33 #include "key.h"
34 #include "u_mem.h"
35 #ifdef OGL
36 #include "ogl_init.h"
37 #endif
38 #ifdef EDITOR
39 #include "editor/editor.h"
40 #endif
41
42
43 #define INITIAL_LOCAL_LIGHT (F1_0/4)    // local light value in segment of occurence (of light emission)
44
45 //used for checking if points have been rotated
46 int     Clear_window_color=-1;
47 int     Clear_window=2; // 1 = Clear whole background window, 2 = clear view portals into rest of world, 0 = no clear
48
49 int RL_framecount=-1;
50 short Rotated_last[MAX_VERTICES];
51
52 // When any render function needs to know what's looking at it, it should 
53 // access Viewer members.
54 object * Viewer = NULL;
55
56 vms_vector Viewer_eye;  //valid during render
57
58 int     N_render_segs;
59
60 #ifndef MACINTOSH
61 fix Render_zoom = 0x9000;                                       //the player's zoom factor
62 #else
63 fix Render_zoom = 0xB000;
64 #endif
65
66 #ifndef NDEBUG
67 ubyte object_rendered[MAX_OBJECTS];
68 #endif
69
70 #define DEFAULT_RENDER_DEPTH 16
71 int Render_depth=DEFAULT_RENDER_DEPTH;          //how many segments deep to render
72
73 int     Detriangulation_on = 1;                                 // 1 = allow rendering of triangulated side as a quad, 0 = don't allow
74
75 #ifdef EDITOR
76 int     Render_only_bottom=0;
77 int     Bottom_bitmap_num = 9;
78 #endif
79
80 fix     Face_reflectivity = (F1_0/2);
81
82 #if 0           //this stuff could probably just be deleted
83
84 int inc_render_depth(void)
85 {
86         return ++Render_depth;
87 }
88
89 int dec_render_depth(void)
90 {
91         return Render_depth==1?Render_depth:--Render_depth;
92 }
93
94 int reset_render_depth(void)
95 {
96         return Render_depth = DEFAULT_RENDER_DEPTH;
97 }
98
99 #endif
100
101 #ifdef EDITOR
102 int _search_mode = 0;                   //true if looking for curseg,side,face
103 short _search_x,_search_y;      //pixel we're looking at
104 int found_seg,found_side,found_face,found_poly;
105 #else
106 #define _search_mode 0
107 #endif
108
109 #ifdef NDEBUG           //if no debug code, set these vars to constants
110
111 #define Outline_mode 0
112 #define Show_only_curside 0
113
114 #else
115
116 int Outline_mode=0,Show_only_curside=0;
117
118 int toggle_outline_mode(void)
119 {
120         return Outline_mode = !Outline_mode;
121 }
122
123 int toggle_show_only_curside(void)
124 {
125         return Show_only_curside = !Show_only_curside;
126 }
127
128 void draw_outline(int nverts,g3s_point **pointlist)
129 {
130         int i;
131
132         gr_setcolor(BM_XRGB(63,63,63));
133
134         for (i=0;i<nverts-1;i++)
135                 g3_draw_line(pointlist[i],pointlist[i+1]);
136
137         g3_draw_line(pointlist[i],pointlist[0]);
138
139 }
140 #endif
141
142 grs_canvas * reticle_canvas = NULL;
143
144 void free_reticle_canvas()
145 {
146         if (reticle_canvas)     {
147                 d_free( reticle_canvas->cv_bitmap.bm_data );
148                 d_free( reticle_canvas );
149                 reticle_canvas  = NULL;
150         }
151 }
152
153 extern void show_reticle(int force_big);
154
155 // Draw the reticle in 3D for head tracking
156 void draw_3d_reticle(fix eye_offset)
157 {
158         g3s_point       reticle_points[4];
159         g3s_uvl         uvl[4];
160         g3s_point       *pointlist[4];
161         int                     i;
162         vms_vector v1, v2;
163         grs_canvas *saved_canvas;
164         int saved_interp_method;
165
166 //      if (!Use_player_head_angles) return;
167         
168         for (i=0; i<4; i++ )    {
169                 pointlist[i] = &reticle_points[i];
170                 uvl[i].l = MAX_LIGHT;
171         }
172         uvl[0].u = 0; uvl[0].v = 0;
173         uvl[1].u = F1_0; uvl[1].v = 0;
174         uvl[2].u = F1_0; uvl[2].v = F1_0;
175         uvl[3].u = 0; uvl[3].v = F1_0;
176
177         vm_vec_scale_add( &v1, &Viewer->pos, &Viewer->orient.fvec, F1_0*4 );
178         vm_vec_scale_add2(&v1,&Viewer->orient.rvec,eye_offset);
179
180         vm_vec_scale_add( &v2, &v1, &Viewer->orient.rvec, -F1_0*1 );
181         vm_vec_scale_add2( &v2, &Viewer->orient.uvec, F1_0*1 );
182         g3_rotate_point(&reticle_points[0],&v2);
183
184         vm_vec_scale_add( &v2, &v1, &Viewer->orient.rvec, +F1_0*1 );
185         vm_vec_scale_add2( &v2, &Viewer->orient.uvec, F1_0*1 );
186         g3_rotate_point(&reticle_points[1],&v2);
187
188         vm_vec_scale_add( &v2, &v1, &Viewer->orient.rvec, +F1_0*1 );
189         vm_vec_scale_add2( &v2, &Viewer->orient.uvec, -F1_0*1 );
190         g3_rotate_point(&reticle_points[2],&v2);
191
192         vm_vec_scale_add( &v2, &v1, &Viewer->orient.rvec, -F1_0*1 );
193         vm_vec_scale_add2( &v2, &Viewer->orient.uvec, -F1_0*1 );
194         g3_rotate_point(&reticle_points[3],&v2);
195
196         if ( reticle_canvas == NULL )   {
197                 reticle_canvas = gr_create_canvas(64,64);
198                 if ( !reticle_canvas )
199                         Error( "Couldn't malloc reticle_canvas" );
200                 atexit( free_reticle_canvas );
201                 reticle_canvas->cv_bitmap.bm_handle = 0;
202                 reticle_canvas->cv_bitmap.bm_flags = BM_FLAG_TRANSPARENT;
203         }
204
205         saved_canvas = grd_curcanv;
206         gr_set_current_canvas(reticle_canvas);
207         gr_clear_canvas( TRANSPARENCY_COLOR );          // Clear to Xparent
208         show_reticle(1);
209         gr_set_current_canvas(saved_canvas);
210         
211         saved_interp_method=Interpolation_method;
212         Interpolation_method    = 3;            // The best, albiet slowest.
213         g3_draw_tmap(4,pointlist,uvl,&reticle_canvas->cv_bitmap);
214         Interpolation_method    = saved_interp_method;
215 }
216
217
218 extern fix Seismic_tremor_magnitude;
219
220 fix flash_scale;
221
222 #define FLASH_CYCLE_RATE f1_0
223
224 fix Flash_rate = FLASH_CYCLE_RATE;
225
226 //cycle the flashing light for when mine destroyed
227 void flash_frame()
228 {
229         static fixang flash_ang=0;
230
231         if (!Control_center_destroyed && !Seismic_tremor_magnitude)
232                 return;
233
234         if (Endlevel_sequence)
235                 return;
236
237         if (PaletteBlueAdd > 10 )               //whiting out
238                 return;
239
240 //      flash_ang += fixmul(FLASH_CYCLE_RATE,FrameTime);
241         if (Seismic_tremor_magnitude) {
242                 fix     added_flash;
243
244                 added_flash = abs(Seismic_tremor_magnitude);
245                 if (added_flash < F1_0)
246                         added_flash *= 16;
247
248                 flash_ang += fixmul(Flash_rate, fixmul(FrameTime, added_flash+F1_0));
249                 fix_fastsincos(flash_ang,&flash_scale,NULL);
250                 flash_scale = (flash_scale + F1_0*3)/4; //      gets in range 0.5 to 1.0
251         } else {
252                 flash_ang += fixmul(Flash_rate,FrameTime);
253                 fix_fastsincos(flash_ang,&flash_scale,NULL);
254                 flash_scale = (flash_scale + f1_0)/2;
255                 if (Difficulty_level == 0)
256                         flash_scale = (flash_scale+F1_0*3)/4;
257         }
258
259
260 }
261
262 // ----------------------------------------------------------------------------
263 //      Render a face.
264 //      It would be nice to not have to pass in segnum and sidenum, but
265 //      they are used for our hideously hacked in headlight system.
266 //      vp is a pointer to vertex ids.
267 //      tmap1, tmap2 are texture map ids.  tmap2 is the pasty one.
268 void render_face(int segnum, int sidenum, int nv, short *vp, int tmap1, int tmap2, uvl *uvlp, int wid_flags)
269 {
270         // -- Using new headlight system...fix                  face_light;
271         grs_bitmap  *bm;
272 #ifdef OGL
273         grs_bitmap  *bm2 = NULL;
274 #endif
275
276         fix                     reflect;
277         uvl                     uvl_copy[8];
278         int                     i;
279         g3s_point       *pointlist[8];
280
281         Assert(nv <= 8);
282
283         for (i=0; i<nv; i++) {
284                 uvl_copy[i] = uvlp[i];
285                 pointlist[i] = &Segment_points[vp[i]];
286         }
287
288         //handle cloaked walls
289         if (wid_flags & WID_CLOAKED_FLAG) {
290                 int wall_num = Segments[segnum].sides[sidenum].wall_num;
291                 Assert(wall_num != -1);
292                 Gr_scanline_darkening_level = Walls[wall_num].cloak_value;
293                 gr_setcolor(BM_XRGB(0, 0, 0));  // set to black (matters for s3)
294
295                 g3_draw_poly(nv, pointlist);    // draw as flat poly
296
297                 Gr_scanline_darkening_level = GR_FADE_LEVELS;
298
299                 return;
300         }
301
302         // -- Using new headlight system...face_light = -vm_vec_dot(&Viewer->orient.fvec,norm);
303
304         if (tmap1 >= NumTextures) {
305                 mprintf((0,"Invalid tmap number %d, NumTextures=%d, changing to 0\n",tmap1,NumTextures));
306
307         #ifndef RELEASE
308                 Int3();
309         #endif
310
311                 Segments[segnum].sides[sidenum].tmap_num = 0;
312         }
313
314 #ifdef OGL
315         if (ogl_alttexmerge){
316                 PIGGY_PAGE_IN(Textures[tmap1]);
317                 bm = &GameBitmaps[Textures[tmap1].index];
318                 if (tmap2){
319                         PIGGY_PAGE_IN(Textures[tmap2&0x3FFF]);
320                         bm2 = &GameBitmaps[Textures[tmap2&0x3FFF].index];
321                 }
322                 if (!OGL_SUPER_TRANSPARENT_OK && bm2 && (bm2->bm_flags & BM_FLAG_SUPER_TRANSPARENT))
323                 {
324                         bm = texmerge_get_cached_bitmap( tmap1, tmap2 );
325                         bm2 = NULL;
326                 }
327         } else
328 #endif
329
330                 // New code for overlapping textures...
331                 if (tmap2 != 0) {
332                         bm = texmerge_get_cached_bitmap( tmap1, tmap2 );
333                 } else {
334                         bm = &GameBitmaps[Textures[tmap1].index];
335                         PIGGY_PAGE_IN(Textures[tmap1]);
336                 }
337
338         Assert( !(bm->bm_flags & BM_FLAG_PAGED_OUT) );
339
340         //reflect = fl2f((1.0-TmapInfo[p->tmap_num].reflect)/2.0 + 0.5);
341         //reflect = fl2f((1.0-TmapInfo[p->tmap_num].reflect));
342
343         reflect = Face_reflectivity;            // f1_0;        //until we figure this stuff out...
344
345         //set light values for each vertex & build pointlist
346         {
347                 int i;
348
349                 // -- Using new headlight system...face_light = fixmul(face_light,reflect);
350
351                 for (i=0;i<nv;i++) {
352
353                         //the uvl struct has static light already in it
354
355                         //scale static light for destruction effect
356                         if (Control_center_destroyed || Seismic_tremor_magnitude)       //make lights flash
357                                 uvl_copy[i].l = fixmul(flash_scale,uvl_copy[i].l);
358
359                         //add in dynamic light (from explosions, etc.)
360                         uvl_copy[i].l += Dynamic_light[vp[i]];
361
362                         //add in light from player's headlight
363                         // -- Using new headlight system...uvl_copy[i].l += compute_headlight_light(&Segment_points[vp[i]].p3_vec,face_light);
364
365                         //saturate at max value
366                         if (uvl_copy[i].l > MAX_LIGHT)
367                                 uvl_copy[i].l = MAX_LIGHT;
368
369                 }
370         }
371
372 #ifdef EDITOR
373         if ((Render_only_bottom) && (sidenum == WBOTTOM))
374                 g3_draw_tmap(nv,pointlist,(g3s_uvl *) uvl_copy,&GameBitmaps[Textures[Bottom_bitmap_num].index]);
375         else
376 #endif
377
378 #ifdef OGL
379                 if (bm2){
380                         g3_draw_tmap_2(nv,pointlist,(g3s_uvl *) uvl_copy,bm,bm2,((tmap2&0xC000)>>14) & 3);
381                 }else
382 #endif
383                         g3_draw_tmap(nv,pointlist,(g3s_uvl *) uvl_copy,bm);
384
385 #ifndef NDEBUG
386         if (Outline_mode) draw_outline(nv, pointlist);
387 #endif
388 }
389
390 #ifdef EDITOR
391 // ----------------------------------------------------------------------------
392 //      Only called if editor active.
393 //      Used to determine which face was clicked on.
394 void check_face(int segnum, int sidenum, int facenum, int nv, short *vp, int tmap1, int tmap2, uvl *uvlp)
395 {
396         int     i;
397
398         if (_search_mode) {
399                 int save_lighting;
400                 grs_bitmap *bm;
401                 uvl uvl_copy[8];
402                 g3s_point *pointlist[4];
403
404                 if (tmap2 > 0 )
405                         bm = texmerge_get_cached_bitmap( tmap1, tmap2 );
406                 else
407                         bm = &GameBitmaps[Textures[tmap1].index];
408
409                 for (i=0; i<nv; i++) {
410                         uvl_copy[i] = uvlp[i];
411                         pointlist[i] = &Segment_points[vp[i]];
412                 }
413
414                 gr_setcolor(0);
415                 gr_pixel(_search_x,_search_y);  //set our search pixel to color zero
416                 gr_setcolor(1);                                 //and render in color one
417  save_lighting = Lighting_on;
418  Lighting_on = 2;
419                 //g3_draw_poly(nv,vp);
420                 g3_draw_tmap(nv,pointlist, (g3s_uvl *)uvl_copy, bm);
421  Lighting_on = save_lighting;
422
423                 if (gr_ugpixel(&grd_curcanv->cv_bitmap,_search_x,_search_y) == 1) {
424                         found_seg = segnum;
425                         found_side = sidenum;
426                         found_face = facenum;
427                 }
428         }
429 }
430 #endif
431
432 fix     Tulate_min_dot = (F1_0/4);
433 //--unused-- fix        Tulate_min_ratio = (2*F1_0);
434 fix     Min_n0_n1_dot   = (F1_0*15/16);
435
436 extern int contains_flare(segment *segp, int sidenum);
437 extern fix      Obj_light_xlate[16];
438
439 // -----------------------------------------------------------------------------------
440 //      Render a side.
441 //      Check for normal facing.  If so, render faces on side dictated by sidep->type.
442 void render_side(segment *segp, int sidenum)
443 {
444         short                   vertnum_list[4];
445         side                    *sidep = &segp->sides[sidenum];
446         vms_vector      tvec;
447         fix                     v_dot_n0, v_dot_n1;
448         uvl                     temp_uvls[3];
449         fix                     min_dot, max_dot;
450         vms_vector  normals[2];
451         int                     wid_flags;
452
453
454         wid_flags = WALL_IS_DOORWAY(segp,sidenum);
455
456         if (!(wid_flags & WID_RENDER_FLAG))             //if (WALL_IS_DOORWAY(segp, sidenum) == WID_NO_WALL)
457                 return;
458
459 #ifdef COMPACT_SEGS
460         get_side_normals(segp, sidenum, &normals[0], &normals[1] );
461 #else
462         normals[0] = segp->sides[sidenum].normals[0];
463         normals[1] = segp->sides[sidenum].normals[1];
464 #endif
465
466         //      ========== Mark: Here is the change...beginning here: ==========
467
468         if (sidep->type == SIDE_IS_QUAD) {
469
470                 vm_vec_sub(&tvec, &Viewer_eye, &Vertices[segp->verts[Side_to_verts[sidenum][0]]]);
471
472                 // -- Old, slow way --  //      Regardless of whether this side is comprised of a single quad, or two triangles, we need to know one normal, so
473                 // -- Old, slow way --  //      deal with it, get the dot product.
474                 // -- Old, slow way --  if (sidep->type == SIDE_IS_TRI_13)
475                 // -- Old, slow way --          vm_vec_normalized_dir(&tvec, &Viewer_eye, &Vertices[segp->verts[Side_to_verts[sidenum][1]]]);
476                 // -- Old, slow way --  else
477                 // -- Old, slow way --          vm_vec_normalized_dir(&tvec, &Viewer_eye, &Vertices[segp->verts[Side_to_verts[sidenum][0]]]);
478
479                 get_side_verts(vertnum_list, SEGMENT_NUMBER(segp), sidenum);
480                 v_dot_n0 = vm_vec_dot(&tvec, &normals[0]);
481
482 // -- flare creates point -- {
483 // -- flare creates point --    int     flare_index;
484 // -- flare creates point -- 
485 // -- flare creates point --    flare_index = contains_flare(segp, sidenum);
486 // -- flare creates point -- 
487 // -- flare creates point --    if (flare_index != -1) {
488 // -- flare creates point --            int                     tri;
489 // -- flare creates point --            fix                     u, v, l;
490 // -- flare creates point --            vms_vector      *hit_point;
491 // -- flare creates point --            short                   vertnum_list[4];
492 // -- flare creates point -- 
493 // -- flare creates point --            hit_point = &Objects[flare_index].pos;
494 // -- flare creates point -- 
495 // -- flare creates point --            find_hitpoint_uv( &u, &v, &l, hit_point, segp, sidenum, 0);     //      last parm means always use face 0.
496 // -- flare creates point -- 
497 // -- flare creates point --            get_side_verts(vertnum_list, SEGMENT_NUMBER(segp), sidenum);
498 // -- flare creates point -- 
499 // -- flare creates point --            g3_rotate_point(&Segment_points[MAX_VERTICES-1], hit_point);
500 // -- flare creates point -- 
501 // -- flare creates point --            for (tri=0; tri<4; tri++) {
502 // -- flare creates point --                    short   tri_verts[3];
503 // -- flare creates point --                    uvl     tri_uvls[3];
504 // -- flare creates point -- 
505 // -- flare creates point --                    tri_verts[0] = vertnum_list[tri];
506 // -- flare creates point --                    tri_verts[1] = vertnum_list[(tri+1) % 4];
507 // -- flare creates point --                    tri_verts[2] = MAX_VERTICES-1;
508 // -- flare creates point -- 
509 // -- flare creates point --                    tri_uvls[0] = sidep->uvls[tri];
510 // -- flare creates point --                    tri_uvls[1] = sidep->uvls[(tri+1)%4];
511 // -- flare creates point --                    tri_uvls[2].u = u;
512 // -- flare creates point --                    tri_uvls[2].v = v;
513 // -- flare creates point --                    tri_uvls[2].l = F1_0;
514 // -- flare creates point -- 
515 // -- flare creates point --                    render_face(SEGMENT_NUMBER(segp), sidenum, 3, tri_verts, sidep->tmap_num, sidep->tmap_num2, tri_uvls, &normals[0]);
516 // -- flare creates point --            }
517 // -- flare creates point -- 
518 // -- flare creates point --    return;
519 // -- flare creates point --    }
520 // -- flare creates point -- }
521
522                 if (v_dot_n0 >= 0) {
523                         render_face(SEGMENT_NUMBER(segp), sidenum, 4, vertnum_list, sidep->tmap_num, sidep->tmap_num2, sidep->uvls, wid_flags);
524                         #ifdef EDITOR
525                         check_face(SEGMENT_NUMBER(segp), sidenum, 0, 4, vertnum_list, sidep->tmap_num, sidep->tmap_num2, sidep->uvls);
526                         #endif
527                 }
528         } else {
529                 //      Regardless of whether this side is comprised of a single quad, or two triangles, we need to know one normal, so
530                 //      deal with it, get the dot product.
531                 if (sidep->type == SIDE_IS_TRI_13)
532                         vm_vec_normalized_dir_quick(&tvec, &Viewer_eye, &Vertices[segp->verts[Side_to_verts[sidenum][1]]]);
533                 else
534                         vm_vec_normalized_dir_quick(&tvec, &Viewer_eye, &Vertices[segp->verts[Side_to_verts[sidenum][0]]]);
535
536                 get_side_verts(vertnum_list, SEGMENT_NUMBER(segp), sidenum);
537
538                 v_dot_n0 = vm_vec_dot(&tvec, &normals[0]);
539
540                 //      ========== Mark: The change ends here. ==========
541
542                 //      Although this side has been triangulated, because it is not planar, see if it is acceptable
543                 //      to render it as a single quadrilateral.  This is a function of how far away the viewer is, how non-planar
544                 //      the face is, how normal to the surfaces the view is.
545                 //      Now, if both dot products are close to 1.0, then render two triangles as a single quad.
546                 v_dot_n1 = vm_vec_dot(&tvec, &normals[1]);
547
548                 if (v_dot_n0 < v_dot_n1) {
549                         min_dot = v_dot_n0;
550                         max_dot = v_dot_n1;
551                 } else {
552                         min_dot = v_dot_n1;
553                         max_dot = v_dot_n0;
554                 }
555
556                 //      Determine whether to detriangulate side: (speed hack, assumes Tulate_min_ratio == F1_0*2, should fixmul(min_dot, Tulate_min_ratio))
557                 if (Detriangulation_on && ((min_dot+F1_0/256 > max_dot) || ((Viewer->segnum != SEGMENT_NUMBER(segp)) && (min_dot > Tulate_min_dot) && (max_dot < min_dot*2)))) {
558                         fix     n0_dot_n1;
559
560                         //      The other detriangulation code doesn't deal well with badly non-planar sides.
561                         n0_dot_n1 = vm_vec_dot(&normals[0], &normals[1]);
562                         if (n0_dot_n1 < Min_n0_n1_dot)
563                                 goto im_so_ashamed;
564
565                         render_face(SEGMENT_NUMBER(segp), sidenum, 4, vertnum_list, sidep->tmap_num, sidep->tmap_num2, sidep->uvls, wid_flags);
566                         #ifdef EDITOR
567                         check_face(SEGMENT_NUMBER(segp), sidenum, 0, 4, vertnum_list, sidep->tmap_num, sidep->tmap_num2, sidep->uvls);
568                         #endif
569                 } else {
570 im_so_ashamed: ;
571                         if (sidep->type == SIDE_IS_TRI_02) {
572                                 if (v_dot_n0 >= 0) {
573                                         render_face(SEGMENT_NUMBER(segp), sidenum, 3, vertnum_list, sidep->tmap_num, sidep->tmap_num2, sidep->uvls, wid_flags);
574                                         #ifdef EDITOR
575                                         check_face(SEGMENT_NUMBER(segp), sidenum, 0, 3, vertnum_list, sidep->tmap_num, sidep->tmap_num2, sidep->uvls);
576                                         #endif
577                                 }
578
579                                 if (v_dot_n1 >= 0) {
580                                         temp_uvls[0] = sidep->uvls[0];          temp_uvls[1] = sidep->uvls[2];          temp_uvls[2] = sidep->uvls[3];
581                                         vertnum_list[1] = vertnum_list[2];      vertnum_list[2] = vertnum_list[3];      // want to render from vertices 0, 2, 3 on side
582                                         render_face(SEGMENT_NUMBER(segp), sidenum, 3, &vertnum_list[0], sidep->tmap_num, sidep->tmap_num2, temp_uvls, wid_flags);
583                                         #ifdef EDITOR
584                                         check_face(SEGMENT_NUMBER(segp), sidenum, 1, 3, vertnum_list, sidep->tmap_num, sidep->tmap_num2, sidep->uvls);
585                                         #endif
586                                 }
587                         } else if (sidep->type ==  SIDE_IS_TRI_13) {
588                                 if (v_dot_n1 >= 0) {
589                                         render_face(SEGMENT_NUMBER(segp), sidenum, 3, &vertnum_list[1], sidep->tmap_num, sidep->tmap_num2, &sidep->uvls[1], wid_flags); // rendering 1,2,3, so just skip 0
590                                         #ifdef EDITOR
591                                         check_face(SEGMENT_NUMBER(segp), sidenum, 1, 3, &vertnum_list[1], sidep->tmap_num, sidep->tmap_num2, sidep->uvls);
592                                         #endif
593                                 }
594
595                                 if (v_dot_n0 >= 0) {
596                                         temp_uvls[0] = sidep->uvls[0];          temp_uvls[1] = sidep->uvls[1];          temp_uvls[2] = sidep->uvls[3];
597                                         vertnum_list[2] = vertnum_list[3];              // want to render from vertices 0,1,3
598                                         render_face(SEGMENT_NUMBER(segp), sidenum, 3, vertnum_list, sidep->tmap_num, sidep->tmap_num2, temp_uvls, wid_flags);
599                                         #ifdef EDITOR
600                                         check_face(SEGMENT_NUMBER(segp), sidenum, 0, 3, vertnum_list, sidep->tmap_num, sidep->tmap_num2, sidep->uvls);
601                                         #endif
602                                 }
603
604                         } else
605                                 Error("Illegal side type in render_side, type = %i, segment # = %i, side # = %i\n", sidep->type, SEGMENT_NUMBER(segp), sidenum);
606                 }
607         }
608
609 }
610
611 #ifdef EDITOR
612 void render_object_search(object *obj)
613 {
614         int changed=0;
615
616         //note that we draw each pixel object twice, since we cannot control
617         //what color the object draws in, so we try color 0, then color 1,
618         //in case the object itself is rendering color 0
619
620         gr_setcolor(0);
621         gr_pixel(_search_x,_search_y);  //set our search pixel to color zero
622         render_object(obj);
623         if (gr_ugpixel(&grd_curcanv->cv_bitmap,_search_x,_search_y) != 0)
624                 changed=1;
625
626         gr_setcolor(1);
627         gr_pixel(_search_x,_search_y);  //set our search pixel to color zero
628         render_object(obj);
629         if (gr_ugpixel(&grd_curcanv->cv_bitmap,_search_x,_search_y) != 1)
630                 changed=1;
631
632         if (changed) {
633                 if (obj->segnum != -1)
634                         Cursegp = &Segments[obj->segnum];
635                 found_seg = -(OBJECT_NUMBER(obj)+1);
636         }
637 }
638 #endif
639
640 extern ubyte DemoDoingRight,DemoDoingLeft;
641
642 void do_render_object(int objnum, int window_num)
643 {
644         #ifdef EDITOR
645         int save_3d_outline=0;
646         #endif
647         object *obj = &Objects[objnum];
648         int count = 0;
649         int n;
650
651         Assert(objnum < MAX_OBJECTS);
652
653         #ifndef NDEBUG
654         if (object_rendered[objnum]) {          //already rendered this...
655                 Int3();         //get Matt!!!
656                 return;
657         }
658
659         object_rendered[objnum] = 1;
660         #endif
661
662    if (Newdemo_state==ND_STATE_PLAYBACK)  
663          {
664           if ((DemoDoingLeft==6 || DemoDoingRight==6) && Objects[objnum].type==OBJ_PLAYER)
665                 {
666                         // A nice fat hack: keeps the player ship from showing up in the
667                         // small extra view when guiding a missile in the big window
668                         
669                         mprintf ((0,"Returning from render_object prematurely...\n"));
670                         return; 
671                 }
672          }
673
674         //      Added by MK on 09/07/94 (at about 5:28 pm, CDT, on a beautiful, sunny late summer day!) so
675         //      that the guided missile system will know what objects to look at.
676         //      I didn't know we had guided missiles before the release of D1. --MK
677         if ((Objects[objnum].type == OBJ_ROBOT) || (Objects[objnum].type == OBJ_PLAYER)) {
678                 //Assert(Window_rendered_data[window_num].rendered_objects < MAX_RENDERED_OBJECTS);
679                 //      This peculiar piece of code makes us keep track of the most recently rendered objects, which
680                 //      are probably the higher priority objects, without overflowing the buffer
681                 if (Window_rendered_data[window_num].num_objects >= MAX_RENDERED_OBJECTS) {
682                         Int3();
683                         Window_rendered_data[window_num].num_objects /= 2;
684                 }
685                 Window_rendered_data[window_num].rendered_objects[Window_rendered_data[window_num].num_objects++] = objnum;
686         }
687
688         if ((count++ > MAX_OBJECTS) || (obj->next == objnum)) {
689                 Int3();                                 // infinite loop detected
690                 obj->next = -1;         // won't this clean things up?
691                 return;                                 // get out of this infinite loop!
692         }
693
694                 //g3_draw_object(obj->class_id,&obj->pos,&obj->orient,obj->size);
695
696         //check for editor object
697
698         #ifdef EDITOR
699         if (Function_mode==FMODE_EDITOR && objnum==Cur_object_index) {
700                 save_3d_outline = g3d_interp_outline;
701                 g3d_interp_outline=1;
702         }
703         #endif
704
705         #ifdef EDITOR
706         if (_search_mode)
707                 render_object_search(obj);
708         else
709         #endif
710                 //NOTE LINK TO ABOVE
711                 render_object(obj);
712
713         for (n=obj->attached_obj;n!=-1;n=Objects[n].ctype.expl_info.next_attach) {
714
715                 Assert(Objects[n].type == OBJ_FIREBALL);
716                 Assert(Objects[n].control_type == CT_EXPLOSION);
717                 Assert(Objects[n].flags & OF_ATTACHED);
718
719                 render_object(&Objects[n]);
720         }
721
722
723         #ifdef EDITOR
724         if (Function_mode==FMODE_EDITOR && objnum==Cur_object_index)
725                 g3d_interp_outline = save_3d_outline;
726         #endif
727
728
729         //DEBUG mprintf( (0, "%d ", objnum ));
730
731 }
732
733 #ifndef NDEBUG
734 int     draw_boxes=0;
735 int window_check=1,draw_edges=0,new_seg_sorting=1,pre_draw_segs=0;
736 int no_migrate_segs=1,migrate_objects=1,behind_check=1;
737 int check_window_check=0;
738 #else
739 #define draw_boxes                      0
740 #define window_check                    1
741 #define draw_edges                      0
742 #define new_seg_sorting         1
743 #define pre_draw_segs           0
744 #define no_migrate_segs         1
745 #define migrate_objects         1
746 #define behind_check                    1
747 #define check_window_check      0
748 #endif
749
750 //increment counter for checking if points rotated
751 //This must be called at the start of the frame if rotate_list() will be used
752 void render_start_frame()
753 {
754         RL_framecount++;
755
756         if (RL_framecount==0) {         //wrap!
757
758                 memset(Rotated_last,0,sizeof(Rotated_last));            //clear all to zero
759                 RL_framecount=1;                                                                                        //and set this frame to 1
760         }
761 }
762
763 //Given a lit of point numbers, rotate any that haven't been rotated this frame
764 g3s_codes rotate_list(int nv,short *pointnumlist)
765 {
766         int i,pnum;
767         g3s_point *pnt;
768         g3s_codes cc;
769
770         cc.and = 0xff;  cc.or = 0;
771
772         for (i=0;i<nv;i++) {
773
774                 pnum = pointnumlist[i];
775
776                 pnt = &Segment_points[pnum];
777
778                 if (Rotated_last[pnum] != RL_framecount) {
779
780                         g3_rotate_point(pnt,&Vertices[pnum]);
781
782                         Rotated_last[pnum] = RL_framecount;
783                 }
784
785                 cc.and &= pnt->p3_codes;
786                 cc.or  |= pnt->p3_codes;
787         }
788
789         return cc;
790
791 }
792
793 //Given a lit of point numbers, project any that haven't been projected
794 void project_list(int nv,short *pointnumlist)
795 {
796         int i,pnum;
797
798         for (i=0;i<nv;i++) {
799
800                 pnum = pointnumlist[i];
801
802                 if (!(Segment_points[pnum].p3_flags & PF_PROJECTED))
803
804                         g3_project_point(&Segment_points[pnum]);
805
806         }
807 }
808
809
810 // -----------------------------------------------------------------------------------
811 void render_segment(int segnum, int window_num)
812 {
813         segment         *seg = &Segments[segnum];
814         g3s_codes       cc;
815         int                     sn;
816
817         Assert(segnum!=-1 && segnum<=Highest_segment_index);
818
819         cc=rotate_list(8,seg->verts);
820
821         if (! cc.and) {         //all off screen?
822
823 //mprintf( (0, "!"));
824                 //DEBUG mprintf( (0, "[Segment %d: ", segnum ));
825
826                 // set_segment_local_light_value(segnum,INITIAL_LOCAL_LIGHT);
827
828       if (Viewer->type!=OBJ_ROBOT)
829                 Automap_visited[segnum]=1;
830
831                 for (sn=0; sn<MAX_SIDES_PER_SEGMENT; sn++)
832                         render_side(seg, sn);
833         }
834
835         //draw any objects that happen to be in this segment
836
837         //sort objects!
838         //object_sort_segment_objects( seg );
839                 
840         #ifndef NDEBUG
841         if (!migrate_objects) {
842                 int objnum;
843                 for (objnum=seg->objects;objnum!=-1;objnum=Objects[objnum].next)
844                         do_render_object(objnum, window_num);
845         }
846         #endif
847
848         //DEBUG mprintf( (0, "]\n", segnum ));
849
850 }
851
852 // ----- This used to be called when Show_only_curside was set.
853 // ----- It is wholly and superiorly replaced by render_side.
854 // -- //render one side of one segment
855 // -- void render_seg_side(segment *seg,int _side)
856 // -- {
857 // --   g3s_codes cc;
858 // --   short vertnum_list[4];
859 // -- 
860 // --   cc=g3_rotate_list(8,&seg->verts);
861 // -- 
862 // --   if (! cc.and) {         //all off screen?
863 // --           int fn,pn,i;
864 // --           side *s;
865 // --           face *f;
866 // --           poly *p;
867 // -- 
868 // --           s=&seg->sides[_side];
869 // -- 
870 // --           for (f=s->faces,fn=s->num_faces;fn;fn--,f++)
871 // --                   for (p=f->polys,pn=f->num_polys;pn;pn--,p++) {
872 // --                           grs_bitmap *tmap;
873 // --   
874 // --                           for (i=0;i<p->num_vertices;i++) vertnum_list[i] = seg->verts[p->verts[i]];
875 // --   
876 // --                           if (p->tmap_num >= NumTextures) {
877 // --                                   Warning("Invalid tmap number %d, NumTextures=%d\n...Changing in poly structure to tmap 0",p->tmap_num,NumTextures);
878 // --                                   p->tmap_num = 0;                //change it permanantly
879 // --                           }
880 // --   
881 // --                           tmap = Textures[p->tmap_num];
882 // --   
883 // --                           g3_check_and_draw_tmap(p->num_vertices,vertnum_list,(g3s_uvl *) &p->uvls,tmap,&f->normal);
884 // --   
885 // --                           if (Outline_mode) draw_outline(p->num_vertices,vertnum_list);
886 // --                   }
887 // --           }
888 // -- 
889 // -- }
890
891 #define CROSS_WIDTH  i2f(8)
892 #define CROSS_HEIGHT i2f(8)
893
894 #ifndef NDEBUG
895
896 //draw outline for curside
897 void outline_seg_side(segment *seg,int _side,int edge,int vert)
898 {
899         g3s_codes cc;
900
901         cc=rotate_list(8,seg->verts);
902
903         if (! cc.and) {         //all off screen?
904                 side *s;
905                 g3s_point *pnt;
906
907                 s=&seg->sides[_side];
908
909                 //render curedge of curside of curseg in green
910
911                 gr_setcolor(BM_XRGB(0,63,0));
912                 g3_draw_line(&Segment_points[seg->verts[Side_to_verts[_side][edge]]],&Segment_points[seg->verts[Side_to_verts[_side][(edge+1)%4]]]);
913
914                 //draw a little cross at the current vert
915
916                 pnt = &Segment_points[seg->verts[Side_to_verts[_side][vert]]];
917
918                 g3_project_point(pnt);          //make sure projected
919
920 //              gr_setcolor(BM_XRGB(0,0,63));
921 //              gr_line(pnt->p3_sx-CROSS_WIDTH,pnt->p3_sy,pnt->p3_sx+CROSS_WIDTH,pnt->p3_sy);
922 //              gr_line(pnt->p3_sx,pnt->p3_sy-CROSS_HEIGHT,pnt->p3_sx,pnt->p3_sy+CROSS_HEIGHT);
923
924                 gr_line(pnt->p3_sx-CROSS_WIDTH,pnt->p3_sy,pnt->p3_sx,pnt->p3_sy-CROSS_HEIGHT);
925                 gr_line(pnt->p3_sx,pnt->p3_sy-CROSS_HEIGHT,pnt->p3_sx+CROSS_WIDTH,pnt->p3_sy);
926                 gr_line(pnt->p3_sx+CROSS_WIDTH,pnt->p3_sy,pnt->p3_sx,pnt->p3_sy+CROSS_HEIGHT);
927                 gr_line(pnt->p3_sx,pnt->p3_sy+CROSS_HEIGHT,pnt->p3_sx-CROSS_WIDTH,pnt->p3_sy);
928         }
929 }
930
931 #endif
932
933 #if 0           //this stuff could probably just be deleted
934
935 #define DEFAULT_PERSPECTIVE_DEPTH 6
936
937 int Perspective_depth=DEFAULT_PERSPECTIVE_DEPTH;        //how many levels deep to render in perspective
938
939 int inc_perspective_depth(void)
940 {
941         return ++Perspective_depth;
942
943 }
944
945 int dec_perspective_depth(void)
946 {
947         return Perspective_depth==1?Perspective_depth:--Perspective_depth;
948
949 }
950
951 int reset_perspective_depth(void)
952 {
953         return Perspective_depth = DEFAULT_PERSPECTIVE_DEPTH;
954
955 }
956 #endif
957
958 typedef struct window {
959         short left,top,right,bot;
960 } window;
961
962 ubyte code_window_point(fix x,fix y,window *w)
963 {
964         ubyte code=0;
965
966         if (x <= w->left)  code |= 1;
967         if (x >= w->right) code |= 2;
968
969         if (y <= w->top) code |= 4;
970         if (y >= w->bot) code |= 8;
971
972         return code;
973 }
974
975 #ifndef NDEBUG
976 void draw_window_box(int color,short left,short top,short right,short bot)
977 {
978         short l,t,r,b;
979
980         gr_setcolor(color);
981
982         l=left; t=top; r=right; b=bot;
983
984         if ( r<0 || b<0 || l>=grd_curcanv->cv_bitmap.bm_w || (t>=grd_curcanv->cv_bitmap.bm_h && b>=grd_curcanv->cv_bitmap.bm_h))
985                 return;
986
987         if (l<0) l=0;
988         if (t<0) t=0;
989         if (r>=grd_curcanv->cv_bitmap.bm_w) r=grd_curcanv->cv_bitmap.bm_w-1;
990         if (b>=grd_curcanv->cv_bitmap.bm_h) b=grd_curcanv->cv_bitmap.bm_h-1;
991
992         gr_line(i2f(l),i2f(t),i2f(r),i2f(t));
993         gr_line(i2f(r),i2f(t),i2f(r),i2f(b));
994         gr_line(i2f(r),i2f(b),i2f(l),i2f(b));
995         gr_line(i2f(l),i2f(b),i2f(l),i2f(t));
996
997 }
998 #endif
999
1000 int matt_find_connect_side(int seg0,int seg1);
1001
1002 #ifndef NDEBUG
1003 char visited2[MAX_SEGMENTS];
1004 #endif
1005
1006 unsigned char visited[MAX_SEGMENTS];
1007 short Render_list[MAX_RENDER_SEGS];
1008 short Seg_depth[MAX_RENDER_SEGS];               //depth for each seg in Render_list
1009 ubyte processed[MAX_RENDER_SEGS];               //whether each entry has been processed
1010 int     lcnt_save,scnt_save;
1011 //@@short *persp_ptr;
1012 short render_pos[MAX_SEGMENTS]; //where in render_list does this segment appear?
1013 //ubyte no_render_flag[MAX_RENDER_SEGS];
1014 window render_windows[MAX_RENDER_SEGS];
1015
1016 short render_obj_list[MAX_RENDER_SEGS+N_EXTRA_OBJ_LISTS][OBJS_PER_SEG];
1017
1018 //for objects
1019
1020
1021
1022 #define RED   BM_XRGB(63,0,0)
1023 #define WHITE BM_XRGB(63,63,63)
1024
1025 //Given two sides of segment, tell the two verts which form the 
1026 //edge between them
1027 short Two_sides_to_edge[6][6][2] = {
1028         { {-1,-1}, {3,7}, {-1,-1}, {2,6}, {6,7}, {2,3} },
1029         { {3,7}, {-1,-1}, {0,4}, {-1,-1}, {4,7}, {0,3} },
1030         { {-1,-1}, {0,4}, {-1,-1}, {1,5}, {4,5}, {0,1} },
1031         { {2,6}, {-1,-1}, {1,5}, {-1,-1}, {5,6}, {1,2} },
1032         { {6,7}, {4,7}, {4,5}, {5,6}, {-1,-1}, {-1,-1} },
1033         { {2,3}, {0,3}, {0,1}, {1,2}, {-1,-1}, {-1,-1} }
1034 };
1035
1036 //given an edge specified by two verts, give the two sides on that edge
1037 int Edge_to_sides[8][8][2] = {
1038         { {-1,-1}, {2,5}, {-1,-1}, {1,5}, {1,2}, {-1,-1}, {-1,-1}, {-1,-1} },
1039         { {2,5}, {-1,-1}, {3,5}, {-1,-1}, {-1,-1}, {2,3}, {-1,-1}, {-1,-1} },
1040         { {-1,-1}, {3,5}, {-1,-1}, {0,5}, {-1,-1}, {-1,-1}, {0,3}, {-1,-1} },
1041         { {1,5}, {-1,-1}, {0,5}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {0,1} },
1042         { {1,2}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {2,4}, {-1,-1}, {1,4} },
1043         { {-1,-1}, {2,3}, {-1,-1}, {-1,-1}, {2,4}, {-1,-1}, {3,4}, {-1,-1} },
1044         { {-1,-1}, {-1,-1}, {0,3}, {-1,-1}, {-1,-1}, {3,4}, {-1,-1}, {0,4} },
1045         { {-1,-1}, {-1,-1}, {-1,-1}, {0,1}, {1,4}, {-1,-1}, {0,4}, {-1,-1} },
1046 };
1047
1048 //@@//perform simple check on tables
1049 //@@check_check()
1050 //@@{
1051 //@@    int i,j;
1052 //@@
1053 //@@    for (i=0;i<8;i++)
1054 //@@            for (j=0;j<8;j++)
1055 //@@                    Assert(Edge_to_sides[i][j][0] == Edge_to_sides[j][i][0] && 
1056 //@@                                    Edge_to_sides[i][j][1] == Edge_to_sides[j][i][1]);
1057 //@@
1058 //@@    for (i=0;i<6;i++)
1059 //@@            for (j=0;j<6;j++)
1060 //@@                    Assert(Two_sides_to_edge[i][j][0] == Two_sides_to_edge[j][i][0] && 
1061 //@@                                    Two_sides_to_edge[i][j][1] == Two_sides_to_edge[j][i][1]);
1062 //@@
1063 //@@
1064 //@@}
1065
1066
1067 //given an edge, tell what side is on that edge
1068 int find_seg_side(segment *seg,short *verts,int notside)
1069 {
1070         int i;
1071         int vv0=-1,vv1=-1;
1072         int side0,side1;
1073         int *eptr;
1074         int     v0,v1;
1075         short   *vp;
1076
1077 //@@    check_check();
1078
1079         v0 = verts[0];
1080         v1 = verts[1];
1081         vp = seg->verts;
1082
1083         for (i=0; i<8; i++) {
1084                 int svv = *vp++;        // seg->verts[i];
1085
1086                 if (vv0==-1 && svv == v0) {
1087                         vv0 = i;
1088                         if (vv1 != -1)
1089                                 break;
1090                 }
1091
1092                 if (vv1==-1 && svv == v1) {
1093                         vv1 = i;
1094                         if (vv0 != -1)
1095                                 break;
1096                 }
1097         }
1098
1099         Assert(vv0!=-1 && vv1!=-1);
1100
1101         eptr = Edge_to_sides[vv0][vv1];
1102
1103         side0 = eptr[0];
1104         side1 = eptr[1];
1105
1106         Assert(side0!=-1 && side1!=-1);
1107
1108         if (side0 != notside) {
1109                 Assert(side1==notside);
1110                 return side0;
1111         }
1112         else {
1113                 Assert(side0==notside);
1114                 return side1;
1115         }
1116
1117 }
1118
1119 //find the two segments that join a given seg though two sides, and
1120 //the sides of those segments the abut. 
1121 int find_joining_side_norms(vms_vector *norm0_0,vms_vector *norm0_1,vms_vector *norm1_0,vms_vector *norm1_1,vms_vector **pnt0,vms_vector **pnt1,segment *seg,int s0,int s1)
1122 {
1123         segment *seg0,*seg1;
1124         short edge_verts[2];
1125         int notside0,notside1;
1126         int edgeside0,edgeside1;
1127
1128         Assert(s0!=-1 && s1!=-1);
1129
1130         seg0 = &Segments[seg->children[s0]];
1131         seg1 = &Segments[seg->children[s1]];
1132
1133         edge_verts[0] = seg->verts[Two_sides_to_edge[s0][s1][0]];
1134         edge_verts[1] = seg->verts[Two_sides_to_edge[s0][s1][1]];
1135
1136         Assert(edge_verts[0]!=-1 && edge_verts[1]!=-1);
1137
1138         notside0 = find_connect_side(seg,seg0);
1139         Assert(notside0 != -1);
1140         notside1 = find_connect_side(seg,seg1);
1141         Assert(notside1 != -1);
1142
1143         edgeside0 = find_seg_side(seg0,edge_verts,notside0);
1144         edgeside1 = find_seg_side(seg1,edge_verts,notside1);
1145
1146         //deal with the case where an edge is shared by more than two segments
1147
1148 //@@    if (IS_CHILD(seg0->children[edgeside0])) {
1149 //@@            segment *seg00;
1150 //@@            int notside00;
1151 //@@
1152 //@@            seg00 = &Segments[seg0->children[edgeside0]];
1153 //@@
1154 //@@            if (seg00 != seg1) {
1155 //@@
1156 //@@                    notside00 = find_connect_side(seg0,seg00);
1157 //@@                    Assert(notside00 != -1);
1158 //@@
1159 //@@                    edgeside0 = find_seg_side(seg00,edge_verts,notside00);
1160 //@@                    seg0 = seg00;
1161 //@@            }
1162 //@@
1163 //@@    }
1164 //@@
1165 //@@    if (IS_CHILD(seg1->children[edgeside1])) {
1166 //@@            segment *seg11;
1167 //@@            int notside11;
1168 //@@
1169 //@@            seg11 = &Segments[seg1->children[edgeside1]];
1170 //@@
1171 //@@            if (seg11 != seg0) {
1172 //@@                    notside11 = find_connect_side(seg1,seg11);
1173 //@@                    Assert(notside11 != -1);
1174 //@@
1175 //@@                    edgeside1 = find_seg_side(seg11,edge_verts,notside11);
1176 //@@                    seg1 = seg11;
1177 //@@            }
1178 //@@    }
1179
1180 //      if ( IS_CHILD(seg0->children[edgeside0]) ||
1181 //                IS_CHILD(seg1->children[edgeside1])) 
1182 //              return 0;
1183
1184         #ifdef COMPACT_SEGS
1185                 get_side_normals(seg0, edgeside0, norm0_0, norm0_1 );
1186                 get_side_normals(seg1, edgeside1, norm1_0, norm1_1 );
1187         #else 
1188                 *norm0_0 = seg0->sides[edgeside0].normals[0];
1189                 *norm0_1 = seg0->sides[edgeside0].normals[1];
1190                 *norm1_0 = seg1->sides[edgeside1].normals[0];
1191                 *norm1_1 = seg1->sides[edgeside1].normals[1];
1192         #endif
1193
1194         *pnt0 = &Vertices[seg0->verts[Side_to_verts[edgeside0][seg0->sides[edgeside0].type==3?1:0]]];
1195         *pnt1 = &Vertices[seg1->verts[Side_to_verts[edgeside1][seg1->sides[edgeside1].type==3?1:0]]];
1196
1197         return 1;
1198 }
1199
1200 //see if the order matters for these two children.
1201 //returns 0 if order doesn't matter, 1 if c0 before c1, -1 if c1 before c0
1202 int compare_children(segment *seg,short c0,short c1)
1203 {
1204         vms_vector norm0_0,norm0_1,*pnt0,temp;
1205         vms_vector norm1_0,norm1_1,*pnt1;
1206         fix d0_0,d0_1,d1_0,d1_1,d0,d1;
1207 int t;
1208
1209         if (Side_opposite[c0] == c1) return 0;
1210
1211         Assert(c0!=-1 && c1!=-1);
1212
1213         //find normals of adjoining sides
1214
1215         t = find_joining_side_norms(&norm0_0,&norm0_1,&norm1_0,&norm1_1,&pnt0,&pnt1,seg,c0,c1);
1216
1217 //if (!t)
1218 // return 0;
1219
1220         vm_vec_sub(&temp,&Viewer_eye,pnt0);
1221         d0_0 = vm_vec_dot(&norm0_0,&temp);
1222         d0_1 = vm_vec_dot(&norm0_1,&temp);
1223
1224         vm_vec_sub(&temp,&Viewer_eye,pnt1);
1225         d1_0 = vm_vec_dot(&norm1_0,&temp);
1226         d1_1 = vm_vec_dot(&norm1_1,&temp);
1227
1228         d0 = (d0_0 < 0 || d0_1 < 0)?-1:1;
1229         d1 = (d1_0 < 0 || d1_1 < 0)?-1:1;
1230
1231         if (d0 < 0 && d1 < 0)
1232                 return 0;
1233
1234         if (d0 < 0)
1235                 return 1;
1236         else if (d1 < 0)
1237                 return -1;
1238         else
1239                 return 0;
1240
1241 }
1242
1243 int ssc_total=0,ssc_swaps=0;
1244
1245 //short the children of segment to render in the correct order
1246 //returns non-zero if swaps were made
1247 int sort_seg_children(segment *seg,int n_children,short *child_list)
1248 {
1249         int i,j;
1250         int r;
1251         int made_swaps,count;
1252
1253         if (n_children == 0) return 0;
1254
1255  ssc_total++;
1256
1257         //for each child,  compare with other children and see if order matters
1258         //if order matters, fix if wrong
1259
1260         count = 0;
1261
1262         do {
1263                 made_swaps = 0;
1264
1265                 for (i=0;i<n_children-1;i++)
1266                         for (j=i+1;child_list[i]!=-1 && j<n_children;j++)
1267                                 if (child_list[j]!=-1) {
1268                                         r = compare_children(seg,child_list[i],child_list[j]);
1269
1270                                         if (r == 1) {
1271                                                 int temp = child_list[i];
1272                                                 child_list[i] = child_list[j];
1273                                                 child_list[j] = temp;
1274                                                 made_swaps=1;
1275                                         }
1276                                 }
1277
1278         } while (made_swaps && ++count<n_children);
1279
1280  if (count)
1281   ssc_swaps++;
1282
1283         return count;
1284 }
1285
1286 void add_obj_to_seglist(int objnum,int listnum)
1287 {
1288         int i,checkn,marker;
1289
1290         checkn = listnum;
1291
1292         //first, find a slot
1293
1294 //mprintf((0,"adding obj %d to %d",objnum,listnum));
1295
1296         do {
1297
1298                 for (i=0;render_obj_list[checkn][i] >= 0;i++);
1299         
1300                 Assert(i < OBJS_PER_SEG);
1301         
1302                 marker = render_obj_list[checkn][i];
1303
1304                 if (marker != -1) {
1305                         checkn = -marker;
1306                         //Assert(checkn < MAX_RENDER_SEGS+N_EXTRA_OBJ_LISTS);
1307                         if (checkn >= MAX_RENDER_SEGS+N_EXTRA_OBJ_LISTS) {
1308                                 Int3();
1309                                 return;
1310                         }
1311                 }
1312
1313         } while (marker != -1);
1314
1315 //mprintf((0,"  slot %d,%d",checkn,i));
1316
1317
1318         //now we have found a slot.  put object in it
1319
1320         if (i != OBJS_PER_SEG-1) {
1321
1322                 render_obj_list[checkn][i] = objnum;
1323                 render_obj_list[checkn][i+1] = -1;
1324         }
1325         else {                          //chain to additional list
1326                 int lookn;
1327
1328                 //find an available sublist
1329
1330                 for (lookn=MAX_RENDER_SEGS;render_obj_list[lookn][0]!=-1 && lookn<MAX_RENDER_SEGS+N_EXTRA_OBJ_LISTS;lookn++);
1331
1332                 //Assert(lookn<MAX_RENDER_SEGS+N_EXTRA_OBJ_LISTS);
1333                 if (lookn >= MAX_RENDER_SEGS+N_EXTRA_OBJ_LISTS) {
1334                         Int3();
1335                         return;
1336                 }
1337
1338                 render_obj_list[checkn][i] = -lookn;
1339                 render_obj_list[lookn][0] = objnum;
1340                 render_obj_list[lookn][1] = -1;
1341
1342         }
1343
1344 //mprintf((0,"  added!\n"));
1345
1346 }
1347 #ifdef __sun__
1348 // the following is a drop-in replacement for the broken libc qsort on solaris
1349 // taken from http://www.snippets.org/snippets/portable/RG_QSORT+C.php3
1350
1351 #define qsort qsort_dropin
1352
1353 /******************************************************************/
1354 /* qsort.c  --  Non-Recursive ANSI Quicksort function             */
1355 /* Public domain by Raymond Gardner, Englewood CO  February 1991  */
1356 /******************************************************************/
1357 #define  COMP(a, b)  ((*comp)((void *)(a), (void *)(b)))
1358 #define  T 7 // subfiles of <= T elements will be insertion sorteded (T >= 3)
1359 #define  SWAP(a, b)  (swap_bytes((char *)(a), (char *)(b), size))
1360
1361 static void swap_bytes(char *a, char *b, size_t nbytes)
1362 {
1363    char tmp;
1364    do {
1365       tmp = *a; *a++ = *b; *b++ = tmp;
1366    } while ( --nbytes );
1367 }
1368
1369 void qsort(void *basep, size_t nelems, size_t size,
1370                             int (*comp)(const void *, const void *))
1371 {
1372    char *stack[40], **sp;       /* stack and stack pointer        */
1373    char *i, *j, *limit;         /* scan and limit pointers        */
1374    size_t thresh;               /* size of T elements in bytes    */
1375    char *base;                  /* base pointer as char *         */
1376    base = (char *)basep;        /* set up char * base pointer     */
1377    thresh = T * size;           /* init threshold                 */
1378    sp = stack;                  /* init stack pointer             */
1379    limit = base + nelems * size;/* pointer past end of array      */
1380    for ( ;; ) {                 /* repeat until break...          */
1381       if ( limit - base > thresh ) {  /* if more than T elements  */
1382                                       /*   swap base with middle  */
1383          SWAP((((limit-base)/size)/2)*size+base, base);
1384          i = base + size;             /* i scans left to right    */
1385          j = limit - size;            /* j scans right to left    */
1386          if ( COMP(i, j) > 0 )        /* Sedgewick's              */
1387             SWAP(i, j);               /*    three-element sort    */
1388          if ( COMP(base, j) > 0 )     /*        sets things up    */
1389             SWAP(base, j);            /*            so that       */
1390          if ( COMP(i, base) > 0 )     /*      *i <= *base <= *j   */
1391             SWAP(i, base);            /* *base is pivot element   */
1392          for ( ;; ) {                 /* loop until break         */
1393             do                        /* move i right             */
1394                i += size;             /*        until *i >= pivot */
1395             while ( COMP(i, base) < 0 );
1396             do                        /* move j left              */
1397                j -= size;             /*        until *j <= pivot */
1398             while ( COMP(j, base) > 0 );
1399             if ( i > j )              /* if pointers crossed      */
1400                break;                 /*     break loop           */
1401             SWAP(i, j);       /* else swap elements, keep scanning*/
1402          }
1403          SWAP(base, j);         /* move pivot into correct place  */
1404          if ( j - base > limit - i ) {  /* if left subfile larger */
1405             sp[0] = base;             /* stack left subfile base  */
1406             sp[1] = j;                /*    and limit             */
1407             base = i;                 /* sort the right subfile   */
1408          } else {                     /* else right subfile larger*/
1409             sp[0] = i;                /* stack right subfile base */
1410             sp[1] = limit;            /*    and limit             */
1411             limit = j;                /* sort the left subfile    */
1412          }
1413          sp += 2;                     /* increment stack pointer  */
1414       } else {      /* else subfile is small, use insertion sort  */
1415          for ( j = base, i = j+size; i < limit; j = i, i += size )
1416             for ( ; COMP(j, j+size) > 0; j -= size ) {
1417                SWAP(j, j+size);
1418                if ( j == base )
1419                   break;
1420             }
1421          if ( sp != stack ) {         /* if any entries on stack  */
1422             sp -= 2;                  /* pop the base and limit   */
1423             base = sp[0];
1424             limit = sp[1];
1425          } else                       /* else stack empty, done   */
1426             break;
1427       }
1428    }
1429 }
1430 #endif // __sun__ qsort drop-in replacement
1431
1432 #define SORT_LIST_SIZE 100
1433
1434 typedef struct sort_item {
1435         int objnum;
1436         fix dist;
1437 } sort_item;
1438
1439 sort_item sort_list[SORT_LIST_SIZE];
1440 int n_sort_items;
1441
1442 //compare function for object sort. 
1443 int sort_func(const sort_item *a,const sort_item *b)
1444 {
1445         fix delta_dist;
1446         object *obj_a,*obj_b;
1447
1448         delta_dist = a->dist - b->dist;
1449
1450         obj_a = &Objects[a->objnum];
1451         obj_b = &Objects[b->objnum];
1452
1453         if (abs(delta_dist) < (obj_a->size + obj_b->size)) {            //same position
1454
1455                 //these two objects are in the same position.  see if one is a fireball
1456                 //or laser or something that should plot on top.  Don't do this for
1457                 //the afterburner blobs, though.
1458
1459                 if (obj_a->type == OBJ_WEAPON || (obj_a->type == OBJ_FIREBALL && obj_a->id != VCLIP_AFTERBURNER_BLOB))
1460                         if (!(obj_b->type == OBJ_WEAPON || obj_b->type == OBJ_FIREBALL))
1461                                 return -1;      //a is weapon, b is not, so say a is closer
1462                         else;                           //both are weapons 
1463                 else
1464                         if (obj_b->type == OBJ_WEAPON || (obj_b->type == OBJ_FIREBALL && obj_b->id != VCLIP_AFTERBURNER_BLOB))
1465                                 return 1;       //b is weapon, a is not, so say a is farther
1466
1467                 //no special case, fall through to normal return
1468         }
1469
1470         return delta_dist;      //return distance
1471 }
1472
1473 void build_object_lists(int n_segs)
1474 {
1475         int nn;
1476
1477 //mprintf((0,"build n_segs=%d",n_segs));
1478
1479         for (nn=0;nn<MAX_RENDER_SEGS+N_EXTRA_OBJ_LISTS;nn++)
1480                 render_obj_list[nn][0] = -1;
1481
1482         for (nn=0;nn<n_segs;nn++) {
1483                 int segnum;
1484
1485                 segnum = Render_list[nn];
1486
1487 //mprintf((0,"nn=%d seg=%d ",nn,segnum));
1488
1489                 if (segnum != -1) {
1490                         int objnum;
1491                         object *obj;
1492
1493                         for (objnum=Segments[segnum].objects;objnum!=-1;objnum = obj->next) {
1494                                 int new_segnum,did_migrate,list_pos;
1495
1496                                 obj = &Objects[objnum];
1497
1498                                 Assert( obj->segnum == segnum );
1499
1500                                 if (obj->flags & OF_ATTACHED)
1501                                         continue;               //ignore this object
1502
1503                                 new_segnum = segnum;
1504                                 list_pos = nn;
1505
1506 //mprintf((0,"objnum=%d ",objnum));
1507                                 if (obj->type != OBJ_CNTRLCEN && !(obj->type==OBJ_ROBOT && obj->id==65))                //don't migrate controlcen
1508                                 do {
1509                                         segmasks m;
1510
1511                                         did_migrate = 0;
1512         
1513                                         m = get_seg_masks(&obj->pos, new_segnum, obj->size, __FILE__, __LINE__);
1514         
1515                                         if (m.sidemask) {
1516                                                 int sn,sf;
1517
1518                                                 for (sn=0,sf=1;sn<6;sn++,sf<<=1)
1519                                                         if (m.sidemask & sf) {
1520                                                                 segment *seg = &Segments[new_segnum];
1521                 
1522                                                                 if (WALL_IS_DOORWAY(seg,sn) & WID_FLY_FLAG) {           //can explosion migrate through
1523                                                                         int child = seg->children[sn];
1524                                                                         int checknp;
1525                 
1526                                                                         for (checknp=list_pos;checknp--;)
1527                                                                                 if (Render_list[checknp] == child) {
1528 //mprintf((0,"mig from %d to %d ",new_segnum,child));
1529                                                                                         new_segnum = child;
1530                                                                                         list_pos = checknp;
1531                                                                                         did_migrate = 1;
1532                                                                                 }
1533                                                                 }
1534                                                         }
1535                                         }
1536         
1537                                 } while (0);    //while (did_migrate);
1538
1539                                 add_obj_to_seglist(objnum,list_pos);
1540         
1541                         }
1542
1543                 }
1544         }
1545
1546 //mprintf((0,"done build "));
1547
1548         //now that there's a list for each segment, sort the items in those lists
1549         for (nn=0;nn<n_segs;nn++) {
1550                 int segnum;
1551
1552                 segnum = Render_list[nn];
1553
1554 //mprintf((0,"nn=%d seg=%d ",nn,segnum));
1555
1556                 if (segnum != -1) {
1557                         int t,lookn,i,n;
1558
1559                         //first count the number of objects & copy into sort list
1560
1561                         lookn = nn;
1562                         i = n_sort_items = 0;
1563                         while ((t=render_obj_list[lookn][i++])!=-1)
1564                                 if (t<0)
1565                                         {lookn = -t; i=0;}
1566                                 else
1567                                         if (n_sort_items < SORT_LIST_SIZE-1) {          //add if room
1568                                                 sort_list[n_sort_items].objnum = t;
1569                                                 //NOTE: maybe use depth, not dist - quicker computation
1570                                                 sort_list[n_sort_items].dist = vm_vec_dist_quick(&Objects[t].pos,&Viewer_eye);
1571                                                 n_sort_items++;
1572                                         }
1573                                         else {                  //no room for object
1574                                                 int ii;
1575
1576                                                 #ifndef NDEBUG
1577                                                 FILE *tfile=fopen("sortlist.out","wt");
1578
1579                                                 //I find this strange, so I'm going to write out
1580                                                 //some information to look at later
1581                                                 if (tfile) {
1582                                                         for (ii=0;ii<SORT_LIST_SIZE;ii++) {
1583                                                                 int objnum = sort_list[ii].objnum;
1584
1585                                                                 fprintf(tfile,"Obj %3d  Type = %2d  Id = %2d  Dist = %08x  Segnum = %3d\n",
1586                                                                         objnum,Objects[objnum].type,Objects[objnum].id,sort_list[ii].dist,Objects[objnum].segnum);
1587                                                         }
1588                                                         fclose(tfile);
1589                                                 }
1590                                                 #endif
1591
1592                                                 Int3(); //Get Matt!!!
1593
1594                                                 //Now try to find a place for this object by getting rid
1595                                                 //of an object we don't care about
1596
1597                                                 for (ii=0;ii<SORT_LIST_SIZE;ii++) {
1598                                                         int objnum = sort_list[ii].objnum;
1599                                                         object *obj = &Objects[objnum];
1600                                                         int type = obj->type;
1601
1602                                                         //replace debris & fireballs
1603                                                         if (type == OBJ_DEBRIS || type == OBJ_FIREBALL) {
1604                                                                 fix dist = vm_vec_dist_quick(&Objects[t].pos,&Viewer_eye);
1605
1606                                                                 //don't replace same kind of object unless new 
1607                                                                 //one is closer
1608
1609                                                                 if (Objects[t].type != type || dist < sort_list[ii].dist) {
1610                                                                         sort_list[ii].objnum = t;
1611                                                                         sort_list[ii].dist = dist;
1612                                                                         break;
1613                                                                 }
1614                                                         }
1615                                                 }
1616
1617                                                 Int3(); //still couldn't find a slot
1618                                         }
1619
1620
1621                         //now call qsort
1622                 #if defined(__WATCOMC__) || defined(MACINTOSH)
1623                         qsort(sort_list,n_sort_items,sizeof(*sort_list),
1624                                    sort_func);
1625                 #else
1626                         qsort(sort_list,n_sort_items,sizeof(*sort_list),
1627                                         (int (*)(const void*,const void*))sort_func);
1628                 #endif  
1629
1630                         //now copy back into list
1631
1632                         lookn = nn;
1633                         i = 0;
1634                         n = n_sort_items;
1635                         while ((t=render_obj_list[lookn][i])!=-1 && n>0)
1636                                 if (t<0)
1637                                         {lookn = -t; i=0;}
1638                                 else
1639                                         render_obj_list[lookn][i++] = sort_list[--n].objnum;
1640                         render_obj_list[lookn][i] = -1; //mark (possibly new) end
1641                 }
1642         }
1643 }
1644
1645 int Use_player_head_angles = 0;
1646 vms_angvec Player_head_angles;
1647
1648 extern int Num_tmaps_drawn;
1649 extern int Total_pixels;
1650 //--unused-- int Total_num_tmaps_drawn=0;
1651
1652 int Rear_view=0;
1653 extern ubyte RenderingType;
1654
1655 void start_lighting_frame(object *viewer);
1656
1657 #ifdef JOHN_ZOOM
1658 fix Zoom_factor=F1_0;
1659 #endif
1660 //renders onto current canvas
1661 void render_frame(fix eye_offset, int window_num)
1662 {
1663         int start_seg_num;
1664
1665 //Total_num_tmaps_drawn += Num_tmaps_drawn;
1666 //if ((FrameCount > 0) && (Total_num_tmaps_drawn))
1667 //      mprintf((0, "Frame: %4i, total = %6i, Avg = %7.3f, Avgpix=%7.3f\n", Num_tmaps_drawn, Total_num_tmaps_drawn, (float) Total_num_tmaps_drawn/FrameCount, (float) Total_pixels/Total_num_tmaps_drawn));
1668 //Num_tmaps_drawn = 0;
1669
1670         if (Endlevel_sequence) {
1671                 render_endlevel_frame(eye_offset);
1672                 FrameCount++;
1673                 return;
1674         }
1675
1676 #ifdef NEWDEMO
1677         if ( Newdemo_state == ND_STATE_RECORDING && eye_offset >= 0 )   {
1678      
1679           //mprintf((0, "Objnum=%d objtype=%d objid=%d\n", OBJECT_NUMBER(Viewer), Viewer->type, Viewer->id));
1680                 
1681       if (RenderingType==0)
1682                 newdemo_record_start_frame(FrameCount, FrameTime );
1683       if (RenderingType!=255)
1684                 newdemo_record_viewer_object(Viewer);
1685         }
1686 #endif
1687   
1688    //Here:
1689
1690         start_lighting_frame(Viewer);           //this is for ugly light-smoothing hack
1691   
1692         g3_start_frame();
1693
1694         Viewer_eye = Viewer->pos;
1695
1696 //      if (Viewer->type == OBJ_PLAYER && (Cockpit_mode.intval != CM_REAR_VIEW))
1697 //              vm_vec_scale_add2(&Viewer_eye,&Viewer->orient.fvec,(Viewer->size*3)/4);
1698
1699         if (eye_offset) {
1700                 vm_vec_scale_add2(&Viewer_eye,&Viewer->orient.rvec,eye_offset);
1701         }
1702
1703         #ifdef EDITOR
1704         if (Function_mode==FMODE_EDITOR)
1705                 Viewer_eye = Viewer->pos;
1706         #endif
1707
1708         start_seg_num = find_point_seg(&Viewer_eye,Viewer->segnum);
1709
1710         if (start_seg_num==-1)
1711                 start_seg_num = Viewer->segnum;
1712
1713         if (Viewer==ConsoleObject && Use_player_head_angles) {
1714                 vms_matrix headm,viewm;
1715                 vm_angles_2_matrix(&headm,&Player_head_angles);
1716                 vm_matrix_x_matrix(&viewm,&Viewer->orient,&headm);
1717                 g3_set_view_matrix(&Viewer_eye,&viewm,Render_zoom);
1718         //@@} else if ((Cockpit_mode.intval == CM_REAR_VIEW) && (Viewer == ConsoleObject)) {
1719         } else if (Rear_view && (Viewer==ConsoleObject)) {
1720                 vms_matrix headm,viewm;
1721                 Player_head_angles.p = Player_head_angles.b = 0;
1722                 Player_head_angles.h = 0x7fff;
1723                 vm_angles_2_matrix(&headm,&Player_head_angles);
1724                 vm_matrix_x_matrix(&viewm,&Viewer->orient,&headm);
1725                 g3_set_view_matrix(&Viewer_eye,&viewm,Render_zoom);
1726         } else  {
1727 #ifdef JOHN_ZOOM
1728                 if (keyd_pressed[KEY_RSHIFT] )  {
1729                         Zoom_factor += FrameTime*4;
1730                         if (Zoom_factor > F1_0*5 ) Zoom_factor=F1_0*5;
1731                 } else {
1732                         Zoom_factor -= FrameTime*4;
1733                         if (Zoom_factor < F1_0 ) Zoom_factor = F1_0;
1734                 }
1735                 g3_set_view_matrix(&Viewer_eye,&Viewer->orient,fixdiv(Render_zoom,Zoom_factor));
1736 #else
1737                 g3_set_view_matrix(&Viewer_eye,&Viewer->orient,Render_zoom);
1738 #endif
1739         }
1740
1741         if (Clear_window == 1) {
1742                 if (Clear_window_color == -1)
1743                         Clear_window_color = BM_XRGB(0, 0, 0);  //BM_XRGB(31, 15, 7);
1744                 gr_clear_canvas(Clear_window_color);
1745         }
1746         #ifndef NDEBUG
1747         if (Show_only_curside)
1748                 gr_clear_canvas(Clear_window_color);
1749         #endif
1750
1751         render_mine(start_seg_num, eye_offset, window_num);
1752
1753         if (Use_player_head_angles ) 
1754                 draw_3d_reticle(eye_offset);
1755
1756         g3_end_frame();
1757
1758    //RenderingType=0;
1759
1760         // -- Moved from here by MK, 05/17/95, wrong if multiple renders/frame! FrameCount++;           //we have rendered a frame
1761 }
1762
1763 int first_terminal_seg;
1764
1765 void update_rendered_data(int window_num, object *viewer, int rear_view_flag, int user)
1766 {
1767         Assert(window_num < MAX_RENDERED_WINDOWS);
1768         Window_rendered_data[window_num].frame = FrameCount;
1769         Window_rendered_data[window_num].viewer = viewer;
1770         Window_rendered_data[window_num].rear_view = rear_view_flag;
1771         Window_rendered_data[window_num].user = user;
1772 }
1773
1774 //build a list of segments to be rendered
1775 //fills in Render_list & N_render_segs
1776 void build_segment_list(int start_seg_num, int window_num)
1777 {
1778         int     lcnt,scnt,ecnt;
1779         int     l,c;
1780         int     ch;
1781
1782         memset(visited, 0, sizeof(visited[0])*(Highest_segment_index+1));
1783         memset(render_pos, -1, sizeof(render_pos[0])*(Highest_segment_index+1));
1784         //memset(no_render_flag, 0, sizeof(no_render_flag[0])*(MAX_RENDER_SEGS));
1785         memset(processed, 0, sizeof(processed));
1786
1787         #ifndef NDEBUG
1788         memset(visited2, 0, sizeof(visited2[0])*(Highest_segment_index+1));
1789         #endif
1790
1791         lcnt = scnt = 0;
1792
1793         Render_list[lcnt] = start_seg_num; visited[start_seg_num]=1;
1794         Seg_depth[lcnt] = 0;
1795         lcnt++;
1796         ecnt = lcnt;
1797         render_pos[start_seg_num] = 0;
1798
1799         #ifndef NDEBUG
1800         if (pre_draw_segs)
1801                 render_segment(start_seg_num, window_num);
1802         #endif
1803
1804         render_windows[0].left=render_windows[0].top=0;
1805         render_windows[0].right=grd_curcanv->cv_bitmap.bm_w-1;
1806         render_windows[0].bot=grd_curcanv->cv_bitmap.bm_h-1;
1807
1808         //breadth-first renderer
1809
1810         //build list
1811
1812         for (l=0;l<Render_depth;l++) {
1813
1814                 //while (scnt < ecnt) {
1815                 for (scnt=0;scnt < ecnt;scnt++) {
1816                         int rotated,segnum;
1817                         window *check_w;
1818                         short child_list[MAX_SIDES_PER_SEGMENT];                //list of ordered sides to process
1819                         int n_children;                                                                         //how many sides in child_list
1820                         segment *seg;
1821
1822                         if (processed[scnt])
1823                                 continue;
1824
1825                         processed[scnt]=1;
1826
1827                         segnum = Render_list[scnt];
1828                         check_w = &render_windows[scnt];
1829
1830                         #ifndef NDEBUG
1831                         if (draw_boxes)
1832                                 draw_window_box(RED,check_w->left,check_w->top,check_w->right,check_w->bot);
1833                         #endif
1834
1835                         if (segnum == -1) continue;
1836
1837                         seg = &Segments[segnum];
1838                         rotated=0;
1839
1840                         //look at all sides of this segment.
1841                         //tricky code to look at sides in correct order follows
1842
1843                         for (c=n_children=0;c<MAX_SIDES_PER_SEGMENT;c++) {              //build list of sides
1844                                 int wid;
1845
1846                                 wid = WALL_IS_DOORWAY(seg, c);
1847
1848                                 ch=seg->children[c];
1849
1850                                 if ( (window_check || !visited[ch]) && (wid & WID_RENDPAST_FLAG) ) {
1851                                         if (behind_check) {
1852                                                 sbyte *sv = Side_to_verts[c];
1853                                                 ubyte codes_and=0xff;
1854                                                 int i;
1855
1856                                                 rotate_list(8,seg->verts);
1857                                                 rotated=1;
1858
1859                                                 for (i=0;i<4;i++)
1860                                                         codes_and &= Segment_points[seg->verts[sv[i]]].p3_codes;
1861
1862                                                 if (codes_and & CC_BEHIND) continue;
1863
1864                                         }
1865                                         child_list[n_children++] = c;
1866                                 }
1867                         }
1868
1869                         //now order the sides in some magical way
1870
1871                         if (new_seg_sorting)
1872                                 sort_seg_children(seg,n_children,child_list);
1873
1874                         //for (c=0;c<MAX_SIDES_PER_SEGMENT;c++) {
1875                         //      ch=seg->children[c];
1876
1877                         for (c=0;c<n_children;c++) {
1878                                 int siden;
1879
1880                                 siden = child_list[c];
1881                                 ch=seg->children[siden];
1882                                 //if ( (window_check || !visited[ch])&& (WALL_IS_DOORWAY(seg, c))) {
1883                                 {
1884                                         if (window_check) {
1885                                                 int i;
1886                                                 ubyte codes_and_3d,codes_and_2d;
1887                                                 short _x,_y,min_x=32767,max_x=-32767,min_y=32767,max_y=-32767;
1888                                                 int no_proj_flag=0;     //a point wasn't projected
1889
1890                                                 if (rotated<2) {
1891                                                         if (!rotated)
1892                                                                 rotate_list(8,seg->verts);
1893                                                         project_list(8,seg->verts);
1894                                                         rotated=2;
1895                                                 }
1896
1897                                                 for (i=0,codes_and_3d=codes_and_2d=0xff;i<4;i++) {
1898                                                         int p = seg->verts[Side_to_verts[siden][i]];
1899                                                         g3s_point *pnt = &Segment_points[p];
1900
1901                                                         if (! (pnt->p3_flags&PF_PROJECTED)) {no_proj_flag=1; break;}
1902
1903                                                         _x = f2i(pnt->p3_sx);
1904                                                         _y = f2i(pnt->p3_sy);
1905
1906                                                         codes_and_3d &= pnt->p3_codes;
1907                                                         codes_and_2d &= code_window_point(_x,_y,check_w);
1908
1909                                                         #ifndef NDEBUG
1910                                                         if (draw_edges) {
1911                                                                 gr_setcolor(BM_XRGB(31,0,31));
1912                                                                 gr_line(pnt->p3_sx,pnt->p3_sy,
1913                                                                         Segment_points[seg->verts[Side_to_verts[siden][(i+1)%4]]].p3_sx,
1914                                                                         Segment_points[seg->verts[Side_to_verts[siden][(i+1)%4]]].p3_sy);
1915                                                         }
1916                                                         #endif
1917
1918                                                         if (_x < min_x) min_x = _x;
1919                                                         if (_x > max_x) max_x = _x;
1920
1921                                                         if (_y < min_y) min_y = _y;
1922                                                         if (_y > max_y) max_y = _y;
1923
1924                                                 }
1925
1926                                                 #ifndef NDEBUG
1927                                                 if (draw_boxes)
1928                                                         draw_window_box(WHITE,min_x,min_y,max_x,max_y);
1929                                                 #endif
1930
1931                                                 if (no_proj_flag || (!codes_and_3d && !codes_and_2d)) { //maybe add this segment
1932                                                         int rp = render_pos[ch];
1933                                                         window *new_w = &render_windows[lcnt];
1934
1935                                                         if (no_proj_flag) *new_w = *check_w;
1936                                                         else {
1937                                                                 new_w->left  = max(check_w->left,min_x);
1938                                                                 new_w->right = min(check_w->right,max_x);
1939                                                                 new_w->top   = max(check_w->top,min_y);
1940                                                                 new_w->bot   = min(check_w->bot,max_y);
1941                                                         }
1942
1943                                                         //see if this seg already visited, and if so, does current window
1944                                                         //expand the old window?
1945                                                         if (rp != -1) {
1946                                                                 if (new_w->left < render_windows[rp].left ||
1947                                                                                  new_w->top < render_windows[rp].top ||
1948                                                                                  new_w->right > render_windows[rp].right ||
1949                                                                                  new_w->bot > render_windows[rp].bot) {
1950
1951                                                                         new_w->left  = min(new_w->left,render_windows[rp].left);
1952                                                                         new_w->right = max(new_w->right,render_windows[rp].right);
1953                                                                         new_w->top   = min(new_w->top,render_windows[rp].top);
1954                                                                         new_w->bot   = max(new_w->bot,render_windows[rp].bot);
1955
1956                                                                         if (no_migrate_segs) {
1957                                                                                 //no_render_flag[lcnt] = 1;
1958                                                                                 Render_list[lcnt] = -1;
1959                                                                                 render_windows[rp] = *new_w;            //get updated window
1960                                                                                 processed[rp] = 0;              //force reprocess
1961                                                                                 goto no_add;
1962                                                                         }
1963                                                                         else
1964                                                                                 Render_list[rp]=-1;
1965                                                                 }
1966                                                                 else goto no_add;
1967                                                         }
1968
1969                                                         #ifndef NDEBUG
1970                                                         if (draw_boxes)
1971                                                                 draw_window_box(5,new_w->left,new_w->top,new_w->right,new_w->bot);
1972                                                         #endif
1973
1974                                                         render_pos[ch] = lcnt;
1975                                                         Render_list[lcnt] = ch;
1976                                                         Seg_depth[lcnt] = l;
1977                                                         lcnt++;
1978                                                         if (lcnt >= MAX_RENDER_SEGS) {mprintf((0,"Too many segs in render list!!\n")); goto done_list;}
1979                                                         visited[ch] = 1;
1980
1981                                                         #ifndef NDEBUG
1982                                                         if (pre_draw_segs)
1983                                                                 render_segment(ch, window_num);
1984                                                         #endif
1985 no_add:
1986         ;
1987
1988                                                 }
1989                                         }
1990                                         else {
1991                                                 Render_list[lcnt] = ch;
1992                                                 Seg_depth[lcnt] = l;
1993                                                 lcnt++;
1994                                                 if (lcnt >= MAX_RENDER_SEGS) {mprintf((0,"Too many segs in render list!!\n")); goto done_list;}
1995                                                 visited[ch] = 1;
1996                                         }
1997                                 }
1998                         }
1999                 }
2000
2001                 scnt = ecnt;
2002                 ecnt = lcnt;
2003
2004         }
2005 done_list:
2006
2007         lcnt_save = lcnt;
2008         scnt_save = scnt;
2009
2010         first_terminal_seg = scnt;
2011         N_render_segs = lcnt;
2012
2013 }
2014
2015 //renders onto current canvas
2016 void render_mine(int start_seg_num,fix eye_offset, int window_num)
2017 {
2018 #ifndef NDEBUG
2019         int             i;
2020 #endif
2021         int             nn;
2022
2023         //      Initialize number of objects (actually, robots!) rendered this frame.
2024         Window_rendered_data[window_num].num_objects = 0;
2025
2026 #ifdef LASER_HACK
2027         Hack_nlasers = 0;
2028 #endif
2029
2030         #ifndef NDEBUG
2031         for (i=0;i<=Highest_object_index;i++)
2032                 object_rendered[i] = 0;
2033         #endif
2034
2035         //set up for rendering
2036
2037         render_start_frame();
2038
2039
2040         #if defined(EDITOR)
2041         if (Show_only_curside) {
2042                 rotate_list(8,Cursegp->verts);
2043                 render_side(Cursegp,Curside);
2044                 goto done_rendering;
2045         }
2046         #endif
2047
2048
2049         #ifdef EDITOR
2050         if (_search_mode)       {
2051                 //lcnt = lcnt_save;
2052                 //scnt = scnt_save;
2053         }
2054         else
2055         #endif
2056                 //NOTE LINK TO ABOVE!!
2057                 build_segment_list(start_seg_num, window_num);          //fills in Render_list & N_render_segs
2058
2059         //render away
2060
2061         #ifndef NDEBUG
2062         if (!window_check) {
2063                 Window_clip_left  = Window_clip_top = 0;
2064                 Window_clip_right = grd_curcanv->cv_bitmap.bm_w-1;
2065                 Window_clip_bot   = grd_curcanv->cv_bitmap.bm_h-1;
2066         }
2067         #endif
2068
2069         #ifndef NDEBUG
2070         if (!(_search_mode)) {
2071                 int i;
2072
2073                 for (i=0;i<N_render_segs;i++) {
2074                         int segnum;
2075
2076                         segnum = Render_list[i];
2077
2078                         if (segnum != -1)
2079                         {
2080                                 if (visited2[segnum])
2081                                         Int3();         //get Matt
2082                                 else
2083                                         visited2[segnum] = 1;
2084                         }
2085                 }
2086         }
2087         #endif
2088
2089         if (!(_search_mode))
2090                 build_object_lists(N_render_segs);
2091
2092         if (eye_offset<=0)              // Do for left eye or zero.
2093                 set_dynamic_light();
2094
2095         if (!_search_mode && Clear_window == 2) {
2096                 if (first_terminal_seg < N_render_segs) {
2097                         int i;
2098
2099                         if (Clear_window_color == -1)
2100                                 Clear_window_color = BM_XRGB(0, 0, 0);  //BM_XRGB(31, 15, 7);
2101         
2102                         gr_setcolor(Clear_window_color);
2103         
2104                         for (i=first_terminal_seg; i<N_render_segs; i++) {
2105                                 if (Render_list[i] != -1) {
2106                                         #ifndef NDEBUG
2107                                         if ((render_windows[i].left == -1) || (render_windows[i].top == -1) || (render_windows[i].right == -1) || (render_windows[i].bot == -1))
2108                                                 Int3();
2109                                         else
2110                                         #endif
2111                                                 //NOTE LINK TO ABOVE!
2112                                                 gr_rect(render_windows[i].left, render_windows[i].top, render_windows[i].right, render_windows[i].bot);
2113                                 }
2114                         }
2115                 }
2116         }
2117
2118         for (nn=N_render_segs;nn--;) {
2119                 int segnum;
2120                 int objnp;
2121
2122                 // Interpolation_method = 0;
2123                 segnum = Render_list[nn];
2124                 Current_seg_depth = Seg_depth[nn];
2125
2126                 //if (!no_render_flag[nn])
2127                 if (segnum!=-1 && (_search_mode || visited[segnum]!=255)) {
2128                         //set global render window vars
2129
2130                         if (window_check) {
2131                                 Window_clip_left  = render_windows[nn].left;
2132                                 Window_clip_top   = render_windows[nn].top;
2133                                 Window_clip_right = render_windows[nn].right;
2134                                 Window_clip_bot   = render_windows[nn].bot;
2135                         }
2136
2137                         //mprintf((0," %d",segnum));
2138
2139                         render_segment(segnum, window_num);
2140                         visited[segnum]=255;
2141
2142                         if (window_check) {             //reset for objects
2143                                 Window_clip_left  = Window_clip_top = 0;
2144                                 Window_clip_right = grd_curcanv->cv_bitmap.bm_w-1;
2145                                 Window_clip_bot   = grd_curcanv->cv_bitmap.bm_h-1;
2146                         }
2147
2148                         if (migrate_objects) {
2149                                 //int n_expl_objs=0,expl_objs[5],i;
2150                                 int listnum;
2151                                 int save_linear_depth = Max_linear_depth;
2152
2153                                 Max_linear_depth = Max_linear_depth_objects;
2154
2155                                 listnum = nn;
2156
2157                                 //mprintf((0,"render objs seg %d",segnum));
2158
2159                                 for (objnp=0;render_obj_list[listnum][objnp]!=-1;)      {
2160                                         int ObjNumber = render_obj_list[listnum][objnp];
2161
2162                                         if (ObjNumber >= 0) {
2163
2164                                                 //mprintf( (0, "Type: %d\n", Objects[ObjNumber].type ));
2165         
2166                                                 //if (Objects[ObjNumber].type == OBJ_FIREBALL && n_expl_objs<5) {
2167                                                 //      expl_objs[n_expl_objs++] = ObjNumber;
2168                                                 //} else
2169                                                 #ifdef LASER_HACK
2170                                                 if (    (Objects[ObjNumber].type==OBJ_WEAPON) &&                                                                //if its a weapon
2171                                                                 (Objects[ObjNumber].lifeleft==Laser_max_time ) &&       //  and its in it's first frame
2172                                                                 (Hack_nlasers< MAX_HACKED_LASERS) &&                                                                    //  and we have space for it
2173                                                                 (Objects[ObjNumber].laser_info.parent_num>-1) &&                                        //  and it has a parent
2174                                                                 (OBJECT_NUMBER(Viewer) == Objects[ObjNumber].laser_info.parent_num) // and it's parent is the viewer
2175                                                    )            {
2176                                                         Hack_laser_list[Hack_nlasers++] = ObjNumber;                                                            //then make it draw after everything else.
2177                                                         //mprintf( (0, "O%d ", ObjNumber ));
2178                                                 } else  
2179                                                 #endif
2180                                                         do_render_object(ObjNumber, window_num);        // note link to above else
2181
2182                                                 objnp++;
2183                                         }
2184                                         else {
2185
2186                                                 listnum = -ObjNumber;
2187                                                 objnp = 0;
2188
2189                                         }
2190
2191                                 }
2192
2193                                 //for (i=0;i<n_expl_objs;i++)
2194                                 //      do_render_object(expl_objs[i], window_num);
2195
2196                                 //mprintf((0,"done seg %d\n",segnum));
2197
2198                                 Max_linear_depth = save_linear_depth;
2199
2200                         }
2201
2202                 }
2203         }
2204
2205         //mprintf((0,"\n"));
2206
2207                                                                 
2208 #ifdef LASER_HACK                                                               
2209         // Draw the hacked lasers last
2210         for (i=0; i < Hack_nlasers; i++ )       {
2211                 //mprintf( (0, "D%d ", Hack_laser_list[i] ));
2212                 do_render_object(Hack_laser_list[i], window_num);
2213         }
2214 #endif
2215
2216         // -- commented out by mk on 09/14/94...did i do a good thing??  object_render_targets();
2217
2218 #ifdef EDITOR
2219         #ifndef NDEBUG
2220         //draw curedge stuff
2221         if (Outline_mode) outline_seg_side(Cursegp,Curside,Curedge,Curvert);
2222         #endif
2223
2224 done_rendering:
2225         ;
2226
2227 #endif
2228
2229 }
2230 #ifdef EDITOR
2231
2232 extern int render_3d_in_big_window;
2233
2234 //finds what segment is at a given x&y -  seg,side,face are filled in
2235 //works on last frame rendered. returns true if found
2236 //if seg<0, then an object was found, and the object number is -seg-1
2237 int find_seg_side_face(short x,short y,int *seg,int *side,int *face,int *poly)
2238 {
2239         _search_mode = -1;
2240
2241         _search_x = x; _search_y = y;
2242
2243         found_seg = -1;
2244
2245         if (render_3d_in_big_window) {
2246                 grs_canvas temp_canvas;
2247
2248                 gr_init_sub_canvas(&temp_canvas,canv_offscreen,0,0,
2249                         LargeView.ev_canv->cv_bitmap.bm_w,LargeView.ev_canv->cv_bitmap.bm_h);
2250
2251                 gr_set_current_canvas(&temp_canvas);
2252
2253                 render_frame(0, 0);
2254         }
2255         else {
2256                 gr_set_current_canvas(&VR_render_sub_buffer[0]);        //render off-screen
2257                 render_frame(0, 0);
2258         }
2259
2260         _search_mode = 0;
2261
2262         *seg = found_seg;
2263         *side = found_side;
2264         *face = found_face;
2265         *poly = found_poly;
2266
2267 //      mprintf((0,"found seg=%d, side=%d, face=%d, poly=%d\n",found_seg,found_side,found_face,found_poly));
2268
2269         return (found_seg!=-1);
2270
2271 }
2272
2273 #endif