Created
February 14, 2018 12:29
-
-
Save ericoporto/2d5577c661ca0a6099a8c0837d76050f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//============================================================================= | |
// | |
// Adventure Game Studio (AGS) | |
// | |
// Copyright (C) 1999-2011 Chris Jones and 2011-20xx others | |
// The full list of copyright holders can be found in the Copyright.txt | |
// file, which is part of this source code distribution. | |
// | |
// The AGS source code is provided under the Artistic License 2.0. | |
// A copy of this license can be found in the file License.txt and at | |
// http://www.opensource.org/licenses/artistic-license-2.0.php | |
// | |
//============================================================================= | |
#if defined(WINDOWS_VERSION) || defined(ANDROID_VERSION) || defined(IOS_VERSION) | |
#include <algorithm> | |
#include "gfx/ali3dexception.h" | |
#include "gfx/ali3dogl.h" | |
#include "gfx/gfxfilter_ogl.h" | |
#include "gfx/gfxfilter_aaogl.h" | |
#include "main/main_allegro.h" | |
#include "platform/base/agsplatformdriver.h" | |
// | |
// NOTE: following external variables are used by the mobile ports: | |
// TODO: parse them during config | |
// | |
// psp_gfx_scaling - scaling style: | |
// * 0 - no scaling | |
// * 1 - stretch and preserve aspect ratio | |
// * 2 - stretch to whole screen | |
// | |
// psp_gfx_smoothing - scaling filter: | |
// * 0 - nearest-neighbour | |
// * 1 - linear | |
// | |
// psp_gfx_renderer - rendering mode | |
// * 1 - render directly to screen | |
// * 2 - render to texture first and then to screen | |
// | |
// psp_gfx_super_sampling - enable super sampling | |
// | |
#if defined(WINDOWS_VERSION) | |
int device_screen_initialized = 1; | |
const char* fbo_extension_string = "GL_EXT_framebuffer_object"; | |
const char* vsync_extension_string = "WGL_EXT_swap_control"; | |
// TODO: linking to glew32 library might be a better option | |
PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT = 0; | |
PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT = 0; | |
PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT = 0; | |
PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT = 0; | |
PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetFramebufferAttachmentParameterivEXT = 0; | |
PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT = 0; | |
PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT = 0; | |
PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT = 0; | |
PFNWGLSWAPINTERVALEXTPROC glSwapIntervalEXT = 0; | |
// Shaders stuff | |
PFNGLCREATESHADERPROC glCreateShader = 0; | |
PFNGLSHADERSOURCEPROC glShaderSource = 0; | |
PFNGLCOMPILESHADERPROC glCompileShader = 0; | |
PFNGLGETSHADERIVPROC glGetShaderiv = 0; | |
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = 0; | |
PFNGLATTACHSHADERPROC glAttachShader = 0; | |
PFNGLDETACHSHADERPROC glDetachShader = 0; | |
PFNGLDELETESHADERPROC glDeleteShader = 0; | |
PFNGLCREATEPROGRAMPROC glCreateProgram = 0; | |
PFNGLLINKPROGRAMPROC glLinkProgram = 0; | |
PFNGLGETPROGRAMIVPROC glGetProgramiv = 0; | |
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = 0; | |
PFNGLUSEPROGRAMPROC glUseProgram = 0; | |
PFNGLDELETEPROGRAMPROC glDeleteProgram = 0; | |
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = 0; | |
PFNGLUNIFORM1IPROC glUniform1i = 0; | |
PFNGLUNIFORM1FPROC glUniform1f = 0; | |
PFNGLUNIFORM3FPROC glUniform3f = 0; | |
#elif defined(ANDROID_VERSION) | |
#define glOrtho glOrthof | |
#define GL_CLAMP GL_CLAMP_TO_EDGE | |
// Defined in Allegro | |
extern "C" | |
{ | |
void android_swap_buffers(); | |
void android_create_screen(int width, int height, int color_depth); | |
void android_mouse_setup(int left, int right, int top, int bottom, float scaling_x, float scaling_y); | |
} | |
extern "C" void android_debug_printf(char* format, ...); | |
extern int psp_gfx_smoothing; | |
extern int psp_gfx_scaling; | |
extern int psp_gfx_renderer; | |
extern int psp_gfx_super_sampling; | |
extern unsigned int android_screen_physical_width; | |
extern unsigned int android_screen_physical_height; | |
extern int android_screen_initialized; | |
#define device_screen_initialized android_screen_initialized | |
#define device_mouse_setup android_mouse_setup | |
#define device_swap_buffers android_swap_buffers | |
const char* fbo_extension_string = "GL_OES_framebuffer_object"; | |
#define glGenFramebuffersEXT glGenFramebuffersOES | |
#define glDeleteFramebuffersEXT glDeleteFramebuffersOES | |
#define glBindFramebufferEXT glBindFramebufferOES | |
#define glCheckFramebufferStatusEXT glCheckFramebufferStatusOES | |
#define glGetFramebufferAttachmentParameterivEXT glGetFramebufferAttachmentParameterivOES | |
#define glGenerateMipmapEXT glGenerateMipmapOES | |
#define glFramebufferTexture2DEXT glFramebufferTexture2DOES | |
#define glFramebufferRenderbufferEXT glFramebufferRenderbufferOES | |
#define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER_OES | |
#define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0_OES | |
#elif defined(IOS_VERSION) | |
extern "C" | |
{ | |
void ios_swap_buffers(); | |
void ios_select_buffer(); | |
void ios_create_screen(); | |
void ios_mouse_setup(int left, int right, int top, int bottom, float scaling_x, float scaling_y); | |
} | |
#define glOrtho glOrthof | |
#define GL_CLAMP GL_CLAMP_TO_EDGE | |
extern int psp_gfx_smoothing; | |
extern int psp_gfx_scaling; | |
extern int psp_gfx_renderer; | |
extern int psp_gfx_super_sampling; | |
extern unsigned int ios_screen_physical_width; | |
extern unsigned int ios_screen_physical_height; | |
extern int ios_screen_initialized; | |
#define device_screen_initialized ios_screen_initialized | |
#define device_mouse_setup ios_mouse_setup | |
#define device_swap_buffers ios_swap_buffers | |
const char* fbo_extension_string = "GL_OES_framebuffer_object"; | |
#define glGenFramebuffersEXT glGenFramebuffersOES | |
#define glDeleteFramebuffersEXT glDeleteFramebuffersOES | |
#define glBindFramebufferEXT glBindFramebufferOES | |
#define glCheckFramebufferStatusEXT glCheckFramebufferStatusOES | |
#define glGetFramebufferAttachmentParameterivEXT glGetFramebufferAttachmentParameterivOES | |
#define glGenerateMipmapEXT glGenerateMipmapOES | |
#define glFramebufferTexture2DEXT glFramebufferTexture2DOES | |
#define glFramebufferRenderbufferEXT glFramebufferRenderbufferOES | |
#define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER_OES | |
#define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0_OES | |
#endif | |
namespace AGS | |
{ | |
namespace Engine | |
{ | |
namespace OGL | |
{ | |
using namespace AGS::Common; | |
void ogl_dummy_vsync() { } | |
#define algetr32(xx) ((xx >> _rgb_r_shift_32) & 0xFF) | |
#define algetg32(xx) ((xx >> _rgb_g_shift_32) & 0xFF) | |
#define algetb32(xx) ((xx >> _rgb_b_shift_32) & 0xFF) | |
#define algeta32(xx) ((xx >> _rgb_a_shift_32) & 0xFF) | |
#define algetr15(xx) ((xx >> _rgb_r_shift_15) & 0x1F) | |
#define algetg15(xx) ((xx >> _rgb_g_shift_15) & 0x1F) | |
#define algetb15(xx) ((xx >> _rgb_b_shift_15) & 0x1F) | |
#define GFX_OPENGL AL_ID('O','G','L',' ') | |
GFX_DRIVER gfx_opengl = | |
{ | |
GFX_OPENGL, | |
empty_string, | |
empty_string, | |
"OpenGL", | |
NULL, // init | |
NULL, // exit | |
NULL, // AL_METHOD(int, scroll, (int x, int y)); | |
ogl_dummy_vsync, // vsync | |
NULL, // setpalette | |
NULL, // AL_METHOD(int, request_scroll, (int x, int y)); | |
NULL, // AL_METHOD(int, poll_scroll, (void)); | |
NULL, // AL_METHOD(void, enable_triple_buffer, (void)); | |
NULL, //create_video_bitmap | |
NULL, //destroy_video_bitmap | |
NULL, //show_video_bitmap | |
NULL, | |
NULL, //gfx_directx_create_system_bitmap, | |
NULL, //gfx_directx_destroy_system_bitmap, | |
NULL, //gfx_directx_set_mouse_sprite, | |
NULL, //gfx_directx_show_mouse, | |
NULL, //gfx_directx_hide_mouse, | |
NULL, //gfx_directx_move_mouse, | |
NULL, // AL_METHOD(void, drawing_mode, (void)); | |
NULL, // AL_METHOD(void, save_video_state, (void*)); | |
NULL, // AL_METHOD(void, restore_video_state, (void*)); | |
NULL, // AL_METHOD(void, set_blender_mode, (int mode, int r, int g, int b, int a)); | |
NULL, // AL_METHOD(int, fetch_mode_list, (void)); | |
0, 0, // int w, h; | |
FALSE, // int linear; | |
0, // long bank_size; | |
0, // long bank_gran; | |
0, // long vid_mem; | |
0, // long vid_phys_base; | |
TRUE // int windowed; | |
}; | |
void OGLBitmap::Dispose() | |
{ | |
if (_tiles != NULL) | |
{ | |
for (int i = 0; i < _numTiles; i++) | |
glDeleteTextures(1, &(_tiles[i].texture)); | |
free(_tiles); | |
_tiles = NULL; | |
_numTiles = 0; | |
} | |
if (_vertex != NULL) | |
{ | |
free(_vertex); | |
_vertex = NULL; | |
} | |
} | |
OGLGraphicsDriver::ShaderProgram::ShaderProgram() : Program(0), SamplerVar(0), ColorVar(0), AuxVar(0) {} | |
OGLGraphicsDriver::OGLGraphicsDriver() | |
{ | |
#if defined (WINDOWS_VERSION) | |
_hDC = NULL; | |
_hRC = NULL; | |
_hWnd = NULL; | |
_hInstance = NULL; | |
device_screen_physical_width = 0; | |
device_screen_physical_height = 0; | |
#elif defined (ANRDOID_VERSION) | |
device_screen_physical_width = android_screen_physical_width | |
device_screen_physical_height = android_screen_physical_height | |
#elif defined (IOS_VERSION) | |
device_screen_physical_width = ios_screen_physical_width | |
device_screen_physical_height = ios_screen_physical_height | |
#endif | |
_firstTimeInit = false; | |
_backbuffer = 0; | |
_fbo = 0; | |
_tint_red = 0; | |
_tint_green = 0; | |
_tint_blue = 0; | |
_screenTintLayer = NULL; | |
_screenTintLayerDDB = NULL; | |
_screenTintSprite.skip = true; | |
_screenTintSprite.x = 0; | |
_screenTintSprite.y = 0; | |
_legacyPixelShader = false; | |
flipTypeLastTime = kFlip_None; | |
_can_render_to_texture = false; | |
_do_render_to_texture = false; | |
_super_sampling = 1; | |
set_up_default_vertices(); | |
} | |
void OGLGraphicsDriver::set_up_default_vertices() | |
{ | |
std::fill(_backbuffer_vertices, _backbuffer_vertices + sizeof(_backbuffer_vertices) / sizeof(GLfloat), 0.0f); | |
std::fill(_backbuffer_texture_coordinates, _backbuffer_texture_coordinates + sizeof(_backbuffer_texture_coordinates) / sizeof(GLfloat), 0.0f); | |
defaultVertices[0].position.x = 0.0f; | |
defaultVertices[0].position.y = 0.0f; | |
defaultVertices[0].tu=0.0; | |
defaultVertices[0].tv=0.0; | |
defaultVertices[1].position.x = 1.0f; | |
defaultVertices[1].position.y = 0.0f; | |
defaultVertices[1].tu=1.0; | |
defaultVertices[1].tv=0.0; | |
defaultVertices[2].position.x = 0.0f; | |
defaultVertices[2].position.y = -1.0f; | |
defaultVertices[2].tu=0.0; | |
defaultVertices[2].tv=1.0; | |
defaultVertices[3].position.x = 1.0f; | |
defaultVertices[3].position.y = -1.0f; | |
defaultVertices[3].tu=1.0; | |
defaultVertices[3].tv=1.0; | |
} | |
#if defined (WINDOWS_VERSION) | |
void OGLGraphicsDriver::create_desktop_screen(int width, int height, int depth) | |
{ | |
device_screen_physical_width = width; | |
device_screen_physical_height = height; | |
} | |
#endif | |
void OGLGraphicsDriver::Vsync() | |
{ | |
// do nothing on OpenGL | |
} | |
void OGLGraphicsDriver::RenderSpritesAtScreenResolution(bool enabled) | |
{ | |
if (_can_render_to_texture) | |
_do_render_to_texture = !enabled; | |
if (_do_render_to_texture) | |
glDisable(GL_SCISSOR_TEST); | |
} | |
bool OGLGraphicsDriver::IsModeSupported(const DisplayMode &mode) | |
{ | |
if (mode.Width <= 0 || mode.Height <= 0 || mode.ColorDepth <= 0) | |
{ | |
set_allegro_error("Invalid resolution parameters: %d x %d x %d", mode.Width, mode.Height, mode.ColorDepth); | |
return false; | |
} | |
return true; | |
} | |
bool OGLGraphicsDriver::SupportsGammaControl() | |
{ | |
return false; | |
} | |
void OGLGraphicsDriver::SetGamma(int newGamma) | |
{ | |
} | |
void OGLGraphicsDriver::SetGraphicsFilter(POGLFilter filter) | |
{ | |
_filter = filter; | |
OnSetFilter(); | |
// Creating ddbs references filter properties at some point, | |
// so we have to redo this part of initialization here. | |
create_screen_tint_bitmap(); | |
} | |
void OGLGraphicsDriver::SetTintMethod(TintMethod method) | |
{ | |
_legacyPixelShader = (method == TintReColourise); | |
} | |
void OGLGraphicsDriver::FirstTimeInit() | |
{ | |
// It was told that GL_VERSION is supposed to begin with digits and may have | |
// custom string after, but mobile version of OpenGL seem to have different | |
// version string format. | |
String ogl_v_str = (const char*)glGetString(GL_VERSION); | |
String digits = "1234567890"; | |
const char *digits_ptr = std::find_first_of(ogl_v_str.GetCStr(), ogl_v_str.GetCStr() + ogl_v_str.GetLength(), | |
digits.GetCStr(), digits.GetCStr() + digits.GetLength()); | |
if (digits_ptr < ogl_v_str.GetCStr() + ogl_v_str.GetLength()) | |
_oglVersion.SetFromString(digits_ptr); | |
Debug::Printf(kDbgMsg_Init, "Running OpenGL: %s", ogl_v_str.GetCStr()); | |
TestVSync(); | |
TestRenderToTexture(); | |
CreateShaders(); | |
_firstTimeInit = true; | |
} | |
bool OGLGraphicsDriver::InitGlScreen(const DisplayMode &mode) | |
{ | |
#if defined(ANDROID_VERSION) | |
android_create_screen(mode.Width, mode.Height, mode.ColorDepth); | |
#elif defined(IOS_VERSION) | |
ios_create_screen(); | |
ios_select_buffer(); | |
#elif defined (WINDOWS_VERSION) | |
if (!mode.Windowed) | |
{ | |
if (platform->EnterFullscreenMode(mode)) | |
platform->AdjustWindowStyleForFullscreen(); | |
} | |
// NOTE: adjust_window may leave task bar visible, so we do not use it for fullscreen mode | |
if (mode.Windowed && adjust_window(mode.Width, mode.Height) != 0) | |
{ | |
set_allegro_error("Window size not supported"); | |
return false; | |
} | |
_hWnd = win_get_window(); | |
if (!(_hDC = GetDC(_hWnd))) | |
return false; | |
// First check if we need to recreate GL context, this will only be | |
// required if different color depth is requested. | |
if (_hRC) | |
{ | |
GLuint pixel_fmt = GetPixelFormat(_hDC); | |
PIXELFORMATDESCRIPTOR pfd; | |
DescribePixelFormat(_hDC, pixel_fmt, sizeof(PIXELFORMATDESCRIPTOR), &pfd); | |
if (pfd.cColorBits != mode.ColorDepth) | |
{ | |
DeleteGlContext(); | |
} | |
} | |
if (!_hRC) | |
{ | |
if (!CreateGlContext(mode)) | |
return false; | |
} | |
create_desktop_screen(mode.Width, mode.Height, mode.ColorDepth); | |
win_grab_input(); | |
#endif | |
gfx_driver = &gfx_opengl; | |
return true; | |
} | |
void OGLGraphicsDriver::InitGlParams(const DisplayMode &mode) | |
{ | |
glDisable(GL_CULL_FACE); | |
glDisable(GL_DEPTH_TEST); | |
glDisable(GL_LIGHTING); | |
glShadeModel(GL_FLAT); | |
glEnable(GL_TEXTURE_2D); | |
glEnable(GL_BLEND); | |
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); | |
glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | |
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); | |
glClear(GL_COLOR_BUFFER_BIT); | |
glViewport(0, 0, device_screen_physical_width, device_screen_physical_height); | |
glMatrixMode(GL_PROJECTION); | |
glLoadIdentity(); | |
glOrtho(0, device_screen_physical_width, 0, device_screen_physical_height, 0, 1); | |
glMatrixMode(GL_MODELVIEW); | |
glLoadIdentity(); | |
glDisableClientState(GL_COLOR_ARRAY); | |
glDisableClientState(GL_NORMAL_ARRAY); | |
glEnableClientState(GL_VERTEX_ARRAY); | |
glEnableClientState(GL_TEXTURE_COORD_ARRAY); | |
#if !defined (ANDROID_VERSION) | |
if (glSwapIntervalEXT) | |
{ | |
if(mode.Vsync) | |
glSwapIntervalEXT(1); | |
else | |
glSwapIntervalEXT(0); | |
} | |
#endif | |
} | |
bool OGLGraphicsDriver::CreateGlContext(const DisplayMode &mode) | |
{ | |
#if defined (WINDOWS_VERSION) | |
PIXELFORMATDESCRIPTOR pfd = | |
{ | |
sizeof(PIXELFORMATDESCRIPTOR), | |
1, | |
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, | |
PFD_TYPE_RGBA, | |
mode.ColorDepth, | |
0, 0, 0, 0, 0, 0, | |
0, | |
0, | |
0, | |
0, 0, 0, 0, | |
0, | |
0, | |
0, | |
PFD_MAIN_PLANE, | |
0, | |
0, 0, 0 | |
}; | |
_oldPixelFormat = GetPixelFormat(_hDC); | |
DescribePixelFormat(_hDC, _oldPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &_oldPixelFormatDesc); | |
GLuint pixel_fmt; | |
if (!(pixel_fmt = ChoosePixelFormat(_hDC, &pfd))) | |
return false; | |
if (!SetPixelFormat(_hDC, pixel_fmt, &pfd)) | |
return false; | |
if (!(_hRC = wglCreateContext(_hDC))) | |
return false; | |
if(!wglMakeCurrent(_hDC, _hRC)) | |
return false; | |
#endif // WINDOWS_VERSION | |
return true; | |
} | |
void OGLGraphicsDriver::DeleteGlContext() | |
{ | |
#if defined (WINDOWS_VERSION) | |
if (_hRC) | |
{ | |
wglMakeCurrent(NULL, NULL); | |
wglDeleteContext(_hRC); | |
_hRC = NULL; | |
} | |
if (_oldPixelFormat > 0) | |
SetPixelFormat(_hDC, _oldPixelFormat, &_oldPixelFormatDesc); | |
#endif | |
} | |
void OGLGraphicsDriver::TestVSync() | |
{ | |
const char* extensions = (const char*)glGetString(GL_EXTENSIONS); | |
const char* extensionsARB = NULL; | |
#if defined(WINDOWS_VERSION) | |
PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB"); | |
extensionsARB = wglGetExtensionsStringARB ? (const char*)wglGetExtensionsStringARB(_hDC) : NULL; | |
#endif | |
#if !defined(ANDROID_VERSION) | |
if (extensions && strstr(extensions, vsync_extension_string) != NULL || | |
extensionsARB && strstr(extensionsARB, vsync_extension_string) != NULL) | |
{ | |
#if defined(WINDOWS_VERSION) | |
glSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); | |
#endif | |
} | |
if (!glSwapIntervalEXT) | |
Debug::Printf(kDbgMsg_Warn, "WARNING: OpenGL extension '%s' not supported, vertical sync will be kept at driver default.", vsync_extension_string); | |
#endif | |
} | |
void OGLGraphicsDriver::TestRenderToTexture() | |
{ | |
const char* extensions = (const char*)glGetString(GL_EXTENSIONS); | |
if (extensions && strstr(extensions, fbo_extension_string) != NULL) | |
{ | |
#if defined(WINDOWS_VERSION) | |
glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)wglGetProcAddress("glGenFramebuffersEXT"); | |
glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC)wglGetProcAddress("glDeleteFramebuffersEXT"); | |
glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)wglGetProcAddress("glBindFramebufferEXT"); | |
glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)wglGetProcAddress("glCheckFramebufferStatusEXT"); | |
glGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)wglGetProcAddress("glGetFramebufferAttachmentParameterivEXT"); | |
glGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC)wglGetProcAddress("glGenerateMipmapEXT"); | |
glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)wglGetProcAddress("glFramebufferTexture2DEXT"); | |
glFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)wglGetProcAddress("glFramebufferRenderbufferEXT"); | |
#endif | |
_can_render_to_texture = true; | |
// Disable super-sampling if it would cause a too large texture size | |
if (_super_sampling > 1) | |
{ | |
int max = 1024; | |
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max); | |
if ((max < _srcRect.GetWidth() * 2) || (max < _srcRect.GetHeight() * 2)) | |
_super_sampling = 1; | |
} | |
} | |
else | |
{ | |
_can_render_to_texture = false; | |
Debug::Printf(kDbgMsg_Warn, "WARNING: OpenGL extension '%s' not supported, rendering to texture mode will be disabled.", fbo_extension_string); | |
} | |
if (!_can_render_to_texture) | |
_do_render_to_texture = false; | |
} | |
void OGLGraphicsDriver::CreateShaders() | |
{ | |
// We need at least OpenGL 3.0 to run our tinting shader | |
if (_oglVersion.Major < 3) | |
Debug::Printf(kDbgMsg_Warn, "WARNING: OpenGL 3.0 or higher is required for tinting shader."); | |
// TODO: linking with GLEW library might be a better idea | |
#if defined(WINDOWS_VERSION) | |
glCreateShader = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader"); | |
glShaderSource = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource"); | |
glCompileShader = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader"); | |
glGetShaderiv = (PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv"); | |
glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog"); | |
glAttachShader = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader"); | |
glDetachShader = (PFNGLDETACHSHADERPROC)wglGetProcAddress("glDetachShader"); | |
glDeleteShader = (PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader"); | |
glCreateProgram = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram"); | |
glLinkProgram = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram"); | |
glGetProgramiv = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv"); | |
glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog"); | |
glUseProgram = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"); | |
glDeleteProgram = (PFNGLDELETEPROGRAMPROC)wglGetProcAddress("glDeleteProgram"); | |
glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)wglGetProcAddress("glGetUniformLocation"); | |
glUniform1i = (PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i"); | |
glUniform1f = (PFNGLUNIFORM1FPROC)wglGetProcAddress("glUniform1f"); | |
glUniform3f = (PFNGLUNIFORM3FPROC)wglGetProcAddress("glUniform3f"); | |
#endif | |
if (!glCreateShader) | |
{ | |
Debug::Printf(kDbgMsg_Error, "ERROR: OpenGL shader functions could not be linked."); | |
return; | |
} | |
CreateTintShader(); | |
CreateLightShader(); | |
} | |
void OGLGraphicsDriver::CreateTintShader() | |
{ | |
const char *fragment_shader_src = | |
// NOTE: this shader emulates "historical" AGS software tinting; it is not | |
// necessarily "proper" tinting in modern terms. | |
// The RGB-HSV-RGB conversion found in the Internet (copyright unknown); | |
// Color processing is replicated from Direct3D shader by Chris Jones | |
// (Engine/resource/tintshaderLegacy.fx). | |
"\ | |
#version 130\n\ | |
out vec4 gl_FragColor;\n\ | |
uniform sampler2D textID;\n\ | |
uniform vec3 tintHSV;\n\ | |
uniform vec3 tintAmnTrsLum;\n\ | |
\ | |
vec3 rgb2hsv(vec3 c)\n\ | |
{\n\ | |
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n\ | |
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n\ | |
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n\ | |
\ | |
float d = q.x - min(q.w, q.y);\n\ | |
float e = 1.0e-10;\n\ | |
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n\ | |
}\n\ | |
\ | |
vec3 hsv2rgb(vec3 c)\n\ | |
{\n\ | |
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n\ | |
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n\ | |
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n\ | |
}\n\ | |
\ | |
float getValue(vec3 color)\n\ | |
{\n\ | |
float colorMax = max (color[0], color[1]);\n\ | |
colorMax = max (colorMax, color[2]);\n\ | |
return colorMax;\n\ | |
}\n\ | |
\ | |
void main()\n\ | |
{\n\ | |
vec2 coords = gl_TexCoord[0].xy;\n\ | |
vec4 src_col = texture2D(textID, coords);\n\ | |
float amount = tintAmnTrsLum[0];\n\ | |
float lum = getValue(src_col.xyz);\n\ | |
lum = max(lum - (1.0 - tintAmnTrsLum[2]), 0.0);\n\ | |
vec3 new_col = (hsv2rgb(vec3(tintHSV[0],tintHSV[1],lum)) * amount + src_col.xyz*(1-amount));\n\ | |
gl_FragColor = vec4(new_col, src_col.w * tintAmnTrsLum[1]);\n\ | |
}\n\ | |
"; | |
CreateShaderProgram(_tintShader, "Tinting", fragment_shader_src, "textID", "tintHSV", "tintAmnTrsLum"); | |
} | |
void OGLGraphicsDriver::CreateLightShader() | |
{ | |
const char *fragment_shader_src = | |
// NOTE: due to how the lighting works in AGS, this is combined MODULATE / ADD shader. | |
// if the light is < 0, then MODULATE operation is used, otherwise ADD is used. | |
// NOTE: it's been said that using branching in shaders produces inefficient code. | |
// If that will ever become a real problem, we can easily split this shader in two. | |
"\ | |
#version 130\n\ | |
out vec4 gl_FragColor;\n\ | |
uniform sampler2D textID;\n\ | |
uniform float light;\n\ | |
uniform float alpha;\n\ | |
\ | |
void main()\n\ | |
{\n\ | |
vec2 coords = gl_TexCoord[0].xy;\n\ | |
gl_FragColor = texture2D(textID, coords);\n\ | |
if (light >= 0.0)\n\ | |
gl_FragColor = vec4(gl_FragColor.xyz + vec3(light, light, light), gl_FragColor.w * alpha);\n\ | |
else\n\ | |
gl_FragColor = vec4(gl_FragColor.xyz * abs(light), gl_FragColor.w * alpha);\n\ | |
}\n\ | |
"; | |
CreateShaderProgram(_lightShader, "Lighting", fragment_shader_src, "textID", "light", "alpha"); | |
} | |
void OGLGraphicsDriver::CreateShaderProgram(ShaderProgram &prg, const char *name, const char *fragment_shader_src, | |
const char *sampler_var, const char *color_var, const char *aux_var) | |
{ | |
GLint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); | |
glShaderSource(fragment_shader, 1, &fragment_shader_src, NULL); | |
glCompileShader(fragment_shader); | |
GLint compile_result; | |
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &compile_result); | |
if (compile_result == GL_FALSE) | |
{ | |
OutputShaderError(fragment_shader, String::FromFormat("%s program's fragment shader", name), true); | |
glDeleteShader(fragment_shader); | |
return; | |
} | |
GLuint program = glCreateProgram(); | |
glAttachShader(program, fragment_shader); | |
glLinkProgram(program); | |
GLint link_result = 0; | |
glGetProgramiv(program, GL_LINK_STATUS, &link_result); | |
if(link_result == GL_FALSE) | |
{ | |
OutputShaderError(program, String::FromFormat("%s program", name), false); | |
glDeleteProgram(program); | |
glDeleteShader(fragment_shader); | |
return; | |
} | |
glDetachShader(program, fragment_shader); | |
glDeleteShader(fragment_shader); | |
prg.Program = program; | |
prg.SamplerVar = glGetUniformLocation(program, sampler_var); | |
prg.ColorVar = glGetUniformLocation(program, color_var); | |
prg.AuxVar = glGetUniformLocation(program, aux_var); | |
} | |
void OGLGraphicsDriver::DeleteShaderProgram(ShaderProgram &prg) | |
{ | |
if (prg.Program) | |
glDeleteProgram(prg.Program); | |
prg.Program = 0; | |
} | |
void OGLGraphicsDriver::OutputShaderError(GLuint obj_id, const String &obj_name, bool is_shader) | |
{ | |
GLint log_len = 0; | |
if (is_shader) | |
glGetShaderiv(obj_id, GL_INFO_LOG_LENGTH, &log_len); | |
else | |
glGetProgramiv(obj_id, GL_INFO_LOG_LENGTH, &log_len); | |
std::vector<GLchar> errorLog(log_len); | |
if (is_shader) | |
glGetShaderInfoLog(obj_id, log_len, &log_len, &errorLog[0]); | |
else | |
glGetProgramInfoLog(obj_id, log_len, &log_len, &errorLog[0]); | |
Debug::Printf(kDbgMsg_Error, "ERROR: OpenGL: %s %s:", obj_name.GetCStr(), is_shader ? "failed to compile" : "failed to link"); | |
Debug::Printf(kDbgMsg_Error, "----------------------------------------"); | |
Debug::Printf(kDbgMsg_Error, "%s", &errorLog[0]); | |
Debug::Printf(kDbgMsg_Error, "----------------------------------------"); | |
} | |
void OGLGraphicsDriver::SetupBackbufferTexture() | |
{ | |
// NOTE: ability to render to texture depends on OGL context, which is | |
// created in SetDisplayMode, therefore creation of textures require | |
// both native size set and context capabilities test passed. | |
if (!IsNativeSizeValid() || !_can_render_to_texture) | |
return; | |
DeleteBackbufferTexture(); | |
// _backbuffer_texture_coordinates defines translation from wanted texture size to actual supported texture size | |
_backRenderSize = _srcRect.GetSize() * _super_sampling; | |
_backTextureSize = _backRenderSize; | |
AdjustSizeToNearestSupportedByCard(&_backTextureSize.Width, &_backTextureSize.Height); | |
const float back_ratio_w = (float)_backRenderSize.Width / (float)_backTextureSize.Width; | |
const float back_ratio_h = (float)_backRenderSize.Height / (float)_backTextureSize.Height; | |
std::fill(_backbuffer_texture_coordinates, _backbuffer_texture_coordinates + sizeof(_backbuffer_texture_coordinates) / sizeof(GLfloat), 0.0f); | |
_backbuffer_texture_coordinates[2] = _backbuffer_texture_coordinates[6] = back_ratio_w; | |
_backbuffer_texture_coordinates[5] = _backbuffer_texture_coordinates[7] = back_ratio_h; | |
glGenTextures(1, &_backbuffer); | |
glBindTexture(GL_TEXTURE_2D, _backbuffer); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); | |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _backTextureSize.Width, _backTextureSize.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); | |
glBindTexture(GL_TEXTURE_2D, 0); | |
glGenFramebuffersEXT(1, &_fbo); | |
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _fbo); | |
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, _backbuffer, 0); | |
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); | |
// Assign vertices of the backbuffer texture position in the scene | |
_backbuffer_vertices[0] = _backbuffer_vertices[4] = 0; | |
_backbuffer_vertices[2] = _backbuffer_vertices[6] = _srcRect.GetWidth(); | |
_backbuffer_vertices[5] = _backbuffer_vertices[7] = _srcRect.GetHeight(); | |
_backbuffer_vertices[1] = _backbuffer_vertices[3] = 0; | |
} | |
void OGLGraphicsDriver::DeleteBackbufferTexture() | |
{ | |
if (_backbuffer) | |
glDeleteTextures(1, &_backbuffer); | |
if (_fbo) | |
glDeleteFramebuffersEXT(1, &_fbo); | |
_backbuffer = 0; | |
_fbo = 0; | |
} | |
void OGLGraphicsDriver::SetupViewport() | |
{ | |
if (!IsModeSet() || !IsRenderFrameValid()) | |
return; | |
// Setup viewport rect and scissor; notice that OpenGL viewport has Y axis inverted | |
_viewportRect = RectWH(_dstRect.Left, device_screen_physical_height - 1 - _dstRect.Bottom, _dstRect.GetWidth(), _dstRect.GetHeight()); | |
glScissor(_viewportRect.Left, _viewportRect.Top, _viewportRect.GetWidth(), _viewportRect.GetHeight()); | |
#if defined(ANDROID_VERSION) || defined(IOS_VERSION) | |
device_mouse_setup( | |
(device_screen_physical_width - _dstRect.GetWidth()) / 2, | |
device_screen_physical_width - ((device_screen_physical_width - _dstRect.GetWidth()) / 2), | |
(device_screen_physical_height - _dstRect.GetHeight()) / 2, | |
device_screen_physical_height - ((device_screen_physical_height - _dstRect.GetHeight()) / 2), | |
1.0f, | |
1.0f); | |
#endif | |
} | |
bool OGLGraphicsDriver::SetDisplayMode(const DisplayMode &mode, volatile int *loopTimer) | |
{ | |
ReleaseDisplayMode(); | |
if (mode.ColorDepth < 15) | |
{ | |
set_allegro_error("OpenGL driver does not support 256-color display mode"); | |
return false; | |
} | |
try | |
{ | |
if (!InitGlScreen(mode)) | |
return false; | |
if (!_firstTimeInit) | |
FirstTimeInit(); | |
InitGlParams(mode); | |
} | |
catch (Ali3DException exception) | |
{ | |
if (exception._message != get_allegro_error()) | |
set_allegro_error(exception._message); | |
return false; | |
} | |
OnInit(loopTimer); | |
// On certain platforms OpenGL renderer ignores requested screen sizes | |
// and uses values imposed by the operating system (device). | |
DisplayMode final_mode = mode; | |
final_mode.Width = device_screen_physical_width; | |
final_mode.Height = device_screen_physical_height; | |
OnModeSet(final_mode); | |
// TODO: move these options parsing into config instead | |
#if defined(ANDROID_VERSION) || defined (IOS_VERSION) | |
if (psp_gfx_renderer == 2 && _can_render_to_texture) | |
{ | |
_super_sampling = ((psp_gfx_super_sampling > 0) ? 2 : 1); | |
_do_render_to_texture = true; | |
} | |
else | |
{ | |
_super_sampling = 1; | |
_do_render_to_texture = false; | |
} | |
#endif | |
create_screen_tint_bitmap(); | |
// If we already have a native size set, then update virtual screen and setup backbuffer texture immediately | |
CreateVirtualScreen(); | |
SetupBackbufferTexture(); | |
// If we already have a render frame configured, then setup viewport and backbuffer mappings immediately | |
SetupViewport(); | |
return true; | |
} | |
void OGLGraphicsDriver::CreateVirtualScreen() | |
{ | |
if (!IsModeSet() || !IsNativeSizeValid()) | |
return; | |
CreateStageScreen(); | |
} | |
bool OGLGraphicsDriver::SetNativeSize(const Size &src_size) | |
{ | |
OnSetNativeSize(src_size); | |
SetupBackbufferTexture(); | |
// If we already have a gfx mode set, then update virtual screen immediately | |
CreateVirtualScreen(); | |
return !_srcRect.IsEmpty(); | |
} | |
bool OGLGraphicsDriver::SetRenderFrame(const Rect &dst_rect) | |
{ | |
if (!IsNativeSizeValid()) | |
return false; | |
OnSetRenderFrame(dst_rect); | |
// Also make sure viewport and backbuffer mappings are updated using new native & destination rectangles | |
SetupViewport(); | |
return !_dstRect.IsEmpty(); | |
} | |
int OGLGraphicsDriver::GetDisplayDepthForNativeDepth(int native_color_depth) const | |
{ | |
// TODO: check for device caps to know which depth is supported? | |
return 32; | |
} | |
IGfxModeList *OGLGraphicsDriver::GetSupportedModeList(int color_depth) | |
{ | |
std::vector<DisplayMode> modes; | |
platform->GetSystemDisplayModes(modes); | |
return new OGLDisplayModeList(modes); | |
} | |
PGfxFilter OGLGraphicsDriver::GetGraphicsFilter() const | |
{ | |
return _filter; | |
} | |
void OGLGraphicsDriver::ReleaseDisplayMode() | |
{ | |
if (!IsModeSet()) | |
return; | |
OnModeReleased(); | |
DeleteBackbufferTexture(); | |
if (_screenTintLayerDDB != NULL) | |
{ | |
this->DestroyDDB(_screenTintLayerDDB); | |
_screenTintLayerDDB = NULL; | |
_screenTintSprite.bitmap = NULL; | |
} | |
delete _screenTintLayer; | |
_screenTintLayer = NULL; | |
DestroyStageScreen(); | |
gfx_driver = NULL; | |
if (platform->ExitFullscreenMode()) | |
platform->RestoreWindowStyle(); | |
} | |
void OGLGraphicsDriver::UnInit() | |
{ | |
OnUnInit(); | |
ReleaseDisplayMode(); | |
DeleteGlContext(); | |
#if defined (WINDOWS_VERSION) | |
_hWnd = NULL; | |
_hDC = NULL; | |
#endif | |
DeleteShaderProgram(_tintShader); | |
DeleteShaderProgram(_lightShader); | |
} | |
OGLGraphicsDriver::~OGLGraphicsDriver() | |
{ | |
UnInit(); | |
} | |
void OGLGraphicsDriver::ClearRectangle(int x1, int y1, int x2, int y2, RGB *colorToUse) | |
{ | |
} | |
void OGLGraphicsDriver::GetCopyOfScreenIntoBitmap(Bitmap *destination, bool at_native_res) | |
{ | |
(void)at_native_res; // TODO: support this at some point | |
Rect retr_rect; | |
if (_do_render_to_texture) | |
{ | |
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _fbo); | |
retr_rect = RectWH(0, 0, _backRenderSize.Width, _backRenderSize.Height); | |
} | |
else | |
{ | |
#if defined(IOS_VERSION) | |
ios_select_buffer(); | |
#elif defined(WINDOWS_VERSION) | |
glReadBuffer(GL_FRONT); | |
#endif | |
retr_rect = _dstRect; | |
} | |
int bpp = _mode.ColorDepth / 8; | |
int bufferSize = retr_rect.GetWidth() * retr_rect.GetHeight() * bpp; | |
unsigned char* buffer = new unsigned char[bufferSize]; | |
if (buffer) | |
{ | |
glReadPixels(retr_rect.Left, retr_rect.Top, retr_rect.GetWidth(), retr_rect.GetHeight(), GL_RGBA, GL_UNSIGNED_BYTE, buffer); | |
unsigned char* surfaceData = buffer; | |
unsigned char* sourcePtr; | |
unsigned char* destPtr; | |
Bitmap* retrieveInto = BitmapHelper::CreateBitmap(retr_rect.GetWidth(), retr_rect.GetHeight(), _mode.ColorDepth); | |
if (retrieveInto) | |
{ | |
for (int y = retrieveInto->GetHeight() - 1; y >= 0; y--) | |
{ | |
sourcePtr = surfaceData; | |
destPtr = &retrieveInto->GetScanLineForWriting(y)[0]; | |
for (int x = 0; x < retrieveInto->GetWidth() * bpp; x += bpp) | |
{ | |
// TODO: find out if it's possible to retrieve pixels in the matching format | |
destPtr[x] = sourcePtr[x + 2]; | |
destPtr[x + 1] = sourcePtr[x + 1]; | |
destPtr[x + 2] = sourcePtr[x]; | |
destPtr[x + 3] = sourcePtr[x + 3]; | |
} | |
surfaceData += retr_rect.GetWidth() * bpp; | |
} | |
destination->StretchBlt(retrieveInto, RectWH(0, 0, retrieveInto->GetWidth(), retrieveInto->GetHeight()), | |
RectWH(0, 0, destination->GetWidth(), destination->GetHeight())); | |
delete retrieveInto; | |
} | |
delete [] buffer; | |
} | |
} | |
void OGLGraphicsDriver::RenderToBackBuffer() | |
{ | |
throw Ali3DException("D3D driver does not have a back buffer"); | |
} | |
void OGLGraphicsDriver::Render() | |
{ | |
Render(kFlip_None); | |
} | |
void OGLGraphicsDriver::Render(GlobalFlipType flip) | |
{ | |
_render(flip, true); | |
} | |
void OGLGraphicsDriver::_reDrawLastFrame() | |
{ | |
drawList = drawListLastTime; | |
} | |
void OGLGraphicsDriver::_renderSprite(OGLDrawListEntry *drawListEntry, bool globalLeftRightFlip, bool globalTopBottomFlip) | |
{ | |
OGLBitmap *bmpToDraw = drawListEntry->bitmap; | |
if (bmpToDraw->_transparency >= 255) | |
return; | |
if (bmpToDraw->_tintSaturation > 0) | |
{ | |
// Use tinting shader | |
if (_tintShader.Program) | |
{ | |
glUseProgram(_tintShader.Program); | |
float rgb[3]; | |
float sat_trs_lum[3]; // saturation / transparency / luminance | |
if (_legacyPixelShader) | |
{ | |
rgb_to_hsv(bmpToDraw->_red, bmpToDraw->_green, bmpToDraw->_blue, &rgb[0], &rgb[1], &rgb[2]); | |
rgb[0] /= 360.0; // In HSV, Hue is 0-360 | |
} | |
else | |
{ | |
rgb[0] = (float)bmpToDraw->_red / 255.0; | |
rgb[1] = (float)bmpToDraw->_green / 255.0; | |
rgb[2] = (float)bmpToDraw->_blue / 255.0; | |
} | |
sat_trs_lum[0] = (float)bmpToDraw->_tintSaturation / 255.0; | |
if (bmpToDraw->_transparency > 0) | |
sat_trs_lum[1] = (float)bmpToDraw->_transparency / 255.0; | |
else | |
sat_trs_lum[1] = 1.0f; | |
if (bmpToDraw->_lightLevel > 0) | |
sat_trs_lum[2] = (float)bmpToDraw->_lightLevel / 255.0; | |
else | |
sat_trs_lum[2] = 1.0f; | |
glUniform1i(_tintShader.SamplerVar, 0); | |
glUniform3f(_tintShader.ColorVar, rgb[0], rgb[1], rgb[2]); | |
glUniform3f(_tintShader.AuxVar, sat_trs_lum[0], sat_trs_lum[1], sat_trs_lum[2]); | |
} | |
} | |
else if (bmpToDraw->_lightLevel > 0) | |
{ | |
// Use light shader | |
glUseProgram(_lightShader.Program); | |
float light_lev = 1.0f; | |
float alpha = 1.0f; | |
// Light level parameter in DDB is weird, it is measured in units of | |
// 1/255 (although effectively 1/250, see draw.cpp), but contains two | |
// ranges: 1-255 is darker range and 256-511 is brighter range. | |
// (light level of 0 means "default color") | |
if ((bmpToDraw->_lightLevel > 0) && (bmpToDraw->_lightLevel < 256)) | |
{ | |
// darkening the sprite... this stupid calculation is for | |
// consistency with the allegro software-mode code that does | |
// a trans blend with a (8,8,8) sprite | |
light_lev = -((bmpToDraw->_lightLevel * 192) / 256 + 64) / 255.f; // darker, uses MODULATE op | |
} | |
else if (bmpToDraw->_lightLevel > 256) | |
{ | |
light_lev = ((bmpToDraw->_lightLevel - 256) / 2) / 255.f; // brighter, uses ADD op | |
} | |
if (bmpToDraw->_transparency > 0) | |
alpha = bmpToDraw->_transparency / 255.f; | |
glUniform1i(_lightShader.SamplerVar, 0); | |
glUniform1f(_lightShader.ColorVar, light_lev); | |
glUniform1f(_lightShader.AuxVar, alpha); | |
} | |
else | |
{ | |
// Use default processing | |
if (bmpToDraw->_transparency == 0) | |
glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | |
else | |
glColor4f(1.0f, 1.0f, 1.0f, bmpToDraw->_transparency / 255.0f); | |
} | |
float width = bmpToDraw->GetWidthToRender(); | |
float height = bmpToDraw->GetHeightToRender(); | |
float xProportion = (float)width / (float)bmpToDraw->_width; | |
float yProportion = (float)height / (float)bmpToDraw->_height; | |
bool flipLeftToRight = globalLeftRightFlip ^ bmpToDraw->_flipped; | |
int drawAtX = drawListEntry->x + _global_x_offset; | |
int drawAtY = drawListEntry->y + _global_y_offset; | |
for (int ti = 0; ti < bmpToDraw->_numTiles; ti++) | |
{ | |
width = bmpToDraw->_tiles[ti].width * xProportion; | |
height = bmpToDraw->_tiles[ti].height * yProportion; | |
float xOffs; | |
float yOffs = bmpToDraw->_tiles[ti].y * yProportion; | |
if (flipLeftToRight != globalLeftRightFlip) | |
{ | |
xOffs = (bmpToDraw->_width - (bmpToDraw->_tiles[ti].x + bmpToDraw->_tiles[ti].width)) * xProportion; | |
} | |
else | |
{ | |
xOffs = bmpToDraw->_tiles[ti].x * xProportion; | |
} | |
int thisX = drawAtX + xOffs; | |
int thisY = drawAtY + yOffs; | |
if (globalLeftRightFlip) | |
{ | |
thisX = (_srcRect.GetWidth() - thisX) - width; | |
} | |
if (globalTopBottomFlip) | |
{ | |
thisY = (_srcRect.GetHeight() - thisY) - height; | |
} | |
thisX = (-(_srcRect.GetWidth() / 2)) + thisX; | |
thisY = (_srcRect.GetHeight() / 2) - thisY; | |
//Setup translation and scaling matrices | |
float widthToScale = (float)width; | |
float heightToScale = (float)height; | |
if (flipLeftToRight) | |
{ | |
// The usual transform changes 0..1 into 0..width | |
// So first negate it (which changes 0..w into -w..0) | |
widthToScale = -widthToScale; | |
// and now shift it over to make it 0..w again | |
thisX += width; | |
} | |
if (globalTopBottomFlip) | |
{ | |
heightToScale = -heightToScale; | |
thisY -= height; | |
} | |
glMatrixMode(GL_MODELVIEW); | |
glLoadIdentity(); | |
if (_do_render_to_texture) | |
glTranslatef(_backRenderSize.Width / 2.0f, _backRenderSize.Height / 2.0f, 0.0f); | |
else | |
glTranslatef(_srcRect.GetWidth() / 2.0f, _srcRect.GetHeight() / 2.0f, 0.0f); | |
glTranslatef((float)thisX, (float)thisY, 0.0f); | |
glScalef(widthToScale, heightToScale, 1.0f); | |
glBindTexture(GL_TEXTURE_2D, bmpToDraw->_tiles[ti].texture); | |
if ((_smoothScaling) && bmpToDraw->_useResampler && (bmpToDraw->_stretchToHeight > 0) && | |
((bmpToDraw->_stretchToHeight != bmpToDraw->_height) || | |
(bmpToDraw->_stretchToWidth != bmpToDraw->_width))) | |
{ | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
} | |
else if (_do_render_to_texture) | |
{ | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
} | |
else | |
{ | |
_filter->SetFilteringForStandardSprite(); | |
} | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); | |
if (bmpToDraw->_vertex != NULL) | |
{ | |
glTexCoordPointer(2, GL_FLOAT, sizeof(OGLCUSTOMVERTEX), &(bmpToDraw->_vertex[ti * 4].tu)); | |
glVertexPointer(2, GL_FLOAT, sizeof(OGLCUSTOMVERTEX), &(bmpToDraw->_vertex[ti * 4].position)); | |
} | |
else | |
{ | |
glTexCoordPointer(2, GL_FLOAT, sizeof(OGLCUSTOMVERTEX), &defaultVertices[0].tu); | |
glVertexPointer(2, GL_FLOAT, sizeof(OGLCUSTOMVERTEX), &defaultVertices[0].position); | |
} | |
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |
} | |
glUseProgram(0); | |
} | |
void OGLGraphicsDriver::_render(GlobalFlipType flip, bool clearDrawListAfterwards) | |
{ | |
#if defined(IOS_VERSION) | |
ios_select_buffer(); | |
#endif | |
if (!device_screen_initialized) | |
{// TODO: find out if this is really necessary here and why | |
if (!_firstTimeInit) | |
FirstTimeInit(); | |
InitGlParams(_mode); | |
device_screen_initialized = 1; | |
} | |
std::vector<OGLDrawListEntry> &listToDraw = drawList; | |
size_t listSize = drawList.size(); | |
bool globalLeftRightFlip = (flip == kFlip_Vertical) || (flip == kFlip_Both); | |
bool globalTopBottomFlip = (flip == kFlip_Horizontal) || (flip == kFlip_Both); | |
if (_do_render_to_texture) | |
{ | |
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _fbo); | |
glClear(GL_COLOR_BUFFER_BIT); | |
glViewport(0, 0, _backRenderSize.Width, _backRenderSize.Height); | |
glMatrixMode(GL_PROJECTION); | |
glLoadIdentity(); | |
glOrtho(0, _backRenderSize.Width, 0, _backRenderSize.Height, 0, 1); | |
glMatrixMode(GL_MODELVIEW); | |
glLoadIdentity(); | |
} | |
else | |
{ | |
glDisable(GL_SCISSOR_TEST); | |
glClear(GL_COLOR_BUFFER_BIT); | |
glEnable(GL_SCISSOR_TEST); | |
glViewport(_viewportRect.Left, _viewportRect.Top, _viewportRect.GetWidth(), _viewportRect.GetHeight()); | |
glMatrixMode(GL_PROJECTION); | |
glLoadIdentity(); | |
glOrtho(0, _srcRect.GetWidth(), 0, _srcRect.GetHeight(), 0, 1); | |
glMatrixMode(GL_MODELVIEW); | |
glLoadIdentity(); | |
} | |
for (size_t i = 0; i < listSize; i++) | |
{ | |
if (listToDraw[i].skip) | |
continue; | |
if (listToDraw[i].bitmap == NULL) | |
{ | |
if (DoNullSpriteCallback(listToDraw[i].x, listToDraw[i].y)) | |
listToDraw[i] = OGLDrawListEntry((OGLBitmap*)_stageVirtualScreenDDB); | |
else | |
continue; | |
} | |
this->_renderSprite(&listToDraw[i], globalLeftRightFlip, globalTopBottomFlip); | |
} | |
if (!_screenTintSprite.skip) | |
{ | |
this->_renderSprite(&_screenTintSprite, false, false); | |
} | |
if (_do_render_to_texture) | |
{ | |
// Texture is ready, now create rectangle in the world space and draw texture upon it | |
#if defined(IOS_VERSION) | |
ios_select_buffer(); | |
#else | |
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); | |
#endif | |
glViewport(_viewportRect.Left, _viewportRect.Top, _viewportRect.GetWidth(), _viewportRect.GetHeight()); | |
glMatrixMode(GL_PROJECTION); | |
glLoadIdentity(); | |
glOrtho(0, _srcRect.GetWidth(), 0, _srcRect.GetHeight(), 0, 1); | |
glMatrixMode(GL_MODELVIEW); | |
glLoadIdentity(); | |
glDisable(GL_BLEND); | |
glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | |
// use correct sampling method when stretching buffer to the final rect | |
_filter->SetFilteringForStandardSprite(); | |
glBindTexture(GL_TEXTURE_2D, _backbuffer); | |
glTexCoordPointer(2, GL_FLOAT, 0, _backbuffer_texture_coordinates); | |
glVertexPointer(2, GL_FLOAT, 0, _backbuffer_vertices); | |
glClear(GL_COLOR_BUFFER_BIT); | |
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |
glEnable(GL_BLEND); | |
} | |
glFinish(); | |
#if defined(WINDOWS_VERSION) | |
SwapBuffers(_hDC); | |
#elif defined(ANDROID_VERSION) || defined(IOS_VERSION) | |
device_swap_buffers(); | |
#endif | |
if (clearDrawListAfterwards) | |
{ | |
drawListLastTime = drawList; | |
flipTypeLastTime = flip; | |
ClearDrawList(); | |
} | |
} | |
void OGLGraphicsDriver::ClearDrawList() | |
{ | |
drawList.clear(); | |
} | |
void OGLGraphicsDriver::DrawSprite(int x, int y, IDriverDependantBitmap* bitmap) | |
{ | |
drawList.push_back(OGLDrawListEntry((OGLBitmap*)bitmap, x, y)); | |
} | |
void OGLGraphicsDriver::DestroyDDB(IDriverDependantBitmap* bitmap) | |
{ | |
for (size_t i = 0; i < drawListLastTime.size(); i++) | |
{ | |
if (drawListLastTime[i].bitmap == bitmap) | |
{ | |
drawListLastTime[i].skip = true; | |
} | |
} | |
delete bitmap; | |
} | |
__inline void get_pixel_if_not_transparent15(unsigned short *pixel, unsigned short *red, unsigned short *green, unsigned short *blue, unsigned short *divisor) | |
{ | |
if (pixel[0] != MASK_COLOR_15) | |
{ | |
*red += algetr15(pixel[0]); | |
*green += algetg15(pixel[0]); | |
*blue += algetb15(pixel[0]); | |
divisor[0]++; | |
} | |
} | |
__inline void get_pixel_if_not_transparent32(unsigned int *pixel, unsigned int *red, unsigned int *green, unsigned int *blue, unsigned int *divisor) | |
{ | |
if (pixel[0] != MASK_COLOR_32) | |
{ | |
*red += algetr32(pixel[0]); | |
*green += algetg32(pixel[0]); | |
*blue += algetb32(pixel[0]); | |
divisor[0]++; | |
} | |
} | |
#define D3DCOLOR_RGBA(r,g,b,a) \ | |
(((((a)&0xff)<<24)|(((b)&0xff)<<16)|(((g)&0xff)<<8)|((r)&0xff))) | |
void OGLGraphicsDriver::UpdateTextureRegion(TextureTile *tile, Bitmap *bitmap, OGLBitmap *target, bool hasAlpha) | |
{ | |
int textureHeight = tile->height; | |
int textureWidth = tile->width; | |
AdjustSizeToNearestSupportedByCard(&textureWidth, &textureHeight); | |
int tileWidth = (textureWidth > tile->width) ? tile->width + 1 : tile->width; | |
int tileHeight = (textureHeight > tile->height) ? tile->height + 1 : tile->height; | |
bool usingLinearFiltering = _filter->UseLinearFiltering(); | |
bool lastPixelWasTransparent = false; | |
char *origPtr = (char*)malloc(4 * tileWidth * tileHeight); | |
char *memPtr = origPtr; | |
for (int y = 0; y < tileHeight; y++) | |
{ | |
// Mimic the behaviour of GL_CLAMP_EDGE for the bottom line | |
if (y == tile->height) | |
{ | |
unsigned int* memPtrLong = (unsigned int*)memPtr; | |
unsigned int* memPtrLong_previous = (unsigned int*)(memPtr - tileWidth * 4); | |
for (int x = 0; x < tileWidth; x++) | |
memPtrLong[x] = memPtrLong_previous[x] & 0x00FFFFFF; | |
continue; | |
} | |
lastPixelWasTransparent = false; | |
const uint8_t *scanline_before = bitmap->GetScanLine(y + tile->y - 1); | |
const uint8_t *scanline_at = bitmap->GetScanLine(y + tile->y); | |
const uint8_t *scanline_after = bitmap->GetScanLine(y + tile->y + 1); | |
for (int x = 0; x < tileWidth; x++) | |
{ | |
/* if (target->_colDepth == 15) | |
{ | |
unsigned short* memPtrShort = (unsigned short*)memPtr; | |
unsigned short* srcData = (unsigned short*)&bitmap->GetScanLine[y + tile->y][(x + tile->x) * 2]; | |
if (*srcData == MASK_COLOR_15) | |
{ | |
if (target->_opaque) // set to black if opaque | |
memPtrShort[x] = 0x8000; | |
else if (!usingLinearFiltering) | |
memPtrShort[x] = 0; | |
// set to transparent, but use the colour from the neighbouring | |
// pixel to stop the linear filter doing black outlines | |
else | |
{ | |
unsigned short red = 0, green = 0, blue = 0, divisor = 0; | |
if (x > 0) | |
get_pixel_if_not_transparent15(&srcData[-1], &red, &green, &blue, &divisor); | |
if (x < tile->width - 1) | |
get_pixel_if_not_transparent15(&srcData[1], &red, &green, &blue, &divisor); | |
if (y > 0) | |
get_pixel_if_not_transparent15((unsigned short*)&bitmap->GetScanLine[y + tile->y - 1][(x + tile->x) * 2], &red, &green, &blue, &divisor); | |
if (y < tile->height - 1) | |
get_pixel_if_not_transparent15((unsigned short*)&bitmap->GetScanLine[y + tile->y + 1][(x + tile->x) * 2], &red, &green, &blue, &divisor); | |
if (divisor > 0) | |
memPtrShort[x] = ((red / divisor) << 10) | ((green / divisor) << 5) | (blue / divisor); | |
else | |
memPtrShort[x] = 0; | |
} | |
lastPixelWasTransparent = true; | |
} | |
else | |
{ | |
memPtrShort[x] = 0x8000 | (algetr15(*srcData) << 10) | (algetg15(*srcData) << 5) | algetb15(*srcData); | |
if (lastPixelWasTransparent) | |
{ | |
// update the colour of the previous tranparent pixel, to | |
// stop black outlines when linear filtering | |
memPtrShort[x - 1] = memPtrShort[x] & 0x7FFF; | |
lastPixelWasTransparent = false; | |
} | |
} | |
} | |
else if (target->_colDepth == 32) | |
*/ | |
{ | |
unsigned int* memPtrLong = (unsigned int*)memPtr; | |
if (x == tile->width) | |
{ | |
memPtrLong[x] = memPtrLong[x - 1] & 0x00FFFFFF; | |
continue; | |
} | |
unsigned int* srcData = (unsigned int*)&scanline_at[(x + tile->x) << 2]; | |
if (*srcData == MASK_COLOR_32) | |
{ | |
if (target->_opaque) // set to black if opaque | |
memPtrLong[x] = 0xFF000000; | |
else if (!usingLinearFiltering) | |
memPtrLong[x] = 0; | |
// set to transparent, but use the colour from the neighbouring | |
// pixel to stop the linear filter doing black outlines | |
else | |
{ | |
unsigned int red = 0, green = 0, blue = 0, divisor = 0; | |
if (x > 0) | |
get_pixel_if_not_transparent32(&srcData[-1], &red, &green, &blue, &divisor); | |
if (x < tile->width - 1) | |
get_pixel_if_not_transparent32(&srcData[1], &red, &green, &blue, &divisor); | |
if (y > 0) | |
get_pixel_if_not_transparent32((unsigned int*)&scanline_before[(x + tile->x) << 2], &red, &green, &blue, &divisor); | |
if (y < tile->height - 1) | |
get_pixel_if_not_transparent32((unsigned int*)&scanline_after[(x + tile->x) << 2], &red, &green, &blue, &divisor); | |
if (divisor > 0) | |
memPtrLong[x] = ((red / divisor) << 16) | ((green / divisor) << 8) | (blue / divisor); | |
else | |
memPtrLong[x] = 0; | |
} | |
lastPixelWasTransparent = true; | |
} | |
else if (hasAlpha) | |
{ | |
memPtrLong[x] = D3DCOLOR_RGBA(algetr32(*srcData), algetg32(*srcData), algetb32(*srcData), algeta32(*srcData)); | |
} | |
else | |
{ | |
memPtrLong[x] = D3DCOLOR_RGBA(algetr32(*srcData), algetg32(*srcData), algetb32(*srcData), 0xff); | |
if (lastPixelWasTransparent) | |
{ | |
// update the colour of the previous tranparent pixel, to | |
// stop black outlines when linear filtering | |
memPtrLong[x - 1] = memPtrLong[x] & 0x00FFFFFF; | |
lastPixelWasTransparent = false; | |
} | |
} | |
} | |
} | |
memPtr += tileWidth * 4; | |
} | |
unsigned int newTexture = tile->texture; | |
glBindTexture(GL_TEXTURE_2D, tile->texture); | |
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tileWidth, tileHeight, GL_RGBA, GL_UNSIGNED_BYTE, origPtr); | |
free(origPtr); | |
} | |
void OGLGraphicsDriver::UpdateDDBFromBitmap(IDriverDependantBitmap* bitmapToUpdate, Bitmap *bitmap, bool hasAlpha) | |
{ | |
OGLBitmap *target = (OGLBitmap*)bitmapToUpdate; | |
Bitmap *source = bitmap; | |
if ((target->_width == bitmap->GetWidth()) && | |
(target->_height == bitmap->GetHeight())) | |
{ | |
if (bitmap->GetColorDepth() != target->_colDepth) | |
{ | |
//throw Ali3DException("Mismatched colour depths"); | |
source = BitmapHelper::CreateBitmapCopy(bitmap, 32); | |
} | |
target->_hasAlpha = hasAlpha; | |
for (int i = 0; i < target->_numTiles; i++) | |
{ | |
UpdateTextureRegion(&target->_tiles[i], source, target, hasAlpha); | |
} | |
if (source != bitmap) | |
delete source; | |
} | |
} | |
Bitmap *OGLGraphicsDriver::ConvertBitmapToSupportedColourDepth(Bitmap *bitmap) | |
{ | |
int colourDepth = bitmap->GetColorDepth(); | |
if (colourDepth != 32) | |
{ | |
int old_conv = get_color_conversion(); | |
set_color_conversion(COLORCONV_KEEP_TRANS | COLORCONV_TOTAL); | |
// we need 32-bit colour | |
Bitmap *tempBmp = BitmapHelper::CreateBitmapCopy(bitmap, 32); | |
set_color_conversion(old_conv); | |
return tempBmp; | |
} | |
return bitmap; | |
} | |
void OGLGraphicsDriver::AdjustSizeToNearestSupportedByCard(int *width, int *height) | |
{ | |
int allocatedWidth = *width, allocatedHeight = *height; | |
bool foundWidth = false, foundHeight = false; | |
int tryThis = 2; | |
while ((!foundWidth) || (!foundHeight)) | |
{ | |
if ((tryThis >= allocatedWidth) && (!foundWidth)) | |
{ | |
allocatedWidth = tryThis; | |
foundWidth = true; | |
} | |
if ((tryThis >= allocatedHeight) && (!foundHeight)) | |
{ | |
allocatedHeight = tryThis; | |
foundHeight = true; | |
} | |
tryThis = tryThis << 1; | |
} | |
*width = allocatedWidth; | |
*height = allocatedHeight; | |
} | |
IDriverDependantBitmap* OGLGraphicsDriver::CreateDDBFromBitmap(Bitmap *bitmap, bool hasAlpha, bool opaque) | |
{ | |
int allocatedWidth = bitmap->GetWidth(); | |
int allocatedHeight = bitmap->GetHeight(); | |
// NOTE: original bitmap object is not modified in this function | |
Bitmap *tempBmp = ConvertBitmapToSupportedColourDepth(bitmap); | |
bitmap = tempBmp; | |
int colourDepth = bitmap->GetColorDepth(); | |
OGLBitmap *ddb = new OGLBitmap(bitmap->GetWidth(), bitmap->GetHeight(), colourDepth, opaque); | |
AdjustSizeToNearestSupportedByCard(&allocatedWidth, &allocatedHeight); | |
int tilesAcross = 1, tilesDown = 1; | |
// *************** REMOVE THESE LINES ************* | |
//direct3ddevicecaps.MaxTextureWidth = 64; | |
//direct3ddevicecaps.MaxTextureHeight = 256; | |
// *************** END REMOVE THESE LINES ************* | |
// Calculate how many textures will be necessary to | |
// store this image | |
int MaxTextureWidth = 512; | |
int MaxTextureHeight = 512; | |
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &MaxTextureWidth); | |
MaxTextureHeight = MaxTextureWidth; | |
tilesAcross = (allocatedWidth + MaxTextureWidth - 1) / MaxTextureWidth; | |
tilesDown = (allocatedHeight + MaxTextureHeight - 1) / MaxTextureHeight; | |
int tileWidth = bitmap->GetWidth() / tilesAcross; | |
int lastTileExtraWidth = bitmap->GetWidth() % tilesAcross; | |
int tileHeight = bitmap->GetHeight() / tilesDown; | |
int lastTileExtraHeight = bitmap->GetHeight() % tilesDown; | |
int tileAllocatedWidth = tileWidth; | |
int tileAllocatedHeight = tileHeight; | |
AdjustSizeToNearestSupportedByCard(&tileAllocatedWidth, &tileAllocatedHeight); | |
int numTiles = tilesAcross * tilesDown; | |
TextureTile *tiles = (TextureTile*)malloc(sizeof(TextureTile) * numTiles); | |
memset(tiles, 0, sizeof(TextureTile) * numTiles); | |
OGLCUSTOMVERTEX *vertices = NULL; | |
if ((numTiles == 1) && | |
(allocatedWidth == bitmap->GetWidth()) && | |
(allocatedHeight == bitmap->GetHeight())) | |
{ | |
// use default whole-image vertices | |
} | |
else | |
{ | |
// The texture is not the same as the bitmap, so create some custom vertices | |
// so that only the relevant portion of the texture is rendered | |
int vertexBufferSize = numTiles * 4 * sizeof(OGLCUSTOMVERTEX); | |
ddb->_vertex = vertices = (OGLCUSTOMVERTEX*)malloc(vertexBufferSize); | |
} | |
for (int x = 0; x < tilesAcross; x++) | |
{ | |
for (int y = 0; y < tilesDown; y++) | |
{ | |
TextureTile *thisTile = &tiles[y * tilesAcross + x]; | |
int thisAllocatedWidth = tileAllocatedWidth; | |
int thisAllocatedHeight = tileAllocatedHeight; | |
thisTile->x = x * tileWidth; | |
thisTile->y = y * tileHeight; | |
thisTile->width = tileWidth; | |
thisTile->height = tileHeight; | |
if (x == tilesAcross - 1) | |
{ | |
thisTile->width += lastTileExtraWidth; | |
thisAllocatedWidth = thisTile->width; | |
AdjustSizeToNearestSupportedByCard(&thisAllocatedWidth, &thisAllocatedHeight); | |
} | |
if (y == tilesDown - 1) | |
{ | |
thisTile->height += lastTileExtraHeight; | |
thisAllocatedHeight = thisTile->height; | |
AdjustSizeToNearestSupportedByCard(&thisAllocatedWidth, &thisAllocatedHeight); | |
} | |
if (vertices != NULL) | |
{ | |
for (int vidx = 0; vidx < 4; vidx++) | |
{ | |
int i = (y * tilesAcross + x) * 4 + vidx; | |
vertices[i] = defaultVertices[vidx]; | |
if (vertices[i].tu > 0.0) | |
{ | |
vertices[i].tu = (float)thisTile->width / (float)thisAllocatedWidth; | |
} | |
if (vertices[i].tv > 0.0) | |
{ | |
vertices[i].tv = (float)thisTile->height / (float)thisAllocatedHeight; | |
} | |
} | |
} | |
glGenTextures(1, &thisTile->texture); | |
glBindTexture(GL_TEXTURE_2D, thisTile->texture); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, thisAllocatedWidth, thisAllocatedHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); | |
} | |
} | |
ddb->_numTiles = numTiles; | |
ddb->_tiles = tiles; | |
UpdateDDBFromBitmap(ddb, bitmap, hasAlpha); | |
if (tempBmp != bitmap) | |
delete tempBmp; | |
return ddb; | |
} | |
void OGLGraphicsDriver::do_fade(bool fadingOut, int speed, int targetColourRed, int targetColourGreen, int targetColourBlue) | |
{ | |
if (fadingOut) | |
{ | |
this->_reDrawLastFrame(); | |
} | |
else if (_drawScreenCallback != NULL) | |
_drawScreenCallback(); | |
Bitmap *blackSquare = BitmapHelper::CreateBitmap(16, 16, 32); | |
blackSquare->Clear(makecol32(targetColourRed, targetColourGreen, targetColourBlue)); | |
IDriverDependantBitmap *d3db = this->CreateDDBFromBitmap(blackSquare, false, false); | |
delete blackSquare; | |
d3db->SetStretch(_srcRect.GetWidth(), _srcRect.GetHeight(), false); | |
this->DrawSprite(-_global_x_offset, -_global_y_offset, d3db); | |
if (speed <= 0) speed = 16; | |
speed *= 2; // harmonise speeds with software driver which is faster | |
for (int a = 1; a < 255; a += speed) | |
{ | |
int timerValue = *_loopTimer; | |
d3db->SetTransparency(fadingOut ? a : (255 - a)); | |
this->_render(flipTypeLastTime, false); | |
do | |
{ | |
if (_pollingCallback) | |
_pollingCallback(); | |
platform->YieldCPU(); | |
} | |
while (timerValue == *_loopTimer); | |
} | |
if (fadingOut) | |
{ | |
d3db->SetTransparency(0); | |
this->_render(flipTypeLastTime, false); | |
} | |
this->DestroyDDB(d3db); | |
this->ClearDrawList(); | |
} | |
void OGLGraphicsDriver::FadeOut(int speed, int targetColourRed, int targetColourGreen, int targetColourBlue) | |
{ | |
do_fade(true, speed, targetColourRed, targetColourGreen, targetColourBlue); | |
} | |
void OGLGraphicsDriver::FadeIn(int speed, PALLETE p, int targetColourRed, int targetColourGreen, int targetColourBlue) | |
{ | |
do_fade(false, speed, targetColourRed, targetColourGreen, targetColourBlue); | |
} | |
void OGLGraphicsDriver::BoxOutEffect(bool blackingOut, int speed, int delay) | |
{ | |
if (blackingOut) | |
{ | |
this->_reDrawLastFrame(); | |
} | |
else if (_drawScreenCallback != NULL) | |
_drawScreenCallback(); | |
Bitmap *blackSquare = BitmapHelper::CreateBitmap(16, 16, 32); | |
blackSquare->Clear(); | |
IDriverDependantBitmap *d3db = this->CreateDDBFromBitmap(blackSquare, false, false); | |
delete blackSquare; | |
d3db->SetStretch(_srcRect.GetWidth(), _srcRect.GetHeight(), false); | |
this->DrawSprite(0, 0, d3db); | |
if (!blackingOut) | |
{ | |
// when fading in, draw four black boxes, one | |
// across each side of the screen | |
this->DrawSprite(0, 0, d3db); | |
this->DrawSprite(0, 0, d3db); | |
this->DrawSprite(0, 0, d3db); | |
} | |
int yspeed = _srcRect.GetHeight() / (_srcRect.GetWidth() / speed); | |
int boxWidth = speed; | |
int boxHeight = yspeed; | |
while (boxWidth < _srcRect.GetWidth()) | |
{ | |
boxWidth += speed; | |
boxHeight += yspeed; | |
size_t last = drawList.size() - 1; | |
if (blackingOut) | |
{ | |
drawList[last].x = _srcRect.GetWidth() / 2- boxWidth / 2; | |
drawList[last].y = _srcRect.GetHeight() / 2 - boxHeight / 2; | |
d3db->SetStretch(boxWidth, boxHeight, false); | |
} | |
else | |
{ | |
drawList[last - 3].x = _srcRect.GetWidth() / 2 - boxWidth / 2 - _srcRect.GetWidth(); | |
drawList[last - 2].y = _srcRect.GetHeight() / 2 - boxHeight / 2 - _srcRect.GetHeight(); | |
drawList[last - 1].x = _srcRect.GetWidth() / 2 + boxWidth / 2; | |
drawList[last ].y = _srcRect.GetHeight() / 2 + boxHeight / 2; | |
d3db->SetStretch(_srcRect.GetWidth(), _srcRect.GetHeight(), false); | |
} | |
this->_render(flipTypeLastTime, false); | |
if (_pollingCallback) | |
_pollingCallback(); | |
platform->Delay(delay); | |
} | |
this->DestroyDDB(d3db); | |
this->ClearDrawList(); | |
} | |
bool OGLGraphicsDriver::PlayVideo(const char *filename, bool useAVISound, VideoSkipType skipType, bool stretchToFullScreen) | |
{ | |
return true; | |
} | |
void OGLGraphicsDriver::create_screen_tint_bitmap() | |
{ | |
// Some work on textures depends on current scaling filter, sadly | |
// TODO: find out if there is a workaround for that | |
if (!IsModeSet() || !_filter) | |
return; | |
_screenTintLayer = BitmapHelper::CreateBitmap(16, 16, _mode.ColorDepth); | |
_screenTintLayer = ReplaceBitmapWithSupportedFormat(_screenTintLayer); | |
_screenTintLayerDDB = (OGLBitmap*)this->CreateDDBFromBitmap(_screenTintLayer, false, false); | |
_screenTintSprite.bitmap = _screenTintLayerDDB; | |
} | |
void OGLGraphicsDriver::SetScreenTint(int red, int green, int blue) | |
{ | |
if ((red != _tint_red) || (green != _tint_green) || (blue != _tint_blue)) | |
{ | |
_tint_red = red; | |
_tint_green = green; | |
_tint_blue = blue; | |
_screenTintLayer->Clear(makecol_depth(_screenTintLayer->GetColorDepth(), red, green, blue)); | |
this->UpdateDDBFromBitmap(_screenTintLayerDDB, _screenTintLayer, false); | |
_screenTintLayerDDB->SetStretch(_srcRect.GetWidth(), _srcRect.GetHeight(), false); | |
_screenTintLayerDDB->SetTransparency(128); | |
_screenTintSprite.skip = ((red == 0) && (green == 0) && (blue == 0)); | |
} | |
} | |
OGLGraphicsFactory *OGLGraphicsFactory::_factory = NULL; | |
OGLGraphicsFactory::~OGLGraphicsFactory() | |
{ | |
_factory = NULL; | |
} | |
size_t OGLGraphicsFactory::GetFilterCount() const | |
{ | |
return 2; | |
} | |
const GfxFilterInfo *OGLGraphicsFactory::GetFilterInfo(size_t index) const | |
{ | |
switch (index) | |
{ | |
case 0: | |
return &OGLGfxFilter::FilterInfo; | |
case 1: | |
return &AAOGLGfxFilter::FilterInfo; | |
default: | |
return NULL; | |
} | |
} | |
String OGLGraphicsFactory::GetDefaultFilterID() const | |
{ | |
return OGLGfxFilter::FilterInfo.Id; | |
} | |
/* static */ OGLGraphicsFactory *OGLGraphicsFactory::GetFactory() | |
{ | |
if (!_factory) | |
_factory = new OGLGraphicsFactory(); | |
return _factory; | |
} | |
OGLGraphicsDriver *OGLGraphicsFactory::EnsureDriverCreated() | |
{ | |
if (!_driver) | |
_driver = new OGLGraphicsDriver(); | |
return _driver; | |
} | |
OGLGfxFilter *OGLGraphicsFactory::CreateFilter(const String &id) | |
{ | |
if (OGLGfxFilter::FilterInfo.Id.CompareNoCase(id) == 0) | |
return new OGLGfxFilter(); | |
else if (AAOGLGfxFilter::FilterInfo.Id.CompareNoCase(id) == 0) | |
return new AAOGLGfxFilter(); | |
return NULL; | |
} | |
} // namespace OGL | |
} // namespace Engine | |
} // namespace AGS | |
#endif // only on Windows, Android and iOS |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment