1 //********************************************************************************************
2 //*
3 //*    This file is part of Egoboo.
4 //*
5 //*    Egoboo is free software: you can redistribute it and/or modify it
6 //*    under the terms of the GNU General Public License as published by
7 //*    the Free Software Foundation, either version 3 of the License, or
8 //*    (at your option) any later version.
9 //*
10 //*    Egoboo is distributed in the hope that it will be useful, but
11 //*    WITHOUT ANY WARRANTY; without even the implied warranty of
12 //*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 //*    General Public License for more details.
14 //*
15 //*    You should have received a copy of the GNU General Public License
16 //*    along with Egoboo.  If not, see <http://www.gnu.org/licenses/>.
17 //*
18 //********************************************************************************************
19 
20 /// @file graphic.c
21 /// @brief Simple Egoboo renderer
22 /// @details All sorts of stuff related to drawing the game
23 
24 #include "graphic.h"
25 #include "graphic_prt.h"
26 #include "graphic_mad.h"
27 #include "graphic_fan.h"
28 
29 #include "mad.h"
30 #include "obj_BSP.h"
31 
32 #include "collision.h"
33 
34 #include "log.h"
35 #include "script.h"
36 #include "camera.h"
37 #include "id_md2.h"
38 #include "input.h"
39 #include "network.h"
40 #include "passage.h"
41 #include "menu.h"
42 #include "script_compile.h"
43 #include "game.h"
44 #include "ui.h"
45 #include "texture.h"
46 #include "clock.h"
47 #include "font_bmp.h"
48 #include "lighting.h"
49 
50 #if defined(USE_LUA_CONSOLE)
51 #    include "lua_console.h"
52 #else
53 #    include "egoboo_console.h"
54 #endif
55 
56 #include "egoboo_vfs.h"
57 #include "egoboo_setup.h"
58 #include "egoboo_strutil.h"
59 #include "egoboo_fileutil.h"
60 #include "egoboo.h"
61 
62 #include "SDL_extensions.h"
63 #include "SDL_GL_extensions.h"
64 
65 #include "char.inl"
66 #include "particle.inl"
67 #include "enchant.inl"
68 #include "profile.inl"
69 #include "mesh.inl"
70 
71 #include <SDL_image.h>
72 
73 #include <assert.h>
74 
75 //--------------------------------------------------------------------------------------------
76 //--------------------------------------------------------------------------------------------
77 
78 #define SPARKLESIZE 28
79 #define SPARKLEADD 2
80 #define BLIPSIZE 6
81 
82 //--------------------------------------------------------------------------------------------
83 //--------------------------------------------------------------------------------------------
84 
85 /// Structure for keeping track of which dynalights are visible
86 struct s_dynalight_registry
87 {
88     int         reference;
89     ego_frect_t bound;
90 };
91 
92 typedef struct s_dynalight_registry dynalight_registry_t;
93 
94 //--------------------------------------------------------------------------------------------
95 //--------------------------------------------------------------------------------------------
96 
97 struct s_dolist
98 {
99     size_t                count;                ///< How many in the list
100     obj_registry_entity_t lst[DOLIST_SIZE];     ///< List of which objects to draw
101 };
102 typedef struct s_dolist dolist_t;
103 
104 #define DOLIST_INIT { 0, OBJ_REGISTRY_ENTITY_INIT }
105 
106 static gfx_rv dolist_sort( dolist_t * pdolist, camera_t * pcam, bool_t do_reflect );
107 static gfx_rv dolist_make( dolist_t * pdolist, ego_mpd_t * pmesh );
108 static gfx_rv dolist_add_chr( dolist_t * pdolist, ego_mpd_t * pmesh, const CHR_REF ichr );
109 static gfx_rv dolist_add_prt( dolist_t * pdolist, ego_mpd_t * pmesh, const PRT_REF iprt );
110 
111 //--------------------------------------------------------------------------------------------
112 
113 /// The active dynamic lights
114 struct s_dynalist
115 {
116     int         count;                    ///< the count
117     dynalight_t lst[TOTAL_MAX_DYNA];      ///< the list
118 };
119 typedef struct s_dynalist dynalist_t;
120 
121 #define DYNALIST_INIT { 0.0f, 0 }
122 
123 static gfx_rv dynalist_init( dynalist_t * pdylist );
124 static gfx_rv do_grid_lighting( renderlist_t * prlist, dynalist_t * pdylist, camera_t * pcam );
125 static gfx_rv gfx_make_dynalist( dynalist_t * pdylist, camera_t * pcam );
126 
127 //--------------------------------------------------------------------------------------------
128 //--------------------------------------------------------------------------------------------
129 
130 INSTANTIATE_LIST( ACCESS_TYPE_NONE, billboard_data_t, BillboardList, BILLBOARD_COUNT );
131 
132 PROFILE_DECLARE( render_scene_init );
133 PROFILE_DECLARE( render_scene_mesh );
134 PROFILE_DECLARE( render_scene_solid );
135 PROFILE_DECLARE( render_scene_water );
136 PROFILE_DECLARE( render_scene_trans );
137 
138 PROFILE_DECLARE( renderlist_make );
139 PROFILE_DECLARE( dolist_make );
140 PROFILE_DECLARE( do_grid_lighting );
141 PROFILE_DECLARE( light_fans );
142 PROFILE_DECLARE( update_all_chr_instance );
143 PROFILE_DECLARE( update_all_prt_instance );
144 
145 PROFILE_DECLARE( render_scene_mesh_dolist_sort );
146 PROFILE_DECLARE( render_scene_mesh_ndr );
147 PROFILE_DECLARE( render_scene_mesh_drf_back );
148 PROFILE_DECLARE( render_scene_mesh_ref );
149 PROFILE_DECLARE( render_scene_mesh_ref_chr );
150 PROFILE_DECLARE( render_scene_mesh_drf_solid );
151 PROFILE_DECLARE( render_scene_mesh_render_shadows );
152 
153 float time_draw_scene       = 0.0f;
154 float time_render_scene_init  = 0.0f;
155 float time_render_scene_mesh  = 0.0f;
156 float time_render_scene_solid = 0.0f;
157 float time_render_scene_water = 0.0f;
158 float time_render_scene_trans = 0.0f;
159 
160 float time_render_scene_init_renderlist_make         = 0.0f;
161 float time_render_scene_init_dolist_make             = 0.0f;
162 float time_render_scene_init_do_grid_dynalight       = 0.0f;
163 float time_render_scene_init_light_fans              = 0.0f;
164 float time_render_scene_init_update_all_chr_instance = 0.0f;
165 float time_render_scene_init_update_all_prt_instance = 0.0f;
166 
167 float time_render_scene_mesh_dolist_sort    = 0.0f;
168 float time_render_scene_mesh_ndr            = 0.0f;
169 float time_render_scene_mesh_drf_back       = 0.0f;
170 float time_render_scene_mesh_ref            = 0.0f;
171 float time_render_scene_mesh_ref_chr        = 0.0f;
172 float time_render_scene_mesh_drf_solid      = 0.0f;
173 float time_render_scene_mesh_render_shadows = 0.0f;
174 
175 //--------------------------------------------------------------------------------------------
176 //--------------------------------------------------------------------------------------------
177 int GFX_WIDTH  = 800;
178 int GFX_HEIGHT = 600;
179 
180 gfx_config_t     gfx;
181 
182 renderlist_t renderlist = RENDERLIST_INIT;         // zero all the counters at startup
183 
184 float            indextoenvirox[EGO_NORMAL_COUNT];
185 float            lighttoenviroy[256];
186 
187 int rotmesh_topside;
188 int rotmesh_bottomside;
189 int rotmesh_up;
190 int rotmesh_down;
191 
192 Uint8   mapon         = bfalse;
193 Uint8   mapvalid      = bfalse;
194 Uint8   youarehereon  = bfalse;
195 
196 size_t  blip_count    = 0;
197 float   blip_x[MAXBLIP];
198 float   blip_y[MAXBLIP];
199 Uint8   blip_c[MAXBLIP];
200 
201 int     msgtimechange = 0;
202 
203 INSTANTIATE_STATIC_ARY( DisplayMsgAry, DisplayMsg );
204 
205 //--------------------------------------------------------------------------------------------
206 //--------------------------------------------------------------------------------------------
207 
208 static gfx_error_stack_t gfx_error_stack = GFX_ERROR_STACK_INIT;
209 
210 static line_data_t line_list[LINE_COUNT];
211 static dolist_t    _dolist = DOLIST_INIT;
212 
213 static SDLX_video_parameters_t sdl_vparam;
214 static oglx_video_parameters_t ogl_vparam;
215 
216 static SDL_bool _sdl_initialized_graphics = SDL_FALSE;
217 static bool_t   _ogl_initialized          = bfalse;
218 
219 static float sinlut[MAXLIGHTROTATION];
220 static float coslut[MAXLIGHTROTATION];
221 
222 // Camera optimization stuff
223 static float                   cornerx[4];
224 static float                   cornery[4];
225 static int                     cornerlistlowtohighy[4];
226 static float                   cornerlowx;
227 static float                   cornerhighx;
228 static float                   cornerlowy;
229 static float                   cornerhighy;
230 
231 static dynalist_t dynalist = DYNALIST_INIT;
232 
233 // Interface stuff
234 static irect_t iconrect;                   // The 32x32 icon rectangle
235 
236 static irect_t tabrect[NUMBAR];            // The tab rectangles
237 static irect_t barrect[NUMBAR];            // The bar rectangles
238 static irect_t bliprect[COLOR_MAX];        // The blip rectangles
239 static irect_t maprect;                    // The map rectangle
240 
241 static bool_t  gfx_page_flip_requested  = bfalse;
242 static bool_t  gfx_page_clear_requested = btrue;
243 
244 static float dynalight_keep = 0.9f;
245 
246 //--------------------------------------------------------------------------------------------
247 //--------------------------------------------------------------------------------------------
248 
249 static void gfx_init_SDL_graphics();
250 
251 static void _flip_pages();
252 static void _debug_print( const char *text );
253 static int  _debug_vprintf( const char *format, va_list args );
254 static int  _va_draw_string( float x, float y, const char *format, va_list args );
255 static int  _draw_string_raw( float x, float y, const char *format, ... );
256 
257 static gfx_rv gfx_project_cam_view( camera_t * pcam );
258 
259 static void init_icon_data();
260 static void init_bar_data();
261 static void init_blip_data();
262 static void init_map_data();
263 
264 static bool_t render_one_billboard( billboard_data_t * pbb, float scale, const fvec3_base_t cam_up, const fvec3_base_t cam_rgt );
265 
266 static void gfx_update_timers();
267 
268 static void gfx_begin_text();
269 static void gfx_end_text();
270 
271 static void gfx_enable_texturing();
272 static void gfx_disable_texturing();
273 
274 static void gfx_begin_2d( void );
275 static void gfx_end_2d( void );
276 
277 static gfx_rv light_fans( renderlist_t * prlist );
278 static gfx_rv render_water( renderlist_t * prlist );
279 
280 static void draw_quad_2d( oglx_texture_t * ptex, const ego_frect_t scr_rect, const ego_frect_t tx_rect, bool_t use_alpha );
281 
282 static gfx_rv update_one_chr_instance( struct s_chr * pchr );
283 static gfx_rv update_all_chr_instance();
284 
285 static gfx_rv do_chr_flashing( dolist_t * pdolist );
286 
287 //--------------------------------------------------------------------------------------------
288 // MODULE "PRIVATE" FUNCTIONS
289 //--------------------------------------------------------------------------------------------
_debug_print(const char * text)290 void _debug_print( const char *text )
291 {
292     /// @details ZZ@> This function sticks a message in the display queue and sets its timer
293 
294     int          slot;
295     const char * src;
296     char       * dst, * dst_end;
297     msg_t      * pmsg;
298 
299     if ( INVALID_CSTR( text ) ) return;
300 
301     // Get a "free" message
302     slot = DisplayMsg_get_free();
303     pmsg = DisplayMsg.ary + slot;
304 
305     // Copy the message
306     for ( src = text, dst = pmsg->textdisplay, dst_end = dst + MESSAGESIZE;
307           CSTR_END != *src && dst < dst_end;
308           src++, dst++ )
309     {
310         *dst = *src;
311     }
312     if ( dst < dst_end ) *dst = CSTR_END;
313 
314     // Set the time
315     pmsg->time = cfg.message_duration;
316 }
317 
318 //--------------------------------------------------------------------------------------------
_debug_vprintf(const char * format,va_list args)319 int _debug_vprintf( const char *format, va_list args )
320 {
321     int retval = 0;
322 
323     if ( VALID_CSTR( format ) )
324     {
325         STRING szTmp;
326 
327         retval = vsnprintf( szTmp, SDL_arraysize( szTmp ), format, args );
328         _debug_print( szTmp );
329     }
330 
331     return retval;
332 }
333 
334 //--------------------------------------------------------------------------------------------
_va_draw_string(float x,float y,const char * format,va_list args)335 int _va_draw_string( float x, float y, const char *format, va_list args )
336 {
337     int cnt = 1;
338     int x_stt;
339     STRING szText;
340     Uint8 cTmp;
341 
342     oglx_texture_t * tx_ptr = TxTexture_get_ptr(( TX_REF )TX_FONT );
343     if ( NULL == tx_ptr ) return y;
344 
345     if ( vsnprintf( szText, SDL_arraysize( szText ) - 1, format, args ) <= 0 )
346     {
347         return y;
348     }
349 
350     gfx_begin_text();
351     {
352         x_stt = x;
353         cnt = 0;
354         cTmp = szText[cnt];
355         while ( CSTR_END != cTmp )
356         {
357             Uint8 iTmp;
358 
359             // Convert ASCII to our own little font
360             if ( '~' == cTmp )
361             {
362                 // Use squiggle for tab
363                 x = ( FLOOR(( float )x / ( float )TABADD ) + 1.0f ) * TABADD;
364             }
365             else if ( C_NEW_LINE_CHAR == cTmp )
366             {
367                 x  = x_stt;
368                 y += fontyspacing;
369             }
370             else if ( isspace( cTmp ) )
371             {
372                 // other whitespace
373                 iTmp = asciitofont[cTmp];
374                 x += fontxspacing[iTmp] / 2;
375             }
376             else
377             {
378                 // Normal letter
379                 iTmp = asciitofont[cTmp];
380                 draw_one_font( tx_ptr, iTmp, x, y );
381                 x += fontxspacing[iTmp];
382             }
383 
384             cnt++;
385             cTmp = szText[cnt];
386         }
387     }
388     gfx_end_text();
389 
390     y += fontyspacing;
391 
392     return y;
393 }
394 
395 //--------------------------------------------------------------------------------------------
_draw_string_raw(float x,float y,const char * format,...)396 int _draw_string_raw( float x, float y, const char *format, ... )
397 {
398     /// @details BB@> the same as draw string, but it does not use the gfx_begin_2d() ... gfx_end_2d()
399     ///    bookends.
400 
401     va_list args;
402 
403     va_start( args, format );
404     y = _va_draw_string( x, y, format, args );
405     va_end( args );
406 
407     return y;
408 }
409 
410 //--------------------------------------------------------------------------------------------
411 // MODULE INITIALIZATION
412 //--------------------------------------------------------------------------------------------
gfx_system_begin()413 void gfx_system_begin()
414 {
415     // set the graphics state
416     gfx_init_SDL_graphics();
417     ogl_init();
418 
419     // initialize the gfx data dtructures
420     BillboardList_free_all();
421     TxTexture_init_all();
422 
423     // initialize the profiling variables
424     PROFILE_INIT( render_scene_init );
425     PROFILE_INIT( render_scene_mesh );
426     PROFILE_INIT( render_scene_solid );
427     PROFILE_INIT( render_scene_water );
428     PROFILE_INIT( render_scene_trans );
429 
430     PROFILE_INIT( renderlist_make );
431     PROFILE_INIT( dolist_make );
432     PROFILE_INIT( do_grid_lighting );
433     PROFILE_INIT( light_fans );
434     PROFILE_INIT( update_all_chr_instance );
435     PROFILE_INIT( update_all_prt_instance );
436 
437     PROFILE_INIT( render_scene_mesh_dolist_sort );
438     PROFILE_INIT( render_scene_mesh_ndr );
439     PROFILE_INIT( render_scene_mesh_drf_back );
440     PROFILE_INIT( render_scene_mesh_ref );
441     PROFILE_INIT( render_scene_mesh_ref_chr );
442     PROFILE_INIT( render_scene_mesh_drf_solid );
443     PROFILE_INIT( render_scene_mesh_render_shadows );
444 
445     // init some other variables
446     stabilized_game_fps        = TARGET_FPS;
447     stabilized_game_fps_sum    = 0.1f * TARGET_FPS;
448     stabilized_game_fps_weight = 0.1f;
449 }
450 
451 //--------------------------------------------------------------------------------------------
gfx_system_end()452 void gfx_system_end()
453 {
454     // initialize the profiling variables
455     PROFILE_FREE( render_scene_init );
456     PROFILE_FREE( render_scene_mesh );
457     PROFILE_FREE( render_scene_solid );
458     PROFILE_FREE( render_scene_water );
459     PROFILE_FREE( render_scene_trans );
460 
461     PROFILE_FREE( renderlist_make );
462     PROFILE_FREE( dolist_make );
463     PROFILE_FREE( do_grid_lighting );
464     PROFILE_FREE( light_fans );
465     PROFILE_FREE( update_all_chr_instance );
466     PROFILE_FREE( update_all_prt_instance );
467 
468     PROFILE_FREE( render_scene_mesh_dolist_sort );
469     PROFILE_FREE( render_scene_mesh_ndr );
470     PROFILE_FREE( render_scene_mesh_drf_back );
471     PROFILE_FREE( render_scene_mesh_ref );
472     PROFILE_FREE( render_scene_mesh_ref_chr );
473     PROFILE_FREE( render_scene_mesh_drf_solid );
474     PROFILE_FREE( render_scene_mesh_render_shadows );
475 
476     BillboardList_free_all();
477     TxTexture_release_all();
478 }
479 
480 //--------------------------------------------------------------------------------------------
ogl_init()481 int ogl_init()
482 {
483     gfx_init_SDL_graphics();
484 
485     // GL_DEBUG(glClear)) stuff
486     GL_DEBUG( glClearColor )( 0.0f, 0.0f, 0.0f, 0.0f ); // Set the background black
487     GL_DEBUG( glClearDepth )( 1.0f );
488 
489     // depth buffer stuff
490     GL_DEBUG( glClearDepth )( 1.0f );
491     GL_DEBUG( glDepthMask )( GL_TRUE );
492 
493     // do not draw hidden surfaces
494     GL_DEBUG( glEnable )( GL_DEPTH_TEST );
495     GL_DEBUG( glDepthFunc )( GL_LESS );
496 
497     // alpha stuff
498     GL_DEBUG( glDisable )( GL_BLEND );
499 
500     // do not display the completely transparent portion
501     GL_DEBUG( glEnable )( GL_ALPHA_TEST );
502     GL_DEBUG( glAlphaFunc )( GL_GREATER, 0.0f );
503 
504     /// @todo Including backface culling here prevents the mesh from getting rendered
505     // backface culling
506     // GL_DEBUG(glEnable)(GL_CULL_FACE);
507     // GL_DEBUG(glFrontFace)(GL_CW);            // GL_POLYGON_BIT
508     // GL_DEBUG(glCullFace)(GL_BACK);
509 
510     // disable OpenGL lighting
511     GL_DEBUG( glDisable )( GL_LIGHTING );
512 
513     // fill mode
514     GL_DEBUG( glPolygonMode )( GL_FRONT, GL_FILL );
515     GL_DEBUG( glPolygonMode )( GL_BACK,  GL_FILL );
516 
517     // ?Need this for color + lighting?
518     GL_DEBUG( glEnable )( GL_COLOR_MATERIAL );  // Need this for color + lighting
519 
520     // set up environment mapping
521     GL_DEBUG( glTexGeni )( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );  // Set The Texture Generation Mode For S To Sphere Mapping (NEW)
522     GL_DEBUG( glTexGeni )( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );  // Set The Texture Generation Mode For T To Sphere Mapping (NEW)
523 
524     //Initialize the motion blur buffer
525     GL_DEBUG( glClearAccum )( 0.0f, 0.0f, 0.0f, 1.0f );
526     GL_DEBUG( glClear )( GL_ACCUM_BUFFER_BIT );
527 
528     // Load the current graphical settings
529     // load_graphics();
530 
531     _ogl_initialized = btrue;
532 
533     return _ogl_initialized && _sdl_initialized_graphics;
534 }
535 
536 //--------------------------------------------------------------------------------------------
gfx_init_SDL_graphics()537 void gfx_init_SDL_graphics()
538 {
539     if ( _sdl_initialized_graphics ) return;
540 
541     ego_init_SDL_base();
542 
543     log_info( "Intializing SDL Video... " );
544     if ( SDL_InitSubSystem( SDL_INIT_VIDEO ) < 0 )
545     {
546         log_message( "Failed!\n" );
547         log_warning( "SDL error == \"%s\"\n", SDL_GetError() );
548     }
549     else
550     {
551         log_message( "Success!\n" );
552     }
553 
554 #if !defined(__APPLE__)
555     {
556         //Setup the cute windows manager icon, don't do this on Mac
557         SDL_Surface *theSurface;
558         char * fname = "icon.bmp";
559         STRING fileload;
560 
561         snprintf( fileload, SDL_arraysize( fileload ), "mp_data/%s", fname );
562 
563         theSurface = IMG_Load( vfs_resolveReadFilename( fileload ) );
564         if ( NULL == theSurface )
565         {
566             log_warning( "Unable to load icon (%s)\n", fname );
567         }
568         else
569         {
570             SDL_WM_SetIcon( theSurface, NULL );
571         }
572     }
573 #endif
574 
575     // Set the window name
576     SDL_WM_SetCaption( "Egoboo " VERSION, "Egoboo" );
577 
578 #if defined(__unix__)
579 
580     // GLX doesn't differentiate between 24 and 32 bpp, asking for 32 bpp
581     // will cause SDL_SetVideoMode to fail with:
582     // "Unable to set video mode: Couldn't find matching GLX visual"
583     if ( 32 == cfg.scrd_req ) cfg.scrd_req = 24;
584     if ( 32 == cfg.scrz_req ) cfg.scrz_req = 24;
585 
586 #endif
587 
588     // the flags to pass to SDL_SetVideoMode
589     sdl_vparam.width                     = cfg.scrx_req;
590     sdl_vparam.height                    = cfg.scry_req;
591     sdl_vparam.depth                     = cfg.scrd_req;
592 
593     sdl_vparam.flags.opengl              = SDL_TRUE;
594     sdl_vparam.flags.double_buf          = SDL_TRUE;
595     sdl_vparam.flags.full_screen         = cfg.fullscreen_req;
596 
597     sdl_vparam.gl_att.buffer_size        = cfg.scrd_req;
598     sdl_vparam.gl_att.depth_size         = cfg.scrz_req;
599     sdl_vparam.gl_att.multi_buffers      = ( cfg.multisamples > 1 ) ? 1 : 0;
600     sdl_vparam.gl_att.multi_samples      = cfg.multisamples;
601     sdl_vparam.gl_att.accelerated_visual = GL_TRUE;
602 
603     ogl_vparam.dither         = cfg.use_dither ? GL_TRUE : GL_FALSE;
604     ogl_vparam.antialiasing   = GL_TRUE;
605     ogl_vparam.perspective    = cfg.use_perspective ? GL_NICEST : GL_FASTEST;
606     ogl_vparam.shading        = GL_SMOOTH;
607     ogl_vparam.userAnisotropy = 16.0f * MAX( 0, cfg.texturefilter_req - TX_TRILINEAR_2 );
608 
609     log_info( "Opening SDL Video Mode...\n" );
610 
611     // redirect the output of the SDL_GL_* debug functions
612     SDL_GL_set_stdout( log_get_file() );
613 
614     // actually set the video mode
615     if ( NULL == SDL_GL_set_mode( NULL, &sdl_vparam, &ogl_vparam, _sdl_initialized_graphics ) )
616     {
617         log_message( "Failed!\n" );
618         log_error( "I can't get SDL to set any video mode: %s\n", SDL_GetError() );
619     }
620     else
621     {
622         GFX_WIDTH = ( float )GFX_HEIGHT / ( float )sdl_vparam.height * ( float )sdl_vparam.width;
623         log_message( "Success!\n" );
624     }
625 
626     _sdl_initialized_graphics = SDL_TRUE;
627 }
628 
629 //--------------------------------------------------------------------------------------------
gfx_set_virtual_screen(gfx_config_t * pgfx)630 bool_t gfx_set_virtual_screen( gfx_config_t * pgfx )
631 {
632     float kx, ky;
633 
634     if ( NULL == pgfx ) return bfalse;
635 
636     kx = ( float )GFX_WIDTH  / ( float )sdl_scr.x;
637     ky = ( float )GFX_HEIGHT / ( float )sdl_scr.y;
638 
639     if ( kx == ky )
640     {
641         pgfx->vw = sdl_scr.x;
642         pgfx->vh = sdl_scr.y;
643     }
644     else if ( kx > ky )
645     {
646         pgfx->vw = sdl_scr.x * kx / ky;
647         pgfx->vh = sdl_scr.y;
648     }
649     else
650     {
651         pgfx->vw = sdl_scr.x;
652         pgfx->vh = sdl_scr.y * ky / kx;
653     }
654 
655     pgfx->vdw = ( GFX_WIDTH  - pgfx->vw ) * 0.5f;
656     pgfx->vdh = ( GFX_HEIGHT - pgfx->vh ) * 0.5f;
657 
658     ui_set_virtual_screen( pgfx->vw, pgfx->vh, GFX_WIDTH, GFX_HEIGHT );
659 
660     return btrue;
661 }
662 
663 //--------------------------------------------------------------------------------------------
gfx_synch_config(gfx_config_t * pgfx,egoboo_config_t * pcfg)664 bool_t gfx_synch_config( gfx_config_t * pgfx, egoboo_config_t * pcfg )
665 {
666     // call gfx_config_init(), even if the config data is invalid
667     if ( !gfx_config_init( pgfx ) ) return bfalse;
668 
669     // if there is no config data, do not proceed
670     if ( NULL == pcfg ) return bfalse;
671 
672     pgfx->antialiasing = pcfg->multisamples > 0;
673 
674     pgfx->refon        = pcfg->reflect_allowed;
675     pgfx->reffadeor    = pcfg->reflect_fade ? 0 : 255;
676 
677     pgfx->shaon        = pcfg->shadow_allowed;
678     pgfx->shasprite    = pcfg->shadow_sprite;
679 
680     pgfx->shading      = pcfg->gouraud_req ? GL_SMOOTH : GL_FLAT;
681     pgfx->dither       = pcfg->use_dither;
682     pgfx->perspective  = pcfg->use_perspective;
683     pgfx->phongon      = pcfg->use_phong;
684 
685     pgfx->draw_background = pcfg->background_allowed && water.background_req;
686     pgfx->draw_overlay    = pcfg->overlay_allowed && water.overlay_req;
687 
688     pgfx->dynalist_max = CLIP( pcfg->dyna_count_req, 0, TOTAL_MAX_DYNA );
689 
690     pgfx->draw_water_0 = !pgfx->draw_overlay && ( water.layer_count > 0 );
691     pgfx->clearson     = !pgfx->draw_background;
692     pgfx->draw_water_1 = !pgfx->draw_background && ( water.layer_count > 1 );
693 
694     gfx_set_virtual_screen( pgfx );
695 
696     return btrue;
697 }
698 
699 //--------------------------------------------------------------------------------------------
gfx_config_init(gfx_config_t * pgfx)700 bool_t gfx_config_init( gfx_config_t * pgfx )
701 {
702     if ( NULL == pgfx ) return bfalse;
703 
704     pgfx->shading          = GL_SMOOTH;
705     pgfx->refon            = btrue;
706     pgfx->reffadeor        = 0;
707     pgfx->antialiasing     = bfalse;
708     pgfx->dither           = bfalse;
709     pgfx->perspective      = bfalse;
710     pgfx->phongon          = btrue;
711     pgfx->shaon            = btrue;
712     pgfx->shasprite        = btrue;
713 
714     pgfx->clearson         = btrue;   // Do we clear every time?
715     pgfx->draw_background  = bfalse;   // Do we draw the background image?
716     pgfx->draw_overlay     = bfalse;   // Draw overlay?
717     pgfx->draw_water_0     = btrue;   // Do we draw water layer 1 (TX_WATER_LOW)
718     pgfx->draw_water_1     = btrue;   // Do we draw water layer 2 (TX_WATER_TOP)
719 
720     pgfx->dynalist_max    = 8;
721 
722     return btrue;
723 }
724 
725 //--------------------------------------------------------------------------------------------
gfx_synch_oglx_texture_parameters(oglx_texture_parameters_t * ptex,egoboo_config_t * pcfg)726 bool_t gfx_synch_oglx_texture_parameters( oglx_texture_parameters_t * ptex, egoboo_config_t * pcfg )
727 {
728     //// @details BB@> synch the texture parameters with the video mode
729 
730     if ( NULL == ptex || NULL == pcfg ) return bfalse;
731 
732     if ( ogl_caps.maxAnisotropy <= 1.0f )
733     {
734         ptex->userAnisotropy = 0.0f;
735         ptex->texturefilter  = ( TX_FILTERS )MIN( pcfg->texturefilter_req, TX_TRILINEAR_2 );
736     }
737     else
738     {
739         ptex->texturefilter  = ( TX_FILTERS )MIN( pcfg->texturefilter_req, TX_FILTER_COUNT );
740         ptex->userAnisotropy = ogl_caps.maxAnisotropy * MAX( 0, ( int )ptex->texturefilter - ( int )TX_TRILINEAR_2 );
741     }
742 
743     return btrue;
744 }
745 
746 //--------------------------------------------------------------------------------------------
747 // SPECIAL FUNCTIONS
748 //--------------------------------------------------------------------------------------------
gfx_error_add(const char * file,const char * function,int line,int id,const char * sz)749 egoboo_rv gfx_error_add( const char * file, const char * function, int line, int id, const char * sz )
750 {
751     gfx_error_state_t * pstate;
752 
753     // too many errors?
754     if ( gfx_error_stack.count >= GFX_ERROR_MAX ) return rv_fail;
755 
756     // grab an error state
757     pstate = gfx_error_stack.lst + gfx_error_stack.count;
758     gfx_error_stack.count++;
759 
760     // where is the error
761     pstate->file     = file;
762     pstate->function = function;
763     pstate->line     = line;
764 
765     // what is the error
766     pstate->type     = id;
767     pstate->string   = sz;
768 
769     return rv_success;
770 }
771 
772 //--------------------------------------------------------------------------------------------
gfx_error_pop()773 gfx_error_state_t * gfx_error_pop()
774 {
775     gfx_error_state_t * retval;
776 
777     if ( 0 == gfx_error_stack.count || gfx_error_stack.count >= GFX_ERROR_MAX ) return NULL;
778 
779     retval = gfx_error_stack.lst + gfx_error_stack.count;
780     gfx_error_stack.count--;
781 
782     return retval;
783 }
784 
785 //--------------------------------------------------------------------------------------------
gfx_error_clear()786 void gfx_error_clear()
787 {
788     gfx_error_stack.count = 0;
789 }
790 
791 //--------------------------------------------------------------------------------------------
792 // 2D RENDERER FUNCTIONS
793 //--------------------------------------------------------------------------------------------
debug_printf(const char * format,...)794 int debug_printf( const char *format, ... )
795 {
796     va_list args;
797     int retval;
798 
799     va_start( args, format );
800     retval = _debug_vprintf( format, args );
801     va_end( args );
802 
803     return retval;
804 }
805 
806 //--------------------------------------------------------------------------------------------
draw_blip(float sizeFactor,Uint8 color,float x,float y,bool_t mini_map)807 void draw_blip( float sizeFactor, Uint8 color, float x, float y, bool_t mini_map )
808 {
809     /// @details ZZ@> This function draws a single blip
810     ego_frect_t tx_rect, sc_rect;
811 
812     float width, height;
813     float loc_x, loc_y;
814 
815     //Adjust the position values so that they fit inside the minimap
816     if ( mini_map )
817     {
818         loc_x = x * MAPSIZE / PMesh->gmem.edge_x;
819         loc_y = ( y * MAPSIZE / PMesh->gmem.edge_y ) + sdl_scr.y - MAPSIZE;
820     }
821     else
822     {
823         loc_x = x;
824         loc_y = y;
825     }
826 
827     //Now draw it
828     if ( loc_x > 0.0f && loc_y > 0.0f )
829     {
830         oglx_texture_t * ptex = TxTexture_get_ptr(( TX_REF )TX_BLIP );
831 
832         tx_rect.xmin = ( float )bliprect[color].left   / ( float )oglx_texture_GetTextureWidth( ptex );
833         tx_rect.xmax = ( float )bliprect[color].right  / ( float )oglx_texture_GetTextureWidth( ptex );
834         tx_rect.ymin = ( float )bliprect[color].top    / ( float )oglx_texture_GetTextureHeight( ptex );
835         tx_rect.ymax = ( float )bliprect[color].bottom / ( float )oglx_texture_GetTextureHeight( ptex );
836 
837         width  = sizeFactor * ( bliprect[color].right  - bliprect[color].left );
838         height = sizeFactor * ( bliprect[color].bottom - bliprect[color].top );
839 
840         sc_rect.xmin = loc_x - ( width / 2 );
841         sc_rect.xmax = loc_x + ( width / 2 );
842         sc_rect.ymin = loc_y - ( height / 2 );
843         sc_rect.ymax = loc_y + ( height / 2 );
844 
845         draw_quad_2d( ptex, sc_rect, tx_rect, btrue );
846     }
847 }
848 
849 //--------------------------------------------------------------------------------------------
draw_one_icon(const TX_REF icontype,float x,float y,Uint8 sparkle)850 void draw_one_icon( const TX_REF icontype, float x, float y, Uint8 sparkle )
851 {
852     /// @details ZZ@> This function draws an icon
853     int     position, blip_x, blip_y;
854     int     width, height;
855     ego_frect_t tx_rect, sc_rect;
856 
857     tx_rect.xmin = (( float )iconrect.left ) / 32.0f;
858     tx_rect.xmax = (( float )iconrect.right ) / 32.0f;
859     tx_rect.ymin = (( float )iconrect.top ) / 32.0f;
860     tx_rect.ymax = (( float )iconrect.bottom ) / 32.0f;
861 
862     width  = iconrect.right  - iconrect.left;
863     height = iconrect.bottom - iconrect.top;
864 
865     sc_rect.xmin = x;
866     sc_rect.xmax = x + width;
867     sc_rect.ymin = y;
868     sc_rect.ymax = y + height;
869 
870     draw_quad_2d( TxTexture_get_ptr( icontype ), sc_rect, tx_rect, bfalse );
871 
872     if ( sparkle != NOSPARKLE )
873     {
874         position = update_wld & 0x1F;
875         position = ( SPARKLESIZE * position >> 5 );
876 
877         blip_x = x + SPARKLEADD + position;
878         blip_y = y + SPARKLEADD;
879         draw_blip( 0.5f, sparkle, blip_x, blip_y, bfalse );
880 
881         blip_x = x + SPARKLEADD + SPARKLESIZE;
882         blip_y = y + SPARKLEADD + position;
883         draw_blip( 0.5f, sparkle, blip_x, blip_y, bfalse );
884 
885         blip_x = blip_x - position;
886         blip_y = y + SPARKLEADD + SPARKLESIZE;
887         draw_blip( 0.5f, sparkle, blip_x, blip_y, bfalse );
888 
889         blip_x = x + SPARKLEADD;
890         blip_y = blip_y - position;
891         draw_blip( 0.5f, sparkle, blip_x, blip_y, bfalse );
892     }
893 }
894 
895 //--------------------------------------------------------------------------------------------
draw_one_font(oglx_texture_t * ptex,int fonttype,float x_stt,float y_stt)896 void draw_one_font( oglx_texture_t * ptex, int fonttype, float x_stt, float y_stt )
897 {
898     /// @details ZZ@> This function draws a letter or number
899     /// GAC@> Very nasty version for starters.  Lots of room for improvement.
900 
901     GLfloat dx, dy, border;
902 
903     ego_frect_t tx_rect, sc_rect;
904 
905     sc_rect.xmin  = x_stt;
906     sc_rect.xmax  = x_stt + fontrect[fonttype].w;
907     sc_rect.ymin  = y_stt + fontoffset - fontrect[fonttype].h;
908     sc_rect.ymax  = y_stt + fontoffset;
909 
910     dx = 2.0f / 512.0f;
911     dy = 1.0f / 256.0f;
912     border = 1.0f / 512.0f;
913 
914     tx_rect.xmin = fontrect[fonttype].x * dx;
915     tx_rect.xmax = tx_rect.xmin + fontrect[fonttype].w * dx;
916     tx_rect.ymin = fontrect[fonttype].y * dy;
917     tx_rect.ymax = tx_rect.ymin + fontrect[fonttype].h * dy;
918 
919     // shrink the texture size slightly
920     tx_rect.xmin += border;
921     tx_rect.xmax -= border;
922     tx_rect.ymin += border;
923     tx_rect.ymax -= border;
924 
925     draw_quad_2d( ptex, sc_rect, tx_rect, btrue );
926 }
927 
928 //--------------------------------------------------------------------------------------------
draw_map_texture(float x,float y)929 void draw_map_texture( float x, float y )
930 {
931     /// @details ZZ@> This function draws the map
932 
933     ego_frect_t sc_rect, tx_rect;
934 
935     oglx_texture_t * ptex = TxTexture_get_ptr(( TX_REF )TX_MAP );
936     if ( NULL == ptex ) return;
937 
938     sc_rect.xmin = x;
939     sc_rect.xmax = x + MAPSIZE;
940     sc_rect.ymin = y;
941     sc_rect.ymax = y + MAPSIZE;
942 
943     tx_rect.xmin = 0;
944     tx_rect.xmax = ptex->imgW / ptex->base.width;
945     tx_rect.ymin = 0;
946     tx_rect.ymax = ptex->imgH / ptex->base.height;
947 
948     draw_quad_2d( ptex, sc_rect, tx_rect, bfalse );
949 }
950 
951 //--------------------------------------------------------------------------------------------
draw_one_xp_bar(float x,float y,Uint8 ticks)952 float draw_one_xp_bar( float x, float y, Uint8 ticks )
953 {
954     /// @details ZF@> This function draws a xp bar and returns the y position for the next one
955 
956     int width, height;
957     Uint8 cnt;
958     ego_frect_t tx_rect, sc_rect;
959 
960     ticks = MIN( ticks, NUMTICK );
961 
962     gfx_enable_texturing();               // Enable texture mapping
963     GL_DEBUG( glColor4f )( 1, 1, 1, 1 );
964 
965     //---- Draw the tab (always colored)
966 
967     width = 16;
968     height = XPTICK;
969 
970     tx_rect.xmin = 0;
971     tx_rect.xmax = 32.00f / 128;
972     tx_rect.ymin = XPTICK / 16;
973     tx_rect.ymax = XPTICK * 2 / 16;
974 
975     sc_rect.xmin = x;
976     sc_rect.xmax = x + width;
977     sc_rect.ymin = y;
978     sc_rect.ymax = y + height;
979 
980     draw_quad_2d( TxTexture_get_ptr(( TX_REF )TX_XP_BAR ), sc_rect, tx_rect, btrue );
981 
982     x += width;
983 
984     //---- Draw the filled ones
985     tx_rect.xmin = 0.0f;
986     tx_rect.xmax = 32 / 128.0f;
987     tx_rect.ymin = XPTICK / 16.0f;
988     tx_rect.ymax = 2 * XPTICK / 16.0f;
989 
990     width  = XPTICK;
991     height = XPTICK;
992 
993     for ( cnt = 0; cnt < ticks; cnt++ )
994     {
995         sc_rect.xmin = x + ( cnt * width );
996         sc_rect.xmax = x + ( cnt * width ) + width;
997         sc_rect.ymin = y;
998         sc_rect.ymax = y + height;
999 
1000         draw_quad_2d( TxTexture_get_ptr(( TX_REF )TX_XP_BAR ), sc_rect, tx_rect, btrue );
1001     }
1002 
1003     //---- Draw the remaining empty ones
1004     tx_rect.xmin = 0;
1005     tx_rect.xmax = 32 / 128.0f;
1006     tx_rect.ymin = 0;
1007     tx_rect.ymax = XPTICK / 16.0f;
1008 
1009     for ( /*nothing*/; cnt < NUMTICK; cnt++ )
1010     {
1011         sc_rect.xmin = x + ( cnt * width );
1012         sc_rect.xmax = x + ( cnt * width ) + width;
1013         sc_rect.ymin = y;
1014         sc_rect.ymax = y + height;
1015 
1016         draw_quad_2d( TxTexture_get_ptr(( TX_REF )TX_XP_BAR ), sc_rect, tx_rect, btrue );
1017     }
1018 
1019     return y + height;
1020 }
1021 
1022 //--------------------------------------------------------------------------------------------
draw_one_bar(Uint8 bartype,float x_stt,float y_stt,int ticks,int maxticks)1023 float draw_one_bar( Uint8 bartype, float x_stt, float y_stt, int ticks, int maxticks )
1024 {
1025     /// @details ZZ@> This function draws a bar and returns the y position for the next one
1026 
1027     const float scale = 1.0f;
1028 
1029     float       width, height;
1030     ego_frect_t tx_rect, sc_rect;
1031     oglx_texture_t * tx_ptr;
1032 
1033     float tx_width, tx_height, img_width;
1034     float tab_width, tick_width, tick_height;
1035 
1036     int total_ticks = maxticks;
1037     int tmp_bartype = bartype;
1038 
1039     float x_left = x_stt;
1040     float x = x_stt;
1041     float y = y_stt;
1042 
1043     if ( maxticks <= 0 || ticks < 0 || bartype > NUMBAR ) return y;
1044 
1045     // limit the values to reasonable ones
1046     if ( total_ticks > MAXTICK ) total_ticks = MAXTICK;
1047     if ( ticks       > total_ticks ) ticks = total_ticks;
1048 
1049     // grab a pointer to the bar texture
1050     tx_ptr = TxTexture_get_ptr(( TX_REF )TX_BARS );
1051 
1052     // allow the bitmap to be scaled to arbitrary size
1053     tx_width   = 128.0f;
1054     tx_height  = 128.0f;
1055     img_width  = 112.0f;
1056     if ( NULL != tx_ptr )
1057     {
1058         tx_width  = tx_ptr->base.width;
1059         tx_height = tx_ptr->base.height;
1060         img_width = tx_ptr->imgW;
1061     }
1062 
1063     // calculate the bar parameters
1064     tick_width  = img_width / 14.0f;
1065     tick_height = img_width / 7.0f;
1066     tab_width   = img_width / 3.5f;
1067 
1068     //---- Draw the tab
1069     tmp_bartype = bartype;
1070 
1071     tx_rect.xmin  = 0.0f       / tx_width;
1072     tx_rect.xmax  = tab_width  / tx_width;
1073     tx_rect.ymin  = tick_height * ( tmp_bartype + 0 ) / tx_height;
1074     tx_rect.ymax  = tick_height * ( tmp_bartype + 1 ) / tx_height;
1075 
1076     width  = ( tx_rect.xmax - tx_rect.xmin ) * scale * tx_width;
1077     height = ( tx_rect.ymax - tx_rect.ymin ) * scale * tx_height;
1078 
1079     sc_rect.xmin = x;
1080     sc_rect.xmax = x + width;
1081     sc_rect.ymin = y;
1082     sc_rect.ymax = y + height;
1083 
1084     draw_quad_2d( tx_ptr, sc_rect, tx_rect, btrue );
1085 
1086     // make the new left-hand margin after the tab
1087     x_left = x_stt + width;
1088     x      = x_left;
1089 
1090     //---- Draw the full rows of ticks
1091     while ( ticks >= NUMTICK )
1092     {
1093         tmp_bartype = bartype;
1094 
1095         tx_rect.xmin  = tab_width  / tx_width;
1096         tx_rect.xmax  = img_width  / tx_width;
1097         tx_rect.ymin  = tick_height * ( tmp_bartype + 0 ) / tx_height;
1098         tx_rect.ymax  = tick_height * ( tmp_bartype + 1 ) / tx_height;
1099 
1100         width  = ( tx_rect.xmax - tx_rect.xmin ) * scale * tx_width;
1101         height = ( tx_rect.ymax - tx_rect.ymin ) * scale * tx_height;
1102 
1103         sc_rect.xmin = x;
1104         sc_rect.xmax = x + width;
1105         sc_rect.ymin = y;
1106         sc_rect.ymax = y + height;
1107 
1108         draw_quad_2d( tx_ptr, sc_rect, tx_rect, btrue );
1109 
1110         y += height;
1111         ticks -= NUMTICK;
1112         total_ticks -= NUMTICK;
1113     }
1114 
1115     if ( ticks > 0 )
1116     {
1117         int full_ticks = NUMTICK - ticks;
1118         int empty_ticks = NUMTICK - ( MIN( NUMTICK, total_ticks ) - ticks );
1119 
1120         //---- draw a partial row of full ticks
1121         tx_rect.xmin  = tab_width  / tx_width;
1122         tx_rect.xmax  = ( img_width - tick_width * full_ticks )  / tx_width;
1123         tx_rect.ymin  = tick_height * ( tmp_bartype + 0 ) / tx_height;
1124         tx_rect.ymax  = tick_height * ( tmp_bartype + 1 ) / tx_height;
1125 
1126         width  = ( tx_rect.xmax - tx_rect.xmin ) * scale * tx_width;
1127         height = ( tx_rect.ymax - tx_rect.ymin ) * scale * tx_height;
1128 
1129         sc_rect.xmin = x;
1130         sc_rect.xmax = x + width;
1131         sc_rect.ymin = y;
1132         sc_rect.ymax = y + height;
1133 
1134         draw_quad_2d( tx_ptr, sc_rect, tx_rect, btrue );
1135 
1136         // move to the right after drawing the full ticks
1137         x += width;
1138 
1139         //---- draw a partial row of empty ticks
1140         tmp_bartype = 0;
1141 
1142         tx_rect.xmin  = tab_width  / tx_width;
1143         tx_rect.xmax  = ( img_width - tick_width * empty_ticks )  / tx_width;
1144         tx_rect.ymin  = tick_height * ( tmp_bartype + 0 ) / tx_height;
1145         tx_rect.ymax  = tick_height * ( tmp_bartype + 1 ) / tx_height;
1146 
1147         width  = ( tx_rect.xmax - tx_rect.xmin ) * scale * tx_width;
1148         height = ( tx_rect.ymax - tx_rect.ymin ) * scale * tx_height;
1149 
1150         sc_rect.xmin = x;
1151         sc_rect.xmax = x + width;
1152         sc_rect.ymin = y;
1153         sc_rect.ymax = y + height;
1154 
1155         draw_quad_2d( tx_ptr, sc_rect, tx_rect, btrue );
1156 
1157         y += height;
1158         ticks = 0;
1159         total_ticks -= NUMTICK;
1160     }
1161 
1162     // reset the x position
1163     x = x_left;
1164 
1165     // Draw full rows of empty ticks
1166     while ( total_ticks >= NUMTICK )
1167     {
1168         tmp_bartype = 0;
1169 
1170         tx_rect.xmin  = tab_width  / tx_width;
1171         tx_rect.xmax  = img_width  / tx_width;
1172         tx_rect.ymin  = tick_height * ( tmp_bartype + 0 ) / tx_height;
1173         tx_rect.ymax  = tick_height * ( tmp_bartype + 1 ) / tx_height;
1174 
1175         width  = ( tx_rect.xmax - tx_rect.xmin ) * scale * tx_width;
1176         height = ( tx_rect.ymax - tx_rect.ymin ) * scale * tx_height;
1177 
1178         sc_rect.xmin = x;
1179         sc_rect.xmax = x + width;
1180         sc_rect.ymin = y;
1181         sc_rect.ymax = y + height;
1182 
1183         draw_quad_2d( tx_ptr, sc_rect, tx_rect, btrue );
1184 
1185         y += height;
1186         total_ticks -= NUMTICK;
1187     }
1188 
1189     // Draw the last of the empty ones
1190     if ( total_ticks > 0 )
1191     {
1192         int remaining = NUMTICK - total_ticks;
1193 
1194         //---- draw a partial row of empty ticks
1195         tmp_bartype = 0;
1196 
1197         tx_rect.xmin  = tab_width  / tx_width;
1198         tx_rect.xmax  = ( img_width - tick_width * remaining )  / tx_width;
1199         tx_rect.ymin  = tick_height * ( tmp_bartype + 0 ) / tx_height;
1200         tx_rect.ymax  = tick_height * ( tmp_bartype + 1 ) / tx_height;
1201 
1202         width  = ( tx_rect.xmax - tx_rect.xmin ) * scale * tx_width;
1203         height = ( tx_rect.ymax - tx_rect.ymin ) * scale * tx_height;
1204 
1205         sc_rect.xmin = x;
1206         sc_rect.xmax = x + width;
1207         sc_rect.ymin = y;
1208         sc_rect.ymax = y + height;
1209 
1210         draw_quad_2d( tx_ptr, sc_rect, tx_rect, btrue );
1211 
1212         y += height;
1213     }
1214 
1215     return y;
1216 }
1217 
1218 //--------------------------------------------------------------------------------------------
draw_string(float x,float y,const char * format,...)1219 float draw_string( float x, float y, const char *format, ... )
1220 {
1221     /// @details ZZ@> This function spits a line of null terminated text onto the backbuffer
1222     ///
1223     /// details BB@> Uses gfx_begin_2d() ... gfx_end_2d() so that the function can basically be called from anywhere
1224     ///    The way they are currently implemented, this breaks the icon drawing in draw_status() if
1225     ///    you use draw_string() and then draw_icon(). Use _draw_string_raw(), instead.
1226 
1227     va_list args;
1228 
1229     gfx_begin_2d();
1230     {
1231         va_start( args, format );
1232         y = _va_draw_string( x, y, format, args );
1233         va_end( args );
1234     }
1235     gfx_end_2d();
1236 
1237     return y;
1238 }
1239 
1240 //--------------------------------------------------------------------------------------------
draw_wrap_string(const char * szText,float x,float y,int maxx)1241 float draw_wrap_string( const char *szText, float x, float y, int maxx )
1242 {
1243     /// @details ZZ@> This function spits a line of null terminated text onto the backbuffer,
1244     ///    wrapping over the right side and returning the new y value
1245 
1246     int stt_x = x;
1247     Uint8 cTmp = szText[0];
1248     int newy = y + fontyspacing;
1249     Uint8 newword = btrue;
1250     int cnt = 1;
1251 
1252     oglx_texture_t * tx_ptr = TxTexture_get_ptr(( TX_REF )TX_FONT );
1253     if ( NULL == tx_ptr ) return y;
1254 
1255     gfx_begin_text();
1256 
1257     maxx = maxx + stt_x;
1258 
1259     while ( CSTR_END != cTmp )
1260     {
1261         // Check each new word for wrapping
1262         if ( newword )
1263         {
1264             int endx = x + font_bmp_length_of_word( szText + cnt - 1 );
1265 
1266             newword = bfalse;
1267             if ( endx > maxx )
1268             {
1269                 // Wrap the end and cut off spaces and tabs
1270                 x = stt_x + fontyspacing;
1271                 y += fontyspacing;
1272                 newy += fontyspacing;
1273 
1274                 while ( ' ' == cTmp || '~' == cTmp )
1275                 {
1276                     cTmp = szText[cnt];
1277                     cnt++;
1278                 }
1279             }
1280         }
1281         else
1282         {
1283             Uint8 iTmp;
1284 
1285             if ( '~' == cTmp )
1286             {
1287                 // Use squiggle for tab
1288                 x = ( FLOOR(( float )x / ( float )TABADD ) + 1.0f ) * TABADD;
1289             }
1290             else if ( C_NEW_LINE_CHAR == cTmp )
1291             {
1292                 x = stt_x;
1293                 y += fontyspacing;
1294                 newy += fontyspacing;
1295             }
1296             else if ( isspace( cTmp ) )
1297             {
1298                 // other whitespace
1299                 iTmp = asciitofont[cTmp];
1300                 x += fontxspacing[iTmp] / 2;
1301             }
1302             else
1303             {
1304                 // Normal letter
1305                 iTmp = asciitofont[cTmp];
1306                 draw_one_font( tx_ptr, iTmp, x, y );
1307                 x += fontxspacing[iTmp];
1308             }
1309 
1310             cTmp = szText[cnt];
1311             cnt++;
1312 
1313             if ( '~' == cTmp || C_NEW_LINE_CHAR == cTmp || C_CARRIAGE_RETURN_CHAR == cTmp || isspace( cTmp ) )
1314             {
1315                 newword = btrue;
1316             }
1317         }
1318     }
1319 
1320     gfx_end_text();
1321     return newy;
1322 }
1323 
1324 //--------------------------------------------------------------------------------------------
draw_one_character_icon(const CHR_REF item,float x,float y,bool_t draw_ammo)1325 void draw_one_character_icon( const CHR_REF item, float x, float y, bool_t draw_ammo )
1326 {
1327     /// @details BB@> Draw an icon for the given item at the position <x,y>.
1328     ///     If the object is invalid, draw the null icon instead of failing
1329 
1330     TX_REF icon_ref;
1331     Uint8  draw_sparkle;
1332 
1333     chr_t * pitem = !INGAME_CHR( item ) ? NULL : ChrList.lst + item;
1334 
1335     // grab the icon reference
1336     icon_ref = chr_get_icon_ref( item );
1337 
1338     // draw the icon
1339     draw_sparkle = ( NULL == pitem ) ? NOSPARKLE : pitem->sparkle;
1340     draw_one_icon( icon_ref, x, y, draw_sparkle );
1341 
1342     // draw the ammo, if requested
1343     if ( draw_ammo && ( NULL != pitem ) )
1344     {
1345         if ( 0 != pitem->ammomax && pitem->ammoknown )
1346         {
1347             cap_t * pitem_cap = chr_get_pcap( item );
1348 
1349             if (( NULL != pitem_cap && !pitem_cap->isstackable ) || pitem->ammo > 1 )
1350             {
1351                 // Show amount of ammo left
1352                 _draw_string_raw( x, y - 8, "%2d", pitem->ammo );
1353             }
1354         }
1355     }
1356 }
1357 
1358 //--------------------------------------------------------------------------------------------
draw_character_xp_bar(const CHR_REF character,float x,float y)1359 float draw_character_xp_bar( const CHR_REF character, float x, float y )
1360 {
1361     chr_t * pchr;
1362     cap_t * pcap;
1363 
1364     if ( !INGAME_CHR( character ) ) return y;
1365     pchr = ChrList.lst + character;
1366 
1367     pcap = pro_get_pcap( pchr->profile_ref );
1368     if ( NULL == pcap ) return y;
1369 
1370     //Draw the small XP progress bar
1371     if ( pchr->experiencelevel < MAXLEVEL )
1372     {
1373         Uint8  curlevel    = pchr->experiencelevel + 1;
1374         Uint32 xplastlevel = pcap->experience_forlevel[curlevel-1];
1375         Uint32 xpneed      = pcap->experience_forlevel[curlevel];
1376 
1377         float fraction = ( float )MAX( pchr->experience - xplastlevel, 0 ) / ( float )MAX( xpneed - xplastlevel, 1 );
1378         int   numticks = fraction * NUMTICK;
1379 
1380         y = draw_one_xp_bar( x, y, numticks );
1381     }
1382 
1383     return y;
1384 }
1385 
1386 //--------------------------------------------------------------------------------------------
draw_status(const CHR_REF character,float x,float y)1387 float draw_status( const CHR_REF character, float x, float y )
1388 {
1389     /// @details ZZ@> This function shows a character's icon, status and inventory
1390     ///    The x,y coordinates are the top left point of the image to draw
1391 
1392     int cnt;
1393     char cTmp;
1394     char *readtext;
1395     STRING generictext;
1396     int life, lifemax;
1397     int mana, manamax;
1398 
1399     chr_t * pchr;
1400     cap_t * pcap;
1401 
1402     if ( !INGAME_CHR( character ) ) return y;
1403     pchr = ChrList.lst + character;
1404 
1405     pcap = chr_get_pcap( character );
1406     if ( NULL == pcap ) return y;
1407 
1408     life     = FP8_TO_INT( pchr->life );
1409     lifemax  = FP8_TO_INT( pchr->lifemax );
1410     mana     = FP8_TO_INT( pchr->mana );
1411     manamax  = FP8_TO_INT( pchr->manamax );
1412 
1413     // grab the character's display name
1414     readtext = ( char * )chr_get_name( character, CHRNAME_CAPITAL );
1415 
1416     // make a short name for the actual display
1417     for ( cnt = 0; cnt < 7; cnt++ )
1418     {
1419         cTmp = readtext[cnt];
1420 
1421         if ( ' ' == cTmp || CSTR_END == cTmp )
1422         {
1423             generictext[cnt] = CSTR_END;
1424             break;
1425         }
1426         else
1427         {
1428             generictext[cnt] = cTmp;
1429         }
1430     }
1431     generictext[7] = CSTR_END;
1432 
1433     // draw the name
1434     y = _draw_string_raw( x + 8, y, generictext );
1435 
1436     // draw the character's money
1437     y = _draw_string_raw( x + 8, y, "$%4d", pchr->money ) + 8;
1438 
1439     // draw the character's main icon
1440     draw_one_character_icon( character, x + 40, y, bfalse );
1441 
1442     // draw the left hand item icon
1443     draw_one_character_icon( pchr->holdingwhich[SLOT_LEFT], x + 8, y, btrue );
1444 
1445     // draw the right hand item icon
1446     draw_one_character_icon( pchr->holdingwhich[SLOT_RIGHT], x + 72, y, btrue );
1447 
1448     // skip to the next row
1449     y += 32;
1450 
1451     //Draw the small XP progress bar
1452     y = draw_character_xp_bar( character, x + 16, y );
1453 
1454     // Draw the life bar
1455     if ( pchr->alive )
1456     {
1457         y = draw_one_bar( pchr->lifecolor, x, y, life, lifemax );
1458     }
1459     else
1460     {
1461         y = draw_one_bar( 0, x, y, 0, lifemax );  // Draw a black bar
1462     }
1463 
1464     // Draw the mana bar
1465     if ( manamax > 0 )
1466     {
1467         y = draw_one_bar( pchr->manacolor, x, y, mana, manamax );
1468     }
1469 
1470     return y;
1471 }
1472 
1473 //--------------------------------------------------------------------------------------------
draw_all_status(float y)1474 float draw_all_status( float y )
1475 {
1476     int cnt;
1477 
1478     if ( StatusList_on )
1479     {
1480         for ( cnt = 0; cnt < StatusList_count && y < sdl_scr.y; cnt++ )
1481         {
1482             y = draw_status( StatusList[cnt], sdl_scr.x - BARX, y );
1483         }
1484     }
1485 
1486     return y;
1487 }
1488 
1489 //--------------------------------------------------------------------------------------------
draw_map()1490 void draw_map()
1491 {
1492     int cnt;
1493 
1494     // Map display
1495     if ( !mapvalid || !mapon ) return;
1496 
1497     ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT );
1498     {
1499 
1500         GL_DEBUG( glEnable )( GL_BLEND );                               // GL_COLOR_BUFFER_BIT
1501         GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );  // GL_COLOR_BUFFER_BIT
1502 
1503         GL_DEBUG( glColor4f )( 1.0f, 1.0f, 1.0f, 1.0f );
1504         draw_map_texture( 0, sdl_scr.y - MAPSIZE );
1505 
1506         GL_DEBUG( glBlendFunc )( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );  // GL_COLOR_BUFFER_BIT
1507 
1508         // If one of the players can sense enemies via ESP, draw them as blips on the map
1509         if ( TEAM_MAX != local_stats.sense_enemies_team )
1510         {
1511             CHR_REF ichr;
1512 
1513             for ( ichr = 0; ichr < MAX_CHR && blip_count < MAXBLIP; ichr++ )
1514             {
1515                 chr_t * pchr;
1516                 cap_t * pcap;
1517 
1518                 if ( !INGAME_CHR( ichr ) ) continue;
1519                 pchr = ChrList.lst + ichr;
1520 
1521                 pcap = chr_get_pcap( ichr );
1522                 if ( NULL == pcap ) continue;
1523 
1524                 // Show only teams that will attack the player
1525                 if ( team_hates_team( pchr->team, local_stats.sense_enemies_team ) )
1526                 {
1527                     // Only if they match the required IDSZ ([NONE] always works)
1528                     if ( local_stats.sense_enemies_idsz == IDSZ_NONE ||
1529                          local_stats.sense_enemies_idsz == pcap->idsz[IDSZ_PARENT] ||
1530                          local_stats.sense_enemies_idsz == pcap->idsz[IDSZ_TYPE  ] )
1531                     {
1532                         // Inside the map?
1533                         if ( pchr->pos.x < PMesh->gmem.edge_x && pchr->pos.y < PMesh->gmem.edge_y )
1534                         {
1535                             // Valid colors only
1536                             blip_x[blip_count] = pchr->pos.x;
1537                             blip_y[blip_count] = pchr->pos.y;
1538                             blip_c[blip_count] = COLOR_RED; // Red blips
1539                             blip_count++;
1540                         }
1541                     }
1542                 }
1543             }
1544         }
1545 
1546         // draw all the blips
1547         for ( cnt = 0; cnt < blip_count; cnt++ )
1548         {
1549             draw_blip( 0.75f, blip_c[cnt], blip_x[cnt], blip_y[cnt], btrue );
1550         }
1551         blip_count = 0;
1552 
1553         // Show local player position(s)
1554         if ( youarehereon && ( update_wld & 8 ) )
1555         {
1556             PLA_REF iplayer;
1557             for ( iplayer = 0; iplayer < MAX_PLAYER; iplayer++ )
1558             {
1559                 if ( !PlaStack.lst[iplayer].valid ) continue;
1560 
1561                 if ( INPUT_BITS_NONE != PlaStack.lst[iplayer].device.bits )
1562                 {
1563                     CHR_REF ichr = PlaStack.lst[iplayer].index;
1564                     if ( INGAME_CHR( ichr ) && ChrList.lst[ichr].alive )
1565                     {
1566                         draw_blip( 0.75f, COLOR_WHITE, ChrList.lst[ichr].pos.x, ChrList.lst[ichr].pos.y, btrue );
1567                     }
1568                 }
1569             }
1570         }
1571 
1572         //// draw the camera
1573         // if ( update_wld & 2 )
1574         // {
1575         //   draw_blip( 0.75f, COLOR_PURPLE, GET_MAP_X(PMesh, PCamera->pos.x), GET_MAP_Y(PMesh, PCamera->pos.y));
1576         // }
1577     }
1578     ATTRIB_POP( __FUNCTION__ )
1579 }
1580 
1581 //--------------------------------------------------------------------------------------------
draw_fps(float y)1582 float draw_fps( float y )
1583 {
1584     // FPS text
1585 
1586     if ( outofsync )
1587     {
1588         y = _draw_string_raw( 0, y, "OUT OF SYNC" );
1589     }
1590 
1591     if ( parseerror )
1592     {
1593         y = _draw_string_raw( 0, y, "SCRIPT ERROR ( see \"/debug/log.txt\" )" );
1594     }
1595 
1596     if ( fpson )
1597     {
1598         y = _draw_string_raw( 0, y, "%2.3f FPS, %2.3f UPS, Update lag = %d", stabilized_game_fps, stabilized_ups, update_lag );
1599 
1600 #    if defined(DEBUG_BSP)
1601         y = _draw_string_raw( 0, y, "BSP chr %d/%d - BSP prt %d/%d", chr_BSP_root.count, MAX_CHR - chr_count_free(), prt_BSP_root.count, maxparticles - prt_count_free() );
1602         y = _draw_string_raw( 0, y, "BSP infinite %d", chr_BSP_root.tree.infinite.count + prt_BSP_root.tree.infinite.count );
1603         y = _draw_string_raw( 0, y, "BSP collisions %d", CHashList_inserted );
1604         //y = _draw_string_raw( 0, y, "chr-mesh tests %04d - prt-mesh tests %04d", chr_stoppedby_tests + chr_pressure_tests, prt_stoppedby_tests + prt_pressure_tests );
1605 #    endif
1606 
1607 #if defined(DEBUG_RENDERLIST)
1608         y = _draw_string_raw( 0, y, "Renderlist tiles %d/%d", renderlist.all_count, PMesh->info.tiles_count );
1609 #endif
1610 
1611 #if defined(_DEBUG)
1612 
1613 #    if defined(DEBUG_PROFILE_DISPLAY) && defined(_DEBUG)
1614 
1615 #        if defined(DEBUG_PROFILE_RENDER) && defined(_DEBUG)
1616         y = _draw_string_raw( 0, y, "estimated max FPS %2.3f UPS %4.2f GFX %4.2f", est_max_fps, est_max_ups, est_max_gfx );
1617         y = _draw_string_raw( 0, y, "gfx:total %2.4f, render:total %2.4f", est_render_time, time_draw_scene );
1618         y = _draw_string_raw( 0, y, "render:init %2.4f,  render:mesh %2.4f", time_render_scene_init, time_render_scene_mesh );
1619         y = _draw_string_raw( 0, y, "render:solid %2.4f, render:water %2.4f", time_render_scene_solid, time_render_scene_water );
1620         y = _draw_string_raw( 0, y, "render:trans %2.4f", time_render_scene_trans );
1621 #        endif
1622 
1623 #        if defined(DEBUG_PROFILE_MESH) && defined(_DEBUG)
1624         y = _draw_string_raw( 0, y, "mesh:total %2.4f", time_render_scene_mesh );
1625         y = _draw_string_raw( 0, y, "mesh:dolist_sort %2.4f, mesh:ndr %2.4f", time_render_scene_mesh_dolist_sort , time_render_scene_mesh_ndr );
1626         y = _draw_string_raw( 0, y, "mesh:drf_back %2.4f, mesh:ref %2.4f", time_render_scene_mesh_drf_back, time_render_scene_mesh_ref );
1627         y = _draw_string_raw( 0, y, "mesh:ref_chr %2.4f, mesh:drf_solid %2.4f", time_render_scene_mesh_ref_chr, time_render_scene_mesh_drf_solid );
1628         y = _draw_string_raw( 0, y, "mesh:render_shadows %2.4f", time_render_scene_mesh_render_shadows );
1629 #        endif
1630 
1631 #        if defined(DEBUG_PROFILE_INIT) && defined(_DEBUG)
1632         y = _draw_string_raw( 0, y, "init:total %2.4f", time_render_scene_init );
1633         y = _draw_string_raw( 0, y, "init:renderlist_make %2.4f, init:dolist_make %2.4f", time_render_scene_init_renderlist_make, time_render_scene_init_dolist_make );
1634         y = _draw_string_raw( 0, y, "init:do_grid_lighting %2.4f, init:light_fans %2.4f", time_render_scene_init_do_grid_dynalight, time_render_scene_init_light_fans );
1635         y = _draw_string_raw( 0, y, "init:update_all_chr_instance %2.4f", time_render_scene_init_update_all_chr_instance );
1636         y = _draw_string_raw( 0, y, "init:update_all_prt_instance %2.4f", time_render_scene_init_update_all_prt_instance );
1637 #        endif
1638 
1639 #    endif
1640 
1641 #endif
1642     }
1643 
1644     return y;
1645 }
1646 
1647 //--------------------------------------------------------------------------------------------
draw_help(float y)1648 float draw_help( float y )
1649 {
1650     if ( SDLKEYDOWN( SDLK_F1 ) )
1651     {
1652         // In-Game help
1653         y = _draw_string_raw( 0, y, "!!!MOUSE HELP!!!" );
1654         y = _draw_string_raw( 0, y, "~~Go to input settings to change" );
1655         y = _draw_string_raw( 0, y, "Default settings" );
1656         y = _draw_string_raw( 0, y, "~~Left Click to use an item" );
1657         y = _draw_string_raw( 0, y, "~~Left and Right Click to grab" );
1658         y = _draw_string_raw( 0, y, "~~Middle Click to jump" );
1659         y = _draw_string_raw( 0, y, "~~A and S keys do stuff" );
1660         y = _draw_string_raw( 0, y, "~~Right Drag to move camera" );
1661     }
1662     if ( SDLKEYDOWN( SDLK_F2 ) )
1663     {
1664         // In-Game help
1665         y = _draw_string_raw( 0, y, "!!!JOYSTICK HELP!!!" );
1666         y = _draw_string_raw( 0, y, "~~Go to input settings to change." );
1667         y = _draw_string_raw( 0, y, "~~Hit the buttons" );
1668         y = _draw_string_raw( 0, y, "~~You'll figure it out" );
1669     }
1670     if ( SDLKEYDOWN( SDLK_F3 ) )
1671     {
1672         // In-Game help
1673         y = _draw_string_raw( 0, y, "!!!KEYBOARD HELP!!!" );
1674         y = _draw_string_raw( 0, y, "~~Go to input settings to change." );
1675         y = _draw_string_raw( 0, y, "Default settings" );
1676         y = _draw_string_raw( 0, y, "~~TGB control left hand" );
1677         y = _draw_string_raw( 0, y, "~~YHN control right hand" );
1678         y = _draw_string_raw( 0, y, "~~Keypad to move and jump" );
1679         y = _draw_string_raw( 0, y, "~~Number keys for stats" );
1680     }
1681 
1682     return y;
1683 }
1684 
1685 //--------------------------------------------------------------------------------------------
draw_debug(float y)1686 float draw_debug( float y )
1687 {
1688     if ( !cfg.dev_mode ) return y;
1689 
1690     if ( SDLKEYDOWN( SDLK_F5 ) )
1691     {
1692         CHR_REF ichr;
1693         PLA_REF ipla;
1694 
1695         // Debug information
1696         y = _draw_string_raw( 0, y, "!!!DEBUG MODE-5!!!" );
1697         y = _draw_string_raw( 0, y, "~~CAM %f %f %f", PCamera->pos.x, PCamera->pos.y, PCamera->pos.z );
1698 
1699         ipla = ( PLA_REF )0;
1700         ichr = PlaStack.lst[ipla].index;
1701         y = _draw_string_raw( 0, y, "~~PLA0DEF %d %d %d %d %d %d %d %d",
1702                               ChrList.lst[ichr].damage_modifier[DAMAGE_SLASH] & 3,
1703                               ChrList.lst[ichr].damage_modifier[DAMAGE_CRUSH] & 3,
1704                               ChrList.lst[ichr].damage_modifier[DAMAGE_POKE ] & 3,
1705                               ChrList.lst[ichr].damage_modifier[DAMAGE_HOLY ] & 3,
1706                               ChrList.lst[ichr].damage_modifier[DAMAGE_EVIL ] & 3,
1707                               ChrList.lst[ichr].damage_modifier[DAMAGE_FIRE ] & 3,
1708                               ChrList.lst[ichr].damage_modifier[DAMAGE_ICE  ] & 3,
1709                               ChrList.lst[ichr].damage_modifier[DAMAGE_ZAP  ] & 3 );
1710 
1711         ichr = PlaStack.lst[ipla].index;
1712         y = _draw_string_raw( 0, y, "~~PLA0 %5.1f %5.1f", ChrList.lst[ichr].pos.x / GRID_FSIZE, ChrList.lst[ichr].pos.y / GRID_FSIZE );
1713 
1714         ipla = ( PLA_REF )1;
1715         ichr = PlaStack.lst[ipla].index;
1716         y = _draw_string_raw( 0, y, "~~PLA1 %5.1f %5.1f", ChrList.lst[ichr].pos.x / GRID_FSIZE, ChrList.lst[ichr].pos.y / GRID_FSIZE );
1717     }
1718 
1719     if ( SDLKEYDOWN( SDLK_F6 ) )
1720     {
1721         // More debug information
1722         STRING text;
1723 
1724         y = _draw_string_raw( 0, y, "!!!DEBUG MODE-6!!!" );
1725         y = _draw_string_raw( 0, y, "~~FREEPRT %d", prt_count_free() );
1726         y = _draw_string_raw( 0, y, "~~FREECHR %d", chr_count_free() );
1727         y = _draw_string_raw( 0, y, "~~MACHINE %d", local_machine );
1728         if ( PMod->exportvalid ) snprintf( text, SDL_arraysize( text ), "~~EXPORT: TRUE" );
1729         else                    snprintf( text, SDL_arraysize( text ), "~~EXPORT: FALSE" );
1730         y = _draw_string_raw( 0, y, text, PMod->exportvalid );
1731         y = _draw_string_raw( 0, y, "~~PASS %d/%d", ShopStack.count, PassageStack.count );
1732         y = _draw_string_raw( 0, y, "~~NETPLAYERS %d", numplayer );
1733         y = _draw_string_raw( 0, y, "~~DAMAGEPART %d", damagetile.part_gpip );
1734 
1735         // y = _draw_string_raw( 0, y, "~~FOGAFF %d", fog_data.affects_water );
1736     }
1737 
1738     if ( SDLKEYDOWN( SDLK_F7 ) )
1739     {
1740         // White debug mode
1741         y = _draw_string_raw( 0, y, "!!!DEBUG MODE-7!!!" );
1742         y = _draw_string_raw( 0, y, "CAM <%f, %f, %f, %f>", PCamera->mView.CNV( 0, 0 ), PCamera->mView.CNV( 1, 0 ), PCamera->mView.CNV( 2, 0 ), PCamera->mView.CNV( 3, 0 ) );
1743         y = _draw_string_raw( 0, y, "CAM <%f, %f, %f, %f>", PCamera->mView.CNV( 0, 1 ), PCamera->mView.CNV( 1, 1 ), PCamera->mView.CNV( 2, 1 ), PCamera->mView.CNV( 3, 1 ) );
1744         y = _draw_string_raw( 0, y, "CAM <%f, %f, %f, %f>", PCamera->mView.CNV( 0, 2 ), PCamera->mView.CNV( 1, 2 ), PCamera->mView.CNV( 2, 2 ), PCamera->mView.CNV( 3, 2 ) );
1745         y = _draw_string_raw( 0, y, "CAM <%f, %f, %f, %f>", PCamera->mView.CNV( 0, 3 ), PCamera->mView.CNV( 1, 3 ), PCamera->mView.CNV( 2, 3 ), PCamera->mView.CNV( 3, 3 ) );
1746         y = _draw_string_raw( 0, y, "CAM center <%f, %f>", PCamera->center.x, PCamera->center.y );
1747         y = _draw_string_raw( 0, y, "CAM turn %d %d", PCamera->turn_mode, PCamera->turn_time );
1748     }
1749 
1750     return y;
1751 }
1752 
1753 //--------------------------------------------------------------------------------------------
draw_timer(float y)1754 float draw_timer( float y )
1755 {
1756     int fifties, seconds, minutes;
1757 
1758     if ( timeron )
1759     {
1760         fifties = ( timervalue % 50 ) << 1;
1761         seconds = (( timervalue / 50 ) % 60 );
1762         minutes = ( timervalue / 3000 );
1763         y = _draw_string_raw( 0, y, "=%d:%02d:%02d=", minutes, seconds, fifties );
1764     }
1765 
1766     return y;
1767 }
1768 
1769 //--------------------------------------------------------------------------------------------
draw_game_status(float y)1770 float draw_game_status( float y )
1771 {
1772 
1773     if ( PNet->waitingforplayers )
1774     {
1775         y = _draw_string_raw( 0, y, "Waiting for players... " );
1776     }
1777 
1778     if ( numplayer > 0 )
1779     {
1780         if ( local_stats.allpladead || PMod->respawnanytime )
1781         {
1782             if ( PMod->respawnvalid && cfg.difficulty < GAME_HARD )
1783             {
1784                 y = _draw_string_raw( 0, y, "PRESS SPACE TO RESPAWN" );
1785             }
1786             else
1787             {
1788                 y = _draw_string_raw( 0, y, "PRESS ESCAPE TO QUIT" );
1789             }
1790         }
1791         else if ( PMod->beat )
1792         {
1793             y = _draw_string_raw( 0, y, "VICTORY!  PRESS ESCAPE" );
1794         }
1795     }
1796     else y = _draw_string_raw( 0, y, "ERROR: MISSING PLAYERS" );
1797 
1798     return y;
1799 }
1800 
1801 //--------------------------------------------------------------------------------------------
draw_messages(float y)1802 float draw_messages( float y )
1803 {
1804     int cnt, tnc;
1805 
1806     // Messages
1807     if ( messageon )
1808     {
1809         // Display the messages
1810         tnc = DisplayMsg.count;
1811         for ( cnt = 0; cnt < maxmessage; cnt++ )
1812         {
1813             if ( DisplayMsg.ary[tnc].time > 0 )
1814             {
1815                 y = draw_wrap_string( DisplayMsg.ary[tnc].textdisplay, 0, y, sdl_scr.x - wraptolerance );
1816                 if ( DisplayMsg.ary[tnc].time > msgtimechange )
1817                 {
1818                     DisplayMsg.ary[tnc].time -= msgtimechange;
1819                 }
1820                 else
1821                 {
1822                     DisplayMsg.ary[tnc].time = 0;
1823                 }
1824             }
1825 
1826             tnc = ( tnc + 1 ) % maxmessage;
1827         }
1828 
1829         msgtimechange = 0;
1830     }
1831 
1832     return y;
1833 }
1834 
1835 //--------------------------------------------------------------------------------------------
draw_text()1836 void draw_text()
1837 {
1838     /// @details ZZ@> draw in-game heads up display
1839 
1840     int y;
1841 
1842     gfx_begin_2d();
1843     {
1844         draw_map();
1845 
1846         y = draw_all_status( 0 );
1847 
1848         y = draw_fps( 0 );
1849         y = draw_help( y );
1850         y = draw_debug( y );
1851         y = draw_timer( y );
1852         y = draw_game_status( y );
1853 
1854         // Network message input
1855         if ( console_mode )
1856         {
1857             char buffer[KEYB_BUFFER_SIZE + 128] = EMPTY_CSTR;
1858 
1859             snprintf( buffer, SDL_arraysize( buffer ), "%s > %s%s", cfg.network_messagename, keyb.buffer, HAS_NO_BITS( update_wld, 8 ) ? "x" : "+" );
1860 
1861             y = draw_wrap_string( buffer, 0, y, sdl_scr.x - wraptolerance );
1862         }
1863 
1864         y = draw_messages( y );
1865     }
1866     gfx_end_2d();
1867 }
1868 
1869 //--------------------------------------------------------------------------------------------
1870 // 3D RENDERER FUNCTIONS
1871 //--------------------------------------------------------------------------------------------
gfx_project_cam_view(camera_t * pcam)1872 gfx_rv gfx_project_cam_view( camera_t * pcam )
1873 {
1874     /// @details ZZ@> This function figures out where the corners of the view area
1875     ///    go when projected onto the plane of the PMesh->  Used later for
1876     ///    determining which mesh fans need to be rendered
1877 
1878     int cnt, tnc, extra[2];
1879     float ztemp;
1880     float numstep;
1881     float zproject;
1882     float xfin, yfin, zfin;
1883     fmat_4x4_t mTemp;
1884 
1885     if ( NULL == pcam ) pcam = PCamera;
1886     if ( NULL == pcam )
1887     {
1888         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "cannot find a valid camera" );
1889         return gfx_error;
1890     }
1891 
1892     // Range
1893     ztemp = ( pcam->pos.z );
1894 
1895     // Topleft
1896     mTemp = MatrixMult( RotateY( -rotmesh_topside * PI / 360 ), PCamera->mView );
1897     mTemp = MatrixMult( RotateX( rotmesh_up * PI / 360 ), mTemp );
1898     zproject = mTemp.CNV( 2, 2 );             // 2,2
1899     // Camera must look down
1900     if ( zproject >= 0.0f )
1901     {
1902         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "failed on corner 0" );
1903         return gfx_error;
1904     }
1905     else
1906     {
1907         numstep = -ztemp / zproject;
1908         xfin = pcam->pos.x + ( numstep * mTemp.CNV( 0, 2 ) );  // xgg      // 0,2
1909         yfin = pcam->pos.y + ( numstep * mTemp.CNV( 1, 2 ) );    // 1,2
1910         zfin = 0;
1911         cornerx[0] = xfin;
1912         cornery[0] = yfin;
1913     }
1914 
1915     // Topright
1916     mTemp = MatrixMult( RotateY( rotmesh_topside * PI / 360 ), PCamera->mView );
1917     mTemp = MatrixMult( RotateX( rotmesh_up * PI / 360 ), mTemp );
1918     zproject = mTemp.CNV( 2, 2 );             // 2,2
1919     // Camera must look down
1920     if ( zproject >= 0.0f )
1921     {
1922         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "failed on corner 1" );
1923         return gfx_error;
1924     }
1925     else
1926     {
1927         numstep = -ztemp / zproject;
1928         xfin = pcam->pos.x + ( numstep * mTemp.CNV( 0, 2 ) );  // xgg      // 0,2
1929         yfin = pcam->pos.y + ( numstep * mTemp.CNV( 1, 2 ) );    // 1,2
1930         zfin = 0;
1931         cornerx[1] = xfin;
1932         cornery[1] = yfin;
1933     }
1934 
1935     // Bottomright
1936     mTemp = MatrixMult( RotateY( rotmesh_bottomside * PI / 360 ), PCamera->mView );
1937     mTemp = MatrixMult( RotateX( -rotmesh_down * PI / 360 ), mTemp );
1938     zproject = mTemp.CNV( 2, 2 );             // 2,2
1939 
1940     // Camera must look down
1941     if ( zproject >= 0.0f )
1942     {
1943         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "failed on corner 2" );
1944         return gfx_error;
1945     }
1946     else
1947     {
1948         numstep = -ztemp / zproject;
1949         xfin = pcam->pos.x + ( numstep * mTemp.CNV( 0, 2 ) );  // xgg      // 0,2
1950         yfin = pcam->pos.y + ( numstep * mTemp.CNV( 1, 2 ) );    // 1,2
1951         zfin = 0;
1952         cornerx[2] = xfin;
1953         cornery[2] = yfin;
1954     }
1955 
1956     // Bottomleft
1957     mTemp = MatrixMult( RotateY( -rotmesh_bottomside * PI / 360 ), PCamera->mView );
1958     mTemp = MatrixMult( RotateX( -rotmesh_down * PI / 360 ), mTemp );
1959     zproject = mTemp.CNV( 2, 2 );             // 2,2
1960     // Camera must look down
1961     if ( zproject >= 0.0f )
1962     {
1963         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "failed on corner 3" );
1964         return gfx_error;
1965     }
1966     else
1967     {
1968         numstep = -ztemp / zproject;
1969         xfin = pcam->pos.x + ( numstep * mTemp.CNV( 0, 2 ) );  // xgg      // 0,2
1970         yfin = pcam->pos.y + ( numstep * mTemp.CNV( 1, 2 ) );    // 1,2
1971         zfin = 0;
1972         cornerx[3] = xfin;
1973         cornery[3] = yfin;
1974     }
1975 
1976     // Get the extreme values
1977     cornerlowx = cornerx[0];
1978     cornerlowy = cornery[0];
1979     cornerhighx = cornerx[0];
1980     cornerhighy = cornery[0];
1981     cornerlistlowtohighy[0] = 0;
1982     cornerlistlowtohighy[3] = 0;
1983 
1984     // sort the corners
1985     for ( cnt = 0; cnt < 4; cnt++ )
1986     {
1987         if ( cornerx[cnt] < cornerlowx )
1988         {
1989             cornerlowx = cornerx[cnt];
1990         }
1991 
1992         if ( cornerx[cnt] > cornerhighx )
1993         {
1994             cornerhighx = cornerx[cnt];
1995         }
1996 
1997         if ( cornery[cnt] < cornerlowy )
1998         {
1999             cornerlowy = cornery[cnt];
2000             cornerlistlowtohighy[0] = cnt;
2001         }
2002 
2003         if ( cornery[cnt] > cornerhighy )
2004         {
2005             cornerhighy = cornery[cnt];
2006             cornerlistlowtohighy[3] = cnt;
2007         }
2008     }
2009 
2010     // Figure out the order of points
2011     for ( cnt = 0, tnc = 0; cnt < 4; cnt++ )
2012     {
2013         if ( cnt != cornerlistlowtohighy[0] && cnt != cornerlistlowtohighy[3] )
2014         {
2015             extra[tnc] = cnt;
2016             tnc++;
2017         }
2018     }
2019 
2020     cornerlistlowtohighy[1] = extra[1];
2021     cornerlistlowtohighy[2] = extra[0];
2022     if ( cornery[extra[0]] < cornery[extra[1]] )
2023     {
2024         cornerlistlowtohighy[1] = extra[0];
2025         cornerlistlowtohighy[2] = extra[1];
2026     }
2027 
2028     return gfx_success;
2029 }
2030 
2031 //--------------------------------------------------------------------------------------------
render_shadow_sprite(float intensity,GLvertex v[])2032 void render_shadow_sprite( float intensity, GLvertex v[] )
2033 {
2034     int i;
2035 
2036     if ( intensity*255.0f < 1.0f ) return;
2037 
2038     GL_DEBUG( glColor4f )( intensity, intensity, intensity, 1.0f );
2039 
2040     GL_DEBUG( glBegin )( GL_TRIANGLE_FAN );
2041     {
2042         for ( i = 0; i < 4; i++ )
2043         {
2044             GL_DEBUG( glTexCoord2fv )( v[i].tex );
2045             GL_DEBUG( glVertex3fv )( v[i].pos );
2046         }
2047     }
2048     GL_DEBUG_END();
2049 }
2050 
2051 //--------------------------------------------------------------------------------------------
render_shadow(const CHR_REF character)2052 void render_shadow( const CHR_REF character )
2053 {
2054     /// @details ZZ@> This function draws a NIFTY shadow
2055     GLvertex v[4];
2056 
2057     TX_REF  itex;
2058     int     itex_style;
2059     float   x, y;
2060     float   level;
2061     float   height, size_umbra, size_penumbra;
2062     float   alpha, alpha_umbra, alpha_penumbra;
2063     chr_t * pchr;
2064 
2065     if ( IS_ATTACHED_CHR( character ) ) return;
2066     pchr = ChrList.lst + character;
2067 
2068     // if the character is hidden, not drawn at all, so no shadow
2069     if ( pchr->is_hidden || 0 == pchr->shadow_size ) return;
2070 
2071     // no shadow if off the mesh
2072     if ( !mesh_grid_is_valid( PMesh, pchr->onwhichgrid ) ) return;
2073 
2074     // no shadow if invalid tile image
2075     if ( TILE_IS_FANOFF( PMesh->tmem.tile_list[pchr->onwhichgrid] ) ) return;
2076 
2077     // no shadow if completely transparent
2078     alpha = ( 255 == pchr->inst.light ) ? pchr->inst.alpha  * INV_FF : ( pchr->inst.alpha - pchr->inst.light ) * INV_FF;
2079 
2080     /// @test ZF@> The previous test didn't work, but this one does
2081     //if ( alpha * 255 < 1 ) return;
2082     if ( pchr->inst.light <= INVISIBLE || pchr->inst.alpha <= INVISIBLE ) return;
2083 
2084     // much reduced shadow if on a reflective tile
2085     if ( 0 != mesh_test_fx( PMesh, pchr->onwhichgrid, MPDFX_DRAWREF ) )
2086     {
2087         alpha *= 0.1f;
2088     }
2089     if ( alpha < INV_FF ) return;
2090 
2091     // Original points
2092     level = pchr->enviro.floor_level;
2093     level += SHADOWRAISE;
2094     height = pchr->inst.matrix.CNV( 3, 2 ) - level;
2095     if ( height < 0 ) height = 0;
2096 
2097     size_umbra    = 1.5f * ( pchr->bump.size - height / 30.0f );
2098     size_penumbra = 1.5f * ( pchr->bump.size + height / 30.0f );
2099 
2100     alpha *= 0.3f;
2101     alpha_umbra = alpha_penumbra = alpha;
2102     if ( height > 0 )
2103     {
2104         float factor_penumbra = ( 1.5f ) * (( pchr->bump.size ) / size_penumbra );
2105         float factor_umbra    = ( 1.5f ) * (( pchr->bump.size ) / size_umbra );
2106 
2107         factor_umbra    = MAX( 1.0f, factor_umbra );
2108         factor_penumbra = MAX( 1.0f, factor_penumbra );
2109 
2110         alpha_umbra    *= 1.0f / factor_umbra / factor_umbra / 1.5f;
2111         alpha_penumbra *= 1.0f / factor_penumbra / factor_penumbra / 1.5f;
2112 
2113         alpha_umbra    = CLIP( alpha_umbra,    0.0f, 1.0f );
2114         alpha_penumbra = CLIP( alpha_penumbra, 0.0f, 1.0f );
2115     }
2116 
2117     x = pchr->inst.matrix.CNV( 3, 0 );
2118     y = pchr->inst.matrix.CNV( 3, 1 );
2119 
2120     // Choose texture.
2121     itex = TX_PARTICLE_LIGHT;
2122     oglx_texture_Bind( TxTexture_get_ptr( itex ) );
2123 
2124     itex_style = prt_get_texture_style( itex );
2125     if ( itex_style < 0 ) itex_style = 0;
2126 
2127     // GOOD SHADOW
2128     v[0].tex[SS] = CALCULATE_PRT_U0( itex_style, 238 );
2129     v[0].tex[TT] = CALCULATE_PRT_V0( itex_style, 238 );
2130 
2131     v[1].tex[SS] = CALCULATE_PRT_U1( itex_style, 255 );
2132     v[1].tex[TT] = CALCULATE_PRT_V0( itex_style, 238 );
2133 
2134     v[2].tex[SS] = CALCULATE_PRT_U1( itex_style, 255 );
2135     v[2].tex[TT] = CALCULATE_PRT_V1( itex_style, 255 );
2136 
2137     v[3].tex[SS] = CALCULATE_PRT_U0( itex_style, 238 );
2138     v[3].tex[TT] = CALCULATE_PRT_V1( itex_style, 255 );
2139 
2140     if ( size_penumbra > 0 )
2141     {
2142         v[0].pos[XX] = x + size_penumbra;
2143         v[0].pos[YY] = y - size_penumbra;
2144         v[0].pos[ZZ] = level;
2145 
2146         v[1].pos[XX] = x + size_penumbra;
2147         v[1].pos[YY] = y + size_penumbra;
2148         v[1].pos[ZZ] = level;
2149 
2150         v[2].pos[XX] = x - size_penumbra;
2151         v[2].pos[YY] = y + size_penumbra;
2152         v[2].pos[ZZ] = level;
2153 
2154         v[3].pos[XX] = x - size_penumbra;
2155         v[3].pos[YY] = y - size_penumbra;
2156         v[3].pos[ZZ] = level;
2157 
2158         render_shadow_sprite( alpha_penumbra, v );
2159     };
2160 
2161     if ( size_umbra > 0 )
2162     {
2163         v[0].pos[XX] = x + size_umbra;
2164         v[0].pos[YY] = y - size_umbra;
2165         v[0].pos[ZZ] = level + 0.1f;
2166 
2167         v[1].pos[XX] = x + size_umbra;
2168         v[1].pos[YY] = y + size_umbra;
2169         v[1].pos[ZZ] = level + 0.1f;
2170 
2171         v[2].pos[XX] = x - size_umbra;
2172         v[2].pos[YY] = y + size_umbra;
2173         v[2].pos[ZZ] = level + 0.1f;
2174 
2175         v[3].pos[XX] = x - size_umbra;
2176         v[3].pos[YY] = y - size_umbra;
2177         v[3].pos[ZZ] = level + 0.1f;
2178 
2179         render_shadow_sprite( alpha_umbra, v );
2180     };
2181 }
2182 
2183 //--------------------------------------------------------------------------------------------
render_bad_shadow(const CHR_REF character)2184 void render_bad_shadow( const CHR_REF character )
2185 {
2186     /// @details ZZ@> This function draws a sprite shadow
2187 
2188     GLvertex v[4];
2189 
2190     TX_REF  itex;
2191     int     itex_style;
2192     float   size, x, y;
2193     float   level, height, height_factor, alpha;
2194     chr_t * pchr;
2195 
2196     if ( IS_ATTACHED_CHR( character ) ) return;
2197     pchr = ChrList.lst + character;
2198 
2199     // if the character is hidden, not drawn at all, so no shadow
2200     if ( pchr->is_hidden || 0 == pchr->shadow_size ) return;
2201 
2202     // no shadow if off the mesh
2203     if ( !mesh_grid_is_valid( PMesh, pchr->onwhichgrid ) ) return;
2204 
2205     // no shadow if invalid tile image
2206     if ( TILE_IS_FANOFF( PMesh->tmem.tile_list[pchr->onwhichgrid] ) ) return;
2207 
2208     // no shadow if completely transparent or completely glowing
2209     alpha = ( 255 == pchr->inst.light ) ? pchr->inst.alpha  * INV_FF : ( pchr->inst.alpha - pchr->inst.light ) * INV_FF;
2210 
2211     /// @test ZF@> previous test didn't work, but this one does
2212     //if ( alpha * 255 < 1 ) return;
2213     if ( pchr->inst.light <= INVISIBLE || pchr->inst.alpha <= INVISIBLE ) return;
2214 
2215     // much reduced shadow if on a reflective tile
2216     if ( 0 != mesh_test_fx( PMesh, pchr->onwhichgrid, MPDFX_DRAWREF ) )
2217     {
2218         alpha *= 0.1f;
2219     }
2220     if ( alpha < INV_FF ) return;
2221 
2222     // Original points
2223     level = pchr->enviro.floor_level;
2224     level += SHADOWRAISE;
2225     height = pchr->inst.matrix.CNV( 3, 2 ) - level;
2226     height_factor = 1.0f - height / ( pchr->shadow_size * 5.0f );
2227     if ( height_factor <= 0.0f ) return;
2228 
2229     // how much transparency from height
2230     alpha *= height_factor * 0.5f + 0.25f;
2231     if ( alpha < INV_FF ) return;
2232 
2233     x = pchr->inst.matrix.CNV( 3, 0 );
2234     y = pchr->inst.matrix.CNV( 3, 1 );
2235 
2236     size = pchr->shadow_size * height_factor;
2237 
2238     v[0].pos[XX] = ( float ) x + size;
2239     v[0].pos[YY] = ( float ) y - size;
2240     v[0].pos[ZZ] = ( float ) level;
2241 
2242     v[1].pos[XX] = ( float ) x + size;
2243     v[1].pos[YY] = ( float ) y + size;
2244     v[1].pos[ZZ] = ( float ) level;
2245 
2246     v[2].pos[XX] = ( float ) x - size;
2247     v[2].pos[YY] = ( float ) y + size;
2248     v[2].pos[ZZ] = ( float ) level;
2249 
2250     v[3].pos[XX] = ( float ) x - size;
2251     v[3].pos[YY] = ( float ) y - size;
2252     v[3].pos[ZZ] = ( float ) level;
2253 
2254     // Choose texture and matrix
2255     itex = TX_PARTICLE_LIGHT;
2256     oglx_texture_Bind( TxTexture_get_ptr( itex ) );
2257 
2258     itex_style = prt_get_texture_style( itex );
2259     if ( itex_style < 0 ) itex_style = 0;
2260 
2261     v[0].tex[SS] = CALCULATE_PRT_U0( itex_style, 236 );
2262     v[0].tex[TT] = CALCULATE_PRT_V0( itex_style, 236 );
2263 
2264     v[1].tex[SS] = CALCULATE_PRT_U1( itex_style, 253 );
2265     v[1].tex[TT] = CALCULATE_PRT_V0( itex_style, 236 );
2266 
2267     v[2].tex[SS] = CALCULATE_PRT_U1( itex_style, 253 );
2268     v[2].tex[TT] = CALCULATE_PRT_V1( itex_style, 253 );
2269 
2270     v[3].tex[SS] = CALCULATE_PRT_U0( itex_style, 236 );
2271     v[3].tex[TT] = CALCULATE_PRT_V1( itex_style, 253 );
2272 
2273     render_shadow_sprite( alpha, v );
2274 }
2275 
2276 //--------------------------------------------------------------------------------------------
update_one_chr_instance(chr_t * pchr)2277 gfx_rv update_one_chr_instance( chr_t * pchr )
2278 {
2279     chr_instance_t * pinst;
2280     gfx_rv retval;
2281 
2282     if ( !ACTIVE_PCHR( pchr ) )
2283     {
2284         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, GET_INDEX_PCHR( pchr ), "invalid character" );
2285         return gfx_error;
2286     }
2287     pinst = &( pchr->inst );
2288 
2289     // make sure that the vertices are interpolated
2290     retval = chr_instance_update_vertices( pinst, -1, -1, btrue );
2291     if ( gfx_error == retval )
2292     {
2293         return gfx_error;
2294     }
2295 
2296     // do the basic lighting
2297     chr_instance_update_lighting_base( pinst, pchr, bfalse );
2298 
2299     return retval;
2300 }
2301 
2302 //--------------------------------------------------------------------------------------------
update_all_chr_instance()2303 gfx_rv update_all_chr_instance()
2304 {
2305     CHR_REF cnt;
2306     gfx_rv retval;
2307     chr_t * pchr;
2308     gfx_rv tmp_rv;
2309 
2310     // assume the best
2311     retval = gfx_success;
2312 
2313     for ( cnt = 0; cnt < MAX_CHR; cnt++ )
2314     {
2315         if ( !INGAME_CHR( cnt ) ) continue;
2316         pchr = ChrList.lst + cnt;
2317 
2318         if ( !mesh_grid_is_valid( PMesh, pchr->onwhichgrid ) ) continue;
2319 
2320         tmp_rv = update_one_chr_instance( pchr );
2321 
2322         // deal with return values
2323         if ( gfx_error == tmp_rv )
2324         {
2325             retval = gfx_error;
2326         }
2327         else if ( gfx_success == tmp_rv )
2328         {
2329             // the instance has changed, refresh the collision bound
2330             chr_update_collision_size( pchr, btrue );
2331         }
2332     }
2333 
2334     return retval;
2335 }
2336 
2337 //--------------------------------------------------------------------------------------------
render_fans_by_list(ego_mpd_t * pmesh,Uint32 list[],size_t list_size)2338 gfx_rv render_fans_by_list( ego_mpd_t * pmesh, Uint32 list[], size_t list_size )
2339 {
2340     Uint32 cnt;
2341     TX_REF tx;
2342 
2343     if ( NULL == pmesh ) pmesh = PMesh;
2344     if ( NULL == pmesh )
2345     {
2346         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "cannot find a valid mesh" );
2347         return gfx_error;
2348     }
2349 
2350     if ( NULL == list )
2351     {
2352         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL tile list" );
2353         return gfx_error;
2354     }
2355 
2356     if ( 0 == list_size ) return gfx_success;
2357 
2358     if ( meshnotexture )
2359     {
2360         meshlasttexture = ( Uint16 )( ~0 );
2361         oglx_texture_Bind( NULL );
2362 
2363         for ( cnt = 0; cnt < list_size; cnt++ )
2364         {
2365             render_fan( pmesh, list[cnt] );
2366         }
2367     }
2368     else
2369     {
2370         for ( tx = TX_TILE_0; tx <= TX_TILE_3; tx++ )
2371         {
2372             meshlasttexture = tx;
2373             oglx_texture_Bind( TxTexture_get_ptr( tx ) );
2374 
2375             for ( cnt = 0; cnt < list_size; cnt++ )
2376             {
2377                 render_fan( pmesh, list[cnt] );
2378             }
2379         }
2380     }
2381 
2382     return gfx_success;
2383 }
2384 
2385 //--------------------------------------------------------------------------------------------
render_scene_init(renderlist_t * prlist,dolist_t * pdolist,dynalist_t * pdylist,ego_mpd_t * pmesh,camera_t * pcam)2386 gfx_rv render_scene_init( renderlist_t * prlist, dolist_t * pdolist, dynalist_t * pdylist, ego_mpd_t * pmesh, camera_t * pcam )
2387 {
2388     gfx_rv retval;
2389 
2390     // assume the best;
2391     retval = gfx_success;
2392 
2393     PROFILE_BEGIN( renderlist_make );
2394     {
2395         // Which tiles can be displayed
2396         if ( gfx_error == renderlist_make( prlist, pmesh, pcam ) )
2397         {
2398             retval = gfx_error;
2399         }
2400     }
2401     PROFILE_END( renderlist_make );
2402 
2403     PROFILE_BEGIN( dolist_make );
2404     {
2405         // determine which objects are visible
2406         if ( gfx_error == dolist_make( pdolist, prlist->pmesh ) )
2407         {
2408             retval = gfx_error;
2409         }
2410     }
2411     PROFILE_END( dolist_make );
2412 
2413     // put off sorting the dolist until later
2414     // because it has to be sorted differently for reflected and non-reflected objects
2415     // dolist_sort( pcam, bfalse );
2416 
2417     PROFILE_BEGIN( do_grid_lighting );
2418     {
2419         // figure out the terrain lighting
2420         if ( gfx_error == do_grid_lighting( prlist, pdylist, pcam ) )
2421         {
2422             retval = gfx_error;
2423         }
2424     }
2425     PROFILE_END( do_grid_lighting );
2426 
2427     PROFILE_BEGIN( light_fans );
2428     {
2429         // apply the lighting to the characters and particles
2430         if ( gfx_error == light_fans( prlist ) )
2431         {
2432             retval = gfx_error;
2433         }
2434     }
2435     PROFILE_END( light_fans );
2436 
2437     PROFILE_BEGIN( update_all_chr_instance );
2438     {
2439         // make sure the characters are ready to draw
2440         if ( gfx_error == update_all_chr_instance() )
2441         {
2442             retval = gfx_error;
2443         }
2444     }
2445     PROFILE_END( update_all_chr_instance );
2446 
2447     PROFILE_BEGIN( update_all_prt_instance );
2448     {
2449         // make sure the particles are ready to draw
2450         if ( gfx_error == update_all_prt_instance( pcam ) )
2451         {
2452             retval = gfx_error;
2453         }
2454     }
2455     PROFILE_END( update_all_prt_instance );
2456 
2457     // do the flashing for kursed objects
2458     if ( gfx_error == do_chr_flashing( pdolist ) )
2459     {
2460         retval = gfx_error;
2461     }
2462 
2463     time_render_scene_init_renderlist_make         = PROFILE_QUERY( renderlist_make ) * TARGET_FPS;
2464     time_render_scene_init_dolist_make             = PROFILE_QUERY( dolist_make ) * TARGET_FPS;
2465     time_render_scene_init_do_grid_dynalight       = PROFILE_QUERY( do_grid_lighting ) * TARGET_FPS;
2466     time_render_scene_init_light_fans              = PROFILE_QUERY( light_fans ) * TARGET_FPS;
2467     time_render_scene_init_update_all_chr_instance = PROFILE_QUERY( update_all_chr_instance ) * TARGET_FPS;
2468     time_render_scene_init_update_all_prt_instance = PROFILE_QUERY( update_all_prt_instance ) * TARGET_FPS;
2469 
2470     return retval;
2471 }
2472 
2473 //--------------------------------------------------------------------------------------------
render_scene_mesh_ndr(renderlist_t * prlist)2474 gfx_rv render_scene_mesh_ndr( renderlist_t * prlist )
2475 {
2476     /// @details BB@> draw all tiles that do not reflect characters
2477 
2478     gfx_rv retval;
2479 
2480     if ( NULL == prlist )
2481     {
2482         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL renderlist" );
2483         return gfx_error;
2484     }
2485 
2486     // assume the best
2487     retval = gfx_success;
2488 
2489     ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
2490     {
2491         // store the surface depth
2492         GL_DEBUG( glDepthMask )( GL_TRUE );         // GL_DEPTH_BUFFER_BIT
2493 
2494         // do not draw hidden surfaces
2495         GL_DEBUG( glEnable )( GL_DEPTH_TEST );      // GL_ENABLE_BIT
2496         GL_DEBUG( glDepthFunc )( GL_LEQUAL );       // GL_DEPTH_BUFFER_BIT
2497 
2498         // no transparency
2499         GL_DEBUG( glDisable )( GL_BLEND );          // GL_ENABLE_BIT
2500 
2501         // draw draw front and back faces of polygons
2502         GL_DEBUG( glDisable )( GL_CULL_FACE );      // GL_ENABLE_BIT
2503 
2504         // do not display the completely transparent portion
2505         // use alpha test to allow the thatched roof tiles to look like thatch
2506         GL_DEBUG( glEnable )( GL_ALPHA_TEST );      // GL_ENABLE_BIT
2507         // speed-up drawing of surfaces with alpha == 0.0f sections
2508         GL_DEBUG( glAlphaFunc )( GL_GREATER, 0.0f );   // GL_COLOR_BUFFER_BIT
2509 
2510         // reduce texture hashing by loading up each texture only once
2511         if ( gfx_error == render_fans_by_list( prlist->pmesh, prlist->ndr, prlist->ndr_count ) )
2512         {
2513             retval = gfx_error;
2514         }
2515     }
2516     ATTRIB_POP( __FUNCTION__ );
2517 
2518     return retval;
2519 }
2520 
2521 //--------------------------------------------------------------------------------------------
render_scene_mesh_drf_back(renderlist_t * prlist)2522 gfx_rv render_scene_mesh_drf_back( renderlist_t * prlist )
2523 {
2524     /// @details BB@> draw the reflective tiles, but turn off the depth buffer
2525     ///               this blanks out any background that might've been drawn
2526 
2527     gfx_rv retval;
2528 
2529     if ( NULL == prlist )
2530     {
2531         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL renderlist" );
2532         return gfx_error;
2533     }
2534 
2535     // assume the best
2536     retval = gfx_success;
2537 
2538     ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
2539     {
2540         // DO NOT store the surface depth
2541         GL_DEBUG( glDepthMask )( GL_FALSE );        // GL_DEPTH_BUFFER_BIT
2542 
2543         // do not draw hidden surfaces
2544         GL_DEBUG( glEnable )( GL_DEPTH_TEST );      // GL_ENABLE_BIT
2545         GL_DEBUG( glDepthFunc )( GL_LEQUAL );       // GL_DEPTH_BUFFER_BIT
2546 
2547         // black out any backgound, but allow the background to show through any holes in the floor
2548         GL_DEBUG( glEnable )( GL_BLEND );                              // GL_ENABLE_BIT
2549         // use the alpha channel to modulate the transparency
2550         GL_DEBUG( glBlendFunc )( GL_ZERO, GL_ONE_MINUS_SRC_ALPHA );    // GL_COLOR_BUFFER_BIT
2551 
2552         // do not display the completely transparent portion
2553         // use alpha test to allow the thatched roof tiles to look like thatch
2554         GL_DEBUG( glEnable )( GL_ALPHA_TEST );      // GL_ENABLE_BIT
2555         // speed-up drawing of surfaces with alpha == 0.0f sections
2556         GL_DEBUG( glAlphaFunc )( GL_GREATER, 0.0f );   // GL_COLOR_BUFFER_BIT
2557 
2558         // reduce texture hashing by loading up each texture only once
2559         if ( gfx_error == render_fans_by_list( prlist->pmesh, prlist->drf, prlist->drf_count ) )
2560         {
2561             retval = gfx_error;
2562         }
2563     }
2564     ATTRIB_POP( __FUNCTION__ );
2565 
2566     return retval;
2567 }
2568 
2569 //--------------------------------------------------------------------------------------------
render_scene_mesh_ref(renderlist_t * prlist,dolist_t * pdolist)2570 gfx_rv render_scene_mesh_ref( renderlist_t * prlist, dolist_t * pdolist )
2571 {
2572     /// @details BB@> Render all reflected objects
2573 
2574     int cnt;
2575     gfx_rv retval;
2576 
2577     ego_mpd_t * pmesh;
2578 
2579     if ( NULL == prlist )
2580     {
2581         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL renderlist" );
2582         return gfx_error;
2583     }
2584 
2585     if ( NULL == pdolist )
2586     {
2587         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL dolist" );
2588         return gfx_error;
2589     }
2590 
2591     if ( pdolist->count >= DOLIST_SIZE )
2592     {
2593         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "invalid dolist size" );
2594         return gfx_error;
2595     }
2596 
2597     if ( NULL == prlist->pmesh )
2598     {
2599         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL renderlist mesh" );
2600         return gfx_error;
2601     }
2602     pmesh = prlist->pmesh;
2603 
2604     // assume the best
2605     retval = gfx_success;
2606 
2607     ATTRIB_PUSH( __FUNCTION__,  GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_POLYGON_BIT | GL_CURRENT_BIT );
2608     {
2609         // don't write into the depth buffer (disable glDepthMask for transparent objects)
2610         // turn off the depth mask by default. Can cause glitches if used improperly.
2611         GL_DEBUG( glDepthMask )( GL_FALSE );      // GL_DEPTH_BUFFER_BIT
2612 
2613         // do not draw hidden surfaces
2614         GL_DEBUG( glEnable )( GL_DEPTH_TEST );    // GL_ENABLE_BIT
2615         // surfaces must be closer to the camera to be drawn
2616         GL_DEBUG( glDepthFunc )( GL_LEQUAL );     // GL_DEPTH_BUFFER_BIT
2617 
2618         for ( cnt = (( int )pdolist->count ) - 1; cnt >= 0; cnt-- )
2619         {
2620             if ( MAX_PRT == pdolist->lst[cnt].iprt && MAX_CHR != pdolist->lst[cnt].ichr )
2621             {
2622                 CHR_REF ichr;
2623                 Uint32 itile;
2624 
2625                 // cull backward facing polygons
2626                 GL_DEBUG( glEnable )( GL_CULL_FACE );   // GL_ENABLE_BIT
2627                 // use couter-clockwise orientation to determine backfaces
2628                 GL_DEBUG( glFrontFace )( GL_CCW );      // GL_POLYGON_BIT
2629 
2630                 // allow transparent objects
2631                 GL_DEBUG( glEnable )( GL_BLEND );                 // GL_ENABLE_BIT
2632                 // use the alpha channel to modulate the transparency
2633                 GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );  // GL_COLOR_BUFFER_BIT
2634 
2635                 ichr  = pdolist->lst[cnt].ichr;
2636                 itile = ChrList.lst[ichr].onwhichgrid;
2637 
2638                 if ( mesh_grid_is_valid( pmesh, itile ) && ( 0 != mesh_test_fx( pmesh, itile, MPDFX_DRAWREF ) ) )
2639                 {
2640                     GL_DEBUG( glColor4f )( 1, 1, 1, 1 );          // GL_CURRENT_BIT
2641 
2642                     if ( gfx_error == render_one_mad_ref( ichr ) )
2643                     {
2644                         retval = gfx_error;
2645                     }
2646                 }
2647             }
2648             else if ( MAX_CHR == pdolist->lst[cnt].ichr && MAX_PRT != pdolist->lst[cnt].iprt )
2649             {
2650                 Uint32 itile;
2651                 PRT_REF iprt;
2652 
2653                 // draw draw front and back faces of polygons
2654                 GL_DEBUG( glDisable )( GL_CULL_FACE );
2655 
2656                 // render_one_prt_ref() actually sets its own blend function, but just to be safe
2657                 // allow transparent objects
2658                 GL_DEBUG( glEnable )( GL_BLEND );                    // GL_ENABLE_BIT
2659                 // set the default particle blending
2660                 GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );     // GL_COLOR_BUFFER_BIT
2661 
2662                 iprt = pdolist->lst[cnt].iprt;
2663                 itile = PrtList.lst[iprt].onwhichgrid;
2664 
2665                 if ( mesh_grid_is_valid( pmesh, itile ) && ( 0 != mesh_test_fx( pmesh, itile, MPDFX_DRAWREF ) ) )
2666                 {
2667                     GL_DEBUG( glColor4f )( 1, 1, 1, 1 );          // GL_CURRENT_BIT
2668 
2669                     if ( gfx_error == render_one_prt_ref( iprt ) )
2670                     {
2671                         retval = gfx_error;
2672                     }
2673                 }
2674             }
2675         }
2676     }
2677     ATTRIB_POP( __FUNCTION__ );
2678 
2679     return retval;
2680 }
2681 
2682 //--------------------------------------------------------------------------------------------
render_scene_mesh_ref_chr(renderlist_t * prlist)2683 gfx_rv render_scene_mesh_ref_chr( renderlist_t * prlist )
2684 {
2685     /// @brief   BB@> Render the shadow floors ( let everything show through )
2686     /// @details BB@> turn on the depth mask, so that no objects under the floor will show through
2687     ///               this assumes that the floor is not partially transparent...
2688 
2689     gfx_rv retval;
2690 
2691     if ( NULL == prlist )
2692     {
2693         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL renderlist" );
2694         return gfx_error;
2695     }
2696 
2697     // assume the best
2698     retval = gfx_success;
2699 
2700     ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
2701     {
2702         // set the depth of these tiles
2703         GL_DEBUG( glDepthMask )( GL_TRUE );                   // GL_DEPTH_BUFFER_BIT
2704 
2705         GL_DEBUG( glEnable )( GL_BLEND );                     // GL_ENABLE_BIT
2706         GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE );      // GL_COLOR_BUFFER_BIT
2707 
2708         // draw draw front and back faces of polygons
2709         GL_DEBUG( glDisable )( GL_CULL_FACE );                // GL_ENABLE_BIT
2710 
2711         // do not draw hidden surfaces
2712         GL_DEBUG( glEnable )( GL_DEPTH_TEST );                // GL_ENABLE_BIT
2713         GL_DEBUG( glDepthFunc )( GL_LEQUAL );                 // GL_DEPTH_BUFFER_BIT
2714 
2715         // reduce texture hashing by loading up each texture only once
2716         if ( gfx_error == render_fans_by_list( prlist->pmesh, prlist->drf, prlist->drf_count ) )
2717         {
2718             retval = gfx_error;
2719         }
2720     }
2721     ATTRIB_POP( __FUNCTION__ );
2722 
2723     return retval;
2724 }
2725 
2726 //--------------------------------------------------------------------------------------------
render_scene_mesh_drf_solid(renderlist_t * prlist)2727 gfx_rv render_scene_mesh_drf_solid( renderlist_t * prlist )
2728 {
2729     /// @brief BB@> Render the shadow floors as normal solid floors
2730 
2731     gfx_rv retval;
2732 
2733     if ( NULL == prlist )
2734     {
2735         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL renderlist" );
2736         return gfx_error;
2737     }
2738 
2739     // assume the best
2740     retval = gfx_success;
2741 
2742     ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
2743     {
2744         // no transparency
2745         GL_DEBUG( glDisable )( GL_BLEND );                    // GL_ENABLE_BIT
2746 
2747         // draw draw front and back faces of polygons
2748         GL_DEBUG( glDisable )( GL_CULL_FACE );                // GL_ENABLE_BIT
2749 
2750         // do not draw hidden surfaces
2751         GL_DEBUG( glEnable )( GL_DEPTH_TEST );                // GL_ENABLE_BIT
2752 
2753         // store the surface depth
2754         GL_DEBUG( glDepthMask )( GL_TRUE );                   // GL_DEPTH_BUFFER_BIT
2755 
2756         // do not display the completely transparent portion
2757         // use alpha test to allow the thatched roof tiles to look like thatch
2758         GL_DEBUG( glEnable )( GL_ALPHA_TEST );                // GL_ENABLE_BIT
2759         // speed-up drawing of surfaces with alpha = 0.0f sections
2760         GL_DEBUG( glAlphaFunc )( GL_GREATER, 0.0f );          // GL_COLOR_BUFFER_BIT
2761 
2762         // reduce texture hashing by loading up each texture only once
2763         if ( gfx_error == render_fans_by_list( prlist->pmesh, prlist->drf, prlist->drf_count ) )
2764         {
2765             retval = gfx_error;
2766         }
2767     }
2768     ATTRIB_POP( __FUNCTION__ );
2769 
2770     return retval;
2771 }
2772 
2773 //--------------------------------------------------------------------------------------------
render_scene_mesh_render_shadows(dolist_t * pdolist)2774 gfx_rv render_scene_mesh_render_shadows( dolist_t * pdolist )
2775 {
2776     /// @details BB@> Render the shadows
2777 
2778     int cnt, tnc;
2779 
2780     if ( NULL == pdolist )
2781     {
2782         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL dolist" );
2783         return gfx_error;
2784     }
2785 
2786     if ( pdolist->count >= DOLIST_SIZE )
2787     {
2788         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "invalid dolist size" );
2789         return gfx_error;
2790     }
2791 
2792     if ( !gfx.shaon ) return gfx_success;
2793 
2794     // don't write into the depth buffer (disable glDepthMask for transparent objects)
2795     GL_DEBUG( glDepthMask )( GL_FALSE );
2796 
2797     // do not draw hidden surfaces
2798     GL_DEBUG( glEnable )( GL_DEPTH_TEST );
2799 
2800     GL_DEBUG( glEnable )( GL_BLEND );
2801     GL_DEBUG( glBlendFunc )( GL_ZERO, GL_ONE_MINUS_SRC_COLOR );
2802 
2803     // keep track of the number of shadows actually rendered
2804     tnc = 0;
2805 
2806     if ( gfx.shasprite )
2807     {
2808         // Bad shadows
2809         for ( cnt = 0; cnt < pdolist->count; cnt++ )
2810         {
2811             CHR_REF ichr = pdolist->lst[cnt].ichr;
2812             if ( !VALID_CHR_RANGE( ichr ) ) continue;
2813 
2814             if ( 0 == ChrList.lst[ichr].shadow_size ) continue;
2815 
2816             render_bad_shadow( ichr );
2817             tnc++;
2818         }
2819     }
2820     else
2821     {
2822         // Good shadows for me
2823         for ( cnt = 0; cnt < pdolist->count; cnt++ )
2824         {
2825             CHR_REF ichr = pdolist->lst[cnt].ichr;
2826             if ( !VALID_CHR_RANGE( ichr ) ) continue;
2827 
2828             if ( 0 == ChrList.lst[ichr].shadow_size ) continue;
2829 
2830             render_shadow( ichr );
2831             tnc++;
2832         }
2833     }
2834 
2835     return gfx_success;
2836 }
2837 
2838 //--------------------------------------------------------------------------------------------
render_scene_mesh(renderlist_t * prlist,dolist_t * pdolist)2839 gfx_rv render_scene_mesh( renderlist_t * prlist, dolist_t * pdolist )
2840 {
2841     /// @details BB@> draw the mesh and any reflected objects
2842 
2843     gfx_rv retval;
2844 
2845     if ( NULL == prlist )
2846     {
2847         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL renderlist" );
2848         return gfx_error;
2849     }
2850 
2851     // assume the best
2852     retval = gfx_success;
2853 
2854     // advance the animation of all animated tiles
2855     animate_all_tiles( prlist->pmesh );
2856 
2857     PROFILE_BEGIN( render_scene_mesh_ndr );
2858     {
2859         // draw all tiles that do not reflect characters
2860         if ( gfx_error == render_scene_mesh_ndr( prlist ) )
2861         {
2862             retval = gfx_error;
2863         }
2864     }
2865     PROFILE_END( render_scene_mesh_ndr );
2866 
2867     //--------------------------------
2868     // draw the reflective tiles and any reflected objects
2869     if ( gfx.refon )
2870     {
2871         PROFILE_BEGIN( render_scene_mesh_drf_back );
2872         {
2873             // blank out the background behind reflective tiles
2874 
2875             if ( gfx_error == render_scene_mesh_drf_back( prlist ) )
2876             {
2877                 retval = gfx_error;
2878             }
2879         }
2880         PROFILE_END( render_scene_mesh_drf_back );
2881 
2882         PROFILE_BEGIN( render_scene_mesh_ref );
2883         {
2884             // Render all reflected objects
2885             if ( gfx_error == render_scene_mesh_ref( prlist, pdolist ) )
2886             {
2887                 retval = gfx_error;
2888             }
2889         }
2890         PROFILE_END( render_scene_mesh_ref );
2891 
2892         PROFILE_BEGIN( render_scene_mesh_ref_chr );
2893         {
2894             // Render the shadow floors
2895             if ( gfx_error == render_scene_mesh_ref_chr( prlist ) )
2896             {
2897                 retval = gfx_error;
2898             }
2899         }
2900         PROFILE_END( render_scene_mesh_ref_chr );
2901     }
2902     else
2903     {
2904         PROFILE_BEGIN( render_scene_mesh_drf_solid );
2905         {
2906             // Render the shadow floors as normal solid floors
2907             if ( gfx_error == render_scene_mesh_drf_solid( prlist ) )
2908             {
2909                 retval = gfx_error;
2910             }
2911         }
2912         PROFILE_END( render_scene_mesh_drf_solid );
2913     }
2914 
2915 #if defined(RENDER_HMAP) && defined(_DEBUG)
2916 
2917     // render the heighmap
2918     for ( cnt = 0; cnt < prlist->all_count; cnt++ )
2919     {
2920         render_hmap_fan( pmesh, prlist->all[cnt] );
2921     }
2922 
2923 #endif
2924 
2925     PROFILE_BEGIN( render_scene_mesh_render_shadows );
2926     {
2927         // Render the shadows
2928         if ( gfx_error == render_scene_mesh_render_shadows( pdolist ) )
2929         {
2930             retval = gfx_error;
2931         }
2932     }
2933     PROFILE_END( render_scene_mesh_render_shadows );
2934 
2935     return retval;
2936 }
2937 
2938 //--------------------------------------------------------------------------------------------
render_scene_solid(dolist_t * pdolist)2939 gfx_rv render_scene_solid( dolist_t * pdolist )
2940 {
2941     /// @detaile BB@> Render all solid objects
2942 
2943     Uint32 cnt;
2944     gfx_rv retval;
2945 
2946     if ( NULL == pdolist )
2947     {
2948         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL dolist" );
2949         return gfx_error;
2950     }
2951 
2952     if ( pdolist->count >= DOLIST_SIZE )
2953     {
2954         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "invalid dolist size" );
2955         return gfx_error;
2956     }
2957 
2958     // assume the best
2959     retval = gfx_success;
2960 
2961     ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT )
2962     {
2963         // scan for solid objects
2964         for ( cnt = 0; cnt < pdolist->count; cnt++ )
2965         {
2966             // solid objects draw into the depth buffer for hidden surface removal
2967             GL_DEBUG( glDepthMask )( GL_TRUE );                     // GL_ENABLE_BIT
2968 
2969             // do not draw hidden surfaces
2970             GL_DEBUG( glEnable )( GL_DEPTH_TEST );                  // GL_ENABLE_BIT
2971             GL_DEBUG( glDepthFunc )( GL_LESS );                   // GL_DEPTH_BUFFER_BIT
2972 
2973             GL_DEBUG( glEnable )( GL_ALPHA_TEST );                 // GL_ENABLE_BIT
2974             GL_DEBUG( glAlphaFunc )( GL_GREATER, 0.0f );             // GL_COLOR_BUFFER_BIT
2975 
2976             if ( MAX_PRT == pdolist->lst[cnt].iprt && VALID_CHR_RANGE( pdolist->lst[cnt].ichr ) )
2977             {
2978                 if ( gfx_error == render_one_mad_solid( pdolist->lst[cnt].ichr ) )
2979                 {
2980                     retval = gfx_error;
2981                 }
2982             }
2983             else if ( MAX_CHR == pdolist->lst[cnt].ichr && VALID_PRT_RANGE( pdolist->lst[cnt].iprt ) )
2984             {
2985                 // draw draw front and back faces of polygons
2986                 GL_DEBUG( glDisable )( GL_CULL_FACE );
2987 
2988                 if ( gfx_error == render_one_prt_solid( pdolist->lst[cnt].iprt ) )
2989                 {
2990                     retval = gfx_error;
2991                 }
2992             }
2993         }
2994     }
2995     ATTRIB_POP( __FUNCTION__ );
2996 
2997     return retval;
2998 }
2999 
3000 //--------------------------------------------------------------------------------------------
render_scene_trans(dolist_t * pdolist)3001 gfx_rv render_scene_trans( dolist_t * pdolist )
3002 {
3003     /// @details BB@> draw transparent objects
3004 
3005     int cnt;
3006     gfx_rv retval;
3007 
3008     if ( NULL == pdolist )
3009     {
3010         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL dolist" );
3011         return gfx_error;
3012     }
3013 
3014     if ( pdolist->count >= DOLIST_SIZE )
3015     {
3016         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "invalid dolist size" );
3017         return gfx_error;
3018     }
3019 
3020     // assume the best
3021     retval = gfx_success;
3022 
3023     ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT )
3024     {
3025         //---- set the the transparency parameters
3026 
3027         // don't write into the depth buffer (disable glDepthMask for transparent objects)
3028         GL_DEBUG( glDepthMask )( GL_FALSE );                   // GL_DEPTH_BUFFER_BIT
3029 
3030         // do not draw hidden surfaces
3031         GL_DEBUG( glEnable )( GL_DEPTH_TEST );                // GL_ENABLE_BIT
3032         GL_DEBUG( glDepthFunc )( GL_LEQUAL );                 // GL_DEPTH_BUFFER_BIT
3033 
3034         // Now render all transparent and light objects
3035         for ( cnt = (( int )pdolist->count ) - 1; cnt >= 0; cnt-- )
3036         {
3037             if ( MAX_PRT == pdolist->lst[cnt].iprt && MAX_CHR != pdolist->lst[cnt].ichr )
3038             {
3039                 if ( gfx_error == render_one_mad_trans( pdolist->lst[cnt].ichr ) )
3040                 {
3041                     retval = gfx_error;
3042                 }
3043             }
3044             else if ( MAX_CHR == pdolist->lst[cnt].ichr && MAX_PRT != pdolist->lst[cnt].iprt )
3045             {
3046                 // this is a particle
3047                 if ( gfx_error == render_one_prt_trans( pdolist->lst[cnt].iprt ) )
3048                 {
3049                     retval = gfx_error;
3050                 }
3051             }
3052         }
3053     }
3054     ATTRIB_POP( __FUNCTION__ );
3055 
3056     return retval;
3057 }
3058 
3059 //--------------------------------------------------------------------------------------------
render_scene(ego_mpd_t * pmesh,camera_t * pcam)3060 gfx_rv render_scene( ego_mpd_t * pmesh, camera_t * pcam )
3061 {
3062     /// @details ZZ@> This function draws 3D objects
3063 
3064     gfx_rv retval;
3065 
3066     if ( NULL == pcam ) pcam = PCamera;
3067     if ( NULL == pcam )
3068     {
3069         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "cannot find a valid camera" );
3070         return gfx_error;
3071     }
3072 
3073     if ( NULL == pmesh ) pmesh = PMesh;
3074     if ( NULL == pmesh )
3075     {
3076         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "cannot find a valid mesh" );
3077         return gfx_error;
3078     }
3079 
3080     // assume the best
3081     retval = gfx_success;
3082 
3083     PROFILE_BEGIN( render_scene_init );
3084     {
3085         if ( gfx_error == render_scene_init( &renderlist, &_dolist, &dynalist, pmesh, pcam ) )
3086         {
3087             retval = gfx_error;
3088         }
3089     }
3090     PROFILE_END( render_scene_init );
3091 
3092     PROFILE_BEGIN( render_scene_mesh );
3093     {
3094         PROFILE_BEGIN( render_scene_mesh_dolist_sort );
3095         {
3096             // sort the dolist for reflected objects
3097             // reflected characters and objects are drawn in this pass
3098             if ( gfx_error == dolist_sort( &_dolist, pcam, btrue ) )
3099             {
3100                 retval = gfx_error;
3101             }
3102         }
3103         PROFILE_END( render_scene_mesh_dolist_sort );
3104 
3105         // do the render pass for the mesh
3106         if ( gfx_error == render_scene_mesh( &renderlist, &_dolist ) )
3107         {
3108             retval = gfx_error;
3109         }
3110 
3111         time_render_scene_mesh_dolist_sort    = PROFILE_QUERY( render_scene_mesh_dolist_sort ) * TARGET_FPS;
3112         time_render_scene_mesh_ndr            = PROFILE_QUERY( render_scene_mesh_ndr ) * TARGET_FPS;
3113         time_render_scene_mesh_drf_back       = PROFILE_QUERY( render_scene_mesh_drf_back ) * TARGET_FPS;
3114         time_render_scene_mesh_ref            = PROFILE_QUERY( render_scene_mesh_ref ) * TARGET_FPS;
3115         time_render_scene_mesh_ref_chr        = PROFILE_QUERY( render_scene_mesh_ref_chr ) * TARGET_FPS;
3116         time_render_scene_mesh_drf_solid      = PROFILE_QUERY( render_scene_mesh_drf_solid ) * TARGET_FPS;
3117         time_render_scene_mesh_render_shadows = PROFILE_QUERY( render_scene_mesh_render_shadows ) * TARGET_FPS;
3118     }
3119     PROFILE_END( render_scene_mesh );
3120 
3121     PROFILE_BEGIN( render_scene_solid );
3122     {
3123         // sort the dolist for non-reflected objects
3124         if ( gfx_error == dolist_sort( &_dolist, pcam, bfalse ) )
3125         {
3126             retval = gfx_error;
3127         }
3128 
3129         // do the render pass for solid objects
3130         if ( gfx_error == render_scene_solid( &_dolist ) )
3131         {
3132             retval = gfx_error;
3133         }
3134     }
3135     PROFILE_END( render_scene_solid );
3136 
3137     PROFILE_BEGIN( render_scene_water );
3138     {
3139         // draw the water
3140         if ( gfx_error == render_water( &renderlist ) )
3141         {
3142             retval = gfx_error;
3143         }
3144     }
3145     PROFILE_END( render_scene_water );
3146 
3147     PROFILE_BEGIN( render_scene_trans );
3148     {
3149         // do the render pass for transparent objects
3150         if ( gfx_error == render_scene_trans( &_dolist ) )
3151         {
3152             retval = gfx_error;
3153         }
3154     }
3155     PROFILE_END( render_scene_trans );
3156 
3157 #if defined(_DEBUG)
3158     render_all_prt_attachment();
3159 
3160     // daw some debugging lines
3161     draw_all_lines( PCamera );
3162 #endif
3163 
3164 #if defined(DRAW_PRT_BBOX)
3165     render_all_prt_bbox();
3166 #endif
3167 
3168     time_render_scene_init  = PROFILE_QUERY( render_scene_init ) * TARGET_FPS;
3169     time_render_scene_mesh  = PROFILE_QUERY( render_scene_mesh ) * TARGET_FPS;
3170     time_render_scene_solid = PROFILE_QUERY( render_scene_solid ) * TARGET_FPS;
3171     time_render_scene_water = PROFILE_QUERY( render_scene_water ) * TARGET_FPS;
3172     time_render_scene_trans = PROFILE_QUERY( render_scene_trans ) * TARGET_FPS;
3173 
3174     time_draw_scene = time_render_scene_init + time_render_scene_mesh + time_render_scene_solid + time_render_scene_water + time_render_scene_trans;
3175 
3176     return retval;
3177 }
3178 
3179 //--------------------------------------------------------------------------------------------
render_world_background(const TX_REF texture)3180 void render_world_background( const TX_REF texture )
3181 {
3182     /// @details ZZ@> This function draws the large background
3183     GLvertex vtlist[4];
3184     int i;
3185     float z0, Qx, Qy;
3186     float light = 1.0f, intens = 1.0f, alpha = 1.0f;
3187 
3188     float xmag, Cx_0, Cx_1;
3189     float ymag, Cy_0, Cy_1;
3190 
3191     ego_mpd_info_t * pinfo;
3192     grid_mem_t     * pgmem;
3193     oglx_texture_t   * ptex;
3194     water_instance_layer_t * ilayer;
3195 
3196     pinfo = &( PMesh->info );
3197     pgmem = &( PMesh->gmem );
3198 
3199     // which layer
3200     ilayer = water.layer + 0;
3201 
3202     // the "official" camera height
3203     z0 = 1500;
3204 
3205     // clip the waterlayer uv offset
3206     ilayer->tx.x = ilayer->tx.x - ( float )FLOOR( ilayer->tx.x );
3207     ilayer->tx.y = ilayer->tx.y - ( float )FLOOR( ilayer->tx.y );
3208 
3209     // determine the constants for the x-coordinate
3210     xmag = water.backgroundrepeat / 4 / ( 1.0f + z0 * ilayer->dist.x ) / GRID_FSIZE;
3211     Cx_0 = xmag * ( 1.0f +  PCamera->pos.z       * ilayer->dist.x );
3212     Cx_1 = -xmag * ( 1.0f + ( PCamera->pos.z - z0 ) * ilayer->dist.x );
3213 
3214     // determine the constants for the y-coordinate
3215     ymag = water.backgroundrepeat / 4 / ( 1.0f + z0 * ilayer->dist.y ) / GRID_FSIZE;
3216     Cy_0 = ymag * ( 1.0f +  PCamera->pos.z       * ilayer->dist.y );
3217     Cy_1 = -ymag * ( 1.0f + ( PCamera->pos.z - z0 ) * ilayer->dist.y );
3218 
3219     // Figure out the coordinates of its corners
3220     Qx = -pgmem->edge_x;
3221     Qy = -pgmem->edge_y;
3222     vtlist[0].pos[XX] = Qx;
3223     vtlist[0].pos[YY] = Qy;
3224     vtlist[0].pos[ZZ] = PCamera->pos.z - z0;
3225     vtlist[0].tex[SS] = Cx_0 * Qx + Cx_1 * PCamera->pos.x + ilayer->tx.x;
3226     vtlist[0].tex[TT] = Cy_0 * Qy + Cy_1 * PCamera->pos.y + ilayer->tx.y;
3227 
3228     Qx = 2 * pgmem->edge_x;
3229     Qy = -pgmem->edge_y;
3230     vtlist[1].pos[XX] = Qx;
3231     vtlist[1].pos[YY] = Qy;
3232     vtlist[1].pos[ZZ] = PCamera->pos.z - z0;
3233     vtlist[1].tex[SS] = Cx_0 * Qx + Cx_1 * PCamera->pos.x + ilayer->tx.x;
3234     vtlist[1].tex[TT] = Cy_0 * Qy + Cy_1 * PCamera->pos.y + ilayer->tx.y;
3235 
3236     Qx = 2 * pgmem->edge_x;
3237     Qy = 2 * pgmem->edge_y;
3238     vtlist[2].pos[XX] = Qx;
3239     vtlist[2].pos[YY] = Qy;
3240     vtlist[2].pos[ZZ] = PCamera->pos.z - z0;
3241     vtlist[2].tex[SS] = Cx_0 * Qx + Cx_1 * PCamera->pos.x + ilayer->tx.x;
3242     vtlist[2].tex[TT] = Cy_0 * Qy + Cy_1 * PCamera->pos.y + ilayer->tx.y;
3243 
3244     Qx = -pgmem->edge_x;
3245     Qy = 2 * pgmem->edge_y;
3246     vtlist[3].pos[XX] = Qx;
3247     vtlist[3].pos[YY] = Qy;
3248     vtlist[3].pos[ZZ] = PCamera->pos.z - z0;
3249     vtlist[3].tex[SS] = Cx_0 * Qx + Cx_1 * PCamera->pos.x + ilayer->tx.x;
3250     vtlist[3].tex[TT] = Cy_0 * Qy + Cy_1 * PCamera->pos.y + ilayer->tx.y;
3251 
3252     light = water.light ? 1.0f : 0.0f;
3253     alpha = ilayer->alpha * INV_FF;
3254 
3255     if ( gfx.usefaredge )
3256     {
3257         float fcos;
3258 
3259         intens = light_a * ilayer->light_add;
3260 
3261         fcos = light_nrm[kZ];
3262         if ( fcos > 0.0f )
3263         {
3264             intens += fcos * fcos * light_d * ilayer->light_dir;
3265         }
3266 
3267         intens = CLIP( intens, 0.0f, 1.0f );
3268     }
3269 
3270     ptex = TxTexture_get_ptr( texture );
3271 
3272     oglx_texture_Bind( ptex );
3273 
3274     ATTRIB_PUSH( __FUNCTION__, GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT );
3275     {
3276         // flat shading
3277         GL_DEBUG( glShadeModel )( GL_FLAT );      // GL_LIGHTING_BIT
3278 
3279         // don't write into the depth buffer (disable glDepthMask for transparent objects)
3280         GL_DEBUG( glDepthMask )( GL_FALSE );      // GL_DEPTH_BUFFER_BIT
3281 
3282         // essentially disable the depth test without calling glDisable( GL_DEPTH_TEST )
3283         GL_DEBUG( glEnable )( GL_DEPTH_TEST );      // GL_ENABLE_BIT
3284         GL_DEBUG( glDepthFunc )( GL_ALWAYS );     // GL_DEPTH_BUFFER_BIT
3285 
3286         // draw draw front and back faces of polygons
3287         GL_DEBUG( glDisable )( GL_CULL_FACE );    // GL_ENABLE_BIT
3288 
3289         if ( alpha > 0.0f )
3290         {
3291             ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT );
3292             {
3293                 GL_DEBUG( glColor4f )( intens, intens, intens, alpha );             // GL_CURRENT_BIT
3294 
3295                 if ( alpha >= 1.0f )
3296                 {
3297                     GL_DEBUG( glDisable )( GL_BLEND );                               // GL_ENABLE_BIT
3298                 }
3299                 else
3300                 {
3301                     GL_DEBUG( glEnable )( GL_BLEND );                               // GL_ENABLE_BIT
3302                     GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );  // GL_COLOR_BUFFER_BIT
3303                 }
3304 
3305                 GL_DEBUG( glBegin )( GL_TRIANGLE_FAN );
3306                 {
3307                     for ( i = 0; i < 4; i++ )
3308                     {
3309                         GL_DEBUG( glTexCoord2fv )( vtlist[i].tex );
3310                         GL_DEBUG( glVertex3fv )( vtlist[i].pos );
3311                     }
3312                 }
3313                 GL_DEBUG_END();
3314             }
3315             ATTRIB_POP( __FUNCTION__ );
3316         }
3317 
3318         if ( light > 0.0f )
3319         {
3320             ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT );
3321             {
3322                 GL_DEBUG( glDisable )( GL_BLEND );                           // GL_COLOR_BUFFER_BIT
3323                 GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE );            // GL_COLOR_BUFFER_BIT
3324 
3325                 GL_DEBUG( glColor4f )( light, light, light, 1.0f );         // GL_CURRENT_BIT
3326 
3327                 GL_DEBUG( glBegin )( GL_TRIANGLE_FAN );
3328                 {
3329                     for ( i = 0; i < 4; i++ )
3330                     {
3331                         GL_DEBUG( glTexCoord2fv )( vtlist[i].tex );
3332                         GL_DEBUG( glVertex3fv )( vtlist[i].pos );
3333                     }
3334                 }
3335                 GL_DEBUG_END();
3336             }
3337             ATTRIB_POP( __FUNCTION__ );
3338         }
3339     }
3340     ATTRIB_POP( __FUNCTION__ );
3341 }
3342 
3343 //--------------------------------------------------------------------------------------------
render_world_overlay(const TX_REF texture)3344 void render_world_overlay( const TX_REF texture )
3345 {
3346     /// @details ZZ@> This function draws the large foreground
3347 
3348     float alpha, ftmp;
3349     fvec3_t   vforw_wind, vforw_cam;
3350 
3351     oglx_texture_t           * ptex;
3352 
3353     water_instance_layer_t * ilayer = water.layer + 1;
3354 
3355     vforw_wind.x = ilayer->tx_add.x;
3356     vforw_wind.y = ilayer->tx_add.y;
3357     vforw_wind.z = 0;
3358     vforw_wind = fvec3_normalize( vforw_wind.v );
3359 
3360     mat_getCamForward( PCamera->mView.v, vforw_cam.v );
3361     fvec3_self_normalize( vforw_cam.v );
3362 
3363     // make the texture begin to disappear if you are not looking straight down
3364     ftmp = fvec3_dot_product( vforw_wind.v, vforw_cam.v );
3365 
3366     alpha = ( 1.0f - ftmp * ftmp ) * ( ilayer->alpha * INV_FF );
3367 
3368     if ( alpha != 0.0f )
3369     {
3370         GLvertex vtlist[4];
3371         int i;
3372         float size;
3373         float sinsize, cossize;
3374         float x, y, z;
3375         float loc_foregroundrepeat;
3376 
3377         // Figure out the screen coordinates of its corners
3378         x = sdl_scr.x << 6;
3379         y = sdl_scr.y << 6;
3380         z = 0;
3381         size = x + y + 1;
3382         sinsize = turntosin[( 3*2047 ) & TRIG_TABLE_MASK] * size;
3383         cossize = turntocos[( 3*2047 ) & TRIG_TABLE_MASK] * size;
3384         loc_foregroundrepeat = water.foregroundrepeat * MIN( x / sdl_scr.x, y / sdl_scr.x );
3385 
3386         vtlist[0].pos[XX] = x + cossize;
3387         vtlist[0].pos[YY] = y - sinsize;
3388         vtlist[0].pos[ZZ] = z;
3389         vtlist[0].tex[SS] = ilayer->tx.x;
3390         vtlist[0].tex[TT] = ilayer->tx.y;
3391 
3392         vtlist[1].pos[XX] = x + sinsize;
3393         vtlist[1].pos[YY] = y + cossize;
3394         vtlist[1].pos[ZZ] = z;
3395         vtlist[1].tex[SS] = ilayer->tx.x + loc_foregroundrepeat;
3396         vtlist[1].tex[TT] = ilayer->tx.y;
3397 
3398         vtlist[2].pos[XX] = x - cossize;
3399         vtlist[2].pos[YY] = y + sinsize;
3400         vtlist[2].pos[ZZ] = z;
3401         vtlist[2].tex[SS] = ilayer->tx.x + loc_foregroundrepeat;
3402         vtlist[2].tex[TT] = ilayer->tx.y + loc_foregroundrepeat;
3403 
3404         vtlist[3].pos[XX] = x - sinsize;
3405         vtlist[3].pos[YY] = y - cossize;
3406         vtlist[3].pos[ZZ] = z;
3407         vtlist[3].tex[SS] = ilayer->tx.x;
3408         vtlist[3].tex[TT] = ilayer->tx.y + loc_foregroundrepeat;
3409 
3410         ptex = TxTexture_get_ptr( texture );
3411 
3412         ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT | GL_COLOR_BUFFER_BIT | GL_HINT_BIT );
3413         {
3414             // make sure that the texture is as smooth as possible
3415             GL_DEBUG( glHint )( GL_POLYGON_SMOOTH_HINT, GL_NICEST );          // GL_HINT_BIT
3416 
3417             // flat shading
3418             GL_DEBUG( glShadeModel )( GL_FLAT );                             // GL_LIGHTING_BIT
3419 
3420             // don't write into the depth buffer (disable glDepthMask for transparent objects)
3421             GL_DEBUG( glDepthMask )( GL_FALSE );                             // GL_DEPTH_BUFFER_BIT
3422 
3423             // essentially disable the depth test without calling glDisable( GL_DEPTH_TEST )
3424             GL_DEBUG( glEnable )( GL_DEPTH_TEST );                           // GL_ENABLE_BIT
3425             GL_DEBUG( glDepthFunc )( GL_ALWAYS );                            // GL_DEPTH_BUFFER_BIT
3426 
3427             // draw draw front and back faces of polygons
3428             GL_DEBUG( glDisable )( GL_CULL_FACE );                           // GL_ENABLE_BIT
3429 
3430             // do not display the completely transparent portion
3431             GL_DEBUG( glEnable )( GL_ALPHA_TEST );                            // GL_ENABLE_BIT
3432             GL_DEBUG( glAlphaFunc )( GL_GREATER, 0.0f );                      // GL_COLOR_BUFFER_BIT
3433 
3434             // make the texture a filter
3435             GL_DEBUG( glEnable )( GL_BLEND );                                 // GL_ENABLE_BIT
3436             GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR );  // GL_COLOR_BUFFER_BIT
3437 
3438             oglx_texture_Bind( ptex );
3439 
3440             GL_DEBUG( glColor4f )( 1.0f, 1.0f, 1.0f, 1.0f - ABS( alpha ) );
3441             GL_DEBUG( glBegin )( GL_TRIANGLE_FAN );
3442             for ( i = 0; i < 4; i++ )
3443             {
3444                 GL_DEBUG( glTexCoord2fv )( vtlist[i].tex );
3445                 GL_DEBUG( glVertex3fv )( vtlist[i].pos );
3446             }
3447             GL_DEBUG_END();
3448         }
3449         ATTRIB_POP( __FUNCTION__ );
3450     }
3451 }
3452 
3453 //--------------------------------------------------------------------------------------------
render_world(camera_t * pcam)3454 void render_world( camera_t * pcam )
3455 {
3456     gfx_error_state_t * err_tmp;
3457 
3458     gfx_error_clear();
3459 
3460     gfx_begin_3d( pcam );
3461     {
3462         if ( gfx.draw_background )
3463         {
3464             // Render the background
3465             render_world_background(( TX_REF )TX_WATER_LOW ); // TX_WATER_LOW for waterlow.bmp
3466         }
3467 
3468         render_scene( PMesh, pcam );
3469 
3470         if ( gfx.draw_overlay )
3471         {
3472             // Foreground overlay
3473             render_world_overlay(( TX_REF )TX_WATER_TOP ); // TX_WATER_TOP is watertop.bmp
3474         }
3475 
3476         if ( pcam->motion_blur > 0 )
3477         {
3478             //Do motion blur
3479             GL_DEBUG( glAccum )( GL_MULT, pcam->motion_blur );
3480             GL_DEBUG( glAccum )( GL_ACCUM, 1.0f - pcam->motion_blur );
3481             GL_DEBUG( glAccum )( GL_RETURN, 1.0f );
3482         }
3483     }
3484     gfx_end_3d();
3485 
3486     // Render the billboards
3487     render_all_billboards( pcam );
3488 
3489     err_tmp = gfx_error_pop();
3490     if ( NULL != err_tmp )
3491     {
3492         printf( "****\nEncountered graphics errors in frame %d\n\n****", game_frame_all );
3493         while ( NULL != err_tmp )
3494         {
3495             printf( "vvvv\n" );
3496             printf(
3497                 "\tfile     == %s\n"
3498                 "\tline     == %d\n"
3499                 "\tfunction == %s\n"
3500                 "\tcode     == %d\n"
3501                 "\tstring   == %s\n",
3502                 err_tmp->file, err_tmp->line, err_tmp->function, err_tmp->type, err_tmp->string );
3503             printf( "^^^^\n\n" );
3504 
3505             err_tmp = gfx_error_pop();
3506         }
3507         printf( "****\n\n" );
3508     }
3509 }
3510 
3511 //--------------------------------------------------------------------------------------------
gfx_main()3512 void gfx_main()
3513 {
3514     /// @details ZZ@> This function does all the drawing stuff
3515 
3516     render_world( PCamera );
3517     draw_text();
3518 
3519     request_flip_pages();
3520 }
3521 
3522 //--------------------------------------------------------------------------------------------
3523 // UTILITY FUNCTIONS
3524 //--------------------------------------------------------------------------------------------
dump_screenshot()3525 bool_t dump_screenshot()
3526 {
3527     /// @details BB@> dumps the current screen (GL context) to a new bitmap file
3528     /// right now it dumps it to whatever the current directory is
3529 
3530     // returns btrue if successful, bfalse otherwise
3531 
3532     int i;
3533     bool_t savefound = bfalse;
3534     bool_t saved     = bfalse;
3535     STRING szFilename, szResolvedFilename;
3536 
3537     // find a valid file name
3538     savefound = bfalse;
3539     i = 0;
3540     while ( !savefound && ( i < 100 ) )
3541     {
3542         snprintf( szFilename, SDL_arraysize( szFilename ), "ego%02d.bmp", i );
3543 
3544         // lame way of checking if the file already exists...
3545         savefound = !vfs_exists( szFilename );
3546         if ( !savefound )
3547         {
3548             i++;
3549         }
3550     }
3551 
3552     if ( !savefound ) return bfalse;
3553 
3554     // convert the file path to the correct write path
3555     strncpy( szResolvedFilename, vfs_resolveWriteFilename( szFilename ), sizeof( szFilename ) );
3556 
3557     // if we are not using OpenGL, use SDL to dump the screen
3558     if ( HAS_NO_BITS( sdl_scr.pscreen->flags, SDL_OPENGL ) )
3559     {
3560         SDL_SaveBMP( sdl_scr.pscreen, szResolvedFilename );
3561         return bfalse;
3562     }
3563 
3564     // we ARE using OpenGL
3565     GL_DEBUG( glPushClientAttrib )( GL_CLIENT_PIXEL_STORE_BIT ) ;
3566     {
3567         SDL_Surface *temp;
3568 
3569         // create a SDL surface
3570         temp = SDL_CreateRGBSurface( SDL_SWSURFACE, sdl_scr.x, sdl_scr.y, 24, sdl_r_mask, sdl_g_mask, sdl_b_mask, 0 );
3571 
3572         if ( NULL == temp )
3573         {
3574             //Something went wrong
3575             SDL_FreeSurface( temp );
3576             return bfalse;
3577         }
3578 
3579         //Now lock the surface so that we can read it
3580         if ( -1 != SDL_LockSurface( temp ) )
3581         {
3582             SDL_Rect rect;
3583 
3584             memcpy( &rect, &( sdl_scr.pscreen->clip_rect ), sizeof( SDL_Rect ) );
3585             if ( 0 == rect.w && 0 == rect.h )
3586             {
3587                 rect.w = sdl_scr.x;
3588                 rect.h = sdl_scr.y;
3589             }
3590             if ( rect.w > 0 && rect.h > 0 )
3591             {
3592                 int y;
3593                 Uint8 * pixels;
3594 
3595                 GL_DEBUG( glGetError )();
3596 
3597                 //// use the allocated screen to tell OpenGL about the row length (including the lapse) in pixels
3598                 //// stolen from SDL ;)
3599                 // GL_DEBUG(glPixelStorei)(GL_UNPACK_ROW_LENGTH, temp->pitch / temp->format->BytesPerPixel );
3600                 // EGOBOO_ASSERT( GL_NO_ERROR == GL_DEBUG(glGetError)() );
3601 
3602                 //// since we have specified the row actual length and will give a pointer to the actual pixel buffer,
3603                 //// it is not necesssaty to mess with the alignment
3604                 // GL_DEBUG(glPixelStorei)(GL_UNPACK_ALIGNMENT, 1 );
3605                 // EGOBOO_ASSERT( GL_NO_ERROR == GL_DEBUG(glGetError)() );
3606 
3607                 // ARGH! Must copy the pixels row-by-row, since the OpenGL video memory is flipped vertically
3608                 // relative to the SDL Screen memory
3609 
3610                 // this is supposed to be a DirectX thing, so it needs to be tested out on glx
3611                 // there should probably be [SCREENSHOT_INVERT] and [SCREENSHOT_VALID] keys in setup.txt
3612                 pixels = ( Uint8 * )temp->pixels;
3613                 for ( y = rect.y; y < rect.y + rect.h; y++ )
3614                 {
3615                     GL_DEBUG( glReadPixels )( rect.x, ( rect.h - y ) - 1, rect.w, 1, GL_RGB, GL_UNSIGNED_BYTE, pixels );
3616                     pixels += temp->pitch;
3617                 }
3618                 EGOBOO_ASSERT( GL_NO_ERROR == GL_DEBUG( glGetError )() );
3619             }
3620 
3621             SDL_UnlockSurface( temp );
3622 
3623             // Save the file as a .bmp
3624             saved = ( -1 != SDL_SaveBMP( temp, szResolvedFilename ) );
3625         }
3626 
3627         // free the SDL surface
3628         SDL_FreeSurface( temp );
3629         if ( saved )
3630         {
3631             // tell the user what we did
3632             debug_printf( "Saved to %s", szFilename );
3633         }
3634     }
3635     GL_DEBUG( glPopClientAttrib )();
3636 
3637     return savefound && saved;
3638 }
3639 
3640 //--------------------------------------------------------------------------------------------
clear_messages()3641 void clear_messages()
3642 {
3643     /// @details ZZ@> This function empties the message buffer
3644     int cnt;
3645 
3646     cnt = 0;
3647 
3648     while ( cnt < MAX_MESSAGE )
3649     {
3650         DisplayMsg.ary[cnt].time = 0;
3651         cnt++;
3652     }
3653 }
3654 
3655 //--------------------------------------------------------------------------------------------
calc_light_rotation(int rotation,int normal)3656 float calc_light_rotation( int rotation, int normal )
3657 {
3658     /// @details ZZ@> This function helps make_lighttable
3659     fvec3_t   nrm, nrm2;
3660     float sinrot, cosrot;
3661 
3662     nrm.x = kMd2Normals[normal][0];
3663     nrm.y = kMd2Normals[normal][1];
3664     nrm.z = kMd2Normals[normal][2];
3665 
3666     sinrot = sinlut[rotation];
3667     cosrot = coslut[rotation];
3668 
3669     nrm2.x = cosrot * nrm.x + sinrot * nrm.y;
3670     nrm2.y = cosrot * nrm.y - sinrot * nrm.x;
3671     nrm2.z = nrm.z;
3672 
3673     return ( nrm2.x < 0 ) ? 0 : ( nrm2.x * nrm2.x );
3674 }
3675 
3676 //--------------------------------------------------------------------------------------------
calc_light_global(int rotation,int normal,float lx,float ly,float lz)3677 float calc_light_global( int rotation, int normal, float lx, float ly, float lz )
3678 {
3679     /// @details ZZ@> This function helps make_lighttable
3680     float fTmp;
3681     fvec3_t   nrm, nrm2;
3682     float sinrot, cosrot;
3683 
3684     nrm.x = kMd2Normals[normal][0];
3685     nrm.y = kMd2Normals[normal][1];
3686     nrm.z = kMd2Normals[normal][2];
3687 
3688     sinrot = sinlut[rotation];
3689     cosrot = coslut[rotation];
3690 
3691     nrm2.x = cosrot * nrm.x + sinrot * nrm.y;
3692     nrm2.y = cosrot * nrm.y - sinrot * nrm.x;
3693     nrm2.z = nrm.z;
3694 
3695     fTmp = nrm2.x * lx + nrm2.y * ly + nrm2.z * lz;
3696     if ( fTmp < 0 ) fTmp = 0;
3697 
3698     return fTmp * fTmp;
3699 }
3700 
3701 //--------------------------------------------------------------------------------------------
make_enviro(void)3702 void make_enviro( void )
3703 {
3704     /// @details ZZ@> This function sets up the environment mapping table
3705     int cnt;
3706     float x, y, z;
3707 
3708     // Find the environment map positions
3709     for ( cnt = 0; cnt < EGO_NORMAL_COUNT; cnt++ )
3710     {
3711         x = kMd2Normals[cnt][0];
3712         y = kMd2Normals[cnt][1];
3713         indextoenvirox[cnt] = ATAN2( y, x ) / TWO_PI;
3714     }
3715 
3716     for ( cnt = 0; cnt < 256; cnt++ )
3717     {
3718         z = cnt / INV_FF;  // Z is between 0 and 1
3719         lighttoenviroy[cnt] = z;
3720     }
3721 }
3722 
3723 //--------------------------------------------------------------------------------------------
grid_lighting_test(ego_mpd_t * pmesh,GLXvector3f pos,float * low_diff,float * hgh_diff)3724 float grid_lighting_test( ego_mpd_t * pmesh, GLXvector3f pos, float * low_diff, float * hgh_diff )
3725 {
3726     int ix, iy, cnt;
3727     Uint32 fan[4];
3728     float u, v;
3729 
3730     ego_grid_info_t  * glist;
3731     lighting_cache_t * cache_list[4];
3732 
3733     if ( NULL == pmesh ) pmesh = PMesh;
3734     if ( NULL == pmesh )
3735     {
3736         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "cannot find a valid mesh" );
3737         return 0.0f;
3738     }
3739 
3740     glist = pmesh->gmem.grid_list;
3741 
3742     ix = FLOOR( pos[XX] / GRID_FSIZE );
3743     iy = FLOOR( pos[YY] / GRID_FSIZE );
3744 
3745     fan[0] = mesh_get_tile_int( pmesh, ix,     iy );
3746     fan[1] = mesh_get_tile_int( pmesh, ix + 1, iy );
3747     fan[2] = mesh_get_tile_int( pmesh, ix,     iy + 1 );
3748     fan[3] = mesh_get_tile_int( pmesh, ix + 1, iy + 1 );
3749 
3750     for ( cnt = 0; cnt < 4; cnt++ )
3751     {
3752         cache_list[cnt] = NULL;
3753         if ( mesh_grid_is_valid( pmesh, fan[cnt] ) )
3754         {
3755             cache_list[cnt] = &( glist[fan[cnt]].cache );
3756         }
3757     }
3758 
3759     u = pos[XX] / GRID_FSIZE - ix;
3760     v = pos[YY] / GRID_FSIZE - iy;
3761 
3762     return lighting_cache_test( cache_list, u, v, low_diff, hgh_diff );
3763 }
3764 
3765 //--------------------------------------------------------------------------------------------
grid_lighting_interpolate(ego_mpd_t * pmesh,lighting_cache_t * dst,float fx,float fy)3766 bool_t grid_lighting_interpolate( ego_mpd_t * pmesh, lighting_cache_t * dst, float fx, float fy )
3767 {
3768     int ix, iy, cnt;
3769     Uint32 fan[4];
3770     float u, v;
3771 
3772     ego_grid_info_t  * glist;
3773     lighting_cache_t * cache_list[4];
3774 
3775     if ( NULL == pmesh ) pmesh = PMesh;
3776     if ( NULL == pmesh )
3777     {
3778         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "cannot find a valid mesh" );
3779         return bfalse;
3780     }
3781 
3782     glist = pmesh->gmem.grid_list;
3783 
3784     ix = FLOOR( fx / GRID_FSIZE );
3785     iy = FLOOR( fy / GRID_FSIZE );
3786 
3787     fan[0] = mesh_get_tile_int( pmesh, ix,     iy );
3788     fan[1] = mesh_get_tile_int( pmesh, ix + 1, iy );
3789     fan[2] = mesh_get_tile_int( pmesh, ix,     iy + 1 );
3790     fan[3] = mesh_get_tile_int( pmesh, ix + 1, iy + 1 );
3791 
3792     for ( cnt = 0; cnt < 4; cnt++ )
3793     {
3794         cache_list[cnt] = NULL;
3795         if ( mesh_grid_is_valid( pmesh, fan[cnt] ) )
3796         {
3797             cache_list[cnt] = &( glist[fan[cnt]].cache );
3798         }
3799     }
3800 
3801     u = fx / GRID_FSIZE - ix;
3802     v = fy / GRID_FSIZE - iy;
3803 
3804     return lighting_cache_interpolate( dst, cache_list, u, v );
3805 }
3806 
3807 //--------------------------------------------------------------------------------------------
3808 //--------------------------------------------------------------------------------------------
gfx_update_timers()3809 void gfx_update_timers()
3810 {
3811     /// @details ZZ@> This function updates the graphics timers
3812 
3813     const float fold = 0.77f;
3814     const float fnew = 1.0f - fold;
3815 
3816     static int gfx_clock_last = 0;
3817     static int gfx_clock      = 0;
3818     static int gfx_clock_stt  = -1;
3819 
3820     int dclock;
3821 
3822     if ( gfx_clock_stt < 0 )
3823     {
3824         gfx_clock_stt  = egoboo_get_ticks();
3825         gfx_clock_last = gfx_clock_stt;
3826         gfx_clock      = gfx_clock_stt;
3827     }
3828 
3829     gfx_clock_last = gfx_clock;
3830     gfx_clock      = egoboo_get_ticks() - gfx_clock_stt;
3831     dclock         = gfx_clock - gfx_clock_last;
3832 
3833     if ( process_running( PROC_PBASE( MProc ) ) )
3834     {
3835         menu_fps_clock += dclock;
3836 
3837         if ( menu_fps_loops > 0 && menu_fps_clock > 0 )
3838         {
3839             stabilized_menu_fps_sum    = fold * stabilized_menu_fps_sum    + fnew * ( float ) menu_fps_loops / (( float ) menu_fps_clock / TICKS_PER_SEC );
3840             stabilized_menu_fps_weight = fold * stabilized_menu_fps_weight + fnew;
3841 
3842             // blank these every so often so that the numbers don't overflow
3843             if ( menu_fps_loops > 10 * TARGET_FPS )
3844             {
3845                 menu_fps_loops = 0;
3846                 menu_fps_clock = 0;
3847             }
3848         };
3849 
3850         if ( stabilized_menu_fps_weight > 0.5f )
3851         {
3852             stabilized_menu_fps = stabilized_menu_fps_sum / stabilized_menu_fps_weight;
3853         }
3854     }
3855 
3856     if ( process_running( PROC_PBASE( GProc ) ) )
3857     {
3858         game_fps_clock += dclock;
3859 
3860         if ( game_fps_loops > 0 && game_fps_clock > 0 )
3861         {
3862             stabilized_game_fps_sum    = fold * stabilized_game_fps_sum    + fnew * ( float ) game_fps_loops / (( float ) game_fps_clock / TICKS_PER_SEC );
3863             stabilized_game_fps_weight = fold * stabilized_game_fps_weight + fnew;
3864 
3865             // blank these every so often so that the numbers don't overflow
3866             if ( game_fps_loops > 10 * TARGET_FPS )
3867             {
3868                 game_fps_loops = 0;
3869                 game_fps_clock = 0;
3870             }
3871         };
3872 
3873         if ( stabilized_game_fps_weight > 0.5f )
3874         {
3875             stabilized_game_fps = stabilized_game_fps_sum / stabilized_game_fps_weight;
3876         }
3877     }
3878 
3879     if ( process_running( PROC_PBASE( GProc ) ) )
3880     {
3881         stabilized_fps = stabilized_game_fps;
3882     }
3883     else if ( process_running( PROC_PBASE( MProc ) ) )
3884     {
3885         stabilized_fps = stabilized_game_fps;
3886     }
3887 
3888 }
3889 
3890 //--------------------------------------------------------------------------------------------
3891 // BILLBOARD DATA IMPLEMENTATION
3892 //--------------------------------------------------------------------------------------------
billboard_data_init(billboard_data_t * pbb)3893 billboard_data_t * billboard_data_init( billboard_data_t * pbb )
3894 {
3895     if ( NULL == pbb ) return pbb;
3896 
3897     memset( pbb, 0, sizeof( *pbb ) );
3898 
3899     pbb->tex_ref = INVALID_TX_TEXTURE;
3900     pbb->ichr    = ( CHR_REF )MAX_CHR;
3901 
3902     pbb->tint[RR] = pbb->tint[GG] = pbb->tint[BB] = pbb->tint[AA] = 1.0f;
3903     pbb->tint_add[AA] -= 1.0f / 100.0f;
3904 
3905     pbb->size = 1.0f;
3906     pbb->size_add -= 1.0f / 200.0f;
3907 
3908     pbb->offset_add[ZZ] += 127 / 50.0f * 2.0f;
3909 
3910     return pbb;
3911 }
3912 
3913 //--------------------------------------------------------------------------------------------
billboard_data_free(billboard_data_t * pbb)3914 bool_t billboard_data_free( billboard_data_t * pbb )
3915 {
3916     if ( NULL == pbb || !pbb->valid ) return bfalse;
3917 
3918     // free any allocated texture
3919     TxTexture_free_one( pbb->tex_ref );
3920 
3921     billboard_data_init( pbb );
3922 
3923     return btrue;
3924 }
3925 
3926 //--------------------------------------------------------------------------------------------
billboard_data_update(billboard_data_t * pbb)3927 bool_t billboard_data_update( billboard_data_t * pbb )
3928 {
3929     fvec3_t     vup, pos_new;
3930     chr_t     * pchr;
3931     float       height, offset;
3932 
3933     if ( NULL == pbb || !pbb->valid ) return bfalse;
3934 
3935     if ( !INGAME_CHR( pbb->ichr ) ) return bfalse;
3936     pchr = ChrList.lst + pbb->ichr;
3937 
3938     // determine where the new position should be
3939     chr_getMatUp( pchr, vup.v );
3940 
3941     height = pchr->bump.height;
3942     offset = MIN( pchr->bump.height * 0.5f, pchr->bump.size );
3943 
3944     pos_new.x = pchr->pos.x + vup.x * ( height + offset );
3945     pos_new.y = pchr->pos.y + vup.y * ( height + offset );
3946     pos_new.z = pchr->pos.z + vup.z * ( height + offset );
3947 
3948     // allow the billboards to be a bit bouncy
3949     pbb->pos.x = pbb->pos.x * 0.5f + pos_new.x * 0.5f;
3950     pbb->pos.y = pbb->pos.y * 0.5f + pos_new.y * 0.5f;
3951     pbb->pos.z = pbb->pos.z * 0.5f + pos_new.z * 0.5f;
3952 
3953     pbb->size += pbb->size_add;
3954 
3955     pbb->tint[RR] += pbb->tint_add[RR];
3956     pbb->tint[GG] += pbb->tint_add[GG];
3957     pbb->tint[BB] += pbb->tint_add[BB];
3958     pbb->tint[AA] += pbb->tint_add[AA];
3959 
3960     pbb->offset[XX] += pbb->offset_add[XX];
3961     pbb->offset[YY] += pbb->offset_add[YY];
3962     pbb->offset[ZZ] += pbb->offset_add[ZZ];
3963 
3964     // automatically kill a billboard that is no longer useful
3965     if ( pbb->tint[AA] == 0.0f || pbb->size == 0.0f )
3966     {
3967         billboard_data_free( pbb );
3968     }
3969 
3970     return btrue;
3971 }
3972 
3973 //--------------------------------------------------------------------------------------------
billboard_data_printf_ttf(billboard_data_t * pbb,Font * font,SDL_Color color,const char * format,...)3974 bool_t billboard_data_printf_ttf( billboard_data_t * pbb, Font *font, SDL_Color color, const char * format, ... )
3975 {
3976     va_list args;
3977     int rv;
3978     oglx_texture_t * ptex;
3979     float texCoords[4];
3980 
3981     if ( NULL == pbb || !pbb->valid ) return bfalse;
3982 
3983     // release any existing texture in case there is an error
3984     ptex = TxTexture_get_ptr( pbb->tex_ref );
3985     oglx_texture_Release( ptex );
3986 
3987     va_start( args, format );
3988     rv = fnt_vprintf( font, color, &( ptex->surface ), ptex->base.binding, texCoords, format, args );
3989     va_end( args );
3990 
3991     ptex->base_valid = bfalse;
3992     oglx_grab_texture_state( GL_TEXTURE_2D, 0, ptex );
3993 
3994     ptex->imgW  = ptex->surface->w;
3995     ptex->imgH  = ptex->surface->h;
3996     strncpy( ptex->name, "billboard text", SDL_arraysize( ptex->name ) );
3997 
3998     return ( rv >= 0 );
3999 }
4000 
4001 //--------------------------------------------------------------------------------------------
4002 // BILLBOARD IMPLEMENTATION
4003 //--------------------------------------------------------------------------------------------
BillboardList_clear_data()4004 void BillboardList_clear_data()
4005 {
4006     /// @details BB@> reset the free billboard list.
4007 
4008     int cnt;
4009 
4010     for ( cnt = 0; cnt < BILLBOARD_COUNT; cnt++ )
4011     {
4012         BillboardList.free_ref[cnt] = cnt;
4013     }
4014     BillboardList.free_count = cnt;
4015 }
4016 
4017 //--------------------------------------------------------------------------------------------
BillboardList_init_all()4018 void BillboardList_init_all()
4019 {
4020     BBOARD_REF cnt;
4021 
4022     for ( cnt = 0; cnt < BILLBOARD_COUNT; cnt++ )
4023     {
4024         billboard_data_init( BillboardList.lst + cnt );
4025     }
4026 
4027     BillboardList_clear_data();
4028 }
4029 
4030 //--------------------------------------------------------------------------------------------
BillboardList_update_all()4031 void BillboardList_update_all()
4032 {
4033     BBOARD_REF cnt;
4034     Uint32     ticks;
4035 
4036     ticks = egoboo_get_ticks();
4037 
4038     for ( cnt = 0; cnt < BILLBOARD_COUNT; cnt++ )
4039     {
4040         bool_t is_invalid;
4041 
4042         billboard_data_t * pbb = BillboardList.lst + cnt;
4043 
4044         if ( !pbb->valid ) continue;
4045 
4046         is_invalid = bfalse;
4047         if ( ticks >= pbb->time || NULL == TxTexture_get_ptr( pbb->tex_ref ) )
4048         {
4049             is_invalid = btrue;
4050         }
4051 
4052         if ( !INGAME_CHR( pbb->ichr ) || INGAME_CHR( ChrList.lst[pbb->ichr].attachedto ) )
4053         {
4054             is_invalid = btrue;
4055         }
4056 
4057         if ( is_invalid )
4058         {
4059             // the billboard has expired
4060 
4061             // unlink it from the character
4062             if ( INGAME_CHR( pbb->ichr ) )
4063             {
4064                 ChrList.lst[pbb->ichr].ibillboard = INVALID_BILLBOARD;
4065             }
4066 
4067             // deallocate the billboard
4068             BillboardList_free_one( REF_TO_INT( cnt ) );
4069         }
4070         else
4071         {
4072             billboard_data_update( BillboardList.lst + cnt );
4073         }
4074     }
4075 }
4076 
4077 //--------------------------------------------------------------------------------------------
BillboardList_free_all()4078 void BillboardList_free_all()
4079 {
4080     BBOARD_REF cnt;
4081 
4082     for ( cnt = 0; cnt < BILLBOARD_COUNT; cnt++ )
4083     {
4084         if ( !BillboardList.lst[cnt].valid ) continue;
4085 
4086         billboard_data_update( BillboardList.lst + cnt );
4087     }
4088 }
4089 
4090 //--------------------------------------------------------------------------------------------
BillboardList_get_free(Uint32 lifetime_secs)4091 size_t BillboardList_get_free( Uint32 lifetime_secs )
4092 {
4093     TX_REF             itex = ( TX_REF )INVALID_TX_TEXTURE;
4094     size_t             ibb  = INVALID_BILLBOARD;
4095     billboard_data_t * pbb  = NULL;
4096 
4097     if ( BillboardList.free_count <= 0 ) return INVALID_BILLBOARD;
4098 
4099     if ( 0 == lifetime_secs ) return INVALID_BILLBOARD;
4100 
4101     itex = TxTexture_get_free(( TX_REF )INVALID_TX_TEXTURE );
4102     if ( INVALID_TX_TEXTURE == itex ) return INVALID_BILLBOARD;
4103 
4104     // grab the top index
4105     BillboardList.free_count--;
4106     BillboardList.update_guid++;
4107 
4108     ibb = BillboardList.free_ref[BillboardList.free_count];
4109 
4110     if ( VALID_BILLBOARD_RANGE( ibb ) )
4111     {
4112         pbb = BillboardList.lst + ( BBOARD_REF )ibb;
4113 
4114         billboard_data_init( pbb );
4115 
4116         pbb->tex_ref = itex;
4117         pbb->time    = egoboo_get_ticks() + lifetime_secs * TICKS_PER_SEC;
4118         pbb->valid   = btrue;
4119     }
4120     else
4121     {
4122         // the billboard allocation returned an ivaild value
4123         // deallocate the texture
4124         TxTexture_free_one( itex );
4125 
4126         ibb = INVALID_BILLBOARD;
4127     }
4128 
4129     return ibb;
4130 }
4131 
4132 //--------------------------------------------------------------------------------------------
BillboardList_free_one(size_t ibb)4133 bool_t BillboardList_free_one( size_t ibb )
4134 {
4135     billboard_data_t * pbb;
4136 
4137     if ( !VALID_BILLBOARD_RANGE( ibb ) ) return bfalse;
4138     pbb = BillboardList.lst + ( BBOARD_REF )ibb;
4139 
4140     billboard_data_free( pbb );
4141 
4142 #if defined(_DEBUG)
4143     {
4144         int cnt;
4145         // determine whether this texture is already in the list of free textures
4146         // that is an error
4147         for ( cnt = 0; cnt < BillboardList.free_count; cnt++ )
4148         {
4149             if ( ibb == BillboardList.free_ref[cnt] ) return bfalse;
4150         }
4151     }
4152 #endif
4153 
4154     if ( BillboardList.free_count >= BILLBOARD_COUNT )
4155         return bfalse;
4156 
4157     // do not put anything below TX_LAST back onto the SDL_free stack
4158     BillboardList.free_ref[BillboardList.free_count] = ibb;
4159 
4160     BillboardList.free_count++;
4161     BillboardList.update_guid++;
4162 
4163     return btrue;
4164 }
4165 
4166 //--------------------------------------------------------------------------------------------
BillboardList_get_ptr(const BBOARD_REF ibb)4167 billboard_data_t * BillboardList_get_ptr( const BBOARD_REF  ibb )
4168 {
4169     if ( !VALID_BILLBOARD( ibb ) ) return NULL;
4170 
4171     return BillboardList.lst + ibb;
4172 }
4173 
4174 //--------------------------------------------------------------------------------------------
4175 //--------------------------------------------------------------------------------------------
render_one_billboard(billboard_data_t * pbb,float scale,const fvec3_base_t cam_up,const fvec3_base_t cam_rgt)4176 bool_t render_one_billboard( billboard_data_t * pbb, float scale, const fvec3_base_t cam_up, const fvec3_base_t cam_rgt )
4177 {
4178     int i;
4179     GLvertex vtlist[4];
4180     float x1, y1;
4181     float w, h;
4182     fvec3_t vec_up, vec_rgt;
4183 
4184     oglx_texture_t     * ptex;
4185     chr_t * pchr;
4186 
4187     if ( NULL == pbb || !pbb->valid ) return bfalse;
4188 
4189     if ( !INGAME_CHR( pbb->ichr ) ) return bfalse;
4190     pchr = ChrList.lst + pbb->ichr;
4191 
4192     // do not display for objects that are mounted or being held
4193     if ( pchr->pack.is_packed || DEFINED_CHR( pchr->attachedto ) ) return bfalse;
4194 
4195     ptex = TxTexture_get_ptr( pbb->tex_ref );
4196 
4197     oglx_texture_Bind( ptex );
4198 
4199     w = oglx_texture_GetImageWidth( ptex );
4200     h = oglx_texture_GetImageHeight( ptex );
4201 
4202     x1 = w  / ( float ) oglx_texture_GetTextureWidth( ptex );
4203     y1 = h  / ( float ) oglx_texture_GetTextureHeight( ptex );
4204 
4205     // @todo this billboard stuff needs to be implemented as a OpenGL transform
4206 
4207     // scale the camera vectors
4208     vec_rgt = fvec3_scale( cam_rgt, w * scale * pbb->size );
4209     vec_up  = fvec3_scale( cam_up, h * scale * pbb->size );
4210 
4211     // bottom left
4212     vtlist[0].pos[XX] = pbb->offset[XX] + pbb->pos.x + ( -vec_rgt.x - 0 * vec_up.x );
4213     vtlist[0].pos[YY] = pbb->offset[YY] + pbb->pos.y + ( -vec_rgt.y - 0 * vec_up.y );
4214     vtlist[0].pos[ZZ] = pbb->offset[ZZ] + pbb->pos.z + ( -vec_rgt.z - 0 * vec_up.z );
4215     vtlist[0].tex[SS] = x1;
4216     vtlist[0].tex[TT] = y1;
4217 
4218     // top left
4219     vtlist[1].pos[XX] = pbb->offset[XX] + pbb->pos.x + ( -vec_rgt.x + 2 * vec_up.x );
4220     vtlist[1].pos[YY] = pbb->offset[YY] + pbb->pos.y + ( -vec_rgt.y + 2 * vec_up.y );
4221     vtlist[1].pos[ZZ] = pbb->offset[ZZ] + pbb->pos.z + ( -vec_rgt.z + 2 * vec_up.z );
4222     vtlist[1].tex[SS] = x1;
4223     vtlist[1].tex[TT] = 0;
4224 
4225     // top right
4226     vtlist[2].pos[XX] = pbb->offset[XX] + pbb->pos.x + ( vec_rgt.x + 2 * vec_up.x );
4227     vtlist[2].pos[YY] = pbb->offset[YY] + pbb->pos.y + ( vec_rgt.y + 2 * vec_up.y );
4228     vtlist[2].pos[ZZ] = pbb->offset[ZZ] + pbb->pos.z + ( vec_rgt.z + 2 * vec_up.z );
4229     vtlist[2].tex[SS] = 0;
4230     vtlist[2].tex[TT] = 0;
4231 
4232     // bottom right
4233     vtlist[3].pos[XX] = pbb->offset[XX] + pbb->pos.x + ( vec_rgt.x - 0 * vec_up.x );
4234     vtlist[3].pos[YY] = pbb->offset[YY] + pbb->pos.y + ( vec_rgt.y - 0 * vec_up.y );
4235     vtlist[3].pos[ZZ] = pbb->offset[ZZ] + pbb->pos.z + ( vec_rgt.z - 0 * vec_up.z );
4236     vtlist[3].tex[SS] = 0;
4237     vtlist[3].tex[TT] = y1;
4238 
4239     ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
4240     {
4241         // Go on and draw it
4242         GL_DEBUG( glBegin )( GL_QUADS );
4243         {
4244             GL_DEBUG( glColor4fv )( pbb->tint );
4245 
4246             for ( i = 0; i < 4; i++ )
4247             {
4248                 GL_DEBUG( glTexCoord2fv )( vtlist[i].tex );
4249                 GL_DEBUG( glVertex3fv )( vtlist[i].pos );
4250             }
4251         }
4252         GL_DEBUG_END();
4253     }
4254     ATTRIB_POP( __FUNCTION__ );
4255 
4256     return btrue;
4257 }
4258 
4259 //--------------------------------------------------------------------------------------------
render_all_billboards(camera_t * pcam)4260 gfx_rv render_all_billboards( camera_t * pcam )
4261 {
4262     BBOARD_REF cnt;
4263 
4264     if ( NULL == pcam ) pcam = PCamera;
4265     if ( NULL == pcam )
4266     {
4267         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "cannot find a valid camera" );
4268         return gfx_error;
4269     }
4270 
4271     gfx_begin_3d( pcam );
4272     {
4273         fvec3_t cam_rgt, cam_up;
4274 
4275         cam_rgt.x =  pcam->mView.CNV( 0, 0 );
4276         cam_rgt.y =  pcam->mView.CNV( 1, 0 );
4277         cam_rgt.z =  pcam->mView.CNV( 2, 0 );
4278 
4279         cam_up.x    = -pcam->mView.CNV( 0, 1 );
4280         cam_up.y    = -pcam->mView.CNV( 1, 1 );
4281         cam_up.z    = -pcam->mView.CNV( 2, 1 );
4282 
4283         ATTRIB_PUSH( __FUNCTION__, GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT );
4284         {
4285             // don't write into the depth buffer (disable glDepthMask for transparent objects)
4286             GL_DEBUG( glDepthMask )( GL_FALSE );                   // GL_DEPTH_BUFFER_BIT
4287 
4288             // do not draw hidden surfaces
4289             GL_DEBUG( glEnable )( GL_DEPTH_TEST );                // GL_ENABLE_BIT
4290             GL_DEBUG( glDepthFunc )( GL_ALWAYS );                 // GL_DEPTH_BUFFER_BIT
4291 
4292             // flat shading
4293             GL_DEBUG( glShadeModel )( GL_FLAT );                                  // GL_LIGHTING_BIT
4294 
4295             // draw draw front and back faces of polygons
4296             GL_DEBUG( glDisable )( GL_CULL_FACE );                                // GL_ENABLE_BIT
4297 
4298             GL_DEBUG( glEnable )( GL_BLEND );                                     // GL_ENABLE_BIT
4299             GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );      // GL_COLOR_BUFFER_BIT
4300 
4301             GL_DEBUG( glEnable )( GL_ALPHA_TEST );                                // GL_ENABLE_BIT
4302             GL_DEBUG( glAlphaFunc )( GL_GREATER, 0.0f );                          // GL_COLOR_BUFFER_BIT
4303 
4304             GL_DEBUG( glColor4f )( 1.0f, 1.0f, 1.0f, 1.0f );
4305 
4306             for ( cnt = 0; cnt < BILLBOARD_COUNT; cnt++ )
4307             {
4308                 billboard_data_t * pbb = BillboardList.lst + cnt;
4309 
4310                 if ( !pbb->valid ) continue;
4311 
4312                 render_one_billboard( pbb, 0.75f, cam_up.v, cam_rgt.v );
4313             }
4314         }
4315         ATTRIB_POP( __FUNCTION__ );
4316     }
4317     gfx_end_3d();
4318 
4319     return gfx_success;
4320 }
4321 
4322 //--------------------------------------------------------------------------------------------
4323 // LINE IMPLENTATION
4324 //--------------------------------------------------------------------------------------------
get_free_line()4325 int get_free_line()
4326 {
4327     int cnt;
4328 
4329     for ( cnt = 0; cnt < LINE_COUNT; cnt++ )
4330     {
4331         if ( line_list[cnt].time < 0 )
4332         {
4333             break;
4334         }
4335     }
4336 
4337     return cnt < LINE_COUNT ? cnt : -1;
4338 }
4339 
4340 //--------------------------------------------------------------------------------------------
draw_all_lines(camera_t * pcam)4341 void draw_all_lines( camera_t * pcam )
4342 {
4343     /// @details BB@> draw some lines for debugging purposes
4344 
4345     int cnt, ticks;
4346 
4347     gfx_begin_3d( pcam );
4348     {
4349         ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT | GL_CURRENT_BIT );
4350         {
4351             // flat shading
4352             GL_DEBUG( glShadeModel )( GL_FLAT );     // GL_LIGHTING_BIT
4353 
4354             // don't write into the depth buffer (disable glDepthMask for transparent objects)
4355             GL_DEBUG( glDepthMask )( GL_FALSE );     // GL_DEPTH_BUFFER_BIT
4356 
4357             // do not draw hidden surfaces
4358             GL_DEBUG( glEnable )( GL_DEPTH_TEST );      // GL_ENABLE_BIT
4359             GL_DEBUG( glDepthFunc )( GL_LEQUAL );    // GL_DEPTH_BUFFER_BIT
4360 
4361             // draw draw front and back faces of polygons
4362             GL_DEBUG( glDisable )( GL_CULL_FACE );   // GL_ENABLE_BIT
4363 
4364             GL_DEBUG( glDisable )( GL_BLEND );       // GL_ENABLE_BIT
4365 
4366             // we do not want texture mapped lines
4367             GL_DEBUG( glDisable )( GL_TEXTURE_2D );  // GL_ENABLE_BIT
4368 
4369             ticks = egoboo_get_ticks();
4370 
4371             for ( cnt = 0; cnt < LINE_COUNT; cnt++ )
4372             {
4373                 if ( line_list[cnt].time < 0 ) continue;
4374 
4375                 if ( line_list[cnt].time < ticks )
4376                 {
4377                     line_list[cnt].time = -1;
4378                     continue;
4379                 }
4380 
4381                 GL_DEBUG( glColor4fv )( line_list[cnt].color.v );       // GL_CURRENT_BIT
4382                 GL_DEBUG( glBegin )( GL_LINES );
4383                 {
4384                     GL_DEBUG( glVertex3fv )( line_list[cnt].src.v );
4385                     GL_DEBUG( glVertex3fv )( line_list[cnt].dst.v );
4386                 }
4387                 GL_DEBUG_END();
4388             }
4389         }
4390         ATTRIB_POP( __FUNCTION__ );
4391     }
4392     gfx_end_3d();
4393 }
4394 
4395 //--------------------------------------------------------------------------------------------
4396 // AXIS BOUNDING BOX IMPLEMENTATION(S)
4397 //--------------------------------------------------------------------------------------------
render_aabb(aabb_t * pbbox)4398 bool_t render_aabb( aabb_t * pbbox )
4399 {
4400     GLXvector3f * pmin, * pmax;
4401     GLint matrix_mode[1];
4402 
4403     if ( NULL == pbbox ) return bfalse;
4404 
4405     // save the matrix mode
4406     GL_DEBUG( glGetIntegerv )( GL_MATRIX_MODE, matrix_mode );
4407 
4408     // store the GL_MODELVIEW matrix (this stack has a finite depth, minimum of 32)
4409     GL_DEBUG( glMatrixMode )( GL_MODELVIEW );
4410     GL_DEBUG( glPushMatrix )();
4411     {
4412         pmin = &( pbbox->mins );
4413         pmax = &( pbbox->maxs );
4414 
4415         // !!!! there must be an optimized way of doing this !!!!
4416 
4417         GL_DEBUG( glBegin )( GL_QUADS );
4418         {
4419             // Front Face
4420             GL_DEBUG( glVertex3f )(( *pmin )[XX], ( *pmin )[YY], ( *pmax )[ZZ] );
4421             GL_DEBUG( glVertex3f )(( *pmax )[XX], ( *pmin )[YY], ( *pmax )[ZZ] );
4422             GL_DEBUG( glVertex3f )(( *pmax )[XX], ( *pmax )[YY], ( *pmax )[ZZ] );
4423             GL_DEBUG( glVertex3f )(( *pmin )[XX], ( *pmax )[YY], ( *pmax )[ZZ] );
4424 
4425             // Back Face
4426             GL_DEBUG( glVertex3f )(( *pmin )[XX], ( *pmin )[YY], ( *pmin )[ZZ] );
4427             GL_DEBUG( glVertex3f )(( *pmin )[XX], ( *pmax )[YY], ( *pmin )[ZZ] );
4428             GL_DEBUG( glVertex3f )(( *pmax )[XX], ( *pmax )[YY], ( *pmin )[ZZ] );
4429             GL_DEBUG( glVertex3f )(( *pmax )[XX], ( *pmin )[YY], ( *pmin )[ZZ] );
4430 
4431             // Top Face
4432             GL_DEBUG( glVertex3f )(( *pmin )[XX], ( *pmax )[YY], ( *pmin )[ZZ] );
4433             GL_DEBUG( glVertex3f )(( *pmin )[XX], ( *pmax )[YY], ( *pmax )[ZZ] );
4434             GL_DEBUG( glVertex3f )(( *pmax )[XX], ( *pmax )[YY], ( *pmax )[ZZ] );
4435             GL_DEBUG( glVertex3f )(( *pmax )[XX], ( *pmax )[YY], ( *pmin )[ZZ] );
4436 
4437             // Bottom Face
4438             GL_DEBUG( glVertex3f )(( *pmin )[XX], ( *pmin )[YY], ( *pmin )[ZZ] );
4439             GL_DEBUG( glVertex3f )(( *pmax )[XX], ( *pmin )[YY], ( *pmin )[ZZ] );
4440             GL_DEBUG( glVertex3f )(( *pmax )[XX], ( *pmin )[YY], ( *pmax )[ZZ] );
4441             GL_DEBUG( glVertex3f )(( *pmin )[XX], ( *pmin )[YY], ( *pmax )[ZZ] );
4442 
4443             // Right face
4444             GL_DEBUG( glVertex3f )(( *pmax )[XX], ( *pmin )[YY], ( *pmin )[ZZ] );
4445             GL_DEBUG( glVertex3f )(( *pmax )[XX], ( *pmax )[YY], ( *pmin )[ZZ] );
4446             GL_DEBUG( glVertex3f )(( *pmax )[XX], ( *pmax )[YY], ( *pmax )[ZZ] );
4447             GL_DEBUG( glVertex3f )(( *pmax )[XX], ( *pmin )[YY], ( *pmax )[ZZ] );
4448 
4449             // Left Face
4450             GL_DEBUG( glVertex3f )(( *pmin )[XX], ( *pmin )[YY], ( *pmin )[ZZ] );
4451             GL_DEBUG( glVertex3f )(( *pmin )[XX], ( *pmin )[YY], ( *pmax )[ZZ] );
4452             GL_DEBUG( glVertex3f )(( *pmin )[XX], ( *pmax )[YY], ( *pmax )[ZZ] );
4453             GL_DEBUG( glVertex3f )(( *pmin )[XX], ( *pmax )[YY], ( *pmin )[ZZ] );
4454         }
4455         GL_DEBUG_END();
4456     }
4457     // Restore the GL_MODELVIEW matrix
4458     GL_DEBUG( glMatrixMode )( GL_MODELVIEW );
4459     GL_DEBUG( glPopMatrix )();
4460 
4461     // restore the matrix mode
4462     GL_DEBUG( glMatrixMode )( matrix_mode[0] );
4463 
4464     return btrue;
4465 }
4466 
4467 //--------------------------------------------------------------------------------------------
render_oct_bb(oct_bb_t * bb,bool_t draw_square,bool_t draw_diamond)4468 bool_t render_oct_bb( oct_bb_t * bb, bool_t draw_square, bool_t draw_diamond )
4469 {
4470     bool_t retval = bfalse;
4471 
4472     if ( NULL == bb ) return bfalse;
4473 
4474     ATTRIB_PUSH( __FUNCTION__, GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_DEPTH_BUFFER_BIT );
4475     {
4476         // don't write into the depth buffer (disable glDepthMask for transparent objects)
4477         GL_DEBUG( glDepthMask )( GL_FALSE );
4478 
4479         // do not draw hidden surfaces
4480         GL_DEBUG( glEnable )( GL_DEPTH_TEST );      // GL_ENABLE_BIT
4481         GL_DEBUG( glDepthFunc )( GL_LEQUAL );
4482 
4483         // fix the poorly chosen normals...
4484         // draw draw front and back faces of polygons
4485         GL_DEBUG( glDisable )( GL_CULL_FACE );
4486 
4487         // make them transparent
4488         GL_DEBUG( glEnable )( GL_BLEND );
4489         GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
4490 
4491         // choose a "white" texture
4492         oglx_texture_Bind( NULL );
4493 
4494         //------------------------------------------------
4495         // DIAGONAL BBOX
4496         if ( draw_diamond )
4497         {
4498             float p1_x, p1_y;
4499             float p2_x, p2_y;
4500 
4501             GL_DEBUG( glColor4f )( 0.5f, 1.0f, 1.0f, 0.1f );
4502 
4503             p1_x = 0.5f * ( bb->maxs[OCT_XY] - bb->maxs[OCT_YX] );
4504             p1_y = 0.5f * ( bb->maxs[OCT_XY] + bb->maxs[OCT_YX] );
4505             p2_x = 0.5f * ( bb->maxs[OCT_XY] - bb->mins[OCT_YX] );
4506             p2_y = 0.5f * ( bb->maxs[OCT_XY] + bb->mins[OCT_YX] );
4507 
4508             GL_DEBUG( glBegin )( GL_QUADS );
4509             GL_DEBUG( glVertex3f )( p1_x, p1_y, bb->mins[OCT_Z] );
4510             GL_DEBUG( glVertex3f )( p2_x, p2_y, bb->mins[OCT_Z] );
4511             GL_DEBUG( glVertex3f )( p2_x, p2_y, bb->maxs[OCT_Z] );
4512             GL_DEBUG( glVertex3f )( p1_x, p1_y, bb->maxs[OCT_Z] );
4513             GL_DEBUG_END();
4514 
4515             p1_x = 0.5f * ( bb->maxs[OCT_XY] - bb->mins[OCT_YX] );
4516             p1_y = 0.5f * ( bb->maxs[OCT_XY] + bb->mins[OCT_YX] );
4517             p2_x = 0.5f * ( bb->mins[OCT_XY] - bb->mins[OCT_YX] );
4518             p2_y = 0.5f * ( bb->mins[OCT_XY] + bb->mins[OCT_YX] );
4519 
4520             GL_DEBUG( glBegin )( GL_QUADS );
4521             GL_DEBUG( glVertex3f )( p1_x, p1_y, bb->mins[OCT_Z] );
4522             GL_DEBUG( glVertex3f )( p2_x, p2_y, bb->mins[OCT_Z] );
4523             GL_DEBUG( glVertex3f )( p2_x, p2_y, bb->maxs[OCT_Z] );
4524             GL_DEBUG( glVertex3f )( p1_x, p1_y, bb->maxs[OCT_Z] );
4525             GL_DEBUG_END();
4526 
4527             p1_x = 0.5f * ( bb->mins[OCT_XY] - bb->mins[OCT_YX] );
4528             p1_y = 0.5f * ( bb->mins[OCT_XY] + bb->mins[OCT_YX] );
4529             p2_x = 0.5f * ( bb->mins[OCT_XY] - bb->maxs[OCT_YX] );
4530             p2_y = 0.5f * ( bb->mins[OCT_XY] + bb->maxs[OCT_YX] );
4531 
4532             GL_DEBUG( glBegin )( GL_QUADS );
4533             GL_DEBUG( glVertex3f )( p1_x, p1_y, bb->mins[OCT_Z] );
4534             GL_DEBUG( glVertex3f )( p2_x, p2_y, bb->mins[OCT_Z] );
4535             GL_DEBUG( glVertex3f )( p2_x, p2_y, bb->maxs[OCT_Z] );
4536             GL_DEBUG( glVertex3f )( p1_x, p1_y, bb->maxs[OCT_Z] );
4537             GL_DEBUG_END();
4538 
4539             p1_x = 0.5f * ( bb->mins[OCT_XY] - bb->maxs[OCT_YX] );
4540             p1_y = 0.5f * ( bb->mins[OCT_XY] + bb->maxs[OCT_YX] );
4541             p2_x = 0.5f * ( bb->maxs[OCT_XY] - bb->maxs[OCT_YX] );
4542             p2_y = 0.5f * ( bb->maxs[OCT_XY] + bb->maxs[OCT_YX] );
4543 
4544             GL_DEBUG( glBegin )( GL_QUADS );
4545             GL_DEBUG( glVertex3f )( p1_x, p1_y, bb->mins[OCT_Z] );
4546             GL_DEBUG( glVertex3f )( p2_x, p2_y, bb->mins[OCT_Z] );
4547             GL_DEBUG( glVertex3f )( p2_x, p2_y, bb->maxs[OCT_Z] );
4548             GL_DEBUG( glVertex3f )( p1_x, p1_y, bb->maxs[OCT_Z] );
4549             GL_DEBUG_END();
4550 
4551             retval = btrue;
4552         }
4553 
4554         //------------------------------------------------
4555         // SQUARE BBOX
4556         if ( draw_square )
4557         {
4558             GL_DEBUG( glColor4f )( 1.0f, 0.5f, 1.0f, 0.1f );
4559 
4560             // XZ FACE, min Y
4561             GL_DEBUG( glBegin )( GL_QUADS );
4562             GL_DEBUG( glVertex3f )( bb->mins[OCT_X], bb->mins[OCT_Y], bb->mins[OCT_Z] );
4563             GL_DEBUG( glVertex3f )( bb->mins[OCT_X], bb->mins[OCT_Y], bb->maxs[OCT_Z] );
4564             GL_DEBUG( glVertex3f )( bb->maxs[OCT_X], bb->mins[OCT_Y], bb->maxs[OCT_Z] );
4565             GL_DEBUG( glVertex3f )( bb->maxs[OCT_X], bb->mins[OCT_Y], bb->mins[OCT_Z] );
4566             GL_DEBUG_END();
4567 
4568             // YZ FACE, min X
4569             GL_DEBUG( glBegin )( GL_QUADS );
4570             GL_DEBUG( glVertex3f )( bb->mins[OCT_X], bb->mins[OCT_Y], bb->mins[OCT_Z] );
4571             GL_DEBUG( glVertex3f )( bb->mins[OCT_X], bb->mins[OCT_Y], bb->maxs[OCT_Z] );
4572             GL_DEBUG( glVertex3f )( bb->mins[OCT_X], bb->maxs[OCT_Y], bb->maxs[OCT_Z] );
4573             GL_DEBUG( glVertex3f )( bb->mins[OCT_X], bb->maxs[OCT_Y], bb->mins[OCT_Z] );
4574             GL_DEBUG_END();
4575 
4576             // XZ FACE, max Y
4577             GL_DEBUG( glBegin )( GL_QUADS );
4578             GL_DEBUG( glVertex3f )( bb->mins[OCT_X], bb->maxs[OCT_Y], bb->mins[OCT_Z] );
4579             GL_DEBUG( glVertex3f )( bb->mins[OCT_X], bb->maxs[OCT_Y], bb->maxs[OCT_Z] );
4580             GL_DEBUG( glVertex3f )( bb->maxs[OCT_X], bb->maxs[OCT_Y], bb->maxs[OCT_Z] );
4581             GL_DEBUG( glVertex3f )( bb->maxs[OCT_X], bb->maxs[OCT_Y], bb->mins[OCT_Z] );
4582             GL_DEBUG_END();
4583 
4584             // YZ FACE, max X
4585             GL_DEBUG( glBegin )( GL_QUADS );
4586             GL_DEBUG( glVertex3f )( bb->maxs[OCT_X], bb->mins[OCT_Y], bb->mins[OCT_Z] );
4587             GL_DEBUG( glVertex3f )( bb->maxs[OCT_X], bb->mins[OCT_Y], bb->maxs[OCT_Z] );
4588             GL_DEBUG( glVertex3f )( bb->maxs[OCT_X], bb->maxs[OCT_Y], bb->maxs[OCT_Z] );
4589             GL_DEBUG( glVertex3f )( bb->maxs[OCT_X], bb->maxs[OCT_Y], bb->mins[OCT_Z] );
4590             GL_DEBUG_END();
4591 
4592             // XY FACE, min Z
4593             GL_DEBUG( glBegin )( GL_QUADS );
4594             GL_DEBUG( glVertex3f )( bb->mins[OCT_X], bb->mins[OCT_Y], bb->mins[OCT_Z] );
4595             GL_DEBUG( glVertex3f )( bb->mins[OCT_X], bb->maxs[OCT_Y], bb->mins[OCT_Z] );
4596             GL_DEBUG( glVertex3f )( bb->maxs[OCT_X], bb->maxs[OCT_Y], bb->mins[OCT_Z] );
4597             GL_DEBUG( glVertex3f )( bb->maxs[OCT_X], bb->mins[OCT_Y], bb->mins[OCT_Z] );
4598             GL_DEBUG_END();
4599 
4600             // XY FACE, max Z
4601             GL_DEBUG( glBegin )( GL_QUADS );
4602             GL_DEBUG( glVertex3f )( bb->mins[OCT_X], bb->mins[OCT_Y], bb->maxs[OCT_Z] );
4603             GL_DEBUG( glVertex3f )( bb->mins[OCT_X], bb->maxs[OCT_Y], bb->maxs[OCT_Z] );
4604             GL_DEBUG( glVertex3f )( bb->maxs[OCT_X], bb->maxs[OCT_Y], bb->maxs[OCT_Z] );
4605             GL_DEBUG( glVertex3f )( bb->maxs[OCT_X], bb->mins[OCT_Y], bb->maxs[OCT_Z] );
4606             GL_DEBUG_END();
4607 
4608             retval = btrue;
4609         }
4610 
4611     }
4612     ATTRIB_POP( __FUNCTION__ );
4613 
4614     return retval;
4615 }
4616 
4617 //--------------------------------------------------------------------------------------------
4618 // GRAPHICS OPTIMIZATIONS
4619 //--------------------------------------------------------------------------------------------
dolist_add_chr(dolist_t * pdolist,ego_mpd_t * pmesh,const CHR_REF ichr)4620 gfx_rv dolist_add_chr( dolist_t * pdolist, ego_mpd_t * pmesh, const CHR_REF ichr )
4621 {
4622     /// ZZ@> This function puts a character in the list
4623 
4624     chr_t * pchr;
4625     cap_t * pcap;
4626     chr_instance_t * pinst;
4627     ego_tile_info_t * ptile;
4628 
4629     // if we are adding the "item" in an empty hand, don't complain
4630     if ( !VALID_CHR_RANGE( ichr ) ) return gfx_fail;
4631 
4632     if ( NULL == pdolist )
4633     {
4634         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL dolist" );
4635         return gfx_error;
4636     }
4637 
4638     if ( pdolist->count >= DOLIST_SIZE )
4639     {
4640         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "invalid dolist size" );
4641         return gfx_error;
4642     }
4643 
4644     if ( NULL == pmesh ) pmesh = PMesh;
4645     if ( NULL == pmesh )
4646     {
4647         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "cannot find a valid mesh" );
4648         return gfx_error;
4649     }
4650 
4651     if ( !INGAME_CHR( ichr ) )
4652     {
4653         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, ichr, "invalid character index" );
4654         return gfx_error;
4655     }
4656 
4657     pchr  = ChrList.lst + ichr;
4658     pinst = &( pchr->inst );
4659 
4660     if ( pinst->indolist ) return gfx_success;
4661 
4662     if ( pchr->is_hidden ) return gfx_fail;
4663 
4664     if ( !mesh_grid_is_valid( pmesh, pchr->onwhichgrid ) ) return gfx_fail;
4665     ptile = pmesh->tmem.tile_list + pchr->onwhichgrid;
4666 
4667     pcap = chr_get_pcap( ichr );
4668     if ( NULL == pcap )
4669     {
4670         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, ichr, "invalid character cap" );
4671         return gfx_error;
4672     }
4673 
4674     if ( ptile->inrenderlist )
4675     {
4676         pdolist->lst[pdolist->count].ichr = ichr;
4677         pdolist->lst[pdolist->count].iprt = ( PRT_REF )MAX_PRT;
4678         pdolist->count++;
4679 
4680         pinst->indolist = btrue;
4681     }
4682     else if ( pcap->alwaysdraw )
4683     {
4684         // Double check for large/special objects
4685 
4686         pdolist->lst[pdolist->count].ichr = ichr;
4687         pdolist->lst[pdolist->count].iprt = ( PRT_REF )MAX_PRT;
4688         pdolist->count++;
4689 
4690         pinst->indolist = btrue;
4691     }
4692 
4693     if ( pinst->indolist )
4694     {
4695         // Add its weapons too
4696         dolist_add_chr( pdolist, pmesh, pchr->holdingwhich[SLOT_LEFT] );
4697         dolist_add_chr( pdolist, pmesh, pchr->holdingwhich[SLOT_RIGHT] );
4698     }
4699 
4700     return pinst->indolist ? gfx_success : gfx_fail;
4701 }
4702 
4703 //--------------------------------------------------------------------------------------------
dolist_add_prt(dolist_t * pdolist,ego_mpd_t * pmesh,const PRT_REF iprt)4704 gfx_rv dolist_add_prt( dolist_t * pdolist, ego_mpd_t * pmesh, const PRT_REF iprt )
4705 {
4706     /// ZZ@> This function puts a character in the list
4707     prt_t * pprt;
4708     prt_instance_t * pinst;
4709     ego_tile_info_t * ptile;
4710 
4711     if ( NULL == pdolist )
4712     {
4713         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL dolist" );
4714         return gfx_error;
4715     }
4716 
4717     if ( pdolist->count >= DOLIST_SIZE )
4718     {
4719         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "invalid dolist size" );
4720         return gfx_error;
4721     }
4722 
4723     if ( !DISPLAY_PRT( iprt ) )
4724     {
4725         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, iprt, "invalid particle index" );
4726         return gfx_error;
4727     }
4728 
4729     pprt = PrtList.lst + iprt;
4730     pinst = &( pprt->inst );
4731 
4732     if ( pinst->indolist ) return gfx_success;
4733 
4734     if ( pprt->is_hidden || 0 == pprt->size ) return gfx_fail;
4735 
4736     if ( !mesh_grid_is_valid( pmesh, pprt->onwhichgrid ) ) return gfx_fail;
4737     ptile = pmesh->tmem.tile_list + pprt->onwhichgrid;
4738 
4739     if ( ptile->inrenderlist )
4740     {
4741         pdolist->lst[pdolist->count].ichr = ( CHR_REF )MAX_CHR;
4742         pdolist->lst[pdolist->count].iprt = iprt;
4743         pdolist->count++;
4744 
4745         pinst->indolist = btrue;
4746     }
4747 
4748     return pinst->indolist ? gfx_success : gfx_fail;
4749 }
4750 
4751 //--------------------------------------------------------------------------------------------
dolist_make(dolist_t * pdolist,ego_mpd_t * pmesh)4752 gfx_rv dolist_make( dolist_t * pdolist, ego_mpd_t * pmesh )
4753 {
4754     /// @details ZZ@> This function finds the characters that need to be drawn and puts them in the list
4755 
4756     int cnt;
4757     CHR_REF ichr;
4758     gfx_rv retval;
4759 
4760     if ( NULL == pdolist )
4761     {
4762         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL dolist" );
4763         return gfx_error;
4764     }
4765 
4766     if ( pdolist->count >= DOLIST_SIZE )
4767     {
4768         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "invalid dolist size" );
4769         return gfx_error;
4770     }
4771 
4772     // Remove everyone from the dolist
4773     for ( cnt = 0; cnt < pdolist->count; cnt++ )
4774     {
4775         if ( MAX_PRT == pdolist->lst[cnt].iprt && VALID_CHR_RANGE( pdolist->lst[cnt].ichr ) )
4776         {
4777             ChrList.lst[ pdolist->lst[cnt].ichr ].inst.indolist = bfalse;
4778         }
4779         else if ( MAX_CHR == pdolist->lst[cnt].ichr && VALID_PRT_RANGE( pdolist->lst[cnt].iprt ) )
4780         {
4781             PrtList.lst[ pdolist->lst[cnt].iprt ].inst.indolist = bfalse;
4782         }
4783     }
4784     pdolist->count = 0;
4785 
4786     // assume the best
4787     retval = gfx_success;
4788 
4789     // Now fill it up again
4790     for ( ichr = 0; ichr < MAX_CHR; ichr++ )
4791     {
4792         if ( INGAME_CHR( ichr ) && !ChrList.lst[ichr].pack.is_packed )
4793         {
4794             // Add the character
4795             if ( gfx_error == dolist_add_chr( pdolist, pmesh, ichr ) )
4796             {
4797                 retval = gfx_error;
4798             }
4799         }
4800     }
4801 
4802     PRT_BEGIN_LOOP_DISPLAY( iprt, prt_bdl )
4803     {
4804         if ( mesh_grid_is_valid( pmesh, prt_bdl.prt_ptr->onwhichgrid ) )
4805         {
4806             // Add the character
4807             if ( gfx_error == dolist_add_prt( pdolist, pmesh, prt_bdl.prt_ref ) )
4808             {
4809                 retval = gfx_error;
4810             }
4811         }
4812     }
4813     PRT_END_LOOP();
4814 
4815     return retval;
4816 }
4817 
4818 //--------------------------------------------------------------------------------------------
dolist_sort(dolist_t * pdolist,camera_t * pcam,bool_t do_reflect)4819 gfx_rv dolist_sort( dolist_t * pdolist, camera_t * pcam, bool_t do_reflect )
4820 {
4821     /// @details ZZ@> This function orders the dolist based on distance from camera,
4822     ///    which is needed for reflections to properly clip themselves.
4823     ///    Order from closest to farthest
4824 
4825     Uint32    cnt;
4826     fvec3_t   vcam;
4827     size_t    count;
4828 
4829     if ( NULL == pdolist )
4830     {
4831         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL dolist" );
4832         return gfx_error;
4833     }
4834 
4835     if ( pdolist->count >= DOLIST_SIZE )
4836     {
4837         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "invalid dolist size" );
4838         return gfx_error;
4839     }
4840 
4841     if ( NULL == pcam ) pcam = PCamera;
4842     if ( NULL == pcam )
4843     {
4844         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "cannot find a valid camera" );
4845         return gfx_error;
4846     }
4847 
4848     mat_getCamForward( pcam->mView.v, vcam.v );
4849 
4850     // Figure the distance of each
4851     count = 0;
4852     for ( cnt = 0; cnt < pdolist->count; cnt++ )
4853     {
4854         fvec3_t   vtmp;
4855         float dist;
4856 
4857         if ( MAX_PRT == pdolist->lst[cnt].iprt && VALID_CHR_RANGE( pdolist->lst[cnt].ichr ) )
4858         {
4859             CHR_REF ichr;
4860             fvec3_t pos_tmp;
4861 
4862             ichr = pdolist->lst[cnt].ichr;
4863 
4864             if ( do_reflect )
4865             {
4866                 mat_getTranslate( ChrList.lst[ichr].inst.ref.matrix.v, pos_tmp.v );
4867             }
4868             else
4869             {
4870                 mat_getTranslate( ChrList.lst[ichr].inst.matrix.v, pos_tmp.v );
4871             }
4872 
4873             vtmp = fvec3_sub( pos_tmp.v, pcam->pos.v );
4874         }
4875         else if ( MAX_CHR == pdolist->lst[cnt].ichr && VALID_PRT_RANGE( pdolist->lst[cnt].iprt ) )
4876         {
4877             PRT_REF iprt = pdolist->lst[cnt].iprt;
4878 
4879             if ( do_reflect )
4880             {
4881                 vtmp = fvec3_sub( PrtList.lst[iprt].inst.pos.v, pcam->pos.v );
4882             }
4883             else
4884             {
4885                 vtmp = fvec3_sub( PrtList.lst[iprt].inst.ref_pos.v, pcam->pos.v );
4886             }
4887         }
4888         else
4889         {
4890             continue;
4891         }
4892 
4893         dist = fvec3_dot_product( vtmp.v, vcam.v );
4894         if ( dist > 0 )
4895         {
4896             pdolist->lst[count].ichr = pdolist->lst[cnt].ichr;
4897             pdolist->lst[count].iprt = pdolist->lst[cnt].iprt;
4898             pdolist->lst[count].dist = dist;
4899             count++;
4900         }
4901     }
4902     pdolist->count = count;
4903 
4904     // use qsort to sort the list in-place
4905     if ( pdolist->count > 1 )
4906     {
4907         qsort( pdolist->lst, pdolist->count, sizeof( obj_registry_entity_t ), obj_registry_entity_cmp );
4908     }
4909 
4910     return gfx_success;
4911 }
4912 
4913 //--------------------------------------------------------------------------------------------
4914 //--------------------------------------------------------------------------------------------
obj_registry_entity_cmp(const void * pleft,const void * pright)4915 int obj_registry_entity_cmp( const void * pleft, const void * pright )
4916 {
4917     obj_registry_entity_t * dleft  = ( obj_registry_entity_t * ) pleft;
4918     obj_registry_entity_t * dright = ( obj_registry_entity_t * ) pright;
4919 
4920     int   rv;
4921     float diff;
4922 
4923     diff = dleft->dist - dright->dist;
4924 
4925     if ( diff < 0.0f )
4926     {
4927         rv = -1;
4928     }
4929     else if ( diff > 0.0f )
4930     {
4931         rv = 1;
4932     }
4933     else
4934     {
4935         rv = 0;
4936     }
4937 
4938     return rv;
4939 }
4940 
4941 //--------------------------------------------------------------------------------------------
renderlist_reset(renderlist_t * prlist)4942 gfx_rv renderlist_reset( renderlist_t * prlist )
4943 {
4944     /// @details BB@> Clear old render lists
4945 
4946     if ( NULL == prlist )
4947     {
4948         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL renderlist" );
4949         return gfx_error;
4950     }
4951 
4952     if ( NULL != prlist->pmesh )
4953     {
4954         int cnt;
4955 
4956         // clear out the inrenderlist flag for the old mesh
4957         ego_tile_info_t * tlist = prlist->pmesh->tmem.tile_list;
4958 
4959         for ( cnt = 0; cnt < prlist->all_count; cnt++ )
4960         {
4961             Uint32 fan = prlist->all[cnt];
4962             if ( fan < prlist->pmesh->info.tiles_count )
4963             {
4964                 tlist[fan].inrenderlist       = bfalse;
4965                 tlist[fan].inrenderlist_frame = 0;
4966             }
4967         }
4968 
4969         prlist->pmesh = NULL;
4970     }
4971 
4972     prlist->all_count = 0;
4973     prlist->ref_count = 0;
4974     prlist->sha_count = 0;
4975     prlist->drf_count = 0;
4976     prlist->ndr_count = 0;
4977     prlist->wat_count = 0;
4978 
4979     return gfx_success;
4980 }
4981 
4982 //--------------------------------------------------------------------------------------------
renderlist_make(renderlist_t * prlist,ego_mpd_t * pmesh,camera_t * pcam)4983 gfx_rv renderlist_make( renderlist_t * prlist, ego_mpd_t * pmesh, camera_t * pcam )
4984 {
4985     /// @details ZZ@> This function figures out which mesh fans to draw
4986 
4987     int cnt, grid_x, grid_y;
4988     int row, run, numrow;
4989     int corner_x[4], corner_y[4];
4990     int leftnum, leftlist[4];
4991     int rightnum, rightlist[4];
4992     int rowstt[128], rowend[128];
4993     int x, stepx, divx, basex;
4994     int from, to;
4995     gfx_rv retval;
4996 
4997     ego_tile_info_t * tlist;
4998 
4999     // Make sure it doesn't die ugly
5000     if ( NULL == prlist )
5001     {
5002         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL renderlist" );
5003         return gfx_error;
5004     }
5005 
5006     if ( NULL == pcam ) pcam = PCamera;
5007     if ( NULL == pcam )
5008     {
5009         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "cannot find a valid camera" );
5010         return gfx_error;
5011     }
5012 
5013     if ( NULL == pmesh ) pmesh = PMesh;
5014     if ( NULL == pmesh )
5015     {
5016         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "cannot find a valid mesh" );
5017         return gfx_error;
5018     }
5019 
5020     // because the main loop of the program will always flip the
5021     // page before rendering the 1st frame of the actual game,
5022     // game_frame_all will always start at 1
5023     if ( 1 != ( game_frame_all & 3 ) ) return gfx_success;
5024 
5025     // Find the render area corners.
5026     // if this fails, we cannot have a valid list of corners, so this function fails
5027     if ( rv_error == gfx_project_cam_view( pcam ) )
5028     {
5029         return gfx_error;
5030     }
5031 
5032     // assume the best
5033     retval = gfx_success;
5034 
5035     // reset the renderlist
5036     if ( gfx_error == renderlist_reset( prlist ) )
5037     {
5038         retval = gfx_error;
5039     }
5040 
5041     prlist->pmesh = pmesh;
5042     tlist = pmesh->tmem.tile_list;
5043 
5044     // It works better this way...
5045     cornery[cornerlistlowtohighy[3]] += 256;
5046 
5047     // Make life simpler
5048     for ( cnt = 0; cnt < 4; cnt++ )
5049     {
5050         corner_x[cnt] = cornerx[cornerlistlowtohighy[cnt]];
5051         corner_y[cnt] = cornery[cornerlistlowtohighy[cnt]];
5052     }
5053 
5054     // Find the center line
5055     divx = corner_y[3] - corner_y[0];
5056     if ( divx < 1 )
5057     {
5058         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "divx < 1" );
5059         return gfx_error;
5060     }
5061     stepx = corner_x[3] - corner_x[0];
5062     basex = corner_x[0];
5063 
5064     // Find the points in each edge
5065     leftlist[0] = 0;  leftnum = 1;
5066     rightlist[0] = 0;  rightnum = 1;
5067     if ( corner_x[1] < ( stepx*( corner_y[1] - corner_y[0] ) / divx ) + basex )
5068     {
5069         leftlist[leftnum] = 1;  leftnum++;
5070         cornerx[1] -= 512;
5071     }
5072     else
5073     {
5074         rightlist[rightnum] = 1;  rightnum++;
5075         cornerx[1] += 512;
5076     }
5077     if ( corner_x[2] < ( stepx*( corner_y[2] - corner_y[0] ) / divx ) + basex )
5078     {
5079         leftlist[leftnum] = 2;  leftnum++;
5080         cornerx[2] -= 512;
5081     }
5082     else
5083     {
5084         rightlist[rightnum] = 2;  rightnum++;
5085         cornerx[2] += 512;
5086     }
5087 
5088     leftlist[leftnum] = 3;  leftnum++;
5089     rightlist[rightnum] = 3;  rightnum++;
5090 
5091     // Make the left edge ( rowstt )
5092     grid_y = corner_y[0] >> GRID_BITS;
5093     row = 0;
5094     cnt = 1;
5095     while ( cnt < leftnum )
5096     {
5097         from = leftlist[cnt-1];  to = leftlist[cnt];
5098         x = corner_x[from];
5099         divx = corner_y[to] - corner_y[from];
5100         stepx = 0;
5101         if ( divx > 0 )
5102         {
5103             stepx = (( corner_x[to] - corner_x[from] ) << GRID_BITS ) / divx;
5104         }
5105 
5106         x -= 256;
5107         run = corner_y[to] >> GRID_BITS;
5108         while ( grid_y < run )
5109         {
5110             if ( grid_y >= 0 && grid_y < pmesh->info.tiles_y )
5111             {
5112                 grid_x = x >> GRID_BITS;
5113                 if ( grid_x < 0 )  grid_x = 0;
5114                 if ( grid_x >= pmesh->info.tiles_x )  grid_x = pmesh->info.tiles_x - 1;
5115 
5116                 rowstt[row] = grid_x;
5117                 row++;
5118             }
5119 
5120             x += stepx;
5121             grid_y++;
5122         }
5123 
5124         cnt++;
5125     }
5126     numrow = row;
5127 
5128     // Make the right edge ( rowrun )
5129     grid_y = corner_y[0] >> GRID_BITS;
5130     row = 0;
5131     cnt = 1;
5132     while ( cnt < rightnum )
5133     {
5134         from = rightlist[cnt-1];  to = rightlist[cnt];
5135         x = corner_x[from];
5136         x += 128;
5137         divx = corner_y[to] - corner_y[from];
5138         stepx = 0;
5139         if ( divx > 0 )
5140         {
5141             stepx = (( corner_x[to] - corner_x[from] ) << GRID_BITS ) / divx;
5142         }
5143 
5144         run = corner_y[to] >> GRID_BITS;
5145 
5146         while ( grid_y < run )
5147         {
5148             if ( grid_y >= 0 && grid_y < pmesh->info.tiles_y )
5149             {
5150                 grid_x = x >> GRID_BITS;
5151                 if ( grid_x < 0 )  grid_x = 0;
5152                 if ( grid_x >= pmesh->info.tiles_x - 1 )  grid_x = pmesh->info.tiles_x - 1;// -2
5153 
5154                 rowend[row] = grid_x;
5155                 row++;
5156             }
5157 
5158             x += stepx;
5159             grid_y++;
5160         }
5161 
5162         cnt++;
5163     }
5164 
5165     if ( numrow != row )
5166     {
5167         log_error( "ROW error (%i, %i)\n", numrow, row );
5168         return gfx_fail;
5169     }
5170 
5171     // fill the renderlist from the projected view
5172     grid_y = corner_y[0] / GRID_ISIZE;
5173     grid_y = CLIP( grid_y, 0, pmesh->info.tiles_y - 1 );
5174     for ( row = 0; row < numrow; row++, grid_y++ )
5175     {
5176         for ( grid_x = rowstt[row]; grid_x <= rowend[row] && prlist->all_count < MAXMESHRENDER; grid_x++ )
5177         {
5178             cnt = pmesh->gmem.tilestart[grid_y] + grid_x;
5179 
5180             // Flag the tile as in the renderlist
5181             tlist[cnt].inrenderlist       = btrue;
5182 
5183             // if the tile was not in the renderlist last frame, then we need to force a lighting update of this tile
5184             if ( tlist[cnt].inrenderlist_frame < game_frame_all - 1 )
5185             {
5186                 tlist[cnt].needs_lighting_update = btrue;
5187             }
5188 
5189             // make sure to cache the frame number of this update
5190             tlist[cnt].inrenderlist_frame = game_frame_all;
5191 
5192             // Put each tile in basic list
5193             prlist->all[prlist->all_count] = cnt;
5194             prlist->all_count++;
5195 
5196             // Put each tile in one other list, for shadows and relections
5197             if ( 0 != mesh_test_fx( pmesh, cnt, MPDFX_SHA ) )
5198             {
5199                 prlist->sha[prlist->sha_count] = cnt;
5200                 prlist->sha_count++;
5201             }
5202             else
5203             {
5204                 prlist->ref[prlist->ref_count] = cnt;
5205                 prlist->ref_count++;
5206             }
5207 
5208             if ( 0 != mesh_test_fx( pmesh, cnt, MPDFX_DRAWREF ) )
5209             {
5210                 prlist->drf[prlist->drf_count] = cnt;
5211                 prlist->drf_count++;
5212             }
5213             else
5214             {
5215                 prlist->ndr[prlist->ndr_count] = cnt;
5216                 prlist->ndr_count++;
5217             }
5218 
5219             if ( 0 != mesh_test_fx( pmesh, cnt, MPDFX_WATER ) )
5220             {
5221                 prlist->wat[prlist->wat_count] = cnt;
5222                 prlist->wat_count++;
5223             }
5224         }
5225     }
5226 
5227     return retval;
5228 }
5229 
5230 //--------------------------------------------------------------------------------------------
5231 //--------------------------------------------------------------------------------------------
DisplayMsg_get_free()5232 int DisplayMsg_get_free()
5233 {
5234     /// ZZ@> This function finds the best message to use
5235     /// Pick the first one
5236 
5237     int tnc = DisplayMsg.count;
5238 
5239     DisplayMsg.count++;
5240     DisplayMsg.count %= maxmessage;
5241 
5242     return tnc;
5243 }
5244 
5245 //--------------------------------------------------------------------------------------------
5246 // ASSET INITIALIZATION
5247 //--------------------------------------------------------------------------------------------
init_icon_data()5248 void init_icon_data()
5249 {
5250     /// @details ZZ@> This function sets the icon pointers to NULL
5251 
5252     iconrect.left = 0;
5253     iconrect.right = 32;
5254     iconrect.top = 0;
5255     iconrect.bottom = 32;
5256 }
5257 
5258 //--------------------------------------------------------------------------------------------
init_bar_data()5259 void init_bar_data()
5260 {
5261     Uint8 cnt;
5262 
5263     // Initialize the life and mana bars
5264     for ( cnt = 0; cnt < NUMBAR; cnt++ )
5265     {
5266         tabrect[cnt].left = 0;
5267         tabrect[cnt].right = TABX;
5268         tabrect[cnt].top = cnt * BARY;
5269         tabrect[cnt].bottom = ( cnt + 1 ) * BARY;
5270 
5271         barrect[cnt].left = TABX;
5272         barrect[cnt].right = BARX;  // This is reset whenever a bar is drawn
5273         barrect[cnt].top = tabrect[cnt].top;
5274         barrect[cnt].bottom = tabrect[cnt].bottom;
5275 
5276     }
5277 }
5278 
5279 //--------------------------------------------------------------------------------------------
init_blip_data()5280 void init_blip_data()
5281 {
5282     int cnt;
5283 
5284     // Set up the rectangles
5285     for ( cnt = 0; cnt < COLOR_MAX; cnt++ )
5286     {
5287         bliprect[cnt].left   = cnt * BLIPSIZE;
5288         bliprect[cnt].right  = cnt * BLIPSIZE + BLIPSIZE;
5289         bliprect[cnt].top    = 0;
5290         bliprect[cnt].bottom = BLIPSIZE;
5291     }
5292 
5293     youarehereon = bfalse;
5294     blip_count      = 0;
5295 }
5296 
5297 //--------------------------------------------------------------------------------------------
init_map_data()5298 void init_map_data()
5299 {
5300     /// @details ZZ@> This function releases all the map images
5301 
5302     // Set up the rectangles
5303     maprect.left   = 0;
5304     maprect.right  = MAPSIZE;
5305     maprect.top    = 0;
5306     maprect.bottom = MAPSIZE;
5307 
5308     mapvalid = bfalse;
5309     mapon    = bfalse;
5310 }
5311 
5312 //--------------------------------------------------------------------------------------------
init_all_graphics()5313 void init_all_graphics()
5314 {
5315     init_icon_data();
5316     init_bar_data();
5317     init_blip_data();
5318     init_map_data();
5319     font_bmp_init();
5320 
5321     BillboardList_free_all();
5322     TxTexture_init_all();
5323 
5324     PROFILE_RESET( render_scene_init );
5325     PROFILE_RESET( render_scene_mesh );
5326     PROFILE_RESET( render_scene_solid );
5327     PROFILE_RESET( render_scene_water );
5328     PROFILE_RESET( render_scene_trans );
5329 
5330     PROFILE_RESET( renderlist_make );
5331     PROFILE_RESET( dolist_make );
5332     PROFILE_RESET( do_grid_lighting );
5333     PROFILE_RESET( light_fans );
5334     PROFILE_RESET( update_all_chr_instance );
5335     PROFILE_RESET( update_all_prt_instance );
5336 
5337     PROFILE_RESET( render_scene_mesh_dolist_sort );
5338     PROFILE_RESET( render_scene_mesh_ndr );
5339     PROFILE_RESET( render_scene_mesh_drf_back );
5340     PROFILE_RESET( render_scene_mesh_ref );
5341     PROFILE_RESET( render_scene_mesh_ref_chr );
5342     PROFILE_RESET( render_scene_mesh_drf_solid );
5343     PROFILE_RESET( render_scene_mesh_render_shadows );
5344 
5345     stabilized_game_fps        = TARGET_FPS;
5346     stabilized_game_fps_sum    = 0.1f * TARGET_FPS;
5347     stabilized_game_fps_weight = 0.1f;
5348 }
5349 
5350 //--------------------------------------------------------------------------------------------
release_all_graphics()5351 void release_all_graphics()
5352 {
5353     init_icon_data();
5354     init_bar_data();
5355     init_blip_data();
5356     init_map_data();
5357 
5358     BillboardList_free_all();
5359     TxTexture_release_all();
5360 }
5361 
5362 //--------------------------------------------------------------------------------------------
delete_all_graphics()5363 void delete_all_graphics()
5364 {
5365     init_icon_data();
5366     init_bar_data();
5367     init_blip_data();
5368     init_map_data();
5369 
5370     BillboardList_free_all();
5371     TxTexture_delete_all();
5372 }
5373 
5374 //--------------------------------------------------------------------------------------------
load_all_global_icons()5375 bool_t load_all_global_icons()
5376 {
5377     /// @details ZF@> Load all the global icons used in all modules
5378 
5379     // Setup
5380     bool_t result = bfalse;
5381 
5382     // Now load every icon
5383     result = INVALID_TX_TEXTURE != TxTexture_load_one_vfs( "mp_data/nullicon", ( TX_REF )ICON_NULL, INVALID_KEY );
5384     result = INVALID_TX_TEXTURE != TxTexture_load_one_vfs( "mp_data/keybicon", ( TX_REF )ICON_KEYB, INVALID_KEY );
5385     result = INVALID_TX_TEXTURE != TxTexture_load_one_vfs( "mp_data/mousicon", ( TX_REF )ICON_MOUS, INVALID_KEY );
5386     result = INVALID_TX_TEXTURE != TxTexture_load_one_vfs( "mp_data/joyaicon", ( TX_REF )ICON_JOYA, INVALID_KEY );
5387     result = INVALID_TX_TEXTURE != TxTexture_load_one_vfs( "mp_data/joybicon", ( TX_REF )ICON_JOYB, INVALID_KEY );
5388 
5389     return result;
5390 }
5391 
5392 //--------------------------------------------------------------------------------------------
load_basic_textures()5393 void load_basic_textures()
5394 {
5395     /// @details ZZ@> This function loads the standard textures for a module
5396 
5397     // Particle sprites
5398     TxTexture_load_one_vfs( "mp_data/particle_trans", ( TX_REF )TX_PARTICLE_TRANS, TRANSCOLOR );
5399     prt_set_texture_params(( TX_REF )TX_PARTICLE_TRANS );
5400 
5401     TxTexture_load_one_vfs( "mp_data/particle_light", ( TX_REF )TX_PARTICLE_LIGHT, INVALID_KEY );
5402     prt_set_texture_params(( TX_REF )TX_PARTICLE_LIGHT );
5403 
5404     // Module background tiles
5405     TxTexture_load_one_vfs( "mp_data/tile0", ( TX_REF )TX_TILE_0, TRANSCOLOR );
5406     TxTexture_load_one_vfs( "mp_data/tile1", ( TX_REF )TX_TILE_1, TRANSCOLOR );
5407     TxTexture_load_one_vfs( "mp_data/tile2", ( TX_REF )TX_TILE_2, TRANSCOLOR );
5408     TxTexture_load_one_vfs( "mp_data/tile3", ( TX_REF )TX_TILE_3, TRANSCOLOR );
5409 
5410     // Water textures
5411     TxTexture_load_one_vfs( "mp_data/watertop", ( TX_REF )TX_WATER_TOP, TRANSCOLOR );
5412     TxTexture_load_one_vfs( "mp_data/waterlow", ( TX_REF )TX_WATER_LOW, TRANSCOLOR );
5413 
5414     // Texture 7 is the phong map
5415     TxTexture_load_one_vfs( "mp_data/phong", ( TX_REF )TX_PHONG, TRANSCOLOR );
5416 
5417     PROFILE_RESET( render_scene_init );
5418     PROFILE_RESET( render_scene_mesh );
5419     PROFILE_RESET( render_scene_solid );
5420     PROFILE_RESET( render_scene_water );
5421     PROFILE_RESET( render_scene_trans );
5422 
5423     PROFILE_RESET( renderlist_make );
5424     PROFILE_RESET( dolist_make );
5425     PROFILE_RESET( do_grid_lighting );
5426     PROFILE_RESET( light_fans );
5427     PROFILE_RESET( update_all_chr_instance );
5428     PROFILE_RESET( update_all_prt_instance );
5429 
5430     PROFILE_RESET( render_scene_mesh_dolist_sort );
5431     PROFILE_RESET( render_scene_mesh_ndr );
5432     PROFILE_RESET( render_scene_mesh_drf_back );
5433     PROFILE_RESET( render_scene_mesh_ref );
5434     PROFILE_RESET( render_scene_mesh_ref_chr );
5435     PROFILE_RESET( render_scene_mesh_drf_solid );
5436     PROFILE_RESET( render_scene_mesh_render_shadows );
5437 
5438     stabilized_game_fps        = TARGET_FPS;
5439     stabilized_game_fps_sum    = 0.1f * TARGET_FPS;
5440     stabilized_game_fps_weight = 0.1f;
5441 }
5442 
5443 //--------------------------------------------------------------------------------------------
load_bars()5444 void load_bars()
5445 {
5446     /// @details ZZ@> This function loads the status bar bitmap
5447 
5448     const char * pname;
5449 
5450     pname = "mp_data/bars";
5451     if ( INVALID_TX_TEXTURE == TxTexture_load_one_vfs( pname, ( TX_REF )TX_BARS, TRANSCOLOR ) )
5452     {
5453         log_warning( "load_bars() - Cannot load file! (\"%s\")\n", pname );
5454     }
5455 
5456     pname = "mp_data/xpbar";
5457     if ( INVALID_TX_TEXTURE == TxTexture_load_one_vfs( pname, ( TX_REF )TX_XP_BAR, TRANSCOLOR ) )
5458     {
5459         log_warning( "load_bars() - Cannot load file! (\"%s\")\n", pname );
5460     }
5461 }
5462 
5463 //--------------------------------------------------------------------------------------------
load_map()5464 void load_map()
5465 {
5466     /// @details ZZ@> This function loads the map bitmap
5467 
5468     const char* szMap;
5469 
5470     // Turn it all off
5471     mapvalid = bfalse;
5472     mapon = bfalse;
5473     youarehereon = bfalse;
5474     blip_count = 0;
5475 
5476     // Load the images
5477     szMap = "mp_data/plan";
5478     if ( INVALID_TX_TEXTURE == TxTexture_load_one_vfs( szMap, ( TX_REF )TX_MAP, INVALID_KEY ) )
5479     {
5480         log_debug( "load_map() - Cannot load file! (\"%s\")\n", szMap );
5481     }
5482     else
5483     {
5484         mapvalid = btrue;
5485     }
5486 }
5487 
5488 //--------------------------------------------------------------------------------------------
load_blips()5489 bool_t load_blips()
5490 {
5491     /// ZZ@> This function loads the blip bitmaps
5492     if ( INVALID_TX_TEXTURE == TxTexture_load_one_vfs( "mp_data/blip", ( TX_REF )TX_BLIP, INVALID_KEY ) )
5493     {
5494         log_warning( "Blip bitmap not loaded! (\"mp_data/blip\")\n" );
5495         return bfalse;
5496     }
5497 
5498     return btrue;
5499 }
5500 
5501 //--------------------------------------------------------------------------------------------
load_graphics()5502 void load_graphics()
5503 {
5504     /// @details ZF@> This function loads all the graphics based on the game settings
5505 
5506     GLenum quality;
5507 
5508     // Check if the computer graphic driver supports anisotropic filtering
5509 
5510     if ( !ogl_caps.anisotropic_supported )
5511     {
5512         if ( tex_params.texturefilter >= TX_ANISOTROPIC )
5513         {
5514             tex_params.texturefilter = TX_TRILINEAR_2;
5515             log_warning( "Your graphics driver does not support anisotropic filtering.\n" );
5516         }
5517     }
5518 
5519     // Enable prespective correction?
5520     if ( gfx.perspective ) quality = GL_NICEST;
5521     else quality = GL_FASTEST;
5522     GL_DEBUG( glHint )( GL_PERSPECTIVE_CORRECTION_HINT, quality );
5523 
5524     // Enable dithering?
5525     if ( gfx.dither )
5526     {
5527         GL_DEBUG( glHint )( GL_GENERATE_MIPMAP_HINT, GL_NICEST );
5528         GL_DEBUG( glEnable )( GL_DITHER );
5529     }
5530     else
5531     {
5532         GL_DEBUG( glHint )( GL_GENERATE_MIPMAP_HINT, GL_FASTEST );
5533         GL_DEBUG( glDisable )( GL_DITHER );                          // ENABLE_BIT
5534     }
5535 
5536     // Enable Gouraud shading? (Important!)
5537     GL_DEBUG( glShadeModel )( gfx.shading );         // GL_LIGHTING_BIT
5538 
5539     // Enable antialiasing?
5540     if ( gfx.antialiasing )
5541     {
5542         GL_DEBUG( glEnable )( GL_MULTISAMPLE_ARB );
5543 
5544         GL_DEBUG( glEnable )( GL_LINE_SMOOTH );
5545         GL_DEBUG( glHint )( GL_LINE_SMOOTH_HINT,    GL_NICEST );
5546 
5547         GL_DEBUG( glEnable )( GL_POINT_SMOOTH );
5548         GL_DEBUG( glHint )( GL_POINT_SMOOTH_HINT,   GL_NICEST );
5549 
5550         GL_DEBUG( glDisable )( GL_POLYGON_SMOOTH );
5551         GL_DEBUG( glHint )( GL_POLYGON_SMOOTH_HINT,    GL_FASTEST );
5552 
5553         // PLEASE do not turn this on unless you use
5554         // GL_DEBUG(glEnable)(GL_BLEND);
5555         // GL_DEBUG(glBlendFunc)(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
5556         // before every single draw command
5557         //
5558         // GL_DEBUG(glEnable)(GL_POLYGON_SMOOTH);
5559         // GL_DEBUG(glHint)(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
5560     }
5561     else
5562     {
5563         GL_DEBUG( glDisable )( GL_MULTISAMPLE_ARB );
5564         GL_DEBUG( glDisable )( GL_POINT_SMOOTH );
5565         GL_DEBUG( glDisable )( GL_LINE_SMOOTH );
5566         GL_DEBUG( glDisable )( GL_POLYGON_SMOOTH );
5567     }
5568 }
5569 
5570 //--------------------------------------------------------------------------------------------
5571 //--------------------------------------------------------------------------------------------
do_chr_flashing(dolist_t * pdolist)5572 gfx_rv do_chr_flashing( dolist_t * pdolist )
5573 {
5574     gfx_rv retval;
5575     Uint32 i;
5576 
5577     if ( NULL == pdolist )
5578     {
5579         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL dolist" );
5580         return gfx_error;
5581     }
5582 
5583     if ( pdolist->count >= DOLIST_SIZE )
5584     {
5585         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "invalid dolist size" );
5586         return gfx_error;
5587     }
5588 
5589     retval = gfx_success;
5590     for ( i = 0; i < pdolist->count; i++ )
5591     {
5592         CHR_REF ichr = pdolist->lst[i].ichr;
5593         if ( !VALID_CHR_RANGE( ichr ) ) continue;
5594 
5595         // Do flashing
5596         if ( HAS_NO_BITS( true_frame, ChrList.lst[ichr].flashand ) && ChrList.lst[ichr].flashand != DONTFLASH )
5597         {
5598             flash_character( ichr, 255 );
5599         }
5600 
5601         // Do blacking
5602         // having one holy player in your party will cause the effect, BUT
5603         // having some non-holy players will dilute it
5604         if ( HAS_NO_BITS( true_frame, SEEKURSEAND ) && ( local_stats.seekurse_level > 0.0f ) && ChrList.lst[ichr].iskursed )
5605         {
5606             float tmp_seekurse_level = MIN( local_stats.seekurse_level, 1.0f );
5607             if ( gfx_error == flash_character( ichr, 255.0f *( 1.0f - tmp_seekurse_level ) ) )
5608             {
5609                 retval = gfx_error;
5610             }
5611         }
5612     }
5613 
5614     return retval;
5615 }
5616 
5617 //--------------------------------------------------------------------------------------------
flash_character(const CHR_REF character,Uint8 value)5618 gfx_rv flash_character( const CHR_REF character, Uint8 value )
5619 {
5620     /// @details ZZ@> This function sets a character's lighting
5621 
5622     int        cnt;
5623     float      flash_val = value * INV_FF;
5624     GLvertex * pv;
5625 
5626     chr_instance_t * pinst = chr_get_pinstance( character );
5627     if ( NULL == pinst ) return gfx_error;
5628 
5629     // flash the ambient color
5630     pinst->color_amb = flash_val;
5631 
5632     // flash the directional lighting
5633     pinst->color_amb = flash_val;
5634     for ( cnt = 0; cnt < pinst->vrt_count; cnt++ )
5635     {
5636         pv = pinst->vrt_lst + cnt;
5637 
5638         pv->color_dir = flash_val;
5639     }
5640 
5641     return gfx_success;
5642 }
5643 
5644 //--------------------------------------------------------------------------------------------
5645 // MODE CONTROL
5646 //--------------------------------------------------------------------------------------------
gfx_begin_text()5647 void gfx_begin_text()
5648 {
5649     // do not use the ATTRIB_PUSH macro, since the glPopAttrib() is in a different function
5650     GL_DEBUG( glPushAttrib )( GL_CURRENT_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT );
5651 
5652     GL_DEBUG( glEnable )( GL_TEXTURE_2D );
5653 
5654     // do not display the completely transparent portion
5655     GL_DEBUG( glEnable )( GL_ALPHA_TEST );                               // GL_ENABLE_BIT
5656     GL_DEBUG( glAlphaFunc )( GL_GREATER, 0.0f );                            // GL_COLOR_BUFFER_BIT
5657 
5658     GL_DEBUG( glEnable )( GL_BLEND );                                    // GL_COLOR_BUFFER_BIT
5659     GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );     // GL_COLOR_BUFFER_BIT
5660 
5661     // don't worry about hidden surfaces
5662     GL_DEBUG( glDisable )( GL_DEPTH_TEST );                              // GL_ENABLE_BIT
5663 
5664     // draw draw front and back faces of polygons
5665     GL_DEBUG( glDisable )( GL_CULL_FACE );                               // GL_ENABLE_BIT
5666 
5667     GL_DEBUG( glColor4f )( 1, 1, 1, 1 );                                // GL_CURRENT_BIT
5668 }
5669 
5670 //--------------------------------------------------------------------------------------------
gfx_end_text()5671 void gfx_end_text()
5672 {
5673     // do not use the ATTRIB_POP macro, since the glPushAttrib() is in a different function
5674     GL_DEBUG( glPopAttrib )();
5675 }
5676 
5677 //--------------------------------------------------------------------------------------------
gfx_enable_texturing()5678 void gfx_enable_texturing()
5679 {
5680     if ( !GL_DEBUG( glIsEnabled )( GL_TEXTURE_2D ) )
5681     {
5682         GL_DEBUG( glEnable )( GL_TEXTURE_2D );
5683     }
5684 }
5685 
5686 //--------------------------------------------------------------------------------------------
gfx_disable_texturing()5687 void gfx_disable_texturing()
5688 {
5689     if ( GL_DEBUG( glIsEnabled )( GL_TEXTURE_2D ) )
5690     {
5691         GL_DEBUG( glDisable )( GL_TEXTURE_2D );
5692     }
5693 }
5694 
5695 //--------------------------------------------------------------------------------------------
gfx_begin_3d(camera_t * pcam)5696 void gfx_begin_3d( camera_t * pcam )
5697 {
5698     // store the GL_PROJECTION matrix (this stack has a finite depth, minimum of 32)
5699     GL_DEBUG( glMatrixMode )( GL_PROJECTION );
5700     GL_DEBUG( glPushMatrix )();
5701     GL_DEBUG( glLoadMatrixf )( pcam->mProjection.v );
5702 
5703     // store the GL_MODELVIEW matrix (this stack has a finite depth, minimum of 32)
5704     GL_DEBUG( glMatrixMode )( GL_MODELVIEW );
5705     GL_DEBUG( glPushMatrix )();
5706     GL_DEBUG( glLoadMatrixf )( pcam->mView.v );
5707 }
5708 
5709 //--------------------------------------------------------------------------------------------
gfx_end_3d()5710 void gfx_end_3d()
5711 {
5712     // Restore the GL_MODELVIEW matrix
5713     GL_DEBUG( glMatrixMode )( GL_MODELVIEW );
5714     GL_DEBUG( glPopMatrix )();
5715 
5716     // Restore the GL_PROJECTION matrix
5717     GL_DEBUG( glMatrixMode )( GL_PROJECTION );
5718     GL_DEBUG( glPopMatrix )();
5719 }
5720 
5721 //--------------------------------------------------------------------------------------------
gfx_begin_2d(void)5722 void gfx_begin_2d( void )
5723 {
5724     GL_DEBUG( glMatrixMode )( GL_PROJECTION );
5725     GL_DEBUG( glLoadIdentity )();                // Reset The Projection Matrix
5726     GL_DEBUG( glOrtho )( 0, sdl_scr.x, sdl_scr.y, 0, -1, 1 );     // Set up an orthogonal projection
5727 
5728     GL_DEBUG( glMatrixMode )( GL_MODELVIEW );
5729     GL_DEBUG( glLoadIdentity )();
5730 
5731     // don't worry about hidden surfaces
5732     GL_DEBUG( glDisable )( GL_DEPTH_TEST );
5733 
5734     // draw draw front and back faces of polygons
5735     GL_DEBUG( glDisable )( GL_CULL_FACE );
5736 }
5737 
5738 //--------------------------------------------------------------------------------------------
gfx_end_2d(void)5739 void gfx_end_2d( void )
5740 {
5741     // cull backward facing polygons
5742     GL_DEBUG( glEnable )( GL_CULL_FACE );
5743 
5744     // do not draw hidden surfaces
5745     GL_DEBUG( glEnable )( GL_DEPTH_TEST );
5746     GL_DEBUG( glDepthFunc )( GL_LEQUAL );
5747 }
5748 
5749 //--------------------------------------------------------------------------------------------
gfx_reshape_viewport(int w,int h)5750 void gfx_reshape_viewport( int w, int h )
5751 {
5752     GL_DEBUG( glViewport )( 0, 0, w, h );
5753 }
5754 
5755 //--------------------------------------------------------------------------------------------
request_clear_screen()5756 void request_clear_screen()
5757 {
5758     gfx_page_clear_requested = btrue;
5759 }
5760 
5761 //--------------------------------------------------------------------------------------------
do_clear_screen()5762 void do_clear_screen()
5763 {
5764     bool_t try_clear;
5765 
5766     try_clear = bfalse;
5767     if ( process_running( PROC_PBASE( GProc ) ) && PROC_PBASE( GProc )->state > proc_begin )
5768     {
5769         try_clear = gfx_page_clear_requested;
5770     }
5771     else if ( process_running( PROC_PBASE( MProc ) ) && PROC_PBASE( MProc )->state > proc_begin )
5772     {
5773         try_clear = gfx_page_clear_requested;
5774     }
5775 
5776     if ( try_clear )
5777     {
5778         bool_t game_needs_clear, menu_needs_clear;
5779 
5780         gfx_page_clear_requested = bfalse;
5781 
5782         // clear the depth buffer no matter what
5783         GL_DEBUG( glDepthMask )( GL_TRUE );
5784         GL_DEBUG( glClear )( GL_DEPTH_BUFFER_BIT );
5785 
5786         // clear the color buffer only if necessary
5787         game_needs_clear = gfx.clearson && process_running( PROC_PBASE( GProc ) );
5788         menu_needs_clear = mnu_draw_background && process_running( PROC_PBASE( MProc ) );
5789 
5790         if ( game_needs_clear || menu_needs_clear )
5791         {
5792             GL_DEBUG( glClear )( GL_COLOR_BUFFER_BIT );
5793         }
5794     }
5795 }
5796 
5797 //--------------------------------------------------------------------------------------------
do_flip_pages()5798 void do_flip_pages()
5799 {
5800     bool_t try_flip;
5801 
5802     try_flip = bfalse;
5803     if ( process_running( PROC_PBASE( GProc ) ) && PROC_PBASE( GProc )->state > proc_begin )
5804     {
5805         try_flip = gfx_page_flip_requested;
5806     }
5807     else if ( process_running( PROC_PBASE( MProc ) ) && PROC_PBASE( MProc )->state > proc_begin )
5808     {
5809         try_flip = gfx_page_flip_requested;
5810     }
5811 
5812     if ( try_flip )
5813     {
5814         gfx_page_flip_requested = bfalse;
5815         _flip_pages();
5816 
5817         gfx_page_clear_requested = btrue;
5818     }
5819 }
5820 
5821 //--------------------------------------------------------------------------------------------
request_flip_pages()5822 void request_flip_pages()
5823 {
5824     gfx_page_flip_requested = btrue;
5825 }
5826 
5827 //--------------------------------------------------------------------------------------------
flip_pages_requested()5828 bool_t flip_pages_requested()
5829 {
5830     return gfx_page_flip_requested;
5831 }
5832 
5833 //--------------------------------------------------------------------------------------------
_flip_pages()5834 void _flip_pages()
5835 {
5836     GL_DEBUG( glFlush )();
5837 
5838     // draw the console on top of everything
5839     egoboo_console_draw_all();
5840 
5841     SDL_GL_SwapBuffers();
5842 
5843     if ( process_running( PROC_PBASE( MProc ) ) )
5844     {
5845         menu_fps_loops++;
5846     }
5847 
5848     if ( process_running( PROC_PBASE( GProc ) ) )
5849     {
5850         game_fps_loops++;
5851         game_frame_all++;
5852     }
5853 
5854     gfx_update_timers();
5855 
5856     if ( screenshot_requested )
5857     {
5858         screenshot_requested = bfalse;
5859 
5860         // take the screenshot NOW, since we have just updated the screen buffer
5861         if ( !dump_screenshot() )
5862         {
5863             debug_printf( "Error writing screenshot!" );    // send a failure message to the screen
5864             log_warning( "Error writing screenshot\n" );    // Log the error in log.txt
5865         }
5866     }
5867 }
5868 
5869 //--------------------------------------------------------------------------------------------
5870 // LIGHTING FUNCTIONS
5871 //--------------------------------------------------------------------------------------------
light_fans(renderlist_t * prlist)5872 gfx_rv light_fans( renderlist_t * prlist )
5873 {
5874     int    entry;
5875     Uint8  type;
5876     float  light;
5877     int    numvertices, vertex, needs_interpolation_count;
5878     float  local_mesh_lighting_keep;
5879 
5880     /// @note we are measuring the change in the intensity at the corner of a tile (the "delta") as
5881     /// a fraction of the current intensity. This is because your eye is much more sensitive to
5882     /// intensity differences when the intensity is low.
5883     ///
5884     /// @note it is normally assumed that 64 colors of gray can make a smoothly colored black and white picture
5885     /// which means that the threshold could be set as low as 1/64 = 0.015625.
5886     const float delta_threshold = 0.05f;
5887 
5888     ego_mpd_t      * pmesh;
5889     tile_mem_t     * ptmem;
5890     grid_mem_t     * pgmem;
5891 
5892     if ( NULL == prlist )
5893     {
5894         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL renderlist" );
5895         return gfx_error;
5896     }
5897 
5898     if ( NULL == prlist->pmesh )
5899     {
5900         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL renderlist mesh" );
5901         return gfx_error;
5902     }
5903     pmesh = prlist->pmesh;
5904 
5905 #if defined(CLIP_ALL_LIGHT_FANS)
5906     // update all visible fans once every 4 updates
5907     if ( 0 != ( game_frame_all & 0x03 ) ) return gfx_success;
5908 #endif
5909 
5910     ptmem = &( pmesh->tmem );
5911     pgmem = &( pmesh->gmem );
5912 
5913 #if !defined(CLIP_LIGHT_FANS)
5914     // update only every frame
5915     local_mesh_lighting_keep = 0.9f;
5916 #else
5917     // update only every 4 frames
5918     local_mesh_lighting_keep = POW( 0.9f, 4 );
5919 #endif
5920 
5921     // cache the grid lighting
5922     needs_interpolation_count = 0;
5923     for ( entry = 0; entry < prlist->all_count; entry++ )
5924     {
5925         bool_t needs_update;
5926         int fan;
5927         float delta;
5928         ego_tile_info_t * ptile;
5929 
5930         fan = prlist->all[entry];
5931         if ( !mesh_grid_is_valid( pmesh, fan ) ) continue;
5932 
5933         ptile = ptmem->tile_list + fan;
5934 
5935 #if defined(CLIP_LIGHT_FANS) && !defined(CLIP_ALL_LIGHT_FANS)
5936 
5937         // visible fans based on the update "need"
5938         needs_update = mesh_test_corners( pmesh, fan, delta_threshold );
5939 
5940         // update every 4 fans even if there is no need
5941         if ( !needs_update )
5942         {
5943             int ix, iy;
5944 
5945             // use a kind of checkerboard pattern
5946             ix = fan % pgmem->grids_x;
5947             iy = fan / pgmem->grids_x;
5948             if ( 0 != ((( ix ^ iy ) + game_frame_all ) & 0x03 ) )
5949             {
5950                 needs_update = btrue;
5951             }
5952         }
5953 
5954 #else
5955         needs_update = btrue;
5956 #endif
5957 
5958         // does thit tile need a lighting update?
5959         ptile->needs_lighting_update = needs_update;
5960 
5961         // if there's no need for an update, go to the next tile
5962         if ( !needs_update ) continue;
5963 
5964         delta = mesh_light_corners( prlist->pmesh, fan, local_mesh_lighting_keep );
5965 
5966 #if defined(CLIP_LIGHT_FANS)
5967         // use the actual measured change in the intensity at the tile edge to
5968         // signal whether we need to calculate the next stage
5969         ptile->needs_lighting_update = ( delta > delta_threshold );
5970 #endif
5971 
5972         // optimize the use of the second loop
5973         if ( ptile->needs_lighting_update )
5974         {
5975             numvertices = tile_dict[ptile->type].numvertices;
5976 
5977             if ( numvertices <= 4 )
5978             {
5979                 int ivrt;
5980 
5981                 vertex = ptile->vrtstart;
5982 
5983                 // the second loop is not needed at all
5984                 for ( ivrt = 0; ivrt < numvertices; ivrt++, vertex++ )
5985                 {
5986                     light = ptile->lcache[ivrt];
5987 
5988                     light = CLIP( light, 0.0f, 255.0f );
5989                     ptmem->clst[vertex][RR] =
5990                         ptmem->clst[vertex][GG] =
5991                             ptmem->clst[vertex][BB] = light * INV_FF;
5992                 };
5993 
5994                 // clear out the deltas
5995                 memset( ptile->d1_cache, 0, sizeof( ptile->d1_cache ) );
5996                 memset( ptile->d2_cache, 0, sizeof( ptile->d2_cache ) );
5997 
5998                 // everything has been handled. no need to do this in another loop.
5999                 ptile->needs_lighting_update = bfalse;
6000             }
6001             else
6002             {
6003                 // the clst cannot be updated at this time. defer it to the next loop.
6004                 needs_interpolation_count++;
6005             }
6006         }
6007     }
6008 
6009     // can we avoid this whole loop?
6010     if ( needs_interpolation_count > 0 )
6011     {
6012         // use the grid to light the tiles
6013         for ( entry = 0; entry < prlist->all_count; entry++ )
6014         {
6015             int ivrt;
6016             Uint32 fan;
6017             ego_tile_info_t * ptile;
6018 
6019             fan = prlist->all[entry];
6020             if ( !mesh_grid_is_valid( pmesh, fan ) ) continue;
6021 
6022             ptile = ptmem->tile_list + fan;
6023 
6024             if ( !ptile->needs_lighting_update ) continue;
6025 
6026             type        = ptile->type;
6027             numvertices = tile_dict[type].numvertices;
6028             vertex      = ptile->vrtstart;
6029 
6030             // copy the 1st 4 vertices
6031             for ( ivrt = 0; ivrt < 4; ivrt++, vertex++ )
6032             {
6033                 light = ptile->lcache[ivrt];
6034 
6035                 light = CLIP( light, 0.0f, 255.0f );
6036                 ptmem->clst[vertex][RR] =
6037                     ptmem->clst[vertex][GG] =
6038                         ptmem->clst[vertex][BB] = light * INV_FF;
6039             };
6040 
6041             for ( /* nothing */ ; ivrt < numvertices; ivrt++, vertex++ )
6042             {
6043                 light = 0;
6044                 mesh_interpolate_vertex( ptmem, fan, ptmem->plst[vertex], &light );
6045 
6046                 light = CLIP( light, 0.0f, 255.0f );
6047                 ptmem->clst[vertex][RR] =
6048                     ptmem->clst[vertex][GG] =
6049                         ptmem->clst[vertex][BB] = light * INV_FF;
6050             };
6051 
6052             // clear out the deltas
6053             memset( ptile->d1_cache, 0, sizeof( ptile->d1_cache ) );
6054             memset( ptile->d2_cache, 0, sizeof( ptile->d2_cache ) );
6055 
6056             // untag this tile
6057             ptile->needs_lighting_update = bfalse;
6058         }
6059     }
6060 
6061     return gfx_success;
6062 }
6063 
6064 //--------------------------------------------------------------------------------------------
get_ambient_level()6065 float get_ambient_level()
6066 {
6067     /// @details BB@> get the actual global ambient level
6068 
6069     float glob_amb, min_amb;
6070 
6071     glob_amb = 0.0f;
6072     min_amb  = 0.0f;
6073     if ( gfx.usefaredge )
6074     {
6075         // for outside modules, max light_a means bright sunlight
6076         glob_amb = light_a * 255.0f;
6077     }
6078     else
6079     {
6080         // for inside modules, max light_a means dingy dungeon lighting
6081         glob_amb = light_a * 32.0f;
6082     }
6083 
6084     // determine the minimum ambient, based on darkvision
6085     min_amb = INVISIBLE / 4;
6086     if ( local_stats.seedark_mag != 1.0f )
6087     {
6088         // start with the global light
6089         min_amb  = glob_amb;
6090 
6091         // give a iny boost in the case of no light_a
6092         if ( local_stats.seedark_mag > 0.0f ) min_amb += 1.0f;
6093 
6094         // light_a can be quite dark, so we need a large magnification
6095         min_amb *= local_stats.seedark_mag * local_stats.seedark_mag;
6096         min_amb *= local_stats.seedark_mag * local_stats.seedark_mag;
6097         min_amb *= local_stats.seedark_mag;
6098     }
6099 
6100     return MAX( glob_amb, min_amb );
6101 }
6102 
6103 //--------------------------------------------------------------------------------------------
sum_global_lighting(lighting_vector_t lighting)6104 bool_t sum_global_lighting( lighting_vector_t lighting )
6105 {
6106     /// @details BB@> do ambient lighting. if the module is inside, the ambient lighting
6107     /// is reduced by up to a factor of 8. It is still kept just high enough
6108     /// so that ordnary objects will not be made invisible. This was breaking some of the AIs
6109 
6110     int cnt;
6111     float glob_amb;
6112 
6113     if ( NULL == lighting ) return bfalse;
6114 
6115     glob_amb = get_ambient_level();
6116 
6117     for ( cnt = 0; cnt < LVEC_AMB; cnt++ )
6118     {
6119         lighting[cnt] = 0.0f;
6120     }
6121     lighting[LVEC_AMB] = glob_amb;
6122 
6123     if ( !gfx.usefaredge ) return btrue;
6124 
6125     // do "outside" directional lighting (i.e. sunlight)
6126     lighting_vector_sum( lighting, light_nrm, light_d * 255, 0.0f );
6127 
6128     return btrue;
6129 }
6130 
6131 //--------------------------------------------------------------------------------------------
6132 // SEMI OBSOLETE FUNCTIONS
6133 //--------------------------------------------------------------------------------------------
draw_cursor()6134 void draw_cursor()
6135 {
6136     /// ZZ@> This function implements a mouse cursor
6137 
6138     oglx_texture_t * tx_ptr = TxTexture_get_ptr(( TX_REF )TX_FONT );
6139 
6140     if ( cursor.x < 6 )  cursor.x = 6;
6141     if ( cursor.x > sdl_scr.x - 16 )  cursor.x = sdl_scr.x - 16;
6142 
6143     if ( cursor.y < 8 )  cursor.y = 8;
6144     if ( cursor.y > sdl_scr.y - 24 )  cursor.y = sdl_scr.y - 24;
6145 
6146     // Needed to setup text mode
6147     gfx_begin_text();
6148     {
6149         draw_one_font( tx_ptr, 95, cursor.x - 5, cursor.y - 7 );
6150     }
6151     // Needed when done with text mode
6152     gfx_end_text();
6153 }
6154 
6155 //--------------------------------------------------------------------------------------------
6156 //--------------------------------------------------------------------------------------------
dynalist_init(dynalist_t * pdylist)6157 gfx_rv dynalist_init( dynalist_t * pdylist )
6158 {
6159     if ( NULL == pdylist )
6160     {
6161         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL dynalist" );
6162         return gfx_error;
6163     }
6164 
6165     pdylist->count        = 0;
6166 
6167     return gfx_success;
6168 }
6169 
6170 //--------------------------------------------------------------------------------------------
gfx_make_dynalist(dynalist_t * pdylist,camera_t * pcam)6171 gfx_rv gfx_make_dynalist( dynalist_t * pdylist, camera_t * pcam )
6172 {
6173     /// @details ZZ@> This function figures out which particles are visible, and it sets up dynamic
6174     ///    lighting
6175 
6176     int      tnc;
6177     fvec3_t  vdist;
6178 
6179     float         distance   = 0.0f;
6180     dynalight_t * plight     = NULL;
6181 
6182     float         distance_max = 0.0f;
6183     dynalight_t * plight_max   = NULL;
6184 
6185     if ( NULL == pdylist )
6186     {
6187         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL dynalist" );
6188         return gfx_error;
6189     }
6190 
6191     // Don't really make a list, just set to visible or not
6192     dynalist_init( pdylist );
6193 
6194     PRT_BEGIN_LOOP_DISPLAY( iprt, prt_bdl )
6195     {
6196         dynalight_info_t * pprt_dyna = &( prt_bdl.prt_ptr->dynalight );
6197 
6198         // is the light on?
6199         if ( !pprt_dyna->on || 0.0f == pprt_dyna->level ) continue;
6200 
6201         // reset the dynalight pointer
6202         plight = NULL;
6203 
6204         // find the distance to the camera
6205         vdist = fvec3_sub( prt_get_pos_v( prt_bdl.prt_ptr ), pcam->track_pos.v );
6206         distance = vdist.x * vdist.x + vdist.y * vdist.y + vdist.z * vdist.z;
6207 
6208         // insert the dynalight
6209         if ( pdylist->count < gfx.dynalist_max &&  pdylist->count < TOTAL_MAX_DYNA )
6210         {
6211             if ( 0 == pdylist->count )
6212             {
6213                 distance_max = distance;
6214             }
6215             else
6216             {
6217                 distance_max = MAX( distance_max, distance );
6218             }
6219 
6220             // grab a new light from the list
6221             plight = pdylist->lst + pdylist->count;
6222             pdylist->count++;
6223 
6224             if ( distance_max == distance )
6225             {
6226                 plight_max = plight;
6227             }
6228         }
6229         else if ( distance < distance_max )
6230         {
6231             plight = plight_max;
6232 
6233             // find the new maximum distance
6234             distance_max = pdylist->lst[0].distance;
6235             plight_max   = pdylist->lst + 0;
6236             for ( tnc = 1; tnc < gfx.dynalist_max; tnc++ )
6237             {
6238                 if ( pdylist->lst[tnc].distance > distance_max )
6239                 {
6240                     plight_max   = pdylist->lst + tnc;
6241                     distance_max = plight->distance;
6242                 }
6243             }
6244         }
6245 
6246         if ( NULL != plight )
6247         {
6248             plight->distance = distance;
6249             plight->pos      = prt_get_pos( prt_bdl.prt_ptr );
6250             plight->level    = pprt_dyna->level;
6251             plight->falloff  = pprt_dyna->falloff;
6252         }
6253     }
6254     PRT_END_LOOP();
6255 
6256     return gfx_success;
6257 }
6258 
6259 //--------------------------------------------------------------------------------------------
do_grid_lighting(renderlist_t * prlist,dynalist_t * pdylist,camera_t * pcam)6260 gfx_rv do_grid_lighting( renderlist_t * prlist, dynalist_t * pdylist, camera_t * pcam )
6261 {
6262     /// @details ZZ@> Do all tile lighting, dynamic and global
6263 
6264     int   cnt, tnc, fan, entry;
6265     int ix, iy;
6266     float x0, y0, local_keep;
6267     bool_t needs_dynalight;
6268     ego_mpd_t * pmesh;
6269 
6270     lighting_vector_t global_lighting;
6271 
6272     int                  reg_count;
6273     dynalight_registry_t reg[TOTAL_MAX_DYNA];
6274 
6275     ego_frect_t mesh_bound, light_bound;
6276 
6277     ego_mpd_info_t  * pinfo;
6278     grid_mem_t      * pgmem;
6279     tile_mem_t      * ptmem;
6280     ego_grid_info_t * glist;
6281 
6282     dynalight_t fake_dynalight;
6283 
6284     if ( NULL == prlist )
6285     {
6286         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL renderlist" );
6287         return gfx_error;
6288     }
6289 
6290     if ( NULL == pcam ) pcam = PCamera;
6291     if ( NULL == pcam )
6292     {
6293         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "cannot find a valid camera" );
6294         return gfx_error;
6295     }
6296 
6297     if ( NULL == prlist->pmesh )
6298     {
6299         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL renderlist mesh" );
6300         return gfx_error;
6301     }
6302     pmesh = prlist->pmesh;
6303 
6304     pinfo = &( pmesh->info );
6305     pgmem = &( pmesh->gmem );
6306     ptmem = &( pmesh->tmem );
6307 
6308     glist = pgmem->grid_list;
6309 
6310     // find a bounding box for the "frustum"
6311     mesh_bound.xmin = pgmem->edge_x;
6312     mesh_bound.xmax = 0;
6313     mesh_bound.ymin = pgmem->edge_y;
6314     mesh_bound.ymax = 0;
6315     for ( entry = 0; entry < prlist->all_count; entry++ )
6316     {
6317         fan = prlist->all[entry];
6318         if ( !mesh_grid_is_valid( pmesh, fan ) ) continue;
6319 
6320         ix = fan % pinfo->tiles_x;
6321         iy = fan / pinfo->tiles_x;
6322 
6323         x0 = ix * GRID_FSIZE;
6324         y0 = iy * GRID_FSIZE;
6325 
6326         mesh_bound.xmin = MIN( mesh_bound.xmin, x0 - GRID_FSIZE / 2 );
6327         mesh_bound.xmax = MAX( mesh_bound.xmax, x0 + GRID_FSIZE / 2 );
6328         mesh_bound.ymin = MIN( mesh_bound.ymin, y0 - GRID_FSIZE / 2 );
6329         mesh_bound.ymax = MAX( mesh_bound.ymax, y0 + GRID_FSIZE / 2 );
6330     }
6331 
6332     // is the visible mesh list empty?
6333     if ( mesh_bound.xmin >= mesh_bound.xmax || mesh_bound.ymin >= mesh_bound.ymax )
6334         return gfx_success;
6335 
6336     // clear out the dynalight registry
6337     reg_count = 0;
6338 
6339     // refresh the dynamic light list
6340     gfx_make_dynalist( pdylist, pcam );
6341 
6342     // assume no dynamic lighting
6343     needs_dynalight = bfalse;
6344 
6345     // assume no "extra halp" for systems with only flat lighting
6346     memset( &fake_dynalight, 0, sizeof( fake_dynalight ) );
6347 
6348     // initialize the light_bound
6349     light_bound.xmin = pgmem->edge_x;
6350     light_bound.xmax = 0;
6351     light_bound.ymin = pgmem->edge_y;
6352     light_bound.ymax = 0;
6353 
6354     // make bounding boxes for each dynamic light
6355     if ( GL_FLAT != gfx.shading )
6356     {
6357         for ( cnt = 0; cnt < pdylist->count; cnt++ )
6358         {
6359             float radius;
6360             ego_frect_t ftmp;
6361 
6362             dynalight_t * pdyna = pdylist->lst + cnt;
6363 
6364             if ( pdyna->falloff <= 0.0f || 0.0f == pdyna->level ) continue;
6365 
6366             radius = SQRT( pdyna->falloff * 765.0f / 2.0f );
6367 
6368             // find the intersection with the frustum boundary
6369             ftmp.xmin = MAX( pdyna->pos.x - radius, mesh_bound.xmin );
6370             ftmp.xmax = MIN( pdyna->pos.x + radius, mesh_bound.xmax );
6371             ftmp.ymin = MAX( pdyna->pos.y - radius, mesh_bound.ymin );
6372             ftmp.ymax = MIN( pdyna->pos.y + radius, mesh_bound.ymax );
6373 
6374             // check to see if it intersects the "frustum"
6375             if ( ftmp.xmin < ftmp.xmax && ftmp.ymin < ftmp.ymax )
6376             {
6377                 reg[reg_count].bound     = ftmp;
6378                 reg[reg_count].reference = cnt;
6379                 reg_count++;
6380 
6381                 // determine the maxumum bounding box that encloses all valid lights
6382                 light_bound.xmin = MIN( light_bound.xmin, ftmp.xmin );
6383                 light_bound.xmax = MAX( light_bound.xmax, ftmp.xmax );
6384                 light_bound.ymin = MIN( light_bound.ymin, ftmp.ymin );
6385                 light_bound.ymax = MAX( light_bound.ymax, ftmp.ymax );
6386             }
6387         }
6388 
6389         // are there any dynalights visible?
6390         if ( reg_count > 0 && light_bound.xmax >= light_bound.xmin && light_bound.ymax >= light_bound.ymin )
6391         {
6392             needs_dynalight = btrue;
6393         }
6394     }
6395     else
6396     {
6397         float dyna_weight = 0.0f;
6398         float dyna_weight_sum = 0.0f;
6399 
6400         fvec3_t       diff;
6401         dynalight_t * pdyna;
6402 
6403         // evaluate all the lights at the camera position
6404         for ( cnt = 0; cnt < pdylist->count; cnt++ )
6405         {
6406             pdyna = pdylist->lst + cnt;
6407 
6408             // evaluate the intensity at the camera
6409             diff.x = pdyna->pos.x - pcam->center.x;
6410             diff.y = pdyna->pos.y - pcam->center.y;
6411             diff.z = pdyna->pos.z - pcam->center.z - 90.0f;   // evaluated at the "head height" of a character
6412 
6413             dyna_weight = ABS( dyna_lighting_intensity( pdyna, diff.v ) );
6414 
6415             fake_dynalight.distance += dyna_weight * pdyna->distance;
6416             fake_dynalight.falloff  += dyna_weight * pdyna->falloff;
6417             fake_dynalight.level    += dyna_weight * pdyna->level;
6418             fake_dynalight.pos.x    += dyna_weight * ( pdyna->pos.x - pcam->center.x );
6419             fake_dynalight.pos.y    += dyna_weight * ( pdyna->pos.y - pcam->center.y );
6420             fake_dynalight.pos.z    += dyna_weight * ( pdyna->pos.z - pcam->center.z );
6421 
6422             dyna_weight_sum         += dyna_weight;
6423         }
6424 
6425         // use a singel dynalight to represent the sum of all dynalights
6426         if ( dyna_weight_sum > 0.0f )
6427         {
6428             float radius;
6429             ego_frect_t ftmp;
6430 
6431             fake_dynalight.distance /= dyna_weight_sum;
6432             fake_dynalight.falloff  /= dyna_weight_sum;
6433             fake_dynalight.level    /= dyna_weight_sum;
6434             fake_dynalight.pos.x    = fake_dynalight.pos.x / dyna_weight_sum + pcam->center.x;
6435             fake_dynalight.pos.y    = fake_dynalight.pos.y / dyna_weight_sum + pcam->center.y;
6436             fake_dynalight.pos.z    = fake_dynalight.pos.z / dyna_weight_sum + pcam->center.z;
6437 
6438             radius = SQRT( fake_dynalight.falloff * 765.0f / 2.0f );
6439 
6440             // find the intersection with the frustum boundary
6441             ftmp.xmin = MAX( fake_dynalight.pos.x - radius, mesh_bound.xmin );
6442             ftmp.xmax = MIN( fake_dynalight.pos.x + radius, mesh_bound.xmax );
6443             ftmp.ymin = MAX( fake_dynalight.pos.y - radius, mesh_bound.ymin );
6444             ftmp.ymax = MIN( fake_dynalight.pos.y + radius, mesh_bound.ymax );
6445 
6446             // make a fake light bound
6447             light_bound = ftmp;
6448 
6449             // register the fake dynalight
6450             reg[reg_count].bound     = ftmp;
6451             reg[reg_count].reference = -1;
6452             reg_count++;
6453 
6454             // leth the downstream calc know we are coming
6455             needs_dynalight = btrue;
6456         }
6457     }
6458 
6459     // sum up the lighting from global sources
6460     sum_global_lighting( global_lighting );
6461 
6462     // make the grids update their lighting every 4 frames
6463     local_keep = POW( dynalight_keep, 4 );
6464 
6465     // Add to base light level in normal mode
6466     for ( entry = 0; entry < prlist->all_count; entry++ )
6467     {
6468         bool_t resist_lighting_calculation = btrue;
6469 
6470         // grab each grid box in the "frustum"
6471         fan = prlist->all[entry];
6472         if ( !mesh_grid_is_valid( pmesh, fan ) ) continue;
6473 
6474         ix = fan % pinfo->tiles_x;
6475         iy = fan / pinfo->tiles_x;
6476 
6477         // Resist the lighting calculation?
6478         // This is a speedup for lighting calculations so that
6479         // not every light-tile calculation is done every single frame
6480         resist_lighting_calculation = ( 0 != ((( ix + iy ) ^ game_frame_all ) & 0x03 ) );
6481 
6482         if ( !resist_lighting_calculation )
6483         {
6484             lighting_cache_t * pcache_old;
6485             lighting_cache_t   cache_new;
6486 
6487             int dynalight_count = 0;
6488 
6489             // this is not a "bad" grid box, so grab the lighting info
6490             pcache_old = &( glist[fan].cache );
6491 
6492             lighting_cache_init( &cache_new );
6493 
6494             // copy the global lighting
6495             for ( tnc = 0; tnc < LIGHTING_VEC_SIZE; tnc++ )
6496             {
6497                 cache_new.low.lighting[tnc] = global_lighting[tnc];
6498                 cache_new.hgh.lighting[tnc] = global_lighting[tnc];
6499             };
6500 
6501             // do we need any dynamic lighting at all?
6502             if ( needs_dynalight )
6503             {
6504                 // calculate the local lighting
6505 
6506                 ego_frect_t fgrid_rect;
6507 
6508                 x0 = ix * GRID_FSIZE;
6509                 y0 = iy * GRID_FSIZE;
6510 
6511                 // check this grid vertex relative to the measured light_bound
6512                 fgrid_rect.xmin = x0 - GRID_FSIZE / 2;
6513                 fgrid_rect.xmax = x0 + GRID_FSIZE / 2;
6514                 fgrid_rect.ymin = y0 - GRID_FSIZE / 2;
6515                 fgrid_rect.ymax = y0 + GRID_FSIZE / 2;
6516 
6517                 // check the bounding box of this grid vs. the bounding box of the lighting
6518                 if ( fgrid_rect.xmin <= light_bound.xmax && fgrid_rect.xmax >= light_bound.xmin )
6519                 {
6520                     if ( fgrid_rect.ymin <= light_bound.ymax && fgrid_rect.ymax >= light_bound.ymin )
6521                     {
6522                         // this grid has dynamic lighting. add it.
6523                         for ( cnt = 0; cnt < reg_count; cnt++ )
6524                         {
6525                             fvec3_t       nrm;
6526                             dynalight_t * pdyna;
6527 
6528                             // does this dynamic light intersects this grid?
6529                             if ( fgrid_rect.xmin > reg[cnt].bound.xmax || fgrid_rect.xmax < reg[cnt].bound.xmin ) continue;
6530                             if ( fgrid_rect.ymin > reg[cnt].bound.ymax || fgrid_rect.ymax < reg[cnt].bound.ymin ) continue;
6531 
6532                             dynalight_count++;
6533 
6534                             // this should be a valid intersection, so proceed
6535                             tnc = reg[cnt].reference;
6536                             if ( tnc < 0 )
6537                             {
6538                                 pdyna = &fake_dynalight;
6539                             }
6540                             else
6541                             {
6542                                 pdyna = pdylist->lst + tnc;
6543                             }
6544 
6545                             nrm.x = pdyna->pos.x - x0;
6546                             nrm.y = pdyna->pos.y - y0;
6547                             nrm.z = pdyna->pos.z - ptmem->bbox.mins[ZZ];
6548                             sum_dyna_lighting( pdyna, cache_new.low.lighting, nrm.v );
6549 
6550                             nrm.z = pdyna->pos.z - ptmem->bbox.maxs[ZZ];
6551                             sum_dyna_lighting( pdyna, cache_new.hgh.lighting, nrm.v );
6552                         }
6553                     }
6554                 }
6555             }
6556             else if ( GL_FLAT == gfx.shading )
6557             {
6558                 // evaluate the intensity at the camera
6559             }
6560 
6561             // blend in the global lighting every single time
6562             // average this in with the existing lighting
6563             lighting_cache_blend( pcache_old, &cache_new, local_keep );
6564 
6565             // find the max intensity
6566             lighting_cache_max_light( pcache_old );
6567         }
6568     }
6569 
6570     return gfx_success;
6571 }
6572 
6573 //--------------------------------------------------------------------------------------------
render_water(renderlist_t * prlist)6574 gfx_rv render_water( renderlist_t * prlist )
6575 {
6576     /// @details ZZ@> This function draws all of the water fans
6577 
6578     int cnt;
6579     gfx_rv retval;
6580 
6581     if ( NULL == prlist )
6582     {
6583         gfx_error_add( __FILE__, __FUNCTION__, __LINE__, 0, "NULL renderlist" );
6584         return gfx_error;
6585     }
6586 
6587     // assume the best
6588     retval = gfx_success;
6589 
6590     // Bottom layer first
6591     if ( gfx.draw_water_1 )
6592     {
6593         for ( cnt = 0; cnt < prlist->wat_count; cnt++ )
6594         {
6595             if ( gfx_error == render_water_fan( prlist->pmesh, prlist->wat[cnt], 1 ) )
6596             {
6597                 retval = gfx_error;
6598             }
6599         }
6600     }
6601 
6602     // Top layer second
6603     if ( gfx.draw_water_0 )
6604     {
6605         for ( cnt = 0; cnt < prlist->wat_count; cnt++ )
6606         {
6607             if ( gfx_error == render_water_fan( prlist->pmesh, prlist->wat[cnt], 0 ) )
6608             {
6609                 retval = gfx_error;
6610             }
6611         }
6612     }
6613 
6614     return retval;
6615 }
6616 
6617 //--------------------------------------------------------------------------------------------
gfx_reload_all_textures()6618 void gfx_reload_all_textures()
6619 {
6620     TxTitleImage_reload_all();
6621     TxTexture_reload_all();
6622 }
6623 
6624 //--------------------------------------------------------------------------------------------
draw_quad_2d(oglx_texture_t * ptex,const ego_frect_t scr_rect,const ego_frect_t tx_rect,const bool_t use_alpha)6625 void draw_quad_2d( oglx_texture_t * ptex, const ego_frect_t scr_rect, const ego_frect_t tx_rect, const bool_t use_alpha )
6626 {
6627     ATTRIB_PUSH( __FUNCTION__, GL_CURRENT_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT )
6628     {
6629         GLboolean texture_1d_enabled, texture_2d_enabled;
6630 
6631         texture_1d_enabled = GL_DEBUG( glIsEnabled )( GL_TEXTURE_1D );
6632         texture_2d_enabled = GL_DEBUG( glIsEnabled )( GL_TEXTURE_2D );
6633 
6634         if ( NULL == ptex || INVALID_GL_ID == ptex->base.binding )
6635         {
6636             GL_DEBUG( glDisable )( GL_TEXTURE_1D );                           // GL_ENABLE_BIT
6637             GL_DEBUG( glDisable )( GL_TEXTURE_2D );                           // GL_ENABLE_BIT
6638         }
6639         else
6640         {
6641             GL_DEBUG( glEnable )( ptex->base.target );                        // GL_ENABLE_BIT
6642             oglx_texture_Bind( ptex );
6643         }
6644 
6645         GL_DEBUG( glColor4f )( 1.0f, 1.0f, 1.0f, 1.0f );                      // GL_CURRENT_BIT
6646 
6647         if ( use_alpha )
6648         {
6649             GL_DEBUG( glEnable )( GL_BLEND );                                 // GL_ENABLE_BIT
6650             GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );  // GL_COLOR_BUFFER_BIT
6651 
6652             GL_DEBUG( glEnable )( GL_ALPHA_TEST );                            // GL_ENABLE_BIT
6653             GL_DEBUG( glAlphaFunc )( GL_GREATER, 0.0f );                      // GL_COLOR_BUFFER_BIT
6654         }
6655         else
6656         {
6657             GL_DEBUG( glDisable )( GL_BLEND );                                 // GL_ENABLE_BIT
6658             GL_DEBUG( glDisable )( GL_ALPHA_TEST );                            // GL_ENABLE_BIT
6659         }
6660 
6661         GL_DEBUG( glBegin )( GL_QUADS );
6662         {
6663             GL_DEBUG( glTexCoord2f )( tx_rect.xmin, tx_rect.ymax ); GL_DEBUG( glVertex2f )( scr_rect.xmin, scr_rect.ymax );
6664             GL_DEBUG( glTexCoord2f )( tx_rect.xmax, tx_rect.ymax ); GL_DEBUG( glVertex2f )( scr_rect.xmax, scr_rect.ymax );
6665             GL_DEBUG( glTexCoord2f )( tx_rect.xmax, tx_rect.ymin ); GL_DEBUG( glVertex2f )( scr_rect.xmax, scr_rect.ymin );
6666             GL_DEBUG( glTexCoord2f )( tx_rect.xmin, tx_rect.ymin ); GL_DEBUG( glVertex2f )( scr_rect.xmin, scr_rect.ymin );
6667         }
6668         GL_DEBUG_END();
6669 
6670         // fix the texture enabling
6671         if ( texture_1d_enabled )
6672         {
6673             GL_DEBUG( glEnable )( GL_TEXTURE_1D );
6674         }
6675         else if ( texture_2d_enabled )
6676         {
6677             GL_DEBUG( glEnable )( GL_TEXTURE_2D );
6678         }
6679     }
6680     ATTRIB_POP( __FUNCTION__ );
6681 }
6682 
6683