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