/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ #ifdef _WIN32 #include #include #endif #include #include #include "globalincs/pstypes.h" #include "osapi/osapi.h" #include "graphics/2d.h" #include "graphics/grstub.h" #include "render/3d.h" #include "bmpman/bmpman.h" #include "palman/palman.h" #include "graphics/font.h" #include "graphics/grinternal.h" #include "globalincs/systemvars.h" #include "cmdline/cmdline.h" #include "graphics/grbatch.h" #include "parse/scripting.h" #include "gamesequence/gamesequence.h" //WMC - for scripting hooks in gr_flip() #include "io/keycontrol.h" // m!m #include "io/cursor.h" #include "graphics/gropengldraw.h" #include "debugconsole/console.h" #include "io/timer.h" #include "parse/parselo.h" #if ( SDL_VERSION_ATLEAST(1, 2, 7) ) #include "SDL_cpuinfo.h" #endif #include "SDL_surface.h" // Includes for different rendering systems #include "graphics/gropengl.h" const char *Resolution_prefixes[GR_NUM_RESOLUTIONS] = { "", "2_" }; screen gr_screen; color_gun Gr_red, Gr_green, Gr_blue, Gr_alpha; color_gun Gr_t_red, Gr_t_green, Gr_t_blue, Gr_t_alpha; color_gun Gr_ta_red, Gr_ta_green, Gr_ta_blue, Gr_ta_alpha; color_gun *Gr_current_red, *Gr_current_green, *Gr_current_blue, *Gr_current_alpha; ubyte Gr_original_palette[768]; // The palette ubyte Gr_current_palette[768]; char Gr_current_palette_name[128] = NOX("none"); // cursor stuff io::mouse::Cursor* Web_cursor = NULL; int Gr_inited = 0; uint Gr_signature = 0; float Gr_gamma = 1.8f; int Gr_gamma_int = 180; // z-buffer stuff int gr_zbuffering = 0; int gr_zbuffering_mode = 0; int gr_global_zbuffering = 0; // stencil buffer stuff int gr_stencil_mode = 0; // alpha mask stuff int gr_alpha_test = 0; // Default clipping distances const float Default_min_draw_distance = 1.0f; const float Default_max_draw_distance = 1e10; float Min_draw_distance = Default_min_draw_distance; float Max_draw_distance = Default_max_draw_distance; static int GL_cursor_nframes = 0; // Pre-computed screen resize vars static float Gr_full_resize_X = 1.0f, Gr_full_resize_Y = 1.0f; static float Gr_resize_X = 1.0f, Gr_resize_Y = 1.0f; static float Gr_menu_offset_X = 0.0f, Gr_menu_offset_Y = 0.0f; static float Gr_menu_zoomed_offset_X = 0.0f, Gr_menu_zoomed_offset_Y = 0.0f; float Gr_save_full_resize_X = 1.0f, Gr_save_full_resize_Y = 1.0f; float Gr_save_resize_X = 1.0f, Gr_save_resize_Y = 1.0f; float Gr_save_menu_offset_X = 0.0f, Gr_save_menu_offset_Y = 0.0f; float Gr_save_menu_zoomed_offset_X = 0.0f, Gr_save_menu_zoomed_offset_Y = 0.0f; bool Save_custom_screen_size; void gr_set_screen_scale(int w, int h, int zoom_w, int zoom_h, int max_w, int max_h, bool force_stretch) { bool do_zoom = zoom_w > 0 && zoom_h > 0 && (zoom_w != w || zoom_h != h); Gr_full_resize_X = (float)max_w / (float)w; Gr_full_resize_Y = (float)max_h / (float)h; if (do_zoom) { float aspect_quotient = ((float)max_w / (float)max_h) / ((float)zoom_w / (float)zoom_h); Gr_resize_X = (float)max_w / (float)zoom_w / ((aspect_quotient > 1.0f) ? aspect_quotient : 1.0f); Gr_resize_Y = (float)max_h / (float)zoom_h * ((aspect_quotient < 1.0f) ? aspect_quotient : 1.0f); Gr_menu_offset_X = (max_w - w * Gr_resize_X) / 2.0f; Gr_menu_offset_Y = (max_h - h * Gr_resize_Y) / 2.0f; Gr_menu_zoomed_offset_X = (Gr_menu_offset_X >= 0.0f) ? Gr_menu_offset_X : 0.0f; Gr_menu_zoomed_offset_Y = (Gr_menu_offset_Y >= 0.0f) ? Gr_menu_offset_Y : 0.0f; if (force_stretch || Cmdline_stretch_menu) { if (Gr_menu_offset_X > 0.0f) { Gr_resize_X = Gr_full_resize_X; Gr_menu_offset_X = Gr_menu_zoomed_offset_X = 0.0f; } if (Gr_menu_offset_Y > 0.0f) { Gr_resize_Y = Gr_full_resize_Y; Gr_menu_offset_Y = Gr_menu_zoomed_offset_Y = 0.0f; } } } else { if (force_stretch || Cmdline_stretch_menu) { Gr_resize_X = Gr_full_resize_X; Gr_resize_Y = Gr_full_resize_Y; Gr_menu_offset_X = Gr_menu_zoomed_offset_X = 0.0f; Gr_menu_offset_Y = Gr_menu_zoomed_offset_Y = 0.0f; } else { float aspect_quotient = ((float)max_w / (float)max_h) / ((float)w / (float)h); Gr_resize_X = Gr_full_resize_X / ((aspect_quotient > 1.0f) ? aspect_quotient : 1.0f); Gr_resize_Y = Gr_full_resize_Y * ((aspect_quotient < 1.0f) ? aspect_quotient : 1.0f); Gr_menu_offset_X = Gr_menu_zoomed_offset_X = (aspect_quotient > 1.0f) ? ((max_w - w * Gr_resize_X) / 2.0f) : 0.0f; Gr_menu_offset_Y = Gr_menu_zoomed_offset_Y = (aspect_quotient < 1.0f) ? ((max_h - h * Gr_resize_Y) / 2.0f) : 0.0f; } } gr_screen.custom_size = (w != max_w || h != max_h); if (gr_screen.rendering_to_texture == -1) { gr_screen.max_w_unscaled = w; gr_screen.max_h_unscaled = h; if (do_zoom) { gr_screen.max_w_unscaled_zoomed = gr_screen.max_w_unscaled + fl2i(Gr_menu_offset_X * 2.0f / Gr_resize_X); gr_screen.max_h_unscaled_zoomed = gr_screen.max_h_unscaled + fl2i(Gr_menu_offset_Y * 2.0f / Gr_resize_Y); if (gr_screen.max_w_unscaled_zoomed > gr_screen.max_w_unscaled) { gr_screen.max_w_unscaled_zoomed = gr_screen.max_w_unscaled; } if (gr_screen.max_h_unscaled_zoomed > gr_screen.max_h_unscaled) { gr_screen.max_h_unscaled_zoomed = gr_screen.max_h_unscaled; } } else { gr_screen.max_w_unscaled_zoomed = gr_screen.max_w_unscaled; gr_screen.max_h_unscaled_zoomed = gr_screen.max_h_unscaled; } } } void gr_reset_screen_scale() { Gr_full_resize_X = Gr_save_full_resize_X; Gr_full_resize_Y = Gr_save_full_resize_Y; Gr_resize_X = Gr_save_resize_X; Gr_resize_Y = Gr_save_resize_Y; Gr_menu_offset_X = Gr_save_menu_offset_X; Gr_menu_offset_Y = Gr_save_menu_offset_Y; Gr_menu_zoomed_offset_X = Gr_save_menu_zoomed_offset_X; Gr_menu_zoomed_offset_Y = Gr_save_menu_zoomed_offset_Y; gr_screen.custom_size = Save_custom_screen_size; if (gr_screen.rendering_to_texture == -1) { gr_screen.max_w_unscaled = gr_screen.max_w_unscaled_zoomed = (gr_screen.res == GR_1024) ? 1024 : 640; gr_screen.max_h_unscaled = gr_screen.max_h_unscaled_zoomed = (gr_screen.res == GR_1024) ? 768 : 480; } } /** * This function is to be called if you wish to scale GR_1024 or GR_640 x and y positions or * lengths in order to keep the correctly scaled to nonstandard resolutions * * @param x X value, can be NULL * @param y Y value, can be NULL * @param w width, can be NULL * @param h height, can be NULL * @param resize_mode * @return always true unless error */ bool gr_resize_screen_pos(int *x, int *y, int *w, int *h, int resize_mode) { if ( resize_mode == GR_RESIZE_NONE || (!gr_screen.custom_size && (gr_screen.rendering_to_texture == -1)) ) { return false; } float xy_tmp = 0.0f; if ( x ) { switch (resize_mode) { case GR_RESIZE_FULL: xy_tmp = (*x) * Gr_full_resize_X; break; case GR_RESIZE_MENU: xy_tmp = (*x) * Gr_resize_X + Gr_menu_offset_X; break; case GR_RESIZE_MENU_ZOOMED: xy_tmp = (*x) * Gr_resize_X + Gr_menu_zoomed_offset_X; break; case GR_RESIZE_MENU_NO_OFFSET: xy_tmp = (*x) * Gr_resize_X; break; } (*x) = fl2ir(xy_tmp); } if ( y ) { switch (resize_mode) { case GR_RESIZE_FULL: xy_tmp = (*y) * Gr_full_resize_Y; break; case GR_RESIZE_MENU: xy_tmp = (*y) * Gr_resize_Y + Gr_menu_offset_Y; break; case GR_RESIZE_MENU_ZOOMED: xy_tmp = (*y) * Gr_resize_Y + Gr_menu_zoomed_offset_Y; break; case GR_RESIZE_MENU_NO_OFFSET: xy_tmp = (*y) * Gr_resize_Y; break; } (*y) = fl2ir(xy_tmp); } if ( w ) { xy_tmp = (*w) * ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_X : Gr_resize_X); (*w) = fl2ir(xy_tmp); } if ( h ) { xy_tmp = (*h) * ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_Y : Gr_resize_Y); (*h) = fl2ir(xy_tmp); } return true; } /** * * @param x X value, can be NULL * @param y Y value, can be NULL * @param w width, can be NULL * @param h height, can be NULL * @param resize_mode * @return always true unless error */ bool gr_unsize_screen_pos(int *x, int *y, int *w, int *h, int resize_mode) { if ( resize_mode == GR_RESIZE_NONE || (!gr_screen.custom_size && (gr_screen.rendering_to_texture == -1)) ) { return false; } float xy_tmp = 0.0f; if ( x ) { switch (resize_mode) { case GR_RESIZE_FULL: xy_tmp = (*x) / Gr_full_resize_X; break; case GR_RESIZE_MENU: xy_tmp = ((*x) - Gr_menu_offset_X) / Gr_resize_X; break; case GR_RESIZE_MENU_ZOOMED: xy_tmp = ((*x) - Gr_menu_zoomed_offset_X) / Gr_resize_X; break; case GR_RESIZE_MENU_NO_OFFSET: xy_tmp = (*x) / Gr_resize_X; break; } (*x) = fl2ir(xy_tmp); } if ( y ) { switch (resize_mode) { case GR_RESIZE_FULL: xy_tmp = (*y) / Gr_full_resize_Y; break; case GR_RESIZE_MENU: xy_tmp = ((*y) - Gr_menu_offset_Y) / Gr_resize_Y; break; case GR_RESIZE_MENU_ZOOMED: xy_tmp = ((*y) - Gr_menu_zoomed_offset_Y) / Gr_resize_Y; break; case GR_RESIZE_MENU_NO_OFFSET: xy_tmp = (*y) / Gr_resize_Y; break; } (*y) = fl2ir(xy_tmp); } if ( w ) { xy_tmp = (*w) / ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_X : Gr_resize_X); (*w) = fl2ir(xy_tmp); } if ( h ) { xy_tmp = (*h) / ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_Y : Gr_resize_Y); (*h) = fl2ir(xy_tmp); } return true; } /** * This function is to be called if you wish to scale GR_1024 or GR_640 x and y positions or * lengths in order to keep the correctly scaled to nonstandard resolutions * * @param x X value, can be NULL * @param y Y value, can be NULL * @param w width, can be NULL * @param h height, can be NULL * @param resize_mode * @return always true unless error */ bool gr_resize_screen_posf(float *x, float *y, float *w, float *h, int resize_mode) { if ( resize_mode == GR_RESIZE_NONE || (!gr_screen.custom_size && (gr_screen.rendering_to_texture == -1)) ) { return false; } float xy_tmp = 0.0f; if ( x ) { switch (resize_mode) { case GR_RESIZE_FULL: xy_tmp = (*x) * Gr_full_resize_X; break; case GR_RESIZE_MENU: xy_tmp = (*x) * Gr_resize_X + Gr_menu_offset_X; break; case GR_RESIZE_MENU_ZOOMED: xy_tmp = (*x) * Gr_resize_X + Gr_menu_zoomed_offset_X; break; case GR_RESIZE_MENU_NO_OFFSET: xy_tmp = (*x) * Gr_resize_X; break; } (*x) = xy_tmp; } if ( y ) { switch (resize_mode) { case GR_RESIZE_FULL: xy_tmp = (*y) * Gr_full_resize_Y; break; case GR_RESIZE_MENU: xy_tmp = (*y) * Gr_resize_Y + Gr_menu_offset_Y; break; case GR_RESIZE_MENU_ZOOMED: xy_tmp = (*y) * Gr_resize_Y + Gr_menu_zoomed_offset_Y; break; case GR_RESIZE_MENU_NO_OFFSET: xy_tmp = (*y) * Gr_resize_Y; break; } (*y) = xy_tmp; } if ( w ) { xy_tmp = (*w) * ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_X : Gr_resize_X); (*w) = xy_tmp; } if ( h ) { xy_tmp = (*h) * ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_Y : Gr_resize_Y); (*h) = xy_tmp; } return true; } /** * * @param x X value, can be NULL * @param y Y value, can be NULL * @param w width, can be NULL * @param h height, can be NULL * @param resize_mode * @return always true unless error */ bool gr_unsize_screen_posf(float *x, float *y, float *w, float *h, int resize_mode) { if ( resize_mode == GR_RESIZE_NONE || (!gr_screen.custom_size && (gr_screen.rendering_to_texture == -1)) ) { return false; } float xy_tmp = 0.0f; if ( x ) { switch (resize_mode) { case GR_RESIZE_FULL: xy_tmp = (*x) / Gr_full_resize_X; break; case GR_RESIZE_MENU: xy_tmp = ((*x) - Gr_menu_offset_X) / Gr_resize_X; break; case GR_RESIZE_MENU_ZOOMED: xy_tmp = ((*x) - Gr_menu_zoomed_offset_X) / Gr_resize_X; break; case GR_RESIZE_MENU_NO_OFFSET: xy_tmp = (*x) / Gr_resize_X; break; } (*x) = xy_tmp; } if ( y ) { switch (resize_mode) { case GR_RESIZE_FULL: xy_tmp = (*y) / Gr_full_resize_Y; break; case GR_RESIZE_MENU: xy_tmp = ((*y) - Gr_menu_offset_Y) / Gr_resize_Y; break; case GR_RESIZE_MENU_ZOOMED: xy_tmp = ((*y) - Gr_menu_zoomed_offset_Y) / Gr_resize_Y; break; case GR_RESIZE_MENU_NO_OFFSET: xy_tmp = (*y) / Gr_resize_Y; break; } (*y) = xy_tmp; } if ( w ) { xy_tmp = (*w) / ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_X : Gr_resize_X); (*w) = xy_tmp; } if ( h ) { xy_tmp = (*h) / ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_Y : Gr_resize_Y); (*h) = xy_tmp; } return true; } void gr_close() { if ( !Gr_inited ) { return; } palette_flush(); switch (gr_screen.mode) { case GR_OPENGL: gr_opengl_cleanup(); break; case GR_STUB: break; default: Int3(); // Invalid graphics mode } gr_font_close(); Gr_inited = 0; } /** * Set screen clear color */ DCF(clear_color, "set clear color r, g, b") { ubyte r, g, b; dc_stuff_ubyte(&r); dc_stuff_ubyte(&g); dc_stuff_ubyte(&b); // set the color gr_set_clear_color(r, g, b); } void gr_set_palette_internal( const char *name, ubyte * palette, int restrict_font_to_128 ) { if ( palette == NULL ) { // Create a default palette int r,g,b,i; i = 0; for (r=0; r<6; r++ ) for (g=0; g<6; g++ ) for (b=0; b<6; b++ ) { Gr_current_palette[i*3+0] = (unsigned char)(r*51); Gr_current_palette[i*3+1] = (unsigned char)(g*51); Gr_current_palette[i*3+2] = (unsigned char)(b*51); i++; } for ( i=216;i<256; i++ ) { Gr_current_palette[i*3+0] = (unsigned char)((i-216)*6); Gr_current_palette[i*3+1] = (unsigned char)((i-216)*6); Gr_current_palette[i*3+2] = (unsigned char)((i-216)*6); } memmove( Gr_original_palette, Gr_current_palette, 768 ); } else { memmove( Gr_original_palette, palette, 768 ); memmove( Gr_current_palette, palette, 768 ); } if ( Gr_inited ) { if (gr_screen.gf_set_palette) { (*gr_screen.gf_set_palette)(Gr_current_palette, restrict_font_to_128 ); // Since the palette set code might shuffle the palette, // reload it into the source palette if ( palette ) { memmove( palette, Gr_current_palette, 768 ); } } // Update Palette Manager tables memmove( gr_palette, Gr_current_palette, 768 ); palette_update(name, restrict_font_to_128); } } void gr_set_palette( const char *name, ubyte * palette, int restrict_font_to_128 ) { char *p; palette_flush(); strcpy_s( Gr_current_palette_name, name ); p = strchr( Gr_current_palette_name, '.' ); if ( p ) *p = 0; gr_screen.signature = Gr_signature++; gr_set_palette_internal( name, palette, restrict_font_to_128 ); } void gr_screen_resize(int width, int height) { // this should only be called from FRED!! if ( !Fred_running ) { Int3(); return; } gr_screen.save_max_w = gr_screen.max_w = gr_screen.max_w_unscaled = gr_screen.max_w_unscaled_zoomed = width; gr_screen.save_max_h = gr_screen.max_h = gr_screen.max_h_unscaled = gr_screen.max_h_unscaled_zoomed = height; gr_screen.offset_x = gr_screen.offset_x_unscaled = 0; gr_screen.offset_y = gr_screen.offset_y_unscaled = 0; gr_screen.clip_left = gr_screen.clip_left_unscaled = 0; gr_screen.clip_top = gr_screen.clip_top_unscaled = 0; gr_screen.clip_right = gr_screen.clip_right_unscaled = gr_screen.max_w - 1; gr_screen.clip_bottom = gr_screen.clip_bottom_unscaled = gr_screen.max_h - 1; gr_screen.clip_width = gr_screen.clip_width_unscaled = gr_screen.max_w; gr_screen.clip_height = gr_screen.clip_height_unscaled = gr_screen.max_h; gr_screen.clip_aspect = i2fl(gr_screen.clip_width) / i2fl(gr_screen.clip_height); if (gr_screen.custom_size) { gr_unsize_screen_pos( &gr_screen.max_w_unscaled, &gr_screen.max_h_unscaled ); gr_unsize_screen_pos( &gr_screen.max_w_unscaled_zoomed, &gr_screen.max_h_unscaled_zoomed ); gr_unsize_screen_pos( &gr_screen.clip_right_unscaled, &gr_screen.clip_bottom_unscaled ); gr_unsize_screen_pos( &gr_screen.clip_width_unscaled, &gr_screen.clip_height_unscaled ); } gr_screen.save_max_w_unscaled = gr_screen.max_w_unscaled; gr_screen.save_max_h_unscaled = gr_screen.max_h_unscaled; gr_screen.save_max_w_unscaled_zoomed = gr_screen.max_w_unscaled_zoomed; gr_screen.save_max_h_unscaled_zoomed = gr_screen.max_h_unscaled_zoomed; if (gr_screen.mode == GR_OPENGL) { extern void opengl_setup_viewport(); opengl_setup_viewport(); } } static bool gr_init_sub(int mode, int width, int height, int depth) { int res = GR_1024; bool rc = false; memset( &gr_screen, 0, sizeof(screen) ); if ( ((width == 640) && (height == 480)) || ((width == 1024) && (height == 768)) ) { gr_screen.custom_size = false; } else { gr_screen.custom_size = true; } if ( (width >= GR_1024_THRESHOLD_WIDTH) && (height >= GR_1024_THRESHOLD_HEIGHT) ) { res = GR_1024; } else { res = GR_640; } if (Fred_running) { gr_screen.custom_size = false; res = GR_640; mode = GR_OPENGL; } Save_custom_screen_size = gr_screen.custom_size; Gr_save_full_resize_X = Gr_full_resize_X = (float)width / ((res == GR_1024) ? 1024.0f : 640.0f); Gr_save_full_resize_Y = Gr_full_resize_Y = (float)height / ((res == GR_1024) ? 768.0f : 480.0f); if (gr_screen.custom_size && !Cmdline_stretch_menu) { float aspect_quotient = ((float)width / (float)height) / (4.0f / 3.0f); Gr_save_resize_X = Gr_resize_X = Gr_full_resize_X / ((aspect_quotient > 1.0f) ? aspect_quotient : 1.0f); Gr_save_resize_Y = Gr_resize_Y = Gr_full_resize_Y * ((aspect_quotient < 1.0f) ? aspect_quotient : 1.0f); Gr_save_menu_offset_X = Gr_menu_offset_X = (aspect_quotient > 1.0f) ? ((width - width / aspect_quotient) / 2.0f) : 0.0f; Gr_save_menu_offset_Y = Gr_menu_offset_Y = (aspect_quotient < 1.0f) ? ((height - height * aspect_quotient) / 2.0f) : 0.0f; } else { Gr_save_resize_X = Gr_resize_X = Gr_full_resize_X; Gr_save_resize_Y = Gr_resize_Y = Gr_full_resize_Y; Gr_save_menu_offset_X = Gr_menu_offset_X = 0.0f; Gr_save_menu_offset_Y = Gr_menu_offset_Y = 0.0f; } Gr_save_menu_zoomed_offset_X = Gr_menu_zoomed_offset_X = Gr_menu_offset_X; Gr_save_menu_zoomed_offset_Y = Gr_menu_zoomed_offset_Y = Gr_menu_offset_Y; gr_screen.signature = Gr_signature++; gr_screen.bits_per_pixel = depth; gr_screen.bytes_per_pixel= depth / 8; gr_screen.rendering_to_texture = -1; gr_screen.recording_state_block = false; gr_screen.envmap_render_target = -1; gr_screen.mode = mode; gr_screen.res = res; gr_screen.aspect = 1.0f; // Normal PC screen gr_screen.save_max_w = gr_screen.max_w = gr_screen.max_w_unscaled = gr_screen.max_w_unscaled_zoomed = width; gr_screen.save_max_h = gr_screen.max_h = gr_screen.max_h_unscaled = gr_screen.max_h_unscaled_zoomed = height; gr_screen.offset_x = gr_screen.offset_x_unscaled = 0; gr_screen.offset_y = gr_screen.offset_y_unscaled = 0; gr_screen.clip_left = gr_screen.clip_left_unscaled = 0; gr_screen.clip_top = gr_screen.clip_top_unscaled = 0; gr_screen.clip_right = gr_screen.clip_right_unscaled = gr_screen.max_w - 1; gr_screen.clip_bottom = gr_screen.clip_bottom_unscaled = gr_screen.max_h - 1; gr_screen.clip_width = gr_screen.clip_width_unscaled = gr_screen.max_w; gr_screen.clip_height = gr_screen.clip_height_unscaled = gr_screen.max_h; gr_screen.clip_aspect = i2fl(gr_screen.clip_width) / i2fl(gr_screen.clip_height); gr_screen.clip_center_x = (gr_screen.clip_left + gr_screen.clip_right) * 0.5f; gr_screen.clip_center_y = (gr_screen.clip_top + gr_screen.clip_bottom) * 0.5f; if (gr_screen.custom_size) { gr_unsize_screen_pos( &gr_screen.max_w_unscaled, &gr_screen.max_h_unscaled ); gr_unsize_screen_pos( &gr_screen.max_w_unscaled_zoomed, &gr_screen.max_h_unscaled_zoomed ); gr_unsize_screen_pos( &gr_screen.clip_right_unscaled, &gr_screen.clip_bottom_unscaled ); gr_unsize_screen_pos( &gr_screen.clip_width_unscaled, &gr_screen.clip_height_unscaled ); } gr_screen.save_max_w_unscaled = gr_screen.max_w_unscaled; gr_screen.save_max_h_unscaled = gr_screen.max_h_unscaled; gr_screen.save_max_w_unscaled_zoomed = gr_screen.max_w_unscaled_zoomed; gr_screen.save_max_h_unscaled_zoomed = gr_screen.max_h_unscaled_zoomed; switch (mode) { case GR_OPENGL: rc = gr_opengl_init(); break; case GR_STUB: rc = gr_stub_init(); break; default: Int3(); // Invalid graphics mode } if ( !rc ) { return false; } return true; } bool gr_init(int d_mode, int d_width, int d_height, int d_depth) { int width = 1024, height = 768, depth = 32, mode = GR_OPENGL; const char *ptr = NULL; if ( !Gr_inited ) { atexit(gr_close); } // If already inited, shutdown the previous graphics if (Gr_inited) { switch (gr_screen.mode) { case GR_OPENGL: gr_opengl_cleanup(); break; case GR_STUB: break; default: Int3(); // Invalid graphics mode } } // We cannot continue without this, quit, but try to help the user out first ptr = os_config_read_string(NULL, NOX("VideocardFs2open"), NULL); // if we don't have a config string then construct one, using OpenGL 1024x768 32-bit as the default if (ptr == NULL) { // If we don't have a display mode, use SDL to get default settings // We need to initialize SDL to do this if (SDL_InitSubSystem(SDL_INIT_VIDEO) == 0) { SDL_DisplayMode mode; if (SDL_GetDesktopDisplayMode(0, &mode) == 0) { width = mode.w; height = mode.h; int sdlBits = SDL_BITSPERPIXEL(mode.format); if (SDL_ISPIXELFORMAT_ALPHA(mode.format)) { depth = sdlBits; } else { // Fix a few values if (sdlBits == 24) { depth = 32; } else if (sdlBits == 15) { depth = 16; } else { depth = sdlBits; } } SCP_string videomode; sprintf(videomode, "OGL -(%dx%d)x%d bit", width, height, depth); os_config_write_string(NULL, NOX("VideocardFs2open"), videomode.c_str()); } } } else { Assert(ptr != NULL); // NOTE: The "ptr+5" is to skip over the initial "????-" in the video string. // If the format of that string changes you'll have to change this too!!! if (sscanf(ptr + 5, "(%dx%d)x%d ", &width, &height, &depth) != 3) { Error(LOCATION, "Can't understand 'VideocardFs2open' config entry!"); } } if (Cmdline_res != NULL) { int tmp_width = 0; int tmp_height = 0; if ( sscanf(Cmdline_res, "%dx%d", &tmp_width, &tmp_height) == 2 ) { width = tmp_width; height = tmp_height; } } if (d_mode == GR_DEFAULT) { // OpenGL should be default mode = GR_OPENGL; } else { mode = d_mode; } // see if we passed good values, and use those instead of the config settings if ( (d_width != GR_DEFAULT) && (d_height != GR_DEFAULT) ) { width = d_width; height = d_height; } if (d_depth != GR_DEFAULT) { depth = d_depth; } // check for hi-res interface files so that we can verify our width/height is correct bool has_sparky_hi = (cf_exists_full("2_ChoosePilot-m.pcx", CF_TYPE_ANY) && cf_exists_full("2_TechShipData-m.pcx", CF_TYPE_ANY)); // if we don't have it then fall back to 640x480 mode instead if (!has_sparky_hi) { if ((width == 1024) && (height == 768)) { width = 640; height = 480; } else { width = 800; height = 600; } } // if we are in standalone mode then just use special defaults if (Is_standalone) { mode = GR_STUB; width = 640; height = 480; depth = 16; } // These compiler macros will force windowed mode at the specified resolution if // built in debug mode. This helps if you run with the debugger active as the // game won't be switching from fullscreen to minimized every time you hit a breakpoint or // warning message. #ifdef _DEBUG #ifdef _FORCE_DEBUG_WIDESCREEN width = 1280; height = 800; depth = 32; Cmdline_window = 1; #elif defined(_FORCE_DEBUG_1024) width = 1024; height = 768; depth = 32; Cmdline_window = 1; #elif defined(_FORCE_DEBUG_640) width = 640; height = 480; depth = 32; Cmdline_window = 1; #endif #endif // now try to actually init everything... if ( gr_init_sub(mode, width, height, depth) == false ) { return false; } gr_set_palette_internal(Gr_current_palette_name, NULL, 0); bm_init(); io::mouse::CursorManager::init(); // load the web pointer cursor bitmap if (Web_cursor == NULL) { //if it still hasn't loaded then this usually means that the executable isn't in the same directory as the main fs2 install if ( (Web_cursor = io::mouse::CursorManager::get()->loadCursor("cursorweb", true)) == NULL ) { Error(LOCATION, "\nWeb cursor bitmap not found. This is most likely due to one of three reasons:\n" "\t1) You're running FreeSpace Open from somewhere other than your FreeSpace 2 folder;\n" "\t2) You've somehow corrupted your FreeSpace 2 installation, e.g. by modifying or removing the retail VP files;\n" "\t3) You haven't installed FreeSpace 2 at all. (Note that installing FreeSpace Open does NOT remove the need for a FreeSpace 2 installation.)\n" "Number 1 can be fixed by simply moving the FreeSpace Open executable file to the FreeSpace 2 folder. Numbers 2 and 3 can be fixed by installing or reinstalling FreeSpace 2.\n"); } } mprintf(("GRAPHICS: Initializing default colors...\n")); gr_set_color(0,0,0); gr_set_clear_color(0, 0, 0); gr_set_shader(NULL); os_set_title(Osreg_title); Gr_inited = 1; return true; } void gr_force_windowed() { if ( !Gr_inited ) { return; } switch( gr_screen.mode ) { case GR_OPENGL: break; case GR_STUB: break; default: Int3(); // Invalid graphics mode } if ( Os_debugger_running ) { os_sleep(1000); } } int gr_activated = 0; void gr_activate(int active) { if (gr_activated == active) { return; } gr_activated = active; if ( !Gr_inited ) { return; } switch( gr_screen.mode ) { case GR_OPENGL: extern void gr_opengl_activate(int active); gr_opengl_activate(active); break; case GR_STUB: break; default: Int3(); // Invalid graphics mode } } // color stuff void gr_get_color( int *r, int *g, int *b ) { if (r) *r = gr_screen.current_color.red; if (g) *g = gr_screen.current_color.green; if (b) *b = gr_screen.current_color.blue; } void gr_init_color(color *c, int r, int g, int b) { CAP(r, 0, 255); CAP(g, 0, 255); CAP(b, 0, 255); c->screen_sig = gr_screen.signature; c->red = (ubyte)r; c->green = (ubyte)g; c->blue = (ubyte)b; c->alpha = 255; c->ac_type = AC_TYPE_NONE; c->alphacolor = -1; c->is_alphacolor = 0; c->magic = 0xAC01; c->raw8 = 0; } void gr_init_alphacolor( color *clr, int r, int g, int b, int alpha, int type ) { CAP(r, 0, 255); CAP(g, 0, 255); CAP(b, 0, 255); CAP(alpha, 0, 255); gr_init_color( clr, r, g, b ); clr->alpha = (ubyte)alpha; clr->ac_type = (ubyte)type; clr->alphacolor = -1; clr->is_alphacolor = 1; } void gr_set_color( int r, int g, int b ) { Assert((r >= 0) && (r < 256)); Assert((g >= 0) && (g < 256)); Assert((b >= 0) && (b < 256)); gr_init_color( &gr_screen.current_color, r, g, b ); } void gr_set_color_fast(color *dst) { if ( dst->screen_sig != gr_screen.signature ) { if (dst->is_alphacolor) { gr_init_alphacolor( dst, dst->red, dst->green, dst->blue, dst->alpha, dst->ac_type ); } else { gr_init_color( dst, dst->red, dst->green, dst->blue ); } } gr_screen.current_color = *dst; } // shader functions void gr_create_shader(shader *shade, ubyte r, ubyte g, ubyte b, ubyte c ) { shade->screen_sig = gr_screen.signature; shade->r = r; shade->g = g; shade->b = b; shade->c = c; } void gr_set_shader(shader *shade) { if (shade) { if (shade->screen_sig != gr_screen.signature) { gr_create_shader( shade, shade->r, shade->g, shade->b, shade->c ); } gr_screen.current_shader = *shade; } else { gr_create_shader( &gr_screen.current_shader, 0, 0, 0, 0 ); } } // new bitmap functions void gr_bitmap(int _x, int _y, int resize_mode) { int _w, _h; float x, y, w, h; vertex verts[4]; if (gr_screen.mode == GR_STUB) { return; } bm_get_info(gr_screen.current_bitmap, &_w, &_h, NULL, NULL, NULL); x = i2fl(_x); y = i2fl(_y); w = i2fl(_w); h = i2fl(_h); // I will tidy this up later - RT if ( resize_mode != GR_RESIZE_NONE && (gr_screen.custom_size || (gr_screen.rendering_to_texture != -1)) ) { gr_resize_screen_posf(&x, &y, &w, &h, resize_mode); } memset(verts, 0, sizeof(verts)); verts[0].screen.xyw.x = x; verts[0].screen.xyw.y = y; verts[0].texture_position.u = 0.0f; verts[0].texture_position.v = 0.0f; verts[1].screen.xyw.x = x + w; verts[1].screen.xyw.y = y; verts[1].texture_position.u = 1.0f; verts[1].texture_position.v = 0.0f; verts[2].screen.xyw.x = x + w; verts[2].screen.xyw.y = y + h; verts[2].texture_position.u = 1.0f; verts[2].texture_position.v = 1.0f; verts[3].screen.xyw.x = x; verts[3].screen.xyw.y = y + h; verts[3].texture_position.u = 0.0f; verts[3].texture_position.v = 1.0f; // turn off zbuffering int saved_zbuffer_mode = gr_zbuffer_get(); gr_zbuffer_set(GR_ZBUFF_NONE); gr_render(4, verts, TMAP_FLAG_TEXTURED | TMAP_FLAG_INTERFACE); gr_zbuffer_set(saved_zbuffer_mode); } void gr_bitmap_uv(int _x, int _y, int _w, int _h, float _u0, float _v0, float _u1, float _v1, int resize_mode) { float x, y, w, h; vertex verts[4]; if (gr_screen.mode == GR_STUB) { return; } x = i2fl(_x); y = i2fl(_y); w = i2fl(_w); h = i2fl(_h); // I will tidy this up later - RT if ( resize_mode != GR_RESIZE_NONE && (gr_screen.custom_size || (gr_screen.rendering_to_texture != -1)) ) { gr_resize_screen_posf(&x, &y, &w, &h, resize_mode); } memset(verts, 0, sizeof(verts)); verts[0].screen.xyw.x = x; verts[0].screen.xyw.y = y; verts[0].texture_position.u = _u0; verts[0].texture_position.v = _v0; verts[1].screen.xyw.x = x + w; verts[1].screen.xyw.y = y; verts[1].texture_position.u = _u1; verts[1].texture_position.v = _v0; verts[2].screen.xyw.x = x + w; verts[2].screen.xyw.y = y + h; verts[2].texture_position.u = _u1; verts[2].texture_position.v = _v1; verts[3].screen.xyw.x = x; verts[3].screen.xyw.y = y + h; verts[3].texture_position.u = _u0; verts[3].texture_position.v = _v1; // turn off zbuffering int saved_zbuffer_mode = gr_zbuffer_get(); gr_zbuffer_set(GR_ZBUFF_NONE); gr_render(4, verts, TMAP_FLAG_TEXTURED | TMAP_FLAG_INTERFACE); gr_zbuffer_set(saved_zbuffer_mode); } // NEW new bitmap functions -Bobboau void gr_bitmap_list(bitmap_2d_list* list, int n_bm, int resize_mode) { for (int i = 0; i < n_bm; i++) { bitmap_2d_list *l = &list[i]; bm_get_info(gr_screen.current_bitmap, &l->w, &l->h, NULL, NULL, NULL); if ( resize_mode != GR_RESIZE_NONE && (gr_screen.custom_size || (gr_screen.rendering_to_texture != -1)) ) { gr_resize_screen_pos(&l->x, &l->y, &l->w, &l->h, resize_mode); } } g3_draw_2d_poly_bitmap_list(list, n_bm, TMAP_FLAG_INTERFACE); } // _->NEW<-_ NEW new bitmap functions -Bobboau //takes a list of rectangles that have assosiated rectangles in a texture void gr_bitmap_list(bitmap_rect_list* list, int n_bm, int resize_mode) { for(int i = 0; i < n_bm; i++) { bitmap_2d_list *l = &list[i].screen_rect; // if no valid hight or width values were given get some from the bitmap if ( (l->w <= 0) || (l->h <= 0) ) { bm_get_info(gr_screen.current_bitmap, &l->w, &l->h, NULL, NULL, NULL); } if ( resize_mode != GR_RESIZE_NONE && (gr_screen.custom_size || (gr_screen.rendering_to_texture != -1)) ) { gr_resize_screen_pos(&l->x, &l->y, &l->w, &l->h, resize_mode); } } g3_draw_2d_poly_bitmap_rect_list(list, n_bm, TMAP_FLAG_INTERFACE); } /** * Given endpoints, and thickness, calculate coords of the endpoint */ void gr_pline_helper(vec3d *out, vec3d *in1, vec3d *in2, int thickness) { vec3d slope; // slope of the line if(vm_vec_same(in1, in2)) { slope = vmd_zero_vector; } else { vm_vec_sub(&slope, in2, in1); float temp = -slope.xyz.x; slope.xyz.x = slope.xyz.y; slope.xyz.y = temp; vm_vec_normalize(&slope); } // get the points vm_vec_scale_add(out, in1, &slope, (float)thickness); } /** * Special function for drawing polylines. * * This function is specifically intended for polylines where each section * is no more than 90 degrees away from a previous section. * Moreover, it is _really_ intended for use with 45 degree angles. */ void gr_pline_special(SCP_vector *pts, int thickness,int resize_mode) { vec3d s1, s2, e1, e2, dir; vec3d last_e1, last_e2; vertex v[4]; vertex *verts[4] = {&v[0], &v[1], &v[2], &v[3]}; int saved_zbuffer_mode, idx; int started_frame = 0; int num_pts = pts->size(); // if we have less than 2 pts, bail if(num_pts < 2) { return; } extern int G3_count; if(G3_count == 0) { g3_start_frame(1); started_frame = 1; } // turn off zbuffering saved_zbuffer_mode = gr_zbuffer_get(); gr_zbuffer_set(GR_ZBUFF_NONE); // turn off culling int cull = gr_set_cull(0); // draw each section last_e1 = vmd_zero_vector; last_e2 = vmd_zero_vector; int j; for(idx=0; idxat(idx); // start 1 (on the line) e1 = pts->at(idx+1); // end 1 (on the line) gr_pline_helper(&s2, &s1, &e1, thickness); // start 2 vm_vec_sub(&dir, &e1, &s1); vm_vec_add(&e2, &s2, &dir); // end 2 // stuff coords v[0].screen.xyw.x = (float)ceil(s1.xyz.x); v[0].screen.xyw.y = (float)ceil(s1.xyz.y); v[0].screen.xyw.w = 0.0f; v[0].texture_position.u = 0.5f; v[0].texture_position.v = 0.5f; v[0].flags = PF_PROJECTED; v[0].codes = 0; v[0].r = gr_screen.current_color.red; v[0].g = gr_screen.current_color.green; v[0].b = gr_screen.current_color.blue; v[1].screen.xyw.x = (float)ceil(s2.xyz.x); v[1].screen.xyw.y = (float)ceil(s2.xyz.y); v[1].screen.xyw.w = 0.0f; v[1].texture_position.u = 0.5f; v[1].texture_position.v = 0.5f; v[1].flags = PF_PROJECTED; v[1].codes = 0; v[1].r = gr_screen.current_color.red; v[1].g = gr_screen.current_color.green; v[1].b = gr_screen.current_color.blue; v[2].screen.xyw.x = (float)ceil(e2.xyz.x); v[2].screen.xyw.y = (float)ceil(e2.xyz.y); v[2].screen.xyw.w = 0.0f; v[2].texture_position.u = 0.5f; v[2].texture_position.v = 0.5f; v[2].flags = PF_PROJECTED; v[2].codes = 0; v[2].r = gr_screen.current_color.red; v[2].g = gr_screen.current_color.green; v[2].b = gr_screen.current_color.blue; v[3].screen.xyw.x = (float)ceil(e1.xyz.x); v[3].screen.xyw.y = (float)ceil(e1.xyz.y); v[3].screen.xyw.w = 0.0f; v[3].texture_position.u = 0.5f; v[3].texture_position.v = 0.5f; v[3].flags = PF_PROJECTED; v[3].codes = 0; v[3].r = gr_screen.current_color.red; v[3].g = gr_screen.current_color.green; v[3].b = gr_screen.current_color.blue; //We could really do this better...but oh well. _WMC if(resize_mode != GR_RESIZE_NONE) { for(j=0;j<4;j++) { gr_resize_screen_posf(&v[j].screen.xyw.x,&v[j].screen.xyw.y,NULL,NULL,resize_mode); } } // draw the polys g3_draw_poly_constant_sw(4, verts, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB, 0.1f); // if we're past the first section, draw a "patch" triangle to fill any gaps if(idx > 0) { // stuff coords v[0].screen.xyw.x = (float)ceil(s1.xyz.x); v[0].screen.xyw.y = (float)ceil(s1.xyz.y); v[0].screen.xyw.w = 0.0f; v[0].texture_position.u = 0.5f; v[0].texture_position.v = 0.5f; v[0].flags = PF_PROJECTED; v[0].codes = 0; v[0].r = gr_screen.current_color.red; v[0].g = gr_screen.current_color.green; v[0].b = gr_screen.current_color.blue; v[1].screen.xyw.x = (float)ceil(s2.xyz.x); v[1].screen.xyw.y = (float)ceil(s2.xyz.y); v[1].screen.xyw.w = 0.0f; v[1].texture_position.u = 0.5f; v[1].texture_position.v = 0.5f; v[1].flags = PF_PROJECTED; v[1].codes = 0; v[1].r = gr_screen.current_color.red; v[1].g = gr_screen.current_color.green; v[1].b = gr_screen.current_color.blue; v[2].screen.xyw.x = (float)ceil(last_e2.xyz.x); v[2].screen.xyw.y = (float)ceil(last_e2.xyz.y); v[2].screen.xyw.w = 0.0f; v[2].texture_position.u = 0.5f; v[2].texture_position.v = 0.5f; v[2].flags = PF_PROJECTED; v[2].codes = 0; v[2].r = gr_screen.current_color.red; v[2].g = gr_screen.current_color.green; v[2].b = gr_screen.current_color.blue; //Inefficiency or flexibility? you be the judge -WMC if(resize_mode != GR_RESIZE_NONE) { for(j=0;j<3;j++) { gr_resize_screen_posf(&v[j].screen.xyw.x,&v[j].screen.xyw.y,NULL,NULL,resize_mode); } } g3_draw_poly_constant_sw(3, verts, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB, 0.1f); } // store our endpoints last_e1 = e1; last_e2 = e2; } if(started_frame) { g3_end_frame(); } // restore zbuffer mode gr_zbuffer_set(saved_zbuffer_mode); // restore culling gr_set_cull(cull); } int poly_list::find_first_vertex(int idx) { vec3d *o_norm = &norm[idx]; vertex *o_vert = &vert[idx]; vec3d *p_norm = &norm[0]; vertex *p_vert = &vert[0]; // we should always equal ourselves, so just use that as the stopping point for (int i = 0; i < idx; i++) { if ( (*p_norm == *o_norm) && (p_vert->world == o_vert->world) && (p_vert->texture_position == o_vert->texture_position) ) { return i; } ++p_norm; ++p_vert; } return idx; } int poly_list::find_first_vertex_fast(int idx) { uint* first_idx = std::lower_bound(sorted_indices, sorted_indices + n_verts, idx, finder(this, NULL, NULL)); if ( first_idx == sorted_indices + n_verts ) { // if this happens then idx was never in the index list to begin with which is not good mprintf(("Sorted index list missing index %d!", idx)); Int3(); return idx; } return *first_idx; } /** * Given a list (plist) find the index within the indexed list that the vert at position idx within list is at */ int poly_list::find_index(poly_list *plist, int idx) { vec3d *o_norm = &plist->norm[idx]; vertex *o_vert = &plist->vert[idx]; vec3d *p_norm = &norm[0]; vertex *p_vert = &vert[0]; for (int i = 0; i < n_verts; i++) { if ( (*p_norm == *o_norm) && (p_vert->world == o_vert->world) && (p_vert->texture_position == o_vert->texture_position)) { return i; } ++p_vert; ++p_norm; } return -1; } int poly_list::find_index_fast(poly_list *plist, int idx) { // searching for an out of bounds index using the finder means we're trying to find the vert and norm we're passing into the finder instance uint* first_idx = std::lower_bound(sorted_indices, sorted_indices + n_verts, n_verts, finder(this, &plist->vert[idx], &plist->norm[idx])); if (first_idx == sorted_indices + n_verts) { return -1; } return *first_idx; } void poly_list::allocate(int _verts) { if (_verts <= currently_allocated) return; if (vert != NULL) { vm_free(vert); vert = NULL; } if (norm != NULL) { vm_free(norm); norm = NULL; } if (tsb != NULL) { vm_free(tsb); tsb = NULL; } if ( submodels != NULL ) { vm_free(submodels); submodels = NULL; } if ( sorted_indices != NULL ) { vm_free(sorted_indices); sorted_indices = NULL; } if (_verts) { vert = (vertex*)vm_malloc(sizeof(vertex) * _verts); norm = (vec3d*)vm_malloc(sizeof(vec3d) * _verts); if (Cmdline_normal) { tsb = (tsb_t*)vm_malloc(sizeof(tsb_t) * _verts); } if ( Use_GLSL >= 3 ) { submodels = (int*)vm_malloc(sizeof(int) * _verts); } sorted_indices = (uint*)vm_malloc(sizeof(uint) * _verts); } n_verts = 0; currently_allocated = _verts; } poly_list::~poly_list() { if (vert != NULL) { vm_free(vert); vert = NULL; } if (norm != NULL) { vm_free(norm); norm = NULL; } if (tsb != NULL) { vm_free(tsb); tsb = NULL; } if ( submodels != NULL ) { vm_free(submodels); submodels = NULL; } if (sorted_indices != NULL) { vm_free(sorted_indices); sorted_indices = NULL; } } void poly_list::calculate_tangent() { vertex *v0, *v1, *v2; vec3d *t0, *t1, *t2; vec3d side0, side1; vec3d vt0, vt1; float deltaU0, deltaV0, deltaU1, deltaV1; vec3d tangent, binormal, cross; float magg, scale; if ( !Cmdline_normal ) { return; } Assert( !(n_verts % 3) ); for (int i = 0; i < n_verts; i += 3) { // vertex (reading) v0 = &vert[i]; v1 = &vert[i+1]; v2 = &vert[i+2]; // tangents (writing) t0 = &tsb[i].tangent; t1 = &tsb[i+1].tangent; t2 = &tsb[i+2].tangent; deltaU0 = v1->texture_position.u - v0->texture_position.u; deltaV0 = v1->texture_position.v - v0->texture_position.v; deltaU1 = v2->texture_position.u - v0->texture_position.u; deltaV1 = v2->texture_position.v - v0->texture_position.v; // quick short circuit for NULL case float n = (deltaU0 * deltaV1) - (deltaU1 * deltaV0); if (n == 0.0f) { // hit NULL, so just set identity tangent = vmd_x_vector; binormal = vmd_y_vector; } else { float blah = 1.0f / n; vm_vec_sub(&side0, &v1->world, &v0->world); vm_vec_sub(&side1, &v2->world, &v0->world); // tangent vm_vec_copy_scale(&vt0, &side0, deltaV1); vm_vec_copy_scale(&vt1, &side1, deltaV0); vm_vec_sub(&tangent, &vt0, &vt1); vm_vec_scale(&tangent, blah); // binormal vm_vec_copy_scale(&vt0, &side0, deltaU1); vm_vec_copy_scale(&vt1, &side1, deltaU0); vm_vec_sub(&binormal, &vt0, &vt1); vm_vec_scale(&binormal, blah); } // orthogonalize tangent (for all 3 verts) magg = vm_vec_dot(&norm[i], &tangent); vm_vec_scale_sub(t0, &tangent, &norm[i], magg); vm_vec_normalize_safe(t0); magg = vm_vec_dot(&norm[i+1], &tangent); vm_vec_scale_sub(t1, &tangent, &norm[i+1], magg); vm_vec_normalize_safe(t1); magg = vm_vec_dot(&norm[i+2], &tangent); vm_vec_scale_sub(t2, &tangent, &norm[i+2], magg); vm_vec_normalize_safe(t2); // compute handedness (for all 3 verts) vm_vec_crossprod(&cross, &norm[i], &tangent); scale = vm_vec_dot(&cross, &binormal); tsb[i].scaler = (scale < 0.0f) ? -1.0f : 1.0f; vm_vec_crossprod(&cross, &norm[i+1], &tangent); scale = vm_vec_dot(&cross, &binormal); tsb[i+1].scaler = (scale < 0.0f) ? -1.0f : 1.0f; vm_vec_crossprod(&cross, &norm[i+2], &tangent); scale = vm_vec_dot(&cross, &binormal); tsb[i+2].scaler = (scale < 0.0f) ? -1.0f : 1.0f; } } static poly_list buffer_list_internal; void poly_list::make_index_buffer(SCP_vector &vertex_list) { int nverts = 0; int j, z = 0; ubyte *nverts_good = NULL; uint t0, t1; t0 = timer_get_milliseconds(); // calculate tangent space data (must be done early) calculate_tangent(); // using vm_malloc() here rather than 'new' so we get the extra out-of-memory check nverts_good = (ubyte *) vm_malloc(n_verts); Assert( nverts_good != NULL ); if ( nverts_good == NULL ) return; memset( nverts_good, 0, n_verts ); vertex_list.reserve(n_verts); generate_sorted_index_list(); for (j = 0; j < n_verts; j++) { if (find_first_vertex_fast(j) == j) { nverts++; nverts_good[j] = 1; vertex_list.push_back(j); } } t1 = timer_get_milliseconds(); //mprintf(("Index Buffer created in %d milliseconds\n", t1-t0)); // if there is nothig to change then bail if (n_verts == nverts) { if (nverts_good != NULL) { vm_free(nverts_good); } return; } buffer_list_internal.n_verts = 0; buffer_list_internal.allocate(nverts); for (j = 0; j < n_verts; j++) { if ( !nverts_good[j] ) { continue; } buffer_list_internal.vert[z] = vert[j]; buffer_list_internal.norm[z] = norm[j]; if (Cmdline_normal) { buffer_list_internal.tsb[z] = tsb[j]; } if ( Use_GLSL >= 3 ) { buffer_list_internal.submodels[z] = submodels[j]; } buffer_list_internal.n_verts++; z++; } Assert(nverts == buffer_list_internal.n_verts); if (nverts_good != NULL) { vm_free(nverts_good); } buffer_list_internal.generate_sorted_index_list(); (*this) = buffer_list_internal; } poly_list& poly_list::operator = (poly_list &other_list) { allocate(other_list.n_verts); memcpy(norm, other_list.norm, sizeof(vec3d) * other_list.n_verts); memcpy(vert, other_list.vert, sizeof(vertex) * other_list.n_verts); if (Cmdline_normal) { memcpy(tsb, other_list.tsb, sizeof(tsb_t) * other_list.n_verts); } if ( Use_GLSL >= 3 ) { memcpy(submodels, other_list.submodels, sizeof(int) * other_list.n_verts); } memcpy(sorted_indices, other_list.sorted_indices, sizeof(uint) * other_list.n_verts); n_verts = other_list.n_verts; return *this; } void poly_list::generate_sorted_index_list() { for ( int j = 0; j < n_verts; ++j) { sorted_indices[j] = j; } std::sort(sorted_indices, sorted_indices + n_verts, finder(this)); } bool poly_list::finder::operator()(const uint a, const uint b) { vertex *vert_a; vertex *vert_b; vec3d *norm_a; vec3d *norm_b; Assert(search_list != NULL); if ( a == (uint)search_list->n_verts ) { Assert(vert_to_find != NULL); Assert(norm_to_find != NULL); Assert(a != b); vert_a = vert_to_find; norm_a = norm_to_find; } else { vert_a = &search_list->vert[a]; norm_a = &search_list->norm[a]; } if ( b == (uint)search_list->n_verts ) { Assert(vert_to_find != NULL); Assert(norm_to_find != NULL); Assert(a != b); vert_b = vert_to_find; norm_b = norm_to_find; } else { vert_b = &search_list->vert[b]; norm_b = &search_list->norm[b]; } if (norm_a->xyz.x != norm_b->xyz.x) { return norm_a->xyz.x < norm_b->xyz.x; } if (norm_a->xyz.y != norm_b->xyz.y) { return norm_a->xyz.y < norm_b->xyz.y; } if (norm_a->xyz.z != norm_b->xyz.z) { return norm_a->xyz.z < norm_b->xyz.z; } if (vert_a->world.xyz.x != vert_b->world.xyz.x) { return vert_a->world.xyz.x < vert_b->world.xyz.x; } if (vert_a->world.xyz.y != vert_b->world.xyz.y) { return vert_a->world.xyz.y < vert_b->world.xyz.y; } if (vert_a->world.xyz.z != vert_b->world.xyz.z) { return vert_a->world.xyz.z < vert_b->world.xyz.z; } if (vert_a->texture_position.u != vert_b->texture_position.u) { return vert_a->texture_position.u < vert_b->texture_position.u; } if ( vert_a->texture_position.v != vert_b->texture_position.v ) { return vert_a->texture_position.v < vert_b->texture_position.v; } if ( !compare_indices ) { return vert_a->texture_position.v < vert_b->texture_position.v; } else { return a < b; } } void gr_shield_icon(coord2d coords[6], int resize_mode) { if (gr_screen.mode == GR_STUB) { return; } if (resize_mode != GR_RESIZE_NONE) { gr_resize_screen_pos(&coords[0].x, &coords[0].y, NULL, NULL, resize_mode); gr_resize_screen_pos(&coords[1].x, &coords[1].y, NULL, NULL, resize_mode); gr_resize_screen_pos(&coords[2].x, &coords[2].y, NULL, NULL, resize_mode); gr_resize_screen_pos(&coords[3].x, &coords[3].y, NULL, NULL, resize_mode); gr_resize_screen_pos(&coords[4].x, &coords[4].y, NULL, NULL, resize_mode); gr_resize_screen_pos(&coords[5].x, &coords[5].y, NULL, NULL, resize_mode); } g3_draw_2d_shield_icon(coords, gr_screen.current_color.red, gr_screen.current_color.green, gr_screen.current_color.blue, gr_screen.current_color.alpha); } void gr_rect(int x, int y, int w, int h, int resize_mode) { if (gr_screen.mode == GR_STUB) { return; } if (resize_mode != GR_RESIZE_NONE) { gr_resize_screen_pos(&x, &y, &w, &h, resize_mode); } g3_draw_2d_rect(x, y, w, h, gr_screen.current_color.red, gr_screen.current_color.green, gr_screen.current_color.blue, gr_screen.current_color.alpha); } void gr_shade(int x, int y, int w, int h, int resize_mode) { int r, g, b, a; if (gr_screen.mode == GR_STUB) { return; } if (resize_mode != GR_RESIZE_NONE) { gr_resize_screen_pos(&x, &y, &w, &h, resize_mode); } r = (int)gr_screen.current_shader.r; g = (int)gr_screen.current_shader.g; b = (int)gr_screen.current_shader.b; a = (int)gr_screen.current_shader.c; g3_draw_2d_rect(x, y, w, h, r, g, b, a); } void gr_set_bitmap(int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha) { gr_screen.current_alpha = alpha; gr_screen.current_alphablend_mode = alphablend_mode; gr_screen.current_bitblt_mode = bitblt_mode; gr_screen.current_bitmap = bitmap_num; } void gr_flip() { // m!m avoid running CHA_ONFRAME when the "Quit mission" popup is shown. See mantis 2446 for reference if (!quit_mission_popup_shown) { //WMC - Evaluate global hook if not override. Script_system.RunBytecode(Script_globalhook); //WMC - Do conditional hooks. Yippee! Script_system.RunCondition(CHA_ONFRAME); //WMC - Do scripting reset stuff Script_system.EndFrame(); } gr_screen.gf_flip(); } uint gr_determine_model_shader_flags( bool lighting, bool fog, bool textured, bool in_shadow_map, bool thruster_scale, bool transform, bool team_color, int tmap_flags, int spec_map, int glow_map, int normal_map, int height_map, int env_map, int misc_map ) { uint shader_flags = 0; if ( Use_GLSL > 1 ) { shader_flags |= SDR_FLAG_MODEL_CLIP; } if ( transform ) { shader_flags |= SDR_FLAG_MODEL_TRANSFORM; } if ( in_shadow_map ) { // if we're building the shadow map, we likely only need the flags here and above so bail shader_flags |= SDR_FLAG_MODEL_SHADOW_MAP; return shader_flags; } // setup shader flags for the things that we want/need if ( lighting ) { shader_flags |= SDR_FLAG_MODEL_LIGHT; } if ( fog ) { shader_flags |= SDR_FLAG_MODEL_FOG; } if ( tmap_flags & TMAP_ANIMATED_SHADER ) { shader_flags |= SDR_FLAG_MODEL_ANIMATED; } if ( textured ) { if ( !Basemap_override ) { shader_flags |= SDR_FLAG_MODEL_DIFFUSE_MAP; } if ( glow_map > 0 ) { shader_flags |= SDR_FLAG_MODEL_GLOW_MAP; } if ( lighting ) { if ( ( spec_map > 0 ) && !Specmap_override ) { shader_flags |= SDR_FLAG_MODEL_SPEC_MAP; if ( ( env_map > 0 ) && !Envmap_override ) { shader_flags |= SDR_FLAG_MODEL_ENV_MAP; } } if ( ( normal_map > 0) && !Normalmap_override ) { shader_flags |= SDR_FLAG_MODEL_NORMAL_MAP; } if ( ( height_map > 0) && !Heightmap_override ) { shader_flags |= SDR_FLAG_MODEL_HEIGHT_MAP; } if ( Cmdline_shadow_quality && !in_shadow_map && !Shadow_override) { shader_flags |= SDR_FLAG_MODEL_SHADOWS; } } if ( misc_map > 0 ) { shader_flags |= SDR_FLAG_MODEL_MISC_MAP; } if ( team_color ) { shader_flags |= SDR_FLAG_MODEL_TEAMCOLOR; } } if ( Deferred_lighting ) { shader_flags |= SDR_FLAG_MODEL_DEFERRED; } if ( thruster_scale ) { shader_flags |= SDR_FLAG_MODEL_THRUSTER; } return shader_flags; }