mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 08:26:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			311 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			311 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "sdl_gl_driver.hpp"
 | 
						|
 | 
						|
#include <SDL.h>
 | 
						|
#include <SDL_image.h>
 | 
						|
#include <SDL_opengl.h>
 | 
						|
#include <stdexcept>
 | 
						|
#include <assert.h>
 | 
						|
 | 
						|
using namespace Mangle::Rend2D;
 | 
						|
 | 
						|
void SDLGL_Sprite::draw(Sprite *s,              // Must be SDLGL_Sprite
 | 
						|
                      int x, int y,             // Destination position
 | 
						|
                      int sx, int sy,           // Source position
 | 
						|
                      int w, int h              // Amount to draw. -1 means remainder.
 | 
						|
                      )
 | 
						|
{
 | 
						|
  // Get source surface
 | 
						|
  SDLGL_Sprite *other = dynamic_cast<SDLGL_Sprite*>(s);
 | 
						|
  assert(other != NULL);
 | 
						|
  SDL_Surface *img = other->getSurface();
 | 
						|
 | 
						|
  // Check coordinate validity
 | 
						|
  assert(sx <= img->w     && sy <= img->h);
 | 
						|
  assert(x  <= surface->w && y  <= surface->h);
 | 
						|
  assert(sx >= 0 && sy >= 0);
 | 
						|
 | 
						|
  // Compute width and height if necessary
 | 
						|
  if(w == -1) w = img->w - sx;
 | 
						|
  if(h == -1) h = img->h - sy;
 | 
						|
 | 
						|
  // Check them if they're valid
 | 
						|
  assert(w >= 0 && w <= img->w);
 | 
						|
  assert(h >= 0 && h <= img->h);
 | 
						|
 | 
						|
  SDL_Rect dest;
 | 
						|
  dest.x = x;
 | 
						|
  dest.y = y;
 | 
						|
  dest.w = w;
 | 
						|
  dest.h = h;
 | 
						|
 | 
						|
  SDL_Rect src;
 | 
						|
  src.x = sx;
 | 
						|
  src.y = sy;
 | 
						|
  src.w = w;
 | 
						|
  src.h = h;
 | 
						|
 | 
						|
  // Do the Blitman
 | 
						|
  SDL_BlitSurface(img, &src, surface, &dest);
 | 
						|
}
 | 
						|
 | 
						|
SDLGL_Sprite::SDLGL_Sprite(SDL_Surface *s, bool autoDelete)
 | 
						|
  : surface(s), autoDel(autoDelete)
 | 
						|
{
 | 
						|
  assert(surface != NULL);
 | 
						|
}
 | 
						|
 | 
						|
SDLGL_Sprite::~SDLGL_Sprite()
 | 
						|
{
 | 
						|
  if(autoDel)
 | 
						|
    SDL_FreeSurface(surface);
 | 
						|
}
 | 
						|
 | 
						|
void SDLGL_Sprite::fill(int value)
 | 
						|
{
 | 
						|
  SDL_FillRect(surface, NULL, value);
 | 
						|
}
 | 
						|
 | 
						|
int SDLGL_Sprite::width()  { return surface->w; }
 | 
						|
int SDLGL_Sprite::height() { return surface->h; }
 | 
						|
 | 
						|
SDLGLDriver::SDLGLDriver() : display(NULL), realDisp(NULL)
 | 
						|
{
 | 
						|
  if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1)
 | 
						|
    throw std::runtime_error("Error initializing SDL video");
 | 
						|
}
 | 
						|
SDLGLDriver::~SDLGLDriver()
 | 
						|
{
 | 
						|
  if(display) delete display;
 | 
						|
  SDL_Quit();
 | 
						|
}
 | 
						|
 | 
						|
// Surface used for the screen. Since OpenGL surfaces must have sizes
 | 
						|
// that are powers of 2, we have to "fake" the returned display size
 | 
						|
// to match the screen, not the surface itself. If we don't use this,
 | 
						|
// the client program will get confused about the actual size of our
 | 
						|
// screen, thinking it is bigger than it is.
 | 
						|
struct FakeSizeSprite : SDLGL_Sprite
 | 
						|
{
 | 
						|
  int fakeW, fakeH;
 | 
						|
 | 
						|
  FakeSizeSprite(SDL_Surface *s, int fw, int fh)
 | 
						|
    : SDLGL_Sprite(s), fakeW(fw), fakeH(fh)
 | 
						|
  {}
 | 
						|
 | 
						|
  int width() { return fakeW; }
 | 
						|
  int height() { return fakeH; }
 | 
						|
};
 | 
						|
 | 
						|
static int makePow2(int num)
 | 
						|
{
 | 
						|
  assert(num);
 | 
						|
  if((num & (num-1)) != 0)
 | 
						|
    {
 | 
						|
      int cnt = 0;
 | 
						|
      while(num)
 | 
						|
        {
 | 
						|
          num >>= 1;
 | 
						|
          cnt++;
 | 
						|
        }
 | 
						|
      num = 1 << cnt;
 | 
						|
    }
 | 
						|
  return num;
 | 
						|
}
 | 
						|
 | 
						|
void SDLGLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen)
 | 
						|
{
 | 
						|
  unsigned int flags;
 | 
						|
 | 
						|
  if(display) delete display;
 | 
						|
 | 
						|
  flags = SDL_OPENGL;
 | 
						|
 | 
						|
  if (fullscreen)
 | 
						|
    flags |= SDL_FULLSCREEN;
 | 
						|
 | 
						|
  SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
 | 
						|
  SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1 );
 | 
						|
 | 
						|
  // Create the surface and check it
 | 
						|
  screen = SDL_SetVideoMode(width, height, bpp, flags);
 | 
						|
  if(screen == NULL)
 | 
						|
    throw std::runtime_error("Failed setting SDL video mode");
 | 
						|
 | 
						|
  // Expand width and height to be powers of 2
 | 
						|
  int width2 = makePow2(width);
 | 
						|
  int height2 = makePow2(height);
 | 
						|
 | 
						|
  // Create a new SDL surface of this size
 | 
						|
  const SDL_PixelFormat& fmt = *(screen->format);
 | 
						|
  realDisp = SDL_CreateRGBSurface(SDL_SWSURFACE,width2,height2,
 | 
						|
                                  fmt.BitsPerPixel,
 | 
						|
                                  fmt.Rmask,fmt.Gmask,fmt.Bmask,fmt.Amask);
 | 
						|
 | 
						|
  // Create a sprite directly representing the display surface. This
 | 
						|
  // allows the user to blit to it directly.
 | 
						|
  display = new FakeSizeSprite(realDisp, width, height);
 | 
						|
 | 
						|
  // Set up the OpenGL format
 | 
						|
  nOfColors = fmt.BytesPerPixel;
 | 
						|
 | 
						|
  if(nOfColors == 4)
 | 
						|
    {
 | 
						|
      if (fmt.Rmask == 0x000000ff)
 | 
						|
        texture_format = GL_RGBA;
 | 
						|
      else
 | 
						|
        texture_format = GL_BGRA;
 | 
						|
    }
 | 
						|
  else if(nOfColors == 3)
 | 
						|
    {
 | 
						|
      if (fmt.Rmask == 0x000000ff)
 | 
						|
        texture_format = GL_RGB;
 | 
						|
      else
 | 
						|
        texture_format = GL_BGR;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    assert(0 && "unsupported screen format");
 | 
						|
 | 
						|
  glEnable(GL_TEXTURE_2D);
 | 
						|
 | 
						|
  // Have OpenGL generate a texture object handle for us
 | 
						|
  glGenTextures( 1, &texture );
 | 
						|
 | 
						|
  // Bind the texture object
 | 
						|
  glBindTexture( GL_TEXTURE_2D, texture );
 | 
						|
 | 
						|
  // Set the texture's stretching properties
 | 
						|
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
 | 
						|
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
 | 
						|
}
 | 
						|
 | 
						|
void SDLGLDriver::updateNoSwap()
 | 
						|
{
 | 
						|
  if(!realDisp) return;
 | 
						|
 | 
						|
  // Fist, set up the screen texture:
 | 
						|
 | 
						|
  // Bind the texture object
 | 
						|
  glBindTexture( GL_TEXTURE_2D, texture );
 | 
						|
 | 
						|
  // Edit the texture object's image data
 | 
						|
  glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, realDisp->w, realDisp->h, 0,
 | 
						|
                texture_format, GL_UNSIGNED_BYTE, realDisp->pixels );
 | 
						|
 | 
						|
  glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);
 | 
						|
  glLoadIdentity();
 | 
						|
 | 
						|
  // OpenGL barf. Set up the projection to match our screen
 | 
						|
  int vPort[4];
 | 
						|
  glGetIntegerv(GL_VIEWPORT, vPort);
 | 
						|
  glMatrixMode(GL_PROJECTION);
 | 
						|
  glPushMatrix();
 | 
						|
  glLoadIdentity();
 | 
						|
  glOrtho(0, vPort[2], 0, vPort[3], -1, 1);
 | 
						|
  glMatrixMode(GL_MODELVIEW);
 | 
						|
  glPushMatrix();
 | 
						|
  glLoadIdentity();
 | 
						|
 | 
						|
  glBegin( GL_QUADS );
 | 
						|
 | 
						|
  // Needed to move the screen into the right place
 | 
						|
  int diff = screen->h - realDisp->h;
 | 
						|
 | 
						|
  // Bottom-left vertex (corner)
 | 
						|
  glTexCoord2i( 0, 1 );
 | 
						|
  glVertex3f(0,diff,0);
 | 
						|
 | 
						|
  // Bottom-right vertex (corner)
 | 
						|
  glTexCoord2i( 1, 1 );
 | 
						|
  glVertex3f( realDisp->w, diff, 0.f );
 | 
						|
 | 
						|
  // Top-right vertex (corner)
 | 
						|
  glTexCoord2i( 1, 0 );
 | 
						|
  glVertex3f( realDisp->w, screen->h, 0.f );
 | 
						|
 | 
						|
  // Top-left vertex (corner)
 | 
						|
  glTexCoord2i( 0, 0 );
 | 
						|
  glVertex3f( 0, screen->h, 0.f );
 | 
						|
  glEnd();
 | 
						|
 | 
						|
  glMatrixMode(GL_PROJECTION);
 | 
						|
  glPopMatrix();
 | 
						|
  glMatrixMode(GL_MODELVIEW);
 | 
						|
  glPopMatrix();
 | 
						|
}
 | 
						|
 | 
						|
void SDLGLDriver::swap()
 | 
						|
{
 | 
						|
  SDL_GL_SwapBuffers();
 | 
						|
}
 | 
						|
 | 
						|
void SDLGLDriver::update()
 | 
						|
{
 | 
						|
  updateNoSwap();
 | 
						|
  swap();
 | 
						|
}
 | 
						|
 | 
						|
/// Set the window title, as well as the title of the window when
 | 
						|
/// "iconified"
 | 
						|
void SDLGLDriver::setWindowTitle(const std::string &title,
 | 
						|
                               const std::string &icon)
 | 
						|
{
 | 
						|
  SDL_WM_SetCaption( title.c_str(), icon.c_str() );
 | 
						|
}
 | 
						|
 | 
						|
// Convert the given surface to display format.
 | 
						|
static SDL_Surface* convertImage(SDL_Surface* surf)
 | 
						|
{
 | 
						|
  if(surf != NULL)
 | 
						|
    {
 | 
						|
      // Convert the image to the display buffer format, for faster
 | 
						|
      // blitting
 | 
						|
      SDL_Surface *surf2 = SDL_DisplayFormat(surf);
 | 
						|
      SDL_FreeSurface(surf);
 | 
						|
      surf = surf2;
 | 
						|
    }
 | 
						|
  return surf;
 | 
						|
}
 | 
						|
 | 
						|
/// Load sprite from an image file, using SDL_image.
 | 
						|
Sprite* SDLGLDriver::loadImage(const std::string &file)
 | 
						|
{
 | 
						|
  SDL_Surface *surf = IMG_Load(file.c_str());
 | 
						|
  surf = convertImage(surf);
 | 
						|
  if(surf == NULL)
 | 
						|
    throw std::runtime_error("SDL failed to load image file '" + file + "'");
 | 
						|
  return spriteFromSDL(surf);
 | 
						|
}
 | 
						|
 | 
						|
/// Load sprite from an SDL_RWops structure. autoFree determines
 | 
						|
/// whether the RWops struct should be closed/freed after use.
 | 
						|
Sprite* SDLGLDriver::loadImage(SDL_RWops *src, bool autoFree)
 | 
						|
{
 | 
						|
  SDL_Surface *surf = IMG_Load_RW(src, autoFree);
 | 
						|
  surf = convertImage(surf);
 | 
						|
  if(surf == NULL)
 | 
						|
    throw std::runtime_error("SDL failed to load image");
 | 
						|
  return spriteFromSDL(surf);
 | 
						|
}
 | 
						|
 | 
						|
/// Load a sprite from an image file stored in memory. Uses
 | 
						|
/// SDL_image.
 | 
						|
Sprite* SDLGLDriver::loadImage(const void* data, size_t size)
 | 
						|
{
 | 
						|
  SDL_RWops *rw = SDL_RWFromConstMem(data, size);
 | 
						|
  return loadImage(rw, true);
 | 
						|
}
 | 
						|
 | 
						|
void SDLGLDriver::setGamma(float red, float green, float blue)
 | 
						|
{
 | 
						|
  SDL_SetGamma(red,green,blue);
 | 
						|
}
 | 
						|
 | 
						|
/// Convert an existing SDL surface into a sprite
 | 
						|
Sprite* SDLGLDriver::spriteFromSDL(SDL_Surface *surf, bool autoFree)
 | 
						|
{
 | 
						|
  assert(surf);
 | 
						|
  return new SDLGL_Sprite(surf, autoFree);
 | 
						|
}
 | 
						|
 | 
						|
void SDLGLDriver::sleep(int ms) { SDL_Delay(ms); }
 | 
						|
unsigned int SDLGLDriver::ticks() { return SDL_GetTicks(); }
 |