FIFE
2008.0
|
00001 /*************************************************************************** 00002 * Copyright (C) 2005-2008 by the FIFE team * 00003 * http://www.fifengine.de * 00004 * This file is part of FIFE. * 00005 * * 00006 * FIFE is free software; you can redistribute it and/or * 00007 * modify it under the terms of the GNU Lesser General Public * 00008 * License as published by the Free Software Foundation; either * 00009 * version 2.1 of the License, or (at your option) any later version. * 00010 * * 00011 * This library is distributed in the hope that it will be useful, * 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 00014 * Lesser General Public License for more details. * 00015 * * 00016 * You should have received a copy of the GNU Lesser General Public * 00017 * License along with this library; if not, write to the * 00018 * Free Software Foundation, Inc., * 00019 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * 00020 ***************************************************************************/ 00021 00022 // Standard C++ library includes 00023 00024 // Platform specific includes 00025 00026 // 3rd party library includes 00027 #include <SDL.h> 00028 00029 // FIFE includes 00030 #include "util/base/exception.h" 00031 #include "util/log/logger.h" 00032 #include "video/devicecaps.h" 00033 00034 #include "fife_opengl.h" 00035 #include "glimage.h" 00036 #include "renderbackendopengl.h" 00037 #include "SDL_image.h" 00038 00039 00040 namespace FIFE { 00041 static Logger _log(LM_VIDEO); 00042 00043 RenderBackendOpenGL::RenderBackendOpenGL(const SDL_Color& colorkey) : RenderBackend(colorkey) { 00044 // Get the pixelformat we want. 00045 SDL_Surface* testsurface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, 1, 1, 32, 00046 RMASK, GMASK, BMASK ,AMASK); 00047 00048 m_rgba_format = *(testsurface->format); 00049 SDL_FreeSurface(testsurface); 00050 m_lightmodel = 0; 00051 m_light_enabled = false; 00052 m_stencil_enabled = false; 00053 m_alpha_enabled = false; 00054 m_sten_ref = 0; 00055 m_sten_buf = 0; 00056 m_sten_op = 0; 00057 m_sten_func = 0; 00058 m_blend_src = GL_SRC_ALPHA; 00059 m_blend_dst = GL_ONE_MINUS_SRC_ALPHA; 00060 } 00061 00062 const std::string& RenderBackendOpenGL::getName() const { 00063 static std::string backend_name = "OpenGL"; 00064 return backend_name; 00065 } 00066 00067 RenderBackendOpenGL::~RenderBackendOpenGL() { 00068 } 00069 00070 00071 void RenderBackendOpenGL::init(const std::string& driver) { 00072 00073 //note: driver has no affect on the opengl renderer so do nothing with it here. 00074 00075 Uint32 flags = SDL_INIT_VIDEO; 00076 if (SDL_InitSubSystem(flags) < 0) 00077 throw SDLException(SDL_GetError()); 00078 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 00079 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); 00080 00081 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); // temporary hack 00082 00083 } 00084 00085 void RenderBackendOpenGL::clearBackBuffer() { 00086 GLDisable flag(GL_SCISSOR_TEST); 00087 glClear(GL_COLOR_BUFFER_BIT); 00088 } 00089 00090 Image* RenderBackendOpenGL::createMainScreen(const ScreenMode& mode, const std::string& title, const std::string& icon){ 00091 if(icon != "") { 00092 SDL_Surface *img = IMG_Load(icon.c_str()); 00093 if(img != NULL) { 00094 SDL_WM_SetIcon(img, 0); 00095 } 00096 } 00097 00098 Image *image = setScreenMode(mode); 00099 00100 SDL_WM_SetCaption(title.c_str(), 0); 00101 00102 return image; 00103 } 00104 00105 Image* RenderBackendOpenGL::setScreenMode(const ScreenMode& mode) { 00106 uint16_t width = mode.getWidth(); 00107 uint16_t height = mode.getHeight(); 00108 uint16_t bitsPerPixel = mode.getBPP(); 00109 bool fs = mode.isFullScreen(); 00110 uint32_t flags = mode.getSDLFlags(); 00111 00112 SDL_Surface* screen = NULL; 00113 00114 if (bitsPerPixel != 0) { 00115 uint16_t bpp = SDL_VideoModeOK(width, height, bitsPerPixel, flags); 00116 if (!bpp){ 00117 throw SDLException("Selected video mode not supported!"); 00118 } 00119 } 00120 00121 screen = SDL_SetVideoMode(width, height, bitsPerPixel, flags); 00122 if( !screen ) { 00123 throw SDLException("Unable to set video mode selected!"); 00124 } 00125 00126 FL_LOG(_log, LMsg("RenderBackendOpenGL") 00127 << "Videomode " << width << "x" << height 00128 << " at " << int(bitsPerPixel) << " bpp"); 00129 00130 //update the screen mode with the actual flags used 00131 m_screenMode = ScreenMode(width, 00132 height, 00133 bitsPerPixel, 00134 screen->flags); 00135 00136 00137 if (!screen) { 00138 throw SDLException(SDL_GetError()); 00139 } 00140 00141 glViewport(0, 0, width, height); 00142 glMatrixMode(GL_PROJECTION); 00143 gluOrtho2D(0, width, height, 0); 00144 glMatrixMode(GL_MODELVIEW); 00145 00146 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 00147 00148 glEnable(GL_TEXTURE_2D); 00149 glEnable(GL_BLEND); 00150 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 00151 00152 glEnable(GL_SCISSOR_TEST); 00153 00154 glPointSize(1.0); 00155 glLineWidth(1.0); 00156 delete m_screen; 00157 delete m_screen; 00158 m_screen = new GLImage(screen); 00159 return m_screen; 00160 } 00161 00162 00163 void RenderBackendOpenGL::startFrame() { 00164 } 00165 00166 void RenderBackendOpenGL::endFrame() { 00167 SDL_GL_SwapBuffers(); 00168 } 00169 00170 Image* RenderBackendOpenGL::createImage(SDL_Surface* surface) { 00171 // Given an abritary surface, we must convert it to the format GLImage will understand. 00172 // It's easiest to let SDL do this for us. 00173 00174 // Uh. Gotta love this :-) 00175 // Check for colorkey too? 00176 // Leave out the loss/shift checks? 00177 if( m_rgba_format.BitsPerPixel == surface->format->BitsPerPixel 00178 && m_rgba_format.Rmask == surface->format->Rmask 00179 && m_rgba_format.Gmask == surface->format->Gmask 00180 && m_rgba_format.Bmask == surface->format->Bmask 00181 && m_rgba_format.Amask == surface->format->Amask 00182 && m_rgba_format.Rshift == surface->format->Rshift 00183 && m_rgba_format.Gshift == surface->format->Gshift 00184 && m_rgba_format.Bshift == surface->format->Bshift 00185 && m_rgba_format.Ashift == surface->format->Ashift 00186 && m_rgba_format.Rloss == surface->format->Rloss 00187 && m_rgba_format.Gloss == surface->format->Gloss 00188 && m_rgba_format.Bloss == surface->format->Bloss 00189 && m_rgba_format.Aloss == surface->format->Aloss 00190 && surface->flags & SDL_SRCALPHA ) { 00191 00192 return new GLImage(surface); 00193 } 00194 00195 SDL_Surface* conv = SDL_ConvertSurface(surface, &m_rgba_format, SDL_SWSURFACE | SDL_SRCALPHA); 00196 GLImage* image = new GLImage(conv); 00197 SDL_FreeSurface( surface ); 00198 return image; 00199 } 00200 00201 Image* RenderBackendOpenGL::createImage(const uint8_t* data, unsigned int width, unsigned int height) { 00202 return new GLImage(data, width, height); 00203 } 00204 00205 void RenderBackendOpenGL::setLightingModel(unsigned int lighting) { 00206 if (m_lightmodel != lighting) { 00207 if (m_lightmodel == 1) { 00208 disableLighting(); 00209 glDisable(GL_COLOR_MATERIAL); 00210 } else if (lighting == 1) { 00211 enableLighting(); 00212 glEnable(GL_LIGHT0); 00213 glColorMaterial(GL_FRONT, GL_DIFFUSE); 00214 glEnable(GL_COLOR_MATERIAL); 00215 } 00216 m_lightmodel = lighting; 00217 } 00218 } 00219 00220 unsigned int RenderBackendOpenGL::getLightingModel() const { 00221 return m_lightmodel; 00222 } 00223 00224 void RenderBackendOpenGL::enableLighting() { 00225 if (m_lightmodel == 1 && !m_light_enabled) { 00226 glEnable(GL_LIGHTING); 00227 m_light_enabled = true; 00228 } 00229 } 00230 00231 void RenderBackendOpenGL::disableLighting() { 00232 if (m_lightmodel == 1 && m_light_enabled) { 00233 glDisable(GL_LIGHTING); 00234 m_light_enabled = false; 00235 } 00236 } 00237 00238 void RenderBackendOpenGL::setLighting(float red, float green, float blue, float alpha) { 00239 if (m_lightmodel == 1) { 00240 GLfloat lightDiffuse[] = {red, green, blue, alpha}; 00241 glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse); 00242 } else if(m_lightmodel == 2) { 00243 m_lred = red; 00244 m_lgreen = green; 00245 m_lblue = blue; 00246 m_lalpha = alpha; 00247 } 00248 } 00249 00250 void RenderBackendOpenGL::resetLighting() { 00251 if (m_lightmodel == 1) { 00252 setLighting(1.0, 1.0, 1.0, 1.0); 00253 } else if (m_lightmodel == 2 && m_lalpha > 0.01) { 00254 uint16_t width = getScreenWidth(); 00255 uint16_t height = getScreenHeight(); 00256 Point p = Point(0,0); 00257 setStencilTest(0, 0, 5); 00258 fillRectangle(p, width, height, m_lred*255, m_lgreen*255, m_lblue*255, m_lalpha*255); 00259 disableStencilTest(); 00260 } 00261 } 00262 00263 void RenderBackendOpenGL::enableStencilTest() { 00264 if (!m_stencil_enabled) { 00265 glEnable(GL_STENCIL_TEST); 00266 m_stencil_enabled = true; 00267 } 00268 } 00269 00270 void RenderBackendOpenGL::disableStencilTest() { 00271 if (m_stencil_enabled) { 00272 glDisable(GL_STENCIL_TEST); 00273 m_stencil_enabled = false; 00274 } 00275 } 00276 00277 void RenderBackendOpenGL::setStencilTest(uint8_t stencil_ref, unsigned int stencil_op, unsigned int stencil_func) { 00278 enableStencilTest(); 00279 if(m_sten_op != stencil_op) { 00280 GLenum op; 00281 m_sten_op = stencil_op; 00282 switch(stencil_op) { 00283 default : 00284 case 0 : op = GL_KEEP; break; 00285 case 1 : op = GL_ZERO; break; 00286 case 2 : op = GL_REPLACE; break; 00287 case 3 : op = GL_INCR; break; 00288 case 4 : op = GL_DECR; break; 00289 case 5 : op = GL_INVERT; break; 00290 } 00291 glStencilOp(GL_KEEP, GL_KEEP, op); 00292 } 00293 00294 if(m_sten_ref != stencil_ref || m_sten_func != stencil_func) { 00295 GLenum func; 00296 m_sten_ref = stencil_ref; 00297 m_sten_func = stencil_func; 00298 switch(stencil_func) { 00299 default : 00300 case 0 : func = GL_NEVER; break; 00301 case 1 : func = GL_LESS; break; 00302 case 2 : func = GL_LEQUAL; break; 00303 case 3 : func = GL_GREATER; break; 00304 case 4 : func = GL_GEQUAL; break; 00305 case 5 : func = GL_EQUAL; break; 00306 case 6 : func = GL_NOTEQUAL; break; 00307 case 7 : func = GL_ALWAYS; break; 00308 } 00309 glStencilFunc(func, stencil_ref, 0xff); 00310 } 00311 } 00312 00313 void RenderBackendOpenGL::resetStencilBuffer(uint8_t buffer) { 00314 if (buffer != m_sten_buf) { 00315 m_sten_buf = buffer; 00316 glClearStencil(buffer); 00317 } 00318 GLDisable flag(GL_SCISSOR_TEST); 00319 glClear(GL_STENCIL_BUFFER_BIT); 00320 } 00321 00322 uint8_t RenderBackendOpenGL::getStencilRef() const { 00323 return m_sten_ref; 00324 } 00325 00326 void RenderBackendOpenGL::enableAlphaTest() { 00327 if (!m_alpha_enabled) { 00328 glEnable(GL_ALPHA_TEST); 00329 m_alpha_enabled = true; 00330 } 00331 } 00332 00333 void RenderBackendOpenGL::disableAlphaTest() { 00334 if (m_alpha_enabled) { 00335 glDisable(GL_ALPHA_TEST); 00336 m_alpha_enabled = false; 00337 } 00338 } 00339 00340 void RenderBackendOpenGL::setAlphaTest(float ref_alpha) { 00341 enableAlphaTest(); 00342 glAlphaFunc(GL_GREATER, ref_alpha); 00343 } 00344 00345 void RenderBackendOpenGL::changeBlending(int src, int dst) { 00346 GLenum src_fact; 00347 GLenum dst_fact; 00348 00349 switch(src) { 00350 case 0 : src_fact = GL_ZERO; break; 00351 case 1 : src_fact = GL_ONE; break; 00352 case 2 : src_fact = GL_DST_COLOR; break; 00353 case 3 : src_fact = GL_ONE_MINUS_DST_COLOR; break; 00354 case 4 : src_fact = GL_SRC_ALPHA; break; 00355 case 5 : src_fact = GL_ONE_MINUS_SRC_ALPHA; break; 00356 case 6 : src_fact = GL_DST_ALPHA; break; 00357 case 7 : src_fact = GL_ONE_MINUS_DST_ALPHA; break; 00358 00359 default : src_fact = GL_DST_COLOR; break; 00360 } 00361 00362 switch(dst) { 00363 case 0 : dst_fact = GL_ZERO; break; 00364 case 1 : dst_fact = GL_ONE; break; 00365 case 2 : dst_fact = GL_SRC_COLOR; break; 00366 case 3 : dst_fact = GL_ONE_MINUS_SRC_COLOR; break; 00367 case 4 : dst_fact = GL_SRC_ALPHA; break; 00368 case 5 : dst_fact = GL_ONE_MINUS_SRC_ALPHA; break; 00369 case 6 : dst_fact = GL_DST_ALPHA; break; 00370 case 7 : dst_fact = GL_ONE_MINUS_DST_ALPHA; break; 00371 00372 default : dst_fact = GL_SRC_ALPHA; break; 00373 } 00374 00375 if (m_blend_src != src_fact || m_blend_dst != dst_fact) { 00376 m_blend_src = src_fact; 00377 m_blend_dst = dst_fact; 00378 glBlendFunc(src_fact, dst_fact); 00379 } 00380 } 00381 00382 bool RenderBackendOpenGL::putPixel(int x, int y, int r, int g, int b, int a) { 00383 if ((x < 0) || (x >= (int)getWidth()) || (y < 0) || (y >= (int)getHeight())) { 00384 return false; 00385 } 00386 00387 glColor4ub(r, g, b, a); 00388 00389 glBegin(GL_POINTS); 00390 glVertex2i(x, y); 00391 glEnd(); 00392 return true; 00393 } 00394 00395 void RenderBackendOpenGL::drawLine(const Point& p1, const Point& p2, int r, int g, int b, int a) { 00396 glColor4ub(r, g, b, a); 00397 00398 glBegin(GL_LINES); 00399 glVertex2f(p1.x+0.5f, p1.y+0.5f); 00400 glVertex2f(p2.x+0.5f, p2.y+0.5f); 00401 glEnd(); 00402 00403 glBegin(GL_POINTS); 00404 glVertex2f(p2.x+0.5f, p2.y+0.5f); 00405 glEnd(); 00406 } 00407 00408 void RenderBackendOpenGL::drawTriangle(const Point& p1, const Point& p2, const Point& p3, int r, int g, int b, int a) { 00409 glColor4ub(r, g, b, a); 00410 00411 glBegin(GL_TRIANGLES); 00412 glVertex2f(p1.x, p1.y); 00413 glVertex2f(p2.x, p2.y); 00414 glVertex2f(p3.x, p3.y); 00415 glEnd(); 00416 } 00417 00418 void RenderBackendOpenGL::drawRectangle(const Point& p, uint16_t w, uint16_t h, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { 00419 glColor4ub(r, g, b, a); 00420 00421 glBegin(GL_LINE_LOOP); 00422 glVertex2f(p.x, p.y); 00423 glVertex2f(p.x+w, p.y); 00424 glVertex2f(p.x+w, p.y+h); 00425 glVertex2f(p.x, p.y+h); 00426 glEnd(); 00427 } 00428 00429 void RenderBackendOpenGL::fillRectangle(const Point& p, uint16_t w, uint16_t h, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { 00430 glColor4ub(r, g, b, a); 00431 00432 glBegin(GL_QUADS); 00433 glVertex2f(p.x, p.y); 00434 glVertex2f(p.x+w, p.y); 00435 glVertex2f(p.x+w, p.y+h); 00436 glVertex2f(p.x, p.y+h); 00437 glEnd(); 00438 } 00439 00440 void RenderBackendOpenGL::drawQuad(const Point& p1, const Point& p2, const Point& p3, const Point& p4, int r, int g, int b, int a) { 00441 glColor4ub(r, g, b, a); 00442 00443 glBegin(GL_QUADS); 00444 glVertex2f(p1.x, p1.y); 00445 glVertex2f(p2.x, p2.y); 00446 glVertex2f(p3.x, p3.y); 00447 glVertex2f(p4.x, p4.y); 00448 glEnd(); 00449 } 00450 00451 void RenderBackendOpenGL::drawVertex(const Point& p, const uint8_t size, int r, int g, int b, int a){ 00452 GLfloat width; 00453 glGetFloatv(GL_LINE_WIDTH, &width); 00454 glLineWidth(1.0); 00455 00456 glColor4ub(r, g, b, a); 00457 00458 glBegin(GL_LINE_LOOP); 00459 glVertex2f(p.x-size, p.y+size); 00460 glVertex2f(p.x+size, p.y+size); 00461 glVertex2f(p.x+size, p.y-size); 00462 glVertex2f(p.x-size, p.y-size); 00463 glEnd(); 00464 00465 glLineWidth(width); 00466 } 00467 00468 void RenderBackendOpenGL::drawLightPrimitive(const Point& p, uint8_t intensity, float radius, int subdivisions, float xstretch, float ystretch, uint8_t red, uint8_t green, uint8_t blue) { 00469 glBegin(GL_TRIANGLE_FAN); 00470 glColor4ub(red, green, blue, intensity); 00471 glVertex2f(p.x, p.y); 00472 if (m_lightmodel == 2) { 00473 glColor4ub(0, 0, 0, intensity); 00474 } else { 00475 glColor4ub(0, 0, 0, 255); 00476 } 00477 for(float angle=0; angle<=Mathf::twoPi(); angle+=(Mathf::twoPi()/subdivisions)){ 00478 glVertex2f( radius*Mathf::Cos(angle)*xstretch + p.x, 00479 radius*Mathf::Sin(angle)*ystretch + p.y); 00480 } 00481 glVertex2f(p.x+radius*xstretch, p.y); 00482 glEnd(); 00483 } 00484 }