home *** CD-ROM | disk | FTP | other *** search
/ Amiga ACS 1998 #6 / amigaacscoverdisc1998-061998.iso / games / descent / source / main / render.c < prev    next >
Text File  |  1998-06-08  |  62KB  |  2,266 lines

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