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